// @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 <T>, so it needs an <I18nProvider> (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<Note> = [ { 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 <title>', () => { 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') }) })