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 } 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'; const DATA_URL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='; async function exportSlideContent(content: SlideContent, scene: Scene): Promise { return buildPptxBlob( [content.canvas], [scene], VIEWPORT_RATIO, VIEWPORT_SIZE, RATIO_PX2_INCH, RATIO_PX2_PT, ); } async function readSlideXml(blob: Blob): Promise { const zip = await JSZip.loadAsync(await blob.arrayBuffer()); const entry = zip.file('ppt/slides/slide1.xml'); if (!entry) throw new Error('PPTX did not contain ppt/slides/slide1.xml'); return entry.async('string'); } /** * Per-op round-trip for `element.reorder`. The renderer paints elements in * array order, and so does the PPTX exporter — a text element becomes a * `` and an image a ``, emitted in the slide's element order. So * the relative position of `` vs `` in slide1.xml IS the z-order. */ describe('round-trip: element.reorder z-order', () => { it('serializes element order; reorder flips which element is painted first', async () => { const { scene, content } = makeSlideFixture(); // a single text element // Append an image → order is [text, image]: text (sp) painted first. const withImage = applySlideEditOperation(content, { type: 'element.add', element: createDefaultImageElement('img-z', DATA_URL), }); const before = await readSlideXml(await exportSlideContent(withImage, scene)); const spBefore = before.indexOf(''); const picBefore = before.indexOf(''); expect(spBefore).toBeGreaterThanOrEqual(0); expect(picBefore).toBeGreaterThanOrEqual(0); // Baseline: text (sp) precedes image (pic) — guards against a tautology. expect(spBefore).toBeLessThan(picBefore); // Send the image to the back (index 0) → order is [image, text]. const reordered = applySlideEditOperation(withImage, { type: 'element.reorder', elementId: 'img-z', index: 0, }); const after = await readSlideXml(await exportSlideContent(reordered, scene)); const spAfter = after.indexOf(''); const picAfter = after.indexOf(''); // Now the image (pic) precedes the text (sp). expect(picAfter).toBeLessThan(spAfter); }); });