Files
2026-10-25 20:29:26 +01:00

116 lines
4.0 KiB
JavaScript

import assert from 'node:assert/strict';
import { rm } from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { after, before, test } from 'node:test';
import { createApp } from '../src/app.js';
import { loadConfig } from '../src/config.js';
import { createContentStore } from '../src/content.js';
import { createFixture } from './fixtures.js';
let dir;
let server;
let base;
before(async () => {
dir = await createFixture();
const config = loadConfig({
production: true,
showDrafts: false,
contentDir: dir,
dataDir: undefined,
cacheDir: path.join(os.tmpdir(), `site-cache-${process.pid}`),
siteUrl: 'https://example.org',
});
const content = createContentStore(config);
const app = await createApp({ config, content });
server = app.listen(0);
await new Promise((resolve) => server.once('listening', resolve));
base = `http://127.0.0.1:${server.address().port}`;
});
after(async () => {
server.close();
await rm(dir, { recursive: true, force: true });
await rm(path.join(os.tmpdir(), `site-cache-${process.pid}`), { recursive: true, force: true });
});
const get = (url, options) => fetch(base + url, { redirect: 'manual', ...options });
test('pages render in both languages', async () => {
for (const url of [
'/de/',
'/en/',
'/de/portfolio',
'/en/portfolio',
'/de/portfolio/alpha',
'/en/portfolio/alpha',
'/de/werdegang',
'/en/journey',
'/de/kontakt',
'/en/contact',
]) {
const res = await get(url);
assert.equal(res.status, 200, url);
assert.match(res.headers.get('content-security-policy'), /script-src 'self'/);
}
});
test('the root redirects by Accept-Language', async () => {
let res = await get('/', { headers: { 'Accept-Language': 'en-GB,en;q=0.9' } });
assert.equal(res.status, 302);
assert.equal(res.headers.get('location'), '/en/');
res = await get('/', { headers: { 'Accept-Language': 'fr' } });
assert.equal(res.headers.get('location'), '/de/');
});
test('drafts, hidden pages and wrong slugs are 404', async () => {
for (const url of ['/de/portfolio/beta', '/de/homelab', '/de/journey', '/en/werdegang', '/de/nope', '/xx', '/admin']) {
const res = await get(url);
assert.equal(res.status, 404, url);
}
});
test('homepage hides links to pages that do not exist yet', async () => {
const html = await (await get('/de/')).text();
assert.doesNotMatch(html, /href="\/de\/homelab"/);
assert.match(html, /<h1 class="hero__title"/);
});
test('portfolio filter works without JavaScript', async () => {
const html = await (await get('/de/portfolio?tag=nothing')).text();
assert.doesNotMatch(html, /data-tags="web" hidden/);
});
test('media: resized images, no traversal, no source files', async () => {
let res = await get('/media/projects/alpha/cover.png?w=480');
assert.equal(res.status, 200);
assert.equal(res.headers.get('content-type'), 'image/webp');
res = await get('/media/projects/alpha/cover.png?w=480&v=abc');
assert.match(res.headers.get('cache-control'), /immutable/);
res = await get('/media/projects/alpha/cover.png?w=123');
assert.equal(res.status, 404);
for (const url of ['/media/site.yml', '/media/projects/alpha/de.md', '/media/..%2Fpackage.json', '/media/projects/%2e%2e/%2e%2e/site.yml']) {
res = await get(url);
assert.equal(res.status, 404, url);
}
});
test('sitemap and robots', async () => {
const sitemap = await (await get('/sitemap.xml')).text();
assert.match(sitemap, /<loc>https:\/\/example\.org\/en\/portfolio\/alpha<\/loc>/);
assert.doesNotMatch(sitemap, /beta|homelab/);
assert.match(await (await get('/robots.txt')).text(), /Sitemap: https:\/\/example\.org\/sitemap\.xml/);
});
test('contact page links to the email address and has no form', async () => {
const html = await (await get('/de/kontakt')).text();
assert.match(html, /href="mailto:test@example\.org"/);
assert.doesNotMatch(html, /<form/);
const res = await get('/de/kontakt', { method: 'POST' });
assert.equal(res.status, 404);
});