optical changes

This commit is contained in:
Harlekin committed 2026-10-25 19:35:32 +01:00
1 parent c8df66d3da
commit 5abad7c475
37 files changed
+110 -737

No files matched your search

+7 -54
View File
@@ -11,7 +11,6 @@ import { createFixture } from './fixtures.js';
let dir;
let server;
let base;
const sent = [];
before(async () => {
dir = await createFixture();
@@ -23,8 +22,7 @@ before(async () => {
siteUrl: 'https://example.org',
});
const content = createContentStore(config);
const mailer = { enabled: true, send: async (message) => sent.push(message) };
const app = await createApp({ config, content, mailer });
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}`;
@@ -50,8 +48,6 @@ test('pages render in both languages', async () => {
'/en/journey',
'/de/kontakt',
'/en/contact',
'/de/datenschutz',
'/en/privacy',
]) {
const res = await get(url);
assert.equal(res.status, 200, url);
@@ -109,53 +105,10 @@ test('sitemap and robots', async () => {
assert.match(await (await get('/robots.txt')).text(), /Sitemap: https:\/\/example\.org\/sitemap\.xml/);
});
const form = (fields) =>
new URLSearchParams({ name: 'Ada', email: 'ada@example.org', message: 'Hallo, ich habe eine Frage.', ts: String(Date.now() - 10_000), ...fields });
const post = (body, headers = {}) =>
get('/de/kontakt', { method: 'POST', body, headers: { Accept: 'application/json', ...headers } });
test('contact form', async (t) => {
await t.test('sends valid messages', async () => {
const res = await post(form());
assert.equal(res.status, 200);
assert.equal((await res.json()).ok, true);
assert.equal(sent.length, 1);
assert.equal(sent[0].replyTo.address, 'ada@example.org');
});
await t.test('rejects invalid input with field errors', async () => {
const res = await post(form({ email: 'nope', message: 'kurz' }));
assert.equal(res.status, 422);
const data = await res.json();
assert.ok(data.errors.email);
assert.ok(data.errors.message);
});
await t.test('quietly drops bots', async () => {
let res = await post(form({ website: 'http://spam' }));
assert.equal((await res.json()).ok, true);
res = await post(form({ ts: String(Date.now()) }));
assert.equal((await res.json()).ok, true);
assert.equal(sent.length, 1);
});
await t.test('blocks foreign origins', async () => {
const res = await post(form(), { Origin: 'https://evil.example' });
assert.equal(res.status, 403);
});
await t.test('rate limits', async () => {
// 4 requests above counted, plus this one = 5, the next one is too many
assert.equal((await post(form())).status, 200);
const res = await post(form());
assert.equal(res.status, 429);
assert.match((await res.json()).message, /Zu viele/);
});
await t.test('works as a plain form post', async () => {
const res = await get('/en/contact', { method: 'POST', body: form() });
assert.equal(res.status, 429);
assert.match(await res.text(), /Too many messages/);
});
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);
});
-2
View File
@@ -16,8 +16,6 @@ export async function createFixture() {
await write('journey.yml', 'entries:\n - title: Live\n from: 2024\n - title: Hidden\n from: 2023\n draft: true\n');
await write('homelab.yml', 'draft: true\nservices:\n - group: Web\n items:\n - name: nginx\n');
await write('pages/journey.de.md', '---\ntitle: Werdegang\n---\nÜber mich');
await write('pages/privacy.de.md', '---\ntitle: Datenschutz\n---\nText');
await write('pages/privacy.en.md', '---\ntitle: Privacy\n---\nText');
await write('projects/alpha/meta.yml', 'date: 2026-05\ntags: [web]\nfeatured: true\ncover: cover.png\nvideo: https://youtu.be/dQw4w9WgXcQ\n');
await write('projects/alpha/de.md', '---\ntitle: Alpha\nsummary: Erstes Projekt\n---\nText mit Bild\n\n![Ein Bild](cover.png)\n');