feat(coach): AI 코칭 버튼 및 버블 팝업 대화형 조언 기능 구현
This commit is contained in:
parent
8a34dd2afc
commit
70d7cb34a3
36 changed files with 4521 additions and 1264 deletions
|
|
@ -177,16 +177,43 @@ async function routeCoachVoiceSession(page: Page, options: CoachVoiceOptions = {
|
|||
);
|
||||
});
|
||||
|
||||
await page.route(`**/api/sessions/${SESSION_ID}/live-coach`, (route) =>
|
||||
route.fulfill(
|
||||
await page.route(`**/api/sessions/${SESSION_ID}/live-coach`, async (route) => {
|
||||
if (route.request().method() === "POST") {
|
||||
await route.fulfill(
|
||||
jsonRoute({
|
||||
status: "ready",
|
||||
tone: "pos",
|
||||
focus: "emotion",
|
||||
title: "공감과 탐색",
|
||||
message: "내담자의 답답한 마음에 먼저 공감하고 구체적인 장면을 열린 질문으로 물어보세요.",
|
||||
next_utterance: "학교 생각만 하면 답답하다고 했는데, 어떤 부분이 제일 마음에 걸려요?",
|
||||
rationale: "감정 타당화 후 구체 촉발 탐색",
|
||||
quota: { remaining: Math.max(0, coachRemaining - 1), max: coachMax },
|
||||
credit_events: [
|
||||
{
|
||||
event_id: "c-001",
|
||||
session_id: SESSION_ID,
|
||||
turn_seq: 1,
|
||||
event_type: "use",
|
||||
delta: -1,
|
||||
balance: Math.max(0, coachRemaining - 1),
|
||||
reason: "live coaching request",
|
||||
created_at: new Date().toISOString(),
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
await route.fulfill(
|
||||
jsonRoute({
|
||||
source: "database",
|
||||
quota: { remaining: coachRemaining, max: coachMax },
|
||||
credit_events: [],
|
||||
events: [],
|
||||
}),
|
||||
),
|
||||
);
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async function openSession(page: Page) {
|
||||
|
|
@ -419,4 +446,49 @@ test.describe("AI 코치·음성 UI — 고지·게이트·피드백 모드", ()
|
|||
const label = await mic.getAttribute("aria-label");
|
||||
expect(label ?? "").not.toBe("");
|
||||
});
|
||||
|
||||
// usecase: AI 코칭 버튼 클릭 시 버블 팝업이 노출되고 추천 칩 및 채팅 조언을 얻을 수 있다
|
||||
test("AI 코칭 버튼 클릭 시 버블 팝업이 뜨고 채팅 질문과 추천 발화 적용이 동작한다", async ({ page }) => {
|
||||
await routeCoachVoiceSession(page, { coachRemaining: 3, coachMax: 3 });
|
||||
await openSession(page);
|
||||
|
||||
const triggerBtn = page.getByRole("button", { name: /^AI 코칭, 남은 기회/ });
|
||||
await expect(triggerBtn).toBeVisible();
|
||||
await expect(triggerBtn).toContainText("3");
|
||||
|
||||
// 1. 버블 팝업 열기
|
||||
await triggerBtn.click({ force: true });
|
||||
const popup = page.getByRole("dialog", { name: "AI 슈퍼바이저 코칭 버블" });
|
||||
await expect(popup).toBeVisible();
|
||||
await expect(popup).toContainText("AI 슈퍼바이저 코칭");
|
||||
await expect(popup).toContainText("기회 3/3");
|
||||
|
||||
// 2. 추천 칩 확인 및 클릭
|
||||
const recommendChip = popup.getByRole("button", { name: "💡 다음 추천 발화" });
|
||||
await expect(recommendChip).toBeVisible();
|
||||
await recommendChip.dispatchEvent("click");
|
||||
|
||||
// 3. 조언 메시지 및 추천 발화 노출 확인
|
||||
await expect(popup.locator(".sx-coach-msg--learner")).toContainText("다음 추천 발화와 방향을 알려줘");
|
||||
await expect(popup.locator(".sx-coach-msg--coach")).toBeVisible();
|
||||
const insertBtn = popup.getByRole("button", { name: "발화창에 넣기" });
|
||||
await expect(insertBtn).toBeAttached();
|
||||
|
||||
// 4. [발화창에 넣기] 클릭 시 하단 발화 입력창에 텍스트 반영
|
||||
await insertBtn.dispatchEvent("click");
|
||||
const composer = page.getByPlaceholder("학습자 발화를 입력하세요.");
|
||||
await expect(composer).not.toHaveValue("");
|
||||
|
||||
// 5. 직접 질문 채팅 입력 및 전송
|
||||
const coachInput = popup.getByPlaceholder("코치에게 조언을 구해보세요... (Enter 전송)");
|
||||
await coachInput.fill("내담자의 저항을 부드럽게 다루려면 어떻게 하나요?");
|
||||
await popup.getByRole("button", { name: "코칭 질문 전송" }).dispatchEvent("click");
|
||||
|
||||
await expect(popup.locator(".sx-coach-msg--learner").last()).toContainText("내담자의 저항을 부드럽게 다루려면");
|
||||
await expect(popup.locator(".sx-coach-msg--coach").last()).toBeVisible();
|
||||
|
||||
// 6. 팝업 닫기 버튼 클릭 시 닫힘 확인
|
||||
await popup.getByRole("button", { name: "코칭 팝업 닫기" }).dispatchEvent("click");
|
||||
await expect(popup).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue