import { test, expect, type Page } from "@playwright/test"; /** * Inject mock data into the Zustand focus store via the browser console. * * Since we run against the Vite dev server (no Tauri runtime), * we seed stores directly to test the real React rendering pipeline. */ async function seedFocusStore(page: Page): Promise { await page.evaluate(() => { // Access Zustand stores via their internal getState/setState // The stores are module-scoped singletons, accessible from window.__ZUSTAND_STORES__ // We expose them in development via a small shim in main.tsx const w = window as Record; const focusStore = w.__MC_FOCUS_STORE__ as { setState: (state: Record) => void; }; if (focusStore) { focusStore.setState({ current: { id: "mr_review:platform/core:847", title: "Fix authentication token refresh logic", type: "mr_review", project: "platform/core", url: "https://gitlab.com/platform/core/-/merge_requests/847", iid: 847, updatedAt: new Date().toISOString(), contextQuote: "The refresh token logic has a race condition", requestedBy: "johndoe", }, queue: [ { id: "issue:platform/core:42", title: "Users unable to login after password reset", type: "issue", project: "platform/core", url: "https://gitlab.com/platform/core/-/issues/42", iid: 42, updatedAt: new Date( Date.now() - 5 * 24 * 60 * 60 * 1000 ).toISOString(), contextQuote: null, requestedBy: null, }, { id: "mr_review:platform/api:101", title: "Add rate limiting to public endpoints", type: "mr_review", project: "platform/api", url: "https://gitlab.com/platform/api/-/merge_requests/101", iid: 101, updatedAt: new Date( Date.now() - 10 * 24 * 60 * 60 * 1000 ).toISOString(), contextQuote: null, requestedBy: "alice", }, ], isLoading: false, error: null, }); } }); } /** * Expose Zustand stores on the window object for E2E test seeding. * This is done by evaluating a script that patches the store modules. */ async function exposeStores(page: Page): Promise { await page.evaluate(() => { // The stores are ES module singletons. We need to wait for them // to be available. In dev mode, Vite's HMR keeps them accessible. // We use a polling approach to find them. return new Promise((resolve) => { const check = (): void => { const w = window as Record; if (w.__MC_FOCUS_STORE__) { resolve(); } else { setTimeout(check, 50); } }; check(); }); }); } test.describe("Mission Control E2E", () => { test.beforeEach(async ({ page }) => { await page.goto("/"); // Wait for React to mount await page.waitForSelector("nav"); }); test.describe("Focus View", () => { test("shows empty state when no items", async ({ page }) => { await expect(page.getByText("All Clear")).toBeVisible(); await expect( page.getByText("Nothing needs your attention right now") ).toBeVisible(); }); test("shows navigation tabs", async ({ page }) => { await expect(page.getByRole("button", { name: "Focus" })).toBeVisible(); await expect(page.getByRole("button", { name: "Queue" })).toBeVisible(); await expect(page.getByRole("button", { name: "Inbox" })).toBeVisible(); }); test("shows focus item when store has data", async ({ page }) => { try { await exposeStores(page); await seedFocusStore(page); } catch { // Stores not exposed -- skip this test in environments without the shim test.skip(); return; } await expect( page.getByText("Fix authentication token refresh logic") ).toBeVisible(); await expect(page.getByText("MR REVIEW")).toBeVisible(); await expect(page.getByText("!847 in platform/core")).toBeVisible(); }); test("shows action buttons when item is focused", async ({ page }) => { try { await exposeStores(page); await seedFocusStore(page); } catch { test.skip(); return; } await expect(page.getByText("Start")).toBeVisible(); await expect(page.getByText("1 hour")).toBeVisible(); await expect(page.getByText("Tomorrow")).toBeVisible(); await expect(page.getByText("Skip")).toBeVisible(); }); }); test.describe("Navigation", () => { test("switches between Focus and Queue views", async ({ page }) => { // Start in Focus view await expect(page.getByText("All Clear")).toBeVisible(); // Click Queue tab await page.getByRole("button", { name: "Queue" }).click(); await expect(page.getByText("No items in the queue")).toBeVisible(); // Click Focus tab await page.getByRole("button", { name: "Focus" }).click(); await expect(page.getByText("All Clear")).toBeVisible(); }); test("shows Inbox view with zero state", async ({ page }) => { await page.getByRole("button", { name: "Inbox" }).click(); await expect(page.getByText("Inbox Zero")).toBeVisible(); await expect(page.getByText("All caught up!")).toBeVisible(); }); test("Queue tab shows item count badge when store has data", async ({ page, }) => { try { await exposeStores(page); await seedFocusStore(page); } catch { test.skip(); return; } // 1 current + 2 queue = 3 const badge = page.getByTestId("queue-badge"); await expect(badge).toBeVisible(); await expect(badge).toHaveText("3"); }); }); test.describe("Queue View", () => { test("shows items grouped by type when store has data", async ({ page, }) => { try { await exposeStores(page); await seedFocusStore(page); } catch { test.skip(); return; } await page.getByRole("button", { name: "Queue" }).click(); // Should have a Reviews section with 2 items await expect(page.getByText("REVIEWS (2)")).toBeVisible(); // Should have an Issues section with 1 item await expect(page.getByText("ISSUES (1)")).toBeVisible(); }); test("clicking item switches to Focus view", async ({ page }) => { try { await exposeStores(page); await seedFocusStore(page); } catch { test.skip(); return; } await page.getByRole("button", { name: "Queue" }).click(); // Click the issue item await page .getByText("Users unable to login after password reset") .click(); // Should switch to focus view -- wait for the Queue header to disappear await expect(page.getByText("ISSUES (1)")).not.toBeVisible(); // The clicked item should now be THE ONE THING await expect( page.getByRole("heading", { name: "Users unable to login after password reset", }) ).toBeVisible(); }); }); test.describe("Dark mode", () => { test("page has dark background", async ({ page }) => { const body = page.locator("body"); await expect(body).toHaveClass(/bg-surface/); }); test("HTML element has dark class", async ({ page }) => { const html = page.locator("html"); await expect(html).toHaveClass(/dark/); }); }); test.describe("Data Flow Smoke Test", () => { test("lore items display correctly in Focus and Queue views", async ({ page, }) => { // This test validates the data path from transformed lore items to UI. // Items are seeded with the exact shape returned by useLoreItems. try { await exposeStores(page); } catch { test.skip(); return; } // Seed with items matching the lore transformation output await page.evaluate(() => { const w = window as Record; const focusStore = w.__MC_FOCUS_STORE__ as { setState: (state: Record) => void; }; if (!focusStore) return; focusStore.setState({ current: { // MR review item from lore id: "mr_review:platform::core:200", title: "Add user authentication middleware", type: "mr_review", project: "platform/core", url: "https://gitlab.com/platform/core/-/merge_requests/200", iid: 200, updatedAt: new Date().toISOString(), contextQuote: null, requestedBy: "alice", // This is set by lore for reviews snoozedUntil: null, }, queue: [ { // Issue from lore id: "issue:platform::api:42", title: "API timeout on large requests", type: "issue", project: "platform/api", url: "https://gitlab.com/platform/api/-/issues/42", iid: 42, updatedAt: new Date( Date.now() - 2 * 24 * 60 * 60 * 1000 ).toISOString(), contextQuote: null, requestedBy: null, // Issues don't have requestedBy snoozedUntil: null, }, { // Authored MR from lore id: "mr_authored:platform::core:150", title: "Refactor database connection pooling", type: "mr_authored", project: "platform/core", url: "https://gitlab.com/platform/core/-/merge_requests/150", iid: 150, updatedAt: new Date( Date.now() - 5 * 24 * 60 * 60 * 1000 ).toISOString(), contextQuote: null, requestedBy: null, snoozedUntil: null, }, ], isLoading: false, error: null, }); }); // Verify Focus view displays the current item correctly await expect( page.getByText("Add user authentication middleware") ).toBeVisible(); await expect(page.getByText("MR REVIEW")).toBeVisible(); await expect(page.getByText("!200 in platform/core")).toBeVisible(); // Navigate to Queue to verify all items render await page.getByRole("button", { name: "Queue" }).click(); // Check badge shows correct count (1 current + 2 queue = 3) const badge = page.getByTestId("queue-badge"); await expect(badge).toHaveText("3"); // Verify issue renders with correct formatting await expect(page.getByText("API timeout on large requests")).toBeVisible(); await expect(page.getByText("#42")).toBeVisible(); // Verify authored MR renders await expect( page.getByText("Refactor database connection pooling") ).toBeVisible(); await expect(page.getByText("!150")).toBeVisible(); }); }); });