Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/health-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jobs:
linkinator:
name: Crawl live sites for broken links
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Setup Node
uses: actions/setup-node@v4
Expand Down
1 change: 1 addition & 0 deletions 404.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<base href="/">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://api.github.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'">
<meta http-equiv="X-Content-Type-Options" content="nosniff">
<meta name="referrer" content="strict-origin-when-cross-origin">
Expand Down
33 changes: 33 additions & 0 deletions tests/not-found.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// @ts-check
const { test, expect } = require('@playwright/test');

const BASE = process.env.BASE_URL || 'http://localhost:8080';
const MISSING = '/no/such/page/';

// GitHub Pages serves /404.html at whatever path was requested, so the page's
// stylesheet, scripts and links must resolve from the site root. Relative
// URLs resolved under the missing path instead: every asset 404'd, and a link
// checker following them generated ever-deeper missing paths without end.
test.describe('404 page served at a nested missing path', () => {
test('its assets load and its links resolve from the site root', async ({ page }) => {
const failed = [];
page.on('response', (r) => {
const url = r.url();
if (url.startsWith(BASE) && r.status() >= 400 && new URL(url).pathname !== MISSING) {
failed.push(`${r.status()} ${url}`);
}
});
const res = await page.goto(`${BASE}${MISSING}`);
expect(res && res.status()).toBe(404);
await page.waitForLoadState('networkidle');
expect(failed).toEqual([]);

const internal = await page.$$eval('a[href]', (as) =>
as.map((a) => a.href).filter((h) => h.startsWith(location.origin)),
);
expect(internal.length).toBeGreaterThan(0);
for (const href of internal) {
expect(new URL(href).pathname.startsWith(MISSING), href).toBe(false);
}
});
});
Loading