UseJunior Book a Demo

safe-docx · Accept Tracked Changes

Accept moves by keeping destination and removing source

When accepting a tracked change (an OOXML revision marker that records an edit inside a Word file), the final document must keep the accepted content and remove the rejected content. A move (a tracked change that relocates existing content) records both sides of that relocation. In the official OOXML specification (ECMA-376 WordprocessingML)[1], <w:moveFrom> is the element that wraps the source content and <w:moveTo> is the element that wraps the destination content. The two wrappers carry a shared w:id integer that pairs them as one logical relocation; the specific integer value is arbitrary, chosen by the document author.

In this repo, safe-docx, a function named acceptChanges[2] takes parsed OOXML content and rewrites it so accepted revision markup no longer remains. The function also returns a summary with counts for accepted insertions, accepted deletions, resolved moves, and resolved property changes. For move content, accepting the change removes <w:moveFrom> with its source content and unwraps <w:moveTo> so the destination content remains as ordinary document content.

Below is a test scenario of the paired-move behavior of acceptChanges[3]: accept moves by keeping destination and removing source.

The scenario

Given a document with move-from and move-to wrappers,
When moves are resolved into destination-only text,
Then two move wrappers are resolved.

  • moveFrom wrappers are removed.
  • moveTo wrappers are removed.
  • old location text is removed.
  • new location text is preserved.

Below is the test fixture code.

const input = [
  '<w:p>',
  '<w:moveFrom w:id="11"><w:r><w:t>Old location</w:t></w:r></w:moveFrom>',
  '<w:moveTo w:id="11"><w:r><w:t>New location</w:t></w:r></w:moveTo>',
  '</w:p>',
].join('');

const result = runAcceptChanges(input);

The expected result shape

Below is the result that runAcceptChanges is expected to return for this scenario.

{
  xml: '<?xml version="1.0" encoding="UTF-8"?><w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:body><w:p><w:r><w:t>New location</w:t></w:r></w:p></w:body></w:document>',
  summary: {
    insertionsAccepted: 0,
    deletionsAccepted: 0,
    movesResolved: 2,
    propertyChangesResolved: 0
  }
}

Below is a description of the expected fields:

What this scenario does not cover

This scenario is deliberately limited to one paired move inside one paragraph. It does not exercise:

The assertions only test the move summary count, the removal of move wrappers, and the presence or absence of the two fixture texts for this paired move.

A non-obvious detail

The shared w:id="11" value makes the fixture a paired move; this scenario checks the accepted output rather than the pair-validation logic. The important output rule is destination-only content: the source text is removed with <w:moveFrom>, while the destination text is kept after <w:moveTo> is unwrapped.