import { describe, expect, it } from 'vitest';
import { validateComponentBlockValue } from '../../fields/component-block.js';
const heroManifest = {
    version: 1,
    generatedAt: new Date().toISOString(),
    rootDir: './examples',
    components: [
        {
            name: 'Hero',
            displayName: 'Hero',
            filePath: 'Hero.tsx',
            props: [
                { name: 'title', required: true, type: { kind: 'string' } },
                { name: 'subtitle', required: false, type: { kind: 'string' } },
                {
                    name: 'alignment',
                    required: false,
                    type: { kind: 'enum', values: ['left', 'center', 'right'] },
                },
                { name: 'fullBleed', required: false, type: { kind: 'boolean' } },
            ],
        },
        {
            name: 'Stat',
            displayName: 'Stat',
            filePath: 'Stat.tsx',
            props: [
                { name: 'value', required: true, type: { kind: 'number' } },
                { name: 'label', required: true, type: { kind: 'string' } },
            ],
        },
    ],
};
function makeField(overrides = {}) {
    return {
        type: 'componentBlock',
        label: 'Body',
        manifest: heroManifest,
        ...overrides,
    };
}
describe('validateComponentBlockValue', () => {
    it('accepts a valid Hero value with only required props', () => {
        const result = validateComponentBlockValue({ component: 'Hero', props: { title: 'Welcome' } }, makeField());
        expect(result).toBe(true);
    });
    it('rejects a non-object stored value', () => {
        const result = validateComponentBlockValue('nope', makeField());
        expect(result).toMatch(/{ component: string; props: object }/);
    });
    it('rejects an unknown component name and lists the known ones', () => {
        const result = validateComponentBlockValue({ component: 'Carousel', props: {} }, makeField());
        expect(result).toMatch(/Unknown component 'Carousel'/);
        expect(result).toMatch(/Hero, Stat/);
    });
    it('rejects a missing required prop', () => {
        const result = validateComponentBlockValue({ component: 'Hero', props: {} }, makeField());
        expect(result).toMatch(/Missing required prop 'title'/);
    });
    it('rejects a wrong-type prop value', () => {
        const result = validateComponentBlockValue({ component: 'Stat', props: { value: 'not a number', label: 'Users' } }, makeField());
        expect(result).toMatch(/'value' must be a number/);
    });
    it('rejects an enum value outside the allowed set', () => {
        const result = validateComponentBlockValue({
            component: 'Hero',
            props: { title: 'Welcome', alignment: 'somewhere' },
        }, makeField());
        expect(result).toMatch(/'alignment' must be one of: left, center, right/);
    });
    it('honors the field-level allow-list', () => {
        const result = validateComponentBlockValue({ component: 'Stat', props: { value: 1, label: 'a' } }, makeField({ allow: ['Hero'] }));
        expect(result).toMatch(/'Stat' is not in the allow-list/);
    });
    it('returns true when the field is not a componentBlock', () => {
        const result = validateComponentBlockValue('anything', {
            type: 'text',
            label: 'Title',
        });
        expect(result).toBe(true);
    });
    it('errors when the manifest is missing or not loaded', () => {
        const broken = {
            type: 'componentBlock',
            label: 'Body',
            manifest: './path-not-resolved.json',
        };
        const result = validateComponentBlockValue({ component: 'Hero', props: { title: 'x' } }, broken);
        expect(result).toMatch(/no resolved manifest/);
    });
    // Copilot review of PR #77 flagged that the original guards were too
    // permissive: `props: []` would pass the `typeof === 'object'` check
    // and a malformed manifest entry with a missing `props` array would
    // crash the validator with a TypeError. Lock in the strengthened
    // guards here so a future regression surfaces as a test failure.
    it('rejects a stored value whose props is an array', () => {
        const result = validateComponentBlockValue({ component: 'Hero', props: [] }, makeField());
        expect(result).toMatch(/{ component: string; props: object }/);
    });
    it('rejects a manifest whose component is missing its props array', () => {
        const broken = {
            type: 'componentBlock',
            label: 'Body',
            manifest: {
                version: 1,
                generatedAt: new Date().toISOString(),
                rootDir: './',
                components: [{ name: 'Hero', displayName: 'Hero', filePath: 'Hero.tsx' }],
            },
        };
        const result = validateComponentBlockValue({ component: 'Hero', props: { title: 'x' } }, broken);
        expect(result).toMatch(/no resolved manifest/);
    });
    it('rejects a manifest whose components entry is not a plain object', () => {
        const broken = {
            type: 'componentBlock',
            label: 'Body',
            manifest: {
                version: 1,
                generatedAt: new Date().toISOString(),
                rootDir: './',
                components: ['Hero'],
            },
        };
        const result = validateComponentBlockValue({ component: 'Hero', props: { title: 'x' } }, broken);
        expect(result).toMatch(/no resolved manifest/);
    });
});
//# sourceMappingURL=component-block.test.js.map