test(cv/v2): pixel-diff visual parity gate for 3 layered presets#52
Merged
Conversation
6e4de08 to
e71eff4
Compare
DemchaAV
added a commit
that referenced
this pull request
May 24, 2026
… rows
Reader feedback: Education / Experience / Projects sections rendered
as a "wall of text" — consecutive university entries, jobs, and
projects shared the same line spacing as their internal lines
(title → subtitle → body), so the boundary between one entry and
the next was visually invisible.
Fix
---
- theme/CvSpacing — new field `entrySeparation` (double, points)
controlling the vertical spacer between consecutive multi-line
entries.
- classic preset: 6.0pt
- modernProfessional preset: 8.0pt (denser overall layout, bigger
separator stands out more)
- @deprecated 13-arg constructor preserved for backward-compat —
fills the new field with 6.0pt default so any pre-existing custom
CvSpacing instance keeps compiling AND benefits from the
improvement automatically.
- components/SectionDispatcher inserts `host.spacer(0, entrySeparation)`
between items for:
- EntriesSection — always (Education, Experience are multi-line)
- RowsSection with BULLETED_STACKED style — between project items
(each project is a two-line block — bold name + indented body)
Single-line styles (PLAIN, BULLETED) are unaffected — they
already breathe via the per-row paragraphMarginTop.
Visual diff (verified by re-rendered cv-modern-professional-v2.pdf):
before: after:
MSc Computer Science MSc Computer Science
University of Manchester University of Manchester
Distinction. Thesis: ... Distinction. Thesis: ...
BSc Software Engineering ← gap
Imperial College London BSc Software Engineering
First-class honours... Imperial College London
First-class honours...
Baselines
---------
Refreshed all 6 visual-baseline PNGs via the
`-Dgraphcompose.visual.approve=true` workflow established in #52 —
this commit is the first deliberate consumer of that workflow,
demonstrating it works as designed.
Tests
-----
- 3/3 CvV2VisualParityTest pass against refreshed baselines
- All existing v2 tests still green (CvNameTest, CvContactTest,
CvDecorationTest, CvDocumentSlotTest, three preset smoke tests,
WidgetSmokeTest)
- No engine / v1 surface edits
4 tasks
DemchaAV
added a commit
that referenced
this pull request
May 24, 2026
… rows
Reader feedback: Education / Experience / Projects sections rendered
as a "wall of text" — consecutive university entries, jobs, and
projects shared the same line spacing as their internal lines
(title → subtitle → body), so the boundary between one entry and
the next was visually invisible.
Fix
---
- theme/CvSpacing — new field `entrySeparation` (double, points)
controlling the vertical spacer between consecutive multi-line
entries.
- classic preset: 6.0pt
- modernProfessional preset: 8.0pt (denser overall layout, bigger
separator stands out more)
- @deprecated 13-arg constructor preserved for backward-compat —
fills the new field with 6.0pt default so any pre-existing custom
CvSpacing instance keeps compiling AND benefits from the
improvement automatically.
- components/SectionDispatcher inserts `host.spacer(0, entrySeparation)`
between items for:
- EntriesSection — always (Education, Experience are multi-line)
- RowsSection with BULLETED_STACKED style — between project items
(each project is a two-line block — bold name + indented body)
Single-line styles (PLAIN, BULLETED) are unaffected — they
already breathe via the per-row paragraphMarginTop.
Visual diff (verified by re-rendered cv-modern-professional-v2.pdf):
before: after:
MSc Computer Science MSc Computer Science
University of Manchester University of Manchester
Distinction. Thesis: ... Distinction. Thesis: ...
BSc Software Engineering ← gap
Imperial College London BSc Software Engineering
First-class honours... Imperial College London
First-class honours...
Baselines
---------
Refreshed all 6 visual-baseline PNGs via the
`-Dgraphcompose.visual.approve=true` workflow established in #52 —
this commit is the first deliberate consumer of that workflow,
demonstrating it works as designed.
Tests
-----
- 3/3 CvV2VisualParityTest pass against refreshed baselines
- All existing v2 tests still green (CvNameTest, CvContactTest,
CvDecorationTest, CvDocumentSlotTest, three preset smoke tests,
WidgetSmokeTest)
- No engine / v1 surface edits
DemchaAV
added a commit
that referenced
this pull request
May 24, 2026
… rows
Reader feedback: Education / Experience / Projects sections rendered
as a "wall of text" — consecutive university entries, jobs, and
projects shared the same line spacing as their internal lines
(title → subtitle → body), so the boundary between one entry and
the next was visually invisible.
Fix
---
- theme/CvSpacing — new field `entrySeparation` (double, points)
controlling the vertical spacer between consecutive multi-line
entries.
- classic preset: 6.0pt
- modernProfessional preset: 8.0pt (denser overall layout, bigger
separator stands out more)
- @deprecated 13-arg constructor preserved for backward-compat —
fills the new field with 6.0pt default so any pre-existing custom
CvSpacing instance keeps compiling AND benefits from the
improvement automatically.
- components/SectionDispatcher inserts `host.spacer(0, entrySeparation)`
between items for:
- EntriesSection — always (Education, Experience are multi-line)
- RowsSection with BULLETED_STACKED style — between project items
(each project is a two-line block — bold name + indented body)
Single-line styles (PLAIN, BULLETED) are unaffected — they
already breathe via the per-row paragraphMarginTop.
Visual diff (verified by re-rendered cv-modern-professional-v2.pdf):
before: after:
MSc Computer Science MSc Computer Science
University of Manchester University of Manchester
Distinction. Thesis: ... Distinction. Thesis: ...
BSc Software Engineering ← gap
Imperial College London BSc Software Engineering
First-class honours... Imperial College London
First-class honours...
Baselines
---------
Refreshed all 6 visual-baseline PNGs via the
`-Dgraphcompose.visual.approve=true` workflow established in #52 —
this commit is the first deliberate consumer of that workflow,
demonstrating it works as designed.
Tests
-----
- 3/3 CvV2VisualParityTest pass against refreshed baselines
- All existing v2 tests still green (CvNameTest, CvContactTest,
CvDecorationTest, CvDocumentSlotTest, three preset smoke tests,
WidgetSmokeTest)
- No engine / v1 surface edits
DemchaAV
added a commit
that referenced
this pull request
May 24, 2026
… rows
Reader feedback: Education / Experience / Projects sections rendered
as a "wall of text" — consecutive university entries, jobs, and
projects shared the same line spacing as their internal lines
(title → subtitle → body), so the boundary between one entry and
the next was visually invisible.
Fix
---
- theme/CvSpacing — new field `entrySeparation` (double, points)
controlling the vertical spacer between consecutive multi-line
entries.
- classic preset: 6.0pt
- modernProfessional preset: 8.0pt (denser overall layout, bigger
separator stands out more)
- @deprecated 13-arg constructor preserved for backward-compat —
fills the new field with 6.0pt default so any pre-existing custom
CvSpacing instance keeps compiling AND benefits from the
improvement automatically.
- components/SectionDispatcher inserts `host.spacer(0, entrySeparation)`
between items for:
- EntriesSection — always (Education, Experience are multi-line)
- RowsSection with BULLETED_STACKED style — between project items
(each project is a two-line block — bold name + indented body)
Single-line styles (PLAIN, BULLETED) are unaffected — they
already breathe via the per-row paragraphMarginTop.
Visual diff (verified by re-rendered cv-modern-professional-v2.pdf):
before: after:
MSc Computer Science MSc Computer Science
University of Manchester University of Manchester
Distinction. Thesis: ... Distinction. Thesis: ...
BSc Software Engineering ← gap
Imperial College London BSc Software Engineering
First-class honours... Imperial College London
First-class honours...
Baselines
---------
Refreshed all 6 visual-baseline PNGs via the
`-Dgraphcompose.visual.approve=true` workflow established in #52 —
this commit is the first deliberate consumer of that workflow,
demonstrating it works as designed.
Tests
-----
- 3/3 CvV2VisualParityTest pass against refreshed baselines
- All existing v2 tests still green (CvNameTest, CvContactTest,
CvDecorationTest, CvDocumentSlotTest, three preset smoke tests,
WidgetSmokeTest)
- No engine / v1 surface edits
DemchaAV
added a commit
that referenced
this pull request
May 24, 2026
… rows
Reader feedback: Education / Experience / Projects sections rendered
as a "wall of text" — consecutive university entries, jobs, and
projects shared the same line spacing as their internal lines
(title → subtitle → body), so the boundary between one entry and
the next was visually invisible.
Fix
---
- theme/CvSpacing — new field `entrySeparation` (double, points)
controlling the vertical spacer between consecutive multi-line
entries.
- classic preset: 6.0pt
- modernProfessional preset: 8.0pt (denser overall layout, bigger
separator stands out more)
- @deprecated 13-arg constructor preserved for backward-compat —
fills the new field with 6.0pt default so any pre-existing custom
CvSpacing instance keeps compiling AND benefits from the
improvement automatically.
- components/SectionDispatcher inserts `host.spacer(0, entrySeparation)`
between items for:
- EntriesSection — always (Education, Experience are multi-line)
- RowsSection with BULLETED_STACKED style — between project items
(each project is a two-line block — bold name + indented body)
Single-line styles (PLAIN, BULLETED) are unaffected — they
already breathe via the per-row paragraphMarginTop.
Visual diff (verified by re-rendered cv-modern-professional-v2.pdf):
before: after:
MSc Computer Science MSc Computer Science
University of Manchester University of Manchester
Distinction. Thesis: ... Distinction. Thesis: ...
BSc Software Engineering ← gap
Imperial College London BSc Software Engineering
First-class honours... Imperial College London
First-class honours...
Baselines
---------
Refreshed all 6 visual-baseline PNGs via the
`-Dgraphcompose.visual.approve=true` workflow established in #52 —
this commit is the first deliberate consumer of that workflow,
demonstrating it works as designed.
Tests
-----
- 3/3 CvV2VisualParityTest pass against refreshed baselines
- All existing v2 tests still green (CvNameTest, CvContactTest,
CvDecorationTest, CvDocumentSlotTest, three preset smoke tests,
WidgetSmokeTest)
- No engine / v1 surface edits
Phase 4 — defensive infrastructure. After 5 PRs (#45-#51, ~4000 lines) of layered architecture work, there was no automated guard against silent visual regression. Refactoring widgets / theme tokens / renderers could change the rendered PDF and nothing would fail the build. This PR closes that gap. What's new ---------- - src/test/java/.../cv/v2/presets/CvV2VisualParityTest.java Parameterised over the 3 shipped v2 presets (BoxedSections, MinimalUnderlined, ModernProfessional). Each renders a canonical CvDocument fixture (Jordan Rivera — exercises every section subtype: ParagraphSection, RowsSection with all 3 RowStyle variants, EntriesSection), rasterises page-by-page via PDFBox, and asserts per-pixel diff against a checked-in baseline PNG. - src/test/resources/visual-baselines/cv-v2-layered/ 6 baseline PNGs — 3 presets × 2 pages each. Captured via `-Dgraphcompose.visual.approve=true` against the current develop-state rendering. - Reused PdfVisualRegression harness (already in the codebase — used by v1's PresetVisualParityTest). Same budget calibration: 20 000 mismatched pixels per page at per-channel tolerance 8 — calibrated for cross-platform PDFBox font/colour drift. - Inline canonical document (not pulled from examples module) so the test depends only on main + main-test code. Mirrors the pattern from v1 PresetVisualParityTest.canonicalCvSpec(). Workflow -------- After a deliberate visual change (theme rebalance, widget tweak, etc.): ./mvnw test -Dtest=CvV2VisualParityTest -Dgraphcompose.visual.approve=true git add src/test/resources/visual-baselines/cv-v2-layered/*.png git commit -m "test: refresh visual baselines after <reason>" Normal CI run (default) just diffs. Failures write <slug>-page-N.actual.png and <slug>-page-N.diff.png next to the baseline for review. Docs ---- - docs/templates/v2-layered/contributor-guide.md gains a "Visual regression — pixel-diff parity gate" section under "Test checklist": explains the workflow, where baselines live, the budget calibration rationale, and points contributors at this test file as a drop-in template for new template families. Test results ------------ - 3/3 visual parity tests pass against fresh baselines - 8/8 CanonicalSurfaceGuardTest still green (no docs surface drift from this change) - No engine, source, or v1 surface edits
e71eff4 to
1eb2500
Compare
DemchaAV
added a commit
that referenced
this pull request
May 24, 2026
… rows
Reader feedback: Education / Experience / Projects sections rendered
as a "wall of text" — consecutive university entries, jobs, and
projects shared the same line spacing as their internal lines
(title → subtitle → body), so the boundary between one entry and
the next was visually invisible.
Fix
---
- theme/CvSpacing — new field `entrySeparation` (double, points)
controlling the vertical spacer between consecutive multi-line
entries.
- classic preset: 6.0pt
- modernProfessional preset: 8.0pt (denser overall layout, bigger
separator stands out more)
- @deprecated 13-arg constructor preserved for backward-compat —
fills the new field with 6.0pt default so any pre-existing custom
CvSpacing instance keeps compiling AND benefits from the
improvement automatically.
- components/SectionDispatcher inserts `host.spacer(0, entrySeparation)`
between items for:
- EntriesSection — always (Education, Experience are multi-line)
- RowsSection with BULLETED_STACKED style — between project items
(each project is a two-line block — bold name + indented body)
Single-line styles (PLAIN, BULLETED) are unaffected — they
already breathe via the per-row paragraphMarginTop.
Visual diff (verified by re-rendered cv-modern-professional-v2.pdf):
before: after:
MSc Computer Science MSc Computer Science
University of Manchester University of Manchester
Distinction. Thesis: ... Distinction. Thesis: ...
BSc Software Engineering ← gap
Imperial College London BSc Software Engineering
First-class honours... Imperial College London
First-class honours...
Baselines
---------
Refreshed all 6 visual-baseline PNGs via the
`-Dgraphcompose.visual.approve=true` workflow established in #52 —
this commit is the first deliberate consumer of that workflow,
demonstrating it works as designed.
Tests
-----
- 3/3 CvV2VisualParityTest pass against refreshed baselines
- All existing v2 tests still green (CvNameTest, CvContactTest,
CvDecorationTest, CvDocumentSlotTest, three preset smoke tests,
WidgetSmokeTest)
- No engine / v1 surface edits
DemchaAV
added a commit
that referenced
this pull request
May 24, 2026
… rows (#53) Reader feedback: Education / Experience / Projects sections rendered as a "wall of text" — consecutive university entries, jobs, and projects shared the same line spacing as their internal lines (title → subtitle → body), so the boundary between one entry and the next was visually invisible. Fix --- - theme/CvSpacing — new field `entrySeparation` (double, points) controlling the vertical spacer between consecutive multi-line entries. - classic preset: 6.0pt - modernProfessional preset: 8.0pt (denser overall layout, bigger separator stands out more) - @deprecated 13-arg constructor preserved for backward-compat — fills the new field with 6.0pt default so any pre-existing custom CvSpacing instance keeps compiling AND benefits from the improvement automatically. - components/SectionDispatcher inserts `host.spacer(0, entrySeparation)` between items for: - EntriesSection — always (Education, Experience are multi-line) - RowsSection with BULLETED_STACKED style — between project items (each project is a two-line block — bold name + indented body) Single-line styles (PLAIN, BULLETED) are unaffected — they already breathe via the per-row paragraphMarginTop. Visual diff (verified by re-rendered cv-modern-professional-v2.pdf): before: after: MSc Computer Science MSc Computer Science University of Manchester University of Manchester Distinction. Thesis: ... Distinction. Thesis: ... BSc Software Engineering ← gap Imperial College London BSc Software Engineering First-class honours... Imperial College London First-class honours... Baselines --------- Refreshed all 6 visual-baseline PNGs via the `-Dgraphcompose.visual.approve=true` workflow established in #52 — this commit is the first deliberate consumer of that workflow, demonstrating it works as designed. Tests ----- - 3/3 CvV2VisualParityTest pass against refreshed baselines - All existing v2 tests still green (CvNameTest, CvContactTest, CvDecorationTest, CvDocumentSlotTest, three preset smoke tests, WidgetSmokeTest) - No engine / v1 surface edits
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 4 — defensive infrastructure. After 5 PRs (#45-#51, ~4000
lines) of layered architecture work, there was no automated guard
against silent visual regression. Refactoring widgets / theme
tokens / renderers could change the rendered PDF and nothing would
fail the build. This PR closes that gap.
What's new
CvV2VisualParityTest.java— parameterised over the 3 shippedv2 presets:
boxed_sectionsBoxedSectionsminimal_underlinedMinimalUnderlinedmodern_professionalModernProfessionalEach renders a canonical
CvDocumentfixture (Jordan Rivera —exercises every section subtype:
ParagraphSection,RowsSectionwith all 3
RowStylevariants,EntriesSection), rasterisespage-by-page via PDFBox, and asserts per-pixel diff against a
checked-in baseline PNG.
src/test/resources/visual-baselines/cv-v2-layered/— 6baseline PNGs (3 presets × 2 pages each). Captured via
-Dgraphcompose.visual.approve=trueagainst the currentdevelop-state rendering.
Reuses
PdfVisualRegression— the same harness used by v1'sPresetVisualParityTest. Same budget calibration: 20 000mismatched pixels per page at per-channel tolerance 8 —
calibrated for cross-platform PDFBox font/colour drift between
Windows-recorded baselines and Linux CI.
Workflow
After a deliberate visual change (theme rebalance, widget tweak,
new component):
Normal CI run (default) just diffs. Failures write
<slug>-page-N.actual.pngand<slug>-page-N.diff.pngnext to thebaseline so a reviewer can see exactly what changed before deciding
to re-bless or fix.
Docs
docs/templates/v2-layered/contributor-guide.mdgains a "Visualregression — pixel-diff parity gate" section under "Test
checklist":
for new template families
required (was previously "encouraged but optional").
What's NOT changed
Test plan
CvV2VisualParityTest— 3/3 tests pass against fresh baselinesCanonicalSurfaceGuardTest— 8/8 still green (no docssurface drift)
module)
Decision points for reviewer
PresetVisualParityTest. If you find a preset flakesconsistently in CI, widen for that preset specifically rather
than relaxing the global setting.
canonicalDocument()vs pull fromExampleDataFactory:chose inline so the test depends only on main + main-test code
(no circular dep with examples module). Tradeoff: sample content
duplicated. If
ExampleDataFactoryever moves into main, we canconsolidate.
Next phase (if you want)
TimelineMinimal(multi-column with slots —first real consumer of
Slot.SIDEBAR)invoice-v2migration following the contributorguide