// @vitest-environment jsdom
//
// `/` SSR-fed-by-the-api page. The loader pulls `notes.list` from the api's
// rpc via the server-only `query` fn; `meta` is a function of the loader data
// so the
reflects the real row count. The component renders the SSR'd
// snapshot, then upgrades to `useSubscription` on the client. We mock
// `@voltro/web` (loader hook) + `@voltro/client` (subscription).
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
import { act, createElement, type ReactNode } from 'react'
import { createRoot, type Root } from 'react-dom/client'
import { I18nProvider } from '@voltro/i18n'
import enCatalog from '../locales/en'
const loaderData = vi.fn<() => unknown>()
const useSubscription = vi.fn<() => { data: unknown }>()
vi.mock('@voltro/web', () => ({ useLoaderData: () => loaderData() }))
vi.mock('@voltro/client', () => ({ useSubscription: () => useSubscription() }))
const { default: Home, meta, loader, renderMode } = await import('./page')
;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
interface Note { id: string; title: string; body: string; done: boolean }
let container: HTMLDivElement
let root: Root
// The page uses , so it needs an (real English catalog).
const render = (node: ReactNode): void => {
container = document.createElement('div')
document.body.appendChild(container)
act(() => {
root = createRoot(container)
root.render(
createElement(I18nProvider, { locale: 'en', messages: enCatalog, defaultLocale: 'en', children: node }),
)
})
}
beforeEach(() => {
loaderData.mockReset()
useSubscription.mockReset()
useSubscription.mockReturnValue({ data: undefined })
})
afterEach(() => {
if (root) act(() => root.unmount())
container?.remove()
document.body.innerHTML = ''
})
const sample: ReadonlyArray = [
{ id: 'n_1', title: 'First note', body: 'hello', done: false },
{ id: 'n_2', title: 'Second note', body: '', done: true },
]
describe('index — loader', () => {
test('drains `notes.list` from the api via the server-side query fn', async () => {
const query = vi.fn(async () => sample)
const data = await loader({ query } as never)
expect(query).toHaveBeenCalledWith('notes.list', {})
expect(data.notes).toEqual(sample)
})
test('returns an empty list when no server-side query fn is present (client nav)', async () => {
const data = await loader({ query: undefined } as never)
expect(data.notes).toEqual([])
})
})
describe('index — meta', () => {
test('bakes the row count into the ', () => {
expect(renderMode).toBe('ssr')
expect(meta({ locale: 'en', loaderData: { notes: sample } }).title).toContain('2 notes')
})
})
describe('index — render', () => {
test('renders the SSR snapshot rows when the subscription has no data yet', () => {
loaderData.mockReturnValue({ notes: sample })
render(createElement(Home))
const items = Array.from(container.querySelectorAll('li'))
expect(items).toHaveLength(2)
expect(container.textContent).toContain('First note')
expect(container.textContent).toContain('✓ done')
})
test('the live subscription data overrides the SSR snapshot once connected', () => {
loaderData.mockReturnValue({ notes: sample })
useSubscription.mockReturnValue({
data: [{ id: 'n_9', title: 'Live note', body: '', done: false }],
})
render(createElement(Home))
const items = Array.from(container.querySelectorAll('li'))
expect(items).toHaveLength(1)
expect(container.textContent).toContain('Live note')
})
test('shows the empty state when there are no notes', () => {
loaderData.mockReturnValue({ notes: [] })
render(createElement(Home))
expect(container.textContent).toContain('No notes yet')
})
})