280 lines
13 KiB
Markdown
280 lines
13 KiB
Markdown
# 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) <button data-slot="button" class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive text-primary-foreground shad…>…</button> aka getByRole('button', { name: 'Gather +1 Mana' })
|
|
2) <button class="flex items-center justify-between w-full text-xs text-gray-400 hover:text-gray-300 mb-2">…</button> aka getByRole('button', { name: 'Elemental Mana (1)' })
|
|
3) <button data-slot="button" class="inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive text-primary-foreground shadow-xs hover…>…</button> aka getByRole('button', { name: 'Climb the Spire' })
|
|
4) <button data-slot="action-button" class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[var(--radius)] text-sm font-medium transition-all duration-100 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:ring-2 focus-visible:ring-[var(--border-focus)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--bg-base)] bg-[var(--interactive-primary)] text-white …>…</button> aka getByRole('button', { name: 'Fabricate' })
|
|
5) <button data-slot="action-button" class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[var(--radius)] text-sm font-medium transition-all duration-100 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:ring-2 focus-visible:ring-[var(--border-focus)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--bg-base)] bg-[var(--interactive-secondary)] text-[var…>…</button> aka getByRole('button', { name: 'Enchant' })
|
|
6) <button id="next-logo" aria-haspopup="menu" data-next-mark="true" aria-expanded="false" aria-label="Open Next.js Dev Tools" data-nextjs-dev-tools-button="true" aria-controls="nextjs-dev-tools-menu">…</button> 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 | });
|
|
``` |