|
1 | 1 | import { describe, it, expect } from "vitest"; |
2 | 2 | import JSZip from "jszip"; |
3 | 3 | import { serializeDeck } from "../index"; |
| 4 | +import { snapshotElement } from "../pptxToDeck"; |
4 | 5 | import { CURRENT_DECK_VERSION } from "@/lib/schema/migrate"; |
5 | | -import type { Deck } from "@/lib/types"; |
| 6 | +import type { Deck, ShapeElement } from "@/lib/types"; |
6 | 7 |
|
7 | 8 | /** |
8 | 9 | * Tests for the synth-OOXML writers added in the full-fidelity export work: |
@@ -102,6 +103,75 @@ describe("synth writers (PRs 1, 2, 3, 4, 5, 6, 7)", () => { |
102 | 103 | expect(slide).not.toMatch(/<a:prstGeom prst="rect"/); |
103 | 104 | }); |
104 | 105 |
|
| 106 | + it("replays verbatim custGeom OOXML from the deck JSON when present (cross-process)", async () => { |
| 107 | + const shape: ShapeElement = { |
| 108 | + ...base, |
| 109 | + id: "bikeXYZ", |
| 110 | + type: "shape", |
| 111 | + shape: "rect", |
| 112 | + x: 100, |
| 113 | + y: 100, |
| 114 | + w: 400, |
| 115 | + h: 300, |
| 116 | + fill: "#EA1B0A", |
| 117 | + path: { |
| 118 | + d: "M 0 0 L 100 0 L 100 100 Z", |
| 119 | + viewW: 100, |
| 120 | + viewH: 100, |
| 121 | + fillRule: "evenodd", |
| 122 | + }, |
| 123 | + }; |
| 124 | + // Self-contained verbatim <p:sp> with a colliding low cNvPr id + a marker. |
| 125 | + const verbatim = |
| 126 | + `<p:sp><p:nvSpPr><p:cNvPr id="7" name="bike"/><p:cNvSpPr/><p:nvPr/></p:nvSpPr>` + |
| 127 | + `<p:spPr><a:xfrm><a:off x="635000" y="635000"/><a:ext cx="2540000" cy="1905000"/></a:xfrm>` + |
| 128 | + `<a:custGeom data-marker="VERBATIM_BIKE"><a:pathLst><a:path w="100" h="100">` + |
| 129 | + `<a:moveTo><a:pt x="0" y="0"/></a:moveTo><a:lnTo><a:pt x="100" y="0"/></a:lnTo><a:close/>` + |
| 130 | + `</a:path></a:pathLst></a:custGeom>` + |
| 131 | + `<a:solidFill><a:srgbClr val="EA1B0A"/></a:solidFill></p:spPr>` + |
| 132 | + `<p:txBody><a:bodyPr/><a:lstStyle/><a:p/></p:txBody></p:sp>`; |
| 133 | + shape.pristineOoxml = { xml: verbatim, snapshot: snapshotElement(shape) }; |
| 134 | + |
| 135 | + const zip = await generate( |
| 136 | + makeDeck([{ id: "s", background: "#FFFFFF", elements: [shape] }]) |
| 137 | + ); |
| 138 | + const slide = await zip.file("ppt/slides/slide1.xml")?.async("string"); |
| 139 | + // The exact source geometry was replayed (marker + source fill present)… |
| 140 | + expect(slide).toContain('data-marker="VERBATIM_BIKE"'); |
| 141 | + expect(slide).toContain('<a:srgbClr val="EA1B0A"/>'); |
| 142 | + // …and the colliding cNvPr id="7" was rewritten to a fresh high id. |
| 143 | + expect(slide).not.toMatch(/<p:cNvPr\b[^>]*\bid="7"/); |
| 144 | + }); |
| 145 | + |
| 146 | + it("falls back to synthesis when a pristine-OOXML shape was edited", async () => { |
| 147 | + const shape: ShapeElement = { |
| 148 | + ...base, |
| 149 | + id: "bikeEdited", |
| 150 | + type: "shape", |
| 151 | + shape: "rect", |
| 152 | + x: 100, |
| 153 | + y: 100, |
| 154 | + w: 400, |
| 155 | + h: 300, |
| 156 | + fill: "#EA1B0A", |
| 157 | + path: { d: "M 0 0 L 100 0 L 100 100 Z", viewW: 100, viewH: 100 }, |
| 158 | + }; |
| 159 | + const verbatim = |
| 160 | + `<p:sp><p:nvSpPr><p:cNvPr id="7" name="bike"/><p:cNvSpPr/><p:nvPr/></p:nvSpPr>` + |
| 161 | + `<p:spPr><a:custGeom data-marker="VERBATIM_BIKE"/></p:spPr></p:sp>`; |
| 162 | + shape.pristineOoxml = { xml: verbatim, snapshot: snapshotElement(shape) }; |
| 163 | + // Edit the shape AFTER the snapshot was taken → snapshot diverges. |
| 164 | + shape.x = 999; |
| 165 | + |
| 166 | + const zip = await generate( |
| 167 | + makeDeck([{ id: "s", background: "#FFFFFF", elements: [shape] }]) |
| 168 | + ); |
| 169 | + const slide = await zip.file("ppt/slides/slide1.xml")?.async("string"); |
| 170 | + // Verbatim replay is rejected; the synth path emits the custGeom from path.d. |
| 171 | + expect(slide).not.toContain("VERBATIM_BIKE"); |
| 172 | + expect(slide).toContain("<a:custGeom>"); |
| 173 | + }); |
| 174 | + |
105 | 175 | it("PR 2: emits <a:gradFill> for shapes with linear-gradient fill", async () => { |
106 | 176 | const deck = makeDeck([ |
107 | 177 | { |
|
0 commit comments