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

View File

@@ -0,0 +1,107 @@
/**
* Navigation health test for the cPad Control Panel.
*
* Two operating modes:
*
* 1. Dynamic — reads tests/.discovered/cpad-links.json (written by
* navigation-discovery.spec.ts) and visits every link.
* Run the discovery spec first to populate this file.
*
* 2. Fallback — when the discovery file is absent, falls back to a static
* list of known /cp routes so the suite never fails silently.
*
* For every visited page the test asserts:
* • No redirect to /cp/login (i.e. session is still valid)
* • HTTP status not 5xx (detected via response interception)
* • No uncaught JavaScript exceptions
* • No browser console errors
* • Page body is visible and non-empty
* • Page does not contain Laravel error text
*
* Run:
* npx playwright test tests/cpad/navigation.spec.ts --project=cpad
*/
import { test, expect } from '@playwright/test';
import { CP_PATH, attachErrorListeners } from '../helpers/auth';
import { hasForm } from '../helpers/formFiller';
import fs from 'fs';
import path from 'path';
// ─────────────────────────────────────────────────────────────────────────────
// Link source
// ─────────────────────────────────────────────────────────────────────────────
const DISCOVERED_FILE = path.join('tests', '.discovered', 'cpad-links.json');
/** Well-known /cp pages used when the discovery file is missing */
const STATIC_FALLBACK_LINKS: string[] = [
'/cp/dashboard',
'/cp/configuration',
'/cp/config',
'/cp/language/app',
'/cp/language/system',
'/cp/translation/app',
'/cp/security/access',
'/cp/security/roles',
'/cp/security/permissions',
'/cp/security/login',
'/cp/plugins',
'/cp/user/profile',
'/cp/messages',
'/cp/api/keys',
'/cp/friendly-url',
];
function loadLinks(): string[] {
if (fs.existsSync(DISCOVERED_FILE)) {
try {
const links: string[] = JSON.parse(fs.readFileSync(DISCOVERED_FILE, 'utf8'));
if (links.length > 0) return links;
} catch { /* fall through to static list */ }
}
console.warn('[navigation] discovery file not found — using static fallback list');
return STATIC_FALLBACK_LINKS;
}
const CP_LINKS = loadLinks();
// ─────────────────────────────────────────────────────────────────────────────
// Main navigation suite
// ─────────────────────────────────────────────────────────────────────────────
test.describe('cPad Navigation Health', () => {
for (const linkPath of CP_LINKS) {
test(`page loads: ${linkPath}`, async ({ page }) => {
const { consoleErrors, networkErrors } = attachErrorListeners(page);
await page.goto(linkPath);
await page.waitForLoadState('networkidle', { timeout: 20_000 });
// ── Auth check ──────────────────────────────────────────────────────
expect(page.url(), `${linkPath} should not redirect to login`).not.toContain('/login');
// ── HTTP errors ─────────────────────────────────────────────────────
expect(networkErrors.length, `HTTP 5xx on ${linkPath}: ${networkErrors.join(' | ')}`).toBe(0);
// ── Body visibility ─────────────────────────────────────────────────
await expect(page.locator('body')).toBeVisible();
const bodyText = await page.locator('body').textContent() ?? '';
expect(bodyText.trim().length, `Body should not be empty on ${linkPath}`).toBeGreaterThan(0);
// ── Laravel / server error page ──────────────────────────────────────
const hasServerError = /Whoops[,!]|Server Error|Call to undefined function|SQLSTATE/.test(bodyText);
expect(hasServerError, `Server/Laravel error page at ${linkPath}: ${bodyText.slice(0, 200)}`).toBe(false);
// ── JS exceptions ────────────────────────────────────────────────────
expect(consoleErrors.length, `JS console errors on ${linkPath}: ${consoleErrors.join(' | ')}`).toBe(0);
// ── Form detection (informational — logged, not asserted) ─────────────
const pageHasForm = await hasForm(page);
if (pageHasForm) {
console.log(` [form] form detected on ${linkPath}`);
}
});
}
});