import { expect, test } from "@playwright/test"; test.describe("Timeline", () => { test.describe.configure({ mode: "serial" }); test.beforeEach(async ({ page }) => { await page.addInitScript(() => { localStorage.setItem("capakraken_theme", JSON.stringify({ mode: "dark" })); }); await page.goto("/auth/signin"); await page.fill('input[type="email"]', "admin@capakraken.dev"); await page.fill('input[type="password"]', "admin123"); await page.click('button[type="submit"]'); await expect(page).toHaveURL(/\/(dashboard|resources)/); await page.goto("/timeline"); }); test("loads and displays the timeline", async ({ page }) => { await expect(page.locator("text=Resource view")).toBeVisible(); await expect(page.locator("text=Project view")).toBeVisible(); await expect(page.getByRole("button", { name: /All Clients|Clients:/ })).toBeVisible(); await expect(page.getByRole("button", { name: /All Chapters|Chapters:/ })).toBeVisible(); await expect(page.getByRole("button", { name: /All people|People:/ })).toBeVisible(); // Timeline canvas should be visible await expect(page.locator("div.app-surface.relative.flex-1.overflow-auto")).toBeVisible(); }); test("can switch between resource and project view", async ({ page }) => { await page.click("text=Project view"); await expect( page.locator("text=0 projects").or(page.locator("text=/\\d+ projects/")), ).toBeVisible(); await page.click("text=Resource view"); await expect(page.locator("text=/\\d+ resources/")).toBeVisible(); }); test("can navigate forward and back", async ({ page }) => { const todayBtn = page.locator("button", { hasText: "Today" }); await expect(todayBtn).toBeVisible(); await page.locator("button", { hasText: "›" }).click(); await page.locator("button", { hasText: "‹" }).click(); await todayBtn.click(); }); test("filter panel opens and closes", async ({ page }) => { await page.locator("button", { hasText: "Filter" }).click(); await expect(page.getByRole("heading", { name: "Filters" })).toBeVisible(); await expect(page.getByPlaceholder("Search projects…")).toBeVisible(); await page.keyboard.press("Escape"); await expect(page.getByRole("heading", { name: "Filters" })).not.toBeVisible(); }); test("shows placeholder bars for unassigned allocations", async ({ page }) => { // Filter to show placeholders (enabled by default) // The timeline should have at least one dashed placeholder bar from seed data await page.waitForSelector(".overflow-auto", { state: "visible" }); // Check that the timeline loaded (resource rows or empty state visible) await expect( page.locator(".app-toolbar").getByText(/\d+ resources · \d+ allocations/), ).toBeVisible(); }); test("clicking a placeholder opens the fill placeholder modal", async ({ page }) => { // Wait for timeline to load await page.waitForSelector(".overflow-auto"); await page.waitForTimeout(1000); // let tRPC queries settle // Try to find and click a placeholder bar (dashed border style) const placeholderBar = page.locator("[style*='dashed']").first(); if ((await placeholderBar.count()) > 0) { await placeholderBar.click(); await expect( page.locator("text=Fill Placeholder").or(page.locator("text=Assign Resource")), ).toBeVisible(); await page.keyboard.press("Escape"); } }); test("resource and project views keep tooltips opaque in dark mode and support right click", async ({ page, }) => { await page.waitForSelector(".overflow-auto", { state: "visible" }); await page.waitForTimeout(1000); const heatmapTooltip = page .locator("div.fixed.pointer-events-none.rounded-xl.border.border-gray-800") .first(); const allocationPopoverField = page.getByText("Hours / day"); const resourceHoverTarget = page.locator(".relative.overflow-hidden.touch-none").first(); await resourceHoverTarget.hover({ position: { x: 120, y: 20 } }); await expect(heatmapTooltip).toBeVisible(); await expect .poll(async () => { return heatmapTooltip.evaluate((element) => getComputedStyle(element).backgroundColor); }) .toBe("rgba(3, 7, 18, 0.96)"); const resourceAllocation = page .locator( "div.absolute.rounded-md.flex.items-stretch.overflow-hidden.transition-all.duration-75", ) .first(); await resourceAllocation.click({ button: "right" }); await expect(allocationPopoverField).toBeVisible(); await page.mouse.click(40, 40); await page.getByText("Project view").click(); await expect(page.getByText(/projects/)).toBeVisible(); await page.waitForTimeout(500); const projectHoverTarget = page.locator(".relative.overflow-hidden.touch-none").first(); await projectHoverTarget.hover({ position: { x: 120, y: 20 } }); await expect(heatmapTooltip).toBeVisible(); await expect .poll(async () => { return heatmapTooltip.evaluate((element) => getComputedStyle(element).backgroundColor); }) .toBe("rgba(3, 7, 18, 0.96)"); const projectAllocation = page.locator("div[style*='top: 2px'][style*='bottom: 2px']").nth(1); await projectAllocation.click({ button: "right" }); await expect(allocationPopoverField).toBeVisible(); }); });