// frontend/playwright/multiuser.spec.ts — 두 사용자 데이터 격리 E2E (auth.config.ts) import { expect, test } from "@playwright/test"; async function login(page, email: string) { await page.goto("/login"); await page.getByPlaceholder("이메일").fill(email); await page.getByPlaceholder("비밀번호").fill("demo-1234"); await page.getByRole("button", { name: "로그인" }).click(); await expect(page).toHaveURL(/\/dashboard$/); } test("A가 만든 작업은 A만 보고 B는 빈 상태(격리)", async ({ browser }) => { // phase-16+: 작업 시드 제거 → A(지우)가 인박스 캡처→확인으로 작업을 만든다(실 Ollama). test.setTimeout(150_000); const ctxA = await browser.newContext(); const ctxB = await browser.newContext(); const pa = await ctxA.newPage(); const pb = await ctxB.newPage(); await login(pa, "jiwoo@lumi.co"); await pa.goto("/inbox"); const input = pa.getByLabel("인박스에 빠르게 캡처"); await input.fill("분기 리포트 초안 마무리"); await input.press("Enter"); const fresh = pa.locator(".sb-cap.fresh").first(); await expect(fresh.getByRole("button", { name: /좋아요, 그렇게 해줘/ })).toBeVisible({ timeout: 90_000, }); await fresh.getByRole("button", { name: /좋아요, 그렇게 해줘/ }).click(); // A 의 작업 페이지엔 작업 ≥1 (확인 반영까지 폴링) await expect .poll( async () => { await pa.goto("/tasks"); await pa.locator(".tree-row.folder", { hasText: "개인" }).click(); const p = await pa.locator(".kcard").count(); await pa.locator(".tree-row.folder", { hasText: "업무" }).click(); const w = await pa.locator(".kcard").count(); return p + w; }, { timeout: 30_000 }, ) .toBeGreaterThan(0); // B(현우)는 소유 작업 0 → 빈 보드(격리) await login(pb, "hyunwoo@lumi.co"); await pb.goto("/tasks"); await expect(pb.locator(".kcard")).toHaveCount(0); await ctxA.close(); await ctxB.close(); });