Skip to content

Commit 781748e

Browse files
authored
test(super-editor): pin SD-3158 start/end-only tcMar round-trip (#3314)
Narrower variant of SD-3152: a w:tcMar with ONLY w:start/w:end (no top/bottom). Exercises the vertical-side branch of the merge loop being fully skipped, while the horizontal branch still has to preserve the logical key family. Fixture is built by the word-api ooxml-fixture SDK CLI (build --type tc-mar-logical) and pre-validated with OpenXmlValidator (Office2019 profile). Same source the fixture-feedback harness uses, so the test exercises identical input. The original SD-3158 report was a stale-dist false positive in fixture-feedback (packages/superdoc/dist is gitignored and was pre-fix). After rebuild, the SDK fixture exports cleanly. This test pins the behavior so a future regression on the start/end-only branch won't slip through CI.
1 parent f14f6eb commit 781748e

2 files changed

Lines changed: 32 additions & 0 deletions

File tree

Binary file not shown.

tests/behavior/tests/exporting/sd-3152-tcmar-roundtrip.spec.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,35 @@ test('@behavior SD-3152: zero-edit round-trip preserves tcMar logical vs physica
7575
expect(/<w:end\b/.test(inner) && /<w:right\b/.test(inner)).toBe(false);
7676
}
7777
});
78+
79+
// SD-3158: narrower variant — a cell with ONLY <w:start>/<w:end> tcMar, no
80+
// top/bottom. The SD-3152 fix needs to hold when the merge loop's vertical-side
81+
// branch is fully skipped (cellMargins.top and .bottom are undefined). Fixture
82+
// is generated by the word-api ooxml-fixture SDK CLI (build --type tc-mar-logical)
83+
// and pre-validated with OpenXmlValidator (Office2019 profile).
84+
const STARTEND_ONLY = path.resolve(__dirname, 'fixtures/sd-3158-tcmar-startend-only.docx');
85+
86+
test('@behavior SD-3158: zero-edit round-trip of start/end-only tcMar emits only logical, no duplicate physical', async ({
87+
superdoc,
88+
}) => {
89+
await superdoc.loadDocument(STARTEND_ONLY);
90+
await superdoc.waitForStable();
91+
92+
const bytes: number[] = await superdoc.page.evaluate(async () => {
93+
const blob: Blob = await (window as any).editor.exportDocx();
94+
const buffer = await blob.arrayBuffer();
95+
return Array.from(new Uint8Array(buffer));
96+
});
97+
98+
const outZip = await JSZip.loadAsync(Buffer.from(bytes));
99+
const outXml = await outZip.file('word/document.xml')!.async('string');
100+
101+
const tcMarBlocks = Array.from(outXml.matchAll(/<w:tcMar\b[^>]*>([\s\S]*?)<\/w:tcMar>/g)).map((m) => m[1]);
102+
expect(tcMarBlocks).toHaveLength(1);
103+
104+
const children = Array.from(tcMarBlocks[0].matchAll(/<w:(top|start|left|bottom|end|right)\b/g)).map(
105+
(m) => `w:${m[1]}`,
106+
);
107+
// Source had ONLY w:start + w:end. Export must match: no top/bottom inserted, no physical pair added.
108+
expect(children).toEqual(['w:start', 'w:end']);
109+
});

0 commit comments

Comments
 (0)