@@ -17,11 +17,31 @@ const TEST_DOC = 'blank-doc.docx';
1717
1818const CT_CUSTOM = 'application/vnd.openxmlformats-officedocument.custom-properties+xml' ;
1919const REL_CUSTOM = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties' ;
20+ const WORD_STAT_TEXT = 'Alpha beta gamma' ;
21+ const PARENT_WORD_STAT_TEXT = 'Alpha beta gamma delta' ;
22+ const CHILD_ONLY_TEXT = 'Header words only' ;
23+
24+ function readXmlTagValue ( xml , tagName ) {
25+ const match = xml . match ( new RegExp ( `<${ tagName } >([^<]*)</${ tagName } >` ) ) ;
26+ return match ?. [ 1 ] ?? null ;
27+ }
28+
29+ function readAppStatistics ( xml ) {
30+ return {
31+ words : readXmlTagValue ( xml , 'Words' ) ,
32+ characters : readXmlTagValue ( xml , 'Characters' ) ,
33+ charactersWithSpaces : readXmlTagValue ( xml , 'CharactersWithSpaces' ) ,
34+ } ;
35+ }
36+
37+ async function createHeadlessEditor ( testDoc = TEST_DOC ) {
38+ const { docx, media, mediaFiles, fonts } = await loadTestDataForEditorTests ( testDoc ) ;
39+ return initTestEditor ( { content : docx , media, mediaFiles, fonts, isHeadless : true } ) ;
40+ }
2041
2142describe ( 'OPC package metadata: custom-properties registration' , ( ) => {
2243 it ( 'getUpdatedDocs includes correct [Content_Types].xml and _rels/.rels for new custom.xml' , async ( ) => {
23- const { docx, media, mediaFiles, fonts } = await loadTestDataForEditorTests ( TEST_DOC ) ;
24- const { editor } = initTestEditor ( { content : docx , media, mediaFiles, fonts, isHeadless : true } ) ;
44+ const { editor } = await createHeadlessEditor ( ) ;
2545
2646 try {
2747 const updatedDocs = await editor . exportDocx ( { getUpdatedDocs : true } ) ;
@@ -48,8 +68,7 @@ describe('OPC package metadata: custom-properties registration', () => {
4868 } ) ;
4969
5070 it ( 'zipped export includes valid package metadata for new custom.xml' , async ( ) => {
51- const { docx, media, mediaFiles, fonts } = await loadTestDataForEditorTests ( TEST_DOC ) ;
52- const { editor } = initTestEditor ( { content : docx , media, mediaFiles, fonts, isHeadless : true } ) ;
71+ const { editor } = await createHeadlessEditor ( ) ;
5372
5473 try {
5574 const exportedBuffer = await editor . exportDocx ( { compression : 'STORE' } ) ;
@@ -86,8 +105,7 @@ describe('OPC package metadata: custom-properties registration', () => {
86105 } ) ;
87106
88107 it ( 'preserves existing managed registrations without duplication' , async ( ) => {
89- const { docx, media, mediaFiles, fonts } = await loadTestDataForEditorTests ( TEST_DOC ) ;
90- const { editor } = initTestEditor ( { content : docx , media, mediaFiles, fonts, isHeadless : true } ) ;
108+ const { editor } = await createHeadlessEditor ( ) ;
91109
92110 try {
93111 const updatedDocs = await editor . exportDocx ( { getUpdatedDocs : true } ) ;
@@ -122,4 +140,52 @@ describe('OPC package metadata: custom-properties registration', () => {
122140 editor . destroy ( ) ;
123141 }
124142 } ) ;
143+
144+ it ( 'getUpdatedDocs includes refreshed docProps/app.xml statistics' , async ( ) => {
145+ const { editor } = await createHeadlessEditor ( ) ;
146+
147+ try {
148+ editor . commands . insertContent ( WORD_STAT_TEXT ) ;
149+
150+ const updatedDocs = await editor . exportDocx ( { getUpdatedDocs : true } ) ;
151+ const appXml = updatedDocs [ 'docProps/app.xml' ] ;
152+
153+ expect ( appXml ) . toBeTruthy ( ) ;
154+ expect ( readAppStatistics ( appXml ) ) . toEqual ( {
155+ words : '3' ,
156+ characters : '14' ,
157+ charactersWithSpaces : '16' ,
158+ } ) ;
159+ } finally {
160+ editor . destroy ( ) ;
161+ }
162+ } ) ;
163+
164+ it ( 'linked child exports keep docProps/app.xml statistics scoped to the main document' , async ( ) => {
165+ const { editor } = await createHeadlessEditor ( ) ;
166+ let childEditor = null ;
167+
168+ try {
169+ editor . commands . insertContent ( PARENT_WORD_STAT_TEXT ) ;
170+
171+ childEditor = editor . createChildEditor ( {
172+ isHeadless : true ,
173+ isHeaderOrFooter : true ,
174+ } ) ;
175+ childEditor . commands . insertContent ( CHILD_ONLY_TEXT ) ;
176+
177+ const updatedDocs = await childEditor . exportDocx ( { getUpdatedDocs : true } ) ;
178+ const appXml = updatedDocs [ 'docProps/app.xml' ] ;
179+
180+ expect ( appXml ) . toBeTruthy ( ) ;
181+ expect ( readAppStatistics ( appXml ) ) . toEqual ( {
182+ words : '4' ,
183+ characters : '19' ,
184+ charactersWithSpaces : '22' ,
185+ } ) ;
186+ } finally {
187+ childEditor ?. destroy ( ) ;
188+ editor . destroy ( ) ;
189+ }
190+ } ) ;
125191} ) ;
0 commit comments