From 0b924047c15f9b757cebc4037fda298de8321f4b Mon Sep 17 00:00:00 2001 From: DemchaAV Date: Sun, 30 Aug 2026 23:33:23 +0100 Subject: [PATCH] test(examples): guard the sidebar CV samples against a wrapped contact row --- CHANGELOG.md | 14 ++++ .../cv/SidebarContactRowsFitTest.java | 84 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 examples/src/test/java/com/demcha/examples/templates/cv/SidebarContactRowsFitTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 757b26c36..1f2d0d97d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -148,6 +148,20 @@ follow semantic versioning; release dates are ISO 8601. subtitle, looked complete. The employer is now drawn between the position and the date, in the shared theme entry-subtitle style. +### Tests + +- **The sidebar CV samples are held to the width of the column they are drawn in.** + A contact channel in `ProfessionalSidebar` and `NavySidebar` is one paragraph — the + mark and the value share a line — so a value wider than the sidebar's text column + wraps and leaves the mark alone on the first line. The promotion gates could not see + it: they measure the published template's own fixture, whose addresses fit, while the + example sample data is written for the repository and can outgrow the column with + every test still green. It shipped that way once, spotted in the rendered preview + rather than by a build. `SidebarContactRowsFitTest` now measures the shape of the row + instead of the length of the string — a single-line channel is as tall as its mark, + a wrapped one close to twice that — and the two sample addresses were shortened to + fit. + ### Documentation - **The presets that cap content say so.** `MonogramSidebar`, `SidebarPortrait` and diff --git a/examples/src/test/java/com/demcha/examples/templates/cv/SidebarContactRowsFitTest.java b/examples/src/test/java/com/demcha/examples/templates/cv/SidebarContactRowsFitTest.java new file mode 100644 index 000000000..cb70a57c0 --- /dev/null +++ b/examples/src/test/java/com/demcha/examples/templates/cv/SidebarContactRowsFitTest.java @@ -0,0 +1,84 @@ +package com.demcha.examples.templates.cv; + +import com.demcha.compose.GraphCompose; +import com.demcha.compose.document.api.DocumentSession; +import com.demcha.compose.document.snapshot.LayoutNodeSnapshot; +import com.demcha.compose.document.templates.api.DocumentTemplate; +import com.demcha.compose.document.templates.cv.data.CvDocument; +import com.demcha.compose.document.templates.cv.presets.NavySidebar; +import com.demcha.compose.document.templates.cv.presets.ProfessionalSidebar; +import com.demcha.examples.support.NavySidebarSampleData; +import com.demcha.examples.support.ProfessionalSidebarSampleData; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Holds the sidebar CV samples to the width of the column they are drawn in. + * + *

A contact channel in both of these presets is one paragraph — the mark + * and the value share a line — so a value wider than the sidebar's text + * column wraps, and the mark is left alone on the first line. Nothing in the + * promotion gates catches it: they measure the published template's own + * fixture, whose addresses fit, while the sample data here is written for + * the repository and can outgrow the column without a single test going + * red. That is exactly how it shipped once, spotted in the rendered preview + * rather than by a build.

+ * + *

So the guard is on the samples, and it is the shape of the row rather + * than the length of the string: a single-line channel is as tall as its + * mark — 8.8 to 11pt across the two designs — and a wrapped one is close to + * twice that. The bound sits between.

+ */ +class SidebarContactRowsFitTest { + + /** Comfortably above the tallest single-line row, well under a wrapped one. */ + private static final double SINGLE_LINE_LIMIT = 14.0; + + @Test + void professionalSidebarChannelsEachFitOneLine() { + assertChannelsFitOneLine("Professional Sidebar", + ProfessionalSidebar::create, ProfessionalSidebarSampleData.sample()); + } + + @Test + void navySidebarChannelsEachFitOneLine() { + assertChannelsFitOneLine("Navy Sidebar", + NavySidebar::create, NavySidebarSampleData.sample()); + } + + private static void assertChannelsFitOneLine(String preset, + Supplier> factory, + CvDocument doc) { + List channels; + try (DocumentSession session = GraphCompose.document().create()) { + factory.get().compose(session, doc); + channels = session.layoutSnapshot().nodes().stream() + .filter(node -> node.entityName() != null + && node.entityName().startsWith("Contact_")) + .toList(); + } + + assertThat(channels) + .describedAs("%s draws its contact channels as Contact_* nodes; finding none " + + "means the guard is looking at the wrong name, not that the sample " + + "is clean", preset) + .isNotEmpty(); + + List wrapped = channels.stream() + .filter(node -> node.placementHeight() > SINGLE_LINE_LIMIT) + .map(node -> node.entityName() + " (" + node.placementHeight() + "pt)") + .collect(Collectors.toList()); + + assertThat(wrapped) + .describedAs("%s: a contact value in the sample is wider than the sidebar " + + "column, so its row wrapped and left the mark alone on the first " + + "line. Shorten the value in the sample data — the column is not the " + + "thing to widen", preset) + .isEmpty(); + } +}