first commit
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {canWalk,movementDirection} from '../src/controls.js';
|
||||
import {products,createState,purchase,INITIAL_BALANCE,formatHetoman} from '../src/game-state.js';
|
||||
import {createFamily} from '../src/characters.js';
|
||||
test('Walking remains enabled during dialogue; arrows up/down only zoom',()=>{
|
||||
assert.ok(canWalk({chapterId:'bridge'}));assert.ok(canWalk({chapterId:'fish'}));
|
||||
assert.equal(canWalk({busy:true}),false);assert.equal(canWalk({shopOpen:true}),false);
|
||||
assert.equal(movementDirection(new Set(['arrowup']),new Set()),0);
|
||||
assert.equal(movementDirection(new Set(['arrowright']),new Set()),1);
|
||||
assert.equal(movementDirection(new Set(),new Set([-1])),-1);
|
||||
});
|
||||
test('Toman balance, charges and picnic reserve',()=>{
|
||||
assert.equal(INITIAL_BALANCE,500);assert.match(formatHetoman(15),/هتومان/);
|
||||
for(const p of products){const s=createState();assert.ok(purchase(s,p.id));assert.equal(s.balance,INITIAL_BALANCE-p.cost);}
|
||||
const s={...createState(),balance:40};assert.equal(purchase(s,'channel-22'),null);assert.equal(s.balance,40);assert.ok(purchase(s,'channel-23'));s.busy=false;assert.ok(purchase(s,'channel-24'));assert.equal(s.balance,0);
|
||||
});
|
||||
test('Character transforms stay finite through walk, pickup and sitting',()=>{
|
||||
const f=createFamily();for(const facing of [-Math.PI/2,Math.PI/2,Math.PI])for(const mode of ['walk','shop','sit']){
|
||||
f.update({time:3,gait:2,dt:.016,moving:mode==='walk',facing,shopping:mode==='shop',finished:mode==='sit'});f.group.updateMatrixWorld(true);f.group.traverse(o=>assert.ok(o.matrixWorld.elements.every(Number.isFinite)));
|
||||
}
|
||||
});
|
||||
|
||||
test('Sixty channels, exactly 21–60 purchasable, Hetoman conversion',async()=>{
|
||||
const {channels,productByCode,hetomanToRial}=await import('../src/game-state.js');
|
||||
assert.equal(channels.length,60);assert.equal(products.length,40);
|
||||
assert.equal(hetomanToRial(10),100000);
|
||||
for(let n=1;n<=60;n++){assert.equal(Boolean(productByCode(String(n))),n>=21);}
|
||||
assert.equal(productByCode('۲۲').channel,22);assert.equal(productByCode('٦٠').channel,60);assert.equal(productByCode('61'),null);
|
||||
const s=createState();assert.equal(purchase(s,'channel-20'),null);assert.equal(s.balance,500);
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {createState,productByCode} from '../src/game-state.js';
|
||||
import {createPayment,beginPayment,swipePayment,cancelPayment} from '../src/payment.js';
|
||||
test('Payment charges once, only after ready and explicit swipe',()=>{
|
||||
const s=createState(),p=createPayment(),product=productByCode('21');assert.ok(product);
|
||||
assert.ok(beginPayment(s,p,product));assert.equal(s.balance,500);assert.equal(s.basket.length,0);assert.equal(s.busy,true);
|
||||
assert.equal(swipePayment(s,p),false);assert.equal(beginPayment(s,p,product),false);
|
||||
p.phase='ready';assert.ok(swipePayment(s,p));assert.equal(s.balance,500-product.cost);assert.equal(s.basket.length,1);
|
||||
assert.equal(swipePayment(s,p),false);assert.equal(cancelPayment(s,p),false);
|
||||
});
|
||||
test('Cancel or invalid payment never changes balance or basket',()=>{
|
||||
const s=createState(),p=createPayment();assert.equal(beginPayment(s,p,productByCode('20')),false);
|
||||
assert.ok(beginPayment(s,p,productByCode('60')));assert.ok(cancelPayment(s,p));assert.equal(s.balance,500);assert.equal(s.basket.length,0);assert.equal(s.busy,false);
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {createStory,completeChapter,nextChapter,storyReady,routeOffset,discoveries} from '../src/story.js';
|
||||
import {createState,purchase,readyForPicnic,productByCode} from '../src/game-state.js';
|
||||
for(const route of ['sunny'])test(`Complete ${route} story and picnic`,()=>{
|
||||
const story=createStory();assert.equal(nextChapter(story,-8),'morning');
|
||||
for(const [id,x,choice] of [['morning',-8],['rabbit',-3,'watch'],['bridge',-2],['fish',0],['courage',3],['wind',12],['paper',14,'recover'],['fork',16,route],[route,19]]){
|
||||
assert.equal(nextChapter(story,x),id);assert.ok(completeChapter(story,id,choice));assert.equal(completeChapter(story,id,choice),false);
|
||||
}
|
||||
assert.ok(storyReady(story));assert.equal(nextChapter(story,22),null);assert.ok(discoveries(story).length>=5);
|
||||
assert.equal(routeOffset(story,16),0);assert.equal(routeOffset(story,21),0);assert.equal(Math.sign(routeOffset(story,18)),route==='sunny'?1:-1);
|
||||
assert.equal(completeChapter(story,'summit','invalid'),false);assert.ok(completeChapter(story,'summit','gold'));assert.equal(story.sky,'gold');
|
||||
const state=createState();assert.ok(purchase(state,productByCode('۲۲').id));state.busy=false;assert.ok(purchase(state,productByCode('۲۴').id));assert.ok(readyForPicnic(state));assert.equal(state.balance,440);
|
||||
});
|
||||
test('No early triggers, unknown choices, or leaked reset state',()=>{
|
||||
const story=createStory();assert.equal(storyReady(story),false);completeChapter(story,'morning');assert.equal(nextChapter(story,-8),null);
|
||||
assert.equal(completeChapter(story,'rabbit','wrong'),false);assert.equal(completeChapter(story,'unknown'),false);
|
||||
assert.deepEqual(discoveries(createStory()),[]);assert.equal(routeOffset(createStory(),18),0);
|
||||
});
|
||||
|
||||
test('Every story action exposes one fixed action instead of choices',async()=>{
|
||||
const {chapters}=await import('../src/story.js');
|
||||
for(const chapter of Object.values(chapters))assert.equal(chapter.choices,undefined);
|
||||
for(const id of ['rabbit','paper','fork','summit']){const s=createStory();assert.equal(completeChapter(s,id,'wrong'),false);assert.ok(completeChapter(s,id,chapters[id].action.value));}
|
||||
});
|
||||
Reference in New Issue
Block a user