// ABOUTME: E2E tests for the history page showing past training decisions. // ABOUTME: Tests table display, pagination, date filtering, and empty states. import { expect, test } from "@playwright/test"; test.describe("history page", () => { test.describe("unauthenticated", () => { test("redirects to login when not authenticated", async ({ page }) => { await page.goto("/history"); // Should redirect to login await expect(page).toHaveURL(/\/login/); }); }); test.describe("authenticated", () => { // These tests require TEST_USER_EMAIL and TEST_USER_PASSWORD env vars test.beforeEach(async ({ page }) => { const email = process.env.TEST_USER_EMAIL; const password = process.env.TEST_USER_PASSWORD; if (!email || !password) { test.skip(); return; } // Login via the login page await page.goto("/login"); await page.waitForLoadState("networkidle"); const emailInput = page.getByLabel(/email/i); const hasEmailForm = await emailInput.isVisible().catch(() => false); if (!hasEmailForm) { test.skip(); return; } await emailInput.fill(email); await page.getByLabel(/password/i).fill(password); await page.getByRole("button", { name: /sign in/i }).click(); // Wait for redirect to dashboard then navigate to history await page.waitForURL("/", { timeout: 10000 }); await page.goto("/history"); }); test("displays history page with title", async ({ page }) => { // Check for history page title const heading = page.getByRole("heading", { name: "History" }); await expect(heading).toBeVisible(); }); test("shows date filter controls", async ({ page }) => { // Check for date filter inputs const startDateInput = page.getByLabel(/start date/i); const endDateInput = page.getByLabel(/end date/i); await expect(startDateInput).toBeVisible(); await expect(endDateInput).toBeVisible(); // Check for Apply and Clear buttons const applyButton = page.getByRole("button", { name: /apply/i }); const clearButton = page.getByRole("button", { name: /clear/i }); await expect(applyButton).toBeVisible(); await expect(clearButton).toBeVisible(); }); test("shows table with correct columns when data exists", async ({ page, }) => { // Wait for data to load await page.waitForLoadState("networkidle"); // Check if there's data or empty state const table = page.locator("table"); const emptyState = page.getByText(/no history found/i); const hasTable = await table.isVisible().catch(() => false); const hasEmptyState = await emptyState.isVisible().catch(() => false); if (hasTable) { // Verify table headers exist const headers = page.locator("thead th"); await expect(headers).toHaveCount(6); // Check for specific column headers await expect( page.getByRole("columnheader", { name: /date/i }), ).toBeVisible(); await expect( page.getByRole("columnheader", { name: /day.*phase/i }), ).toBeVisible(); await expect( page.getByRole("columnheader", { name: /decision/i }), ).toBeVisible(); await expect( page.getByRole("columnheader", { name: /body battery/i }), ).toBeVisible(); await expect( page.getByRole("columnheader", { name: /hrv/i }), ).toBeVisible(); await expect( page.getByRole("columnheader", { name: /intensity/i }), ).toBeVisible(); } else if (hasEmptyState) { // Empty state is valid when no history data await expect(emptyState).toBeVisible(); } }); test("shows empty state when no data", async ({ page }) => { // This test verifies empty state UI is present when applicable await page.waitForLoadState("networkidle"); const emptyState = page.getByText(/no history found/i); const table = page.locator("table tbody tr"); const hasRows = await table .first() .isVisible() .catch(() => false); const hasEmptyState = await emptyState.isVisible().catch(() => false); // Either has data rows OR shows empty state (both valid) expect(hasRows || hasEmptyState).toBe(true); }); test("has link back to dashboard", async ({ page }) => { const dashboardLink = page.getByRole("link", { name: /back to dashboard/i, }); await expect(dashboardLink).toBeVisible(); // Click and verify navigation await dashboardLink.click(); await expect(page).toHaveURL("/"); }); test("shows entry count", async ({ page }) => { await page.waitForLoadState("networkidle"); // Look for entries count text (e.g., "5 entries") const entriesText = page.getByText(/\d+ entries/); const hasEntriesText = await entriesText.isVisible().catch(() => false); // May not be visible if no data, check for either count or empty state const emptyState = page.getByText(/no history found/i); const hasEmptyState = await emptyState.isVisible().catch(() => false); expect(hasEntriesText || hasEmptyState).toBe(true); }); }); });