# 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: canvas-mount.spec.js >> Canvas mount >> login and show lesson canvas within 5s - Location: src/__tests__/e2e/canvas-mount.spec.js:37:3 # Error details ``` TimeoutError: page.waitForURL: Timeout 15000ms exceeded. =========================== logs =========================== waiting for navigation until "load" ============================================================ ``` # Page snapshot ```yaml - generic [ref=e3]: - banner [ref=e4]: - generic [ref=e5]: - generic [ref=e6] [cursor=pointer]: - img [ref=e7] - text: Classroom Copilot - button "menu" [ref=e10] [cursor=pointer]: - img [ref=e11] - main [ref=e13]: - generic [ref=e14]: - heading "ClassroomCopilot.ai" [level=1] [ref=e15] - heading "Login" [level=4] [ref=e16] - generic [ref=e18]: - generic [ref=e19]: - generic: - text: Email - generic: "*" - generic [ref=e20]: - textbox "Email" [ref=e21] - group: - generic: Email * - generic [ref=e22]: - generic: - text: Password - generic: "*" - generic [ref=e23]: - textbox "Password" [ref=e24] - group: - generic: Password * - button "Login" [ref=e25] [cursor=pointer]: Login ``` # Test source ```ts 1 | import { test, expect, request } from '@playwright/test'; 2 | 3 | const BASE = process.env.PLAYWRIGHT_BASE_URL || 'http://192.168.0.251:13000'; 4 | const TEST_EMAIL = process.env.VITE_TEST_TEACHER_EMAIL; 5 | const TEST_PASSWORD = process.env.VITE_TEST_TEACHER_PASSWORD; 6 | 7 | async function serverReachable() { 8 | try { 9 | const ctx = await request.newContext(); 10 | const res = await ctx.get(BASE, { timeout: 3000 }).catch(() => null); 11 | await ctx.dispose(); 12 | return Boolean(res); 13 | } catch { 14 | return false; 15 | } 16 | } 17 | 18 | async function login(page) { 19 | await page.goto(`${BASE}/login`); 20 | await page.getByLabel('Email').fill(TEST_EMAIL); 21 | await page.getByLabel('Password').fill(TEST_PASSWORD); 22 | await page.getByRole('button', { name: 'Login' }).click(); > 23 | await page.waitForURL((url) => /\/dashboard|\/node\//.test(url.pathname), { timeout: 15000 }); | ^ TimeoutError: page.waitForURL: Timeout 15000ms exceeded. 24 | } 25 | 26 | function skipIfUnconfigured() { 27 | if (!TEST_EMAIL || !TEST_PASSWORD) test.skip(true, 'VITE_TEST_TEACHER_EMAIL / VITE_TEST_TEACHER_PASSWORD not set'); 28 | } 29 | 30 | test.describe('Canvas mount', () => { 31 | test.beforeAll(async () => { 32 | skipIfUnconfigured(); 33 | const reachable = await serverReachable(); 34 | test.skip(!reachable, `Dev server not reachable at ${BASE}`); 35 | }); 36 | 37 | test('login and show lesson canvas within 5s', async ({ page }) => { 38 | await login(page); 39 | const canvasVisible = await page 40 | .locator('[data-testid="tldraw-canvas"], .tl-container') 41 | .waitFor({ timeout: 10000 }) 42 | .then(() => true) 43 | .catch(() => false); 44 | expect(canvasVisible).toBe(true); 45 | await expect(page.locator('[class*="error-state"], [data-testid="error"]').first()).toBeHidden({ timeout: 2000 }).catch(() => {}); 46 | }); 47 | }); 48 | ```