108 lines
3.4 KiB
TypeScript
108 lines
3.4 KiB
TypeScript
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.', 'Order.', 'Outside.'])
|
|
})
|
|
|
|
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()
|
|
})
|
|
})
|