회기 무발화 0턴 분리, 자기예측 락 불변식 및 TDD 회귀 검증 완료
Some checks failed
API contract / OpenAPI type drift (push) Failing after 3m27s
Some checks failed
API contract / OpenAPI type drift (push) Failing after 3m27s
This commit is contained in:
parent
a479db7a5a
commit
a0311c5957
100 changed files with 4884 additions and 11210 deletions
|
|
@ -1,4 +1,3 @@
|
|||
import path from "node:path";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { parseVoicePracticeContext } from "../src/lib/voicePracticeContext";
|
||||
import { expectNoHorizontalOverflow } from "./support";
|
||||
|
|
@ -424,6 +423,7 @@ async function routeMvpApi(page: Page, options: RouteMvpOptions = {}) {
|
|||
growthPoints: [{ title: "탐색 확장", body: "다음 턴에서 구체 상황을 더 묻습니다." }],
|
||||
nextLine: "그 말을 꺼내는 것도 쉽지 않았을 것 같아요.",
|
||||
clientFeedback: clientReply,
|
||||
learnerFeedbackEnabled: true,
|
||||
audioUrl: null,
|
||||
pdfExportUrl: null,
|
||||
degraded: false,
|
||||
|
|
@ -773,7 +773,6 @@ async function installVoiceLifecycleFixture(page: Page) {
|
|||
releaseEmptyFinal() {
|
||||
const socket = sockets[2];
|
||||
if (!socket || socket.readyState !== FixtureWebSocket.OPEN) return;
|
||||
socket.emitJson({ type: "state", state: "thinking" });
|
||||
socket.emitJson({ type: "transcript", text: "", final: true });
|
||||
socket.emitJson({ type: "state", state: "idle" });
|
||||
},
|
||||
|
|
@ -877,11 +876,12 @@ async function installVoiceLifecycleFixture(page: Page) {
|
|||
}
|
||||
if (payload.type !== "audio_end") return;
|
||||
window.setTimeout(() => {
|
||||
if (this.connectionNumber === 1) {
|
||||
this.emitJson({ type: "transcript", text: firstInterim, final: false });
|
||||
} else if (this.connectionNumber === 2) {
|
||||
this.emitJson({ type: "transcript", text: secondInterim, final: false });
|
||||
}
|
||||
if (this.connectionNumber > 2) return;
|
||||
this.emitJson({
|
||||
type: "transcript",
|
||||
text: this.connectionNumber === 1 ? firstInterim : secondInterim,
|
||||
final: false,
|
||||
});
|
||||
this.emitJson({
|
||||
type: "eot",
|
||||
ready: false,
|
||||
|
|
@ -1116,7 +1116,7 @@ test.describe("P1 MVP core loop", () => {
|
|||
await expect(
|
||||
page.getByRole("heading", { name: "원본 회기 정보를 확인할 수 없습니다." }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "새 회기 시작" })).toBeDisabled();
|
||||
await expect(page.getByRole("button", { name: "완전히 새로 시작" })).toBeDisabled();
|
||||
await expectNoHorizontalOverflow(page);
|
||||
|
||||
await page.goto(`/learn/session/P1?${malformedQuery}`);
|
||||
|
|
@ -1153,11 +1153,7 @@ test.describe("P1 MVP core loop", () => {
|
|||
await expect(page.getByRole("heading", { name: "첫 발화 전에 내 기준을 잠급니다" })).toBeVisible();
|
||||
await expect(page.getByLabel("학습자 발화 입력")).toBeDisabled();
|
||||
await page.screenshot({
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
"../../docs/ops/evidence",
|
||||
`g1-alliance-pre-${testInfo.project.name}-2026-08-07.png`,
|
||||
),
|
||||
path: testInfo.outputPath("g1-alliance-pre.png"),
|
||||
fullPage: true,
|
||||
});
|
||||
await selectAllianceScore(page, "목표", "3 보통이다");
|
||||
|
|
@ -1176,11 +1172,7 @@ test.describe("P1 MVP core loop", () => {
|
|||
await expect(page.getByLabel("학습자 발화 입력")).toBeEnabled();
|
||||
await page.getByRole("button", { name: "30초 점검" }).click();
|
||||
await page.screenshot({
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
"../../docs/ops/evidence",
|
||||
`g1-alliance-mid-${testInfo.project.name}-2026-08-07.png`,
|
||||
),
|
||||
path: testInfo.outputPath("g1-alliance-mid.png"),
|
||||
fullPage: true,
|
||||
});
|
||||
await selectAllianceScore(page, "목표", "4 대체로 그렇다");
|
||||
|
|
@ -1210,7 +1202,8 @@ test.describe("P1 MVP core loop", () => {
|
|||
page.getByRole("button", { name: "회기 시작" }),
|
||||
diagnostics.join("\n") || (await page.locator("#root").innerText().catch(() => "")),
|
||||
).toBeVisible();
|
||||
await page.getByRole("button", { name: /CBT/ }).click();
|
||||
await page.locator("details.sx-prestart__settings > summary").click();
|
||||
await page.getByRole("button", { name: /^CBT/ }).click();
|
||||
await page.getByRole("button", { name: "회기 시작" }).click();
|
||||
|
||||
await expect(page.locator(".sx-page.sx-page--active")).toBeVisible();
|
||||
|
|
@ -1253,7 +1246,7 @@ test.describe("P1 MVP core loop", () => {
|
|||
|
||||
await page.goto("/learn/session/P1");
|
||||
await page.getByRole("button", { name: "회기 시작" }).click();
|
||||
await page.getByRole("button", { name: "코칭" }).click();
|
||||
await page.getByRole("button", { name: /^코칭 모드, 남은 기회 \d+개$/ }).click();
|
||||
await page.getByLabel("학습자 발화 입력").fill(learnerText);
|
||||
await page.getByRole("button", { name: "보내기" }).click();
|
||||
|
||||
|
|
@ -1292,7 +1285,7 @@ test.describe("P1 MVP core loop", () => {
|
|||
|
||||
await page.goto("/learn/session/P1");
|
||||
await page.getByRole("button", { name: "회기 시작" }).click();
|
||||
await page.getByRole("button", { name: "코칭" }).click();
|
||||
await page.getByRole("button", { name: /^코칭 모드, 남은 기회 \d+개$/ }).click();
|
||||
await page.getByLabel("학습자 발화 입력").fill(learnerText);
|
||||
await page.getByRole("button", { name: "보내기" }).click();
|
||||
|
||||
|
|
@ -1317,7 +1310,7 @@ test.describe("P1 MVP core loop", () => {
|
|||
|
||||
await page.goto("/learn/session/P1");
|
||||
await page.getByRole("button", { name: "회기 시작" }).click();
|
||||
await page.getByRole("button", { name: "코칭" }).click();
|
||||
await page.getByRole("button", { name: /^코칭 모드, 남은 기회 \d+개$/ }).click();
|
||||
await page.getByLabel("학습자 발화 입력").fill(learnerText);
|
||||
await page.getByRole("button", { name: "보내기" }).click();
|
||||
|
||||
|
|
@ -1341,7 +1334,7 @@ test.describe("P1 MVP core loop", () => {
|
|||
|
||||
await page.goto("/learn/session/P1");
|
||||
await page.getByRole("button", { name: "회기 시작" }).click();
|
||||
await page.getByRole("button", { name: "코칭" }).click();
|
||||
await page.getByRole("button", { name: /^코칭 모드, 남은 기회 \d+개$/ }).click();
|
||||
await page.getByLabel("학습자 발화 입력").fill(learnerText);
|
||||
await page.getByRole("button", { name: "보내기" }).click();
|
||||
|
||||
|
|
@ -1367,7 +1360,7 @@ test.describe("P1 MVP core loop", () => {
|
|||
|
||||
await page.goto("/learn/session/P1");
|
||||
await page.getByRole("button", { name: "회기 시작" }).click();
|
||||
await page.getByRole("button", { name: "코칭" }).click();
|
||||
await page.getByRole("button", { name: /^코칭 모드, 남은 기회 \d+개$/ }).click();
|
||||
await page.getByLabel("학습자 발화 입력").fill(learnerText);
|
||||
await page.getByRole("button", { name: "보내기" }).click();
|
||||
|
||||
|
|
@ -1505,6 +1498,14 @@ test.describe("P1 MVP core loop", () => {
|
|||
|
||||
await page.getByRole("button", { name: "음성 다시 연결" }).click();
|
||||
await page.getByRole("button", { name: "발화 보내기" }).click();
|
||||
await expect.poll(async () =>
|
||||
page.evaluate(
|
||||
() =>
|
||||
window.__voiceLifecycleFixture?.sent.filter((payload) =>
|
||||
payload.includes('"audio_end"'),
|
||||
).length ?? 0,
|
||||
),
|
||||
).toBe(3);
|
||||
await page.evaluate(() => window.__voiceLifecycleFixture?.releaseEmptyFinal());
|
||||
await expect(transcriptLog.locator(".sx-utt.is-learner")).toHaveCount(2);
|
||||
await expect(page.locator(".sx-mic-block__h")).toContainText("음성을 인식하지 못했습니다");
|
||||
|
|
@ -1519,7 +1520,7 @@ test.describe("P1 MVP core loop", () => {
|
|||
|
||||
await page.goto("/learn/session/P1");
|
||||
await page.getByRole("button", { name: "회기 시작" }).click();
|
||||
await page.getByRole("button", { name: "코칭" }).click();
|
||||
await page.getByRole("button", { name: /^코칭 모드, 남은 기회 \d+개$/ }).click();
|
||||
const micButton = page.getByRole("button", { name: "마이크 켜기" });
|
||||
await expect(micButton).toBeEnabled();
|
||||
await micButton.click();
|
||||
|
|
@ -1548,7 +1549,7 @@ test.describe("P1 MVP core loop", () => {
|
|||
|
||||
await page.goto("/learn/session/P1");
|
||||
await page.getByRole("button", { name: "회기 시작" }).click();
|
||||
await page.getByRole("button", { name: "코칭" }).click();
|
||||
await page.getByRole("button", { name: /^코칭 모드, 남은 기회 \d+개$/ }).click();
|
||||
await page.getByLabel("학습자 발화 입력").fill(learnerText);
|
||||
await page.getByRole("button", { name: "보내기" }).click();
|
||||
|
||||
|
|
@ -1583,7 +1584,7 @@ test.describe("P1 MVP core loop", () => {
|
|||
|
||||
await page.goto("/learn/session/P1");
|
||||
await page.getByRole("button", { name: "회기 시작" }).click();
|
||||
await page.getByRole("button", { name: "코칭" }).click();
|
||||
await page.getByRole("button", { name: /^코칭 모드, 남은 기회 \d+개$/ }).click();
|
||||
await page.getByLabel("학습자 발화 입력").fill("저 지금 자살하고 싶어요. 도와주세요.");
|
||||
await page.getByRole("button", { name: "보내기" }).click();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue