When reviewing comments that were just added to a Word document, the comment reader must return the saved metadata and comment text from the document package. That readback matters because comment content is stored in word/comments.xml, while the comment range is anchored by markers in word/document.xml.
getComments reads the comment part from the package, extracts the visible comment text, and combines it with range metadata from the document XML.[1] The scenario uses addComment only as setup, so the asserted outcome is the comment array returned by getComments, not the setup primitive's return value.
Below is a test scenario of getComments: reading a comment that was written by addComment.
The scenario
Given a document with a comment added via addComment,
When reading comments via getComments,
Then
- exactly one comment is returned
- comment ID is
0 - author is
Alice - text is
"Nice intro" - initials is
"A" - date is populated
- paragraphId is populated
- replies array is empty.
The test fixture
The fixture builds a document, initializes the comment parts, adds one comment, and then reads comments from the same package and document XML.[2]
Below is the test fixture code.
test('reads comments written by addComment', async ({ given, when, then, and }: AllureBddContext) => {
let zip: DocxZip;
let doc: Document;
let comments: Awaited<ReturnType<typeof getComments>>;
await given('a document with a comment added via addComment', async () => {
const bodyXml = '<w:p><w:r><w:t>Hello World</w:t></w:r></w:p>';
const buf = await makeDocxBuffer(bodyXml);
zip = await loadZip(buf);
await bootstrapCommentParts(zip);
const docXml = await zip.readText('word/document.xml');
doc = parseXml(docXml);
const p = doc.getElementsByTagNameNS(W_NS, W.p).item(0) as Element;
await addComment(doc, zip, {
paragraphEl: p,
start: 0,
end: 5,
author: 'Alice',
text: 'Nice intro',
initials: 'A',
});
});
await when('reading comments via getComments', async () => {
comments = await getComments(zip, doc);
});
await then('exactly one comment is returned', async () => {
expect(comments).toHaveLength(1);
});
await and('comment ID is 0', async () => {
expect(comments[0]!.id).toBe(0);
});
await and('author is Alice', async () => {
expect(comments[0]!.author).toBe('Alice');
});
await and('text is "Nice intro"', async () => {
expect(comments[0]!.text).toBe('Nice intro');
});
await and('initials is "A"', async () => {
expect(comments[0]!.initials).toBe('A');
});
await and('date is populated', async () => {
expect(comments[0]!.date).toBeTruthy();
});
await and('paragraphId is populated', async () => {
expect(comments[0]!.paragraphId).toBeTruthy();
});
await and('replies array is empty', async () => {
expect(comments[0]!.replies).toEqual([]);
});
});
The expected result shape
The scenario asserts the length of the array returned by getComments and the fields on its first comment.
Below is the result that getComments is expected to return for this scenario.
[
{
id: 0,
author: 'Alice',
text: 'Nice intro',
initials: 'A',
date: expect.anything(),
paragraphId: expect.anything(),
replies: [],
},
]
Below is a description of the expected fields:
idis expected to be0, because the fixture creates the first comment in a newly bootstrapped comments part.authoris expected to be'Alice', becauseaddCommentwrites that author value into the comment element during setup.textis expected to be'Nice intro', becausegetCommentsextracts text from the<w:t>elements inside the stored comment body.initialsis expected to be'A', because the setup passes that initials value when creating the comment.dateis expected to be populated, becauseaddCommentwrites a date attribute for the new comment and the scenario checks the returned field withtoBeTruthy().paragraphIdis expected to be populated, because the stored comment paragraph carries the paragraph identifier thatgetCommentsreads from the comment part.repliesis expected to be[], because the fixture creates a root comment without any reply relationship incommentsExtended.xml.
A non-obvious detail
getComments returns root-level comments after it checks commentsExtended.xml for reply links. In this scenario, no child comment is linked to the new comment, so the returned root comment keeps an empty replies array.