import { describe, it, expect } from 'vitest'; import { validateForResource } from '../src/admin/validate'; import type { Resource } from '../src/admin/resource-types'; // Minimal resource fixture covering the field kinds the validator handles. const resource: Resource = { key: 'fixtures', label: 'Fixture', pluralLabel: 'Fixtures', singularLabel: 'Fixture', groupKey: 'system', list: { queryFn: () => [], columns: [] }, ops: {}, form: { fields: [ { key: 'title', label: 'Title', kind: 'text', required: true, maxLength: 5 }, { key: 'kind', label: 'Kind', kind: 'select', options: [{ value: 'a', label: 'A' }] }, { key: 'options', label: 'Options', kind: 'multi-text', minItems: 2, maxItems: 4, }, { key: 'count', label: 'Count', kind: 'number', min: 0, max: 10 }, { key: 'extras', label: 'Extras', kind: 'text', // Only visible when kind === 'a' visibleWhen: (ctx) => ctx.formValues.kind === 'a', required: true, }, ], }, }; const baseArgs = { resource, item: null, actingUserId: 1 }; describe('validateForResource', () => { it('reports required fields that are empty', () => { const errors = validateForResource({ ...baseArgs, data: {} }); expect(errors.title).toBe('Title is required'); }); it('reports maxLength violations on text fields', () => { const errors = validateForResource({ ...baseArgs, data: { title: 'too long' } }); expect(errors.title).toMatch(/5 characters or fewer/); }); it('skips fields hidden by visibleWhen', () => { // kind = 'b' hides the 'extras' required field — no error expected const errors = validateForResource({ ...baseArgs, data: { title: 'ok', kind: 'b' }, }); expect(errors.extras).toBeUndefined(); }); it('enforces visibleWhen-revealed required fields', () => { const errors = validateForResource({ ...baseArgs, data: { title: 'ok', kind: 'a' }, }); expect(errors.extras).toBe('Extras is required'); }); it('enforces multi-text min/max items', () => { const tooFew = validateForResource({ ...baseArgs, data: { title: 'ok', options: ['only-one'] }, }); expect(tooFew.options).toMatch(/at least 2/); const tooMany = validateForResource({ ...baseArgs, data: { title: 'ok', options: ['1', '2', '3', '4', '5'] }, }); expect(tooMany.options).toMatch(/at most 4/); }); it('enforces number min/max', () => { const tooHigh = validateForResource({ ...baseArgs, data: { title: 'ok', count: 99 }, }); expect(tooHigh.count).toMatch(/no more than 10/); }); it('returns no errors when every field is valid', () => { const errors = validateForResource({ ...baseArgs, data: { title: 'ok', kind: 'a', options: ['x', 'y'], count: 5, extras: 'fine', }, }); expect(errors).toEqual({}); }); it('treats form: null as always valid', () => { const readOnly: Resource = { ...resource, form: null }; const errors = validateForResource({ ...baseArgs, resource: readOnly, data: {}, }); expect(errors).toEqual({}); }); });