- the query and path forms from the resolve answer, the site answer and
the layer option, page 1 unnumbered
- prev and next links at the ends, %pages% from the page count
- routeServes reads {param} and :param alike and ignores the query part
107 lines
4.5 KiB
TypeScript
107 lines
4.5 KiB
TypeScript
// usePagination (D-40 to D-42, UI-SPEC L2) with the Nuxt composables it
|
||
// calls stubbed on globalThis: the page hrefs in the form the resolve
|
||
// answer, the site answer or the layer option names; the prev and next
|
||
// head links at the ends; the number of pages handed to the CMS head,
|
||
// whose %pages% seoFillPages replaces.
|
||
import assert from 'node:assert/strict'
|
||
import test from 'node:test'
|
||
import { computed, ref, toValue, watch } from 'vue'
|
||
import { seoFillPages } from '../app/utils/seo.ts'
|
||
|
||
interface Stubs {
|
||
fullPath: string
|
||
answer: unknown
|
||
site: unknown
|
||
option?: unknown
|
||
}
|
||
|
||
const g = globalThis as Record<string, unknown>
|
||
let heads: Array<() => { link: Array<{ rel: string; href: string }> }> = []
|
||
const states = new Map<string, ReturnType<typeof ref>>()
|
||
|
||
function stub(s: Stubs) {
|
||
heads = []
|
||
states.clear()
|
||
Object.assign(g, {
|
||
computed,
|
||
toValue,
|
||
watch,
|
||
useRoute: () => ({ fullPath: s.fullPath }),
|
||
useSeoAnswer: () => ref(s.answer),
|
||
useRuntimeConfig: () => ({ public: { seo: s.option === undefined ? {} : { pagination: s.option } } }),
|
||
useNuxtData: () => ({ data: ref(s.site) }),
|
||
useState: (key: string, init: () => unknown) => {
|
||
if (!states.has(key)) states.set(key, ref(init()))
|
||
return states.get(key)
|
||
},
|
||
useHead: (input: { value: { link: Array<{ rel: string; href: string }> } }) => heads.push(() => input.value),
|
||
})
|
||
}
|
||
|
||
const { usePagination, useSeoPages } = await import('../app/composables/usePagination.ts')
|
||
|
||
test('the query form from the resolve answer: page 1 unnumbered, other parameters kept', () => {
|
||
stub({ fullPath: '/blog?sort=new&page=2', answer: { locale: 'pl', pagination: { form: 'query', param: 'page', segment: '' } }, site: null })
|
||
const p = usePagination({ current: 2, last: 4 })
|
||
assert.equal(p.href(1), '/blog?sort=new')
|
||
assert.equal(p.href(3), '/blog?sort=new&page=3')
|
||
assert.deepEqual(p.form.value, { form: 'query', param: 'page', segment: '' })
|
||
})
|
||
|
||
test('the path form from the site answer with the locale segment', () => {
|
||
stub({
|
||
fullPath: '/en/blog/page/2',
|
||
answer: { locale: 'en' },
|
||
site: { default_locale: 'pl', pagination: { form: 'path', param: 'page', segments: { pl: 'strona', en: 'page' } } },
|
||
})
|
||
const p = usePagination({ current: 2, last: 3 })
|
||
assert.equal(p.href(1), '/en/blog')
|
||
assert.equal(p.href(3), '/en/blog/page/3')
|
||
stub({
|
||
fullPath: '/blog',
|
||
answer: null,
|
||
site: { default_locale: 'pl', pagination: { form: 'path', segments: { pl: 'strona' } } },
|
||
})
|
||
assert.equal(usePagination({ current: 1, last: 3 }).href(2), '/blog/strona/2')
|
||
})
|
||
|
||
test('the layer option without a CMS answer, with the given locale', () => {
|
||
stub({ fullPath: '/de/blog', answer: null, site: null, option: { form: 'path', segments: { de: 'seite' } } })
|
||
const p = usePagination({ current: 1, last: 2, locale: 'de' })
|
||
assert.equal(p.href(2), '/de/blog/seite/2')
|
||
})
|
||
|
||
test('prev and next head links: none on a single page, only next on the first, only prev on the last', () => {
|
||
const links = (current: number, last: number) => {
|
||
stub({ fullPath: '/blog', answer: { pagination: { form: 'query', param: 'page' } }, site: null })
|
||
usePagination({ current, last })
|
||
return heads[0]!().link.map((l) => `${l.rel} ${l.href}`)
|
||
}
|
||
assert.deepEqual(links(1, 1), [])
|
||
assert.deepEqual(links(1, 3), ['next /blog?page=2'])
|
||
assert.deepEqual(links(2, 3), ['prev /blog', 'next /blog?page=3'])
|
||
assert.deepEqual(links(3, 3), ['prev /blog?page=2'])
|
||
assert.deepEqual(links(5, 3), [])
|
||
})
|
||
|
||
test('the number of pages for %pages%: set from last, null without pages', () => {
|
||
stub({ fullPath: '/blog', answer: null, site: null })
|
||
usePagination({ current: 1, last: 7 })
|
||
assert.equal(useSeoPages().value, 7)
|
||
const head = seoFillPages({ title: 'Blog – strona 1 z %pages%', meta: [{ content: 'Wpisy (%pages% stron)' }, { content: 'x' }] }, useSeoPages().value as number)
|
||
assert.equal(head.title, 'Blog – strona 1 z 7')
|
||
assert.equal(head.meta[0]!.content, 'Wpisy (7 stron)')
|
||
stub({ fullPath: '/blog', answer: null, site: null })
|
||
usePagination({ current: 1, last: 0 })
|
||
assert.equal(useSeoPages().value, null)
|
||
assert.equal(seoFillPages({ title: '%pages%', meta: [] }, null).title, '%pages%')
|
||
})
|
||
|
||
test('current and last are whole numbers: current at least 1, last at least 0', () => {
|
||
stub({ fullPath: '/blog', answer: null, site: null })
|
||
const p = usePagination({ current: 0, last: -2.5 })
|
||
assert.equal(p.current.value, 1)
|
||
assert.equal(p.last.value, 0)
|
||
assert.equal(usePagination({ current: 2.9, last: '4' as unknown as number }).current.value, 2)
|
||
})
|