import { expect, test, uploadTestImage } from "./helpers"; // --------------------------------------------------------------------------- // Login Page (unauthenticated) // --------------------------------------------------------------------------- test.describe("Login Page", () => { test.use({ storageState: { cookies: [], origins: [] } }); test("renders split layout with form and marketing text", async ({ page }) => { await page.goto("/login"); // Left side: form panel await expect(page.getByRole("heading", { name: /login/i })).toBeVisible(); await expect(page.getByLabel("Username")).toBeVisible(); await expect(page.getByLabel("Password")).toBeVisible(); await expect(page.getByRole("button", { name: /login/i })).toBeVisible(); // Right side: marketing text (hidden on mobile, visible on lg+) await expect(page.getByText("Your one-stop-shop")).toBeVisible(); }); test("username and password inputs start empty", async ({ page }) => { await page.goto("/login"); await expect(page.getByLabel("Username")).toHaveValue(""); await expect(page.getByLabel("Password")).toHaveValue(""); }); test("login button is disabled when fields are empty", async ({ page }) => { await page.goto("/login"); const loginBtn = page.getByRole("button", { name: /login/i }); await expect(loginBtn).toBeDisabled(); }); test("login button enables when both fields are filled", async ({ page }) => { await page.goto("/login"); const loginBtn = page.getByRole("button", { name: /login/i }); await expect(loginBtn).toBeDisabled(); await page.getByLabel("Username").fill("admin"); await expect(loginBtn).toBeDisabled(); await page.getByLabel("Password").fill("admin"); await expect(loginBtn).toBeEnabled(); }); test("successful login redirects to /", async ({ page }) => { await page.goto("/login"); await page.getByLabel("Username").fill("admin"); await page.getByLabel("Password").fill("admin"); await page.getByRole("button", { name: /login/i }).click(); await page.waitForURL("/", { timeout: 15_000 }); await expect(page).toHaveURL("/"); }); test("failed login shows error and stays on login page", async ({ page }) => { await page.goto("/login"); await page.getByLabel("Username").fill("wrong-user"); await page.getByLabel("Password").fill("wrong-pass"); await page.getByRole("button", { name: /login/i }).click(); await expect(page.getByText(/invalid|incorrect|error/i).first()).toBeVisible({ timeout: 10_000, }); await expect(page).toHaveURL(/\/login/); }); test("pressing Enter in password field submits the form", async ({ page }) => { await page.goto("/login"); await page.getByLabel("Username").fill("admin"); await page.getByLabel("Password").fill("admin"); await page.getByLabel("Password").press("Enter"); await page.waitForURL("/", { timeout: 15_000 }); await expect(page).toHaveURL("/"); }); }); // --------------------------------------------------------------------------- // Home Page (authenticated) // --------------------------------------------------------------------------- test.describe("Home Page - Before Upload", () => { test("shows dropzone with dashed border and upload button", async ({ loggedInPage: page }) => { const dropzone = page.locator("[class*='border-dashed']").first(); await expect(dropzone).toBeVisible(); await expect(page.getByText("Upload from computer")).toBeVisible(); }); test("tool panel is visible with search bar and categories", async ({ loggedInPage: page }) => { await expect(page.getByPlaceholder(/search/i).first()).toBeVisible(); await expect(page.getByText("Essentials").first()).toBeVisible(); }); test("search filters tools in tool panel", async ({ loggedInPage: page }) => { const searchInput = page.getByPlaceholder(/search/i).first(); await searchInput.fill("resize"); // Resize tool should remain visible await expect(page.getByText("Resize").first()).toBeVisible(); }); }); test.describe("Home Page - After Upload", () => { test("shows green checkmark, filename, and file size", async ({ loggedInPage: page }) => { await uploadTestImage(page); // Green checkmark indicator await expect(page.locator("[class*='text-green']").first()).toBeVisible(); // Filename await expect(page.getByText(/test-image/i).first()).toBeVisible(); // File size in KB await expect(page.getByText(/KB/i).first()).toBeVisible(); }); test("shows Change file button", async ({ loggedInPage: page }) => { await uploadTestImage(page); await expect(page.getByText("Change file")).toBeVisible(); }); test("shows Quick Actions with 4 buttons", async ({ loggedInPage: page }) => { await uploadTestImage(page); await expect(page.getByText("Quick Actions").first()).toBeVisible(); await expect(page.getByRole("button", { name: /resize/i }).first()).toBeVisible(); await expect(page.getByRole("button", { name: /compress/i }).first()).toBeVisible(); await expect(page.getByRole("button", { name: /convert/i }).first()).toBeVisible(); await expect(page.getByRole("button", { name: /remove background/i }).first()).toBeVisible(); }); test("shows All Tools section with categorized list", async ({ loggedInPage: page }) => { await uploadTestImage(page); await expect(page.getByText("All Tools").first()).toBeVisible(); // Categories should be visible within the tool list await expect(page.getByText("Essentials").first()).toBeVisible(); }); test("clicking a tool navigates to the tool page", async ({ loggedInPage: page }) => { await uploadTestImage(page); await page .getByRole("button", { name: /resize/i }) .first() .click(); await expect(page).toHaveURL("/resize"); }); }); // --------------------------------------------------------------------------- // Fullscreen Grid Page (/fullscreen) // --------------------------------------------------------------------------- test.describe("Fullscreen Grid Page", () => { test("grid renders with search bar", async ({ loggedInPage: page }) => { await page.goto("/fullscreen"); await expect(page.getByPlaceholder(/search/i)).toBeVisible(); }); test("show/hide details toggle is visible", async ({ loggedInPage: page }) => { await page.goto("/fullscreen"); // The toggle button text await expect( page.getByRole("button", { name: /hide details|show details/i }).first(), ).toBeVisible(); }); test("all category headers are visible", async ({ loggedInPage: page }) => { await page.goto("/fullscreen"); await expect(page.getByText("Essentials")).toBeVisible(); await expect(page.getByText("Optimization")).toBeVisible(); await expect(page.getByText("Adjustments")).toBeVisible(); }); test("tool cards are links to tool pages", async ({ loggedInPage: page }) => { await page.goto("/fullscreen"); const resizeLink = page.getByRole("link", { name: /^Resize/ }).first(); await expect(resizeLink).toBeVisible(); await resizeLink.click(); await expect(page).toHaveURL("/resize"); }); test("search filters tools in grid", async ({ loggedInPage: page }) => { await page.goto("/fullscreen"); const searchInput = page.getByPlaceholder(/search/i); await searchInput.fill("compress"); await expect(page.getByRole("link", { name: /^Compress/ }).first()).toBeVisible(); // Another unrelated tool should be hidden await expect(page.getByRole("link", { name: /^Resize/ })).toHaveCount(0); }); }); // --------------------------------------------------------------------------- // Tool Page (/:toolId) - tested with "resize" // --------------------------------------------------------------------------- test.describe("Tool Page - Resize", () => { test("shows tool icon and name", async ({ loggedInPage: page }) => { await page.goto("/resize"); await expect(page.getByText("Resize").first()).toBeVisible(); }); test("shows dropzone with dashed border before upload", async ({ loggedInPage: page }) => { await page.goto("/resize"); const dropzone = page.locator("[class*='border-dashed']").first(); await expect(dropzone).toBeVisible(); await expect(page.getByText("Upload from computer")).toBeVisible(); }); test("after upload shows Files section, Settings section, and Process button", async ({ loggedInPage: page, }) => { await page.goto("/resize"); await uploadTestImage(page); // Files section await expect(page.getByText("Files").first()).toBeVisible(); // Settings section await expect(page.getByText("Settings").first()).toBeVisible(); }); test("invalid tool ID shows not found message", async ({ loggedInPage: page }) => { await page.goto("/this-tool-does-not-exist-xyz"); await expect(page.getByText("Tool not found")).toBeVisible(); }); }); // --------------------------------------------------------------------------- // Automate Page (/automate) // --------------------------------------------------------------------------- test.describe("Automate Page", () => { test("shows pipeline builder with empty state", async ({ loggedInPage: page }) => { await page.goto("/automate"); await expect(page.getByText("Pipeline Builder")).toBeVisible(); await expect(page.getByText("No steps yet")).toBeVisible(); await expect( page.getByText("Click tools from the palette to build your pipeline"), ).toBeVisible(); }); test("tool palette is visible with searchable list", async ({ loggedInPage: page }) => { await page.goto("/automate"); await expect(page.getByText("Tool Palette")).toBeVisible(); await expect(page.getByPlaceholder(/search/i).first()).toBeVisible(); }); test("process button is disabled when no steps are configured", async ({ loggedInPage: page, }) => { await page.goto("/automate"); const processBtn = page.getByRole("button", { name: /process/i }).first(); await expect(processBtn).toBeDisabled(); }); }); // --------------------------------------------------------------------------- // Files Page (/files) // --------------------------------------------------------------------------- test.describe("Files Page", () => { test("renders file management layout on desktop", async ({ loggedInPage: page }) => { await page.goto("/files"); // Left nav column with "My Files" heading await expect(page.getByText("My Files")).toBeVisible(); // Navigation items await expect(page.getByRole("button", { name: /recent/i }).first()).toBeVisible(); }); }); // --------------------------------------------------------------------------- // Sidebar Navigation // --------------------------------------------------------------------------- test.describe("Sidebar Navigation", () => { test("sidebar has 4 top items: Tools, Grid, Automate, Files", async ({ loggedInPage: page }) => { const sidebar = page.locator("aside"); await expect(sidebar).toBeVisible(); await expect(sidebar.getByText("Tools")).toBeVisible(); await expect(sidebar.getByText("Grid")).toBeVisible(); await expect(sidebar.getByText("Automate")).toBeVisible(); await expect(sidebar.getByText("Files")).toBeVisible(); }); test("sidebar has 2 bottom items: Help, Settings", async ({ loggedInPage: page }) => { const sidebar = page.locator("aside"); await expect(sidebar.getByText("Help")).toBeVisible(); await expect(sidebar.getByText("Settings")).toBeVisible(); }); test("Tools link navigates to home /", async ({ loggedInPage: page }) => { await page.goto("/automate"); await page.locator("aside").getByText("Tools").click(); await expect(page).toHaveURL("/"); }); test("Grid link navigates to /fullscreen", async ({ loggedInPage: page }) => { const gridLink = page.locator("aside").getByText("Grid"); if (await gridLink.isVisible({ timeout: 3000 }).catch(() => false)) { await gridLink.click(); } else { await page.locator('aside a[href="/fullscreen"]').click(); } await expect(page).toHaveURL("/fullscreen"); }); test("Automate link navigates to /automate", async ({ loggedInPage: page }) => { await page.locator("aside").getByText("Automate").click(); await expect(page).toHaveURL("/automate"); }); test("Files link navigates to /files", async ({ loggedInPage: page }) => { await page.locator("aside").getByText("Files").click(); await expect(page).toHaveURL("/files"); }); test("active sidebar item is highlighted", async ({ loggedInPage: page }) => { // On home page, "Tools" should have the active styling (bg-primary) const toolsItem = page.locator("aside").getByText("Tools").locator(".."); await expect(toolsItem).toHaveClass(/bg-primary/); }); test("Help button opens HelpDialog modal", async ({ loggedInPage: page }) => { await page.locator("aside").getByText("Help").click(); // Help dialog header await expect(page.getByRole("heading", { name: "Help" })).toBeVisible(); }); test("Settings button opens SettingsDialog modal", async ({ loggedInPage: page }) => { await page.locator("aside").getByText("Settings").click(); await expect(page.getByRole("heading", { name: "General" })).toBeVisible(); await expect(page.getByRole("button", { name: "Security" })).toBeVisible(); }); }); // --------------------------------------------------------------------------- // Routing Edge Cases // --------------------------------------------------------------------------- test.describe("Routing Edge Cases", () => { test("invalid tool ID shows error state", async ({ loggedInPage: page }) => { await page.goto("/nonexistent-tool-abc123"); await expect(page.getByText("Tool not found")).toBeVisible(); }); test("legacy /brightness-contrast redirects to /adjust-colors", async ({ loggedInPage: page, }) => { await page.goto("/brightness-contrast"); await expect(page).toHaveURL("/adjust-colors"); }); test("legacy /saturation redirects to /adjust-colors", async ({ loggedInPage: page }) => { await page.goto("/saturation"); await expect(page).toHaveURL("/adjust-colors"); }); test("legacy /color-channels redirects to /adjust-colors", async ({ loggedInPage: page }) => { await page.goto("/color-channels"); await expect(page).toHaveURL("/adjust-colors"); }); test("/privacy renders the privacy policy page", async ({ loggedInPage: page }) => { await page.goto("/privacy"); await expect(page.getByRole("heading", { name: "Privacy Policy" })).toBeVisible(); await expect(page.getByText("Back to app")).toBeVisible(); }); });