Files
wehub-resource-sync c48612c494
CI / E2E Tests (push) Has been cancelled
CI / Lint, Typecheck & Unit Tests (push) Has been cancelled
Docs Build / Build docs site (push) Has been cancelled
Publish @openmaic packages / Build, validate & publish (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:03:23 +08:00

89 lines
3.3 KiB
TypeScript

import JSZip from 'jszip';
import { describe, expect, it } from 'vitest';
import { buildPptxBlob } from '@/lib/export/use-export-pptx';
import { applySlideEditOperation } from '@/lib/edit/slide-ops';
import {
createDefaultImageElement,
createDefaultTextElement,
} from '@/lib/edit/slide-edit-elements';
import type { Scene, SlideContent } from '@/lib/types/stage';
import {
makeSlideFixture,
RATIO_PX2_INCH,
RATIO_PX2_PT,
VIEWPORT_RATIO,
VIEWPORT_SIZE,
} from './fixtures';
/**
* Round-trip gate: tests element.add on default text and image elements.
*
* (a) Text element: verifies default content survives PPTX export.
* (b) Image element with remote URL: verifies that the export pipeline
* does NOT crash when network-fetched images cannot be embedded (CI has no network);
* the real image round-trip (data-URL) is covered by image-data-url.test.ts.
*/
async function exportSlideContent(content: SlideContent, scene: Scene): Promise<Blob> {
return buildPptxBlob(
[content.canvas],
[scene],
VIEWPORT_RATIO,
VIEWPORT_SIZE,
RATIO_PX2_INCH,
RATIO_PX2_PT,
);
}
async function readPptxEntry(blob: Blob, path: string): Promise<string> {
const zip = await JSZip.loadAsync(await blob.arrayBuffer());
const entry = zip.file(path);
if (!entry) throw new Error(`PPTX did not contain entry: ${path}`);
return entry.async('string');
}
describe('round-trip: element.add inserts (PR2 gate)', () => {
it('(a) inserted default text element — default content survives export', async () => {
const { scene, content } = makeSlideFixture();
// (a) Tests that createDefaultTextElement's default content ('<p>New text</p>')
// survives the PPTX export pipeline — verifies element.add and export don't
// lose or corrupt the text element's content.
const DEFAULT_TEXT_NEEDLE = 'New text';
const after = applySlideEditOperation(content, {
type: 'element.add',
element: createDefaultTextElement('rt-text-1'),
});
const blob = await exportSlideContent(after, scene);
const slideXml = await readPptxEntry(blob, 'ppt/slides/slide1.xml');
expect(slideXml).toContain(DEFAULT_TEXT_NEEDLE);
});
it('(b) inserted default image element (remote URL) — slide XML is non-empty', async () => {
const { scene, content } = makeSlideFixture();
const after = applySlideEditOperation(content, {
type: 'element.add',
element: createDefaultImageElement('rt-img-1', 'https://example.com/x.png'),
});
const blob = await exportSlideContent(after, scene);
// (b) Tests that element.add on a remote-URL image does NOT crash or corrupt
// export. The remote URL cannot be fetched in CI (no network), so the exporter
// logs "Failed to convert image to base64, skipping element" and omits the image.
// This case gates that export pipeline is resilient; the REAL image round-trip
// (data-URL, the PR2 local-upload path) is covered by image-data-url.test.ts
// and is deliberately not duplicated here.
// Basic size guard — a valid PPTX is always several KB at minimum.
expect(blob.size).toBeGreaterThan(0);
// The slide XML entry must be present and non-empty.
const slideXml = await readPptxEntry(blob, 'ppt/slides/slide1.xml');
expect(slideXml.length).toBeGreaterThan(0);
});
});