# 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: review-fixes.spec.ts >> 리뷰: 실제 진행 막대는 현재 문항의 1/3·2/3·3/3을 채운다 - Location: tests/browser/review-fixes.spec.ts:38:1 # Error details ``` Error: expect(received).toBeCloseTo(expected) Expected: 0.3333333333333333 Received: 0 Expected precision: 2 Expected difference: < 0.005 Received difference: 0.3333333333333333 ``` ``` Error: expect(locator).toHaveAccessibleName(expected) failed Locator: getByRole('progressbar') Expected: "현재 문항 진행률" Received: "전체 진행률" Timeout: 1000ms Call log: - Expect "soft toHaveAccessibleName" getByRole('progressbar') with timeout 1000ms - waiting for getByRole('progressbar') 11 × locator resolved to - unexpected value "전체 진행률" ``` ```yaml - progressbar "전체 진행률" ``` ``` Error: expect(received).toBeCloseTo(expected) Expected: 0.6666666666666666 Received: 0.3333333333333333 Expected precision: 2 Expected difference: < 0.005 Received difference: 0.3333333333333333 ``` ``` Error: expect(locator).toHaveAccessibleName(expected) failed Locator: getByRole('progressbar') Expected: "현재 문항 진행률" Received: "전체 진행률" Timeout: 1000ms Call log: - Expect "soft toHaveAccessibleName" getByRole('progressbar') with timeout 1000ms - waiting for getByRole('progressbar') 11 × locator resolved to - unexpected value "전체 진행률" ``` ```yaml - progressbar "전체 진행률" ``` ``` Error: expect(received).toBeCloseTo(expected) Expected: 1 Received: 0.6666666666666666 Expected precision: 2 Expected difference: < 0.005 Received difference: 0.33333333333333337 ``` ``` Error: expect(locator).toHaveAccessibleName(expected) failed Locator: getByRole('progressbar') Expected: "현재 문항 진행률" Received: "전체 진행률" Timeout: 1000ms Call log: - Expect "soft toHaveAccessibleName" getByRole('progressbar') with timeout 1000ms - waiting for getByRole('progressbar') 11 × locator resolved to - unexpected value "전체 진행률" ``` ```yaml - progressbar "전체 진행률" ``` ``` Error: expect(received).toEqual(expected) // deep equality - Expected - 1 + Received + 4 - Array [] + Array [ + "split: 만나요", + "split: 최대", + ] ``` # Test source ```ts 1 | import { expect, test, type Page } from "@playwright/test"; 2 | import { WORDS } from "../../src/content"; 3 | 4 | // 글자별 스타일 추측 대신 실제 Range 줄 상자를 비교해 어절 내부 분리를 검출한다. 5 | async function copyLayout(page: Page, selector: string) { 6 | return page.locator(selector).evaluateAll((elements) => elements.flatMap((element) => { 7 | const problems: string[] = []; 8 | const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT); 9 | while (walker.nextNode()) { 10 | const node = walker.currentNode; 11 | for (const match of (node.textContent ?? "").matchAll(/[가-힣]+/g)) { 12 | const range = document.createRange(); 13 | range.setStart(node, match.index!); 14 | range.setEnd(node, match.index! + match[0].length); 15 | if (new Set(Array.from(range.getClientRects(), (r) => r.y)).size > 1) 16 | problems.push(`split: ${match[0]}`); 17 | } 18 | } 19 | if (element.scrollWidth > element.clientWidth) problems.push(`overflow: ${element.textContent}`); 20 | return problems; 21 | })); 22 | } 23 | 24 | test("리뷰: 한국어 모드·결과 어절은 320px와 360px에서도 갈라지거나 넘치지 않는다", async ({ page }, info) => { 25 | for (const width of [320, 360]) { 26 | await page.setViewportSize({ width, height: 800 }); 27 | await page.goto("/"); 28 | expect.soft(await copyLayout(page, ".mode-option small")).toEqual([]); 29 | await page.screenshot({ path: info.outputPath(`copy-${width}-home.png`), fullPage: true }); 30 | await page.getByRole("button", { name: "게임 시작 · 소리 듣기" }).click(); 31 | await page.getByRole("button", { name: "문제 풀이 종료" }).click(); 32 | expect.soft(await copyLayout(page, ".results-panel h2, .results-panel p, .stats span, .results-panel th, .results-panel td")).toEqual([]); 33 | expect.soft(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true); 34 | await page.screenshot({ path: info.outputPath(`copy-${width}-results.png`), fullPage: true }); 35 | } 36 | }); 37 | 38 | test("리뷰: 실제 진행 막대는 현재 문항의 1/3·2/3·3/3을 채운다", async ({ page }, info) => { 39 | let target = ""; 40 | page.on("request", (r) => { 41 | const path = new URL(r.url()).pathname; 42 | if (path.startsWith("/audio/") && path !== "/audio/intro.wav") target = path; 43 | }); 44 | await page.goto("/"); 45 | await page.getByRole("radio", { name: "보기에서 선택" }).check(); 46 | await page.getByRole("button", { name: "게임 시작 · 소리 듣기" }).click(); 47 | for (let index = 0; index < 3; index++) { 48 | const bar = page.getByRole("progressbar"); 49 | expect.soft(await bar.evaluate((el) => (el as HTMLProgressElement).position)).toBeCloseTo((index + 1) / 3); 50 | await expect.soft(bar).toHaveAccessibleName("현재 문항 진행률", { timeout: 1000 }); 51 | await bar.screenshot({ path: info.outputPath(`progress-${index + 1}.png`) }); 52 | if (index) await page.getByRole("button", { name: /문장 듣기/ }).click(); 53 | await expect(page.getByRole("group", { name: "답 선택" })).toBeVisible({ timeout: 30000 }); 54 | const answer = WORDS.find((w) => target.endsWith(`-${w.id}.wav`))!.answer; 55 | await page.getByRole("button", { name: new RegExp(answer) }).click(); 56 | await page.getByRole("button", { name: index === 2 ? /결과 보기/ : /다음 문제/ }).click(); 57 | } 58 | await page.screenshot({ path: info.outputPath("normal-results.png"), fullPage: true }); > 59 | expect(await copyLayout(page, ".results-panel h2, .results-panel p, .stats span")).toEqual([]); | ^ Error: expect(received).toEqual(expected) // deep equality 60 | }); 61 | ```