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,95 @@
/**
* Navigation Discovery — scans the cPad sidebar and collects all /cp links.
*
* This test acts as both:
* 1. A standalone spec that validates the nav scan returns ≥ 1 link.
* 2. A data producer: it writes the discovered URLs to
* tests/.discovered/cpad-links.json so that navigation.spec.ts can
* consume them dynamically.
*
* Ignored links:
* • /cp/logout
* • javascript:void / # anchors
* • External URLs (not starting with /cp)
*
* Run:
* npx playwright test tests/cpad/navigation-discovery.spec.ts --project=cpad
*/
import { test, expect } from '@playwright/test';
import { DASHBOARD_PATH, CP_PATH } from '../helpers/auth';
import fs from 'fs';
import path from 'path';
const DISCOVERED_DIR = path.join('tests', '.discovered');
const DISCOVERED_FILE = path.join(DISCOVERED_DIR, 'cpad-links.json');
// ─────────────────────────────────────────────────────────────────────────────
// Helpers
// ─────────────────────────────────────────────────────────────────────────────
/** Returns true if the href should be included in navigation tests */
function isNavigableLink(href: string | null): boolean {
if (!href) return false;
if (!href.startsWith(CP_PATH)) return false;
if (href.includes('/logout')) return false;
if (href.startsWith('javascript:')) return false;
if (href === CP_PATH || href === CP_PATH + '/') return false; // exclude root (same as dashboard)
return true;
}
/** Remove query strings and anchors for clean deduplication */
function normalise(href: string): string {
try {
const u = new URL(href, 'http://placeholder');
return u.pathname;
} catch {
return href.split('?')[0].split('#')[0];
}
}
// ─────────────────────────────────────────────────────────────────────────────
// Discovery test
// ─────────────────────────────────────────────────────────────────────────────
test.describe('cPad Navigation Discovery', () => {
test('scan sidebar and collect all /cp navigation links', async ({ page }) => {
await page.goto(DASHBOARD_PATH);
await page.waitForLoadState('networkidle');
// Ensure we're not on the login page
expect(page.url()).not.toContain('/login');
// ── Widen the scan to cover lazy-loaded submenu items ──────────────────
// Hover over sidebar nav items to expand hidden submenus
const menuItems = page.locator('.nav-item, .sidebar-item, .menu-item, li.nav-item');
const menuCount = await menuItems.count();
for (let i = 0; i < Math.min(menuCount, 30); i++) {
await menuItems.nth(i).hover().catch(() => null);
}
// ── Collect all anchor hrefs ───────────────────────────────────────────
const rawHrefs: string[] = await page.$$eval('a[href]', (anchors) =>
anchors.map((a) => (a as HTMLAnchorElement).getAttribute('href') ?? ''),
);
const discovered = [...new Set(
rawHrefs
.map((h) => normalise(h))
.filter(isNavigableLink),
)].sort();
console.log(`[discovery] Found ${discovered.length} navigable /cp links`);
discovered.forEach((l) => console.log(' •', l));
// Must find at least a few links — if not, something is wrong with auth
expect(discovered.length, 'Expected to find /cp navigation links').toBeGreaterThanOrEqual(1);
// ── Persist for navigation.spec.ts ────────────────────────────────────
if (!fs.existsSync(DISCOVERED_DIR)) {
fs.mkdirSync(DISCOVERED_DIR, { recursive: true });
}
fs.writeFileSync(DISCOVERED_FILE, JSON.stringify(discovered, null, 2), 'utf8');
console.log(`[discovery] Links saved to ${DISCOVERED_FILE}`);
});
});