test(10.1-04): bring the SPA extension point under Vitest

- WidgetField: skeleton and aria-busy, the 5000 ms whenDefined timeout,
  script failure, attributes only, fill-values and locale sync, one POST
  while busy, fill-key-only patching, danger toasts, unmount
- PartialHost and partialNodes: exhaustive tag, attribute and URL
  allowlist, depth and node caps, skeleton sizes, empty, failure,
  busy refetch and ?id= only with a record
- PartialField, ListToolbar, ListView, registry, formState, FormField and
  FormView: group labels, registered toolbar actions, header refetch
  rules, form context provision and asset loading
This commit is contained in:
Jakub Zych
2026-09-29 02:59:13 +02:00
parent 6b0ac15086
commit 11c4e5466b
9 changed files with 1194 additions and 9 deletions

View File

@@ -3,8 +3,8 @@ import { mount } from '@vue/test-utils'
import FieldRenderer from '../../src/components/form/FieldRenderer.vue'
import FormErrorBanner from '../../src/components/form/FormErrorBanner.vue'
import FormField from '../../src/components/form/FormField.vue'
import { formField } from '../fixtures/typed'
import { resetState } from '../helpers'
import { clone, extensionFormSchemaFixture, formField } from '../fixtures/typed'
import { mockApi, resetState } from '../helpers'
beforeEach(() => {
resetState()
@@ -55,6 +55,44 @@ describe('form field', () => {
})
})
describe('widget and partial rows (Phase 10.1)', () => {
function extensionField(name: string, change: Record<string, unknown> = {}) {
const field = clone(extensionFormSchemaFixture).data.fields.find((item) => item.name === name)
if (!field) {
throw new Error(`fixture field ${name} missing`)
}
return { ...field, ...change }
}
it.each(['lookup', 'summary'])('labels the %s group with a span the group points at', (name) => {
mockApi({})
const wrapper = mount(FormField, {
props: { field: extensionField(name, { required: true }), modelValue: undefined, idPrefix: 'f', source: null },
})
const label = wrapper.find(`#f-${name}-label`)
expect(label.element.tagName).toBe('SPAN')
expect(label.classes()).toContain('font-semibold')
expect(label.find('[aria-hidden="true"]').text()).toBe('*')
expect(wrapper.find('label').exists()).toBe(false)
expect(wrapper.find(`#f-${name}`).attributes('aria-labelledby')).toBe(`f-${name}-label`)
})
it.each(['lookup', 'summary'])('shows no label row for an unlabelled %s', (name) => {
mockApi({})
const wrapper = mount(FormField, { props: { field: extensionField(name, { label: '' }), modelValue: undefined, idPrefix: 'f', source: null } })
expect(wrapper.find(`#f-${name}-label`).exists()).toBe(false)
expect(wrapper.find('label').exists()).toBe(false)
expect(wrapper.find(`#f-${name}`).attributes('aria-labelledby')).toBeUndefined()
})
it('points the widget group at its comment', () => {
mockApi({})
const wrapper = mount(FormField, { props: { field: extensionField('lookup'), modelValue: undefined, idPrefix: 'f', source: null } })
expect(wrapper.find('#f-lookup-comment').text()).toBe('Fills in the name. Save to keep it.')
expect(wrapper.find('#f-lookup').attributes('aria-describedby')).toBe('f-lookup-comment')
})
})
describe('field renderer', () => {
it('picks the control by type and forwards the shared props and updates', async () => {
const wrapper = mount(FieldRenderer, {