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() return { ...actual, useApi: vi.fn() } }) addCollection(lucide) afterEach(unmountApp) function props(extra: Record = {}) { 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(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('[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('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() }) })