// frontend/playwright/connectors.spec.ts — phase-13 외부 연동(연결 관리) E2E // 연결 상태 표시 → 동기화 → 해제 → 임포트(미지원 graceful) + a11y. import AxeBuilder from "@axe-core/playwright"; import { expect, test } from "@playwright/test"; import { resetSeed } from "./fixtures/seed-reset"; test.describe.configure({ mode: "serial" }); test.beforeEach(async ({ page }) => { await resetSeed(); // 설정 페이지의 '연결' 탭에서 연결 관리 (라이프 페이지 제거됨) await page.goto("/settings?tab=connect"); await expect(page.getByRole("heading", { name: "연결된 데이터 소스" })).toBeVisible(); }); test("10개 커넥터 카드 + 8/10 연결됨 + 미연결 카드 표시", async ({ page }) => { // phase-16+: 메일/일정 mock 제거 → 금융·건강·지식 10개만 await expect(page.locator(".cn-grid .cn-card")).toHaveCount(10); await expect(page.locator(".cn-count")).toHaveText("8/10 연결됨"); // Google Fit / KB증권 은 미연결(off + '연결 안 됨') const fit = page.locator(".cn-card", { hasText: "Google Fit" }); await expect(fit).toHaveClass(/off/); await expect(fit.locator(".cs-badge")).toContainText("연결 안 됨"); // 우리카드는 연결됨(동기화/해제 액션) const woori = page.locator(".cn-card", { hasText: "우리카드" }); await expect(woori.getByRole("button", { name: /동기화/ })).toBeVisible(); await expect(woori.getByRole("button", { name: /해제/ })).toBeVisible(); }); test("동기화 → 토스트 노출", async ({ page }) => { const woori = page.locator(".cn-card", { hasText: "우리카드" }).first(); await woori.getByRole("button", { name: /동기화/ }).click(); await expect(page.locator(".au-toast")).toContainText("새 항목"); }); test("연결 해제 → 미연결 상태로 전환", async ({ page }) => { const toss = page.locator(".cn-card", { hasText: "토스" }); await expect(toss).not.toHaveClass(/off/); await toss.getByRole("button", { name: /해제/ }).click(); await expect(page.locator(".au-toast")).toContainText("연결을 해제"); await expect(toss).toHaveClass(/off/); await expect(toss.locator(".cs-badge")).toContainText("연결 안 됨"); await expect(toss.getByRole("button", { name: /연결$/ })).toBeVisible(); // [연결] 으로 전환 }); test("파일 가져오기 — 미지원 소스(mock)는 안내 토스트", async ({ page }) => { // 기본 env(mock)에서 mock 커넥터는 import 미지원 → 400 → graceful 토스트 const kakao = page.locator(".cn-card", { hasText: "카카오뱅크" }); await kakao.locator('input[type="file"]').setInputFiles({ name: "statement.csv", mimeType: "text/csv", buffer: Buffer.from("거래일,가맹점,금액\n2026-06-08,쿠팡,23900\n"), }); await expect(page.locator(".au-toast")).toContainText("가져오기를 지원하지 않아요"); }); test("연결 모듈 a11y — 위반 0건", async ({ page }) => { const r = await new AxeBuilder({ page }) .withTags(["wcag2a", "wcag2aa", "wcag21aa"]) .disableRules(["color-contrast"]) .analyze(); expect(r.violations, JSON.stringify(r.violations, null, 2)).toEqual([]); });