loop-benchmarking

Controlled experiments across agentic coding configurations. Same task, one variable, what actually works.
git clone https://git.shiptheloop.com/loop-benchmarking.git
Log | Files | Refs | README

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 });

Impressum · Datenschutz