When rejecting tracked moves in a DOCX document, the original position must retain the moved content while the destination position is removed. A tracked move records source content in w:moveFrom and destination content in w:moveTo, so rejecting the move means preserving the source content and discarding the destination content.
The rejectChanges primitive (i.e., an operation that mutates a DOCX document tree) applies that rule by unwrapping w:moveFrom content, removing w:moveTo content, and returning a revision summary that includes movesReverted.[1] The surrounding document text then reflects the pre-move position because the retained source content remains readable as paragraph text.
Below is a test scenario of the baseline successful case of rejectChanges: moved source content is kept and moved destination content is removed.
The scenario
Given a document with moveFrom and moveTo paragraphs,
When rejectChanges is called,
Then
result.movesRevertedis greater than or equal to1.- the first paragraph text is
moved text.
The test fixture
The fixture builds two paragraphs that contain the same moved content in the source and destination move wrappers. Rejecting the tracked move should keep the source paragraph text and remove the destination wrapper from the document.[2]
Below is the test fixture code.
test('should unwrap moveFrom and remove moveTo content', async ({ given, when, then }: AllureBddContext) => {
let doc: Document;
let result: ReturnType<typeof rejectChanges>;
await given('a document with moveFrom and moveTo paragraphs', async () => {
doc = makeDoc(
'<w:p>' +
'<w:moveFrom w:author="Author">' +
'<w:r><w:t>moved text</w:t></w:r>' +
'</w:moveFrom>' +
'</w:p>' +
'<w:p>' +
'<w:moveTo w:author="Author">' +
'<w:r><w:t>moved text</w:t></w:r>' +
'</w:moveTo>' +
'</w:p>',
);
});
await when('rejectChanges is called', async () => {
result = rejectChanges(doc);
});
await then('moves are reverted and text stays at original position', async () => {
expect(result.movesReverted).toBeGreaterThanOrEqual(1);
// The first paragraph should keep the text at original position
const texts = getAllParagraphTexts(doc);
expect(texts[0]).toBe('moved text');
});
});
The expected outcome
The scenario asserts both the summary count returned by rejectChanges and the paragraph text read from the mutated document. Because the paragraph text is read after mutation, the expected outcome is the observable document state plus the returned move counter predicate.
Below is the asserted outcome for this scenario.
expect(result.movesReverted).toBeGreaterThanOrEqual(1);
const texts = getAllParagraphTexts(doc);
expect(texts[0]).toBe('moved text');
Below is a description of the expected fields:
result.movesRevertedis expected to be greater than or equal to1, because rejecting the move unwraps onew:moveFromsource wrapper and removes onew:moveTodestination wrapper.texts[0]is expected to bemoved text, because the source move wrapper is unwrapped instead of removed.
A non-obvious detail
Tracked move rejection combines two opposite actions on matching revision concepts. The source wrapper is unwrapped so its content remains in the original paragraph, while the destination wrapper is removed so the relocated copy no longer appears as accepted content.[3]