import { describe, expect, test } from 'vitest'; import { extractWidgetConfig, generateWidgetContent } from '@openmaic/generation'; import type { AICallFn } from '@openmaic/generation'; import type { SceneOutline } from '@openmaic/generation'; const renderDiagramPrompt = async (widgetOutline: SceneOutline['widgetOutline']) => { let capturedPrompt = ''; const aiCall: AICallFn = async (_system, user) => { capturedPrompt = user; return `
`; }; const outline: SceneOutline = { id: 'diagram-scene', type: 'interactive', title: 'System Map', description: 'Explore the system hierarchy.', keyPoints: ['Root', 'Branch'], order: 1, widgetType: 'diagram', widgetOutline, }; await generateWidgetContent(outline, aiCall); return capturedPrompt; }; describe('diagram widget node constraints', () => { test('forwards the requested count and prescribed nodes to the content prompt', async () => { const prompt = await renderDiagramPrompt({ diagramType: 'hierarchy', nodeCount: 3, nodes: [ { id: 'root', label: 'Root' }, { id: 'branch', label: 'Branch', parentId: 'root' }, ], }); expect(prompt).toContain('Maximum node count: 3'); expect(prompt).toContain('"id": "root"'); expect(prompt).toContain('"parentId": "root"'); expect(prompt).toContain('Do not add, remove, or replace prescribed nodes'); expect(prompt).not.toContain('{{'); }); test('forwards a count constraint without requiring prescribed nodes', async () => { const prompt = await renderDiagramPrompt({ diagramType: 'flowchart', nodeCount: 4, }); expect(prompt).toContain('Maximum node count: 4'); expect(prompt).not.toContain('## Prescribed Nodes'); expect(prompt).not.toContain('{{'); }); test('omits node constraints when the outline does not provide them', async () => { const prompt = await renderDiagramPrompt({ diagramType: 'system' }); expect(prompt).not.toContain('## Node Count Constraint'); expect(prompt).not.toContain('## Prescribed Nodes'); expect(prompt).not.toContain('{{'); }); }); describe('extractWidgetConfig', () => { const configHtml = (json: string) => ` `; test('seeds a missing type from the routed widget type', () => { expect( extractWidgetConfig(configHtml('{"nodes":[],"edges":[],"revealOrder":[]}'), 'diagram'), ).toEqual({ type: 'diagram', nodes: [], edges: [], revealOrder: [] }); }); test('preserves an existing valid type', () => { expect(extractWidgetConfig(configHtml('{"type":"game","questions":[]}'), 'diagram')).toEqual({ type: 'game', questions: [], }); }); test.each(['[]', '"diagram"', '42'])('drops non-object config JSON %s', (json) => { expect(extractWidgetConfig(configHtml(json), 'diagram')).toBeUndefined(); }); });