// SEO values for Journal pages: description fallback, absolute og:image and // list titles. import { test } from 'node:test' import assert from 'node:assert/strict' import { absoluteUrl, cmsHeadPresent, htmlToText, journalArticleMeta, journalItemList, journalPostTitleTag, journalSiteOrigin, listTitle, postSeoMeta, truncateText } from '../app/utils/seo.ts' import type { JournalPost } from '../app/utils/types.ts' function post(extra: Partial = {}): JournalPost { return { id: 1, title: 'Najnowsze Badania nad Cordycepsem', slug: 'blogbadania-nad-cordycepsem', excerpt: null, content: null, content_html: null, reading_time: 1, published: true, published_at: '2024-06-12T06:00:00+00:00', is_pinned: false, metadata: null, sources: null, author: null, categories: [], tags: [], featured_images: [], featured_image_url: null, translations: null, created_at: '2024-06-12T06:00:00+00:00', updated_at: '2024-06-13T06:00:00+00:00', ...extra, } } test('htmlToText drops tags, scripts and styles and decodes entities', () => { assert.equal( htmlToText('

A &<b> "c" 'd'

\n

e

'), 'A & "c" \'d\' e', ) assert.equal(htmlToText(null), '') }) test('truncateText keeps short text and cuts long text at a word with an ellipsis', () => { assert.equal(truncateText('krótki', 160), 'krótki') const long = 'słowo '.repeat(40).trim() const cut = truncateText(long, 160) assert.ok(cut.length <= 160) assert.ok(cut.endsWith('…')) assert.ok(!cut.endsWith(' …')) assert.equal(truncateText('x'.repeat(200), 10), `${'x'.repeat(9)}…`) }) test('the excerpt is the description when set', () => { const meta = postSeoMeta(post({ excerpt: 'Co mówi nauka.', content_html: '

Treść

' })) assert.equal(meta.description, 'Co mówi nauka.') assert.equal(meta.ogDescription, 'Co mówi nauka.') assert.equal(meta.title, 'Najnowsze Badania nad Cordycepsem') assert.equal(meta.ogTitle, meta.title) assert.equal(meta.ogType, 'article') }) test('without an excerpt the description is the first 160 characters of the body text', () => { const body = `

${'Grzyby funkcjonalne wspierają zdrowie. '.repeat(10)}

` const meta = postSeoMeta(post({ excerpt: '', content_html: body })) assert.ok(meta.description.length <= 160) assert.ok(meta.description.startsWith('Grzyby funkcjonalne wspierają zdrowie.')) assert.ok(!meta.description.includes('<')) }) test('og:image becomes absolute against the site URL', () => { const url = '/storage/app/uploads/public/665/f1e/2a3/665f1e2a3b4c5d6e7f8091.png' assert.equal(postSeoMeta(post({ featured_image_url: url }), 'https://grzybyfunkcjonalne.pl/').ogImage, `https://grzybyfunkcjonalne.pl${url}`) assert.equal(postSeoMeta(post({ featured_image_url: 'https://cdn.test/a.png' }), 'https://x.test').ogImage, 'https://cdn.test/a.png') assert.equal(postSeoMeta(post()).ogImage, undefined) }) test('article times come from published_at and updated_at', () => { const meta = postSeoMeta(post()) assert.equal(meta.articlePublishedTime, '2024-06-12T06:00:00+00:00') assert.equal(meta.articleModifiedTime, '2024-06-13T06:00:00+00:00') const bare = postSeoMeta(post({ published_at: null, updated_at: null })) assert.equal(bare.articlePublishedTime, undefined) assert.equal(bare.articleModifiedTime, undefined) }) test('journalArticleMeta: og:type article and the post times, which the CMS never sets (L4 WR-02)', () => { assert.deepEqual(journalArticleMeta(post()), { ogType: 'article', articlePublishedTime: '2024-06-12T06:00:00+00:00', articleModifiedTime: '2024-06-13T06:00:00+00:00', }) assert.deepEqual(journalArticleMeta(post({ published_at: null, updated_at: null })), { ogType: 'article' }) assert.deepEqual(journalArticleMeta(post({ updated_at: null })), { ogType: 'article', articlePublishedTime: '2024-06-12T06:00:00+00:00' }) }) test('absoluteUrl passes through absolute URLs and a missing site URL', () => { assert.equal(absoluteUrl('a.png', 'https://x.test'), 'https://x.test/a.png') assert.equal(absoluteUrl('HTTPS://y.test/a.png', 'https://x.test'), 'HTTPS://y.test/a.png') assert.equal(absoluteUrl('/a.png', null), '/a.png') }) test('listTitle adds the page title only after page 1', () => { assert.equal(listTitle('Artykuły', 1, 'Strona 1'), 'Artykuły') assert.equal(listTitle('Artykuły', 2, 'Strona 2'), 'Artykuły · Strona 2') }) test('journalPostTitleTag follows the Journal H1 setting unless the option forces a tag', () => { assert.equal(journalPostTitleTag('auto', true), 'h1') assert.equal(journalPostTitleTag('auto', undefined), 'h1') assert.equal(journalPostTitleTag('auto', null), 'h1') assert.equal(journalPostTitleTag('auto', false), 'p') assert.equal(journalPostTitleTag(undefined, false), 'p') assert.equal(journalPostTitleTag('h2', false), 'p') assert.equal(journalPostTitleTag('h1', false), 'h1') assert.equal(journalPostTitleTag('p', true), 'p') }) test('cmsHeadPresent is true only for an ok answer with head values', () => { assert.equal(cmsHeadPresent({ action: 'ok', seo: { title: 'x' } }), true) assert.equal(cmsHeadPresent({ action: 'ok', seo: null }), false) assert.equal(cmsHeadPresent({ action: 'not_found', seo: null }), false) assert.equal(cmsHeadPresent(null), false) assert.equal(cmsHeadPresent(undefined), false) }) test('journalSiteOrigin prefers the CMS page URL origin over the request origin', () => { assert.equal(journalSiteOrigin('https://acme.test/blog/x', 'http://127.0.0.1:3000'), 'https://acme.test') assert.equal(journalSiteOrigin('/blog/x', 'http://127.0.0.1:3000'), 'http://127.0.0.1:3000') assert.equal(journalSiteOrigin('', 'http://127.0.0.1:3000'), 'http://127.0.0.1:3000') assert.equal(journalSiteOrigin(null, 'http://127.0.0.1:3000'), 'http://127.0.0.1:3000') assert.equal(journalSiteOrigin('javascript:alert(1)', 'http://h.test'), 'http://h.test') }) test('journalItemList: one ListItem per linked post, absolute URLs, positions continuing across pages', () => { assert.deepEqual(journalItemList('https://acme.test/blog?page=2', 'https://acme.test', ['/blog/a', null, '/blog/b'], 10), { '@type': 'ItemList', '@id': 'https://acme.test/blog?page=2#itemlist', itemListElement: [ { '@type': 'ListItem', position: 10, url: 'https://acme.test/blog/a' }, { '@type': 'ListItem', position: 11, url: 'https://acme.test/blog/b' }, ], }) assert.equal(journalItemList('https://acme.test/blog', 'https://acme.test', []), null) assert.equal(journalItemList('https://acme.test/blog', 'https://acme.test', [null, '']), null) assert.equal(journalItemList('https://acme.test/blog#x', 'https://acme.test', ['/p'])?.['@id'], 'https://acme.test/blog#itemlist') })