When rejecting tracked formatting changes, a document editor must restore the formatting that existed before the revision rather than keep the formatting introduced by the revision. In OOXML, an rPrChange element records earlier run properties (the formatting properties attached to a run element, i.e., a <w:r> text container), so rejecting the change means replacing the current <w:rPr> with the original <w:rPr>.
rejectChanges applies that rule while it removes or unwraps other tracked-change markup in the document. For run-property revisions, it finds each rPrChange, extracts the child rPr that represents the original state, replaces the current property element, and increments the property-change count.[1]
Below is a test scenario of rejectChanges: restoring original run properties from an rPrChange record.
The scenario
Given a document with rPrChange from italic to bold,
When rejectChanges is called,
Then
propertyChangesRevertedis1.- The resulting run properties contain italic formatting and do not contain bold formatting.
The test fixture
The fixture builds a paragraph whose current run properties differ from the original properties stored in the revision record. That setup lets the scenario check both the summary count and the resulting OOXML state after rejection.[2]
Below is the test fixture code.
test('should restore original properties from rPrChange', async ({ given, when, then }: AllureBddContext) => {
let doc: Document;
let result: ReturnType<typeof rejectChanges>;
await given('a document with rPrChange from italic to bold', async () => {
doc = makeDoc(
'<w:p><w:r>' +
'<w:rPr>' +
'<w:b/>' +
'<w:rPrChange w:author="Author">' +
'<w:rPr><w:i/></w:rPr>' +
'</w:rPrChange>' +
'</w:rPr>' +
'<w:t>Formatted</w:t>' +
'</w:r></w:p>',
);
});
await when('rejectChanges is called', async () => {
result = rejectChanges(doc);
});
await then('original italic property is restored and bold removed', async () => {
expect(result.propertyChangesReverted).toBe(1);
// After reject, the rPr should contain the original (italic), not current (bold)
const run = doc.getElementsByTagNameNS(W_NS, 'r')[0]!;
const rPr = run.getElementsByTagNameNS(W_NS, 'rPr')[0]!;
expect(rPr.getElementsByTagNameNS(W_NS, 'i').length).toBe(1);
expect(rPr.getElementsByTagNameNS(W_NS, 'b').length).toBe(0);
});
});
The expected outcome
The scenario asserts a side effect on the document as well as the summary field returned by rejectChanges. The outcome is therefore the reverted property-change count together with the post-rejection run-property state, not the full return object.
Below are the assertions that describe the expected outcome for this scenario.
expect(result.propertyChangesReverted).toBe(1);
expect(rPr.getElementsByTagNameNS(W_NS, 'i').length).toBe(1);
expect(rPr.getElementsByTagNameNS(W_NS, 'b').length).toBe(0);
Below is a description of the expected values:
propertyChangesRevertedis expected to be1, because the document contains one run-property revision record that is reverted.rPr.getElementsByTagNameNS(W_NS, 'i').lengthis expected to be1, because the original run properties stored insiderPrChangecontain italic formatting.rPr.getElementsByTagNameNS(W_NS, 'b').lengthis expected to be0, because rejecting the revision removes the current bold formatting from the run properties.
A non-obvious detail
An rPrChange record stores the original formatting inside the change element rather than beside it. rejectChanges uses that nested property element as the source of truth for the restored state, which matches the tracked-change structure covered by the ECMA-376 revision-marking model.[3]