// journal.routes: defaults, overrides, disabling, validation and path filling. import { test } from 'node:test' import assert from 'node:assert/strict' import { DEFAULT_JOURNAL_ROUTES, JOURNAL_ROUTE_NAMES, fillJournalPath, journalPagedRoutes, journalPaginationSource, normalizeJournalRoutes, } from '../app/utils/routes.ts' test('no input gives the defaults', () => { assert.deepEqual(normalizeJournalRoutes(), { ...DEFAULT_JOURNAL_ROUTES }) assert.deepEqual(normalizeJournalRoutes(null), { ...DEFAULT_JOURNAL_ROUTES }) assert.deepEqual(normalizeJournalRoutes({}), { ...DEFAULT_JOURNAL_ROUTES }) }) test('the defaults round-trip through normalization', () => { const once = normalizeJournalRoutes({ ...DEFAULT_JOURNAL_ROUTES }) assert.deepEqual(normalizeJournalRoutes(once), once) }) test('the result is a copy, never the frozen defaults', () => { const routes = normalizeJournalRoutes() assert.notEqual(routes, DEFAULT_JOURNAL_ROUTES) assert.ok(Object.isFrozen(DEFAULT_JOURNAL_ROUTES)) }) test('an override replaces one key and keeps the others', () => { const routes = normalizeJournalRoutes({ category: '/:category' }) assert.equal(routes.category, '/:category') assert.equal(routes.index, DEFAULT_JOURNAL_ROUTES.index) assert.equal(routes.post, DEFAULT_JOURNAL_ROUTES.post) assert.equal(routes.tag, DEFAULT_JOURNAL_ROUTES.tag) assert.equal(routes.author, DEFAULT_JOURNAL_ROUTES.author) }) test('a site URL map normalizes as given (grzybyfunkcjonalne shape)', () => { const input = { index: '/blog', post: '/blog/:slug', category: '/:category', author: '/autor/:author', tag: false, search: '/szukaj' } assert.deepEqual(normalizeJournalRoutes(input), input) }) test('false disables a route; null and undefined keep the default', () => { const routes = normalizeJournalRoutes({ tag: false, author: null, post: undefined }) assert.equal(routes.tag, false) assert.equal(routes.author, DEFAULT_JOURNAL_ROUTES.author) assert.equal(routes.post, DEFAULT_JOURNAL_ROUTES.post) }) test('unknown keys are ignored', () => { assert.deepEqual(normalizeJournalRoutes({ archive: '/archive' }), { ...DEFAULT_JOURNAL_ROUTES }) }) test('a post path without :slug throws naming journal.routes.post', () => { assert.throws(() => normalizeJournalRoutes({ post: '/blog/:id' }), /journal\.routes\.post/) }) test('a param prefix is not the param (:slugs is not :slug)', () => { assert.throws(() => normalizeJournalRoutes({ post: '/blog/:slugs' }), /journal\.routes\.post/) assert.equal(normalizeJournalRoutes({ post: '/blog/:slug(.*)' }).post, '/blog/:slug(.*)') }) test('a category path without :category throws naming journal.routes.category', () => { assert.throws(() => normalizeJournalRoutes({ category: '/kategoria' }), /journal\.routes\.category/) }) test('tag and author paths need their params', () => { assert.throws(() => normalizeJournalRoutes({ tag: '/tags/:slug' }), /journal\.routes\.tag/) assert.throws(() => normalizeJournalRoutes({ author: '/autor' }), /journal\.routes\.author/) }) test('a path without a leading / throws naming the key', () => { assert.throws(() => normalizeJournalRoutes({ index: 'blog' }), /journal\.routes\.index/) assert.throws(() => normalizeJournalRoutes({ post: 'blog/:slug' }), /journal\.routes\.post/) }) test('true and non-string values throw', () => { assert.throws(() => normalizeJournalRoutes({ index: true }), /journal\.routes\.index/) assert.throws(() => normalizeJournalRoutes({ index: 42 }), /journal\.routes\.index/) }) test('route names are stable', () => { assert.deepEqual({ ...JOURNAL_ROUTE_NAMES }, { index: 'journal-index', post: 'journal-post', category: 'journal-category', tag: 'journal-tag', author: 'journal-author', search: 'journal-search', }) }) test('the search route is off by default and takes a plain path', () => { assert.equal(DEFAULT_JOURNAL_ROUTES.search, false) assert.equal(normalizeJournalRoutes().search, false) assert.equal(normalizeJournalRoutes({ search: '/szukaj' }).search, '/szukaj') assert.throws(() => normalizeJournalRoutes({ search: 'szukaj' }), /journal\.routes\.search/) }) test('fillJournalPath encodes a value', () => { assert.equal(fillJournalPath('/blog/:slug', { slug: 'a b' }), '/blog/a%20b') assert.equal(fillJournalPath('/blog/:slug', { slug: 'zażółć' }), '/blog/za%C5%BC%C3%B3%C5%82%C4%87') }) test('fillJournalPath encodes a slug with / per segment', () => { assert.equal(fillJournalPath('/blog/:slug', { slug: '2024/a b?' }), '/blog/2024/a%20b%3F') }) test('fillJournalPath fills several params and numbers', () => { assert.equal(fillJournalPath('/:category/:slug', { category: 'cordyceps', slug: 7 }), '/cordyceps/7') }) test('fillJournalPath drops a custom matcher and a modifier with the param', () => { assert.equal(fillJournalPath('/blog/:slug(.*)', { slug: 'a/b' }), '/blog/a/b') assert.equal(fillJournalPath('/blog/:slug?', { slug: 'x' }), '/blog/x') }) test('fillJournalPath leaves unknown params empty', () => { assert.equal(fillJournalPath('/autor/:author', {}), '/autor/') }) test('journalPagedRoutes: the path form adds //:page per segment', () => { const routes = normalizeJournalRoutes({ search: '/search' }) assert.deepEqual(journalPagedRoutes(routes, { form: 'path', param: 'page', segments: { pl: 'strona', en: 'page' } }), [ { key: 'index', name: 'journal-index-page-strona', path: '/blog/strona/:page(\\d+)' }, { key: 'index', name: 'journal-index-page-page', path: '/blog/page/:page(\\d+)' }, { key: 'category', name: 'journal-category-page-strona', path: '/blog/category/:category/strona/:page(\\d+)' }, { key: 'category', name: 'journal-category-page-page', path: '/blog/category/:category/page/:page(\\d+)' }, { key: 'tag', name: 'journal-tag-page-strona', path: '/blog/tag/:tag/strona/:page(\\d+)' }, { key: 'tag', name: 'journal-tag-page-page', path: '/blog/tag/:tag/page/:page(\\d+)' }, { key: 'author', name: 'journal-author-page-strona', path: '/blog/author/:author/strona/:page(\\d+)' }, { key: 'author', name: 'journal-author-page-page', path: '/blog/author/:author/page/:page(\\d+)' }, ]) }) test('journalPagedRoutes: the query form, no option and disabled views add nothing', () => { const routes = normalizeJournalRoutes({ tag: false, author: false, category: false }) assert.deepEqual(journalPagedRoutes(routes, { form: 'query' }), []) assert.deepEqual(journalPagedRoutes(routes, undefined), []) assert.deepEqual(journalPagedRoutes(routes, { form: 'path' }), [{ key: 'index', name: 'journal-index-page-page', path: '/blog/page/:page(\\d+)' }]) assert.deepEqual(journalPagedRoutes(normalizeJournalRoutes({ index: '/news/' }), { form: 'path', param: 'p', segments: { pl: 'bad/seg' } })[0], { key: 'index', name: 'journal-index-page-p', path: '/news/p/:page(\\d+)', }) }) test('journalPaginationSource: the SEO module\'s resolved option, else the raw seo.pagination (L4 WR-03)', () => { const resolved = { form: 'path', param: 'page', segments: { pl: 'strona', en: 'page' } } assert.deepEqual(journalPaginationSource({ runtimeConfig: { public: { seo: { pagination: resolved } } }, seo: { pagination: { form: 'path' } } }), resolved) assert.deepEqual(journalPaginationSource({ runtimeConfig: { public: {} }, seo: { pagination: { form: 'path' } } }), { form: 'path' }) assert.deepEqual(journalPaginationSource({ runtimeConfig: { public: { seo: { cms: true } } }, seo: { pagination: { form: 'query' } } }), { form: 'query' }) assert.equal(journalPaginationSource({}), undefined) assert.equal(journalPaginationSource(null), undefined) }) test('journalPagedRoutes: an app that set only form path gets the default pl and en segments through the resolved option', () => { // vue-seo-layer's setup writes runtimeConfig.public.seo.pagination with its defaults const nuxtOptions = { seo: { pagination: { form: 'path' } }, runtimeConfig: { public: { seo: { pagination: { form: 'path', param: 'page', segments: { pl: 'strona', en: 'page' } } } } } } const paths = journalPagedRoutes(normalizeJournalRoutes({ category: false, tag: false, author: false }), journalPaginationSource(nuxtOptions)).map((r) => r.path) assert.deepEqual(paths, ['/blog/strona/:page(\\d+)', '/blog/page/:page(\\d+)']) })