/* --------------------------------------------------------------------------- * Verifier for the Backstage admin resource registry. * * Walks every registered resource and asserts the invariants that keep the * shared components renderable. Compile-time TypeScript already catches most * shape issues via the strict Resource generic — this suite covers what * TS can't see at the value level (function-ness of handlers, kind strings * actually being in the registered set, sentinel resource keys not colliding). * * Note on "every column.key is a valid field on the entity": * That's a structural assertion best enforced at compile time. Resource * narrows the render/value callbacks to the entity's keys; this suite skips * trying to re-check it at runtime. * ------------------------------------------------------------------------- */ import { describe, it, expect } from 'vitest'; import { groups } from '../src/admin/resources'; import type { Field, Resource, ResourceGroup, Column, FormEmbed, } from '../src/admin/resource-types'; const KNOWN_FIELD_KINDS: ReadonlySet = new Set([ 'text', 'textarea', 'markdown', 'select', 'select-async', 'multi-select-async', 'multi-text', 'date', 'datetime', 'number', 'readonly', 'image-upload', ]); const KNOWN_COLUMN_KINDS: ReadonlySet = new Set([ 'text', 'pill', 'relative-date', 'number', 'tag-list', ]); const KNOWN_EMBED_COMPONENTS: ReadonlySet = new Set([ 'pulse-sub-form', ]); function allResources(): Resource[] { return groups.flatMap((g: ResourceGroup) => g.resources as Resource[]); } describe('admin resource registry', () => { it('has at least one group with resources registered', () => { expect(groups.length).toBeGreaterThan(0); expect(allResources().length).toBeGreaterThan(0); }); it('every resource key is unique across the registry', () => { const keys = allResources().map((r) => r.key); const dups = keys.filter((k, i) => keys.indexOf(k) !== i); expect(dups).toEqual([]); }); it('every resource.groupKey points at a real group', () => { const groupKeys = new Set(groups.map((g) => g.key)); for (const r of allResources()) { expect(groupKeys.has(r.groupKey), `${r.key} → unknown groupKey ${r.groupKey}`).toBe(true); } }); describe.each(allResources())('resource: $key', (resource: Resource) => { it('has required identity fields', () => { expect(resource.key).toBeTruthy(); expect(resource.label).toBeTruthy(); expect(resource.pluralLabel).toBeTruthy(); expect(resource.singularLabel).toBeTruthy(); expect(resource.groupKey).toBeTruthy(); }); it('list.queryFn is a function', () => { expect(typeof resource.list.queryFn).toBe('function'); }); it('every column has a registered kind (or none = text)', () => { for (const col of resource.list.columns as Column[]) { const kind = col.kind ?? 'text'; expect(KNOWN_COLUMN_KINDS.has(kind), `${resource.key}: column ${col.key} → unknown kind ${kind}`).toBe(true); } if (resource.list.columnsByFilter) { for (const [filterKey, cols] of Object.entries(resource.list.columnsByFilter)) { for (const col of cols as Column[]) { const kind = col.kind ?? 'text'; expect(KNOWN_COLUMN_KINDS.has(kind), `${resource.key}: columnsByFilter.${filterKey}.${col.key} → unknown kind ${kind}`).toBe(true); } } } }); it('exactly one or zero filters is isDefault', () => { const filters = resource.list.filters ?? []; const defaults = filters.filter((f) => f.isDefault); expect(defaults.length).toBeLessThanOrEqual(1); }); it('every filter.predicate is a function', () => { for (const f of resource.list.filters ?? []) { expect(typeof f.predicate, `${resource.key}: filter ${f.key} predicate`).toBe('function'); } }); it('every form field has a registered kind', () => { for (const field of resource.form?.fields ?? []) { expect( KNOWN_FIELD_KINDS.has(field.kind), `${resource.key}: field ${field.key} → unknown kind ${field.kind}`, ).toBe(true); } }); it('every embed.component is in the registered set', () => { for (const embed of resource.form?.embeds ?? []) { expect( KNOWN_EMBED_COMPONENTS.has(embed.component), `${resource.key}: embed ${embed.key} → unknown component ${embed.component}`, ).toBe(true); } }); it('every ops member is a function (when defined)', () => { const ops = resource.ops; if (ops.create) expect(typeof ops.create).toBe('function'); if (ops.update) expect(typeof ops.update).toBe('function'); if (ops.delete) expect(typeof ops.delete).toBe('function'); if (ops.getById) expect(typeof ops.getById).toBe('function'); }); it('every action.handler is a function', () => { for (const action of resource.actions ?? []) { expect(typeof action.handler, `${resource.key}: action ${action.key}`).toBe('function'); } }); it('action keys are unique', () => { const keys = (resource.actions ?? []).map((a) => a.key); const dups = keys.filter((k, i) => keys.indexOf(k) !== i); expect(dups).toEqual([]); }); it('renders SOMETHING when an item is clicked (form OR summary, or no clicks)', () => { // If form is null and there's no summary, the resource is non-clickable. // If form is null but a summary is defined → review panel renders. // If form is defined → edit panel renders. // The only invalid shape is form=null + summary defined + no actions, // which would render an empty review panel. Flag it. if (resource.form === null && resource.summary !== undefined) { const actions = resource.actions ?? []; expect(actions.length, `${resource.key}: review-mode resource with no actions`).toBeGreaterThan(0); } }); it('when ops.create is defined, the form is defined too', () => { // Can't render the create panel without a form. if (resource.ops.create) { expect(resource.form, `${resource.key}: ops.create is defined but form is null`).not.toBeNull(); } }); }); });