clone

a browser grid puzzle game where you push words around to rewrite the rules of each level

Cover bundled worlds in browser share flow

Dami committed f82ad3d
commit
f82ad3dccd56d4d91c8fae52afd15801b6cb3b6b
parent
30170c8577377fe728e4b1b6085f343276769d55
author
Dami <dami.etoile@gmail.com>
1 file changed, +56 -8
Mtests/browser/share-flow.mjs64++++++++++++++++++++++++++----
M tests/browser/share-flow.mjs +56 -8
@@ -172,6 +172,7 @@ async function importLocalModule(page, filePath, { install = true } = {}) {
172172 await page.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'gift');173173 await page.keyboard.press('Enter');174174 await page.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'index');175+ await dismissFirstWorldCard(page);175176 }176177 }177178
@@ -316,18 +317,28 @@ async function assertGiftActionsBelowPreview(page, label) {
316317 }317318 318319 async function openDevProof(page) {320+ await page.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'title');319321 await page.keyboard.press('Enter');320322 await page.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'index');323+ // First entry to every world presents a card before map actions are accepted.324+ await dismissFirstWorldCard(page);321325 await page.keyboard.press('Enter');322326 await page.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'proof');323327 }324328 329+async function dismissFirstWorldCard(page) {330+ await page.keyboard.press('Enter');331+ await page.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'index');332+}333+325334 const tempDir = await mkdtemp(`${tmpdir()}/miau-share-flow-`);326335 await mkdir(stageScreenshotDir, { recursive: true });327336 const firstPath = resolve(tempDir, 'browser-smoke-one.cjs');328337 const secondPath = resolve(tempDir, 'browser-smoke-two.cjs');329338 const invalidPath = resolve(tempDir, 'browser-smoke-invalid.cjs');330339 const selfWinPath = resolve(tempDir, 'browser-self-win.cjs');340+const gardenExamplePath = resolve(projectRoot, 'mods/garden-basics.cjs');341+const wordLabExamplePath = resolve(projectRoot, 'mods/word-lab.cjs');331342 await writeFile(firstPath, fixtureOne);332343 await writeFile(secondPath, fixtureTwo);333344 await writeFile(invalidPath, invalidFixture);
@@ -360,6 +371,9 @@ try {
360371 report('title Space opens Workshop and Escape returns to title');361372 await stagePage.keyboard.press('Enter');362373 await stagePage.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'index');374+ await stagePage.waitForTimeout(700);375+ await stagePage.screenshot({ path: `${stageScreenshotDir}/d20-core-world-card.png` });376+ await dismissFirstWorldCard(stagePage);363377 await stagePage.screenshot({ path: `${stageScreenshotDir}/index.png` });364378 await stagePage.keyboard.press('Space');365379 await stagePage.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'workshop');
@@ -373,6 +387,7 @@ try {
373387 await pointerPage.goto(playerUrl, { waitUntil: 'networkidle' });374388 await pointerPage.keyboard.press('Enter');375389 await pointerPage.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'index');390+ await dismissFirstWorldCard(pointerPage);376391 await pointerPage.waitForTimeout(900);377392 for (const [x, y] of [[1035, 760], [1035, 775], [1080, 760]]) {378393 await pointerPage.mouse.click(x, y);
@@ -428,9 +443,29 @@ try {
428443 await openDevProof(page);429444 await page.locator('#level-title').waitFor();430445 const initialPackCount = await packCount(page);431- assert.ok(initialPackCount >= 1, 'the built-in catalog must expose at least one pack');432- assert.ok(await page.locator('#level-list [data-level], #level-list .level-choice').count() >= 3, 'the built-in campaign must remain playable');433- report(`built-in catalog loaded (${initialPackCount} pack choice${initialPackCount === 1 ? '' : 's'})`);446+ assert.equal(initialPackCount, 4, 'the bundled catalog should contain core and three approved worlds');447+ const bundledPackIds = await page.locator('#pack-list [data-pack-id]').evaluateAll(nodes => nodes.map(node => node.dataset.packId));448+ assert.deepEqual(bundledPackIds, ['core', 'crossword', 'crate-works', 'tidewater'], 'bundled worlds should keep the approved order');449+ for (const [packId, minimumLevels] of [['core', 3], ['crossword', 7], ['crate-works', 7], ['tidewater', 7]]) {450+ assert.ok(await page.locator(`[data-level-id^="${packId}:"]`).count() >= minimumLevels, `${packId} should expose its authored world levels`);451+ }452+ await page.screenshot({ path: `${stageScreenshotDir}/d20-bundled-world-catalog.png` });453+ report('bundled catalog order and per-world level cards are present');454+455+ const examplesContext = await browser.newContext();456+ const examplesPage = await examplesContext.newPage();457+ examplesPage.setDefaultTimeout(timeout);458+ await examplesPage.goto(devUrl, { waitUntil: 'networkidle' });459+ await openDevProof(examplesPage);460+ await importLocalModule(examplesPage, gardenExamplePath);461+ await examplesPage.waitForFunction(() => document.body.textContent.includes('Basic Proofs'));462+ await importLocalModule(examplesPage, wordLabExamplePath);463+ await examplesPage.waitForFunction(() => document.body.textContent.includes('Word Lab'));464+ assert.ok(await examplesPage.locator('[data-pack-id="garden-basics"]').count(), 'Garden Basics should remain importable as an example pack');465+ assert.ok(await examplesPage.locator('[data-pack-id="word-lab"]').count(), 'Word Lab should remain importable as an example pack');466+ await examplesPage.close();467+ await examplesContext.close();468+ report('Garden Basics and Word Lab remain importable examples');434469 435470 await importLocalModule(page, firstPath);436471 await page.waitForFunction(() => document.body.textContent.includes('Browser Smoke One'));
@@ -509,7 +544,9 @@ try {
509544 await workshopPage.screenshot({ path: `${stageScreenshotDir}/workshop.png` });510545 assert.equal(await workshopPage.locator('.stage-shell').getAttribute('data-scene'), 'workshop', 'pause W should open Workshop');511546 report('Workshop opens from pause and renders its canvas scene');512- for (let index = 0; index < 4; index += 1) await workshopPage.keyboard.press('ArrowDown');547+ // Workshop rows begin with IMPORT, followed by the four bundled packs.548+ // Browser Smoke One is therefore the fifth down step from the import row.549+ for (let index = 0; index < 5; index += 1) await workshopPage.keyboard.press('ArrowDown');513550 for (let index = 0; index < 3; index += 1) await workshopPage.keyboard.press('ArrowRight');514551 await workshopPage.keyboard.press('Enter');515552 await workshopPage.screenshot({ path: `${stageScreenshotDir}/workshop-remove-confirm.png` });
@@ -546,6 +583,7 @@ try {
546583 await returnFlowPage.waitForTimeout(700);547584 await returnFlowPage.keyboard.press('Enter');548585 await returnFlowPage.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'index');586+ await dismissFirstWorldCard(returnFlowPage);549587 await returnFlowPage.keyboard.press('Space');550588 await returnFlowPage.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'workshop');551589 await importLocalModule(returnFlowPage, secondPath, { install: false });
@@ -666,6 +704,8 @@ try {
666704 await navigationPage.goto(devUrl, { waitUntil: 'networkidle' });667705 await openDevProof(navigationPage);668706 await navigationPage.locator('#level-title').waitFor();707+ await importLocalModule(navigationPage, wordLabExamplePath);708+ await navigationPage.waitForFunction(() => document.body.textContent.includes('Word Lab'));669709 await selectPack(navigationPage, 'word-lab');670710 await navigationPage.waitForFunction(() => document.querySelector('[data-pack-id="word-lab"]')?.classList.contains('active'));671711 const wordLabLevels = navigationPage.locator('[data-level-id^="word-lab:"]');
@@ -732,6 +772,11 @@ try {
732772 await continuePage.waitForFunction(() => JSON.parse(window.render_game_to_text()).mode === 'won');733773 await continuePage.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'win');734774 const firstProof = JSON.parse(await continuePage.evaluate(() => window.render_game_to_text()));775+ await continuePage.waitForFunction(() => {776+ const button = document.querySelector('#continue-btn');777+ return button && !button.disabled;778+ });779+ await continuePage.waitForTimeout(700);735780 await continuePage.locator('#continue-btn').click();736781 await continuePage.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'proof');737782 const secondProof = JSON.parse(await continuePage.evaluate(() => window.render_game_to_text()));
@@ -834,16 +879,19 @@ try {
834879 assert.equal(await accessibilityPage.locator('.stage-shell').getAttribute('data-scene'), 'title', 'title should accept a single movement key without leaving title');835880 await accessibilityPage.keyboard.press('Enter');836881 await accessibilityPage.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'index');882+ await dismissFirstWorldCard(accessibilityPage);837883 await accessibilityPage.screenshot({ path: `${stageScreenshotDir}/mobile-index.png` });838884 const indexCanvas = accessibilityPage.locator('.stage-canvas');839885 const indexBox = await indexCanvas.boundingBox();840886 assert.ok(indexBox, 'mobile INDEX should expose a tappable canvas');841- await accessibilityPage.mouse.click(indexBox.x + 270, indexBox.y + 820);842- await accessibilityPage.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'workshop');887+ // The compact mobile footer is the menu/workshop button at the lower-right888+ // of the canvas; click its center rather than the adjacent empty margin.889+ await accessibilityPage.mouse.click(indexBox.x + 320, indexBox.y + 826);890+ await accessibilityPage.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'pause');843891 await accessibilityPage.keyboard.press('Escape');844892 await accessibilityPage.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'index');845- report('mobile INDEX Workshop footer pointer opens Workshop and Escape returns');846- await accessibilityPage.mouse.click(indexBox.x + 64, indexBox.y + 405);893+ report('mobile INDEX menu footer pointer opens Pause and Escape returns');894+ await accessibilityPage.mouse.click(indexBox.x + 64, indexBox.y + 440);847895 await accessibilityPage.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'proof');848896 report('mobile reduced-motion title key and INDEX touch entry work without audio');849897 assert.deepEqual(accessibilityErrors, [], 'missing audio should not produce browser errors');