import { expect, test } from "@playwright/test"; import { CHANNEL_SLUG, VIDEO_CHAT_LARGE, VIDEO_CHAT_SMALL, VIDEO_TRANSCRIPT_ONLY, } from "./fixtures/data"; import { installRoutes, showAll } from "./helpers"; // The unified workspace shell: `/` (results) and `/ask` (chat) share one layout, // so the persistent search bar and its committed search survive navigation // between the two views. These tests prove that persistence — the whole point of // the shell — which no per-page test covered before. const qt = (tree: unknown) => encodeURIComponent(JSON.stringify(tree)); const ALPHA_TREE = { k: "g", o: "AND", c: [{ k: "l", q: "alpha", s: "transcripts" }], }; const ALPHA_SLUGS = [ `${CHANNEL_SLUG}/${VIDEO_TRANSCRIPT_ONLY}`, `${CHANNEL_SLUG}/${VIDEO_CHAT_SMALL}`, `${CHANNEL_SLUG}/${VIDEO_CHAT_LARGE}`, ]; test.describe("workspace shell", () => { test.beforeEach(async ({ page }) => { await installRoutes(page); }); test("the committed search persists across Results ⇄ Chat navigation", async ({ page, }) => { await page.goto(`/?qt=${qt(ALPHA_TREE)}`); // Results render on the results view. await expect(page.locator("[data-card-header]")).toHaveCount(ALPHA_SLUGS.length); const nav = page.getByTestId("workspace-nav"); // Navigate to the chat via the workspace nav (real client-side routing). await nav.getByRole("link", { name: "Chat" }).click(); await expect(page).toHaveURL(/\/ask\/?$/); await expect(page.getByPlaceholder(/Ask about the transcripts/)).toBeVisible(); // The persistent bar is still mounted — its query-builder leaf keeps the term. await expect( page.locator('input[data-testid^="leaf-query-"]').first(), ).toHaveValue("alpha"); // Navigate back to results — the search is preserved (no ?qt re-seed needed). await nav.getByRole("link", { name: "Search" }).click(); await expect(page).not.toHaveURL(/\/ask/); await expect(page.locator("[data-card-header]")).toHaveCount(ALPHA_SLUGS.length); }); test("the workspace nav marks the active view", async ({ page }) => { await page.goto("/"); const nav = page.getByTestId("workspace-nav"); await expect(nav.getByRole("link", { name: "Search" })).toHaveAttribute( "aria-current", "page", ); // Wait for the results pane to hydrate (an empty Search lists every video) // before clicking — a client Link click lost to a pre-hydration window would // leave us stranded on `/`. await showAll(page); await expect(page.locator("[data-card-header]").first()).toBeVisible(); await nav.getByRole("link", { name: "Chat" }).click(); await expect(page.getByPlaceholder(/Ask about the transcripts/)).toBeVisible(); await expect(nav.getByRole("link", { name: "Chat" })).toHaveAttribute( "aria-current", "page", ); }); });