// @vitest-environment jsdom // // `/schema-ui` — the schema-driven-UI twin of the notes page. It binds // to the `notes.create` mutation and to the `notes.list` // query, with title + body Fields. The real AutoForm/DataTable need a live // framework runtime; here we mock them (from @voltro/web) as prop-recording // sentinels and assert the page passes the right descriptor bindings + defaults. 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' interface AutoFormProps { api: string mutation: string defaults?: Record submitLabel?: string children?: () => ReactNode } interface DataTableProps { api: string query: string emptyText?: string } const autoFormProps: AutoFormProps[] = [] const dataTableProps: DataTableProps[] = [] vi.mock('@voltro/web', () => ({ AutoForm: (props: AutoFormProps) => { autoFormProps.push(props) return createElement('div', { 'data-testid': 'auto-form' }, props.children?.()) }, DataTable: (props: DataTableProps) => { dataTableProps.push(props) return createElement('div', { 'data-testid': 'data-table' }) }, Field: ({ name }: { name: string }) => createElement('span', { 'data-testid': `field-${name}` }, name), })) const { default: SchemaUiPage } = await import('./page') ;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true let container: HTMLDivElement let root: Root // The page uses /useTFn, 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(() => { autoFormProps.length = 0 dataTableProps.length = 0 }) afterEach(() => { if (root) act(() => root.unmount()) container?.remove() document.body.innerHTML = '' }) describe('schema-ui — descriptor bindings', () => { test('binds AutoForm to notes.create with the tenant default + title & body Fields', () => { render(createElement(SchemaUiPage)) expect(autoFormProps).toHaveLength(1) expect(autoFormProps[0]).toMatchObject({ api: 'app', mutation: 'notes.create', defaults: { tenantId: 'acme', done: false }, }) expect(container.querySelector('[data-testid="field-title"]')).toBeTruthy() expect(container.querySelector('[data-testid="field-body"]')).toBeTruthy() }) test('binds DataTable to the notes.list query with empty text', () => { render(createElement(SchemaUiPage)) expect(dataTableProps).toHaveLength(1) expect(dataTableProps[0]).toMatchObject({ api: 'app', query: 'notes.list' }) expect(dataTableProps[0]!.emptyText).toContain('No notes yet') }) test('renders the Schema-driven UI heading', () => { render(createElement(SchemaUiPage)) const headings = Array.from(container.querySelectorAll('h1,h2')).map((h) => h.textContent) expect(headings.some((h) => h?.includes('Schema-driven UI'))).toBe(true) }) })