144 lines
4.7 KiB
TypeScript
144 lines
4.7 KiB
TypeScript
import { describe, expect, test } from 'vitest';
|
|
import { generateSceneOutlinesFromRequirements } from '@openmaic/generation';
|
|
import { generateSceneContent } from '@openmaic/generation';
|
|
import type { SceneOutline, UserRequirements } from '@openmaic/generation';
|
|
import type { AICallFn } from '@openmaic/generation';
|
|
|
|
describe('media prompt condition wiring', () => {
|
|
test('outline generation passes media enable flags into conditional snippets', async () => {
|
|
let capturedPrompt = '';
|
|
const aiCall: AICallFn = async (system, user) => {
|
|
capturedPrompt = `${system}\n${user}`;
|
|
return JSON.stringify({
|
|
languageDirective: 'Teach in English.',
|
|
courseTitle: 'Evaporation',
|
|
outlines: [],
|
|
});
|
|
};
|
|
|
|
const requirements: UserRequirements = {
|
|
requirement: 'Teach evaporation with an animation',
|
|
};
|
|
|
|
const result = await generateSceneOutlinesFromRequirements(
|
|
requirements,
|
|
undefined,
|
|
undefined,
|
|
aiCall,
|
|
{ imageGenerationEnabled: false, videoGenerationEnabled: true },
|
|
);
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(capturedPrompt).toContain('gen_vid_1');
|
|
expect(capturedPrompt).not.toContain('gen_img_');
|
|
expect(capturedPrompt).not.toContain('suggestedImageIds');
|
|
expect(capturedPrompt).not.toContain('{{');
|
|
});
|
|
|
|
test('slide content generation exposes only media element rules backed by outline media', async () => {
|
|
let capturedPrompt = '';
|
|
const aiCall: AICallFn = async (system, user) => {
|
|
capturedPrompt = `${system}\n${user}`;
|
|
return JSON.stringify({
|
|
background: { type: 'solid', color: '#ffffff' },
|
|
elements: [
|
|
{
|
|
id: 'title',
|
|
type: 'text',
|
|
left: 60,
|
|
top: 80,
|
|
width: 880,
|
|
height: 76,
|
|
content: '<p style="font-size: 28px;">Evaporation</p>',
|
|
defaultFontName: '',
|
|
defaultColor: '#333333',
|
|
},
|
|
],
|
|
});
|
|
};
|
|
|
|
const outline: SceneOutline = {
|
|
id: 'scene_1',
|
|
type: 'slide',
|
|
title: 'Evaporation Motion',
|
|
description: 'Explain evaporation as a moving process',
|
|
keyPoints: ['Molecules gain energy', 'Water changes into vapor'],
|
|
order: 1,
|
|
mediaGenerations: [
|
|
{
|
|
type: 'video',
|
|
prompt: 'Animation of water molecules evaporating',
|
|
elementId: 'gen_vid_unique1',
|
|
aspectRatio: '16:9',
|
|
},
|
|
],
|
|
};
|
|
|
|
const result = await generateSceneContent(outline, aiCall);
|
|
|
|
expect(result).not.toBeNull();
|
|
expect(capturedPrompt).toContain('VideoElement');
|
|
expect(capturedPrompt).toContain('mediaRef');
|
|
expect(capturedPrompt).toContain('gen_vid_unique1');
|
|
expect(capturedPrompt).not.toContain('"src": "gen_vid_1"');
|
|
expect(capturedPrompt).not.toContain('ImageElement');
|
|
expect(capturedPrompt).not.toContain('gen_img_');
|
|
expect(capturedPrompt).not.toContain('{{');
|
|
});
|
|
});
|
|
|
|
describe('outline courseTitle parsing', () => {
|
|
const baseRequirements: UserRequirements = { requirement: 'Teach photosynthesis' };
|
|
|
|
async function runWith(raw: unknown) {
|
|
const aiCall: AICallFn = async (_system, _user) => JSON.stringify(raw);
|
|
return generateSceneOutlinesFromRequirements(baseRequirements, undefined, undefined, aiCall);
|
|
}
|
|
|
|
test('adopts a string courseTitle from the wrapper object', async () => {
|
|
const result = await runWith({
|
|
languageDirective: 'Teach in English.',
|
|
courseTitle: 'Photosynthesis Basics',
|
|
outlines: [],
|
|
});
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data?.courseTitle).toBe('Photosynthesis Basics');
|
|
});
|
|
|
|
test('trims whitespace and caps overlong courseTitle defensively', async () => {
|
|
const long = 'A '.repeat(80); // 160 chars
|
|
const result = await runWith({
|
|
languageDirective: 'Teach in English.',
|
|
courseTitle: ` ${long} `,
|
|
outlines: [],
|
|
});
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data?.courseTitle?.length).toBeLessThanOrEqual(120);
|
|
// trimmed
|
|
expect(result.data?.courseTitle?.startsWith(' ')).toBe(false);
|
|
});
|
|
|
|
test('returns undefined courseTitle when the field is missing (graceful fallback)', async () => {
|
|
const result = await runWith({
|
|
languageDirective: 'Teach in English.',
|
|
outlines: [],
|
|
});
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data?.courseTitle).toBeUndefined();
|
|
});
|
|
|
|
test('ignores a non-string / empty courseTitle', async () => {
|
|
const result = await runWith({
|
|
languageDirective: 'Teach in English.',
|
|
courseTitle: ' ',
|
|
outlines: [],
|
|
});
|
|
|
|
expect(result.success).toBe(true);
|
|
expect(result.data?.courseTitle).toBeUndefined();
|
|
});
|
|
});
|