전 저장소 리팩터링과 SSOT 정비
This commit is contained in:
parent
14ecbd4e7d
commit
3dfddcac6f
173 changed files with 19679 additions and 6952 deletions
|
|
@ -184,6 +184,13 @@ function teacherReviewResponse(sessionId: string) {
|
|||
};
|
||||
}
|
||||
|
||||
/** 리뷰는 모든 폭에서 가로 탭으로 영역을 전환한다. */
|
||||
async function openReviewTab(page: Page, label: "축어록" | "피드백" | "워크시트") {
|
||||
const tabs = page.locator(".sr-tabs");
|
||||
await tabs.waitFor({ state: "attached", timeout: 20_000 });
|
||||
await tabs.locator("button", { hasText: label }).click();
|
||||
}
|
||||
|
||||
test.describe("teacher console", () => {
|
||||
test("lets a teacher approve a pending persona review from the console @single-run", async ({
|
||||
page,
|
||||
|
|
@ -317,7 +324,7 @@ test.describe("teacher console", () => {
|
|||
});
|
||||
});
|
||||
|
||||
await page.goto("/teach/personas");
|
||||
await page.goto("/teach/personas?view=personas");
|
||||
const row = page.locator('[data-approved-persona-row="true"]').filter({ hasText: "P1" });
|
||||
await expect(row).toBeVisible();
|
||||
await row.getByRole("button", { name: "수정" }).click();
|
||||
|
|
@ -373,7 +380,7 @@ test.describe("teacher console", () => {
|
|||
});
|
||||
});
|
||||
|
||||
await page.goto("/teach/personas");
|
||||
await page.goto("/teach/personas?view=personas&mode=create&step=edit");
|
||||
await page.getByLabel("표시 이름").fill("항목형 페르소나");
|
||||
|
||||
await page.getByRole("tab", { name: "임상" }).click();
|
||||
|
|
@ -387,7 +394,7 @@ test.describe("teacher console", () => {
|
|||
const forbidden = page.locator(".ps-list-field").filter({ hasText: "상담자 금기" });
|
||||
await forbidden.getByRole("textbox", { name: "상담자 금기 1", exact: true }).fill("네가 예민한 거라고 단정");
|
||||
|
||||
await page.getByRole("button", { name: "초안 저장" }).click();
|
||||
await page.getByRole("button", { name: "중간 저장" }).click();
|
||||
await expect(page.getByText("P1 v1 초안을 저장했습니다.")).toBeVisible();
|
||||
|
||||
expect(savedPayload).toBeTruthy();
|
||||
|
|
@ -408,7 +415,7 @@ test.describe("teacher console", () => {
|
|||
return route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify([]) });
|
||||
});
|
||||
|
||||
await page.goto("/teach/personas");
|
||||
await page.goto("/teach/personas?view=personas&mode=create&step=edit");
|
||||
await page.getByLabel("표시 이름").fill("프롬프트 검토 페르소나");
|
||||
await page.getByRole("tab", { name: "임상" }).click();
|
||||
const automaticThoughts = page.locator(".ps-list-field").filter({ hasText: "자동사고" });
|
||||
|
|
@ -484,7 +491,7 @@ test.describe("teacher console", () => {
|
|||
});
|
||||
});
|
||||
|
||||
await page.goto("/teach/personas");
|
||||
await page.goto("/teach/personas?view=personas");
|
||||
const row = page.locator('[data-approved-persona-row="true"]').filter({ hasText: "P7" });
|
||||
await expect(row).toBeVisible();
|
||||
await expectVisibleButtonsFit(page, ".ps-approved-row__actions .vg-btn", "approved persona actions");
|
||||
|
|
@ -492,11 +499,104 @@ test.describe("teacher console", () => {
|
|||
await row.getByRole("button", { name: "삭제" }).click();
|
||||
|
||||
await expect(page.getByText("P7 페르소나를 보관 처리했습니다.")).toBeVisible();
|
||||
await expect(page.getByText("공개 목록 없음")).toBeVisible();
|
||||
await expect(page.getByText("조건에 맞는 페르소나 없음")).toBeVisible();
|
||||
expect(archived).toBeTruthy();
|
||||
await expectNoHorizontalOverflow(page);
|
||||
});
|
||||
|
||||
test("navigates persona dashboard, catalog, table, detail, and review queue @single-run", async ({ page }) => {
|
||||
const approvedPersona = {
|
||||
persona_id: "00000000-0000-0000-0000-000000000721",
|
||||
code: "P1",
|
||||
version: 3,
|
||||
status: "approved",
|
||||
display_name: "서연(가명) · 고2 · 우울/자살사고",
|
||||
difficulty: "hard",
|
||||
theory_target: ["humanistic"],
|
||||
demographics: { age_band: "F-teen" },
|
||||
presenting_summary: "최근 무기력과 관계 단절을 호소합니다.",
|
||||
source: "database",
|
||||
degraded: false,
|
||||
voice_preset: null,
|
||||
};
|
||||
const reviewPersona = {
|
||||
persona_id: "00000000-0000-0000-0000-000000000722",
|
||||
code: "P12",
|
||||
version: 1,
|
||||
status: "review",
|
||||
display_name: "직장 적응 훈련 페르소나",
|
||||
difficulty: "moderate",
|
||||
theory_target: ["cbt"],
|
||||
source_provenance: "교수자 작성 초안",
|
||||
is_synthetic: true,
|
||||
created_at: "2026-07-15T08:00:00Z",
|
||||
approved_at: null,
|
||||
};
|
||||
await signInAsTeacher(page);
|
||||
await page.route("**/api/personas", (route) => {
|
||||
if (route.request().method() !== "GET") return route.fallback();
|
||||
return route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify([approvedPersona]) });
|
||||
});
|
||||
await page.route("**/api/personas/review", (route) =>
|
||||
route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify([reviewPersona]) }),
|
||||
);
|
||||
await page.route("**/api/teacher/dashboard", (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
source: "database",
|
||||
cohort_label: "E2E cohort",
|
||||
total_learners: 1,
|
||||
active_sessions: 1,
|
||||
ended_sessions: 1,
|
||||
learner_growth: [{
|
||||
learner_id: "00000000-0000-0000-0000-000000000723",
|
||||
learner_label: "학습자 A",
|
||||
sessions: 2,
|
||||
ended_sessions: 1,
|
||||
latest_at: "2026-07-15T08:20:00Z",
|
||||
avg_score: 0.75,
|
||||
avg_rapport: 0.4,
|
||||
trend: "up",
|
||||
points: [{ session_id: "persona-session-1", session_no: 1, persona_code: "P1", stage: "탐색", started_at: "2026-07-15T08:00:00Z", ended_at: "2026-07-15T08:20:00Z", score: 0.75, rapport: 0.4, technique_count: 3, watch_count: 0 }],
|
||||
}],
|
||||
safety_alerts: [],
|
||||
pending_reviews: [],
|
||||
recent_sessions: [{ session_id: "persona-session-2", learner_id: "00000000-0000-0000-0000-000000000723", learner_label: "학습자 A", persona_code: "P1", persona_name: approvedPersona.display_name, session_no: 2, status: "active", stage: "탐색", turn_count: 4, learner_turn_count: 2, client_turn_count: 2, started_at: "2026-07-15T08:30:00Z", ended_at: null }],
|
||||
message: "페르소나 운영 fixture",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
await page.goto("/teach/personas");
|
||||
await expect(page.getByRole("heading", { name: "페르소나 운영" })).toBeVisible();
|
||||
await expect(page.getByText("상담 수행 점수")).toBeVisible();
|
||||
|
||||
await page.getByRole("button", { name: /카탈로그/ }).first().click();
|
||||
await expect(page.getByRole("heading", { name: "페르소나 카탈로그" })).toBeVisible();
|
||||
await expect(page.getByText("학습자가 회기를 시작할 때 선택하는 승인본 모음입니다.")).toBeVisible();
|
||||
|
||||
await page.getByRole("button", { name: /페르소나/ }).first().click();
|
||||
await expect(page.getByRole("button", { name: "새로운 페르소나 만들기" })).toBeVisible();
|
||||
const approvedRow = page.locator('[data-approved-persona-row="true"]').filter({ hasText: "P1" });
|
||||
await expect(approvedRow).toBeVisible();
|
||||
await approvedRow.getByRole("button", { name: "상세" }).click();
|
||||
await expect(page.getByRole("tab", { name: "소개·정보" })).toBeVisible();
|
||||
await page.getByRole("tab", { name: "학습 현황" }).click();
|
||||
await expect(page.getByText("75%").first()).toBeVisible();
|
||||
await page.getByRole("button", { name: "목록으로" }).click();
|
||||
await page.getByRole("tab", { name: /검수 현황/ }).click();
|
||||
await expect(page.getByText("직장 적응 훈련 페르소나")).toBeVisible();
|
||||
await page.getByRole("button", { name: "새로운 페르소나 만들기" }).click();
|
||||
await expect(page.getByRole("navigation", { name: "페르소나 작성 단계" })).toBeVisible();
|
||||
await page.getByRole("button", { name: /설정 임상·회기·말투 조정/ }).click();
|
||||
await expect(page.getByRole("tab", { name: "개요" })).toBeVisible();
|
||||
await page.getByRole("button", { name: "이전" }).click();
|
||||
await expect(page).toHaveURL(/step=generate/);
|
||||
await expectNoHorizontalOverflow(page);
|
||||
});
|
||||
|
||||
test("renders real server sessions from server-owned rows", async ({ page }) => {
|
||||
const sessionId = await createEndedLearnerSession(page);
|
||||
await signInAsTeacher(page);
|
||||
|
|
@ -768,9 +868,11 @@ test.describe("teacher console", () => {
|
|||
|
||||
await expect(page).toHaveURL(new RegExp(`/teach/session/${sessionId}/review$`));
|
||||
await expect(page.getByText("교수자 검토 화면", { exact: true })).toBeVisible();
|
||||
await expect(page.getByText("교수자 검토", { exact: true })).toBeVisible();
|
||||
await openReviewTab(page, "워크시트");
|
||||
await expect(page.getByText("축어록 자동 초안 읽기 전용")).toBeVisible();
|
||||
await expect(page.getByText("검토 전용")).toBeVisible();
|
||||
await openReviewTab(page, "피드백");
|
||||
await expect(page.getByText("교수자 검토", { exact: true })).toBeVisible();
|
||||
await page.getByLabel("검토 메모").fill("다음 회기에서 감정 반영을 먼저 확인");
|
||||
await page.getByRole("button", { name: "검토 완료" }).click();
|
||||
await expect(page.getByText("완료 시각 2026-06-27T10:00:00Z")).toBeVisible();
|
||||
|
|
@ -953,6 +1055,7 @@ test.describe("teacher console", () => {
|
|||
await expect(page).toHaveURL(new RegExp(`/teach/session/${activeSession.session_id}/review$`));
|
||||
await expect(page.getByText("교수자 검토 화면", { exact: true })).toBeVisible();
|
||||
await expect(page.getByText("진행 중", { exact: true })).toBeVisible();
|
||||
await openReviewTab(page, "피드백");
|
||||
await expect(page.getByText("평가 대기", { exact: true })).toBeVisible();
|
||||
await expectNoHorizontalOverflow(page);
|
||||
});
|
||||
|
|
@ -1022,6 +1125,7 @@ test.describe("teacher console", () => {
|
|||
await expect(page).toHaveURL(new RegExp(`/teach/session/${sessionId}/review$`));
|
||||
await expect(page.getByText("교수자 검토 화면", { exact: true })).toBeVisible();
|
||||
await expect(page.getByText("진행 중", { exact: true })).toBeVisible();
|
||||
await openReviewTab(page, "워크시트");
|
||||
await expect(page.getByText("검토 전용")).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: /^저장/ })).toHaveCount(0);
|
||||
await expectNoHorizontalOverflow(page);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue