The post page links previous_in_category and next_in_category (the Journal's opt-in scored related mode) as server-rendered links with the title in the anchor text when the Journal answers one, else the site-wide previous_post and next_post (postNeighbours). Types, a fixture post and expectation lines for both cases.
65 lines
2.7 KiB
TypeScript
65 lines
2.7 KiB
TypeScript
// The post page's payload: the body is sanitized, never blanked, and the raw
|
|
// source, translations and related post bodies stay out of it.
|
|
import { test } from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { postNeighbours, toPostView } from '../app/utils/postView.ts'
|
|
import type { JournalPost, JournalPostDetail } from '../app/utils/types.ts'
|
|
|
|
function post(overrides: Partial<JournalPost> = {}): JournalPost {
|
|
return {
|
|
id: 1,
|
|
title: 'Hello',
|
|
slug: 'hello',
|
|
excerpt: null,
|
|
content: 'source',
|
|
content_html: '<p>Hello</p>',
|
|
reading_time: 1,
|
|
published: true,
|
|
published_at: '2026-01-01T00:00:00+00:00',
|
|
is_pinned: false,
|
|
metadata: null,
|
|
sources: null,
|
|
author: null,
|
|
categories: [],
|
|
tags: [],
|
|
featured_images: [],
|
|
featured_image_url: null,
|
|
translations: { en: { title: 'Hello' } },
|
|
created_at: null,
|
|
updated_at: null,
|
|
...overrides,
|
|
}
|
|
}
|
|
|
|
function detail(overrides: Partial<JournalPostDetail> = {}): JournalPostDetail {
|
|
return { ...post(), previous_post: null, next_post: null, related_posts: [], ...overrides }
|
|
}
|
|
|
|
test('the body is sanitized and the safe parts are kept', () => {
|
|
const view = toPostView(detail({ content_html: '<p>Data: 12 maja 2024</p><script>alert(1)</script><p>After</p>' }))
|
|
assert.equal(view.content_html, '<p>Data: 12 maja 2024</p><p>After</p>')
|
|
})
|
|
|
|
test('source, translations and related post bodies stay out of the payload', () => {
|
|
const view = toPostView(detail({ related_posts: [post({ id: 2, slug: 'other' })] }))
|
|
assert.equal(view.content, null)
|
|
assert.equal(view.translations, null)
|
|
assert.equal(view.related_posts.length, 1)
|
|
assert.equal(view.related_posts[0]!.content_html, null)
|
|
assert.equal(view.related_posts[0]!.content, null)
|
|
assert.equal(view.related_posts[0]!.translations, null)
|
|
})
|
|
|
|
test('a missing body becomes an empty string', () => {
|
|
assert.equal(toPostView(detail({ content_html: null })).content_html, '')
|
|
})
|
|
|
|
test('postNeighbours prefers the in-category neighbours when the Journal answers one', () => {
|
|
const site = { previous_post: { slug: 'p', title: 'P' }, next_post: { slug: 'n', title: 'N' } }
|
|
assert.deepEqual(postNeighbours(site), { previous: site.previous_post, next: site.next_post, inCategory: false })
|
|
assert.deepEqual(postNeighbours({ ...site, previous_in_category: null, next_in_category: null }), { previous: site.previous_post, next: site.next_post, inCategory: false })
|
|
const cat = { slug: 'c', title: 'C' }
|
|
assert.deepEqual(postNeighbours({ ...site, previous_in_category: null, next_in_category: cat }), { previous: null, next: cat, inCategory: true })
|
|
assert.deepEqual(postNeighbours({}), { previous: null, next: null, inCategory: false })
|
|
})
|