회기 무발화 0턴 분리, 자기예측 락 불변식 및 TDD 회귀 검증 완료
Some checks failed
API contract / OpenAPI type drift (push) Failing after 3m27s

This commit is contained in:
Yun Chan 2026-09-08 23:28:06 +09:00
parent a479db7a5a
commit a0311c5957
100 changed files with 4884 additions and 11210 deletions

View file

@ -1,5 +1,5 @@
import path from "node:path";
import { expect, test, type Page, type Route } from "@playwright/test";
import { expect, test, type Page, type Route, type TestInfo } from "@playwright/test";
import type { DeliberatePracticeReadModel } from "../src/pages/session-review/deliberatePracticeApi";
import { parsePracticeLaunchIntent } from "../src/lib/practiceLaunchIntent";
import {
@ -893,17 +893,14 @@ async function expectMobileActiveVisualIntegrity(page: Page) {
expect(result.latestReplyVisible, diagnostic).toBe(true);
}
async function capture(page: Page, projectName: string, stage: string) {
async function capture(page: Page, testInfo: TestInfo, stage: string) {
if (process.env.E2E_SKIP_EVIDENCE_CAPTURE === "1") return;
const safeProject = projectName.replace(/[^a-z0-9-]+/gi, "-").toLowerCase();
const evidenceDirectory = process.env.E2E_EVIDENCE_DIR
? path.resolve(process.env.E2E_EVIDENCE_DIR)
: path.resolve(process.cwd(), "../../docs/ops/evidence");
const safeProject = testInfo.project.name.replace(/[^a-z0-9-]+/gi, "-").toLowerCase();
const evidenceDirectory = process.env.E2E_EVIDENCE_DIR;
await page.screenshot({
path: path.resolve(
evidenceDirectory,
`self-directed-loop-${stage}-${safeProject}-2026-08-07.png`,
),
path: evidenceDirectory
? path.resolve(evidenceDirectory, `self-directed-loop-${stage}-${safeProject}.png`)
: testInfo.outputPath(`self-directed-loop-${stage}.png`),
fullPage: true,
animations: "disabled",
});
@ -1030,11 +1027,11 @@ test.describe("학습자 자기주도 전체 루프 — 실제 src UI / route fi
);
await expectMinimumHitTargets(page, ".lh-tabs [role='tab']", "320px 학습 홈 탭");
await expectNoHorizontalOverflow(page);
await capture(page, testInfo.project.name, "home-320x568");
await capture(page, testInfo, "home-320x568");
await page.setViewportSize({ width: 390, height: 844 });
await page.evaluate(() => new Promise(requestAnimationFrame));
}
await capture(page, testInfo.project.name, "home");
await capture(page, testInfo, "home");
await primaryAction.click();
await expect(page).toHaveURL(/\/learn\/practice$/);
@ -1095,11 +1092,11 @@ test.describe("학습자 자기주도 전체 루프 — 실제 src UI / route fi
await expectMobileActiveVisualIntegrity(page);
await expectCoachNudgeContrast(page);
await expectNoHorizontalOverflow(page);
await capture(page, testInfo.project.name, viewport.stage);
await capture(page, testInfo, viewport.stage);
}
await page.setViewportSize({ width: 390, height: 844 });
} else {
await capture(page, testInfo.project.name, "active-session");
await capture(page, testInfo, "active-session");
}
await page.getByRole("button", { name: "회기 종료" }).click();
@ -1173,12 +1170,12 @@ test.describe("학습자 자기주도 전체 루프 — 실제 src UI / route fi
"320px 처방 연습 시작 행동",
);
await expectNoHorizontalOverflow(page);
await capture(page, testInfo.project.name, "review-prescription-320x568");
await capture(page, testInfo, "review-prescription-320x568");
await page.setViewportSize({ width: 390, height: 844 });
await page.evaluate(() => new Promise(requestAnimationFrame));
}
await practiceCard.scrollIntoViewIfNeeded();
await capture(page, testInfo.project.name, "review-prescription");
await capture(page, testInfo, "review-prescription");
const launch = practiceCard.getByRole("link", {
name: "이 처방으로 연습 시작",
@ -1222,7 +1219,7 @@ test.describe("학습자 자기주도 전체 루프 — 실제 src UI / route fi
await page.evaluate(() => new Promise(requestAnimationFrame));
await expectMinimumHitTargets(page, ".lh-actions .vg-btn", "320px 재연습 대상 행동");
await expectNoHorizontalOverflow(page);
await capture(page, testInfo.project.name, "repractice-home-320x568");
await capture(page, testInfo, "repractice-home-320x568");
await page.setViewportSize({ width: 390, height: 844 });
await page.evaluate(() => new Promise(requestAnimationFrame));
}
@ -1264,11 +1261,11 @@ test.describe("학습자 자기주도 전체 루프 — 실제 src UI / route fi
"320px 처방 재연습 시작 행동",
);
await expectNoHorizontalOverflow(page);
await capture(page, testInfo.project.name, "repractice-prestart-320x568");
await capture(page, testInfo, "repractice-prestart-320x568");
await page.setViewportSize({ width: 390, height: 844 });
await page.evaluate(() => new Promise(requestAnimationFrame));
}
await capture(page, testInfo.project.name, "repractice-prestart");
await capture(page, testInfo, "repractice-prestart");
await page.getByRole("button", { name: "회기 시작" }).click();
await expect(page).toHaveURL(
@ -1368,12 +1365,12 @@ test.describe("학습자 자기주도 전체 루프 — 실제 src UI / route fi
await expect(observation).not.toContainText(RETRY_SESSION_ID.slice(0, 8));
await expect(observation).not.toContainText("[NAME]");
await expectNoHorizontalOverflow(page);
await capture(page, testInfo.project.name, "repractice-observation-320x568");
await capture(page, testInfo, "repractice-observation-320x568");
await page.setViewportSize({ width: 390, height: 844 });
await page.evaluate(() => new Promise(requestAnimationFrame));
}
await observation.scrollIntoViewIfNeeded();
await capture(page, testInfo.project.name, "repractice-observation");
await capture(page, testInfo, "repractice-observation");
await page.reload();
observation = page.locator(".dp-runtime-observation");