feat: assign custom 404 pages with the website theme (1.8.0)
This commit is contained in:
1 parent
a3c89bb69d
commit
16da844934
17 files changed
+638
-92
No files matched your search
@@ -0,0 +1,137 @@
|
||||
import { chromium } from 'playwright';
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
import assert from 'node:assert/strict';
|
||||
import { execFileSync, spawn } from 'node:child_process';
|
||||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
import net from 'node:net';
|
||||
|
||||
const php = process.env.PHP_BINARY || 'php';
|
||||
const fixture = JSON.parse(execFileSync(php, ['tests/pilot-fixture.php'], { encoding: 'utf8', windowsHide: true }));
|
||||
const env = { ...process.env, APP_ENV: 'prod', APP_DEBUG: '0', APP_SECRET: randomBytes(32).toString('hex'), DATABASE_URL: fixture.databaseUrl, MEDIA_DIRECTORY: fixture.mediaDirectory };
|
||||
execFileSync(php, ['bin/console', 'cache:clear', '--env=prod', '--no-debug'], { stdio: 'ignore', windowsHide: true, env });
|
||||
const probe = net.createServer(); await new Promise(resolve => probe.listen(0, '127.0.0.1', resolve));
|
||||
const port = probe.address().port; await new Promise(resolve => probe.close(resolve));
|
||||
const origin = `http://127.0.0.1:${port}`;
|
||||
const server = spawn(php, ['-d', 'memory_limit=256M', '-S', `127.0.0.1:${port}`, '-t', 'public', 'bin/dev-router.php'], { windowsHide: true, stdio: 'ignore', env: { ...env, SITE_ORIGIN: origin } });
|
||||
const report = 'reports/not-found'; await mkdir(report, { recursive: true });
|
||||
let browser, page;
|
||||
const checks = [], errors = [];
|
||||
try {
|
||||
for (let attempt = 0; attempt < 60; attempt++) {
|
||||
try { if ((await fetch(origin)).ok) break; } catch {}
|
||||
if (attempt === 59) throw new Error('Pilot test server did not start');
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
}
|
||||
browser = await chromium.launch();
|
||||
const context = await browser.newContext({ viewport: { width: 1600, height: 1050 } });
|
||||
page = await context.newPage(); page.setDefaultTimeout(15000); page.on('pageerror', error => errors.push(error.message));
|
||||
await page.goto(origin + '/admin/login');
|
||||
await page.getByLabel('Benutzername').fill(fixture.username); await page.getByLabel('Passwort', { exact: true }).fill(fixture.password);
|
||||
await page.getByRole('button', { name: 'Anmelden', exact: true }).click(); await page.waitForURL(origin + '/admin');
|
||||
const visitor = await context.newPage();
|
||||
visitor.on('pageerror', error => errors.push(error.message));
|
||||
await visitor.goto(origin);
|
||||
const shell = await visitor.evaluate(() => ({
|
||||
header: document.querySelector('.site-header').textContent.replace(/\s+/g, ' ').trim(),
|
||||
footer: document.querySelector('.site-footer').textContent.replace(/\s+/g, ' ').trim(),
|
||||
font: getComputedStyle(document.body).fontFamily,
|
||||
}));
|
||||
const checkError = async (target, heading = 'Seite nicht gefunden', targetPage = visitor) => {
|
||||
const response = await targetPage.goto(origin + target);
|
||||
assert.equal(response.status(), 404);
|
||||
assert.ok(response.headers()['x-robots-tag'].includes('noindex'));
|
||||
assert.ok(response.headers()['content-security-policy'].includes("'sha256-"));
|
||||
assert.equal(await targetPage.locator('h1').innerText(), heading);
|
||||
assert.equal((await targetPage.locator('.site-header').textContent()).replace(/\s+/g, ' ').trim(), shell.header);
|
||||
assert.equal((await targetPage.locator('.site-footer').textContent()).replace(/\s+/g, ' ').trim(), shell.footer);
|
||||
assert.equal(await targetPage.evaluate(() => getComputedStyle(document.body).fontFamily), shell.font);
|
||||
assert.equal(await targetPage.locator('link[rel="canonical"]').count(), 0);
|
||||
assert.equal(await targetPage.locator('meta[name="robots"]').getAttribute('content'), 'noindex, follow');
|
||||
};
|
||||
const audit = async targetPage => {
|
||||
const result = await new AxeBuilder({ page: targetPage }).analyze();
|
||||
assert.deepEqual(result.violations.map(v => ({ id: v.id, nodes: v.nodes.map(n => n.target) })), []);
|
||||
assert.equal(await targetPage.evaluate(() => document.documentElement.scrollWidth <= innerWidth), true);
|
||||
};
|
||||
await checkError('/eine-unbekannte-seite');
|
||||
await audit(visitor); await visitor.screenshot({ path: `${report}/default-desktop.png`, fullPage: true });
|
||||
await checkError('/Alte/Seite.html'); await checkError('/eins/zwei/drei/vier/fuenf');
|
||||
await visitor.setViewportSize({ width: 390, height: 900 });
|
||||
await checkError('/__404-vorschau__'); await audit(visitor);
|
||||
await visitor.screenshot({ path: `${report}/default-mobile.png`, fullPage: true });
|
||||
checks.push('default 404, unusual/deep URLs, correct status/noindex, standard header/footer/fonts, desktop/mobile axe and layout');
|
||||
|
||||
await page.goto(origin + '/admin/settings#not-found');
|
||||
await page.getByRole('button', { name: '404-Vorlage anlegen', exact: true }).click();
|
||||
await page.waitForURL(/\/admin\/pages\/[a-f0-9]{32}$/);
|
||||
const id = new URL(page.url()).pathname.split('/').pop();
|
||||
const selectHeading = async () => {
|
||||
await page.locator('#structure [data-block-id="error-heading"]').click();
|
||||
await page.locator('[data-panel="content"]').click();
|
||||
};
|
||||
await selectHeading(); await page.getByLabel('Text', { exact: true }).fill('Hier bist du falsch abgebogen');
|
||||
await page.locator('#save').click(); await page.locator('#save-status').filter({ hasText: 'Entwurf gespeichert' }).waitFor();
|
||||
await checkError('/ein-fehlender-pfad');
|
||||
await page.locator('#publish').click(); await page.locator('#confirm-publish').click();
|
||||
await page.locator('#save-status').filter({ hasText: 'Veröffentlicht' }).waitFor();
|
||||
await checkError('/ein-fehlender-pfad', 'Hier bist du falsch abgebogen');
|
||||
await checkError('/404', 'Hier bist du falsch abgebogen');
|
||||
await audit(visitor); await visitor.screenshot({ path: `${report}/custom-mobile.png`, fullPage: true });
|
||||
await visitor.setViewportSize({ width: 1600, height: 1050 });
|
||||
await checkError('/ein-fehlender-pfad', 'Hier bist du falsch abgebogen'); await audit(visitor);
|
||||
await visitor.screenshot({ path: `${report}/custom-desktop.png`, fullPage: true });
|
||||
const sitemap = await (await context.request.get(origin + '/sitemap.xml')).text();
|
||||
assert.ok(!sitemap.includes(origin + '/404'));
|
||||
checks.push('create an assigned draft, edit in normal editor, keep draft private, publish immediately, assigned own URL remains 404 and out of sitemap');
|
||||
|
||||
await page.goto(origin + '/admin/settings#not-found');
|
||||
await page.getByRole('link', { name: '404-Seite bearbeiten', exact: true }).waitFor();
|
||||
const clear = () => page.getByRole('button', { name: '404-Seite suchen: Auswahl entfernen', exact: true });
|
||||
const saveAssignment = async () => {
|
||||
await page.getByRole('button', { name: '404-Zuordnung speichern', exact: true }).click();
|
||||
await page.waitForURL(origin + '/admin/settings#not-found');
|
||||
};
|
||||
await clear().click(); await saveAssignment();
|
||||
await page.getByText('Aktiv: Standard-404 im Website-Design.', { exact: true }).waitFor();
|
||||
await checkError('/ein-fehlender-pfad');
|
||||
const assignExisting = async () => {
|
||||
await page.getByLabel('404-Seite suchen', { exact: true }).fill('Seite nicht gefunden');
|
||||
await page.locator('#not-found-picker-results [role="option"]').first().waitFor();
|
||||
await page.getByLabel('404-Seite suchen', { exact: true }).press('ArrowDown');
|
||||
await page.getByLabel('404-Seite suchen', { exact: true }).press('Enter');
|
||||
assert.equal(await page.locator('#not-found-form .picker-value').inputValue(), id);
|
||||
await saveAssignment();
|
||||
await page.getByRole('link', { name: '404-Seite bearbeiten', exact: true }).waitFor();
|
||||
};
|
||||
await assignExisting(); await page.reload();
|
||||
assert.equal(await page.locator('#not-found-form .picker-value').inputValue(), id);
|
||||
await checkError('/ein-fehlender-pfad', 'Hier bist du falsch abgebogen');
|
||||
const stale = await context.newPage(); await stale.goto(origin + '/admin/settings#not-found');
|
||||
await clear().click(); await saveAssignment();
|
||||
await stale.getByRole('button', { name: '404-Zuordnung speichern', exact: true }).click();
|
||||
await stale.getByText('Die 404-Zuordnung wurde inzwischen geändert. Lade die Einstellungen neu.', { exact: true }).waitFor();
|
||||
await checkError('/ein-fehlender-pfad'); await stale.close();
|
||||
await assignExisting();
|
||||
for (const width of [1600, 390]) {
|
||||
await page.setViewportSize({ width, height: 1100 }); await page.locator('#not-found').scrollIntoViewIfNeeded();
|
||||
await audit(page); await page.locator('#not-found').screenshot({ path: `${report}/settings-${width}.png` });
|
||||
}
|
||||
checks.push('live search without submitting, keyboard assignment, reload persistence, reset to default and protection against stale settings forms');
|
||||
|
||||
const withoutScripts = await browser.newContext({ javaScriptEnabled: false, viewport: { width: 390, height: 900 } });
|
||||
const plain = await withoutScripts.newPage();
|
||||
await checkError('/Alte/Seite.html', 'Hier bist du falsch abgebogen', plain);
|
||||
await plain.getByRole('link', { name: 'Zur Startseite', exact: true }).click();
|
||||
await plain.waitForURL(origin + '/'); await withoutScripts.close();
|
||||
const normal = await visitor.goto(origin); assert.equal(normal.status(), 200);
|
||||
assert.equal(await visitor.locator('link[rel="canonical"]').getAttribute('href'), origin + '/');
|
||||
await audit(visitor);
|
||||
checks.push('public 404 and working home link without JavaScript; normal home remains 200 with canonical and passes axe');
|
||||
assert.deepEqual(errors, []);
|
||||
await writeFile(`${report}/result.json`, JSON.stringify({ checks, passed: true }, null, 2));
|
||||
console.log('Custom 404 checks passed:', checks.length, 'groups.');
|
||||
} catch (error) {
|
||||
if (page) await page.screenshot({ path: `${report}/failure.png`, fullPage: true }).catch(() => {});
|
||||
throw error;
|
||||
} finally { await browser?.close(); server.kill(); }
|
||||
Reference in new issue
Block a user