From 97de1e3fccc9f0fde127b77c7b299a905a32c2b2 Mon Sep 17 00:00:00 2001 From: xy200303 <3483421977@qq.com> Date: Wed, 3 Jun 2026 11:52:03 +0800 Subject: [PATCH 1/2] fix(super-editor): support nested content controls --- .../v3/handlers/helpers/is-inline-node.js | 1 + .../handlers/helpers/is-inline-node.test.js | 1 + .../helpers/handle-structured-content-node.js | 66 ++++++- .../handle-structured-content-node.test.js | 184 +++++++++++++++++- 4 files changed, 242 insertions(+), 10 deletions(-) diff --git a/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/helpers/is-inline-node.js b/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/helpers/is-inline-node.js index 32f754de9b..c674101d3c 100644 --- a/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/helpers/is-inline-node.js +++ b/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/helpers/is-inline-node.js @@ -24,6 +24,7 @@ const INLINE_FALLBACK_TYPES = new Set([ 'endnoteReference', 'fieldAnnotation', 'structuredContent', + 'image', 'mathInline', 'passthroughInline', 'page-number', diff --git a/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/helpers/is-inline-node.test.js b/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/helpers/is-inline-node.test.js index 04bb399e29..a2e058ef4a 100644 --- a/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/helpers/is-inline-node.test.js +++ b/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/helpers/is-inline-node.test.js @@ -8,6 +8,7 @@ describe('isInlineNode', () => { expect(isInlineNode({ type: 'bookmarkStart', attrs: { id: '1' } })).toBe(true); expect(isInlineNode({ type: 'bookmarkEnd', attrs: { id: '1' } })).toBe(true); expect(isInlineNode({ type: 'tab' })).toBe(true); + expect(isInlineNode({ type: 'image', attrs: { src: 'media/image1.png' } })).toBe(true); expect(isInlineNode({ type: 'footnoteReference', attrs: { id: '1' } })).toBe(true); }); diff --git a/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/sdt/helpers/handle-structured-content-node.js b/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/sdt/helpers/handle-structured-content-node.js index ee57751e11..4b34895035 100644 --- a/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/sdt/helpers/handle-structured-content-node.js +++ b/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/sdt/helpers/handle-structured-content-node.js @@ -1,6 +1,56 @@ import { parseAnnotationMarks } from './handle-annotation-node'; import { parseStrictStOnOff } from '../../../utils.js'; import { BLOCK_FIELD_XML_NAMES } from '../../../sd/shared/block-field-xml-names.js'; +import { isInlineNode } from '../../../helpers/is-inline-node.js'; + +const INLINE_CONTEXT_XML_NAMES = new Set(['w:p', 'w:r', 'w:hyperlink', 'w:smartTag']); + +function hasDirectBlockSignal(sdtContent) { + return Boolean( + sdtContent?.elements?.some( + (el) => el?.name === 'w:p' || el?.name === 'w:tbl' || BLOCK_FIELD_XML_NAMES.has(el?.name), + ), + ); +} + +function canEmitInlineStructuredContent(path = []) { + return path.some((entry) => INLINE_CONTEXT_XML_NAMES.has(entry?.name) || entry?.name === 'w:sdtContent'); +} + +function hasTranslatedBlockContent(content = [], schema) { + return content.some((node) => node?.type && !isInlineNode(node, schema)); +} + +function wrapInlineRunsAsParagraphs(content = [], schema) { + const normalized = []; + let pendingInline = []; + + const flushInline = () => { + if (!pendingInline.length) return; + normalized.push({ + type: 'paragraph', + attrs: null, + content: pendingInline, + marks: [], + }); + pendingInline = []; + }; + + for (const node of content) { + if (!node) continue; + + if (isInlineNode(node, schema)) { + pendingInline.push(node); + continue; + } + + flushInline(); + normalized.push(node); + } + + flushInline(); + return normalized; +} /** * Detect the semantic control type from sdtPr child elements. @@ -113,12 +163,6 @@ export function handleStructuredContentNode(params) { return null; } - const paragraph = sdtContent.elements?.find((el) => el.name === 'w:p'); - const table = sdtContent.elements?.find((el) => el.name === 'w:tbl'); - // SD-3005: a content control wrapping a block field (e.g. BIBLIOGRAPHY) has - // no direct w:p after preprocessing — its child is an sd:* block node. It is - // block content and must not be emitted as an inline structuredContent. - const blockField = sdtContent.elements?.find((el) => BLOCK_FIELD_XML_NAMES.has(el?.name)); const { marks } = parseAnnotationMarks(sdtContent); const translatedContent = nodeListHandler.handler({ ...params, @@ -126,12 +170,18 @@ export function handleStructuredContentNode(params) { path: [...(params.path || []), sdtContent], }); - const isBlockNode = paragraph || table || blockField; + const schema = params.editor?.schema; + const content = Array.isArray(translatedContent) ? translatedContent : []; + const isBlockNode = + hasTranslatedBlockContent(content, schema) || + hasDirectBlockSignal(sdtContent) || + !canEmitInlineStructuredContent(params.path); const sdtContentType = isBlockNode ? 'structuredContentBlock' : 'structuredContent'; + const normalizedContent = isBlockNode ? wrapInlineRunsAsParagraphs(content, schema) : content; let result = { type: sdtContentType, - content: translatedContent, + content: normalizedContent, marks, attrs: { id: id?.attributes?.['w:val'] || null, diff --git a/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/sdt/helpers/handle-structured-content-node.test.js b/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/sdt/helpers/handle-structured-content-node.test.js index 92e14f098d..68ea753d9b 100644 --- a/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/sdt/helpers/handle-structured-content-node.test.js +++ b/packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/w/sdt/helpers/handle-structured-content-node.test.js @@ -1,6 +1,8 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { handleStructuredContentNode } from './handle-structured-content-node'; import { parseAnnotationMarks } from './handle-annotation-node'; +import { defaultNodeListHandler } from '../../../../../v2/importer/docxImporter.js'; +import { initTestEditor } from '@tests/helpers/helpers.js'; // Mock dependencies vi.mock('./handle-annotation-node', () => ({ @@ -31,6 +33,10 @@ describe('handleStructuredContentNode', () => { parseAnnotationMarks.mockReturnValue({ marks: [] }); }); + afterEach(() => { + vi.restoreAllMocks(); + }); + it('returns null when nodes array is empty', () => { const params = { nodes: [], nodeListHandler: mockNodeListHandler }; const result = handleStructuredContentNode(params); @@ -79,7 +85,7 @@ describe('handleStructuredContentNode', () => { const params = { nodes: [node], nodeListHandler: mockNodeListHandler, - path: [], + path: [{ name: 'w:p' }], }; parseAnnotationMarks.mockReturnValue({ marks: [{ type: 'bold' }] }); @@ -363,3 +369,177 @@ describe('handleStructuredContentNode', () => { }); }); }); + +describe('handleStructuredContentNode nested SDT import regression', () => { + let editor; + + const textRun = (text) => ({ + name: 'w:r', + elements: [{ name: 'w:t', elements: [{ type: 'text', text }] }], + }); + + const paragraph = (text) => ({ + name: 'w:p', + elements: [textRun(text)], + }); + + const sdtPr = ({ id, tag, alias, lockMode = 'unlocked', controlType = 'w:richText' }) => ({ + name: 'w:sdtPr', + elements: [ + { name: 'w:id', attributes: { 'w:val': id } }, + { name: 'w:tag', attributes: { 'w:val': tag } }, + { name: 'w:alias', attributes: { 'w:val': alias } }, + { name: 'w:lock', attributes: { 'w:val': lockMode } }, + { name: controlType }, + ], + }); + + const sdt = (props, contentElements) => ({ + name: 'w:sdt', + elements: [sdtPr(props), { name: 'w:sdtContent', elements: contentElements }], + }); + + const table = (text) => ({ + name: 'w:tbl', + elements: [ + { + name: 'w:tblPr', + elements: [{ name: 'w:tblW', attributes: { 'w:w': '2400', 'w:type': 'dxa' } }], + }, + { + name: 'w:tblGrid', + elements: [{ name: 'w:gridCol', attributes: { 'w:w': '2400' } }], + }, + { + name: 'w:tr', + elements: [ + { + name: 'w:tc', + elements: [ + { + name: 'w:tcPr', + elements: [{ name: 'w:tcW', attributes: { 'w:w': '2400', 'w:type': 'dxa' } }], + }, + paragraph(text), + ], + }, + ], + }, + ], + }); + + const importNodes = (nodes) => { + const nodeListHandler = defaultNodeListHandler(); + return nodeListHandler.handler({ + nodes, + nodeListHandler, + docx: {}, + editor, + path: [], + }); + }; + + const expectSchemaValid = (content) => { + let pmDoc; + expect(() => { + pmDoc = editor.schema.nodeFromJSON({ type: 'doc', content }); + pmDoc.check(); + }).not.toThrow(); + return pmDoc; + }; + + const findFirstJson = (node, predicate) => { + if (!node) return null; + if (predicate(node)) return node; + for (const child of node.content || []) { + const found = findFirstJson(child, predicate); + if (found) return found; + } + return null; + }; + + beforeEach(() => { + ({ editor } = initTestEditor({ + isHeadless: true, + loadFromSchema: true, + content: { type: 'doc', content: [{ type: 'paragraph' }] }, + })); + parseAnnotationMarks.mockReturnValue({ marks: [] }); + }); + + afterEach(() => { + editor?.destroy(); + editor = null; + vi.restoreAllMocks(); + }); + + it('imports nested block SDT when outer sdtContent directly contains w:sdt wrapping a paragraph', () => { + const inner = sdt({ id: 'inner-block', tag: 'inner-tag', alias: 'Inner Alias', lockMode: 'contentLocked' }, [ + paragraph('Nested paragraph'), + ]); + const outer = sdt({ id: 'outer-block', tag: 'outer-tag', alias: 'Outer Alias', lockMode: 'sdtLocked' }, [inner]); + + const result = importNodes([outer]); + + expect(result).toHaveLength(1); + expect(result[0].type).toBe('structuredContentBlock'); + expect(result[0].attrs).toMatchObject({ + id: 'outer-block', + tag: 'outer-tag', + alias: 'Outer Alias', + lockMode: 'sdtLocked', + controlType: 'richText', + }); + + const nested = result[0].content?.[0]; + expect(nested?.type).toBe('structuredContentBlock'); + expect(nested.attrs).toMatchObject({ + id: 'inner-block', + tag: 'inner-tag', + alias: 'Inner Alias', + lockMode: 'contentLocked', + controlType: 'richText', + }); + expect(nested.attrs.sdtPr?.elements?.find((el) => el.name === 'w:alias')?.attributes?.['w:val']).toBe( + 'Inner Alias', + ); + + expectSchemaValid(result); + }); + + it('wraps nested inline SDT safely when an outer block SDT also contains paragraph and table content', () => { + const inlineNested = sdt( + { id: 'inner-inline', tag: 'inline-tag', alias: 'Inline Alias', lockMode: 'sdtContentLocked' }, + [textRun('Inline value')], + ); + const outer = sdt({ id: 'outer-mixed', tag: 'outer-mixed-tag', alias: 'Outer Mixed', lockMode: 'sdtLocked' }, [ + inlineNested, + paragraph('Outer paragraph'), + table('Cell text'), + ]); + + const result = importNodes([outer]); + + expect(result).toHaveLength(1); + expect(result[0].type).toBe('structuredContentBlock'); + expect(result[0].content?.map((node) => node.type)).toEqual(['paragraph', 'paragraph', 'table']); + + const nested = findFirstJson( + result[0], + (node) => node.type === 'structuredContent' && node.attrs?.id === 'inner-inline', + ); + expect(nested).toBeTruthy(); + expect(nested.attrs).toMatchObject({ + id: 'inner-inline', + tag: 'inline-tag', + alias: 'Inline Alias', + lockMode: 'sdtContentLocked', + controlType: 'richText', + }); + expect(nested.attrs.sdtPr?.elements?.find((el) => el.name === 'w:lock')?.attributes?.['w:val']).toBe( + 'sdtContentLocked', + ); + + expectSchemaValid(result); + }); +}); From 4848caa02a1cb47060db0d016902b7437340fa8b Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Wed, 3 Jun 2026 08:31:15 -0300 Subject: [PATCH 2/2] test(super-editor): fixture-backed nested SDT classification coverage Real .docx fixtures + integration test validating the translate-first SDT classifier from PR #3616: nested block, nested inline, mixed-block (a defensive malformed-normalization case), and inline picture control. Each fixture asserts source XML shape, imported PM node types with no content loss, and export wrapper round-trip. A portable generator regenerates them. Row-level SDTs (w:tbl > w:sdt > w:tr) are out of scope here; see SD-3118/IT-1040. --- .../v1/tests/data/sdt-fixtures.README.md | 48 ++++ .../v1/tests/data/sdt-fixtures.generate.cjs | 121 ++++++++++ .../v1/tests/data/sdt-inline-picture.docx | Bin 0 -> 24976 bytes .../v1/tests/data/sdt-mixed-block.docx | Bin 0 -> 13321 bytes .../v1/tests/data/sdt-nested-block.docx | Bin 0 -> 13234 bytes .../v1/tests/data/sdt-nested-inline.docx | Bin 0 -> 13259 bytes .../editor/sdt-nested-classification.test.js | 210 ++++++++++++++++++ 7 files changed, 379 insertions(+) create mode 100644 packages/super-editor/src/editors/v1/tests/data/sdt-fixtures.README.md create mode 100644 packages/super-editor/src/editors/v1/tests/data/sdt-fixtures.generate.cjs create mode 100644 packages/super-editor/src/editors/v1/tests/data/sdt-inline-picture.docx create mode 100644 packages/super-editor/src/editors/v1/tests/data/sdt-mixed-block.docx create mode 100644 packages/super-editor/src/editors/v1/tests/data/sdt-nested-block.docx create mode 100644 packages/super-editor/src/editors/v1/tests/data/sdt-nested-inline.docx create mode 100644 packages/super-editor/src/editors/v1/tests/editor/sdt-nested-classification.test.js diff --git a/packages/super-editor/src/editors/v1/tests/data/sdt-fixtures.README.md b/packages/super-editor/src/editors/v1/tests/data/sdt-fixtures.README.md new file mode 100644 index 0000000000..2892f6e49c --- /dev/null +++ b/packages/super-editor/src/editors/v1/tests/data/sdt-fixtures.README.md @@ -0,0 +1,48 @@ +# SDT classification fixtures (PR #3616) + +Real `.docx` fixtures that validate the nested content-control classifier in +`super-converter/v3/handlers/w/sdt/`. Exercised by +`tests/editor/sdt-nested-classification.test.js`. + +The claim under test: block vs run/inline SDT classification is driven by the +translated ProseMirror content shape plus import context, not only by the direct +XML child names of `w:sdtContent`. + +## Provenance and conformance + +Each fixture's surrounding package (content types, rels, styles, theme, fonts, and +image media) is taken verbatim from a Word-authored base already in this folder. +Only `word/document.xml` is hand-authored to encode the exact OOXML shape, so the +package stays valid while the structure is precise. All fixtures are therefore +**schema-only** (hand-authored structure, not produced or validated by Word). + +The `conformance` column distinguishes shapes that are valid ECMA-376 from one that +is deliberately malformed to exercise the PR's defensive normalization: + +| Fixture | Conformance | Base package | Shape under `w:body` | +|---|---|---|---| +| `sdt-nested-block.docx` | conformant | `blank-doc.docx` | block `w:sdt` whose `w:sdtContent` directly contains a nested block `w:sdt` (no direct `w:p`) wrapping a paragraph. Legal: `EG_ContentBlockContent` permits `sdt`. | +| `sdt-nested-inline.docx` | conformant | `blank-doc.docx` | `w:p` containing an inline `w:sdt` that contains a nested inline `w:sdt` of runs, between two text runs. Legal: `CT_SdtContentRun` is `EG_PContent`. | +| `sdt-mixed-block.docx` | **defensive (malformed)** | `blank-doc.docx` | block `w:sdt` whose `w:sdtContent` holds a bare inline `w:sdt`, a `w:p`, and a `w:tbl`. The bare inline `w:sdt` is **non-conformant**: a `w:sdt` directly under block content is positionally `CT_SdtBlock`, whose content may not be a bare `w:r` (`EG_ContentBlockContent` allows only `customXml/sdt/p/tbl/EG_RunLevelElts`, and `EG_RunLevelElts` excludes `w:r`). Included on purpose to drive `wrapInlineRunsAsParagraphs`, which the PR uses to normalize bare inline content inside a block SDT. | +| `sdt-inline-picture.docx` | conformant | `anchor_images.docx` (reuses `media/image1.png`, `rId4`) | `w:p` > inline `w:sdt` with `` marker > `w:sdtContent` > `w:r` > `w:drawing`. Legal per ECMA-376 §17.5.2.24 (picture content control wrapping a single DrawingML picture). | + +## Rebuild + +``` +node packages/super-editor/src/editors/v1/tests/data/sdt-fixtures.generate.cjs +``` + +The generator resolves all paths from its own location and reads the two base +packages (`blank-doc.docx`, `anchor_images.docx`) from this folder, so it is +portable. It re-reads each built file and asserts the intended shape. Set +`SDT_FIXTURE_OUT=/some/dir` to write to a scratch dir instead of overwriting the +committed fixtures (useful for a dry run). Regeneration is content-equivalent; +only zip metadata may differ. + +## Out of scope + +Row-level SDTs (`w:tbl > w:sdt > w:sdtContent > w:tr`, Google Docs `goog_rdk_*` +exports) are a separate table-walk concern tracked by SD-3118 / IT-1040. The real +Google Docs artifact attached to those tickets should be used as that fixture, and +its preservation checked through a Word round-trip before choosing transparent +unwrap vs. `rowSdt` metadata. Not covered by these fixtures. diff --git a/packages/super-editor/src/editors/v1/tests/data/sdt-fixtures.generate.cjs b/packages/super-editor/src/editors/v1/tests/data/sdt-fixtures.generate.cjs new file mode 100644 index 0000000000..c6659b4388 --- /dev/null +++ b/packages/super-editor/src/editors/v1/tests/data/sdt-fixtures.generate.cjs @@ -0,0 +1,121 @@ +/* + * Regenerate the SDT classification fixtures for the nested content-control + * classifier (PR #3616). Exercised by tests/editor/sdt-nested-classification.test.js. + * Provenance and per-fixture conformance are documented in sdt-fixtures.README.md. + * + * Each fixture derives from a Word-authored base in this folder (blank-doc.docx / + * anchor_images.docx) and replaces only word/document.xml with a hand-authored body + * that encodes a precise SDT shape; every other package part is inherited from the + * base, so the package stays valid. After writing, each built file is re-read and its + * intended shape is asserted. + * + * Portable: all paths resolve from this file's location. No external state. + * node packages/super-editor/src/editors/v1/tests/data/sdt-fixtures.generate.cjs + * SDT_FIXTURE_OUT=/tmp/sdt-verify node .../sdt-fixtures.generate.cjs # dry run + */ +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const { execSync } = require('child_process'); + +const DATA = __dirname; +const OUT = process.env.SDT_FIXTURE_OUT || __dirname; +const STAGE = fs.mkdtempSync(path.join(os.tmpdir(), 'sdt-fixture-')); +fs.mkdirSync(path.join(STAGE, 'word'), { recursive: true }); +fs.mkdirSync(OUT, { recursive: true }); + +// Exact opening tag (full namespaces) copied verbatim from blank-doc.docx. +const HEADER = + '\n' + + ''; + +const SECTPR = + ''; + +// Clean inline drawing referencing anchor_images.docx's small image1.png (rId4). +const INLINE_DRAWING = + ''; + +const fixtures = [ + { + name: 'sdt-nested-block.docx', + base: 'blank-doc.docx', + // Conformant: outer block w:sdt whose sdtContent's only direct child is a nested + // block w:sdt (no direct w:p). The inner SDT wraps a paragraph. + body: + '' + + '' + + 'Nested block content' + + '' + + '', + mustContain: ['OuterBlock', 'InnerBlock', 'Nested block content', ''], + }, + { + name: 'sdt-nested-inline.docx', + base: 'blank-doc.docx', + // Conformant: inline w:sdt (with a nested inline w:sdt) inside a paragraph, between + // two text runs. Proves the path/context gate keeps valid inline SDTs inline. + body: + '' + + 'Before ' + + '' + + 'outer ' + + '' + + 'inner' + + '' + + '' + + ' after' + + '', + mustContain: ['OuterInline', 'InnerInline', 'Before ', 'inner', ' after', ''], + }, + { + name: 'sdt-mixed-block.docx', + base: 'blank-doc.docx', + // DEFENSIVE / MALFORMED: a block w:sdt whose sdtContent mixes a bare inline w:sdt, + // a w:p, and a w:tbl. The bare inline w:sdt is non-conformant in block content + // (EG_ContentBlockContent excludes bare w:r). Drives wrapInlineRunsAsParagraphs. + body: + '' + + '' + + 'inline sdt' + + '' + + 'A paragraph' + + '' + + 'Cell' + + '', + mustContain: ['MixedBlock', 'InlineInMixed', 'inline sdt', 'A paragraph', '', 'Cell'], + }, + { + name: 'sdt-inline-picture.docx', + base: 'anchor_images.docx', + // Conformant (ECMA-376 17.5.2.24): inline picture content control. + // marker; content is a run with an inline drawing referencing the base image (rId4). + body: + '' + + INLINE_DRAWING + + '', + mustContain: ['PictureControl', '', 'r:embed="rId4"', ''], + }, +]; + +let allPass = true; +for (const f of fixtures) { + const documentXml = HEADER + '' + f.body + SECTPR + ''; + fs.writeFileSync(path.join(STAGE, 'word', 'document.xml'), documentXml); + + const outPath = path.join(OUT, f.name); + fs.copyFileSync(path.join(DATA, f.base), outPath); + // Replace only word/document.xml; every other part comes from the Word-authored base. + execSync(`zip -X -q "${outPath}" word/document.xml`, { cwd: STAGE }); + + const rebuilt = execSync(`unzip -p "${outPath}" word/document.xml`, { encoding: 'utf8' }); + const missing = f.mustContain.filter((s) => !rebuilt.includes(s)); + const ok = missing.length === 0; + allPass = allPass && ok; + console.log(`${ok ? 'PASS' : 'FAIL'} ${f.name} (${(fs.statSync(outPath).size / 1024).toFixed(1)} KB, base ${f.base})`); + if (!ok) console.log(` missing shape markers: ${JSON.stringify(missing)}`); +} + +fs.rmSync(STAGE, { recursive: true, force: true }); +console.log(allPass ? `\nAll fixtures generated + shape-verified -> ${OUT}` : '\nSHAPE VERIFICATION FAILED.'); +process.exit(allPass ? 0 : 1); diff --git a/packages/super-editor/src/editors/v1/tests/data/sdt-inline-picture.docx b/packages/super-editor/src/editors/v1/tests/data/sdt-inline-picture.docx new file mode 100644 index 0000000000000000000000000000000000000000..4214939d18e22172b28f4a9e8df6f28f06cf2717 GIT binary patch literal 24976 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 O^?icsDqDu%&i)7HW~B=N literal 0 HcmV?d00001 diff --git a/packages/super-editor/src/editors/v1/tests/data/sdt-mixed-block.docx b/packages/super-editor/src/editors/v1/tests/data/sdt-mixed-block.docx new file mode 100644 index 0000000000000000000000000000000000000000..b2da580a8350d6300266d1ff0864aedebabaf4d1 GIT binary patch literal 13321 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 literal 0 HcmV?d00001 diff --git a/packages/super-editor/src/editors/v1/tests/data/sdt-nested-block.docx b/packages/super-editor/src/editors/v1/tests/data/sdt-nested-block.docx new file mode 100644 index 0000000000000000000000000000000000000000..9ed9441c224e3dada029328ca16b4f6d50d8bb49 GIT binary patch literal 13234 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 literal 0 HcmV?d00001 diff --git a/packages/super-editor/src/editors/v1/tests/editor/sdt-nested-classification.test.js b/packages/super-editor/src/editors/v1/tests/editor/sdt-nested-classification.test.js new file mode 100644 index 0000000000..b3d66d92f4 --- /dev/null +++ b/packages/super-editor/src/editors/v1/tests/editor/sdt-nested-classification.test.js @@ -0,0 +1,210 @@ +import { describe, it, expect } from 'vitest'; +import { loadTestDataForEditorTests, initTestEditor, getTestDataByFileName } from '@tests/helpers/helpers'; +import { getExportedResult } from '@tests/export/export-helpers/export-helpers'; + +/** + * Fixture-backed integration coverage for PR #3616 (nested content controls). + * + * The architectural claim under test: block/run SDT classification is driven by the + * TRANSLATED ProseMirror content shape plus import context (path), NOT only by the + * direct XML child names of w:sdtContent. Each fixture is a real .docx (see + * tests/data/sdt-*.docx, provenance in tests/data/sdt-fixtures.README.md) so the full + * import + export pipeline runs, not a mocked translator. + * + * For every fixture we assert three things, because "opens without crashing" would + * miss quiet data loss: + * 1. XML shape - the intended OOXML actually exists in word/document.xml. + * 2. Import - the imported PM node types are correct and no content is lost. + * 3. Round-trip - export still produces the expected w:sdt wrapper / w:sdtPr shape. + * + * Scope: this validates the block/run classifier only. Row-level SDTs + * (w:tbl > w:sdt > w:tr, tracked by SD-3118 / IT-1040) are a separate table-walk + * concern and are intentionally NOT covered here. + */ + +const NESTED_BLOCK = 'sdt-nested-block.docx'; +const NESTED_INLINE = 'sdt-nested-inline.docx'; +const MIXED_BLOCK = 'sdt-mixed-block.docx'; +const INLINE_PICTURE = 'sdt-inline-picture.docx'; + +// ---- ProseMirror JSON traversal helpers ---- +const pmAll = (node, acc = []) => { + if (!node || typeof node !== 'object') return acc; + if (node.type) acc.push(node); + (node.content || []).forEach((c) => pmAll(c, acc)); + return acc; +}; +const pmCollect = (node, type) => pmAll(node).filter((n) => n.type === type); +const pmFirst = (node, type) => pmCollect(node, type)[0] || null; +const pmByAlias = (node, alias) => pmAll(node).find((n) => n.attrs?.alias === alias) || null; +const pmText = (node) => { + if (!node || typeof node !== 'object') return ''; + if (node.type === 'text') return node.text || ''; + return (node.content || []).map(pmText).join(''); +}; +const pmChildTypes = (node) => (node?.content || []).map((c) => c.type); +const BLOCK_TYPES = ['paragraph', 'table', 'structuredContentBlock']; +// Schema invariant: an inline structuredContent must never directly contain a block node. +const assertNoBlockInsideInline = (doc) => { + pmCollect(doc, 'structuredContent').forEach((sc) => { + expect(pmChildTypes(sc).some((t) => BLOCK_TYPES.includes(t))).toBe(false); + }); +}; + +// ---- OOXML JSON traversal helpers (parseXmlToJson / exportSchemaToJson shape) ---- +const xmlAll = (node, acc = []) => { + if (!node || typeof node !== 'object') return acc; + if (node.name) acc.push(node); + (node.elements || []).forEach((c) => xmlAll(c, acc)); + return acc; +}; +const xmlCollect = (node, name) => xmlAll(node).filter((n) => n.name === name); +const xmlFirst = (node, name) => xmlCollect(node, name)[0] || null; +const xmlDirectChildren = (node, name) => (node?.elements || []).filter((e) => e.name === name); +const xmlText = (node) => { + if (!node || typeof node !== 'object') return ''; + if (node.type === 'text') return node.text || ''; + return (node.elements || []).map(xmlText).join(''); +}; +const sdtAlias = (sdt) => { + const pr = xmlFirst(sdt, 'w:sdtPr'); + return xmlDirectChildren(pr, 'w:alias')[0]?.attributes?.['w:val']; +}; +const xmlSdtByAlias = (root, alias) => xmlCollect(root, 'w:sdt').find((sdt) => sdtAlias(sdt) === alias) || null; +const xmlParaContainingSdt = (root) => xmlCollect(root, 'w:p').find((p) => xmlCollect(p, 'w:sdt').length > 0) || null; + +// ---- pipeline helpers ---- +const importDoc = async (name) => { + const { docx, media, mediaFiles, fonts } = await loadTestDataForEditorTests(name); + const { editor } = initTestEditor({ content: docx, media, mediaFiles, fonts }); + return editor.getJSON(); +}; +const documentXmlOf = async (name) => (await getTestDataByFileName(name))['word/document.xml']; + +describe('SDT classification (PR #3616) - nested block content controls', () => { + it('XML shape: outer w:sdtContent has a direct nested w:sdt and no direct w:p', async () => { + const docXml = await documentXmlOf(NESTED_BLOCK); + const outer = xmlSdtByAlias(docXml, 'OuterBlock'); + expect(outer).toBeTruthy(); + const outerContent = xmlFirst(outer, 'w:sdtContent'); + expect(xmlDirectChildren(outerContent, 'w:sdt').length).toBe(1); + expect(xmlDirectChildren(outerContent, 'w:p').length).toBe(0); // the case the old check missed + expect(xmlText(xmlSdtByAlias(docXml, 'InnerBlock'))).toContain('Nested block content'); + }); + + it('Import: both SDTs classify as block via translated content, content preserved', async () => { + const doc = await importDoc(NESTED_BLOCK); + const outer = pmByAlias(doc, 'OuterBlock'); + const inner = pmByAlias(doc, 'InnerBlock'); + expect(outer?.type).toBe('structuredContentBlock'); + expect(inner?.type).toBe('structuredContentBlock'); + expect(pmText(outer)).toContain('Nested block content'); + assertNoBlockInsideInline(doc); + }); + + it('Round-trip: nested w:sdt wrappers and sdtPr survive export', async () => { + const exported = await getExportedResult(NESTED_BLOCK); + const outer = xmlSdtByAlias(exported, 'OuterBlock'); + expect(outer).toBeTruthy(); + expect(xmlFirst(outer, 'w:sdtPr')).toBeTruthy(); + expect(xmlSdtByAlias(exported, 'InnerBlock')).toBeTruthy(); + expect(xmlText(exported)).toContain('Nested block content'); + }); +}); + +describe('SDT classification (PR #3616) - nested inline content controls', () => { + it('XML shape: nested inline w:sdt lives inside a w:p', async () => { + const docXml = await documentXmlOf(NESTED_INLINE); + expect(xmlParaContainingSdt(docXml)).toBeTruthy(); + expect(xmlSdtByAlias(docXml, 'OuterInline')).toBeTruthy(); + expect(xmlSdtByAlias(docXml, 'InnerInline')).toBeTruthy(); + }); + + it('Import: inline SDTs stay inline (path gate does not force block)', async () => { + const doc = await importDoc(NESTED_INLINE); + expect(pmByAlias(doc, 'OuterInline')?.type).toBe('structuredContent'); + expect(pmByAlias(doc, 'InnerInline')?.type).toBe('structuredContent'); + expect(pmCollect(doc, 'structuredContentBlock').length).toBe(0); // nothing promoted to block + const text = pmText(doc); + expect(text).toContain('Before'); + expect(text).toContain('outer'); + expect(text).toContain('inner'); + expect(text).toContain('after'); + assertNoBlockInsideInline(doc); + }); + + it('Round-trip: inline w:sdt stays inside the paragraph on export', async () => { + const exported = await getExportedResult(NESTED_INLINE); + expect(xmlParaContainingSdt(exported)).toBeTruthy(); + expect(xmlSdtByAlias(exported, 'OuterInline')).toBeTruthy(); + expect(xmlSdtByAlias(exported, 'InnerInline')).toBeTruthy(); + expect(xmlText(exported)).toContain('inner'); + }); +}); + +describe('SDT classification (PR #3616) - mixed block content control', () => { + it('XML shape: block w:sdtContent mixes a bare inline w:sdt, a w:p, and a w:tbl', async () => { + const docXml = await documentXmlOf(MIXED_BLOCK); + const outerContent = xmlFirst(xmlSdtByAlias(docXml, 'MixedBlock'), 'w:sdtContent'); + expect(xmlDirectChildren(outerContent, 'w:sdt').length).toBe(1); + expect(xmlDirectChildren(outerContent, 'w:p').length).toBe(1); + expect(xmlDirectChildren(outerContent, 'w:tbl').length).toBe(1); + }); + + it('Import: bare inline SDT is wrapped into a paragraph; paragraph and table preserved', async () => { + const doc = await importDoc(MIXED_BLOCK); + const outer = pmByAlias(doc, 'MixedBlock'); + expect(outer?.type).toBe('structuredContentBlock'); + + const childTypes = pmChildTypes(outer); + expect(childTypes).not.toContain('structuredContent'); // inline child must be wrapped, never a direct block child + expect(childTypes.filter((t) => t === 'paragraph').length).toBeGreaterThanOrEqual(2); + expect(childTypes).toContain('table'); + + const wrappedInline = pmByAlias(outer, 'InlineInMixed'); + expect(wrappedInline?.type).toBe('structuredContent'); + expect(pmText(wrappedInline)).toContain('inline sdt'); + expect(pmText(outer)).toContain('A paragraph'); + expect(pmText(outer)).toContain('Cell'); + assertNoBlockInsideInline(doc); + }); + + it('Round-trip: paragraph, table, and inline SDT content all survive export', async () => { + const exported = await getExportedResult(MIXED_BLOCK); + expect(xmlSdtByAlias(exported, 'MixedBlock')).toBeTruthy(); + expect(xmlFirst(exported, 'w:tbl')).toBeTruthy(); + const text = xmlText(exported); + expect(text).toContain('A paragraph'); + expect(text).toContain('Cell'); + expect(text).toContain('inline sdt'); + }); +}); + +describe('SDT classification (PR #3616) - inline picture content control', () => { + it('XML shape: inline w:sdt has a w:picture marker and a w:drawing in its content', async () => { + const docXml = await documentXmlOf(INLINE_PICTURE); + const pic = xmlSdtByAlias(docXml, 'PictureControl'); + expect(xmlDirectChildren(xmlFirst(pic, 'w:sdtPr'), 'w:picture').length).toBe(1); + expect(xmlFirst(pic, 'w:drawing')).toBeTruthy(); + }); + + it('Import: image classifies inline; picture marker is not modeled but sdtPr is kept', async () => { + const doc = await importDoc(INLINE_PICTURE); + const pic = pmByAlias(doc, 'PictureControl'); + expect(pic?.type).toBe('structuredContent'); // inline, not block + expect(pmFirst(pic, 'image')).toBeTruthy(); // image preserved as inline content + expect(pic?.attrs?.controlType).not.toBe('picture'); // picture marker not modeled semantically + expect(pic?.attrs?.controlType ?? null).toBeNull(); + expect(pic?.attrs?.sdtPr).toBeTruthy(); // raw sdtPr retained for round-trip + expect(pmCollect(doc, 'structuredContentBlock').length).toBe(0); + assertNoBlockInsideInline(doc); + }); + + it('Round-trip: w:picture marker round-trips raw via sdtPr and the drawing survives', async () => { + const exported = await getExportedResult(INLINE_PICTURE); + const pic = xmlSdtByAlias(exported, 'PictureControl'); + expect(pic).toBeTruthy(); + expect(xmlDirectChildren(xmlFirst(pic, 'w:sdtPr'), 'w:picture').length).toBe(1); + expect(xmlFirst(pic, 'w:drawing')).toBeTruthy(); + }); +});