Files
vue-journal-layer/tests/seo.test.ts
Jakub Zych a4dca42d77 test(16-05): Journal layer util tests
- node --test over routes, client (fake fetcher, include_empty=1),
  pagination, htmlGate (Go FormatHTML parity cases), sitemap, seo and
  pl/en i18n parity
- package script test and a Testing line in the README
2026-10-09 20:03:46 +02:00

95 lines
3.8 KiB
TypeScript

// 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, htmlToText, listTitle, postSeoMeta, truncateText } from '../app/utils/seo.ts'
import type { JournalPost } from '../app/utils/types.ts'
function post(extra: Partial<JournalPost> = {}): 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('<p>A&nbsp;&amp;&lt;b&gt; &quot;c&quot; &#39;d&#39;</p><script>x()</script><style>p{}</style>\n<p>e</p>'),
'A &<b> "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: '<p>Treść</p>' }))
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 = `<p>${'Grzyby funkcjonalne wspierają zdrowie. '.repeat(10)}</p>`
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('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')
})