개선관리 요구사항과 Google 로그인을 완료

This commit is contained in:
Yun Chan 2026-08-28 16:07:09 +09:00
parent cc0a15b7c6
commit 2a39636163
112 changed files with 10166 additions and 527 deletions

View file

@ -130,6 +130,14 @@ const S_END = {
const BASE_SESSIONS = [S_ACT, S_END];
const TRAINING_EXPOSURE_CONTRACT = {
version: "training-exposure-dominant-share.v1",
minimum_completed_sessions: 4,
attention_threshold: 0.75,
definition:
"종료 회기의 페르소나별 최다 노출 비중을 보여 주는 투명한 훈련 노출 지표이며, 공정성 평가나 임상진단이 아닙니다.",
};
// 평가가 아직 없는 학습자 — 리뷰 0건, 평가 0회, 라포 평균 null.
const DASH_NO_EVAL = {
message: "학습 현황 요약",
@ -185,6 +193,16 @@ const DASH_NO_EVAL = {
},
],
achievements: [],
training_exposure: {
...TRAINING_EXPOSURE_CONTRACT,
status: "insufficient",
label: "판정 근거 부족",
completed_sessions: 1,
dominant_persona_code: "P2",
dominant_persona_name: "김도윤",
dominant_sessions: 1,
dominant_share: 1,
},
};
// 라포 게이지 검증용 — 서로 다른 rapport_percent(81/47)를 가진 진행 기록.
@ -240,6 +258,16 @@ const EMPTY_DASHBOARD = {
recent_feedback: [],
persona_progress: [],
achievements: [],
training_exposure: {
...TRAINING_EXPOSURE_CONTRACT,
status: "insufficient",
label: "판정 근거 부족",
completed_sessions: 0,
dominant_persona_code: null,
dominant_persona_name: null,
dominant_sessions: 0,
dominant_share: null,
},
};
// 스포트라이트(진행 중 회기) 상세 — 리캡 fetch 오류 상태를 만들지 않기 위해 고정.
@ -309,6 +337,36 @@ const DASH_BULK = {
learner_turns: 150,
client_turns: 150,
},
training_exposure: {
...TRAINING_EXPOSURE_CONTRACT,
status: "balanced",
label: "균형",
completed_sessions: 50,
dominant_persona_code: "P1",
dominant_persona_name: "성하늘",
dominant_sessions: 25,
dominant_share: 0.5,
},
};
const DASH_ATTENTION = {
...DASH_NO_EVAL,
overview: {
...DASH_NO_EVAL.overview,
total_sessions: 4,
active_sessions: 0,
completed_sessions: 4,
},
training_exposure: {
...TRAINING_EXPOSURE_CONTRACT,
status: "attention",
label: "훈련 집중 주의",
completed_sessions: 4,
dominant_persona_code: "P1",
dominant_persona_name: "성하늘",
dominant_sessions: 3,
dominant_share: 0.75,
},
};
interface FixtureOptions {
@ -366,6 +424,68 @@ test.describe("uc learner home dashboard", () => {
await expect(rapportCard).toContainText("성하늘 · 1회");
});
test("종료 회기 0회는 훈련 노출 판정 근거 부족으로 표시한다", async ({ page }) => {
await installFixtures(page, {
sessions: [],
dashboard: EMPTY_DASHBOARD,
detail: null,
});
await page.goto("/learn");
await page.getByRole("tab", { name: "성장 지표" }).click();
const exposure = page.locator("#lh-panel-growth .lh-insight-list").filter({
hasText: "훈련 노출 점검",
});
await expect(exposure).toContainText("판정 근거 부족");
await expect(exposure).toContainText("종료 회기 0/4회");
await expect(exposure).toContainText(
"공정성 평가나 임상진단이 아닌 투명한 훈련 노출 지표입니다.",
);
});
test("종료 회기 1회는 비중과 무관하게 훈련 노출 판정을 보류한다", async ({ page }) => {
await installFixtures(page);
await page.goto("/learn");
await page.getByRole("tab", { name: "성장 지표" }).click();
const exposure = page.locator("#lh-panel-growth .lh-insight-list").filter({
hasText: "훈련 노출 점검",
});
await expect(exposure).toContainText("판정 근거 부족");
await expect(exposure).toContainText("종료 회기 1/4회");
});
test("종료 회기 50회의 동률 노출은 균형으로 표시한다", async ({ page }) => {
await installFixtures(page, {
sessions: BULK_SESSIONS,
dashboard: DASH_BULK,
detail: null,
});
await page.goto("/learn");
await page.getByRole("tab", { name: "성장 지표" }).click();
const exposure = page.locator("#lh-panel-growth .lh-insight-list").filter({
hasText: "훈련 노출 점검",
});
await expect(exposure).toContainText("균형");
await expect(exposure).toContainText("성하늘 25/50회 · 최다 노출 50%");
});
test("종료 회기 4회 중 3회 노출은 훈련 집중 주의로 표시한다", async ({ page }) => {
await installFixtures(page, { dashboard: DASH_ATTENTION });
await page.goto("/learn");
await page.getByRole("tab", { name: "성장 지표" }).click();
const exposure = page.locator("#lh-panel-growth .lh-insight-list").filter({
hasText: "훈련 노출 점검",
});
await expect(exposure).toContainText("훈련 집중 주의");
await expect(exposure).toContainText("성하늘 3/4회 · 최다 노출 75%");
await expect(exposure).toContainText(
"공정성 평가나 임상진단이 아닌 투명한 훈련 노출 지표입니다.",
);
});
// usecase: 학습자가 '리뷰 대기' 메트릭 카드를 눌러 기록 화면으로 이동한다
test("리뷰 대기 메트릭 카드를 누르면 기록 화면으로 이동한다", async ({ page }) => {
await installFixtures(page);