import { expect, test, type Page } from "@playwright/test"; async function signIn(page: Page) { 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)/); } test.describe("Chargeability Report", () => { test.beforeEach(async ({ page }) => { await signIn(page); await page.goto("/reports/chargeability"); }); test("chargeability forecast page loads with heading", async ({ page }) => { await page.waitForLoadState("networkidle"); await expect( page.locator("h1", { hasText: "Chargeability Forecast" }), ).toBeVisible({ timeout: 10000 }); }); test("filter controls are present", async ({ page }) => { await page.waitForLoadState("networkidle"); // Should have at least one filter (e.g., chapter, period, resource search) await expect( page.locator('input[type="text"]') .or(page.locator('input[type="search"]')) .or(page.locator("select")) .first(), ).toBeVisible({ timeout: 10000 }); }); test("data table or chart renders after load", async ({ page }) => { await page.waitForLoadState("networkidle"); // Either a table with chargeability rows or a chart container should appear await expect( page.locator("table").or(page.locator("[data-testid]")).or(page.locator("canvas")).first(), ).toBeVisible({ timeout: 15000 }); }); test("no console errors on load", async ({ page }) => { const consoleErrors: string[] = []; page.on("console", (msg) => { if (msg.type() === "error") { consoleErrors.push(msg.text()); } }); await page.goto("/reports/chargeability"); await page.waitForLoadState("networkidle"); const appErrors = consoleErrors.filter( (e) => !e.includes("extension") && !e.includes("favicon"), ); expect(appErrors, `Unexpected console errors: ${appErrors.join("\n")}`).toHaveLength(0); }); }); test.describe("Report Builder", () => { test.beforeEach(async ({ page }) => { await signIn(page); await page.goto("/reports/builder"); }); test("report builder page loads with heading", async ({ page }) => { await page.waitForLoadState("networkidle"); await expect( page.getByRole("heading", { name: "Report Builder" }), ).toBeVisible({ timeout: 10000 }); }); test("entity selector is present with expected options", async ({ page }) => { await page.waitForLoadState("networkidle"); // The builder has a Template or Entity select const select = page.locator("select").first(); await expect(select).toBeVisible({ timeout: 10000 }); }); test("run report button is visible", async ({ page }) => { await page.waitForLoadState("networkidle"); await expect( page.locator("button", { hasText: /Run|Export|Generate/i }).first(), ).toBeVisible({ timeout: 10000 }); }); test("running a default report produces output or empty state", async ({ page }) => { await page.waitForLoadState("networkidle"); const runBtn = page.locator("button", { hasText: /Run|Export|Generate/i }).first(); if ((await runBtn.count()) > 0) { await runBtn.click(); await page.waitForTimeout(1500); await expect( page.locator("table").or(page.locator("text=No rows")).or(page.locator("text=0 rows")).first(), ).toBeVisible({ timeout: 15000 }); } }); });