feat: Journal list, category pages, pagination, tokens and pl/en strings

- index and category pages paginate with ?page=N and 404 out of range
- category content renders above the list through the html gate
- related posts fall back to the latest posts
- --journal-* tokens and neutral base styles
- pl/en strings registered only for locales the app enables
This commit is contained in:
2026-10-09 18:02:48 +02:00
parent 6f6792f51c
commit c408eeb2ca
23 changed files with 807 additions and 21 deletions

View File

@@ -7,3 +7,19 @@
404 /blog/missing-post
200 /blog/unsafe-sample contains=journal-post-body--blocked notcontains=alert(
404 /blog/draft-note
# Post index and pagination (9 per page, 13 published posts: 2 pages)
200 /blog contains=Hello Acme
200 /blog?page=2
404 /blog?page=3
404 /blog?page=abc
404 /blog?page=0
200 /pl/blog contains=Najnowsze wpisy
# Categories (include_empty=1; content renders above the list)
200 /blog/category/news contains=Acme news marker
200 /blog/category/quiet contains=There are no posts in this category yet.
404 /blog/category/nope
# Related posts fall back to the latest posts for a post without a category
200 /blog/uncategorised-note contains=Hello Acme

View File

@@ -10,7 +10,8 @@
// end of the line, so it may contain spaces. Requests are plain GETs that do
// not follow redirects and carry `X-Forwarded-For: 203.0.113.7`. After the
// lines, at least one fixture API request must have carried that address
// (the SSR fetches forward the visitor's x-forwarded-for).
// (the SSR fetches forward the visitor's x-forwarded-for), and every
// categories request must have carried include_empty=1.
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
@@ -114,6 +115,13 @@ async function main() {
if (!api.requests.some((r) => (r.xff ?? '').includes(VISITOR_IP))) {
failures.push('x-forwarded-for: x-forwarded-for not forwarded')
}
// Categories are always requested with include_empty=1, so empty ones resolve.
for (const r of api.requests) {
const u = new URL(r.url, 'http://fixture.local')
if (u.pathname.endsWith('/categories') && u.searchParams.get('include_empty') !== '1') {
failures.push(`include_empty: ${r.url} requested without include_empty=1`)
}
}
} finally {
if (nuxt) await nuxt.close()
await api.close()