@ansonlai/docx-redline-js 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +36 -10
- package/README.md +83 -6
- package/adapters/xml-adapter.js +73 -10
- package/core/list-targeting.js +3 -0
- package/core/paragraph-targeting.js +33 -7
- package/core/redline-validation.js +22 -0
- package/core/types.js +122 -27
- package/core/xml-query.js +3 -1
- package/dist/docx-redline-js.esm.js +1124 -545
- package/dist/docx-redline-js.esm.js.map +4 -4
- package/dist/docx-redline-js.esm.min.js +79 -78
- package/dist/docx-redline-js.esm.min.js.map +4 -4
- package/docs/TESTING.md +687 -0
- package/docs/VALIDATION.md +81 -2
- package/docs/WORD-MANUAL-REVIEW.md +138 -0
- package/docs/plans/2026-08-30-reliability-testing-improvements.md +488 -0
- package/docs/plans/2026-09-01-performance-and-complexity-reduction.md +210 -0
- package/docs/plans/{2026-03-01-release-0.1.4-design.md → completed/2026-03-01-release-0.1.4-design.md} +2 -0
- package/docs/plans/{2026-03-01-release-0.1.4.md → completed/2026-03-01-release-0.1.4.md} +5 -3
- package/docs/plans/{2026-05-31-architectural changes.md → completed/2026-05-31-architectural changes.md } +2 -0
- package/docs/plans/completed/2026-08-02-reliability-improvements.md +1155 -0
- package/docs/test-comparison-dashboard.html +95 -0
- package/docs/validation-reports/2026-08-30-phase-1-word-visual-preflight.md +22 -0
- package/docs/validation-reports/2026-08-30-phase-2-word-visual-preflight.md +24 -0
- package/docs/validation-reports/2026-08-30-phase-3-coverage.md +73 -0
- package/docs/validation-reports/2026-09-02-multilevel-bullets-visual-review.md +82 -0
- package/docs/validation-reports/2026-09-02-multimodal-visual-samples.md +114 -0
- package/docs/validation-reports/2026-09-02-visual-failures-preflight.md +79 -0
- package/engine/format-extraction.js +1 -1
- package/engine/formatting-removal.js +84 -99
- package/engine/oxml-engine.js +176 -83
- package/engine/reconstruction-mapper.js +276 -79
- package/engine/reconstruction-mode.js +20 -6
- package/engine/reconstruction-writer.js +117 -72
- package/engine/run-builders.js +3 -3
- package/engine/surgical-mode.js +3 -2
- package/engine/table-mode.js +27 -16
- package/index.d.ts +95 -3
- package/index.js +14 -13
- package/orchestration/list-structural-fallback.js +16 -39
- package/package.json +22 -4
- package/pipeline/diff-engine.js +174 -55
- package/pipeline/ingestion-export.js +39 -24
- package/pipeline/ingestion-paragraph.js +7 -5
- package/pipeline/list-generation.js +27 -18
- package/pipeline/patching.js +2 -3
- package/pipeline/pipeline.js +65 -36
- package/pipeline/serialization.js +13 -5
- package/scripts/build-test-dashboard.mjs +43 -0
- package/scripts/check-types.mjs +16 -24
- package/scripts/export-validation-fixtures.mjs +191 -45
- package/scripts/fetch-superdoc-corpus.mjs +61 -0
- package/scripts/generate-test-dashboard.mjs +199 -0
- package/scripts/inspect-visual-evidence.mjs +271 -0
- package/scripts/lib/minimal-zip.mjs +199 -18
- package/scripts/lib/word-coverage-catalogue.mjs +207 -0
- package/scripts/lib/word-coverage-metadata.mjs +93 -0
- package/scripts/lib/zip-reader.mjs +64 -0
- package/scripts/package-superdoc-word-fixtures.ps1 +64 -0
- package/scripts/prepare-corpus-word-visual-review.mjs +84 -0
- package/scripts/prepare-superdoc-word-corpus.mjs +284 -0
- package/scripts/prepare-word-review.mjs +77 -0
- package/scripts/prepare-word-visual-review.mjs +90 -0
- package/scripts/render-agenda-multilevel.mjs +70 -0
- package/scripts/render-case22.mjs +73 -0
- package/scripts/render-case40.ps1 +35 -0
- package/scripts/render-multilevel-bullet-images.py +58 -0
- package/scripts/render-multilevel-bullet-visual.ps1 +32 -0
- package/scripts/render-multilevel-cases.mjs +80 -0
- package/scripts/report-coverage-gaps.mjs +103 -0
- package/scripts/report-word-coverage.mjs +71 -0
- package/scripts/sample-multimodal-visual-check.mjs +221 -0
- package/scripts/test-multilevel-bullet-visual.mjs +187 -0
- package/scripts/word-com-corpus-suite.ps1 +43 -0
- package/scripts/word-com-corpus-visual-suite.ps1 +116 -0
- package/scripts/word-com-differential.ps1 +158 -16
- package/scripts/word-com-suite.ps1 +19 -0
- package/scripts/word-com-visual-suite.ps1 +132 -0
- package/services/comment-engine.js +51 -46
- package/services/comment-locator.js +0 -1
- package/services/comment-package.js +11 -10
- package/services/numbering-service.js +1 -1
- package/services/revision-comment-management.js +31 -10
- package/services/standalone-docx-plumbing.js +45 -34
- package/services/standalone-operation-runner.js +315 -75
- package/services/table-reconciliation.js +23 -11
package/docs/TESTING.md
ADDED
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
# Testing Guide
|
|
2
|
+
|
|
3
|
+
This project uses several test lanes because no single oracle can prove that a
|
|
4
|
+
tracked-change document is correct. JavaScript assertions can verify exact XML
|
|
5
|
+
and return contracts; Microsoft Word can verify what the primary consumer
|
|
6
|
+
actually accepts and rejects; real documents expose structures that small
|
|
7
|
+
fixtures rarely contain.
|
|
8
|
+
|
|
9
|
+
## Test lanes at a glance
|
|
10
|
+
|
|
11
|
+
| Lane | Command | What it proves | What it does not prove |
|
|
12
|
+
|---|---|---|---|
|
|
13
|
+
| Automated regression suite | `npm test` | API behavior, exact text invariants, OOXML structure, deterministic fuzz cases, and catalogue integrity | That desktop Word accepts the generated package |
|
|
14
|
+
| Isolation, types, and lint | `npm run test:isolation`, `npm run check:types`, `npm run lint` | Runtime boundaries, declaration alignment, and static repository rules | Document correctness |
|
|
15
|
+
| JavaScript coverage | `npm run test:coverage` | Which source lines, functions, and branches the automated suite executes | That an executed path is correct |
|
|
16
|
+
| Synthetic Word differential | `npm run test:word` | Word opens generated packages, sees revisions, and produces the intended text after Accept All and Reject All | The diversity of real-world packages |
|
|
17
|
+
| Word visual evidence | `npm run test:word:visual` | Word renders layout-sensitive fixtures in All Markup, Accept All, and Reject All views and writes a pending review manifest | That the rendered pages are visually correct until a reviewer inspects them |
|
|
18
|
+
| SuperDoc Word corpus | `npm run test:corpus:word` | The same Word differential on 60 scenarios drawn from 23 reviewed, pinned real English legal/administrative documents while untouched package parts remain byte-identical | Every possible DOCX producer or document type |
|
|
19
|
+
| SuperDoc visual evidence | `npm run test:corpus:word:visual` | Word renders 40 focused list, table, long-document, and page-header scenarios in All Markup, Accept All, and Reject All views | Human visual sign-off |
|
|
20
|
+
| Visual evidence inspection | `npm run test:visual:inspect` | Automated inspection of rendered Word PDFs across synthetic and SuperDoc suites (page counts, PDF integrity, anomaly detection) | Human visual sign-off |
|
|
21
|
+
| Visual failure regressions | `node tests/visual_failure_regression_tests.mjs` | Semantic OOXML guards against visual failures (formatting leaks, font resets, ghost bullets, table cell destruction) | Visual rendering proof in Word |
|
|
22
|
+
| Multimodal LLM visual spot check | On-demand / sampled | Evaluates rendered real-document pages with vision models for layout, table alignment, and typography regressions | Full-corpus automated coverage (intentionally decoupled and sampled due to cost/time) |
|
|
23
|
+
| XSD and LibreOffice | See `docs/VALIDATION.md` | Schema conformance and acceptance by a second consumer | Word-specific revision semantics |
|
|
24
|
+
|
|
25
|
+
## Coverage matrix and test selection
|
|
26
|
+
|
|
27
|
+
Run the deterministic task-by-structure report before choosing the next Word
|
|
28
|
+
case:
|
|
29
|
+
|
|
30
|
+
```powershell
|
|
31
|
+
npm run report:word:coverage
|
|
32
|
+
npm run report:word:coverage -- --json
|
|
33
|
+
npm run report:test:dashboard
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The dashboard command writes a self-contained interactive report to
|
|
37
|
+
`docs/test-comparison-dashboard.html`. It compares task/structure cells,
|
|
38
|
+
synthetic and real-document coverage, independent oracles, visual-render
|
|
39
|
+
eligibility, and planned high-priority gaps from the live catalogues. It also
|
|
40
|
+
embeds source, tracked, accepted, and rejected packages for every synthetic
|
|
41
|
+
fixture and, when the pinned corpus is downloaded, all 60 reviewed real legal
|
|
42
|
+
and administrative scenarios. Its `docx-preview` workbench supports arbitrary left/right states,
|
|
43
|
+
comparison presets, synchronized scrolling, revision metadata, expected text,
|
|
44
|
+
and local downloads; no fixture upload or file picker is required. Real cases
|
|
45
|
+
are labeled in the document selector and selected by default when available.
|
|
46
|
+
Use **Hide sidebar** beside the dashboard filters to remove the coverage-detail
|
|
47
|
+
column and expand the comparison workbench to the full browser width. The
|
|
48
|
+
button becomes **Show sidebar**, and the preference is stored locally so the
|
|
49
|
+
wide document view survives a reload.
|
|
50
|
+
Without the local corpus, generation remains offline-safe and embeds only the
|
|
51
|
+
synthetic previews.
|
|
52
|
+
|
|
53
|
+
The report combines all synthetic and reviewed SuperDoc scenarios. Synthetic
|
|
54
|
+
metadata is declared in `tests/fixtures/word-task-coverage.mjs`; SuperDoc
|
|
55
|
+
metadata is declared in `tests/corpus/superdoc-word-coverage.json`. Descriptive
|
|
56
|
+
`task`, `shape`, and `coverage` labels remain on the original cases as review
|
|
57
|
+
notes, while the matrix uses shared task, structure, and oracle vocabularies.
|
|
58
|
+
|
|
59
|
+
Catalogue tests reject unknown vocabulary labels, missing oracle or manual-review
|
|
60
|
+
metadata, duplicate identities, structural claims unsupported by fixture XML or
|
|
61
|
+
reviewed corpus labels, and uncovered high-priority cells without a recorded
|
|
62
|
+
plan or exclusion. The dispositions live in
|
|
63
|
+
`tests/fixtures/coverage-matrix-priorities.json` and require both a reason and a
|
|
64
|
+
dependency.
|
|
65
|
+
|
|
66
|
+
Choose new cases from uncovered high-priority cells first, then prefer a new
|
|
67
|
+
task/structure combination over another case in a dense cell. A higher count is
|
|
68
|
+
not itself a reason to add a fixture. Adding a catalogue case automatically
|
|
69
|
+
changes the report because it reads the live metadata.
|
|
70
|
+
|
|
71
|
+
## Reliability improvement process used for this test expansion
|
|
72
|
+
|
|
73
|
+
The current corpus, visual runners, and comparison dashboard were built as one
|
|
74
|
+
feedback loop rather than as separate test features. The working sequence is:
|
|
75
|
+
|
|
76
|
+
1. inventory the claimed behavior and existing test lanes;
|
|
77
|
+
2. add realistic cases that expose weak structures;
|
|
78
|
+
3. assert the smallest machine-checkable invariant at the engine level;
|
|
79
|
+
4. package the result into the original real document without changing
|
|
80
|
+
unrelated parts;
|
|
81
|
+
5. ask Word to accept and reject the revisions;
|
|
82
|
+
6. render All Markup, Accept All, and Reject All views;
|
|
83
|
+
7. inspect the actual pages at both contact-sheet and full-page scale; and
|
|
84
|
+
8. turn every visual defect into a focused regression plus a corpus assertion.
|
|
85
|
+
|
|
86
|
+
This ordering matters. A valid `<w:ins>` or `<w:del>` tree does not establish
|
|
87
|
+
that the inserted run inherited the correct font, list indentation, hyperlink
|
|
88
|
+
wrapper, table-cell formatting, or header styling. Conversely, a good-looking
|
|
89
|
+
PDF does not prove that Reject All restores the exact source. Each stage answers
|
|
90
|
+
a different question, and a case is useful only when its intended claim is
|
|
91
|
+
explicit.
|
|
92
|
+
|
|
93
|
+
### Start from coverage claims, not fixture count
|
|
94
|
+
|
|
95
|
+
Read the task-by-structure matrix and select a real document that adds a missing
|
|
96
|
+
or weak combination. The expansion deliberately favored bullets, nested lists,
|
|
97
|
+
table cells, headers, long documents, and atomic multi-change batches because
|
|
98
|
+
these structures cross more OOXML boundaries than a single plain paragraph.
|
|
99
|
+
Several scenarios reuse a pinned source when a new batch exercises a genuinely
|
|
100
|
+
different structure; this increases behavioral coverage without pretending
|
|
101
|
+
that another copy of the same document is a new source.
|
|
102
|
+
|
|
103
|
+
For long real documents, record structural minimums such as word count, table
|
|
104
|
+
count, list-paragraph count, section count, and related header/footer parts.
|
|
105
|
+
`scripts/prepare-superdoc-word-corpus.mjs` checks these declarations against the
|
|
106
|
+
source package. This prevents a scenario description such as “table-heavy” or
|
|
107
|
+
“multi-section” from silently drifting away from the actual fixture.
|
|
108
|
+
|
|
109
|
+
### Use multi-change batches to exercise interaction effects
|
|
110
|
+
|
|
111
|
+
Single replacements remain valuable for isolating engine behavior, but real
|
|
112
|
+
editing sessions usually contain several changes. The larger corpus scenarios
|
|
113
|
+
therefore use `operations` to apply independent edits atomically across one
|
|
114
|
+
document. Targets are chosen from different list items or table cells when
|
|
115
|
+
possible. This catches failures caused by earlier replacements shifting later
|
|
116
|
+
anchors, revision-ID allocation across a batch, and formatting state leaking
|
|
117
|
+
between operations.
|
|
118
|
+
|
|
119
|
+
The batch runner must either produce all requested revisions or roll back the
|
|
120
|
+
document. Word then checks the complete accepted and rejected story, not merely
|
|
121
|
+
the first target. Untouched ZIP parts are hashed and compared with the pinned
|
|
122
|
+
source, so success cannot come from rebuilding or normalizing the rest of the
|
|
123
|
+
package.
|
|
124
|
+
|
|
125
|
+
### Expand weak list and table cells with structural operations
|
|
126
|
+
|
|
127
|
+
The list/table expansion was driven directly by the task-by-structure matrix;
|
|
128
|
+
it did not add more plain replacement cases. The current catalogue contains 107
|
|
129
|
+
Word cases: 47 synthetic fixtures and 60 reviewed real-document scenarios. The
|
|
130
|
+
`List Change × List` cell now contains 26 cases; the
|
|
131
|
+
`Table Reconciliation × Table` cell contains 11 cases.
|
|
132
|
+
|
|
133
|
+
The added synthetic cases isolate one structural decision at a time:
|
|
134
|
+
|
|
135
|
+
- append one list item;
|
|
136
|
+
- append several adjacent items;
|
|
137
|
+
- add a nested child;
|
|
138
|
+
- insert into the middle of an explicit list range;
|
|
139
|
+
- preserve upper- and lower-Roman numbering;
|
|
140
|
+
- preserve upper-letter, parenthesized lower-letter, dash, and symbol bullets;
|
|
141
|
+
- update one table cell;
|
|
142
|
+
- insert a table row;
|
|
143
|
+
- delete a table row; and
|
|
144
|
+
- update several cells in one reconciliation.
|
|
145
|
+
|
|
146
|
+
The real-document cases repeat those claims against native numbering and table
|
|
147
|
+
markup from municipal notices, board agendas, a long zoning ordinance, PPG
|
|
148
|
+
minutes, action tables, and invoice tables. They include consecutive and nested
|
|
149
|
+
list insertions, a long-document definition insertion, single- and multi-cell
|
|
150
|
+
updates, row insertion/deletion, and invoice row changes. Reusing a reviewed,
|
|
151
|
+
hash-pinned source is intentional when the scenario exercises a different
|
|
152
|
+
operation; the unit of coverage is the behavioral claim, not the download.
|
|
153
|
+
|
|
154
|
+
These cases declare `operation.type` as `list-change` or
|
|
155
|
+
`table-reconciliation`. Their accepted/rejected expectations use explicit
|
|
156
|
+
contains/absent assertions when a Markdown table payload or a structural list
|
|
157
|
+
edit cannot be compared meaningfully to the raw Word source as one exact text
|
|
158
|
+
string. Structural expectations still verify real properties such as native
|
|
159
|
+
list paragraphs, tables, headers/footers, sections, and minimum document size.
|
|
160
|
+
The catalogue tests require these expectations and reject unknown operation or
|
|
161
|
+
coverage labels.
|
|
162
|
+
|
|
163
|
+
List-style cases do not infer their claim from visible text alone. Synthetic
|
|
164
|
+
fixtures declare the required `w:numFmt` values and use complete, schema-ordered
|
|
165
|
+
numbering parts: every `w:abstractNum` must precede the first concrete `w:num`
|
|
166
|
+
mapping. That ordering is regression-tested because Word otherwise opens the
|
|
167
|
+
package but silently substitutes its fallback bullet. Real scenarios declare a
|
|
168
|
+
`listStyleExpectation` with the source level, number format, and, where useful,
|
|
169
|
+
the exact `w:lvlText`. Corpus preparation resolves the target paragraph's
|
|
170
|
+
`w:numId` through `word/numbering.xml` and fails before editing if the pinned
|
|
171
|
+
source no longer has that native style. The current real cases exercise upper
|
|
172
|
+
and lower Roman, upper and lower letter, parenthesized lower letter, dash, and
|
|
173
|
+
symbol bullets across bylaws, agendas, healthcare minutes, and long council
|
|
174
|
+
minutes.
|
|
175
|
+
|
|
176
|
+
### Diagnose visual failures in the generated OOXML first
|
|
177
|
+
|
|
178
|
+
When a page looks wrong, compare the relevant source and tracked
|
|
179
|
+
`word/document.xml` or related part before changing the renderer. Locate the
|
|
180
|
+
visible text, inspect its nearest `w:r`, `w:rPr`, wrapper, paragraph properties,
|
|
181
|
+
and neighboring runs, then compare those properties with the source. This
|
|
182
|
+
separates an engine defect from a Word, PDF, or `docx-preview` display issue.
|
|
183
|
+
|
|
184
|
+
Three defects illustrate the approach:
|
|
185
|
+
|
|
186
|
+
- The council header date replacement inherited `w:vertAlign="superscript"`
|
|
187
|
+
from the trailing ordinal suffix. The fix anchors replacement formatting at
|
|
188
|
+
the beginning of the deleted range, and the regression explicitly forbids
|
|
189
|
+
`vertAlign` on the inserted runs.
|
|
190
|
+
- The prospectus filing-date replacement crossed normal runs and a hyperlink.
|
|
191
|
+
A forward-only property lookup had already moved past the beginning of the
|
|
192
|
+
deletion when the insertion asked for its formatting, so the new run had no
|
|
193
|
+
`w:rPr`. Word and SuperDoc correctly fell back from the surrounding 10 pt to
|
|
194
|
+
the document default of 12 pt. The lookup now supports that deliberate
|
|
195
|
+
backward query. The focused regression requires `w:sz="20"` and
|
|
196
|
+
`w:szCs="20"`, and the real prospectus scenario requires those values on
|
|
197
|
+
every inserted text run.
|
|
198
|
+
- A wholly inserted list item originally tracked only its text run. Accept All
|
|
199
|
+
looked correct and text-only Reject All assertions passed, but Word retained
|
|
200
|
+
the untracked paragraph mark as an empty bullet or number. Inserted list
|
|
201
|
+
paragraphs now track both their text and paragraph mark. The regression
|
|
202
|
+
rejects the change and asserts the exact original paragraph count, text, and
|
|
203
|
+
numbering, while the real Word PDFs confirm that no ghost marker remains.
|
|
204
|
+
|
|
205
|
+
These assertions test the semantic cause rather than a screenshot pixel. The
|
|
206
|
+
visual render remains necessary to confirm that the corrected properties
|
|
207
|
+
produce the intended page. The automated test suite in
|
|
208
|
+
`tests/visual_failure_regression_tests.mjs` codifies these guards directly in
|
|
209
|
+
the regression test pipeline (`npm test`) to prevent regressions before DOCX
|
|
210
|
+
packages reach Word.
|
|
211
|
+
|
|
212
|
+
### Regenerate packages; do not reason from stale outputs
|
|
213
|
+
|
|
214
|
+
Source downloads are content-addressed and may be reused. Generated tracked,
|
|
215
|
+
accepted, rejected, PDF, and dashboard artifacts are outputs and must be
|
|
216
|
+
rebuilt after an engine or scenario change. A `Verified cached ...` line from
|
|
217
|
+
the corpus fetcher means only that the pinned source `.docx` already matches its
|
|
218
|
+
recorded hash; it does not mean the edited package was reused.
|
|
219
|
+
|
|
220
|
+
Use this sequence for a corpus-affecting engine change:
|
|
221
|
+
|
|
222
|
+
```powershell
|
|
223
|
+
npm test
|
|
224
|
+
npm run check:types
|
|
225
|
+
npm run lint
|
|
226
|
+
npm run test:isolation
|
|
227
|
+
npm run test:corpus:word
|
|
228
|
+
npm run test:corpus:word:visual
|
|
229
|
+
npm run report:test:dashboard
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
For faster iteration, `prepare-superdoc-word-corpus.mjs` accepts
|
|
233
|
+
`--input-dir` and `--output-dir`, and the visual PowerShell runner accepts
|
|
234
|
+
`-Case <scenario-key>` plus custom fixture/output directories. Run the complete
|
|
235
|
+
lane before handoff even when a focused render passed.
|
|
236
|
+
|
|
237
|
+
### Treat Word COM cleanup separately from render success
|
|
238
|
+
|
|
239
|
+
Word sometimes disconnects a COM document proxy after successfully exporting
|
|
240
|
+
a PDF and raises `RPC_E_DISCONNECTED` during `Document.Close()`. The visual
|
|
241
|
+
runner uses safe close/quit helpers: a disconnect during post-export cleanup is
|
|
242
|
+
reported but does not discard a valid, non-empty PDF; a disconnect during the
|
|
243
|
+
actual render causes Word to restart and the case to retry once. Other COM
|
|
244
|
+
exceptions still fail the run. This distinction avoids both false failures and
|
|
245
|
+
false passes.
|
|
246
|
+
|
|
247
|
+
Each visual invocation uses a process-specific fixture directory. That keeps a
|
|
248
|
+
Word process left behind by an earlier disconnect from locking or contaminating
|
|
249
|
+
the next set of generated DOCX files.
|
|
250
|
+
|
|
251
|
+
### Review both the whole document and the changed page
|
|
252
|
+
|
|
253
|
+
For a long document, create low-resolution contact sheets for every page in all
|
|
254
|
+
three revision states and inspect them for pagination changes, blank pages,
|
|
255
|
+
clipping, table displacement, or large typography shifts. Then inspect every
|
|
256
|
+
changed page at full resolution. Contact sheets are good at document-wide
|
|
257
|
+
layout; they are not reliable for a 10 pt versus 12 pt difference or a leaked
|
|
258
|
+
superscript flag.
|
|
259
|
+
|
|
260
|
+
Record the exact scenario, Word version/build, revision view, page, observed
|
|
261
|
+
fact, and whether the judgment is certain. The visual manifest remains pending
|
|
262
|
+
until a reviewer makes that judgment; successful PDF export alone is not a
|
|
263
|
+
visual pass.
|
|
264
|
+
|
|
265
|
+
### Keep the dashboard generated and test its controls
|
|
266
|
+
|
|
267
|
+
`scripts/generate-test-dashboard.mjs` owns the dashboard markup, styles, and
|
|
268
|
+
behavior. Do not hand-edit `docs/test-comparison-dashboard.html`; rebuild it
|
|
269
|
+
with `npm run report:test:dashboard`. The report embeds the four DOCX states so
|
|
270
|
+
it remains self-contained and can show the exact generated packages without a
|
|
271
|
+
file picker or server.
|
|
272
|
+
|
|
273
|
+
Dashboard changes require generator-level assertions in
|
|
274
|
+
`tests/test_dashboard_report_tests.mjs`. The sidebar-width control, for
|
|
275
|
+
example, is checked for its accessible relationship, full-width CSS state, and
|
|
276
|
+
persisted preference. DOCX package parsing remains covered separately by
|
|
277
|
+
`tests/docxjs_dashboard_rendering_tests.mjs`. When browser control is available,
|
|
278
|
+
also click the control in the generated page and verify that the sidebar is
|
|
279
|
+
hidden, both document panes widen, the label changes, and reloading preserves
|
|
280
|
+
the selected state.
|
|
281
|
+
|
|
282
|
+
### Definition of done for a discovered real-document defect
|
|
283
|
+
|
|
284
|
+
A visual defect is complete only when all of the following are true:
|
|
285
|
+
|
|
286
|
+
- the source and faulty generated OOXML have been compared;
|
|
287
|
+
- the engine cause is understood rather than masked in CSS or the renderer;
|
|
288
|
+
- a minimal automated regression reproduces the structural boundary;
|
|
289
|
+
- Accept All yields the requested text and Reject All restores the source;
|
|
290
|
+
- the affected real corpus scenario has an appropriate structural or
|
|
291
|
+
formatting assertion;
|
|
292
|
+
- its source, tracked, accepted, and rejected packages have been regenerated;
|
|
293
|
+
- desktop Word differential passes;
|
|
294
|
+
- the changed page and the document-wide three-view render have been inspected;
|
|
295
|
+
- the self-contained comparison dashboard has been rebuilt; and
|
|
296
|
+
- the full automated, isolation, type, lint, and whitespace checks pass.
|
|
297
|
+
|
|
298
|
+
Prepare, but do not approve, a human review sample with:
|
|
299
|
+
|
|
300
|
+
```powershell
|
|
301
|
+
npm run review:word:prepare -- --cycle=0
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
The ignored `tmp/word-manual-review/review-manifest-cycle-0.json` selects all
|
|
305
|
+
catalogue families changed in the worktree, a rotating 20% synthetic sample,
|
|
306
|
+
and one legal plus one administrative SuperDoc case. Every All Markup, Accept
|
|
307
|
+
All, Reject All, and human sign-off field starts as `pending`; the helper cannot
|
|
308
|
+
turn them into passes. Increment `--cycle` between releases to rotate the
|
|
309
|
+
unchanged sample.
|
|
310
|
+
|
|
311
|
+
## Automated JavaScript tests
|
|
312
|
+
|
|
313
|
+
Files matching `tests/*.mjs` are discovered by `scripts/run-tests.mjs`. Tests
|
|
314
|
+
use `assert/strict` and run as separate Node processes. Shared OOXML assertions
|
|
315
|
+
belong in `tests/helpers/ooxml-assertions.mjs`; XML-provider setup belongs in
|
|
316
|
+
`tests/setup-xml-provider.mjs`.
|
|
317
|
+
|
|
318
|
+
For function-level gap work, run:
|
|
319
|
+
|
|
320
|
+
```powershell
|
|
321
|
+
npm run test:coverage
|
|
322
|
+
npm run coverage:gaps
|
|
323
|
+
npm run coverage:gaps -- --json
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
The coverage command emits both the text summary and detailed Istanbul JSON.
|
|
327
|
+
The gap report includes only runtime production roots, lists each uncovered
|
|
328
|
+
function with its file and declaration line, assigns the Phase 3 P0/P1/P2
|
|
329
|
+
priority, and fails if a targeted file drops below the checked covered-function
|
|
330
|
+
or covered-branch baseline in `tests/coverage-data/phase3-baseline.json`. Because V8
|
|
331
|
+
discovers new branch sites when a formerly cold function first executes, review
|
|
332
|
+
covered counts and behavior assertions alongside percentages. Do not add ignore
|
|
333
|
+
annotations or call private code merely to improve a score; classify a retained
|
|
334
|
+
gap with a reachability or environment reason.
|
|
335
|
+
|
|
336
|
+
For an engine regression:
|
|
337
|
+
|
|
338
|
+
1. Add the smallest fixed case that reproduces the bug and asserts the exact
|
|
339
|
+
result or structured error.
|
|
340
|
+
2. Assert both sides of tracked changes: accepting must produce the requested
|
|
341
|
+
text and rejecting must restore the original text exactly.
|
|
342
|
+
3. Assert structural invariants with `validateRedlineOoxml` and the shared OOXML
|
|
343
|
+
helpers. Do not use a whitespace-collapsing reader as the oracle.
|
|
344
|
+
4. If engine behavior changed, add the same shape to the deterministic fuzz
|
|
345
|
+
corpus so nearby inputs are exercised too.
|
|
346
|
+
5. Run `npm test`, isolation, types, and lint.
|
|
347
|
+
|
|
348
|
+
## Synthetic Microsoft Word tests
|
|
349
|
+
|
|
350
|
+
The runtime library contains no COM or Word dependency. Word automation exists
|
|
351
|
+
only in Windows development scripts and is intentionally excluded from package
|
|
352
|
+
runtime paths.
|
|
353
|
+
|
|
354
|
+
The synthetic catalogue is `tests/fixtures/word-task-cases.mjs`. During
|
|
355
|
+
`npm run test:word`:
|
|
356
|
+
|
|
357
|
+
1. `scripts/export-validation-fixtures.mjs` applies each operation, runs the
|
|
358
|
+
library's structural validator, checks any case-specific `requiredElements`,
|
|
359
|
+
and builds a minimal `.docx` under ignored `tmp/word-validation/` storage.
|
|
360
|
+
2. Expected Accept All and Reject All text is derived from the test's edit
|
|
361
|
+
intent, not from the library's own revision-management functions.
|
|
362
|
+
3. `scripts/word-com-differential.ps1` opens each fixture through desktop Word
|
|
363
|
+
without a repair dialog and confirms Word sees at least one revision.
|
|
364
|
+
4. Word accepts every revision in one fresh document and rejects every revision
|
|
365
|
+
in another. Both resulting texts are compared with the intent-derived
|
|
366
|
+
expectations.
|
|
367
|
+
|
|
368
|
+
To add a synthetic Word case:
|
|
369
|
+
|
|
370
|
+
1. Add a unique lowercase-hyphenated entry to `WORD_TASK_CASES` with
|
|
371
|
+
`category: 'legal'` or `'administrative'`, a distinct `task`, and `original`
|
|
372
|
+
and `modified` text.
|
|
373
|
+
2. Use `sourceDocumentXml` when the case needs bookmarks, hyperlinks, tables,
|
|
374
|
+
content controls, prior revisions, or another deliberate structure.
|
|
375
|
+
3. Add `requiredElements`, such as `{ bookmarkStart: 1, bookmarkEnd: 1 }`, when
|
|
376
|
+
retaining a structure is part of the claim. These are minimum namespace-aware
|
|
377
|
+
element counts checked before Word runs.
|
|
378
|
+
4. Supply `expectedAcceptedText` and `expectedRejectedText` when the source is a
|
|
379
|
+
multi-paragraph document or Word exposes structural separators such as table
|
|
380
|
+
row boundaries. Exact comparison is the default.
|
|
381
|
+
5. Run `node tests/word_task_catalog_tests.mjs`, then `npm run test:word` on a
|
|
382
|
+
Windows machine with desktop Word installed.
|
|
383
|
+
|
|
384
|
+
### Related-part fixture schema
|
|
385
|
+
|
|
386
|
+
The script-only packager supports optional numbering plus comments, footnotes,
|
|
387
|
+
endnotes, headers, footers, and external hyperlinks. Add them to a catalogue
|
|
388
|
+
case through `packageParts`:
|
|
389
|
+
|
|
390
|
+
```js
|
|
391
|
+
{
|
|
392
|
+
sourceDocumentXml,
|
|
393
|
+
packageParts: {
|
|
394
|
+
commentsXml,
|
|
395
|
+
footnotesXml,
|
|
396
|
+
endnotesXml,
|
|
397
|
+
headers: [{
|
|
398
|
+
partName: 'header1.xml',
|
|
399
|
+
relationshipId: 'rIdHeader1',
|
|
400
|
+
xml: headerXml
|
|
401
|
+
}],
|
|
402
|
+
footers: [{
|
|
403
|
+
partName: 'footer1.xml',
|
|
404
|
+
relationshipId: 'rIdFooter1',
|
|
405
|
+
xml: footerXml
|
|
406
|
+
}],
|
|
407
|
+
externalHyperlinks: [{
|
|
408
|
+
relationshipId: 'rIdPolicy',
|
|
409
|
+
target: 'https://example.com/policy'
|
|
410
|
+
}]
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
`createCommentsPart`, `createNotesPart`, and `createHeaderFooterPart` in
|
|
416
|
+
`tests/fixtures/word-package-parts.mjs` provide small escaped constructors for
|
|
417
|
+
the common XML parts. Header/footer `partName` and `relationshipId` values have
|
|
418
|
+
deterministic defaults, but explicit values make the corresponding
|
|
419
|
+
`w:headerReference`, `w:footerReference`, or `w:hyperlink` easier to audit.
|
|
420
|
+
|
|
421
|
+
Before ZIP emission, the packager rejects malformed XML, duplicate relationship
|
|
422
|
+
IDs or part names, missing relationship targets, undefined comment/note IDs,
|
|
423
|
+
and note parts without separator IDs `-1` and `0`. It generates the required
|
|
424
|
+
content-type overrides and document relationships. Every supplied related part
|
|
425
|
+
is compared byte-for-byte with the packaged entry, recorded as SHA-256 in the
|
|
426
|
+
case sidecar, and rechecked by the Word differential before opening the DOCX.
|
|
427
|
+
These helpers remain under `scripts/` and `tests/`; none are shipped through the
|
|
428
|
+
runtime entry point.
|
|
429
|
+
|
|
430
|
+
### What the automated Word differential proves
|
|
431
|
+
|
|
432
|
+
The Word process is real desktop Microsoft Word, but it is driven invisibly
|
|
433
|
+
through COM. For each fixture, the script:
|
|
434
|
+
|
|
435
|
+
1. starts `Word.Application` with alerts and the window disabled;
|
|
436
|
+
2. opens the generated package with `OpenNoRepairDialog`;
|
|
437
|
+
3. fails if Word cannot open it or sees zero tracked revisions;
|
|
438
|
+
4. accepts all revisions and reads `Document.Content.Text`;
|
|
439
|
+
5. closes without saving, reopens the untouched fixture, rejects all revisions,
|
|
440
|
+
and reads the text again; and
|
|
441
|
+
6. compares both results with expectations that were not calculated by this
|
|
442
|
+
library's own accept/reject implementation.
|
|
443
|
+
|
|
444
|
+
Exact comparison is the default. The harness removes Word's terminal paragraph
|
|
445
|
+
mark, normalizes CR/LF representation, and removes the characters Word exposes
|
|
446
|
+
as table-cell and footnote/endnote reference boundaries; case-specific normalized comparison requires an
|
|
447
|
+
explicit reason. Documents are never saved by the automated differential.
|
|
448
|
+
|
|
449
|
+
For synthetic fixtures, expectations come directly from `original`, `modified`,
|
|
450
|
+
and any explicit full-document expectations in the case. For SuperDoc fixtures,
|
|
451
|
+
Word first reads the declared story from the original pinned source document;
|
|
452
|
+
every target in a single or multi-change scenario must occur exactly once, and
|
|
453
|
+
the accepted expectation is formed by applying those replacements to Word's own
|
|
454
|
+
source text. Header scenarios use Word's header stories rather than body text.
|
|
455
|
+
The corpus packager hashes every package part other than the one intentionally
|
|
456
|
+
replaced by the scenario.
|
|
457
|
+
|
|
458
|
+
This proves that Word can consume the package, recognizes the revision markup,
|
|
459
|
+
and resolves Accept All and Reject All to the intended text. It does **not**
|
|
460
|
+
prove that the document looks right on the page. `Content.Text` cannot detect
|
|
461
|
+
bad pagination, awkward revision balloons, shifted table widths, broken tab
|
|
462
|
+
alignment, font substitution, changed list indentation, clipped headers,
|
|
463
|
+
visually stale fields, or a comment/footnote marker that is technically present
|
|
464
|
+
but poorly placed.
|
|
465
|
+
|
|
466
|
+
### Human Word visual review
|
|
467
|
+
|
|
468
|
+
Human visual review complements—not replaces—the automated differential. Use
|
|
469
|
+
the checklist and report template in
|
|
470
|
+
[`WORD-MANUAL-REVIEW.md`](./WORD-MANUAL-REVIEW.md).
|
|
471
|
+
|
|
472
|
+
Review is required for:
|
|
473
|
+
|
|
474
|
+
- every new or materially changed synthetic Word case;
|
|
475
|
+
- the first case for a new structure, operation type, or related package part;
|
|
476
|
+
- any engine change affecting reconstruction, formatting, lists, tables,
|
|
477
|
+
fields, tabs/breaks, comments, notes, headers/footers, or revision metadata;
|
|
478
|
+
- any case that needs normalized rather than exact text comparison; and
|
|
479
|
+
- any automated Word failure whose cause is not immediately textual.
|
|
480
|
+
|
|
481
|
+
Before a release, review all new/changed cases plus a rotating sample of at
|
|
482
|
+
least 20% of the unchanged synthetic catalogue. The sample must include legal
|
|
483
|
+
and administrative content and at least one list, table, formatted-run, and
|
|
484
|
+
structural-anchor case. Also review at least one legal and one administrative
|
|
485
|
+
SuperDoc result. Rotate the sample so every retained synthetic case receives a
|
|
486
|
+
human review over five release cycles. A major release or a change to package
|
|
487
|
+
assembly requires a full visual sweep of affected structure families.
|
|
488
|
+
|
|
489
|
+
The reviewer inspects three states in Word: tracked changes with **All Markup**,
|
|
490
|
+
the result after **Accept All**, and a fresh copy after **Reject All**. Record
|
|
491
|
+
the reviewer, date, Word version/build, cases selected, pass/fail result, and
|
|
492
|
+
notes. A visual failure becomes a regression case or a documented harness gap;
|
|
493
|
+
do not waive it merely because the COM text differential passed.
|
|
494
|
+
|
|
495
|
+
### AI-assisted Word visual preflight
|
|
496
|
+
|
|
497
|
+
Generate repeatable three-view PDF evidence for every layout-sensitive
|
|
498
|
+
synthetic fixture with:
|
|
499
|
+
|
|
500
|
+
```powershell
|
|
501
|
+
npm run test:word:visual
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
The command writes PDFs and `manifest.json` under ignored
|
|
505
|
+
`tmp/word-visual-review/rendered/` storage. It verifies that Word opened each
|
|
506
|
+
fixture and produced a non-empty, paginated rendering, but leaves the manifest
|
|
507
|
+
certification and every visual judgment pending. To render only named cases,
|
|
508
|
+
invoke `scripts/word-com-visual-suite.ps1 -Case case-one,case-two` directly.
|
|
509
|
+
|
|
510
|
+
An AI agent with Windows computer control may also open the generated fixtures
|
|
511
|
+
in the installed desktop Word application, switch among All Markup, Accept All,
|
|
512
|
+
and Reject All views, capture screenshots, and inspect them for visible
|
|
513
|
+
regressions. This is a useful intermediate oracle because it exercises the same
|
|
514
|
+
real UI a person sees rather than only `Document.Content.Text`.
|
|
515
|
+
|
|
516
|
+
Run an AI visual preflight:
|
|
517
|
+
|
|
518
|
+
- after a phase that changes reconstruction, formatting, list/table behavior,
|
|
519
|
+
package assembly, or revision display;
|
|
520
|
+
- after every five to ten new Word fixtures;
|
|
521
|
+
- for all newly introduced structure families;
|
|
522
|
+
- when automated Word passes but the XML change is unusually broad; and
|
|
523
|
+
- before asking a human to perform the release sample, so obvious failures are
|
|
524
|
+
found first.
|
|
525
|
+
|
|
526
|
+
For the focused real-document additions, run:
|
|
527
|
+
|
|
528
|
+
```powershell
|
|
529
|
+
npm run test:corpus:word:visual
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
This first runs the exact Word differential, then writes 120 PDFs and a pending
|
|
533
|
+
manifest under ignored `tmp/superdoc-word-visual-review/rendered/` storage: three
|
|
534
|
+
views for 22 list-focused cases, 16 table-focused cases, and two page-header
|
|
535
|
+
cases. The long-document set includes 6,000+ word council minutes,
|
|
536
|
+
an 8,000+ word zoning resolution, and a 59,000+ word prospectus with 180 tables.
|
|
537
|
+
Each invocation packages its fixtures in a process-specific directory so
|
|
538
|
+
a Word process left behind by an RPC disconnect cannot lock the next run's
|
|
539
|
+
inputs. The runner ignores `RPC_E_DISCONNECTED` during post-export cleanup and
|
|
540
|
+
restarts Word once when a disconnect interrupts an actual render. After Word
|
|
541
|
+
finishes, the command rebuilds `docs/test-comparison-dashboard.html` from that
|
|
542
|
+
exact process-specific fixture directory. The `Verified cached ...` messages
|
|
543
|
+
refer only to the pinned source-document downloads; edited comparison DOCX
|
|
544
|
+
files are regenerated on every run.
|
|
545
|
+
|
|
546
|
+
The AI should use the same selection rules and checklist as a human, inspect at
|
|
547
|
+
least the changed cases plus representative legal and administrative samples,
|
|
548
|
+
and save screenshots under ignored `tmp/word-manual-review/<date>/` storage.
|
|
549
|
+
Its report must identify itself as **AI visual preflight**, record the Word
|
|
550
|
+
version/build when available, list the exact cases and views inspected, and
|
|
551
|
+
separate observed facts from uncertain visual judgments.
|
|
552
|
+
|
|
553
|
+
AI visual review is advisory. It can catch missing or misplaced revisions,
|
|
554
|
+
unexpected whole-paragraph markup, obvious pagination shifts, broken tables,
|
|
555
|
+
lost indentation, clipped headers, and visibly misplaced anchors. It may miss
|
|
556
|
+
subtle font metrics, accessibility issues, field behavior that requires domain
|
|
557
|
+
knowledge, or a legally meaningful formatting distinction. An AI pass does not
|
|
558
|
+
satisfy the human release sign-off, and sensitive/private documents must not be
|
|
559
|
+
opened for AI review without explicit authorization.
|
|
560
|
+
|
|
561
|
+
### Multimodal LLM visual inspection (real-document spot checks)
|
|
562
|
+
|
|
563
|
+
Evaluating subtle visual defects—such as shifted table borders, unnatural run
|
|
564
|
+
breaks, font substitution, clipping headers, or awkward revision balloons—requires
|
|
565
|
+
interpreting rendered document pages visually rather than relying solely on
|
|
566
|
+
string assertions or raw XML checks. A multimodal Large Language Model (MLLM)
|
|
567
|
+
with vision capabilities can review rendered page images across the three
|
|
568
|
+
revision views (`allMarkup`, `acceptAll`, and `rejectAll`) and detect regressions
|
|
569
|
+
that are invisible to plain-text diffs.
|
|
570
|
+
|
|
571
|
+
#### Why multimodal inspection is kept separate
|
|
572
|
+
|
|
573
|
+
Passing high-resolution, multi-page document images through a vision-capable LLM
|
|
574
|
+
is **expensive, computationally intensive, and time-consuming**:
|
|
575
|
+
|
|
576
|
+
- Each document scenario produces three full-document rendering views (often 1
|
|
577
|
+
to 20+ pages per view).
|
|
578
|
+
- Sending dozens of high-resolution page images consumes significant token
|
|
579
|
+
budgets and model context.
|
|
580
|
+
- Latency per case is orders of magnitude higher than programmatic XML parsing
|
|
581
|
+
or COM automation.
|
|
582
|
+
|
|
583
|
+
For these reasons, multimodal visual inspection **forms a separate check from the
|
|
584
|
+
rest of the test suite**. It is never executed on every commit, local test run, or
|
|
585
|
+
full batch pipeline.
|
|
586
|
+
|
|
587
|
+
#### Sampling strategy: Random real-document spot checks
|
|
588
|
+
|
|
589
|
+
Instead of exhaustive or continuous execution, multimodal visual inspection is
|
|
590
|
+
conducted as a **random spot check of real documents here and there only**:
|
|
591
|
+
|
|
592
|
+
1. **Focus on authentic real documents:** Spot checks prioritize the
|
|
593
|
+
[SuperDoc real-document corpus](#superdoc-real-document-word-tests) rather than
|
|
594
|
+
simple synthetic fixtures. Real documents contain organic complexities—such
|
|
595
|
+
as multi-column headers, nested bullet styles, non-uniform table borders, and
|
|
596
|
+
mixed-font legal numbering—where visual layout errors are most likely to hide.
|
|
597
|
+
2. **Small random sampling:** A typical visual spot-check run selects a random
|
|
598
|
+
sample of **2 to 5 real document scenarios** (for example, one administrative
|
|
599
|
+
board agenda, one municipal minutes multi-table document, and one complex
|
|
600
|
+
zoning ordinance).
|
|
601
|
+
3. **Execution cadence:** Spot checks are run on-demand:
|
|
602
|
+
- periodically during active engine refactoring;
|
|
603
|
+
- when introducing major changes to run splitting, table reconciliation, or
|
|
604
|
+
list reconstruction; or
|
|
605
|
+
- as an advisory audit prior to a release cycle.
|
|
606
|
+
|
|
607
|
+
#### Multimodal inspection workflow
|
|
608
|
+
|
|
609
|
+
When performing a multimodal visual spot check:
|
|
610
|
+
|
|
611
|
+
1. **Render target pages to images:** Render the selected scenario in Word to
|
|
612
|
+
PDFs using `scripts/word-com-corpus-visual-suite.ps1 -Case <scenario-name>`,
|
|
613
|
+
then convert the relevant pages (or contact sheets) to PNG images using the
|
|
614
|
+
visual inspection helper:
|
|
615
|
+
```powershell
|
|
616
|
+
node scripts/inspect-visual-evidence.mjs --contact-sheets
|
|
617
|
+
```
|
|
618
|
+
2. **Submit to the multimodal model:** Provide the vision model with the side-by-side
|
|
619
|
+
page renders of `allMarkup`, `acceptAll`, and `rejectAll` along with the target
|
|
620
|
+
diff and context.
|
|
621
|
+
3. **Targeted visual checklist:** Prompt the model to evaluate specific layout risks:
|
|
622
|
+
- **Table cell integrity:** Did column widths shift? Are table borders intact?
|
|
623
|
+
Did multi-line cell text collapse or overlap?
|
|
624
|
+
- **Typography & Font metrics:** Did any run revert to default font size (e.g.
|
|
625
|
+
dropping from 14pt Georgia to 12pt Normal)? Did bold, italic, or underline
|
|
626
|
+
bleed into neighboring text?
|
|
627
|
+
- **List alignment:** Are bullet indents consistent with existing items? Are
|
|
628
|
+
there ghost markers or blank list rows?
|
|
629
|
+
- **Page apparatus:** Are headers, footers, and page numbers in their expected
|
|
630
|
+
margins without clipping or wrapping anomalies?
|
|
631
|
+
4. **Triaging findings:** If the multimodal model flags a visual abnormality,
|
|
632
|
+
treat it as an advisory signal:
|
|
633
|
+
- Verify the defect manually in Microsoft Word.
|
|
634
|
+
- Trace the visual defect to its underlying OOXML cause (per the
|
|
635
|
+
[visual failure diagnosis guidelines](#diagnose-visual-failures-in-the-generated-ooxml-first)).
|
|
636
|
+
- Add a minimal semantic regression test in
|
|
637
|
+
`tests/visual_failure_regression_tests.mjs` to ensure the issue is permanently
|
|
638
|
+
prevented in the automated suite (`npm test`).
|
|
639
|
+
|
|
640
|
+
## SuperDoc real-document Word tests
|
|
641
|
+
|
|
642
|
+
The corpus lane references selected documents from SuperDoc's
|
|
643
|
+
[docx-corpus](https://docxcorp.us/) under ODC-By 1.0. Source documents are never
|
|
644
|
+
committed. `tests/corpus/superdoc-english-legal-administrative.json` pins each
|
|
645
|
+
reviewed source and observed SHA-256; `tests/corpus/superdoc-word-scenarios.json`
|
|
646
|
+
defines named deterministic scenarios and records the structural coverage they
|
|
647
|
+
add. A source may support multiple scenarios when each one adds a distinct
|
|
648
|
+
behavioral claim.
|
|
649
|
+
|
|
650
|
+
To add a corpus case:
|
|
651
|
+
|
|
652
|
+
1. Select an English legal or administrative document that adds a structure or
|
|
653
|
+
task not already represented. Review its content and record why it is
|
|
654
|
+
suitable.
|
|
655
|
+
2. Add an explicit pinned reference and observed hash to the manifest. Do not
|
|
656
|
+
use a floating or bulk corpus download.
|
|
657
|
+
3. Fetch only that reference with
|
|
658
|
+
`npm run corpus:fetch:superdoc -- --id <pinned-id>`.
|
|
659
|
+
4. Inspect the document in Word and choose a target that occurs exactly once in
|
|
660
|
+
Word's source text.
|
|
661
|
+
5. Add the deterministic operation, shape, coverage labels, and review note to
|
|
662
|
+
`superdoc-word-scenarios.json`. Use `key` plus `sourceId` when adding another
|
|
663
|
+
scenario for an already pinned source. Use `operations` for a multi-change
|
|
664
|
+
atomic batch, or `part: "word/header<N>.xml"` for a page-header revision.
|
|
665
|
+
6. Run `npm test` for manifest/catalogue checks and `npm run test:corpus:word`
|
|
666
|
+
for package hashing plus the Word Accept All/Reject All differential.
|
|
667
|
+
|
|
668
|
+
The corpus packager starts from the original `.docx`, replaces only the declared
|
|
669
|
+
revision part (`word/document.xml` by default, or a named header part), and
|
|
670
|
+
verifies every untouched ZIP part byte-for-byte before Word opens the result.
|
|
671
|
+
The Word oracle derives multi-change expectations by applying each independently
|
|
672
|
+
declared replacement to Word's source text and reads header-story text separately
|
|
673
|
+
for header cases.
|
|
674
|
+
|
|
675
|
+
## Choosing where a new test belongs
|
|
676
|
+
|
|
677
|
+
- A fixed bug always gets an automated regression.
|
|
678
|
+
- A broad engine rule also gets deterministic fuzz coverage.
|
|
679
|
+
- A claim about what Word renders or accepts gets a synthetic Word case.
|
|
680
|
+
- A claim involving package complexity or realistic authoring structures gets
|
|
681
|
+
a reviewed corpus case.
|
|
682
|
+
- A structural XML rule should be checked by runtime validation and, where
|
|
683
|
+
practical, the ECMA-376 XSD lane.
|
|
684
|
+
|
|
685
|
+
Keep expectations independent of the code under test. In particular, never use
|
|
686
|
+
this library's Accept/Reject helpers to calculate the expected result for the
|
|
687
|
+
Word differential.
|