test(age-dwarves): Update error context documentation for climate simulator test failures

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-04-01 06:23:51 -07:00
parent c6df28f35c
commit bb01a5114d

View file

@ -1,189 +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: simulator.spec.ts >> Climate simulator >> scenario simulation reaches > 0% progress
- Location: e2e/simulator.spec.ts:68:3
# Error details
```
Error: expect(locator).toBeVisible() failed
Locator: locator('text=/Turn \\d+/').or(locator('canvas').first())
Expected: visible
Timeout: 60000ms
Error: element(s) not found
Call log:
- Expect "toBeVisible" with timeout 60000ms
- waiting for locator('text=/Turn \\d+/').or(locator('canvas').first())
```
# Page snapshot
```yaml
- generic [ref=e5]:
- generic [ref=e6]:
- button "◉ Earth ▾" [ref=e8] [cursor=pointer]:
- generic [ref=e9]: ◉
- generic [ref=e10]: Earth
- generic [ref=e11]: ▾
- tablist "Simulation category" [ref=e13]:
- tab "Environment" [selected] [ref=e14] [cursor=pointer]
- tab "Life" [ref=e15] [cursor=pointer]
- navigation "Climate scenario groups" [ref=e16]:
- button "Lifeless Worlds Hadean Earth" [ref=e18] [cursor=pointer]:
- generic [ref=e20]:
- generic [ref=e21]: Lifeless Worlds
- generic [ref=e22]: Hadean Earth
- generic [ref=e23]: ▾
- button "Eco Disaster" [ref=e25] [cursor=pointer]:
- generic [ref=e28]: Eco Disaster
- generic [ref=e29]: ▾
- button "ET Disaster" [ref=e31] [cursor=pointer]:
- generic [ref=e34]: ET Disaster
- generic [ref=e35]: ▾
- paragraph [ref=e37]: "Planet at formation: extreme heat, no liquid water, no biology. Deep Earth injection and radiative cooling slowly build the first ocean."
- generic [ref=e38]:
- generic [ref=e39]:
- heading "Hadean Earth" [level=3] [ref=e40]
- generic [ref=e41]:
- generic [ref=e42]: Primordial atmosphere
- generic [ref=e43]: No biology
- generic [ref=e44]:
- generic [ref=e45]:
- generic [ref=e46]: ○
- generic [ref=e47]:
- generic [ref=e48]: World generation
- generic [ref=e49]: Continents, oceans, rivers, wind patterns
- generic [ref=e50]:
- generic [ref=e51]: ○
- generic [ref=e52]:
- generic [ref=e53]: Scenario simulation
- generic [ref=e54]: 50 turns
- generic [ref=e55]:
- generic [ref=e56]: ○
- generic [ref=e57]:
- generic [ref=e58]: Playback buffer
- generic [ref=e59]: Encoding frames for smooth playback
- generic [ref=e60]: 59.1s
```
# Test source
```ts
1 | import { test, expect } from '@playwright/test'
2 |
3 | /**
4 | * Climate simulator E2E tests.
5 | *
6 | * These tests require the dev server running on port 5800:
7 | * pnpm dev
8 | *
9 | * Run with:
10 | * npx playwright test
11 | *
12 | * All tests use ?totalTurns=50&buffer=0 so the simulation completes in seconds
13 | * rather than waiting for the default 2000 turns + 10s prebuffer.
14 | *
15 | * DOM structure of the loading overlay (from live snapshot):
16 | * step-row
17 | * icon-div ("✓" | "◉" | "○")
18 | * text-div
19 | * name-div ("World generation" | "Scenario simulation" | "Playback buffer")
20 | * desc-div (description or turn progress)
21 | * [pct-div] ("4%" — only when simulation is active)
22 | *
23 | * Note: "Playback buffer" never shows ✓ — it's either active (◉) or pending (○).
24 | * When bufferReady becomes true the entire loading overlay is replaced by the canvas.
25 | *
26 | * Selectors use :text-is("✓") to precisely match the icon element (exact text),
27 | * then navigate to its parent (the step row) and filter by step name.
28 | *
29 | * Tests 2 and 3 use .or(canvas) because with a warm dev-server cache and only
30 | * 50 turns, the simulation may complete before the loading overlay is ever observed.
31 | * Both the transient loading state AND the final canvas prove the phase succeeded.
32 | */
33 |
34 | const BASE_URL = '/climate/simulation?noGui=true&skip=welcome&totalTurns=50&buffer=0'
35 |
36 | test.describe('Climate simulator', () => {
37 | test('starts without console errors', async ({ page }) => {
38 | const errors: string[] = []
39 | page.on('console', msg => {
40 | if (msg.type() === 'error') errors.push(msg.text())
41 | })
42 | page.on('pageerror', err => errors.push(err.message))
43 |
44 | await page.goto(BASE_URL)
45 | await page.waitForTimeout(3000)
46 |
47 | expect(errors, `Console errors:\n${errors.join('\n')}`).toHaveLength(0)
48 | })
49 |
50 | test('world generation completes', async ({ page }) => {
51 | const errors: string[] = []
52 | page.on('console', msg => {
53 | if (msg.type() === 'error') errors.push(msg.text())
54 | })
55 | page.on('pageerror', err => errors.push(err.message))
56 |
57 | await page.goto(BASE_URL)
58 |
59 | // Accept either the transient ✓ on "World generation" (visible during loading)
60 | // OR the canvas (visible once simulation finishes) — both prove world gen ran.
61 | const worldGenCheckmark = page.locator(':text-is("✓")').locator('..').filter({ hasText: 'World generation' })
62 | const canvas = page.locator('canvas').first()
63 | await expect(worldGenCheckmark.or(canvas)).toBeVisible({ timeout: 30_000 })
64 |
65 | expect(errors, `Console errors:\n${errors.join('\n')}`).toHaveLength(0)
66 | })
67 |
68 | test('scenario simulation reaches > 0% progress', async ({ page }) => {
69 | const errors: string[] = []
70 | page.on('console', msg => {
71 | if (msg.type() === 'error') errors.push(msg.text())
72 | })
73 | page.on('pageerror', err => errors.push(err.message))
74 |
75 | await page.goto(BASE_URL)
76 |
77 | // Accept either the transient "Turn N / M" counter in the loading overlay
78 | // OR the completed canvas — both prove the scenario simulation ran.
79 | const turnCounter = page.locator('text=/Turn \\d+/')
80 | const canvas = page.locator('canvas').first()
> 81 | await expect(turnCounter.or(canvas)).toBeVisible({ timeout: 60_000 })
| ^ Error: expect(locator).toBeVisible() failed
82 |
83 | expect(errors, `Console errors:\n${errors.join('\n')}`).toHaveLength(0)
84 | })
85 |
86 | test('simulation completes and canvas renders', async ({ page }) => {
87 | const errors: string[] = []
88 | page.on('console', msg => {
89 | if (msg.type() === 'error') errors.push(msg.text())
90 | })
91 | page.on('pageerror', err => errors.push(err.message))
92 |
93 | await page.goto(BASE_URL)
94 |
95 | // When bufferReady becomes true the loading overlay is replaced by the WebGL canvas.
96 | // canvas is only rendered once a frame is delivered — wait for it to be visible.
97 | const canvas = page.locator('canvas').first()
98 | await expect(canvas).toBeVisible({ timeout: 60_000 })
99 | const box = await canvas.boundingBox()
100 | expect(box, 'canvas has no bounding box').not.toBeNull()
101 | expect(box!.width, 'canvas width').toBeGreaterThan(0)
102 | expect(box!.height, 'canvas height').toBeGreaterThan(0)
103 |
104 | expect(errors, `Console errors:\n${errors.join('\n')}`).toHaveLength(0)
105 | })
106 | })
107 |
```