bend_puzzle/test/v4782-audio-highlight-store-internal-gate-smoke-test.js
2026-08-01 16:06:14 +09:00

36 lines
4.8 KiB
JavaScript

'use strict';
const {assert,functionSource,app,css,html,read,buildMeta}=require('./helpers/app-source');
const packageVersion=JSON.parse(read('package.json')).version;
assert(packageVersion===buildMeta.PACKAGE_VERSION&&buildMeta.APP_VERSION==='48.0','v47.84 canonical version marker is missing');
const sound=functionSource('playSound');
assert(app.includes('SOUND_GAIN_MULTIPLIER=5.2')&&functionSource('soundTone').includes('Math.min(.42')&&functionSource('soundNoise').includes('Math.min(.24'),'Requested sound level increase is missing');
for(const kind of['grab','stretch','gate','clear','buy','reset','remove','shop','warp','key','error'])assert(sound.includes(`kind==='${kind}'`),`Missing distinct ${kind} sound`);
assert(functionSource('finalizeAtGate').includes("playSound('gate')")&&functionSource('removeDetachedPathAtOwnEndpoint').includes("playSound('remove')")&&functionSource('beginMinimapPointer').includes("playSound('shop')"),'New sound variants are not wired to gameplay events');
const makeBoard=functionSource('makeBoard');
assert(!makeBoard.includes('active-board-boundary')&&!css.includes('active-board-boundary')&&!css.includes('@keyframes activeBoardOrbit'),'Retired active-board orbit boundary remains');
assert(html.includes('<div class="modal-actions settings-actions"><button class="pill quiet" id="resetSettings"')&&html.includes('</button><button class="pill close" id="closeSettings"'),'Settings reset and close buttons are not adjacent in document order');
assert(css.includes('.settings-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px}')&&css.includes('.settings-actions .pill{flex:1 1 0;width:auto;margin:0!important}'),'Settings actions are not horizontally aligned');
const remove=functionSource('removeDetachedPathAtOwnEndpoint'),bind=functionSource('bindBoard');
assert(remove.includes("if(typeof renderBoardNow==='function')renderBoardNow(b)")&&bind.includes('renderBoardNow(b);scheduleBoardCommandSettlement'),'Line removal does not synchronously repaint before deferred settlement');
assert(bind.includes('const release=(paintCurrent=false)=>{if(paintCurrent&&b.card?.isConnected)renderBoardNow(b)'),'Drag cleanup can still expose stale pre-removal geometry');
assert(makeBoard.includes('<small>ショップ</small>')&&makeBoard.includes('<em></em>'),'Cyber-pop shop markup is missing');
assert(css.includes('repeating-linear-gradient(90deg,#ff45c5')&&css.includes('.line-store .shop-shell em{'),'Cyber-pop shop appearance is missing');
const minimap=functionSource('rebuildMinimapWorld'),overview=functionSource('rebuildWorldOverviewCache'),minimapPointer=functionSource('beginMinimapPointer'),pan=functionSource('beginPan');
assert(minimap.includes('drawMapStores')&&overview.includes('drawMapStores'),'Shop positions are not rendered in both minimap and zoomed-out overview');
assert(minimapPointer.includes('nearestStoreMetaAtWorldPoint')&&minimapPointer.includes('openStoreMeta(storeMeta)')&&pan.includes('nearestStoreMetaAtWorldPoint')&&pan.includes('openStoreMeta(storeMeta)'),'Shop markers are not clickable from minimap and simplified overview');
assert(!html.includes('minimapStatus')&&functionSource('drawMinimap').includes('店舗マーカーをクリックするとショップを開きます。'),'Minimap still exposes counts or lacks shop instructions');
assert(!app.includes('INTERNAL_GATE_DEBUG_ALL_LEVELS')&&functionSource('addSpecialCellPattern').includes('AppLogic.specialSchedule'),'Internal gates are not governed solely by the production special-mechanic schedule');
const addInternal=functionSource('addInternalGateSpecial'),gatePoint=functionSource('gatePoint'),outside=functionSource('outsidePoint');
assert(addInternal.includes('special.internalGates.push({a:aIndex,b:bIndex})')&&addInternal.includes('p.solution.splice(chosen.pathIndex,1,prefix,suffix)'),'Internal gates do not split a normal solution path into operable gate endpoints');
assert(addInternal.includes('const clueable=[prefix,suffix].every')&&addInternal.includes('special.internalGates.pop()'),'Internal-gate generation does not retry unsafe zero-clue splits');
assert(gatePoint.includes('if(g.internal)return[x,y]')&&outside.includes('if(g?.internal)return null'),'Internal gates are not rendered/analysed as in-board endpoints');
assert(makeBoard.includes("'aria-label':g.internal?`盤面内ゲート ${i+1}`")&&makeBoard.includes('gateHitBox(gp,g.side,g.internal)'),'Internal gates do not share normal gate input affordances');
for(const name of['matchingNeighborGate','gateFrontierCandidates','missingGateConnections','placementConnectionRequirements'])assert(functionSource(name).includes('internalGates'),`${name} can misclassify an internal gate as a world-expansion gate`);
console.log('v47.84 audio, active-board emphasis, shop navigation, removal repaint, and internal-gate regression test passed');