import { test, expect, Page } from "@playwright/test"; // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- async function loginAs(page: Page, email: string, password: string) { await page.goto("/login"); await page.fill('input[name="email"]', email); await page.fill('input[name="password"]', password); await page.click('button[type="submit"]'); // Wait for redirect away from login await page.waitForURL((url) => !url.pathname.includes("/login"), { timeout: 10000, }); } // --------------------------------------------------------------------------- // 1. Login Page // --------------------------------------------------------------------------- test.describe("Login Page", () => { test("renders login form with email and password fields", async ({ page, }) => { await page.goto("/login"); await expect(page.locator("h1")).toContainText("NetForge"); await expect(page.locator('input[name="email"]')).toBeVisible(); await expect(page.locator('input[name="password"]')).toBeVisible(); await expect(page.locator('button[type="submit"]')).toBeVisible(); await expect(page.locator('a[href="/signup"]')).toBeVisible(); }); test("shows error on invalid credentials", async ({ page }) => { await page.goto("/login"); await page.fill('input[name="email"]', "bad@example.com"); await page.fill('input[name="password"]', "wrongpassword"); await page.click('button[type="submit"]'); // Should stay on login page and show error await page.waitForTimeout(2000); const url = page.url(); expect(url).toContain("/login"); // Check for error message or error in URL const hasError = url.includes("error") || (await page.locator('[role="alert"], .text-red, .error').count()) > 0; expect(hasError).toBe(true); }); test("successful admin login redirects to dashboard", async ({ page }) => { await loginAs(page, "admin@demo.com", "admin123"); // Should land on dashboard or a valid authenticated page const url = page.url(); expect(url).not.toContain("/login"); }); test("session persists across page refresh", async ({ page }) => { await loginAs(page, "admin@demo.com", "admin123"); // Reload the page await page.reload(); await page.waitForLoadState("networkidle"); // Should still be on an authenticated page (not redirected to login) const url = page.url(); expect(url).not.toContain("/login"); }); }); // --------------------------------------------------------------------------- // 2. Signup Page // --------------------------------------------------------------------------- test.describe("Signup Page", () => { test("renders signup form with all required fields", async ({ page }) => { await page.goto("/signup"); await expect(page.locator("h1, h2").first()).toBeVisible(); // Check for key form fields const inputs = await page.locator("input").count(); expect(inputs).toBeGreaterThanOrEqual(4); // name, email, password, confirm await expect(page.locator('button[type="submit"]')).toBeVisible(); }); test("can register a new tenant", async ({ page }) => { const ts = Date.now(); await page.goto("/signup"); // Fill in signup form fields // The form has: company name, owner name, email, password, confirm password const allInputs = page.locator("input"); const inputCount = await allInputs.count(); // Try to fill known field patterns const companyInput = page.locator( 'input[name*="company"], input[name*="tenant"], input[name*="business"], input[placeholder*="company" i], input[placeholder*="ISP" i]' ); if ((await companyInput.count()) > 0) { await companyInput.first().fill(`UAT Test ISP ${ts}`); } const nameInputs = page.locator( 'input[name*="name"]:not([name*="company"]):not([name*="tenant"]):not([name*="business"]):not([type="email"]):not([type="password"])' ); for (let i = 0; i < (await nameInputs.count()); i++) { const name = await nameInputs.nth(i).getAttribute("name"); if (name?.includes("first") || name?.includes("First")) { await nameInputs.nth(i).fill("UAT"); } else if (name?.includes("last") || name?.includes("Last")) { await nameInputs.nth(i).fill("Tester"); } else { await nameInputs.nth(i).fill("UAT Tester"); } } const emailInput = page.locator('input[type="email"], input[name="email"]'); if ((await emailInput.count()) > 0) { await emailInput.first().fill(`uat-${ts}@test.com`); } const passwordInputs = page.locator('input[type="password"]'); const pwCount = await passwordInputs.count(); for (let i = 0; i < pwCount; i++) { await passwordInputs.nth(i).fill("TestPass123!"); } // Submit await page.click('button[type="submit"]'); // Wait for result — should redirect to login with success or show success message await page.waitForTimeout(3000); const url = page.url(); const pageText = await page.textContent("body"); const success = url.includes("registered") || url.includes("login") || url.includes("success") || pageText?.toLowerCase().includes("created") || pageText?.toLowerCase().includes("success") || pageText?.toLowerCase().includes("registered"); expect(success).toBe(true); }); }); // --------------------------------------------------------------------------- // 3. Dashboard Page (Authenticated) // --------------------------------------------------------------------------- test.describe("Dashboard", () => { test.beforeEach(async ({ page }) => { await loginAs(page, "admin@demo.com", "admin123"); }); test("dashboard page loads", async ({ page }) => { await page.goto("/dashboard"); await page.waitForLoadState("networkidle"); // Page should render without error const status = page.url(); expect(status).toContain("/dashboard"); // Check for dashboard content (may be minimal since API-only was built) const body = await page.textContent("body"); expect(body).toBeTruthy(); }); test("dashboard is not accessible when logged out", async ({ page }) => { // Clear cookies to simulate logout await page.context().clearCookies(); await page.goto("/dashboard"); await page.waitForLoadState("networkidle"); // Should redirect to login const url = page.url(); expect(url).toContain("/login"); }); }); // --------------------------------------------------------------------------- // 4. Super-Admin Panel // --------------------------------------------------------------------------- test.describe("Super-Admin Panel", () => { test("super-admin can access tenant management", async ({ page }) => { await loginAs(page, "superadmin@netforge.com", "super123"); await page.goto("/admin/tenants"); await page.waitForLoadState("networkidle"); // Should see tenant list const body = await page.textContent("body"); expect(body).toContain("Demo ISP"); }); test("regular admin cannot access super-admin panel", async ({ page }) => { await loginAs(page, "admin@demo.com", "admin123"); await page.goto("/admin/tenants"); await page.waitForLoadState("networkidle"); // Should be forbidden or redirected const url = page.url(); const body = await page.textContent("body"); const blocked = url.includes("/login") || url.includes("/dashboard") || body?.toLowerCase().includes("forbidden") || body?.toLowerCase().includes("denied") || body?.toLowerCase().includes("403"); expect(blocked).toBe(true); }); }); // --------------------------------------------------------------------------- // 5. API Smoke Tests via Page Context // --------------------------------------------------------------------------- test.describe("API via authenticated browser context", () => { test.beforeEach(async ({ page }) => { await loginAs(page, "admin@demo.com", "admin123"); }); test("GET /api/subscribers returns valid JSON", async ({ page }) => { const response = await page.goto("/api/subscribers"); expect(response?.status()).toBe(200); const json = await response?.json(); expect(json).toHaveProperty("subscribers"); expect(json).toHaveProperty("total"); }); test("GET /api/dashboard returns dashboard summary", async ({ page }) => { const response = await page.goto("/api/dashboard"); expect(response?.status()).toBe(200); const json = await response?.json(); expect(json).toHaveProperty("revenue"); expect(json).toHaveProperty("subscribers"); expect(json).toHaveProperty("cashFlow"); }); test("GET /api/service-plans returns array", async ({ page }) => { const response = await page.goto("/api/service-plans"); expect(response?.status()).toBe(200); const json = await response?.json(); expect(Array.isArray(json)).toBe(true); }); test("GET /api/tickets returns tickets", async ({ page }) => { const response = await page.goto("/api/tickets"); expect(response?.status()).toBe(200); const json = await response?.json(); expect(json).toHaveProperty("tickets"); }); test("GET /api/zones returns array", async ({ page }) => { const response = await page.goto("/api/zones"); expect(response?.status()).toBe(200); const json = await response?.json(); expect(Array.isArray(json)).toBe(true); }); test("GET /api/reports/trial-balance returns balanced books", async ({ page, }) => { const response = await page.goto("/api/reports/trial-balance"); expect(response?.status()).toBe(200); const json = await response?.json(); expect(json).toHaveProperty("isBalanced"); expect(json.isBalanced).toBe(true); }); });