more fixes

This commit is contained in:
2026-03-12 07:22:38 +01:00
parent 547215cbe8
commit 4f576ceb04
226 changed files with 14380 additions and 4453 deletions

149
tests/cpad/auth.spec.ts Normal file
View File

@@ -0,0 +1,149 @@
/**
* Authentication tests for the cPad Control Panel.
*
* Coverage:
* • Login page renders correctly
* • Login with valid credentials → redirected to /cp/dashboard (or /cp/)
* • Login with invalid credentials → error message shown, no redirect
* • Logout → session is destroyed, login page is shown
*
* These tests do NOT use the pre-saved storageState; they exercise the actual
* login/logout flow from scratch.
*
* Run:
* npx playwright test tests/cpad/auth.spec.ts --project=chromium
*/
import { test, expect } from '@playwright/test';
import {
ADMIN_EMAIL,
ADMIN_PASSWORD,
CP_PATH,
DASHBOARD_PATH,
attachErrorListeners,
} from '../helpers/auth';
// ─────────────────────────────────────────────────────────────────────────────
// Login page
// ─────────────────────────────────────────────────────────────────────────────
test.describe('cPad Login Page', () => {
test('login page loads and shows email + password fields', async ({ page }) => {
const { consoleErrors, networkErrors } = attachErrorListeners(page);
await page.goto(CP_PATH + '/login');
// Basic page health
await expect(page.locator('body')).toBeVisible();
const title = await page.title();
expect(title.length, 'Page title should not be empty').toBeGreaterThan(0);
// Form fields
const emailField = page.locator('input[name="email"], input[type="email"]').first();
const passwordField = page.locator('input[name="password"], input[type="password"]').first();
const submitButton = page.locator('button[type="submit"], input[type="submit"]').first();
await expect(emailField).toBeVisible();
await expect(passwordField).toBeVisible();
await expect(submitButton).toBeVisible();
// No JS crashes on page load
expect(consoleErrors.length, `Console errors: ${consoleErrors.join(' | ')}`).toBe(0);
expect(networkErrors.length, `Network errors: ${networkErrors.join(' | ')}`).toBe(0);
});
});
// ─────────────────────────────────────────────────────────────────────────────
// Successful login
// ─────────────────────────────────────────────────────────────────────────────
test.describe('cPad Successful Login', () => {
test('admin can log in and is redirected to dashboard', async ({ page }) => {
const { consoleErrors, networkErrors } = attachErrorListeners(page);
await page.goto(CP_PATH + '/login');
await page.locator('input[name="email"], input[type="email"]').first().fill(ADMIN_EMAIL);
await page.locator('input[name="password"], input[type="password"]').first().fill(ADMIN_PASSWORD);
await page.locator('button[type="submit"], input[type="submit"]').first().click();
// After successful login the URL should be within /cp and not be /login
await page.waitForURL(
(url) => url.pathname.startsWith(CP_PATH) && !url.pathname.endsWith('/login'),
{ timeout: 20_000 },
);
await expect(page.locator('body')).toBeVisible();
// No server errors
const body = await page.locator('body').textContent() ?? '';
expect(/Whoops|Server Error|500/.test(body), 'Server error page after login').toBe(false);
expect(networkErrors.length, `HTTP 5xx errors: ${networkErrors.join(' | ')}`).toBe(0);
});
});
// ─────────────────────────────────────────────────────────────────────────────
// Failed login
// ─────────────────────────────────────────────────────────────────────────────
test.describe('cPad Failed Login', () => {
test('wrong password shows error and stays on login page', async ({ page }) => {
const { networkErrors } = attachErrorListeners(page);
await page.goto(CP_PATH + '/login');
await page.locator('input[name="email"], input[type="email"]').first().fill(ADMIN_EMAIL);
await page.locator('input[name="password"], input[type="password"]').first().fill('WrongPassword999!');
await page.locator('button[type="submit"], input[type="submit"]').first().click();
// Should stay on the login page
await page.waitForLoadState('networkidle');
expect(page.url()).toContain('/login');
// No 5xx errors from a bad credentials attempt
expect(networkErrors.length, `HTTP 5xx errors: ${networkErrors.join(' | ')}`).toBe(0);
});
test('empty credentials show validation errors', async ({ page }) => {
await page.goto(CP_PATH + '/login');
// Submit without filling in anything
await page.locator('button[type="submit"], input[type="submit"]').first().click();
await page.waitForLoadState('networkidle');
// Still on login page
expect(page.url()).toContain(CP_PATH);
});
});
// ─────────────────────────────────────────────────────────────────────────────
// Logout
// ─────────────────────────────────────────────────────────────────────────────
test.describe('cPad Logout', () => {
test('logout destroys session and shows login page', async ({ page }) => {
// Log in first
await page.goto(CP_PATH + '/login');
await page.locator('input[name="email"], input[type="email"]').first().fill(ADMIN_EMAIL);
await page.locator('input[name="password"], input[type="password"]').first().fill(ADMIN_PASSWORD);
await page.locator('button[type="submit"], input[type="submit"]').first().click();
await page.waitForURL(
(url) => url.pathname.startsWith(CP_PATH) && !url.pathname.endsWith('/login'),
{ timeout: 20_000 },
);
// Perform logout via the /cp/logout route
await page.goto(CP_PATH + '/logout');
await page.waitForLoadState('networkidle');
// Should land on the login page again
expect(page.url()).toContain('/login');
// Attempting to access dashboard now should redirect back to login
await page.goto(DASHBOARD_PATH);
await page.waitForLoadState('networkidle');
expect(page.url()).toContain('/login');
});
});