한신대 피드백 개선팩 반영

This commit is contained in:
Yun Chan 2026-07-03 19:53:14 +09:00
parent 5a9c110c11
commit 6b6241f468
25 changed files with 1247 additions and 94 deletions

View file

@ -46,6 +46,31 @@ async function routeSupervisorCapableLearner(page: Page) {
});
}
async function routeLearnerReviewUser(page: Page) {
await page.route("**/api/auth/me", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({
user_id: "00000000-0000-0000-0000-000000000101",
email: "learner@hs.ac.kr",
role: "learner",
display_name: "E2E Learner",
admin_access: false,
super_admin: false,
account_status: "approved",
approval_required: false,
cohort_ids: [],
consent_at: 1782820000,
onboarding_completed_at: 1782820001,
nickname: "E2E Learner",
self_introduction: "",
avatar_url: "",
}),
});
});
}
function isReviewResponse(sessionId: string) {
return (response: Response) => {
const url = new URL(response.url());
@ -201,6 +226,19 @@ test.describe("session review", () => {
expect(reviewAttempts).toBeGreaterThanOrEqual(2);
});
test("shows supervisor evaluation scope boundaries", async ({ page }) => {
await routeSupervisorCapableLearner(page);
const sessionId = "supervisor-evaluation-scope";
await routeFilledSessionReview(page, sessionId);
await page.goto(`/teach/session/${sessionId}/review`);
await expect(page.getByText("AI 평가 범위", { exact: true })).toBeVisible();
await expect(page.getByText("현재 구현", { exact: true })).toBeVisible();
await expect(page.getByText(/fast-loop.*deep-loop/)).toBeVisible();
await expect(page.getByText("임상팀 확정 필요", { exact: true })).toBeVisible();
});
test("refreshes an ended review from evaluation pending to ready", async ({ page }) => {
await routeSupervisorCapableLearner(page);
await routePrepostMeasures(page);
@ -376,6 +414,10 @@ test.describe("session review", () => {
await page.goto(`/teach/session/${sessionId}/review`);
await expect(page.getByText("평가 실패")).toBeVisible();
await expect(
page.getByText("저장된 축어록은 확인했지만 deep-loop 평가 AI 산출물을 표시하지 못했습니다. AI 평가 재시도가 필요합니다."),
).toBeVisible();
await expect(page.getByText("engine_error: evaluator timeout")).toHaveCount(0);
await expect(page.getByRole("button", { name: "AI 평가 재시도" })).toBeVisible();
await expect(page.getByRole("button", { name: "검토 완료" })).toBeDisabled();
@ -438,7 +480,10 @@ test.describe("session review", () => {
await page.getByRole("button", { name: "AI 평가 재시도" }).click();
await expect(page.getByText("engine_error: retry timeout")).toBeVisible();
await expect(
page.getByText("AI 평가 재시도를 완료하지 못했습니다. 잠시 뒤 다시 실행해 주세요."),
).toBeVisible();
await expect(page.getByText("engine_error: retry timeout")).toHaveCount(0);
await expect(page.getByText("평가 실패")).toBeVisible();
await expect(page.getByRole("button", { name: "AI 평가 재시도" })).toBeEnabled();
expect(reevaluateRequests).toBe(1);
@ -538,6 +583,50 @@ test.describe("session review", () => {
await expectNoHorizontalOverflow(page);
});
test("keeps masked API transcript while naturalizing transcript placeholders", async ({
page,
}) => {
await routeLearnerReviewUser(page);
await routePrepostMeasures(page);
const sessionId = "review-placeholder-display";
const response = filledReviewResponse(sessionId);
response.turns[0] = {
...response.turns[0],
text: "저는 [NAME]이고 [ORG]에서 상담받고 있어요.",
};
if (!response.caseWorksheet) {
throw new Error("filled review fixture must include a worksheet");
}
response.caseWorksheet.sections[0].items[0].evidence = [
{
turnId: "t1",
speaker: "client",
quote: "저는 [NAME]이고 [ORG]에서 상담받고 있어요.",
},
];
await page.route(`**/api/sessions/${sessionId}/review`, async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(response),
});
});
const reviewResponsePromise = page.waitForResponse(isReviewResponse(sessionId));
await page.goto(`/learn/session/${sessionId}/review`);
const reviewResponse = await reviewResponsePromise;
await expectResponseOk(reviewResponse);
const apiReview = await reviewResponse.json();
expect(apiReview.turns[0].text).toContain("[NAME]");
const transcript = page.locator(".sr-card--transcript");
await expect(transcript).toContainText(
"저는 익명 내담자이고 소속 기관에서 상담받고 있어요.",
);
await expect(transcript).not.toContainText("[NAME]");
await expect(page.locator(".sr-ws-evidence").filter({ hasText: "[NAME]" })).toBeVisible();
});
test("stores learner pre/post pilot evidence without effect claims", async ({ page }) => {
await signInAsLearner(page);
await routeFilledSessionReview(page);