feat: schema builder's create flow

This commit is contained in:
Florian Sylvain committed 2026-10-20 00:29:01 +02:00
1 parent f514146fed
commit 083cc9e23e
71 files changed
+7223 -61

No files matched your search

@@ -0,0 +1,107 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { flushPromises } from '@vue/test-utils'
import { addCollection } from '@iconify/vue'
import lucide from 'virtual:lucide-subset'
import FieldInspector from '../FieldInspector.vue'
import { createField } from '@/lib/schemaBuilderModel'
import { mountInApp, text, unmountApp, holder } from './directoryKit'
vi.mock('@/composables/useApi', async (importOriginal) => {
const actual = await importOriginal<typeof import('@/composables/useApi')>()
return { ...actual, useApi: vi.fn() }
})
addCollection(lucide)
afterEach(unmountApp)
function props(extra: Record<string, unknown> = {}) {
return {
field: createField(1, { label: 'Title', key: 'title' }),
errors: {},
disabled: false,
compact: false,
focusToken: 0,
...extra,
}
}
function emitted(name: string) {
return holder.wrapper!.findComponent(FieldInspector).emitted(name) ?? []
}
async function type(selector: string, value: string) {
const input = document.body.querySelector<HTMLInputElement>(selector)!
input.value = value
input.dispatchEvent(new Event('input', { bubbles: true }))
await flushPromises()
}
describe('FieldInspector', () => {
it('emits label, key, type, limit and default edits', async () => {
await mountInApp(
FieldInspector,
props({ field: createField(1, { label: 'T', key: 't', keyMode: 'manual' }) }),
)
await type('input[name="label"]', 'Headline')
await type('input[name="field-key"]', 'headline')
await type('input[name="max"]', '80')
await type('input[name="default"]', 'Draft')
document.body.querySelector<HTMLElement>('[role="radio"][value="number"]')!.click()
await flushPromises()
expect(emitted('label')).toEqual([['Headline']])
expect(emitted('key')).toEqual([['headline']])
expect(emitted('limit')).toEqual([['max', '80']])
expect(emitted('default')).toEqual([['Draft']])
expect(emitted('type')).toEqual([['number']])
})
it('emits the Required switch', async () => {
await mountInApp(FieldInspector, props())
const label = [...document.body.querySelectorAll('label')].find((node) =>
text(node).startsWith('Required'),
)!
document.getElementById(label.getAttribute('for')!)!.click()
await flushPromises()
expect(emitted('required')).toEqual([[true]])
})
it('dims and disables every control while disabled', async () => {
await mountInApp(FieldInspector, props({ disabled: true }))
const root = document.body.querySelector('[data-testid="field-inspector"]')!
expect(root.className).toContain('opacity-60')
expect(
[...root.querySelectorAll<HTMLInputElement>('input')].every((input) => input.disabled),
).toBe(true)
})
it('shows the errors under their controls', async () => {
await mountInApp(
FieldInspector,
props({
field: createField(1, { type: 'number', min: '6', max: '5', defaultValue: '0' }),
errors: {
label: 'Give the field a label.',
key: 'Bad key.',
min: 'Order.',
default: 'Outside.',
},
}),
)
const alerts = [...document.body.querySelectorAll('[role="alert"]')].map(text)
expect(alerts).toEqual(['Give the field a label.', 'Bad key.', 'Outside.', 'Order.'])
})
it('lays the controls out in one column when compact', async () => {
await mountInApp(FieldInspector, props({ compact: true }))
expect(document.body.querySelector('.grid-cols-1')).not.toBeNull()
expect(document.body.querySelector('.grid-cols-2')).not.toBeNull()
})
})