Skip to content

test(behavior): document-api locked SDT mutations (SD-3144)#3291

Merged
caio-pizzol merged 3 commits into
mainfrom
caio-pizzol/SD-3144-locked-cc-behavior
May 14, 2026
Merged

test(behavior): document-api locked SDT mutations (SD-3144)#3291
caio-pizzol merged 3 commits into
mainfrom
caio-pizzol/SD-3144-locked-cc-behavior

Conversation

@caio-pizzol

Copy link
Copy Markdown
Contributor

Real-editor regression coverage for SD-3123 (#3287). Mock-based unit tests in #3287 prove the engine emits AttrSteps for metadata writes and inner-range ReplaceSteps for content writes, but they don't run the real structured-content lock plugin's filterTransaction. This spec closes that gap by exercising editor.doc.contentControls.* in a real browser.

Five tests in tests/behavior/tests/sdt/document-api-locked-mutations.spec.ts:

  • text.setValue updates an sdtLocked inline plain-text control
  • replaceContent updates an sdtLocked block rich-text control
  • clearContent empties an sdtLocked block control (wrapper preserved)
  • Round-trip sdtLockedunlockedtext.setValue (lockMode AttrStep writes)
  • contentLocked + text.setValue returns LOCK_VIOLATION (API guard fires)

Does not un-fixme the older sdt-lock-modes.spec.ts — that one exercises editor.commands.*, while SD-3123 is about the Document API. A new focused spec is the right level.

Verified: 5/5 pass on Chromium against #3287's branch. Cross-browser will run in CI.

Merge order: this PR must merge after #3287, since it depends on the engine fix. Once #3287 merges, GitHub will auto-retarget this PR's base to main.

Related: SD-3123.

…ge writes (SD-3123)

Per ECMA-376, sdtLocked protects the SDT wrapper from removal but the content
stays editable. Before this fix, programmatic mutations flowed through
editor.commands.updateStructuredContentById, which dispatches
tr.replaceWith(pos, pos + node.nodeSize, ...) spanning the entire SDT. The
structured-content lock plugin's filterTransaction reads that as wrapper
damage and silently filters the transaction on sdtLocked controls -
producing false-success no-ops on operations the API allows (text.setValue,
replaceContent, appendContent, prependContent, setLockMode).

Two engine changes:

- applyAttrsUpdate (sdt-properties-write.ts) - switch from
  editor.commands.updateStructuredContentById to per-key tr.setNodeAttribute.
  AttrSteps have no from/to range and are explicitly skipped by the lock
  plugin's filterTransaction. This is the path that patch, setLockMode,
  patchRawProperties, setType, and the date/binding/multiline family use.
- replaceSdtTextContent (content-controls-wrappers.ts) - replace the SDT's
  inner content range (pos+1 to pos+nodeSize-1) instead of rewriting the
  whole wrapper. The lock plugin classifies inner-range steps as
  wouldModifyContent: allowed on sdtLocked, still blocked on contentLocked
  / sdtContentLocked (both per spec).

The search in applyAttrsUpdate uses SDT_NODE_NAMES (imported from
target-resolution.ts) so it resolves the same nodes the upstream
resolveSdtByTarget would resolve. documentSection / documentPartObject are
intentionally excluded - they have their own write paths and could collide
on id.

No changes to API-level lock guards (assertNotSdtLocked on patch, setType,
setBinding, etc.) - relaxing those is a separate behavior decision and out
of scope here.

Tests:
- 4 new regression tests for sdtLocked (setLockMode, text.setValue,
  text.clearValue, replaceContent) that assert the actual inner-range
  positions (pos+1, pos+nodeSize-1), not just call counts.
- 11 existing tests migrated from "expect updateStructuredContentById
  called" to "expect tr.setNodeAttribute / tr.replaceWith / tr.delete".
- 19 metadata-mutation operations added to CC_DIRECT_DISPATCH_OPS in
  conformance (no synthetic-failure mode after this refactor, matching the
  existing precedent for wrap/unwrap/etc.).

All pass: 46 wrappers + 1170 conformance + 104 SDT-adjacent + 1398
document-api. Zero new TypeScript errors.

Related: SD-3131, SD-3139, IT-1046.
Real-editor regression coverage for SD-3123 (#3287). The engine fix was
verified at the unit-test level (mock transaction shapes: AttrSteps for
metadata, inner-range ReplaceSteps for content), but mocks don't run the
real structured-content lock plugin's filterTransaction. This spec closes
that gap by exercising the customer surface (editor.doc.contentControls.*)
in a real browser with the real lock plugin loaded.

Five tests:
- text.setValue updates an sdtLocked inline plain-text control
- replaceContent updates an sdtLocked block rich-text control
- clearContent empties an sdtLocked block control (wrapper preserved)
- round-trip sdtLocked → unlocked → text.setValue (lockMode AttrStep writes)
- contentLocked + text.setValue returns LOCK_VIOLATION (API guard fires)

Lives in tests/behavior/tests/sdt/document-api-locked-mutations.spec.ts.
Does NOT un-fixme the older sdt-lock-modes.spec.ts (different concern —
that one exercises editor.commands.*, SD-3123 is about the Document API).

5/5 pass on chromium. Stacked on SD-3123 (#3287).
@caio-pizzol
caio-pizzol requested a review from a team as a code owner May 14, 2026 12:04
@linear

linear Bot commented May 14, 2026

Copy link
Copy Markdown

SD-3144

Base automatically changed from caio-pizzol/SD-3123-attr-mutations to main May 14, 2026 12:14
@caio-pizzol
caio-pizzol merged commit 1515843 into main May 14, 2026
4 checks passed
@caio-pizzol
caio-pizzol deleted the caio-pizzol/SD-3144-locked-cc-behavior branch May 14, 2026 12:14
msviderok pushed a commit to msviderok/superdoc that referenced this pull request May 16, 2026
…-dev#3291)

* fix(content-controls): mutate locked SDTs via AttrSteps and inner-range writes (SD-3123)

Per ECMA-376, sdtLocked protects the SDT wrapper from removal but the content
stays editable. Before this fix, programmatic mutations flowed through
editor.commands.updateStructuredContentById, which dispatches
tr.replaceWith(pos, pos + node.nodeSize, ...) spanning the entire SDT. The
structured-content lock plugin's filterTransaction reads that as wrapper
damage and silently filters the transaction on sdtLocked controls -
producing false-success no-ops on operations the API allows (text.setValue,
replaceContent, appendContent, prependContent, setLockMode).

Two engine changes:

- applyAttrsUpdate (sdt-properties-write.ts) - switch from
  editor.commands.updateStructuredContentById to per-key tr.setNodeAttribute.
  AttrSteps have no from/to range and are explicitly skipped by the lock
  plugin's filterTransaction. This is the path that patch, setLockMode,
  patchRawProperties, setType, and the date/binding/multiline family use.
- replaceSdtTextContent (content-controls-wrappers.ts) - replace the SDT's
  inner content range (pos+1 to pos+nodeSize-1) instead of rewriting the
  whole wrapper. The lock plugin classifies inner-range steps as
  wouldModifyContent: allowed on sdtLocked, still blocked on contentLocked
  / sdtContentLocked (both per spec).

The search in applyAttrsUpdate uses SDT_NODE_NAMES (imported from
target-resolution.ts) so it resolves the same nodes the upstream
resolveSdtByTarget would resolve. documentSection / documentPartObject are
intentionally excluded - they have their own write paths and could collide
on id.

No changes to API-level lock guards (assertNotSdtLocked on patch, setType,
setBinding, etc.) - relaxing those is a separate behavior decision and out
of scope here.

Tests:
- 4 new regression tests for sdtLocked (setLockMode, text.setValue,
  text.clearValue, replaceContent) that assert the actual inner-range
  positions (pos+1, pos+nodeSize-1), not just call counts.
- 11 existing tests migrated from "expect updateStructuredContentById
  called" to "expect tr.setNodeAttribute / tr.replaceWith / tr.delete".
- 19 metadata-mutation operations added to CC_DIRECT_DISPATCH_OPS in
  conformance (no synthetic-failure mode after this refactor, matching the
  existing precedent for wrap/unwrap/etc.).

All pass: 46 wrappers + 1170 conformance + 104 SDT-adjacent + 1398
document-api. Zero new TypeScript errors.

Related: SD-3131, SD-3139, IT-1046.

* test(content-controls): add clearContent sdtLocked regression test (SD-3123)

* test(behavior): document-api locked SDT mutations (SD-3144)

Real-editor regression coverage for SD-3123 (superdoc-dev#3287). The engine fix was
verified at the unit-test level (mock transaction shapes: AttrSteps for
metadata, inner-range ReplaceSteps for content), but mocks don't run the
real structured-content lock plugin's filterTransaction. This spec closes
that gap by exercising the customer surface (editor.doc.contentControls.*)
in a real browser with the real lock plugin loaded.

Five tests:
- text.setValue updates an sdtLocked inline plain-text control
- replaceContent updates an sdtLocked block rich-text control
- clearContent empties an sdtLocked block control (wrapper preserved)
- round-trip sdtLocked → unlocked → text.setValue (lockMode AttrStep writes)
- contentLocked + text.setValue returns LOCK_VIOLATION (API guard fires)

Lives in tests/behavior/tests/sdt/document-api-locked-mutations.spec.ts.
Does NOT un-fixme the older sdt-lock-modes.spec.ts (different concern —
that one exercises editor.commands.*, SD-3123 is about the Document API).

5/5 pass on chromium. Stacked on SD-3123 (superdoc-dev#3287).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant