Files
openmaic/OpenMAIC/packages/@openmaic/editor/test/ui/defaultElements.test.ts
2026-08-16 14:58:47 +08:00

122 lines
3.7 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import type { SlideContent } from '@openmaic/dsl';
import { applyEditorTransaction, createEditorTransaction } from '../../src/core';
import {
createDefaultAudioElement,
createDefaultChartElement,
createDefaultImageElement,
createDefaultLatexElement,
createDefaultLineElement,
createDefaultTableElement,
createDefaultTextElement,
createDefaultVideoElement,
fitImageSize,
} from '../../src/ui/adapters/defaultElements';
const emptyContent: SlideContent = {
type: 'slide' as const,
canvas: {
id: 'slide-1',
viewportSize: 1000,
viewportRatio: 0.5625,
theme: { backgroundColor: '#fff', themeColors: [], fontColor: '#111', fontName: 'Inter' },
elements: [],
},
};
describe('built-in editor element factories', () => {
it('creates complete media elements with stable defaults', () => {
expect(createDefaultVideoElement('video-1', { src: 'a.mp4', ext: 'mp4' })).toMatchObject({
id: 'video-1',
type: 'video',
autoplay: false,
src: 'a.mp4',
ext: 'mp4',
});
expect(createDefaultAudioElement('audio-1', { src: 'a.mp3' })).toMatchObject({
id: 'audio-1',
type: 'audio',
autoplay: false,
loop: false,
fixedRatio: true,
src: 'a.mp3',
});
});
it('preserves natural image ratios while fitting large sources into 600 by 400', () => {
expect(fitImageSize(1200, 600)).toEqual({ width: 600, height: 300 });
expect(fitImageSize(600, 1200)).toEqual({ width: 200, height: 400 });
expect(fitImageSize(120, 80)).toEqual({ width: 120, height: 80 });
expect(
createDefaultImageElement('portrait', 'portrait.png', { width: 600, height: 1200 }),
).toMatchObject({ width: 200, height: 400 });
});
it('clamps table dimensions and creates stable cell ids', () => {
const table = createDefaultTableElement('table-1', 2, 3);
const minimum = createDefaultTableElement('minimum', 0, -1);
expect(table.data).toHaveLength(2);
expect(table.data[0]).toHaveLength(3);
expect(table.data[1][2].id).toBe('table-1-cell-1-2');
expect(minimum.data).toHaveLength(1);
expect(minimum.data[0]).toHaveLength(1);
});
it.each([
['broken', { isBroken: true }, { broken: [0, 200] }],
['double broken', { isBroken2: true }, { broken2: [100, 0] }],
['curve', { isCurve: true }, { curve: [0, 200] }],
[
'cubic',
{ isCubic: true },
{
cubic: [
[200, 0],
[0, 200],
],
},
],
] as const)('preserves %s preset geometry for a diagonal drag', (_label, preset, controls) => {
const line = createDefaultLineElement(
'line-1',
{ start: [100, 200], end: [300, 400] },
{ path: '', style: 'solid', points: ['', ''], ...preset },
);
expect(line).toMatchObject({ start: [0, 0], end: [200, 200], ...controls });
});
it('creates insertable DSL elements accepted by the transaction core', () => {
const elements = [
createDefaultTextElement('text-1', { left: 10, top: 20, width: 200, height: 60 }),
createDefaultImageElement('image-1', 'image.png'),
createDefaultChartElement('chart-1', 'bar'),
createDefaultLatexElement('latex-1', {
latex: 'x^2',
html: '<span>x²</span>',
width: 120,
height: 60,
}),
];
let content = emptyContent;
for (const element of elements) {
content = applyEditorTransaction(
content,
createEditorTransaction({
origin: 'toolbar',
operations: [{ type: 'element.add', element }],
}),
);
}
expect(content.canvas.elements.map((element) => element.id)).toEqual([
'text-1',
'image-1',
'chart-1',
'latex-1',
]);
});
});