// @vitest-environment jsdom // // `/` changelog index — lists every release, newest first. The real release // source (`../lib/releases`) reads .mdx via `import.meta.glob` + node-gated // shiki, so we mock it with canned releases and assert the observable render: // config exports, meta, one entry per release, and its tags + link. import { afterEach, describe, expect, test, vi } from 'vitest' import { act, createElement, type ReactNode } from 'react' import { createRoot, type Root } from 'react-dom/client' interface Release { version: string releasedAt: string slug: string tags: ReadonlyArray title: string summary: string html: string } const releases: ReadonlyArray = [ { version: '0.2.0', releasedAt: '2026-06-21', slug: 'v0-2-0', tags: ['feature', 'performance'], title: 'Typed env + faster boot', summary: 'Declare your environment once.', html: '

body

', }, { version: '0.1.0', releasedAt: '2026-01-15', slug: 'v0-1-0', tags: ['feature'], title: 'First release', summary: 'The initial cut.', html: '

body

', }, ] vi.mock('../lib/releases', () => ({ releases })) // `withLocalePrefix` (lib/locale) reads useLocation; the page pins the locale // via useLocale, so any bare path is fine here. vi.mock('@voltro/web', () => ({ useLocation: () => '/' })) // Chrome (meta via getCatalog + the empty state) comes from @voltro/i18n; // stub it (identity catalog defs, useLocale → en, passes children through). vi.mock('@voltro/i18n', () => ({ defineCatalog: (c: T): T => c, defineLocale: () => (c: T): T => c, useLocale: () => 'en', useT: (id: string) => id, T: ({ id }: { id: string }) => id, })) const { default: ChangelogIndex, meta, renderMode, interactive } = await import('./page') ;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true let container: HTMLDivElement let root: Root const render = (node: ReactNode): void => { container = document.createElement('div') document.body.appendChild(container) act(() => { root = createRoot(container) root.render(node) }) } afterEach(() => { if (root) act(() => root.unmount()) container?.remove() document.body.innerHTML = '' }) describe('changelog index — config + localized meta', () => { test('is a static page with zero client JS', () => { expect(renderMode).toBe('static') expect(interactive).toBe('none') }) test('meta() returns a localized title + description per locale', () => { expect(meta({ locale: 'en' }).title).toContain('Changelog') expect(meta({ locale: 'en' }).description).toContain('release notes') expect(meta({ locale: 'de' }).description).toContain('Release-Notes') }) }) describe('changelog index — render', () => { test('renders one entry per release, each linking to / with its tags', () => { render(createElement(ChangelogIndex)) const entries = Array.from(container.querySelectorAll('.changelog-entry')) expect(entries).toHaveLength(2) const links = Array.from(container.querySelectorAll('.changelog-entry h2 a')) expect(links.map((a) => a.getAttribute('href'))).toEqual(['/v0-2-0', '/v0-1-0']) expect(links[0]!.textContent).toBe('Typed env + faster boot') // The first entry surfaces both of its tags. const firstTags = Array.from(entries[0]!.querySelectorAll('.changelog-tag')).map( (t) => t.textContent, ) expect(firstTags).toEqual(['feature', 'performance']) expect(container.textContent).toContain('v0.2.0') }) })