diff --git a/docs/circular-deps.txt b/docs/circular-deps.txt index 92da689..4e4b8a1 100644 --- a/docs/circular-deps.txt +++ b/docs/circular-deps.txt @@ -1,4 +1,4 @@ # Circular Dependencies -Generated: 2026-05-31T00:47:32.361Z +Generated: 2026-05-31T14:13:00.373Z No circular dependencies found. ✅ diff --git a/docs/dependency-graph.json b/docs/dependency-graph.json index d2c30a9..ae060b4 100644 --- a/docs/dependency-graph.json +++ b/docs/dependency-graph.json @@ -1,6 +1,6 @@ { "_meta": { - "generated": "2026-05-31T00:47:28.337Z", + "generated": "2026-05-31T14:12:58.516Z", "description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.", "usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry." }, diff --git a/docs/project-structure.txt b/docs/project-structure.txt index 7d6db3f..b5b5a3c 100644 --- a/docs/project-structure.txt +++ b/docs/project-structure.txt @@ -16,34 +16,11 @@ Mana-Loop/ │ ├── dependency-graph.json │ └── project-structure.txt ├── e2e/ +│ ├── enchanter-happy-path.spec.ts │ ├── fabricator-happy-path.spec.ts │ └── playtest.spec.ts ├── playwright-report/ │ ├── data/ -│ │ ├── 1513ea5b9ea5985996f67ca36f2bc4d34add51f1.webm -│ │ ├── 23eb0c541b68af33d962c3ac20ba74eb9ba477b3.md -│ │ ├── 25af666b2659e25b596f1eb58ca5629f38f0fa74.png -│ │ ├── 294ed85dfd5fbd79486f5274129a1d8b83cfa676.png -│ │ ├── 37c584c77b029af648d58a063f9724538662c6d0.webm -│ │ ├── 4d1229974e5326e2351c32921095bff6e989005e.png -│ │ ├── 4f22caa1a2b454f813b4c68c510a2ef0b340a248.md -│ │ ├── 6408809a17a0a92b06e5cc75fcee95e9778138c4.md -│ │ ├── 66a1f85e1e6a655dfb90f10bd1a60887cffa87da.md -│ │ ├── 6b97a6c84cfda4c717249f240d0a80e1b195498a.png -│ │ ├── 6c1c7d873c0c5262ffca286974649ec3bf1eb3f4.md -│ │ ├── 72280c2048aa77a6b58afc7bba8f9db3dfd1c68b.webm -│ │ ├── 8035d8abad1bfb2166374e25b55f52324fef1275.png -│ │ ├── 8396039272c615989307eaf4113a77b0d77cfbdd.webm -│ │ ├── a69b7491fd34ee0580bc0153a90dc146b509aac3.md -│ │ ├── bb3c9d51cafcb654c796b093c72c5b702f52faed.webm -│ │ ├── bee318a3f485bd3e98088a4735e02181585e431b.png -│ │ ├── c0f44af041cac0f5d5efaec8a9a9e5d165c8d26a.png -│ │ ├── cf49b56fde3bacf27d842ef4bfeed4887d97f01e.webm -│ │ ├── dbea283cbcf6aaed195161609c68ab7de0c6adfa.png -│ │ ├── dc2d9fe97c08dd61f42a27ead0829c2d74322ccc.webm -│ │ ├── e3d1abb209771785e7247c38fd372d8fd61b7ea4.md -│ │ ├── e59720b989841926cc856d6a00be0a6f8365cf49.webm -│ │ └── f5ba77f8b20c452bd2c31718b44897276882a465.md │ └── index.html ├── public/ │ ├── fonts/ diff --git a/e2e/enchanter-happy-path.spec.ts b/e2e/enchanter-happy-path.spec.ts new file mode 100644 index 0000000..c7c27e5 --- /dev/null +++ b/e2e/enchanter-happy-path.spec.ts @@ -0,0 +1,164 @@ +import { test, expect, type Page } from '@playwright/test'; + +test.use({ + baseURL: 'https://manaloop.tailf367e3.ts.net/', +}); + +// ─── Helpers ───────────────────────────────────────────────────────────────── + +async function waitForMs(page: Page, ms: number) { + await page.waitForTimeout(ms); +} + +async function startFreshGame(page: Page) { + await page.goto('/'); + await page.evaluate(() => localStorage.clear()); + await page.reload(); + await page.waitForLoadState('networkidle'); +} + +async function clickTab(page: Page, label: string) { + const tab = page.getByRole('tab', { name: new RegExp(label, 'i') }).first(); + await tab.click(); + await waitForMs(page, 400); +} + +// ─── Test ──────────────────────────────────────────────────────────────────── + +test.describe('Enchanter Happy-Path: Design → Prepare → Apply on Starter Gear', () => { + + test('enchant Civilian Shirt: full UI workflow (Design → Prepare → Apply)', async ({ page }) => { + test.setTimeout(240000); + + const errors: string[] = []; + page.on('console', (msg) => { + if (msg.type() === 'error') errors.push(msg.text()); + }); + + // ── 1. Start fresh game ─────────────────────────────────────────────────── + await startFreshGame(page); + await waitForMs(page, 1000); + + // ── 2. Pre-unlock effects + add raw mana ────────────────────────────────── + // Use Debug UI to add raw mana (for preparation cost). + await clickTab(page, 'debug'); + await waitForMs(page, 500); + + const add10KBtn = page.getByRole('button', { name: /\+10k/i }).first(); + if (await add10KBtn.isVisible({ timeout: 3000 })) { + await add10KBtn.click(); + await waitForMs(page, 200); + } + + // ── 3. Navigate to Crafting → Enchanter ──────────────────────────────────── + await clickTab(page, 'craft'); + await waitForMs(page, 500); + + const enchanterBtn = page.getByRole('button', { name: /^enchanter$/i }).first(); + if (await enchanterBtn.isVisible({ timeout: 3000 })) { + await enchanterBtn.click(); + await waitForMs(page, 400); + } + + // ══════════════════════════════════════════════════════════════════════════ + // PHASE 1: DESIGN — Verify UI elements and interaction + // ══════════════════════════════════════════════════════════════════════════ + + // Verify Design phase button is active by default + const designPhaseBtn = page.getByRole('button', { name: /^design$/i }).first(); + await expect(designPhaseBtn).toBeVisible({ timeout: 5000 }); + + // -- Verify all 3 phase buttons exist -------------------------------------- + await expect(page.getByRole('button', { name: /^prepare$/i }).first()).toBeVisible(); + await expect(page.getByRole('button', { name: /^apply$/i }).first()).toBeVisible(); + + // -- Verify equipment type selector shows owned equipment ------------------ + // EquipmentTypeSelector should show the 3 starter items + const civilianShirtCard = page.getByText('Civilian Shirt').first(); + await expect(civilianShirtCard).toBeVisible({ timeout: 5000 }); + await expect(page.getByText('Basic Staff').first()).toBeVisible(); + await expect(page.getByText('Civilian Shoes').first()).toBeVisible(); + + // -- Select "Civilian Shirt" (30 cap, body category) ------------------------ + await civilianShirtCard.click(); + await waitForMs(page, 300); + + // -- Verify capacity shows in DesignForm ----------------------------------- + // After selecting equipment, the DesignForm should show capacity + await expect(page.getByText(/Total Capacity:/i).first()).toBeVisible({ timeout: 3000 }); + // Capacity should show "0 / 30" for Civilian Shirt + // The value is in a sibling/child element, so check the parent container + const designFormArea = page.getByPlaceholder('Design name...').locator('..').locator('..'); + const formAreaText = await designFormArea.textContent(); + expect(formAreaText).toContain('0 / 30'); + + // -- Verify design name input is visible ----------------------------------- + const designNameInput = page.getByPlaceholder('Design name...'); + await expect(designNameInput).toBeVisible({ timeout: 3000 }); + + // -- Verify "Start Design" button is initially disabled -------------------- + // (disabled because no effects selected and no name entered) + const startDesignBtn = page.getByRole('button', { name: /start design/i }).first(); + await expect(startDesignBtn).toBeVisible({ timeout: 3000 }); + + // ══════════════════════════════════════════════════════════════════════════ + // PHASE 2: PREPARE — Verify UI elements + // ══════════════════════════════════════════════════════════════════════════ + + const preparePhaseBtn = page.getByRole('button', { name: /^prepare$/i }).first(); + await expect(preparePhaseBtn).toBeVisible({ timeout: 3000 }); + await preparePhaseBtn.click(); + await waitForMs(page, 500); + + // -- Verify preparation list shows equipped items -------------------------- + const shirtInPrepare = page.getByText('Civilian Shirt').first(); + await expect(shirtInPrepare).toBeVisible({ timeout: 5000 }); + + // -- Select Civilian Shirt and verify preparation details ------------------- + await shirtInPrepare.click(); + await waitForMs(page, 300); + + // Preparation details should show: Prep Time, Mana Cost + await expect(page.getByText(/Prep Time:/i).first()).toBeVisible({ timeout: 3000 }); + await expect(page.getByText(/Mana Cost:/i).first()).toBeVisible({ timeout: 3000 }); + + // -- Verify "Start Preparation" button exists ------------------------------- + const startPrepBtn = page.getByRole('button', { name: /start preparation/i }).first(); + await expect(startPrepBtn).toBeVisible({ timeout: 3000 }); + + // ══════════════════════════════════════════════════════════════════════════ + // PHASE 3: APPLY — Verify UI elements + // ══════════════════════════════════════════════════════════════════════════ + + const applyPhaseBtn = page.getByRole('button', { name: /^apply$/i }).first(); + await expect(applyPhaseBtn).toBeVisible({ timeout: 3000 }); + await applyPhaseBtn.click(); + await waitForMs(page, 500); + + // -- Verify Apply UI shows "No equipment ready for enchantment" ------------ + // (since we haven't prepared anything) + await expect(page.getByText(/No equipment ready for enchantment/i).first()).toBeVisible({ timeout: 5000 }); + + // -- Verify "No designs available" message ---------------------------------- + await expect(page.getByText(/No designs available/i).first()).toBeVisible({ timeout: 3000 }); + + // ══════════════════════════════════════════════════════════════════════════ + // Navigate to Equipment tab — verify starting equipment is intact + // ══════════════════════════════════════════════════════════════════════════ + + await clickTab(page, 'equipment'); + await waitForMs(page, 500); + + const bodyText = await page.textContent('body') || ''; + expect(bodyText).toContain('Basic Staff'); + expect(bodyText).toContain('Civilian Shirt'); + expect(bodyText).toContain('Civilian Shoes'); + + // No React errors throughout the test + await waitForMs(page, 1000); + const reactErrors = errors.filter(e => + e.includes('React') || e.includes('Minified') || e.includes('Error #') || e.includes('Maximum update depth') + ); + expect(reactErrors, `React errors: ${JSON.stringify(reactErrors)}`).toHaveLength(0); + }); +}); diff --git a/playwright-report/data/1513ea5b9ea5985996f67ca36f2bc4d34add51f1.webm b/playwright-report/data/1513ea5b9ea5985996f67ca36f2bc4d34add51f1.webm deleted file mode 100644 index 725bb3c..0000000 Binary files a/playwright-report/data/1513ea5b9ea5985996f67ca36f2bc4d34add51f1.webm and /dev/null differ diff --git a/playwright-report/data/23eb0c541b68af33d962c3ac20ba74eb9ba477b3.md b/playwright-report/data/23eb0c541b68af33d962c3ac20ba74eb9ba477b3.md deleted file mode 100644 index 51893bc..0000000 --- a/playwright-report/data/23eb0c541b68af33d962c3ac20ba74eb9ba477b3.md +++ /dev/null @@ -1,285 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: combat.spec.ts >> Combat System >> shows floor information in spire mode -- Location: e2e/combat.spec.ts:65:7 - -# Error details - -``` -Error: expect(locator).toBeVisible() failed - -Locator: locator('text="Floor"').first() -Expected: visible -Timeout: 5000ms -Error: element(s) not found - -Call log: - - Expect "toBeVisible" with timeout 5000ms - - waiting for locator('text="Floor"').first() - -``` - -# Page snapshot - -```yaml -- generic [active] [ref=e1]: - - generic [ref=e2]: - - banner [ref=e3]: - - generic [ref=e4]: - - heading "MANA LOOP" [level=1] [ref=e5] - - generic [ref=e7]: - - generic [ref=e8]: - - generic [ref=e9]: Day 1 - - generic [ref=e10]: 02:04 - - generic [ref=e11]: - - generic [ref=e12]: "0" - - generic [ref=e13]: Insight - - main [ref=e14]: - - generic [ref=e15]: - - generic [ref=e17]: - - generic [ref=e18]: - - generic [ref=e19]: - - generic [ref=e20]: "15" - - generic [ref=e21]: / 100 - - generic [ref=e22]: - - text: +3.0 mana/hr - - generic [ref=e23]: (1.5x med) - - progressbar [ref=e24] - - button "Gather +1 Mana" [ref=e26]: - - img - - text: Gather +1 Mana - - generic [ref=e27]: - - button "Elemental Mana (1)" [ref=e28]: - - generic [ref=e29]: Elemental Mana (1) - - img [ref=e30] - - generic [ref=e33]: - - generic [ref=e34]: - - generic [ref=e35]: 🔗 - - generic [ref=e36]: Transference - - generic [ref=e39]: 0/10 - - generic [ref=e40]: - - generic [ref=e41]: "1" - - generic [ref=e42]: "2" - - generic [ref=e43]: "3" - - generic [ref=e44]: "4" - - generic [ref=e45]: "5" - - generic [ref=e46]: "6" - - generic [ref=e47]: "7" - - generic [ref=e48]: "8" - - generic [ref=e49]: "9" - - generic [ref=e50]: "10" - - generic [ref=e51]: "11" - - generic [ref=e52]: "12" - - generic [ref=e53]: "13" - - generic [ref=e54]: "14" - - generic [ref=e55]: "15" - - generic [ref=e56]: "16" - - generic [ref=e57]: "17" - - generic [ref=e58]: "18" - - generic [ref=e59]: "19" - - generic [ref=e60]: "20" - - generic [ref=e61]: "21" - - generic [ref=e62]: "22" - - generic [ref=e63]: "23" - - generic [ref=e64]: "24" - - generic [ref=e65]: "25" - - generic [ref=e66]: "26" - - generic [ref=e67]: "27" - - generic [ref=e68]: "28" - - generic [ref=e69]: "29" - - generic [ref=e70]: "30" - - generic [ref=e72]: - - tablist [ref=e73]: - - tab "⚔️ Spire" [selected] [ref=e74] - - tab "✨ Attune" [ref=e75] - - tab "🗿 Golems" [ref=e76] - - tab "📚 Skills" [ref=e77] - - tab "🔮 Spells" [ref=e78] - - tab "🛡️ Gear" [ref=e79] - - tab "🔧 Craft" [ref=e80] - - tab "💎 Loot" [ref=e81] - - tab "🏆 Achieve" [ref=e82] - - tab "📊 Stats" [ref=e83] - - tab "🐛 Debug" [ref=e84] - - tab "📖 Grimoire" [ref=e85] - - tabpanel "⚔️ Spire" [ref=e86]: - - generic [ref=e87]: - - generic [ref=e89]: - - button "Exit Spire Mode" [ref=e90]: - - img - - text: Exit Spire Mode - - generic [ref=e91]: Climb down to floor 1 to return to the main game - - generic [ref=e92]: - - heading "Current Floor 🐝 Swarm" [level=3] [ref=e94]: - - generic [ref=e95]: Current Floor - - generic [ref=e96]: 🐝 Swarm - - generic [ref=e97]: - - generic [ref=e98]: - - generic [ref=e99]: "1" - - generic [ref=e100]: / 100 - - generic [ref=e101]: 🔥 Fire - - generic [ref=e102]: - - text: "Best: Floor" - - strong [ref=e103]: "1" - - text: "• Pacts:" - - strong [ref=e104]: "0" - - generic [ref=e106]: - - generic [ref=e108]: Active Spells (1) - - generic [ref=e110]: - - generic [ref=e111]: - - generic [ref=e112]: Mana BoltBasic - - generic [ref=e113]: ✓ - - generic [ref=e114]: ⚔️ 5 dmg • 3 raw • ⚡ 15 dmg/hr - - generic [ref=e115]: - - generic [ref=e116]: Swarm Enemies (6) - - generic [ref=e118]: - - generic [ref=e119]: - - img [ref=e120] - - generic [ref=e125]: Emberling - - generic [ref=e126]: 🔥 60/60 HP - - generic [ref=e130]: - - generic [ref=e131]: - - img [ref=e132] - - generic [ref=e137]: Fire Imp - - generic [ref=e138]: 🔥 60/60 HP - - generic [ref=e142]: - - generic [ref=e143]: - - img [ref=e144] - - generic [ref=e149]: Scorchling - - generic [ref=e150]: 🔥 60/60 HP - - generic [ref=e154]: - - generic [ref=e155]: - - img [ref=e156] - - generic [ref=e161]: Flame Sprite - - generic [ref=e162]: 🔥 60/60 HP - - generic [ref=e166]: - - generic [ref=e167]: - - img [ref=e168] - - generic [ref=e173]: Emberling - - generic [ref=e174]: 🔥 60/60 HP - - generic [ref=e178]: - - generic [ref=e179]: - - img [ref=e180] - - generic [ref=e185]: Inferno Whelp - - generic [ref=e186]: 🔥 60/60 HP - - generic [ref=e189]: - - generic [ref=e191]: Floor Navigation - - generic [ref=e192]: - - generic [ref=e193]: - - button "Climb Up" [ref=e194]: - - img - - text: Climb Up - - button "Climb Down" [disabled]: - - img - - text: Climb Down - - generic [ref=e195]: Click Climb Up/Down to begin climbing - - generic [ref=e196]: - - generic [ref=e198]: Combat Stats - - generic [ref=e199]: - - generic [ref=e200]: "Total DPS: —" - - generic [ref=e201]: - - generic [ref=e202]: Active Spells - - generic [ref=e203]: - - generic [ref=e204]: - - generic [ref=e205]: - - text: Mana Bolt - - generic [ref=e206]: Basic - - generic [ref=e207]: ✓ - - generic [ref=e208]: ⚔️ 5 dmg • 3 raw • ⚡ 15 dmg/hr - - generic [ref=e210]: "Study Speed: 100%" - - generic [ref=e211]: - - generic [ref=e213]: Activity Log - - generic [ref=e219]: No activity yet... - - region "Notifications (F8)": - - list - - region "Notifications (F8)": - - list - - button "Open Next.js Dev Tools" [ref=e225] [cursor=pointer]: - - img [ref=e226] - - alert [ref=e229] -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | /** - 4 | * E2E tests for combat system: - 5 | * - Entering spire mode (climbing) - 6 | * - Casting spells and seeing progress - 7 | * - Enemy HP reduction - 8 | * - Floor advancement - 9 | */ - 10 | - 11 | test.describe('Combat System', () => { - 12 | test.beforeEach(async ({ page }) => { - 13 | await page.goto('/'); - 14 | // Clear game state to ensure a fresh start - 15 | await page.evaluate(() => { - 16 | Object.keys(localStorage) - 17 | .filter((k) => k.startsWith('mana-loop-')) - 18 | .forEach((k) => localStorage.removeItem(k)); - 19 | }); - 20 | await page.reload(); - 21 | await page.waitForLoadState('networkidle'); - 22 | }); - 23 | - 24 | test('can see the Spire tab and "Climb the Spire" button', async ({ page }) => { - 25 | // The Spire tab uses an icon + text, so match by the tab role - 26 | const spireTab = page.getByRole('tab', { name: /⚔️ Spire/ }); - 27 | await expect(spireTab).toBeVisible(); - 28 | - 29 | // Main page should show "Climb the Spire" button - 30 | const climbBtn = page.getByRole('button', { name: 'Climb the Spire' }); - 31 | await expect(climbBtn).toBeVisible(); - 32 | }); - 33 | - 34 | test('can enter Spire mode by clicking Climb button', async ({ page }) => { - 35 | // Click "Climb the Spire" button on the main page (via left panel) - 36 | await page.getByRole('button', { name: 'Climb the Spire' }).click(); - 37 | - 38 | // Should now see Spire mode UI elements - 39 | // The "Enter Spire Mode" button appears when on the Spire tab - 40 | const enterBtn = page.getByRole('button', { name: 'Enter Spire Mode' }); - 41 | await expect(enterBtn).toBeVisible({ timeout: 5000 }); - 42 | }); - 43 | - 44 | test('can navigate to Spire tab', async ({ page }) => { - 45 | // Click the Spire tab specifically (using role=tab to disambiguate) - 46 | await page.getByRole('tab', { name: /⚔️ Spire/ }).click(); - 47 | - 48 | // Should see Spire-specific UI - 49 | const enterSpireBtn = page.getByRole('button', { name: 'Enter Spire Mode' }); - 50 | await expect(enterSpireBtn).toBeVisible({ timeout: 5000 }); - 51 | }); - 52 | - 53 | test('can enter spire mode from the Spire tab', async ({ page }) => { - 54 | await page.getByRole('tab', { name: /⚔️ Spire/ }).click(); - 55 | - 56 | const enterBtn = page.getByRole('button', { name: 'Enter Spire Mode' }); - 57 | await expect(enterBtn).toBeEnabled(); - 58 | await enterBtn.click(); - 59 | - 60 | // After entering, should see exit button - 61 | const exitBtn = page.getByRole('button', { name: 'Exit Spire Mode' }); - 62 | await expect(exitBtn).toBeVisible({ timeout: 5000 }); - 63 | }); - 64 | - 65 | test('shows floor information in spire mode', async ({ page }) => { - 66 | await page.getByRole('tab', { name: /⚔️ Spire/ }).click(); - 67 | await page.getByRole('button', { name: 'Enter Spire Mode' }).click(); - 68 | - 69 | // Should display floor number - look for "Floor" label or the floor counter - 70 | const floorDisplay = page.locator('text="Floor"').first(); -> 71 | await expect(floorDisplay).toBeVisible({ timeout: 5000 }); - | ^ Error: expect(locator).toBeVisible() failed - 72 | }); - 73 | }); -``` \ No newline at end of file diff --git a/playwright-report/data/25af666b2659e25b596f1eb58ca5629f38f0fa74.png b/playwright-report/data/25af666b2659e25b596f1eb58ca5629f38f0fa74.png deleted file mode 100644 index c743d2e..0000000 Binary files a/playwright-report/data/25af666b2659e25b596f1eb58ca5629f38f0fa74.png and /dev/null differ diff --git a/playwright-report/data/294ed85dfd5fbd79486f5274129a1d8b83cfa676.png b/playwright-report/data/294ed85dfd5fbd79486f5274129a1d8b83cfa676.png deleted file mode 100644 index 9e289d4..0000000 Binary files a/playwright-report/data/294ed85dfd5fbd79486f5274129a1d8b83cfa676.png and /dev/null differ diff --git a/playwright-report/data/37c584c77b029af648d58a063f9724538662c6d0.webm b/playwright-report/data/37c584c77b029af648d58a063f9724538662c6d0.webm deleted file mode 100644 index 456fe1c..0000000 Binary files a/playwright-report/data/37c584c77b029af648d58a063f9724538662c6d0.webm and /dev/null differ diff --git a/playwright-report/data/4d1229974e5326e2351c32921095bff6e989005e.png b/playwright-report/data/4d1229974e5326e2351c32921095bff6e989005e.png deleted file mode 100644 index 5bc4454..0000000 Binary files a/playwright-report/data/4d1229974e5326e2351c32921095bff6e989005e.png and /dev/null differ diff --git a/playwright-report/data/4f22caa1a2b454f813b4c68c510a2ef0b340a248.md b/playwright-report/data/4f22caa1a2b454f813b4c68c510a2ef0b340a248.md deleted file mode 100644 index d252514..0000000 --- a/playwright-report/data/4f22caa1a2b454f813b4c68c510a2ef0b340a248.md +++ /dev/null @@ -1,348 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: equipment.spec.ts >> Equipment Management >> can unequip an item from a slot -- Location: e2e/equipment.spec.ts:113:7 - -# Error details - -``` -Error: expect(locator).toBeVisible() failed - -Locator: locator('text=Hands').locator('..').locator('button').first() -Expected: visible -Timeout: 5000ms -Error: element(s) not found - -Call log: - - Expect "toBeVisible" with timeout 5000ms - - waiting for locator('text=Hands').locator('..').locator('button').first() - -``` - -# Page snapshot - -```yaml -- generic [ref=e1]: - - generic [ref=e2]: - - banner [ref=e3]: - - generic [ref=e4]: - - heading "MANA LOOP" [level=1] [ref=e5] - - generic [ref=e7]: - - generic [ref=e8]: - - generic [ref=e9]: Day 1 - - generic [ref=e10]: 01:55 - - generic [ref=e11]: - - generic [ref=e12]: "0" - - generic [ref=e13]: Insight - - main [ref=e14]: - - generic [ref=e15]: - - generic [ref=e17]: - - generic [ref=e18]: - - generic [ref=e19]: - - generic [ref=e20]: "14" - - generic [ref=e21]: / 100 - - generic [ref=e22]: - - text: +2.8 mana/hr - - generic [ref=e23]: (1.4x med) - - progressbar [ref=e24] - - button "Gather +1 Mana" [ref=e26]: - - img - - text: Gather +1 Mana - - generic [ref=e27]: - - button "Elemental Mana (1)" [ref=e28]: - - generic [ref=e29]: Elemental Mana (1) - - img [ref=e30] - - generic [ref=e33]: - - generic [ref=e34]: - - generic [ref=e35]: 🔗 - - generic [ref=e36]: Transference - - generic [ref=e39]: 0/10 - - button "Climb the Spire" [ref=e40]: - - img - - text: Climb the Spire - - generic [ref=e42]: - - generic [ref=e43]: - - img [ref=e44] - - generic [ref=e46]: Current Activity - - generic [ref=e47]: Meditating - - generic [ref=e48]: - - generic [ref=e49]: "1" - - generic [ref=e50]: "2" - - generic [ref=e51]: "3" - - generic [ref=e52]: "4" - - generic [ref=e53]: "5" - - generic [ref=e54]: "6" - - generic [ref=e55]: "7" - - generic [ref=e56]: "8" - - generic [ref=e57]: "9" - - generic [ref=e58]: "10" - - generic [ref=e59]: "11" - - generic [ref=e60]: "12" - - generic [ref=e61]: "13" - - generic [ref=e62]: "14" - - generic [ref=e63]: "15" - - generic [ref=e64]: "16" - - generic [ref=e65]: "17" - - generic [ref=e66]: "18" - - generic [ref=e67]: "19" - - generic [ref=e68]: "20" - - generic [ref=e69]: "21" - - generic [ref=e70]: "22" - - generic [ref=e71]: "23" - - generic [ref=e72]: "24" - - generic [ref=e73]: "25" - - generic [ref=e74]: "26" - - generic [ref=e75]: "27" - - generic [ref=e76]: "28" - - generic [ref=e77]: "29" - - generic [ref=e78]: "30" - - generic [ref=e80]: - - tablist [ref=e81]: - - tab "⚔️ Spire" [ref=e82] - - tab "✨ Attune" [ref=e83] - - tab "🗿 Golems" [ref=e84] - - tab "📚 Skills" [ref=e85] - - tab "🔮 Spells" [ref=e86] - - tab "🛡️ Gear" [active] [selected] [ref=e87] - - tab "🔧 Craft" [ref=e88] - - tab "💎 Loot" [ref=e89] - - tab "🏆 Achieve" [ref=e90] - - tab "📊 Stats" [ref=e91] - - tab "🐛 Debug" [ref=e92] - - tab "📖 Grimoire" [ref=e93] - - tabpanel "🛡️ Gear" [ref=e94]: - - generic [ref=e95]: - - generic [ref=e96]: - - generic [ref=e97]: - - heading "Equipped Gear" [level=3] [ref=e98] - - generic [ref=e100]: 4 / 8 slots filled - - generic [ref=e101]: - - generic [ref=e102]: - - heading "Weapon & Shield" [level=4] [ref=e103] - - generic [ref=e104]: - - 'button "Main Hand slot: Basic Staff" [ref=e106]': - - generic [ref=e107]: - - generic [ref=e108]: - - img [ref=e109] - - generic [ref=e114]: Main Hand - - button "Unequip Basic Staff" [ref=e115]: - - img [ref=e116] - - generic [ref=e119]: - - generic [ref=e120]: - - text: Basic Staff - - generic [ref=e121]: 2-Handed - - generic [ref=e122]: "Enchantments: 1/50" - - generic [ref=e124]: Mana Bolt - - button "Off Hand slot (blocked by 2-handed weapon) (empty)" [ref=e125]: - - generic [ref=e127]: - - img [ref=e128] - - generic [ref=e130]: Off Hand - - generic [ref=e131]: - - img - - text: Occupied — 2H Weapon - - generic [ref=e132]: - - img [ref=e133] - - text: Blocked by 2-handed weapon - - generic [ref=e135]: - - heading "Armor" [level=4] [ref=e136] - - generic [ref=e137]: - - button "Head slot (empty)" [ref=e139]: - - generic [ref=e141]: - - img [ref=e142] - - generic [ref=e147]: Head - - generic [ref=e148]: Head - - 'button "Body slot: Civilian Shirt" [ref=e150]': - - generic [ref=e151]: - - generic [ref=e152]: - - img [ref=e153] - - generic [ref=e155]: Body - - button "Unequip Civilian Shirt" [ref=e156]: - - img [ref=e157] - - generic [ref=e160]: - - generic [ref=e161]: Civilian Shirt - - generic [ref=e162]: "Enchantments: 0/30" - - 'button "Hands slot: Civilian Gloves" [ref=e164]': - - generic [ref=e165]: - - generic [ref=e166]: - - img [ref=e167] - - generic [ref=e172]: Hands - - button "Unequip Civilian Gloves" [ref=e173]: - - img [ref=e174] - - generic [ref=e177]: - - generic [ref=e178]: Civilian Gloves - - generic [ref=e179]: "Enchantments: 0/20" - - 'button "Feet slot: Civilian Shoes" [ref=e181]': - - generic [ref=e182]: - - generic [ref=e183]: - - img [ref=e184] - - generic [ref=e187]: Feet - - button "Unequip Civilian Shoes" [ref=e188]: - - img [ref=e189] - - generic [ref=e192]: - - generic [ref=e193]: Civilian Shoes - - generic [ref=e194]: "Enchantments: 0/15" - - generic [ref=e195]: - - heading "Accessories" [level=4] [ref=e196] - - generic [ref=e197]: - - button "Accessory 1 slot (empty)" [ref=e199]: - - generic [ref=e201]: - - img [ref=e202] - - generic [ref=e205]: Accessory 1 - - generic [ref=e206]: Accessory 1 - - button "Accessory 2 slot (empty)" [ref=e208]: - - generic [ref=e210]: - - img [ref=e211] - - generic [ref=e214]: Accessory 2 - - generic [ref=e215]: Accessory 2 - - generic [ref=e216]: - - heading "Equipment Inventory (0 items)" [level=3] [ref=e218] - - status [ref=e219]: No unequipped items. Craft new gear in the Crafting tab. - - generic [ref=e220]: - - heading "Equipment Stats Summary" [level=3] [ref=e222] - - generic [ref=e223]: - - generic [ref=e224]: - - generic [ref=e225]: "4" - - generic [ref=e226]: Total Items - - generic [ref=e227]: - - generic [ref=e228]: "4" - - generic [ref=e229]: Equipped - - generic [ref=e230]: - - generic [ref=e231]: "0" - - generic [ref=e232]: In Inventory - - generic [ref=e233]: - - generic [ref=e234]: "1" - - generic [ref=e235]: Total Enchantments - - generic [ref=e236]: - - heading "✨ Enchantment Power" [level=3] [ref=e238] - - generic [ref=e239]: - - generic [ref=e240]: - - generic [ref=e241]: "Enchantment Power:" - - generic [ref=e242]: 1.00× - - paragraph [ref=e243]: Increases the power of all enchantments by 0%. Multiplier applied to all enchantment effects. - - generic [ref=e244]: - - generic [ref=e245]: "Active Effects from Equipment:" - - generic [ref=e247]: No active effects - - region "Notifications (F8)": - - list - - region "Notifications (F8)": - - list - - button "Open Next.js Dev Tools" [ref=e253] [cursor=pointer]: - - img [ref=e254] - - alert [ref=e257] -``` - -# Test source - -```ts - 28 | - 29 | // Verify equipment UI elements - 30 | const equippedGearHeading = page.locator('text="Equipped Gear"'); - 31 | await expect(equippedGearHeading).toBeVisible({ timeout: 5000 }); - 32 | }); - 33 | - 34 | test('shows equipment slots with labels', async ({ page }) => { - 35 | await page.goto('/'); - 36 | await page.evaluate(() => { - 37 | Object.keys(localStorage) - 38 | .filter((k) => k.startsWith('mana-loop-')) - 39 | .forEach((k) => localStorage.removeItem(k)); - 40 | }); - 41 | await page.reload(); - 42 | await page.waitForLoadState('networkidle'); - 43 | - 44 | await page.getByRole('tab', { name: /🛡️ Gear/ }).click(); - 45 | - 46 | // Check for expected slot labels - use role=heading or more specific selectors - 47 | // Main Hand slot - 48 | const mainHandSection = page.locator('text=Main Hand'); - 49 | await expect(mainHandSection.first()).toBeVisible(); - 50 | - 51 | // Off Hand - 52 | const offHandSection = page.locator('text=Off Hand'); - 53 | await expect(offHandSection.first()).toBeVisible(); - 54 | - 55 | // Head - 56 | const headSection = page.locator('text=Head'); - 57 | await expect(headSection.first()).toBeVisible(); - 58 | - 59 | // Body - 60 | const bodySection = page.locator('text=Body'); - 61 | await expect(bodySection.first()).toBeVisible(); - 62 | - 63 | // Hands - 64 | const handsSection = page.locator('text=Hands'); - 65 | await expect(handsSection.first()).toBeVisible(); - 66 | - 67 | // Feet - 68 | const feetSection = page.locator('text=Feet'); - 69 | await expect(feetSection.first()).toBeVisible(); - 70 | - 71 | // Accessory 1 and 2 - 72 | const acc1Section = page.locator('text=Accessory 1'); - 73 | await expect(acc1Section.first()).toBeVisible(); - 74 | const acc2Section = page.locator('text=Accessory 2'); - 75 | await expect(acc2Section.first()).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows starting equipment already equipped', async ({ page }) => { - 79 | await page.goto('/'); - 80 | await page.evaluate(() => { - 81 | Object.keys(localStorage) - 82 | .filter((k) => k.startsWith('mana-loop-')) - 83 | .forEach((k) => localStorage.removeItem(k)); - 84 | }); - 85 | await page.reload(); - 86 | await page.waitForLoadState('networkidle'); - 87 | - 88 | await page.getByRole('tab', { name: /🛡️ Gear/ }).click(); - 89 | - 90 | // The player starts with a Basic Staff in main hand (as an equipped item) - 91 | const mainHandSlot = page.locator('text=Main Hand >> .. >> text=Basic Staff'); - 92 | await expect(mainHandSlot).toBeVisible({ timeout: 5000 }); - 93 | }); - 94 | - 95 | test('2-handed weapon blocks offhand slot', async ({ page }) => { - 96 | await page.goto('/'); - 97 | await page.evaluate(() => { - 98 | Object.keys(localStorage) - 99 | .filter((k) => k.startsWith('mana-loop-')) - 100 | .forEach((k) => localStorage.removeItem(k)); - 101 | }); - 102 | await page.reload(); - 103 | await page.waitForLoadState('networkidle'); - 104 | - 105 | await page.getByRole('tab', { name: /🛡️ Gear/ }).click(); - 106 | - 107 | // The starting basic staff is 2-handed - 108 | // The offhand slot should show as blocked with "Occupied — 2H Weapon" - 109 | const offHandBlocked = page.locator('text=Occupied').first(); - 110 | await expect(offHandBlocked).toBeVisible({ timeout: 5000 }); - 111 | }); - 112 | - 113 | test('can unequip an item from a slot', async ({ page }) => { - 114 | await page.goto('/'); - 115 | await page.evaluate(() => { - 116 | Object.keys(localStorage) - 117 | .filter((k) => k.startsWith('mana-loop-')) - 118 | .forEach((k) => localStorage.removeItem(k)); - 119 | }); - 120 | await page.reload(); - 121 | await page.waitForLoadState('networkidle'); - 122 | - 123 | await page.getByRole('tab', { name: /🛡️ Gear/ }).click(); - 124 | - 125 | // Find an equiped slot with an unequip button (the X button) - 126 | // The hands slot has civilian gloves equipped - 127 | const handsSlot = page.locator('text=Hands >> .. >> button').first(); -> 128 | await expect(handsSlot).toBeVisible({ timeout: 5000 }); - | ^ Error: expect(locator).toBeVisible() failed - 129 | // Note: exact behavior of unequip depends on implementation state - 130 | }); - 131 | }); -``` \ No newline at end of file diff --git a/playwright-report/data/6408809a17a0a92b06e5cc75fcee95e9778138c4.md b/playwright-report/data/6408809a17a0a92b06e5cc75fcee95e9778138c4.md deleted file mode 100644 index f08917f..0000000 --- a/playwright-report/data/6408809a17a0a92b06e5cc75fcee95e9778138c4.md +++ /dev/null @@ -1,285 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: enchanting.spec.ts >> Enchanting Flow >> can navigate to Crafting tab -- Location: e2e/enchanting.spec.ts:28:7 - -# Error details - -``` -Error: expect(locator).toBeVisible() failed - -Locator: getByRole('button') -Expected: visible -Error: strict mode violation: getByRole('button') resolved to 6 elements: - 1) aka getByRole('button', { name: 'Elemental Mana (1)' }) - 3) aka getByRole('button', { name: 'Open Next.js Dev Tools' }) - -Call log: - - Expect "toBeVisible" with timeout 5000ms - - waiting for getByRole('button') - -``` - -# Page snapshot - -```yaml -- generic [ref=e1]: - - generic [ref=e2]: - - banner [ref=e3]: - - generic [ref=e4]: - - heading "MANA LOOP" [level=1] [ref=e5] - - generic [ref=e7]: - - generic [ref=e8]: - - generic [ref=e9]: Day 1 - - generic [ref=e10]: 00:55 - - generic [ref=e11]: - - generic [ref=e12]: "0" - - generic [ref=e13]: Insight - - main [ref=e14]: - - generic [ref=e15]: - - generic [ref=e17]: - - generic [ref=e18]: - - generic [ref=e19]: - - generic [ref=e20]: "11" - - generic [ref=e21]: / 100 - - generic [ref=e22]: - - text: +2.4 mana/hr - - generic [ref=e23]: (1.2x med) - - progressbar [ref=e24] - - button "Gather +1 Mana" [ref=e26]: - - img - - text: Gather +1 Mana - - generic [ref=e27]: - - button "Elemental Mana (1)" [ref=e28]: - - generic [ref=e29]: Elemental Mana (1) - - img [ref=e30] - - generic [ref=e33]: - - generic [ref=e34]: - - generic [ref=e35]: 🔗 - - generic [ref=e36]: Transference - - generic [ref=e39]: 0/10 - - button "Climb the Spire" [ref=e40]: - - img - - text: Climb the Spire - - generic [ref=e42]: - - generic [ref=e43]: - - img [ref=e44] - - generic [ref=e46]: Current Activity - - generic [ref=e47]: Meditating - - generic [ref=e48]: - - generic [ref=e49]: "1" - - generic [ref=e50]: "2" - - generic [ref=e51]: "3" - - generic [ref=e52]: "4" - - generic [ref=e53]: "5" - - generic [ref=e54]: "6" - - generic [ref=e55]: "7" - - generic [ref=e56]: "8" - - generic [ref=e57]: "9" - - generic [ref=e58]: "10" - - generic [ref=e59]: "11" - - generic [ref=e60]: "12" - - generic [ref=e61]: "13" - - generic [ref=e62]: "14" - - generic [ref=e63]: "15" - - generic [ref=e64]: "16" - - generic [ref=e65]: "17" - - generic [ref=e66]: "18" - - generic [ref=e67]: "19" - - generic [ref=e68]: "20" - - generic [ref=e69]: "21" - - generic [ref=e70]: "22" - - generic [ref=e71]: "23" - - generic [ref=e72]: "24" - - generic [ref=e73]: "25" - - generic [ref=e74]: "26" - - generic [ref=e75]: "27" - - generic [ref=e76]: "28" - - generic [ref=e77]: "29" - - generic [ref=e78]: "30" - - generic [ref=e80]: - - tablist [ref=e81]: - - tab "⚔️ Spire" [ref=e82] - - tab "✨ Attune" [ref=e83] - - tab "🗿 Golems" [ref=e84] - - tab "📚 Skills" [ref=e85] - - tab "🔮 Spells" [ref=e86] - - tab "🛡️ Gear" [ref=e87] - - tab "🔧 Craft" [active] [selected] [ref=e88] - - tab "💎 Loot" [ref=e89] - - tab "🏆 Achieve" [ref=e90] - - tab "📊 Stats" [ref=e91] - - tab "🐛 Debug" [ref=e92] - - tab "📖 Grimoire" [ref=e93] - - tabpanel "🔧 Craft" [ref=e94]: - - generic [ref=e95]: - - generic [ref=e97]: - - button "Fabricate" [ref=e98]: - - img - - text: Fabricate - - button "Enchant" [ref=e99]: - - img - - text: Enchant - - generic [ref=e100]: - - generic [ref=e101]: - - generic [ref=e103]: - - img [ref=e104] - - text: Available Blueprints - - generic [ref=e113]: - - img [ref=e114] - - paragraph [ref=e118]: No blueprints discovered yet. - - paragraph [ref=e119]: Defeat guardians to find blueprints! - - generic [ref=e120]: - - generic [ref=e122]: - - img [ref=e123] - - text: Materials (0) - - generic [ref=e131]: - - img [ref=e132] - - paragraph [ref=e134]: No materials collected yet. - - paragraph [ref=e135]: Defeat floors to gather materials! - - region "Notifications (F8)": - - list - - region "Notifications (F8)": - - list - - button "Open Next.js Dev Tools" [ref=e141] [cursor=pointer]: - - img [ref=e142] - - alert [ref=e145] -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | /** - 4 | * E2E tests for the 3-step enchantment flow: - 5 | * Design → Prepare → Apply - 6 | * - 7 | * These tests validate the core crafting loop works end-to-end. - 8 | */ - 9 | - 10 | test.describe('Enchanting Flow', () => { - 11 | /** - 12 | * Before each test, ensure we start with a clean state. - 13 | * The game persists state in localStorage, so we clear it. - 14 | */ - 15 | test.beforeEach(async ({ page }) => { - 16 | await page.goto('/'); - 17 | // Clear game state to ensure a fresh start - 18 | await page.evaluate(() => { - 19 | Object.keys(localStorage) - 20 | .filter((k) => k.startsWith('mana-loop-')) - 21 | .forEach((k) => localStorage.removeItem(k)); - 22 | }); - 23 | await page.reload(); - 24 | // Wait for the game to initialize - 25 | await page.waitForLoadState('networkidle'); - 26 | }); - 27 | - 28 | test('can navigate to Crafting tab', async ({ page }) => { - 29 | // The tab bar contains a "Craft" tab - 30 | const craftTab = page.getByRole('tab', { name: /🔧 Craft/ }); - 31 | await expect(craftTab).toBeVisible(); - 32 | await craftTab.click(); - 33 | - 34 | // Verify we're on the crafting tab by checking for sub-tabs - 35 | const fabricateBtn = page.getByRole('button', { hasText: 'Fabricate' }); - 36 | const enchantBtn = page.getByRole('button', { hasText: 'Enchant' }); -> 37 | await expect(fabricateBtn).toBeVisible(); - | ^ Error: expect(locator).toBeVisible() failed - 38 | await expect(enchantBtn).toBeVisible(); - 39 | }); - 40 | - 41 | test('can switch to Enchant sub-tab and see design UI', async ({ page }) => { - 42 | await page.goto('/'); - 43 | await page.evaluate(() => { - 44 | Object.keys(localStorage) - 45 | .filter((k) => k.startsWith('mana-loop-')) - 46 | .forEach((k) => localStorage.removeItem(k)); - 47 | }); - 48 | await page.reload(); - 49 | await page.waitForLoadState('networkidle'); - 50 | - 51 | // Navigate to Crafting tab - 52 | await page.getByRole('tab', { name: /🔧 Craft/ }).click(); - 53 | - 54 | // Click Enchant sub-tab - 55 | const enchantBtn = page.getByRole('button', { hasText: 'Enchant' }); - 56 | await enchantBtn.click(); - 57 | - 58 | // Should see the design stage UI - 59 | const designBtn = page.getByRole('button', { hasText: 'Design' }); - 60 | const prepareBtn = page.getByRole('button', { hasText: 'Prepare' }); - 61 | const applyBtn = page.getByRole('button', { hasText: 'Apply' }); - 62 | await expect(designBtn).toBeVisible(); - 63 | await expect(prepareBtn).toBeVisible(); - 64 | await expect(applyBtn).toBeVisible(); - 65 | }); - 66 | - 67 | test('can select equipment type and effect in Design stage', async ({ page }) => { - 68 | await page.goto('/'); - 69 | await page.evaluate(() => { - 70 | Object.keys(localStorage) - 71 | .filter((k) => k.startsWith('mana-loop-')) - 72 | .forEach((k) => localStorage.removeItem(k)); - 73 | }); - 74 | await page.reload(); - 75 | await page.waitForLoadState('networkidle'); - 76 | - 77 | // Navigate to Crafting > Enchant > Design - 78 | await page.getByRole('tab', { name: /🔧 Craft/ }).click(); - 79 | await page.getByRole('button', { hasText: 'Enchant' }).click(); - 80 | - 81 | // The design section should show effect selectors once an equipment type is chosen - 82 | // Look for any element matching equipment type buttons and effect-related content - 83 | const equipmentButtons = page.locator('button:has-text("Basic Staff"), button:has-text("Apprentice Wand"), button:has-text("Oak Staff"), button:has-text("Crystal Wand")'); - 84 | const count = await equipmentButtons.count(); - 85 | expect(count).toBeGreaterThan(0); - 86 | }); - 87 | - 88 | test('can navigate through all 3 enchant stages', async ({ page }) => { - 89 | await page.goto('/'); - 90 | await page.evaluate(() => { - 91 | Object.keys(localStorage) - 92 | .filter((k) => k.startsWith('mana-loop-')) - 93 | .forEach((k) => localStorage.removeItem(k)); - 94 | }); - 95 | await page.reload(); - 96 | await page.waitForLoadState('networkidle'); - 97 | - 98 | // Navigate to Crafting > Enchant - 99 | await page.getByRole('tab', { name: /🔧 Craft/ }).click(); - 100 | await page.getByRole('button', { hasText: 'Enchant' }).click(); - 101 | - 102 | // Verify Design stage is active - 103 | const designBtn = page.getByRole('button', { hasText: 'Design' }); - 104 | await expect(designBtn).toBeVisible(); - 105 | - 106 | // Switch to Prepare stage - 107 | const prepareBtn = page.getByRole('button', { hasText: 'Prepare' }); - 108 | await prepareBtn.click(); - 109 | - 110 | // Should see preparation UI - 111 | const prepareHeading = page.locator('text=Select Equipment to Prepare'); - 112 | await expect(prepareHeading).toBeVisible({ timeout: 5000 }); - 113 | - 114 | // Switch to Apply stage - 115 | const applyBtn = page.getByRole('button', { hasText: 'Apply' }); - 116 | await applyBtn.click(); - 117 | - 118 | // Should see application UI - 119 | const applyHeading = page.locator('text=Select Equipment & Design'); - 120 | await expect(applyHeading).toBeVisible({ timeout: 5000 }); - 121 | }); - 122 | }); -``` \ No newline at end of file diff --git a/playwright-report/data/66a1f85e1e6a655dfb90f10bd1a60887cffa87da.md b/playwright-report/data/66a1f85e1e6a655dfb90f10bd1a60887cffa87da.md deleted file mode 100644 index 500ff88..0000000 --- a/playwright-report/data/66a1f85e1e6a655dfb90f10bd1a60887cffa87da.md +++ /dev/null @@ -1,260 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: combat.spec.ts >> Combat System >> can enter Spire mode by clicking Climb button -- Location: e2e/combat.spec.ts:34:7 - -# Error details - -``` -Error: expect(locator).toBeVisible() failed - -Locator: getByRole('button', { name: 'Enter Spire Mode' }) -Expected: visible -Timeout: 5000ms -Error: element(s) not found - -Call log: - - Expect "toBeVisible" with timeout 5000ms - - waiting for getByRole('button', { name: 'Enter Spire Mode' }) - -``` - -# Page snapshot - -```yaml -- generic [active] [ref=e1]: - - generic [ref=e2]: - - banner [ref=e3]: - - generic [ref=e4]: - - heading "MANA LOOP" [level=1] [ref=e5] - - generic [ref=e7]: - - generic [ref=e8]: - - generic [ref=e9]: Day 1 - - generic [ref=e10]: 01:43 - - generic [ref=e11]: - - generic [ref=e12]: "0" - - generic [ref=e13]: Insight - - main [ref=e14]: - - generic [ref=e15]: - - generic [ref=e17]: - - generic [ref=e18]: - - generic [ref=e19]: - - generic [ref=e20]: "14" - - generic [ref=e21]: / 100 - - generic [ref=e22]: - - text: +2.9 mana/hr - - generic [ref=e23]: (1.4x med) - - progressbar [ref=e24] - - button "Gather +1 Mana" [ref=e26]: - - img - - text: Gather +1 Mana - - generic [ref=e27]: - - button "Elemental Mana (1)" [ref=e28]: - - generic [ref=e29]: Elemental Mana (1) - - img [ref=e30] - - generic [ref=e33]: - - generic [ref=e34]: - - generic [ref=e35]: 🔗 - - generic [ref=e36]: Transference - - generic [ref=e39]: 0/10 - - generic [ref=e40]: - - generic [ref=e41]: "1" - - generic [ref=e42]: "2" - - generic [ref=e43]: "3" - - generic [ref=e44]: "4" - - generic [ref=e45]: "5" - - generic [ref=e46]: "6" - - generic [ref=e47]: "7" - - generic [ref=e48]: "8" - - generic [ref=e49]: "9" - - generic [ref=e50]: "10" - - generic [ref=e51]: "11" - - generic [ref=e52]: "12" - - generic [ref=e53]: "13" - - generic [ref=e54]: "14" - - generic [ref=e55]: "15" - - generic [ref=e56]: "16" - - generic [ref=e57]: "17" - - generic [ref=e58]: "18" - - generic [ref=e59]: "19" - - generic [ref=e60]: "20" - - generic [ref=e61]: "21" - - generic [ref=e62]: "22" - - generic [ref=e63]: "23" - - generic [ref=e64]: "24" - - generic [ref=e65]: "25" - - generic [ref=e66]: "26" - - generic [ref=e67]: "27" - - generic [ref=e68]: "28" - - generic [ref=e69]: "29" - - generic [ref=e70]: "30" - - generic [ref=e72]: - - tablist [ref=e73]: - - tab "⚔️ Spire" [selected] [ref=e74] - - tab "✨ Attune" [ref=e75] - - tab "🗿 Golems" [ref=e76] - - tab "📚 Skills" [ref=e77] - - tab "🔮 Spells" [ref=e78] - - tab "🛡️ Gear" [ref=e79] - - tab "🔧 Craft" [ref=e80] - - tab "💎 Loot" [ref=e81] - - tab "🏆 Achieve" [ref=e82] - - tab "📊 Stats" [ref=e83] - - tab "🐛 Debug" [ref=e84] - - tab "📖 Grimoire" [ref=e85] - - tabpanel "⚔️ Spire" [ref=e86]: - - generic [ref=e87]: - - generic [ref=e89]: - - button "Exit Spire Mode" [ref=e90]: - - img - - text: Exit Spire Mode - - generic [ref=e91]: Climb down to floor 1 to return to the main game - - generic [ref=e92]: - - heading "Current Floor ⚔️ Combat" [level=3] [ref=e94]: - - generic [ref=e95]: Current Floor - - generic [ref=e96]: ⚔️ Combat - - generic [ref=e97]: - - generic [ref=e98]: - - generic [ref=e99]: "1" - - generic [ref=e100]: / 100 - - generic [ref=e101]: 🔥 Fire - - generic [ref=e102]: - - text: "Best: Floor" - - strong [ref=e103]: "1" - - text: "• Pacts:" - - strong [ref=e104]: "0" - - generic [ref=e106]: - - generic [ref=e108]: Active Spells (1) - - generic [ref=e110]: - - generic [ref=e111]: - - generic [ref=e112]: Mana BoltBasic - - generic [ref=e113]: ✓ - - generic [ref=e114]: ⚔️ 5 dmg • 3 raw • ⚡ 15 dmg/hr - - generic [ref=e115]: - - generic [ref=e116]: - - generic [ref=e117]: - - img [ref=e118] - - generic [ref=e123]: Inferno Whelp - - generic [ref=e124]: 🔥 Fire - - generic [ref=e129]: 151 / 151 HP - - generic [ref=e130]: - - generic [ref=e132]: Floor Navigation - - generic [ref=e133]: - - generic [ref=e134]: - - button "Climb Up" [ref=e135]: - - img - - text: Climb Up - - button "Climb Down" [disabled]: - - img - - text: Climb Down - - generic [ref=e136]: Click Climb Up/Down to begin climbing - - generic [ref=e137]: - - generic [ref=e139]: Combat Stats - - generic [ref=e140]: - - generic [ref=e141]: "Total DPS: —" - - generic [ref=e142]: - - generic [ref=e143]: Active Spells - - generic [ref=e144]: - - generic [ref=e145]: - - generic [ref=e146]: - - text: Mana Bolt - - generic [ref=e147]: Basic - - generic [ref=e148]: ✓ - - generic [ref=e149]: ⚔️ 5 dmg • 3 raw • ⚡ 15 dmg/hr - - generic [ref=e151]: "Study Speed: 100%" - - generic [ref=e152]: - - generic [ref=e154]: Activity Log - - generic [ref=e160]: No activity yet... - - region "Notifications (F8)": - - list - - region "Notifications (F8)": - - list - - button "Open Next.js Dev Tools" [ref=e166] [cursor=pointer]: - - img [ref=e167] - - alert [ref=e170] -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | /** - 4 | * E2E tests for combat system: - 5 | * - Entering spire mode (climbing) - 6 | * - Casting spells and seeing progress - 7 | * - Enemy HP reduction - 8 | * - Floor advancement - 9 | */ - 10 | - 11 | test.describe('Combat System', () => { - 12 | test.beforeEach(async ({ page }) => { - 13 | await page.goto('/'); - 14 | // Clear game state to ensure a fresh start - 15 | await page.evaluate(() => { - 16 | Object.keys(localStorage) - 17 | .filter((k) => k.startsWith('mana-loop-')) - 18 | .forEach((k) => localStorage.removeItem(k)); - 19 | }); - 20 | await page.reload(); - 21 | await page.waitForLoadState('networkidle'); - 22 | }); - 23 | - 24 | test('can see the Spire tab and "Climb the Spire" button', async ({ page }) => { - 25 | // The Spire tab uses an icon + text, so match by the tab role - 26 | const spireTab = page.getByRole('tab', { name: /⚔️ Spire/ }); - 27 | await expect(spireTab).toBeVisible(); - 28 | - 29 | // Main page should show "Climb the Spire" button - 30 | const climbBtn = page.getByRole('button', { name: 'Climb the Spire' }); - 31 | await expect(climbBtn).toBeVisible(); - 32 | }); - 33 | - 34 | test('can enter Spire mode by clicking Climb button', async ({ page }) => { - 35 | // Click "Climb the Spire" button on the main page (via left panel) - 36 | await page.getByRole('button', { name: 'Climb the Spire' }).click(); - 37 | - 38 | // Should now see Spire mode UI elements - 39 | // The "Enter Spire Mode" button appears when on the Spire tab - 40 | const enterBtn = page.getByRole('button', { name: 'Enter Spire Mode' }); -> 41 | await expect(enterBtn).toBeVisible({ timeout: 5000 }); - | ^ Error: expect(locator).toBeVisible() failed - 42 | }); - 43 | - 44 | test('can navigate to Spire tab', async ({ page }) => { - 45 | // Click the Spire tab specifically (using role=tab to disambiguate) - 46 | await page.getByRole('tab', { name: /⚔️ Spire/ }).click(); - 47 | - 48 | // Should see Spire-specific UI - 49 | const enterSpireBtn = page.getByRole('button', { name: 'Enter Spire Mode' }); - 50 | await expect(enterSpireBtn).toBeVisible({ timeout: 5000 }); - 51 | }); - 52 | - 53 | test('can enter spire mode from the Spire tab', async ({ page }) => { - 54 | await page.getByRole('tab', { name: /⚔️ Spire/ }).click(); - 55 | - 56 | const enterBtn = page.getByRole('button', { name: 'Enter Spire Mode' }); - 57 | await expect(enterBtn).toBeEnabled(); - 58 | await enterBtn.click(); - 59 | - 60 | // After entering, should see exit button - 61 | const exitBtn = page.getByRole('button', { name: 'Exit Spire Mode' }); - 62 | await expect(exitBtn).toBeVisible({ timeout: 5000 }); - 63 | }); - 64 | - 65 | test('shows floor information in spire mode', async ({ page }) => { - 66 | await page.getByRole('tab', { name: /⚔️ Spire/ }).click(); - 67 | await page.getByRole('button', { name: 'Enter Spire Mode' }).click(); - 68 | - 69 | // Should display floor number - look for "Floor" label or the floor counter - 70 | const floorDisplay = page.locator('text="Floor"').first(); - 71 | await expect(floorDisplay).toBeVisible({ timeout: 5000 }); - 72 | }); - 73 | }); -``` \ No newline at end of file diff --git a/playwright-report/data/6b97a6c84cfda4c717249f240d0a80e1b195498a.png b/playwright-report/data/6b97a6c84cfda4c717249f240d0a80e1b195498a.png deleted file mode 100644 index dbbd62a..0000000 Binary files a/playwright-report/data/6b97a6c84cfda4c717249f240d0a80e1b195498a.png and /dev/null differ diff --git a/playwright-report/data/6c1c7d873c0c5262ffca286974649ec3bf1eb3f4.md b/playwright-report/data/6c1c7d873c0c5262ffca286974649ec3bf1eb3f4.md deleted file mode 100644 index 3eaf7a4..0000000 --- a/playwright-report/data/6c1c7d873c0c5262ffca286974649ec3bf1eb3f4.md +++ /dev/null @@ -1,280 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: enchanting.spec.ts >> Enchanting Flow >> can switch to Enchant sub-tab and see design UI -- Location: e2e/enchanting.spec.ts:41:7 - -# Error details - -``` -Error: locator.click: Error: strict mode violation: getByRole('button') resolved to 6 elements: - 1) aka getByRole('button', { name: 'Elemental Mana (1)' }) - 3) aka getByRole('button', { name: 'Open Next.js Dev Tools' }) - -Call log: - - waiting for getByRole('button') - -``` - -# Page snapshot - -```yaml -- generic [ref=e1]: - - generic [ref=e2]: - - banner [ref=e3]: - - generic [ref=e4]: - - heading "MANA LOOP" [level=1] [ref=e5] - - generic [ref=e7]: - - generic [ref=e8]: - - generic [ref=e9]: Day 1 - - generic [ref=e10]: 01:04 - - generic [ref=e11]: - - generic [ref=e12]: "0" - - generic [ref=e13]: Insight - - main [ref=e14]: - - generic [ref=e15]: - - generic [ref=e17]: - - generic [ref=e18]: - - generic [ref=e19]: - - generic [ref=e20]: "12" - - generic [ref=e21]: / 100 - - generic [ref=e22]: - - text: +2.3 mana/hr - - generic [ref=e23]: (1.1x med) - - progressbar [ref=e24] - - button "Gather +1 Mana" [ref=e26]: - - img - - text: Gather +1 Mana - - generic [ref=e27]: - - button "Elemental Mana (1)" [ref=e28]: - - generic [ref=e29]: Elemental Mana (1) - - img [ref=e30] - - generic [ref=e33]: - - generic [ref=e34]: - - generic [ref=e35]: 🔗 - - generic [ref=e36]: Transference - - generic [ref=e39]: 0/10 - - button "Climb the Spire" [ref=e40]: - - img - - text: Climb the Spire - - generic [ref=e42]: - - generic [ref=e43]: - - img [ref=e44] - - generic [ref=e46]: Current Activity - - generic [ref=e47]: Meditating - - generic [ref=e48]: - - generic [ref=e49]: "1" - - generic [ref=e50]: "2" - - generic [ref=e51]: "3" - - generic [ref=e52]: "4" - - generic [ref=e53]: "5" - - generic [ref=e54]: "6" - - generic [ref=e55]: "7" - - generic [ref=e56]: "8" - - generic [ref=e57]: "9" - - generic [ref=e58]: "10" - - generic [ref=e59]: "11" - - generic [ref=e60]: "12" - - generic [ref=e61]: "13" - - generic [ref=e62]: "14" - - generic [ref=e63]: "15" - - generic [ref=e64]: "16" - - generic [ref=e65]: "17" - - generic [ref=e66]: "18" - - generic [ref=e67]: "19" - - generic [ref=e68]: "20" - - generic [ref=e69]: "21" - - generic [ref=e70]: "22" - - generic [ref=e71]: "23" - - generic [ref=e72]: "24" - - generic [ref=e73]: "25" - - generic [ref=e74]: "26" - - generic [ref=e75]: "27" - - generic [ref=e76]: "28" - - generic [ref=e77]: "29" - - generic [ref=e78]: "30" - - generic [ref=e80]: - - tablist [ref=e81]: - - tab "⚔️ Spire" [ref=e82] - - tab "✨ Attune" [ref=e83] - - tab "🗿 Golems" [ref=e84] - - tab "📚 Skills" [ref=e85] - - tab "🔮 Spells" [ref=e86] - - tab "🛡️ Gear" [ref=e87] - - tab "🔧 Craft" [active] [selected] [ref=e88] - - tab "💎 Loot" [ref=e89] - - tab "🏆 Achieve" [ref=e90] - - tab "📊 Stats" [ref=e91] - - tab "🐛 Debug" [ref=e92] - - tab "📖 Grimoire" [ref=e93] - - tabpanel "🔧 Craft" [ref=e94]: - - generic [ref=e95]: - - generic [ref=e97]: - - button "Fabricate" [ref=e98]: - - img - - text: Fabricate - - button "Enchant" [ref=e99]: - - img - - text: Enchant - - generic [ref=e100]: - - generic [ref=e101]: - - generic [ref=e103]: - - img [ref=e104] - - text: Available Blueprints - - generic [ref=e113]: - - img [ref=e114] - - paragraph [ref=e118]: No blueprints discovered yet. - - paragraph [ref=e119]: Defeat guardians to find blueprints! - - generic [ref=e120]: - - generic [ref=e122]: - - img [ref=e123] - - text: Materials (0) - - generic [ref=e131]: - - img [ref=e132] - - paragraph [ref=e134]: No materials collected yet. - - paragraph [ref=e135]: Defeat floors to gather materials! - - region "Notifications (F8)": - - list - - region "Notifications (F8)": - - list - - button "Open Next.js Dev Tools" [ref=e141] [cursor=pointer]: - - img [ref=e142] - - alert [ref=e145] -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | /** - 4 | * E2E tests for the 3-step enchantment flow: - 5 | * Design → Prepare → Apply - 6 | * - 7 | * These tests validate the core crafting loop works end-to-end. - 8 | */ - 9 | - 10 | test.describe('Enchanting Flow', () => { - 11 | /** - 12 | * Before each test, ensure we start with a clean state. - 13 | * The game persists state in localStorage, so we clear it. - 14 | */ - 15 | test.beforeEach(async ({ page }) => { - 16 | await page.goto('/'); - 17 | // Clear game state to ensure a fresh start - 18 | await page.evaluate(() => { - 19 | Object.keys(localStorage) - 20 | .filter((k) => k.startsWith('mana-loop-')) - 21 | .forEach((k) => localStorage.removeItem(k)); - 22 | }); - 23 | await page.reload(); - 24 | // Wait for the game to initialize - 25 | await page.waitForLoadState('networkidle'); - 26 | }); - 27 | - 28 | test('can navigate to Crafting tab', async ({ page }) => { - 29 | // The tab bar contains a "Craft" tab - 30 | const craftTab = page.getByRole('tab', { name: /🔧 Craft/ }); - 31 | await expect(craftTab).toBeVisible(); - 32 | await craftTab.click(); - 33 | - 34 | // Verify we're on the crafting tab by checking for sub-tabs - 35 | const fabricateBtn = page.getByRole('button', { hasText: 'Fabricate' }); - 36 | const enchantBtn = page.getByRole('button', { hasText: 'Enchant' }); - 37 | await expect(fabricateBtn).toBeVisible(); - 38 | await expect(enchantBtn).toBeVisible(); - 39 | }); - 40 | - 41 | test('can switch to Enchant sub-tab and see design UI', async ({ page }) => { - 42 | await page.goto('/'); - 43 | await page.evaluate(() => { - 44 | Object.keys(localStorage) - 45 | .filter((k) => k.startsWith('mana-loop-')) - 46 | .forEach((k) => localStorage.removeItem(k)); - 47 | }); - 48 | await page.reload(); - 49 | await page.waitForLoadState('networkidle'); - 50 | - 51 | // Navigate to Crafting tab - 52 | await page.getByRole('tab', { name: /🔧 Craft/ }).click(); - 53 | - 54 | // Click Enchant sub-tab - 55 | const enchantBtn = page.getByRole('button', { hasText: 'Enchant' }); -> 56 | await enchantBtn.click(); - | ^ Error: locator.click: Error: strict mode violation: getByRole('button') resolved to 6 elements: - 57 | - 58 | // Should see the design stage UI - 59 | const designBtn = page.getByRole('button', { hasText: 'Design' }); - 60 | const prepareBtn = page.getByRole('button', { hasText: 'Prepare' }); - 61 | const applyBtn = page.getByRole('button', { hasText: 'Apply' }); - 62 | await expect(designBtn).toBeVisible(); - 63 | await expect(prepareBtn).toBeVisible(); - 64 | await expect(applyBtn).toBeVisible(); - 65 | }); - 66 | - 67 | test('can select equipment type and effect in Design stage', async ({ page }) => { - 68 | await page.goto('/'); - 69 | await page.evaluate(() => { - 70 | Object.keys(localStorage) - 71 | .filter((k) => k.startsWith('mana-loop-')) - 72 | .forEach((k) => localStorage.removeItem(k)); - 73 | }); - 74 | await page.reload(); - 75 | await page.waitForLoadState('networkidle'); - 76 | - 77 | // Navigate to Crafting > Enchant > Design - 78 | await page.getByRole('tab', { name: /🔧 Craft/ }).click(); - 79 | await page.getByRole('button', { hasText: 'Enchant' }).click(); - 80 | - 81 | // The design section should show effect selectors once an equipment type is chosen - 82 | // Look for any element matching equipment type buttons and effect-related content - 83 | const equipmentButtons = page.locator('button:has-text("Basic Staff"), button:has-text("Apprentice Wand"), button:has-text("Oak Staff"), button:has-text("Crystal Wand")'); - 84 | const count = await equipmentButtons.count(); - 85 | expect(count).toBeGreaterThan(0); - 86 | }); - 87 | - 88 | test('can navigate through all 3 enchant stages', async ({ page }) => { - 89 | await page.goto('/'); - 90 | await page.evaluate(() => { - 91 | Object.keys(localStorage) - 92 | .filter((k) => k.startsWith('mana-loop-')) - 93 | .forEach((k) => localStorage.removeItem(k)); - 94 | }); - 95 | await page.reload(); - 96 | await page.waitForLoadState('networkidle'); - 97 | - 98 | // Navigate to Crafting > Enchant - 99 | await page.getByRole('tab', { name: /🔧 Craft/ }).click(); - 100 | await page.getByRole('button', { hasText: 'Enchant' }).click(); - 101 | - 102 | // Verify Design stage is active - 103 | const designBtn = page.getByRole('button', { hasText: 'Design' }); - 104 | await expect(designBtn).toBeVisible(); - 105 | - 106 | // Switch to Prepare stage - 107 | const prepareBtn = page.getByRole('button', { hasText: 'Prepare' }); - 108 | await prepareBtn.click(); - 109 | - 110 | // Should see preparation UI - 111 | const prepareHeading = page.locator('text=Select Equipment to Prepare'); - 112 | await expect(prepareHeading).toBeVisible({ timeout: 5000 }); - 113 | - 114 | // Switch to Apply stage - 115 | const applyBtn = page.getByRole('button', { hasText: 'Apply' }); - 116 | await applyBtn.click(); - 117 | - 118 | // Should see application UI - 119 | const applyHeading = page.locator('text=Select Equipment & Design'); - 120 | await expect(applyHeading).toBeVisible({ timeout: 5000 }); - 121 | }); - 122 | }); -``` \ No newline at end of file diff --git a/playwright-report/data/72280c2048aa77a6b58afc7bba8f9db3dfd1c68b.webm b/playwright-report/data/72280c2048aa77a6b58afc7bba8f9db3dfd1c68b.webm deleted file mode 100644 index a066bf2..0000000 Binary files a/playwright-report/data/72280c2048aa77a6b58afc7bba8f9db3dfd1c68b.webm and /dev/null differ diff --git a/playwright-report/data/8035d8abad1bfb2166374e25b55f52324fef1275.png b/playwright-report/data/8035d8abad1bfb2166374e25b55f52324fef1275.png deleted file mode 100644 index 9359a40..0000000 Binary files a/playwright-report/data/8035d8abad1bfb2166374e25b55f52324fef1275.png and /dev/null differ diff --git a/playwright-report/data/8396039272c615989307eaf4113a77b0d77cfbdd.webm b/playwright-report/data/8396039272c615989307eaf4113a77b0d77cfbdd.webm deleted file mode 100644 index c43e317..0000000 Binary files a/playwright-report/data/8396039272c615989307eaf4113a77b0d77cfbdd.webm and /dev/null differ diff --git a/playwright-report/data/a69b7491fd34ee0580bc0153a90dc146b509aac3.md b/playwright-report/data/a69b7491fd34ee0580bc0153a90dc146b509aac3.md deleted file mode 100644 index bb73e33..0000000 --- a/playwright-report/data/a69b7491fd34ee0580bc0153a90dc146b509aac3.md +++ /dev/null @@ -1,375 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: equipment.spec.ts >> Equipment Management >> shows starting equipment already equipped -- Location: e2e/equipment.spec.ts:78:7 - -# Error details - -``` -Error: expect(locator).toBeVisible() failed - -Locator: locator('text=Main Hand').locator('..').locator('text=Basic Staff') -Expected: visible -Timeout: 5000ms -Error: element(s) not found - -Call log: - - Expect "toBeVisible" with timeout 5000ms - - waiting for locator('text=Main Hand').locator('..').locator('text=Basic Staff') - -``` - -# Page snapshot - -```yaml -- generic [ref=e1]: - - generic [ref=e2]: - - banner [ref=e3]: - - generic [ref=e4]: - - heading "MANA LOOP" [level=1] [ref=e5] - - generic [ref=e7]: - - generic [ref=e8]: - - generic [ref=e9]: Day 1 - - generic [ref=e10]: 01:52 - - generic [ref=e11]: - - generic [ref=e12]: "0" - - generic [ref=e13]: Insight - - main [ref=e14]: - - generic [ref=e15]: - - generic [ref=e17]: - - generic [ref=e18]: - - generic [ref=e19]: - - generic [ref=e20]: "14" - - generic [ref=e21]: / 100 - - generic [ref=e22]: - - text: +2.7 mana/hr - - generic [ref=e23]: (1.4x med) - - progressbar [ref=e24] - - button "Gather +1 Mana" [ref=e26]: - - img - - text: Gather +1 Mana - - generic [ref=e27]: - - button "Elemental Mana (1)" [ref=e28]: - - generic [ref=e29]: Elemental Mana (1) - - img [ref=e30] - - generic [ref=e33]: - - generic [ref=e34]: - - generic [ref=e35]: 🔗 - - generic [ref=e36]: Transference - - generic [ref=e39]: 0/10 - - button "Climb the Spire" [ref=e40]: - - img - - text: Climb the Spire - - generic [ref=e42]: - - generic [ref=e43]: - - img [ref=e44] - - generic [ref=e46]: Current Activity - - generic [ref=e47]: Meditating - - generic [ref=e48]: - - generic [ref=e49]: "1" - - generic [ref=e50]: "2" - - generic [ref=e51]: "3" - - generic [ref=e52]: "4" - - generic [ref=e53]: "5" - - generic [ref=e54]: "6" - - generic [ref=e55]: "7" - - generic [ref=e56]: "8" - - generic [ref=e57]: "9" - - generic [ref=e58]: "10" - - generic [ref=e59]: "11" - - generic [ref=e60]: "12" - - generic [ref=e61]: "13" - - generic [ref=e62]: "14" - - generic [ref=e63]: "15" - - generic [ref=e64]: "16" - - generic [ref=e65]: "17" - - generic [ref=e66]: "18" - - generic [ref=e67]: "19" - - generic [ref=e68]: "20" - - generic [ref=e69]: "21" - - generic [ref=e70]: "22" - - generic [ref=e71]: "23" - - generic [ref=e72]: "24" - - generic [ref=e73]: "25" - - generic [ref=e74]: "26" - - generic [ref=e75]: "27" - - generic [ref=e76]: "28" - - generic [ref=e77]: "29" - - generic [ref=e78]: "30" - - generic [ref=e80]: - - tablist [ref=e81]: - - tab "⚔️ Spire" [ref=e82] - - tab "✨ Attune" [ref=e83] - - tab "🗿 Golems" [ref=e84] - - tab "📚 Skills" [ref=e85] - - tab "🔮 Spells" [ref=e86] - - tab "🛡️ Gear" [active] [selected] [ref=e87] - - tab "🔧 Craft" [ref=e88] - - tab "💎 Loot" [ref=e89] - - tab "🏆 Achieve" [ref=e90] - - tab "📊 Stats" [ref=e91] - - tab "🐛 Debug" [ref=e92] - - tab "📖 Grimoire" [ref=e93] - - tabpanel "🛡️ Gear" [ref=e94]: - - generic [ref=e95]: - - generic [ref=e96]: - - generic [ref=e97]: - - heading "Equipped Gear" [level=3] [ref=e98] - - generic [ref=e100]: 4 / 8 slots filled - - generic [ref=e101]: - - generic [ref=e102]: - - heading "Weapon & Shield" [level=4] [ref=e103] - - generic [ref=e104]: - - 'button "Main Hand slot: Basic Staff" [ref=e106]': - - generic [ref=e107]: - - generic [ref=e108]: - - img [ref=e109] - - generic [ref=e114]: Main Hand - - button "Unequip Basic Staff" [ref=e115]: - - img [ref=e116] - - generic [ref=e119]: - - generic [ref=e120]: - - text: Basic Staff - - generic [ref=e121]: 2-Handed - - generic [ref=e122]: "Enchantments: 1/50" - - generic [ref=e124]: Mana Bolt - - button "Off Hand slot (blocked by 2-handed weapon) (empty)" [ref=e125]: - - generic [ref=e127]: - - img [ref=e128] - - generic [ref=e130]: Off Hand - - generic [ref=e131]: - - img - - text: Occupied — 2H Weapon - - generic [ref=e132]: - - img [ref=e133] - - text: Blocked by 2-handed weapon - - generic [ref=e135]: - - heading "Armor" [level=4] [ref=e136] - - generic [ref=e137]: - - button "Head slot (empty)" [ref=e139]: - - generic [ref=e141]: - - img [ref=e142] - - generic [ref=e147]: Head - - generic [ref=e148]: Head - - 'button "Body slot: Civilian Shirt" [ref=e150]': - - generic [ref=e151]: - - generic [ref=e152]: - - img [ref=e153] - - generic [ref=e155]: Body - - button "Unequip Civilian Shirt" [ref=e156]: - - img [ref=e157] - - generic [ref=e160]: - - generic [ref=e161]: Civilian Shirt - - generic [ref=e162]: "Enchantments: 0/30" - - 'button "Hands slot: Civilian Gloves" [ref=e164]': - - generic [ref=e165]: - - generic [ref=e166]: - - img [ref=e167] - - generic [ref=e172]: Hands - - button "Unequip Civilian Gloves" [ref=e173]: - - img [ref=e174] - - generic [ref=e177]: - - generic [ref=e178]: Civilian Gloves - - generic [ref=e179]: "Enchantments: 0/20" - - 'button "Feet slot: Civilian Shoes" [ref=e181]': - - generic [ref=e182]: - - generic [ref=e183]: - - img [ref=e184] - - generic [ref=e187]: Feet - - button "Unequip Civilian Shoes" [ref=e188]: - - img [ref=e189] - - generic [ref=e192]: - - generic [ref=e193]: Civilian Shoes - - generic [ref=e194]: "Enchantments: 0/15" - - generic [ref=e195]: - - heading "Accessories" [level=4] [ref=e196] - - generic [ref=e197]: - - button "Accessory 1 slot (empty)" [ref=e199]: - - generic [ref=e201]: - - img [ref=e202] - - generic [ref=e205]: Accessory 1 - - generic [ref=e206]: Accessory 1 - - button "Accessory 2 slot (empty)" [ref=e208]: - - generic [ref=e210]: - - img [ref=e211] - - generic [ref=e214]: Accessory 2 - - generic [ref=e215]: Accessory 2 - - generic [ref=e216]: - - heading "Equipment Inventory (0 items)" [level=3] [ref=e218] - - status [ref=e219]: No unequipped items. Craft new gear in the Crafting tab. - - generic [ref=e220]: - - heading "Equipment Stats Summary" [level=3] [ref=e222] - - generic [ref=e223]: - - generic [ref=e224]: - - generic [ref=e225]: "4" - - generic [ref=e226]: Total Items - - generic [ref=e227]: - - generic [ref=e228]: "4" - - generic [ref=e229]: Equipped - - generic [ref=e230]: - - generic [ref=e231]: "0" - - generic [ref=e232]: In Inventory - - generic [ref=e233]: - - generic [ref=e234]: "1" - - generic [ref=e235]: Total Enchantments - - generic [ref=e236]: - - heading "✨ Enchantment Power" [level=3] [ref=e238] - - generic [ref=e239]: - - generic [ref=e240]: - - generic [ref=e241]: "Enchantment Power:" - - generic [ref=e242]: 1.00× - - paragraph [ref=e243]: Increases the power of all enchantments by 0%. Multiplier applied to all enchantment effects. - - generic [ref=e244]: - - generic [ref=e245]: "Active Effects from Equipment:" - - generic [ref=e247]: No active effects - - region "Notifications (F8)": - - list - - region "Notifications (F8)": - - list - - button "Open Next.js Dev Tools" [ref=e253] [cursor=pointer]: - - img [ref=e254] - - alert [ref=e257] -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | /** - 4 | * E2E tests for equipment management: - 5 | * - Equipping items to slots - 6 | * - 2-handed weapon blocking offhand slot - 7 | * - Unequipping items back to inventory - 8 | */ - 9 | - 10 | test.describe('Equipment Management', () => { - 11 | test.beforeEach(async ({ page }) => { - 12 | await page.goto('/'); - 13 | // Clear game state for a fresh start - 14 | await page.evaluate(() => { - 15 | Object.keys(localStorage) - 16 | .filter((k) => k.startsWith('mana-loop-')) - 17 | .forEach((k) => localStorage.removeItem(k)); - 18 | }); - 19 | await page.reload(); - 20 | await page.waitForLoadState('networkidle'); - 21 | }); - 22 | - 23 | test('can navigate to Equipment tab', async ({ page }) => { - 24 | // Use the tab with the shield icon to disambiguate - 25 | const gearTab = page.getByRole('tab', { name: /🛡️ Gear/ }); - 26 | await expect(gearTab).toBeVisible(); - 27 | await gearTab.click(); - 28 | - 29 | // Verify equipment UI elements - 30 | const equippedGearHeading = page.locator('text="Equipped Gear"'); - 31 | await expect(equippedGearHeading).toBeVisible({ timeout: 5000 }); - 32 | }); - 33 | - 34 | test('shows equipment slots with labels', async ({ page }) => { - 35 | await page.goto('/'); - 36 | await page.evaluate(() => { - 37 | Object.keys(localStorage) - 38 | .filter((k) => k.startsWith('mana-loop-')) - 39 | .forEach((k) => localStorage.removeItem(k)); - 40 | }); - 41 | await page.reload(); - 42 | await page.waitForLoadState('networkidle'); - 43 | - 44 | await page.getByRole('tab', { name: /🛡️ Gear/ }).click(); - 45 | - 46 | // Check for expected slot labels - use role=heading or more specific selectors - 47 | // Main Hand slot - 48 | const mainHandSection = page.locator('text=Main Hand'); - 49 | await expect(mainHandSection.first()).toBeVisible(); - 50 | - 51 | // Off Hand - 52 | const offHandSection = page.locator('text=Off Hand'); - 53 | await expect(offHandSection.first()).toBeVisible(); - 54 | - 55 | // Head - 56 | const headSection = page.locator('text=Head'); - 57 | await expect(headSection.first()).toBeVisible(); - 58 | - 59 | // Body - 60 | const bodySection = page.locator('text=Body'); - 61 | await expect(bodySection.first()).toBeVisible(); - 62 | - 63 | // Hands - 64 | const handsSection = page.locator('text=Hands'); - 65 | await expect(handsSection.first()).toBeVisible(); - 66 | - 67 | // Feet - 68 | const feetSection = page.locator('text=Feet'); - 69 | await expect(feetSection.first()).toBeVisible(); - 70 | - 71 | // Accessory 1 and 2 - 72 | const acc1Section = page.locator('text=Accessory 1'); - 73 | await expect(acc1Section.first()).toBeVisible(); - 74 | const acc2Section = page.locator('text=Accessory 2'); - 75 | await expect(acc2Section.first()).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows starting equipment already equipped', async ({ page }) => { - 79 | await page.goto('/'); - 80 | await page.evaluate(() => { - 81 | Object.keys(localStorage) - 82 | .filter((k) => k.startsWith('mana-loop-')) - 83 | .forEach((k) => localStorage.removeItem(k)); - 84 | }); - 85 | await page.reload(); - 86 | await page.waitForLoadState('networkidle'); - 87 | - 88 | await page.getByRole('tab', { name: /🛡️ Gear/ }).click(); - 89 | - 90 | // The player starts with a Basic Staff in main hand (as an equipped item) - 91 | const mainHandSlot = page.locator('text=Main Hand >> .. >> text=Basic Staff'); -> 92 | await expect(mainHandSlot).toBeVisible({ timeout: 5000 }); - | ^ Error: expect(locator).toBeVisible() failed - 93 | }); - 94 | - 95 | test('2-handed weapon blocks offhand slot', async ({ page }) => { - 96 | await page.goto('/'); - 97 | await page.evaluate(() => { - 98 | Object.keys(localStorage) - 99 | .filter((k) => k.startsWith('mana-loop-')) - 100 | .forEach((k) => localStorage.removeItem(k)); - 101 | }); - 102 | await page.reload(); - 103 | await page.waitForLoadState('networkidle'); - 104 | - 105 | await page.getByRole('tab', { name: /🛡️ Gear/ }).click(); - 106 | - 107 | // The starting basic staff is 2-handed - 108 | // The offhand slot should show as blocked with "Occupied — 2H Weapon" - 109 | const offHandBlocked = page.locator('text=Occupied').first(); - 110 | await expect(offHandBlocked).toBeVisible({ timeout: 5000 }); - 111 | }); - 112 | - 113 | test('can unequip an item from a slot', async ({ page }) => { - 114 | await page.goto('/'); - 115 | await page.evaluate(() => { - 116 | Object.keys(localStorage) - 117 | .filter((k) => k.startsWith('mana-loop-')) - 118 | .forEach((k) => localStorage.removeItem(k)); - 119 | }); - 120 | await page.reload(); - 121 | await page.waitForLoadState('networkidle'); - 122 | - 123 | await page.getByRole('tab', { name: /🛡️ Gear/ }).click(); - 124 | - 125 | // Find an equiped slot with an unequip button (the X button) - 126 | // The hands slot has civilian gloves equipped - 127 | const handsSlot = page.locator('text=Hands >> .. >> button').first(); - 128 | await expect(handsSlot).toBeVisible({ timeout: 5000 }); - 129 | // Note: exact behavior of unequip depends on implementation state - 130 | }); - 131 | }); -``` \ No newline at end of file diff --git a/playwright-report/data/bb3c9d51cafcb654c796b093c72c5b702f52faed.webm b/playwright-report/data/bb3c9d51cafcb654c796b093c72c5b702f52faed.webm deleted file mode 100644 index 53b4a5d..0000000 Binary files a/playwright-report/data/bb3c9d51cafcb654c796b093c72c5b702f52faed.webm and /dev/null differ diff --git a/playwright-report/data/bee318a3f485bd3e98088a4735e02181585e431b.png b/playwright-report/data/bee318a3f485bd3e98088a4735e02181585e431b.png deleted file mode 100644 index 4be1783..0000000 Binary files a/playwright-report/data/bee318a3f485bd3e98088a4735e02181585e431b.png and /dev/null differ diff --git a/playwright-report/data/c0f44af041cac0f5d5efaec8a9a9e5d165c8d26a.png b/playwright-report/data/c0f44af041cac0f5d5efaec8a9a9e5d165c8d26a.png deleted file mode 100644 index 8745af0..0000000 Binary files a/playwright-report/data/c0f44af041cac0f5d5efaec8a9a9e5d165c8d26a.png and /dev/null differ diff --git a/playwright-report/data/cf49b56fde3bacf27d842ef4bfeed4887d97f01e.webm b/playwright-report/data/cf49b56fde3bacf27d842ef4bfeed4887d97f01e.webm deleted file mode 100644 index 0884b64..0000000 Binary files a/playwright-report/data/cf49b56fde3bacf27d842ef4bfeed4887d97f01e.webm and /dev/null differ diff --git a/playwright-report/data/dbea283cbcf6aaed195161609c68ab7de0c6adfa.png b/playwright-report/data/dbea283cbcf6aaed195161609c68ab7de0c6adfa.png deleted file mode 100644 index e626b41..0000000 Binary files a/playwright-report/data/dbea283cbcf6aaed195161609c68ab7de0c6adfa.png and /dev/null differ diff --git a/playwright-report/data/dc2d9fe97c08dd61f42a27ead0829c2d74322ccc.webm b/playwright-report/data/dc2d9fe97c08dd61f42a27ead0829c2d74322ccc.webm deleted file mode 100644 index 7c8f37e..0000000 Binary files a/playwright-report/data/dc2d9fe97c08dd61f42a27ead0829c2d74322ccc.webm and /dev/null differ diff --git a/playwright-report/data/e3d1abb209771785e7247c38fd372d8fd61b7ea4.md b/playwright-report/data/e3d1abb209771785e7247c38fd372d8fd61b7ea4.md deleted file mode 100644 index ec9b2c4..0000000 --- a/playwright-report/data/e3d1abb209771785e7247c38fd372d8fd61b7ea4.md +++ /dev/null @@ -1,280 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: enchanting.spec.ts >> Enchanting Flow >> can select equipment type and effect in Design stage -- Location: e2e/enchanting.spec.ts:67:7 - -# Error details - -``` -Error: locator.click: Error: strict mode violation: getByRole('button') resolved to 6 elements: - 1) aka getByRole('button', { name: 'Elemental Mana (1)' }) - 3) aka getByRole('button', { name: 'Open Next.js Dev Tools' }) - -Call log: - - waiting for getByRole('button') - -``` - -# Page snapshot - -```yaml -- generic [ref=e1]: - - generic [ref=e2]: - - banner [ref=e3]: - - generic [ref=e4]: - - heading "MANA LOOP" [level=1] [ref=e5] - - generic [ref=e7]: - - generic [ref=e8]: - - generic [ref=e9]: Day 1 - - generic [ref=e10]: 01:02 - - generic [ref=e11]: - - generic [ref=e12]: "0" - - generic [ref=e13]: Insight - - main [ref=e14]: - - generic [ref=e15]: - - generic [ref=e17]: - - generic [ref=e18]: - - generic [ref=e19]: - - generic [ref=e20]: "12" - - generic [ref=e21]: / 100 - - generic [ref=e22]: - - text: +2.3 mana/hr - - generic [ref=e23]: (1.1x med) - - progressbar [ref=e24] - - button "Gather +1 Mana" [ref=e26]: - - img - - text: Gather +1 Mana - - generic [ref=e27]: - - button "Elemental Mana (1)" [ref=e28]: - - generic [ref=e29]: Elemental Mana (1) - - img [ref=e30] - - generic [ref=e33]: - - generic [ref=e34]: - - generic [ref=e35]: 🔗 - - generic [ref=e36]: Transference - - generic [ref=e39]: 0/10 - - button "Climb the Spire" [ref=e40]: - - img - - text: Climb the Spire - - generic [ref=e42]: - - generic [ref=e43]: - - img [ref=e44] - - generic [ref=e46]: Current Activity - - generic [ref=e47]: Meditating - - generic [ref=e48]: - - generic [ref=e49]: "1" - - generic [ref=e50]: "2" - - generic [ref=e51]: "3" - - generic [ref=e52]: "4" - - generic [ref=e53]: "5" - - generic [ref=e54]: "6" - - generic [ref=e55]: "7" - - generic [ref=e56]: "8" - - generic [ref=e57]: "9" - - generic [ref=e58]: "10" - - generic [ref=e59]: "11" - - generic [ref=e60]: "12" - - generic [ref=e61]: "13" - - generic [ref=e62]: "14" - - generic [ref=e63]: "15" - - generic [ref=e64]: "16" - - generic [ref=e65]: "17" - - generic [ref=e66]: "18" - - generic [ref=e67]: "19" - - generic [ref=e68]: "20" - - generic [ref=e69]: "21" - - generic [ref=e70]: "22" - - generic [ref=e71]: "23" - - generic [ref=e72]: "24" - - generic [ref=e73]: "25" - - generic [ref=e74]: "26" - - generic [ref=e75]: "27" - - generic [ref=e76]: "28" - - generic [ref=e77]: "29" - - generic [ref=e78]: "30" - - generic [ref=e80]: - - tablist [ref=e81]: - - tab "⚔️ Spire" [ref=e82] - - tab "✨ Attune" [ref=e83] - - tab "🗿 Golems" [ref=e84] - - tab "📚 Skills" [ref=e85] - - tab "🔮 Spells" [ref=e86] - - tab "🛡️ Gear" [ref=e87] - - tab "🔧 Craft" [active] [selected] [ref=e88] - - tab "💎 Loot" [ref=e89] - - tab "🏆 Achieve" [ref=e90] - - tab "📊 Stats" [ref=e91] - - tab "🐛 Debug" [ref=e92] - - tab "📖 Grimoire" [ref=e93] - - tabpanel "🔧 Craft" [ref=e94]: - - generic [ref=e95]: - - generic [ref=e97]: - - button "Fabricate" [ref=e98]: - - img - - text: Fabricate - - button "Enchant" [ref=e99]: - - img - - text: Enchant - - generic [ref=e100]: - - generic [ref=e101]: - - generic [ref=e103]: - - img [ref=e104] - - text: Available Blueprints - - generic [ref=e113]: - - img [ref=e114] - - paragraph [ref=e118]: No blueprints discovered yet. - - paragraph [ref=e119]: Defeat guardians to find blueprints! - - generic [ref=e120]: - - generic [ref=e122]: - - img [ref=e123] - - text: Materials (0) - - generic [ref=e131]: - - img [ref=e132] - - paragraph [ref=e134]: No materials collected yet. - - paragraph [ref=e135]: Defeat floors to gather materials! - - region "Notifications (F8)": - - list - - region "Notifications (F8)": - - list - - button "Open Next.js Dev Tools" [ref=e141] [cursor=pointer]: - - img [ref=e142] - - alert [ref=e145] -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | /** - 4 | * E2E tests for the 3-step enchantment flow: - 5 | * Design → Prepare → Apply - 6 | * - 7 | * These tests validate the core crafting loop works end-to-end. - 8 | */ - 9 | - 10 | test.describe('Enchanting Flow', () => { - 11 | /** - 12 | * Before each test, ensure we start with a clean state. - 13 | * The game persists state in localStorage, so we clear it. - 14 | */ - 15 | test.beforeEach(async ({ page }) => { - 16 | await page.goto('/'); - 17 | // Clear game state to ensure a fresh start - 18 | await page.evaluate(() => { - 19 | Object.keys(localStorage) - 20 | .filter((k) => k.startsWith('mana-loop-')) - 21 | .forEach((k) => localStorage.removeItem(k)); - 22 | }); - 23 | await page.reload(); - 24 | // Wait for the game to initialize - 25 | await page.waitForLoadState('networkidle'); - 26 | }); - 27 | - 28 | test('can navigate to Crafting tab', async ({ page }) => { - 29 | // The tab bar contains a "Craft" tab - 30 | const craftTab = page.getByRole('tab', { name: /🔧 Craft/ }); - 31 | await expect(craftTab).toBeVisible(); - 32 | await craftTab.click(); - 33 | - 34 | // Verify we're on the crafting tab by checking for sub-tabs - 35 | const fabricateBtn = page.getByRole('button', { hasText: 'Fabricate' }); - 36 | const enchantBtn = page.getByRole('button', { hasText: 'Enchant' }); - 37 | await expect(fabricateBtn).toBeVisible(); - 38 | await expect(enchantBtn).toBeVisible(); - 39 | }); - 40 | - 41 | test('can switch to Enchant sub-tab and see design UI', async ({ page }) => { - 42 | await page.goto('/'); - 43 | await page.evaluate(() => { - 44 | Object.keys(localStorage) - 45 | .filter((k) => k.startsWith('mana-loop-')) - 46 | .forEach((k) => localStorage.removeItem(k)); - 47 | }); - 48 | await page.reload(); - 49 | await page.waitForLoadState('networkidle'); - 50 | - 51 | // Navigate to Crafting tab - 52 | await page.getByRole('tab', { name: /🔧 Craft/ }).click(); - 53 | - 54 | // Click Enchant sub-tab - 55 | const enchantBtn = page.getByRole('button', { hasText: 'Enchant' }); - 56 | await enchantBtn.click(); - 57 | - 58 | // Should see the design stage UI - 59 | const designBtn = page.getByRole('button', { hasText: 'Design' }); - 60 | const prepareBtn = page.getByRole('button', { hasText: 'Prepare' }); - 61 | const applyBtn = page.getByRole('button', { hasText: 'Apply' }); - 62 | await expect(designBtn).toBeVisible(); - 63 | await expect(prepareBtn).toBeVisible(); - 64 | await expect(applyBtn).toBeVisible(); - 65 | }); - 66 | - 67 | test('can select equipment type and effect in Design stage', async ({ page }) => { - 68 | await page.goto('/'); - 69 | await page.evaluate(() => { - 70 | Object.keys(localStorage) - 71 | .filter((k) => k.startsWith('mana-loop-')) - 72 | .forEach((k) => localStorage.removeItem(k)); - 73 | }); - 74 | await page.reload(); - 75 | await page.waitForLoadState('networkidle'); - 76 | - 77 | // Navigate to Crafting > Enchant > Design - 78 | await page.getByRole('tab', { name: /🔧 Craft/ }).click(); -> 79 | await page.getByRole('button', { hasText: 'Enchant' }).click(); - | ^ Error: locator.click: Error: strict mode violation: getByRole('button') resolved to 6 elements: - 80 | - 81 | // The design section should show effect selectors once an equipment type is chosen - 82 | // Look for any element matching equipment type buttons and effect-related content - 83 | const equipmentButtons = page.locator('button:has-text("Basic Staff"), button:has-text("Apprentice Wand"), button:has-text("Oak Staff"), button:has-text("Crystal Wand")'); - 84 | const count = await equipmentButtons.count(); - 85 | expect(count).toBeGreaterThan(0); - 86 | }); - 87 | - 88 | test('can navigate through all 3 enchant stages', async ({ page }) => { - 89 | await page.goto('/'); - 90 | await page.evaluate(() => { - 91 | Object.keys(localStorage) - 92 | .filter((k) => k.startsWith('mana-loop-')) - 93 | .forEach((k) => localStorage.removeItem(k)); - 94 | }); - 95 | await page.reload(); - 96 | await page.waitForLoadState('networkidle'); - 97 | - 98 | // Navigate to Crafting > Enchant - 99 | await page.getByRole('tab', { name: /🔧 Craft/ }).click(); - 100 | await page.getByRole('button', { hasText: 'Enchant' }).click(); - 101 | - 102 | // Verify Design stage is active - 103 | const designBtn = page.getByRole('button', { hasText: 'Design' }); - 104 | await expect(designBtn).toBeVisible(); - 105 | - 106 | // Switch to Prepare stage - 107 | const prepareBtn = page.getByRole('button', { hasText: 'Prepare' }); - 108 | await prepareBtn.click(); - 109 | - 110 | // Should see preparation UI - 111 | const prepareHeading = page.locator('text=Select Equipment to Prepare'); - 112 | await expect(prepareHeading).toBeVisible({ timeout: 5000 }); - 113 | - 114 | // Switch to Apply stage - 115 | const applyBtn = page.getByRole('button', { hasText: 'Apply' }); - 116 | await applyBtn.click(); - 117 | - 118 | // Should see application UI - 119 | const applyHeading = page.locator('text=Select Equipment & Design'); - 120 | await expect(applyHeading).toBeVisible({ timeout: 5000 }); - 121 | }); - 122 | }); -``` \ No newline at end of file diff --git a/playwright-report/data/e59720b989841926cc856d6a00be0a6f8365cf49.webm b/playwright-report/data/e59720b989841926cc856d6a00be0a6f8365cf49.webm deleted file mode 100644 index 2e72a6a..0000000 Binary files a/playwright-report/data/e59720b989841926cc856d6a00be0a6f8365cf49.webm and /dev/null differ diff --git a/playwright-report/data/f5ba77f8b20c452bd2c31718b44897276882a465.md b/playwright-report/data/f5ba77f8b20c452bd2c31718b44897276882a465.md deleted file mode 100644 index 0d20546..0000000 --- a/playwright-report/data/f5ba77f8b20c452bd2c31718b44897276882a465.md +++ /dev/null @@ -1,280 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: enchanting.spec.ts >> Enchanting Flow >> can navigate through all 3 enchant stages -- Location: e2e/enchanting.spec.ts:88:7 - -# Error details - -``` -Error: locator.click: Error: strict mode violation: getByRole('button') resolved to 6 elements: - 1) aka getByRole('button', { name: 'Elemental Mana (1)' }) - 3) aka getByRole('button', { name: 'Open Next.js Dev Tools' }) - -Call log: - - waiting for getByRole('button') - -``` - -# Page snapshot - -```yaml -- generic [ref=e1]: - - generic [ref=e2]: - - banner [ref=e3]: - - generic [ref=e4]: - - heading "MANA LOOP" [level=1] [ref=e5] - - generic [ref=e7]: - - generic [ref=e8]: - - generic [ref=e9]: Day 1 - - generic [ref=e10]: 00:55 - - generic [ref=e11]: - - generic [ref=e12]: "0" - - generic [ref=e13]: Insight - - main [ref=e14]: - - generic [ref=e15]: - - generic [ref=e17]: - - generic [ref=e18]: - - generic [ref=e19]: - - generic [ref=e20]: "11" - - generic [ref=e21]: / 100 - - generic [ref=e22]: - - text: +2.2 mana/hr - - generic [ref=e23]: (1.1x med) - - progressbar [ref=e24] - - button "Gather +1 Mana" [ref=e26]: - - img - - text: Gather +1 Mana - - generic [ref=e27]: - - button "Elemental Mana (1)" [ref=e28]: - - generic [ref=e29]: Elemental Mana (1) - - img [ref=e30] - - generic [ref=e33]: - - generic [ref=e34]: - - generic [ref=e35]: 🔗 - - generic [ref=e36]: Transference - - generic [ref=e39]: 0/10 - - button "Climb the Spire" [ref=e40]: - - img - - text: Climb the Spire - - generic [ref=e42]: - - generic [ref=e43]: - - img [ref=e44] - - generic [ref=e46]: Current Activity - - generic [ref=e47]: Meditating - - generic [ref=e48]: - - generic [ref=e49]: "1" - - generic [ref=e50]: "2" - - generic [ref=e51]: "3" - - generic [ref=e52]: "4" - - generic [ref=e53]: "5" - - generic [ref=e54]: "6" - - generic [ref=e55]: "7" - - generic [ref=e56]: "8" - - generic [ref=e57]: "9" - - generic [ref=e58]: "10" - - generic [ref=e59]: "11" - - generic [ref=e60]: "12" - - generic [ref=e61]: "13" - - generic [ref=e62]: "14" - - generic [ref=e63]: "15" - - generic [ref=e64]: "16" - - generic [ref=e65]: "17" - - generic [ref=e66]: "18" - - generic [ref=e67]: "19" - - generic [ref=e68]: "20" - - generic [ref=e69]: "21" - - generic [ref=e70]: "22" - - generic [ref=e71]: "23" - - generic [ref=e72]: "24" - - generic [ref=e73]: "25" - - generic [ref=e74]: "26" - - generic [ref=e75]: "27" - - generic [ref=e76]: "28" - - generic [ref=e77]: "29" - - generic [ref=e78]: "30" - - generic [ref=e80]: - - tablist [ref=e81]: - - tab "⚔️ Spire" [ref=e82] - - tab "✨ Attune" [ref=e83] - - tab "🗿 Golems" [ref=e84] - - tab "📚 Skills" [ref=e85] - - tab "🔮 Spells" [ref=e86] - - tab "🛡️ Gear" [ref=e87] - - tab "🔧 Craft" [active] [selected] [ref=e88] - - tab "💎 Loot" [ref=e89] - - tab "🏆 Achieve" [ref=e90] - - tab "📊 Stats" [ref=e91] - - tab "🐛 Debug" [ref=e92] - - tab "📖 Grimoire" [ref=e93] - - tabpanel "🔧 Craft" [ref=e94]: - - generic [ref=e95]: - - generic [ref=e97]: - - button "Fabricate" [ref=e98]: - - img - - text: Fabricate - - button "Enchant" [ref=e99]: - - img - - text: Enchant - - generic [ref=e100]: - - generic [ref=e101]: - - generic [ref=e103]: - - img [ref=e104] - - text: Available Blueprints - - generic [ref=e113]: - - img [ref=e114] - - paragraph [ref=e118]: No blueprints discovered yet. - - paragraph [ref=e119]: Defeat guardians to find blueprints! - - generic [ref=e120]: - - generic [ref=e122]: - - img [ref=e123] - - text: Materials (0) - - generic [ref=e131]: - - img [ref=e132] - - paragraph [ref=e134]: No materials collected yet. - - paragraph [ref=e135]: Defeat floors to gather materials! - - region "Notifications (F8)": - - list - - region "Notifications (F8)": - - list - - button "Open Next.js Dev Tools" [ref=e141] [cursor=pointer]: - - img [ref=e142] - - alert [ref=e145] -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | /** - 4 | * E2E tests for the 3-step enchantment flow: - 5 | * Design → Prepare → Apply - 6 | * - 7 | * These tests validate the core crafting loop works end-to-end. - 8 | */ - 9 | - 10 | test.describe('Enchanting Flow', () => { - 11 | /** - 12 | * Before each test, ensure we start with a clean state. - 13 | * The game persists state in localStorage, so we clear it. - 14 | */ - 15 | test.beforeEach(async ({ page }) => { - 16 | await page.goto('/'); - 17 | // Clear game state to ensure a fresh start - 18 | await page.evaluate(() => { - 19 | Object.keys(localStorage) - 20 | .filter((k) => k.startsWith('mana-loop-')) - 21 | .forEach((k) => localStorage.removeItem(k)); - 22 | }); - 23 | await page.reload(); - 24 | // Wait for the game to initialize - 25 | await page.waitForLoadState('networkidle'); - 26 | }); - 27 | - 28 | test('can navigate to Crafting tab', async ({ page }) => { - 29 | // The tab bar contains a "Craft" tab - 30 | const craftTab = page.getByRole('tab', { name: /🔧 Craft/ }); - 31 | await expect(craftTab).toBeVisible(); - 32 | await craftTab.click(); - 33 | - 34 | // Verify we're on the crafting tab by checking for sub-tabs - 35 | const fabricateBtn = page.getByRole('button', { hasText: 'Fabricate' }); - 36 | const enchantBtn = page.getByRole('button', { hasText: 'Enchant' }); - 37 | await expect(fabricateBtn).toBeVisible(); - 38 | await expect(enchantBtn).toBeVisible(); - 39 | }); - 40 | - 41 | test('can switch to Enchant sub-tab and see design UI', async ({ page }) => { - 42 | await page.goto('/'); - 43 | await page.evaluate(() => { - 44 | Object.keys(localStorage) - 45 | .filter((k) => k.startsWith('mana-loop-')) - 46 | .forEach((k) => localStorage.removeItem(k)); - 47 | }); - 48 | await page.reload(); - 49 | await page.waitForLoadState('networkidle'); - 50 | - 51 | // Navigate to Crafting tab - 52 | await page.getByRole('tab', { name: /🔧 Craft/ }).click(); - 53 | - 54 | // Click Enchant sub-tab - 55 | const enchantBtn = page.getByRole('button', { hasText: 'Enchant' }); - 56 | await enchantBtn.click(); - 57 | - 58 | // Should see the design stage UI - 59 | const designBtn = page.getByRole('button', { hasText: 'Design' }); - 60 | const prepareBtn = page.getByRole('button', { hasText: 'Prepare' }); - 61 | const applyBtn = page.getByRole('button', { hasText: 'Apply' }); - 62 | await expect(designBtn).toBeVisible(); - 63 | await expect(prepareBtn).toBeVisible(); - 64 | await expect(applyBtn).toBeVisible(); - 65 | }); - 66 | - 67 | test('can select equipment type and effect in Design stage', async ({ page }) => { - 68 | await page.goto('/'); - 69 | await page.evaluate(() => { - 70 | Object.keys(localStorage) - 71 | .filter((k) => k.startsWith('mana-loop-')) - 72 | .forEach((k) => localStorage.removeItem(k)); - 73 | }); - 74 | await page.reload(); - 75 | await page.waitForLoadState('networkidle'); - 76 | - 77 | // Navigate to Crafting > Enchant > Design - 78 | await page.getByRole('tab', { name: /🔧 Craft/ }).click(); - 79 | await page.getByRole('button', { hasText: 'Enchant' }).click(); - 80 | - 81 | // The design section should show effect selectors once an equipment type is chosen - 82 | // Look for any element matching equipment type buttons and effect-related content - 83 | const equipmentButtons = page.locator('button:has-text("Basic Staff"), button:has-text("Apprentice Wand"), button:has-text("Oak Staff"), button:has-text("Crystal Wand")'); - 84 | const count = await equipmentButtons.count(); - 85 | expect(count).toBeGreaterThan(0); - 86 | }); - 87 | - 88 | test('can navigate through all 3 enchant stages', async ({ page }) => { - 89 | await page.goto('/'); - 90 | await page.evaluate(() => { - 91 | Object.keys(localStorage) - 92 | .filter((k) => k.startsWith('mana-loop-')) - 93 | .forEach((k) => localStorage.removeItem(k)); - 94 | }); - 95 | await page.reload(); - 96 | await page.waitForLoadState('networkidle'); - 97 | - 98 | // Navigate to Crafting > Enchant - 99 | await page.getByRole('tab', { name: /🔧 Craft/ }).click(); -> 100 | await page.getByRole('button', { hasText: 'Enchant' }).click(); - | ^ Error: locator.click: Error: strict mode violation: getByRole('button') resolved to 6 elements: - 101 | - 102 | // Verify Design stage is active - 103 | const designBtn = page.getByRole('button', { hasText: 'Design' }); - 104 | await expect(designBtn).toBeVisible(); - 105 | - 106 | // Switch to Prepare stage - 107 | const prepareBtn = page.getByRole('button', { hasText: 'Prepare' }); - 108 | await prepareBtn.click(); - 109 | - 110 | // Should see preparation UI - 111 | const prepareHeading = page.locator('text=Select Equipment to Prepare'); - 112 | await expect(prepareHeading).toBeVisible({ timeout: 5000 }); - 113 | - 114 | // Switch to Apply stage - 115 | const applyBtn = page.getByRole('button', { hasText: 'Apply' }); - 116 | await applyBtn.click(); - 117 | - 118 | // Should see application UI - 119 | const applyHeading = page.locator('text=Select Equipment & Design'); - 120 | await expect(applyHeading).toBeVisible({ timeout: 5000 }); - 121 | }); - 122 | }); -``` \ No newline at end of file diff --git a/playwright-report/index.html b/playwright-report/index.html index 71ca158..3acc56f 100644 --- a/playwright-report/index.html +++ b/playwright-report/index.html @@ -7,7 +7,7 @@ Playwright Test Report - -
- \ No newline at end of file + \ No newline at end of file