Add the paper direction with thin tracks, outlined hexagons and line arrows, without exterior color markers or piece numbers. Add a unified catalog for five designs and their 25 themes, with direct review links. Organize each design into an authored design.md, generated tokens.json and preview. Add Agent routing, shared integration guidance, a machine-readable manifest and an executable rendering example. Preserve legacy preview and document links. Validation: 16 focused design, theme and package tests passed; npm run design and static build passed. Confirmed all eight authored documents survive generation unchanged. Browser checks covered mobile, tablet and desktop layouts, theme links and migrated previews. Native engine integration and visual verification remain pending.
61 lines
4.4 KiB
JavaScript
61 lines
4.4 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { ART_DIRECTIONS, directionTheme, artDirection, appearanceTile } from '../src/art-directions.js';
|
|
import { contrast } from '../src/design-tokens.js';
|
|
import { drawScene, drawTile, drawExit } from '../src/renderer.js';
|
|
import { createStudyLevel } from '../src/study-levels.js';
|
|
import { available, trace } from '../src/core.js';
|
|
import { layout } from '../src/geometry.js';
|
|
test('paper study preserves the variants reference: pale thin rails and outlined hexagons with line arrows',()=>{
|
|
const theme=directionTheme(artDirection('paper'),'lagoon'),commands=[];
|
|
const p={polygon:(points,color)=>commands.push({kind:'polygon',points,color}),line:(points,color,width)=>commands.push({kind:'line',points,color,width}),circle:()=>{}};
|
|
assert.equal(theme.background,'#EAF0F3');assert.equal(theme.materials.board,'#FAFBF7');assert.equal(theme.materials.track,'#B2C4CE');
|
|
assert.deepEqual(theme.pigments.slice(0,2).map(p=>p.color),['#D66C4D','#477FC0']);
|
|
drawTile(p,50,50,40,appearanceTile({color:0,dir:0},theme));
|
|
assert.equal(commands[0].points.length,6);assert.equal(commands[0].color,'#D66C4D');
|
|
assert.ok(Math.abs(commands[0].points[0].x-50-40*.88)<1e-9);
|
|
assert.equal(commands[1].color,'#FFFFFFCC');assert.ok(Math.abs(commands[1].width-40*.035)<1e-9);
|
|
assert.deepEqual(commands.slice(2).map(c=>[c.kind,c.points.length,c.color]),[['line',2,'#FFFFFF'],['line',3,'#FFFFFF']]);
|
|
assert.ok(commands[2].points[1].y<commands[2].points[0].y,'direction zero still points up');
|
|
commands.length=0;
|
|
const level=createStudyLevel(3);drawScene(p,level,[],{size:40,ox:260,oy:260},{theme});
|
|
const rails=commands.filter(c=>c.kind==='line'&&c.color===theme.materials.track);
|
|
assert.equal(rails.length,level.cells.length*3);
|
|
assert.ok(rails.every(c=>Math.abs(c.width-40*.055)<1e-9));
|
|
assert.ok(!commands.some(c=>c.kind==='line'&&c.color===theme.materials.trackHalo),'no thick rail halo');
|
|
});
|
|
test('each of five art directions has five readable themes and independent token colors',()=>{
|
|
assert.equal(ART_DIRECTIONS.length,5);assert.equal(new Set(ART_DIRECTIONS.map(d=>d.id)).size,5);
|
|
for(const d of ART_DIRECTIONS){
|
|
assert.deepEqual(d.themes.map(t=>t.label),['轻松','进阶','烧脑','挑战','每周挑战']);
|
|
for(const t of d.themes){assert.ok(contrast(t.background,t.ink)>=4.5);assert.equal(directionTheme(d,t.id).artStyle,d.style);}
|
|
for(const p of d.pigments)assert.ok(contrast(p.color,p.arrow)>=3);
|
|
}
|
|
assert.equal(artDirection('invalid').id,'calm');
|
|
});
|
|
test('art styles change actual geometry without changing level state, including moving tokens',()=>{
|
|
const level=createStudyLevel(3),copy=JSON.stringify(level),view=layout(level.cells,520,600),tile=available(level,level.tiles)[0],route=trace(level,tile,level.tiles);
|
|
const signatures=[];
|
|
for(const d of ART_DIRECTIONS){
|
|
const commands=[],p={polygon:(points,color)=>{assert.ok(color);assert.ok(points.every(p=>Number.isFinite(p.x)&&Number.isFinite(p.y)));commands.push(['polygon',points.length,color]);},circle:(x,y,r,color)=>{assert.ok(Number.isFinite(r)&&r>=0);commands.push(['circle',r,color]);},line:(points,color,width)=>{assert.ok(width>0);commands.push(['line',points.length,color]);}};
|
|
const theme=directionTheme(d,'lagoon'),t=appearanceTile({color:0,dir:1},theme);
|
|
drawTile(p,50,50,40,t);signatures.push(JSON.stringify(commands));
|
|
drawScene(p,level,level.tiles,view,{theme});drawExit(p,route,view,.4,t);drawExit(p,route,view,.99,t);
|
|
}
|
|
assert.equal(new Set(signatures).size,5);assert.equal(JSON.stringify(level),copy);
|
|
});
|
|
test('vivid worlds retain strong chroma and each new theme has its own seven-color mix',()=>{
|
|
const vivid=artDirection('vivid');
|
|
assert.equal(vivid.themes[0].background,'#B8F34A');
|
|
for(const t of vivid.themes){
|
|
const rgb=t.background.slice(1).match(/../g).map(v=>parseInt(v,16)/255),hi=Math.max(...rgb),lo=Math.min(...rgb),l=(hi+lo)/2,s=(hi-lo)/(1-Math.abs(2*l-1));
|
|
assert.ok(s>=.65&&l<=.65,`${t.name} should keep the saturated poster treatment`);
|
|
}
|
|
for(const d of ART_DIRECTIONS.slice(1)){
|
|
assert.equal(new Set(d.themes.map(t=>directionTheme(d,t.id).palette.slice().sort((a,b)=>a-b).join(','))).size,5);
|
|
assert.equal(new Set(d.themes.map(t=>t.materials.board)).size,5);
|
|
// The paper study deliberately retains the reference's low-contrast rails.
|
|
if(d.style!=='paper')for(const t of d.themes)assert.ok(contrast(t.materials.track,t.materials.board)>=3);
|
|
}
|
|
});
|