functional.spec.ts (4580B)
1 import { test, expect, type Page } from "@playwright/test"; 2 3 // Helper: find the game page. Tries common entry points. 4 async function loadGame(page: Page) { 5 // Try common entry points in order 6 const candidates = [ 7 "index.html", 8 "dist/index.html", 9 "public/index.html", 10 "build/index.html", 11 ]; 12 13 for (const candidate of candidates) { 14 try { 15 const response = await page.goto(`http://localhost:__PORT__/${candidate}`, { 16 timeout: 5000, 17 }); 18 if (response && response.ok()) return; 19 } catch { 20 continue; 21 } 22 } 23 24 // Last resort: try root 25 await page.goto(`http://localhost:__PORT__/`, { timeout: 5000 }); 26 } 27 28 test.describe("Tetris - Functional Tests", () => { 29 test.beforeEach(async ({ page }) => { 30 await loadGame(page); 31 // Wait for the page to be interactive 32 await page.waitForLoadState("domcontentloaded"); 33 }); 34 35 test("page loads without errors", async ({ page }) => { 36 const errors: string[] = []; 37 page.on("pageerror", (err) => errors.push(err.message)); 38 await page.waitForTimeout(2000); 39 expect(errors).toEqual([]); 40 }); 41 42 test("game board is visible", async ({ page }) => { 43 // Look for a canvas or a grid-like container 44 const canvas = page.locator("canvas"); 45 const gridContainer = page.locator( 46 '[class*="board"], [class*="grid"], [class*="game"], [id*="board"], [id*="grid"], [id*="game"]' 47 ); 48 49 const canvasCount = await canvas.count(); 50 const gridCount = await gridContainer.count(); 51 52 expect(canvasCount + gridCount).toBeGreaterThan(0); 53 }); 54 55 test("score display exists", async ({ page }) => { 56 // Look for score text on the page 57 const pageText = await page.textContent("body"); 58 const hasScore = 59 pageText !== null && 60 (pageText.toLowerCase().includes("score") || 61 pageText.toLowerCase().includes("points") || 62 /\b0\b/.test(pageText)); 63 expect(hasScore).toBe(true); 64 }); 65 66 test("keyboard input is responsive", async ({ page }) => { 67 // Press arrow keys and check that no errors occur 68 const errors: string[] = []; 69 page.on("pageerror", (err) => errors.push(err.message)); 70 71 await page.keyboard.press("ArrowLeft"); 72 await page.waitForTimeout(100); 73 await page.keyboard.press("ArrowRight"); 74 await page.waitForTimeout(100); 75 await page.keyboard.press("ArrowDown"); 76 await page.waitForTimeout(100); 77 await page.keyboard.press("ArrowUp"); 78 await page.waitForTimeout(100); 79 await page.keyboard.press("Space"); 80 await page.waitForTimeout(100); 81 82 expect(errors).toEqual([]); 83 }); 84 85 test("game shows changing state over time", async ({ page }) => { 86 // Take a screenshot, wait, take another, compare 87 // If the game is running, pixels should change (pieces falling) 88 const screenshot1 = await page.screenshot(); 89 await page.waitForTimeout(3000); 90 const screenshot2 = await page.screenshot(); 91 92 // Screenshots should differ if the game is animating 93 const buf1 = Buffer.from(screenshot1); 94 const buf2 = Buffer.from(screenshot2); 95 expect(buf1.equals(buf2)).toBe(false); 96 }); 97 98 test("arrow down accelerates piece", async ({ page }) => { 99 // Rapidly press down and verify no crash 100 const errors: string[] = []; 101 page.on("pageerror", (err) => errors.push(err.message)); 102 103 for (let i = 0; i < 20; i++) { 104 await page.keyboard.press("ArrowDown"); 105 await page.waitForTimeout(50); 106 } 107 108 expect(errors).toEqual([]); 109 }); 110 111 test("game continues running for 10 seconds without crash", async ({ 112 page, 113 }) => { 114 const errors: string[] = []; 115 page.on("pageerror", (err) => errors.push(err.message)); 116 117 // Simulate some gameplay 118 for (let i = 0; i < 10; i++) { 119 await page.keyboard.press("ArrowLeft"); 120 await page.waitForTimeout(200); 121 await page.keyboard.press("ArrowRight"); 122 await page.waitForTimeout(200); 123 await page.keyboard.press("ArrowDown"); 124 await page.waitForTimeout(200); 125 await page.keyboard.press("ArrowUp"); 126 await page.waitForTimeout(200); 127 await page.keyboard.press("Space"); 128 await page.waitForTimeout(200); 129 } 130 131 expect(errors).toEqual([]); 132 }); 133 134 test("page has no accessibility violations for basic structure", async ({ 135 page, 136 }) => { 137 // Basic a11y: page has a title or heading 138 const title = await page.title(); 139 const h1 = await page.locator("h1").count(); 140 const heading = await page 141 .locator("h1, h2, h3, [role='heading']") 142 .count(); 143 144 expect(title.length + h1 + heading).toBeGreaterThan(0); 145 }); 146 });