You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2026/08/30 11:25:14 UTC
(juneau) branch master updated: Fix nav-tab .active contrast, widen CI js-tests to the full browser suite, and score ChromeScaleScanner's remaining families
This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 09a5661ed0 Fix nav-tab .active contrast, widen CI js-tests to the full browser suite, and score ChromeScaleScanner's remaining families
09a5661ed0 is described below
commit 09a5661ed0cc29190f4fe92f8b31d0d399b0d29e
Author: James Bognar <ja...@apache.org>
AuthorDate: Sun Aug 30 07:25:08 2026 -0400
Fix nav-tab .active contrast, widen CI js-tests to the full browser suite, and score ChromeScaleScanner's remaining families
[J0484] nav-tab .active solid-fill contrast reusing --jc-accent-selected/--jc-on-accent (incl. j04 count-guard 2->3 reconciliation); [J0481] widen CI js-tests to the full 20-class browser suite (folds J0473); [J0476] wire CONTROL_PADDING_X + LINE_HEIGHT into ChromeScaleScanner scoring.
---
.github/workflows/maven.yml | 9 ++--
.../resources/org/apache/juneau/console/chrome.css | 4 +-
.../server/console/ConsoleChromeMixin_Test.java | 18 ++++++--
.../rest/server/views/ChromeScaleScanner.java | 48 +++++++++++++++++++---
.../views/ChromeScale_ContractScan_Test.java | 25 +++++++++++
5 files changed, 89 insertions(+), 15 deletions(-)
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index a356c42ba6..2439cb454e 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -109,16 +109,15 @@ jobs:
- name: Show Node version
run: node --version && npm --version
# -pl/-am rather than -f: -f would resolve the module's SNAPSHOT dependencies from ~/.m2, which a cold runner does
- # not have. -Dtest= narrows Surefire to the harness so the upstream modules are built but not re-tested; the
- # matrix job above is what runs their suites, including the always-on markup/CSS half of this same contract.
+ # not have. -pl -am builds the upstream modules but does not re-test them; the matrix job above is what runs
+ # their suites, including the always-on markup/CSS half of this same contract. This step runs the module's
+ # full *_BrowserTest suite.
- name: Run JS harness
run: >
mvn --no-transfer-progress --batch-mode --show-version
-Pjs-tests
-pl juneau-rest/juneau-rest-server-views -am
test
- -Dtest=PagePanelVisibility_BrowserTest
- -Dsurefire.failIfNoSpecifiedTests=false
-Djuneau.jsTests.browsers="${HOME}/.cache/ms-playwright"
# Surefire already prints each failed assertion with the full rendered-state JSON attached, which is the actual
# diagnosis (a panel reported as "jc-subpanel jc-active" with "visible":false is the blank-tab defect, stated).
@@ -141,6 +140,6 @@ jobs:
echo 'Contract being asserted: PageTable.java javadoc, "Panel markup contract".'
echo '================================================================================'
echo '--- Surefire report ---'
- cat "${VIEWS}"/target/surefire-reports/*PagePanelVisibility_BrowserTest.txt 2>/dev/null || echo '(none - the failure was before the tests ran)'
+ cat "${VIEWS}"/target/surefire-reports/*_BrowserTest.txt 2>/dev/null || echo '(none - the failure was before the tests ran)'
echo '--- Prober stderr (non-empty only if Chromium or Node itself failed) ---'
cat "${VIEWS}/target/js/prober-stderr.txt" 2>/dev/null || echo '(none)'
diff --git a/juneau-rest/juneau-rest-server-console-ui/src/main/resources/org/apache/juneau/console/chrome.css b/juneau-rest/juneau-rest-server-console-ui/src/main/resources/org/apache/juneau/console/chrome.css
index 36f552478e..6eb3e20fc5 100644
--- a/juneau-rest/juneau-rest-server-console-ui/src/main/resources/org/apache/juneau/console/chrome.css
+++ b/juneau-rest/juneau-rest-server-console-ui/src/main/resources/org/apache/juneau/console/chrome.css
@@ -324,9 +324,9 @@ a:hover { text-decoration: underline; }
}
.jc-nav-tab.active {
- background-color: var(--jc-accent-wash);
+ background-color: var(--jc-accent-selected);
border-top-color: var(--jc-accent);
- color: var(--jc-text);
+ color: var(--jc-on-accent);
}
/* ==========================================================================
diff --git a/juneau-rest/juneau-rest-server-console-ui/src/test/java/org/apache/juneau/rest/server/console/ConsoleChromeMixin_Test.java b/juneau-rest/juneau-rest-server-console-ui/src/test/java/org/apache/juneau/rest/server/console/ConsoleChromeMixin_Test.java
index e91fe11682..b48ee5a1f7 100644
--- a/juneau-rest/juneau-rest-server-console-ui/src/test/java/org/apache/juneau/rest/server/console/ConsoleChromeMixin_Test.java
+++ b/juneau-rest/juneau-rest-server-console-ui/src/test/java/org/apache/juneau/rest/server/console/ConsoleChromeMixin_Test.java
@@ -546,12 +546,24 @@ class ConsoleChromeMixin_Test extends TestBase {
assertTrue(onAccentVsFace >= 3.0, () -> "--jc-on-accent/selected-face contrast " + onAccentVsFace + ":1 is below WCAG 1.4.11's 3:1 floor");
// Substring pin: both rewritten selected-state selectors consume var(--jc-accent-selected), with no
- // var() fallback (requirement 3) - exactly two "background-color: var(--jc-accent-selected)" sinks.
+ // var() fallback (requirement 3) - exactly three "background-color: var(--jc-accent-selected)" sinks.
var css = readChromeCss();
assertTrue(css.contains(".jc-tab.jc-tab-active,\n.jc-subtab.jc-subtab-active {"), () -> "missing raised-specificity selected selector, css:\n" + css);
assertTrue(css.contains(".juneau-view-ribbon-group[data-juneau-strip-mode=\"tab\"] .juneau-view-ribbon-btn[aria-selected=\"true\"] {"), () -> "missing widget selected selector, css:\n" + css);
- assertEquals(2, countOccurrences(css, "background-color: var(--jc-accent-selected)"),
- () -> "expected exactly the two selected-face rules to consume --jc-accent-selected, css:\n" + css);
+ assertEquals(3, countOccurrences(css, "background-color: var(--jc-accent-selected)"),
+ () -> "expected exactly the three consumers of --jc-accent-selected: the two ribbon-format selected-state selectors plus .jc-nav-tab.active (added by J0484), css:\n" + css);
+ }
+
+ @Test void j05_navTabActive_consumesAccentSelectedToken_andNoLongerConsumesAccentWash() throws Exception {
+ var css = readChromeCss();
+ var navTabActiveStart = css.indexOf(".jc-nav-tab.active {");
+ assertTrue(navTabActiveStart != -1, () -> "missing .jc-nav-tab.active rule, css:\n" + css);
+ var navTabActiveEnd = css.indexOf("}", navTabActiveStart);
+ var navTabActiveBlock = css.substring(navTabActiveStart, navTabActiveEnd);
+ assertTrue(navTabActiveBlock.contains("background-color: var(--jc-accent-selected)"),
+ () -> "expected .jc-nav-tab.active to consume --jc-accent-selected, block:\n" + navTabActiveBlock);
+ assertFalse(navTabActiveBlock.contains("var(--jc-accent-wash)"),
+ () -> "expected .jc-nav-tab.active to no longer consume --jc-accent-wash, block:\n" + navTabActiveBlock);
}
/** WCAG 2.x contrast ratio between two {@code "#rrggbb"} literals: {@code (lighter+0.05)/(darker+0.05)}. */
diff --git a/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ChromeScaleScanner.java b/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ChromeScaleScanner.java
index d6fe4892a4..09afd9731a 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ChromeScaleScanner.java
+++ b/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ChromeScaleScanner.java
@@ -157,14 +157,23 @@ final class ChromeScaleScanner {
"a third small-glyph consumer; enrolling it widens a named glyph role and is not this contract's call")
);
- /** Properties a chrome surface sizes with, and the step families each may legitimately spend. */
+ /**
+ * Properties a chrome surface sizes with, and the step families each may legitimately spend.
+ *
+ * <p>
+ * {@code padding-left}/{@code padding-right} are the longhand-only reading of {@code CONTROL_PADDING_X}
+ * (the {@code padding} shorthand is not parsed to isolate its horizontal component - a shorthand
+ * declaration hiding the value is an accepted, stated limitation).
+ */
private static final Map<String,Set<Family>> CHECKED_PROPERTIES = Map.of(
"height", Set.of(Family.CONTROL_HEIGHT, Family.SPACE, Family.GLYPH),
"min-height", Set.of(Family.CONTROL_HEIGHT, Family.SPACE),
"font-size", Set.of(Family.FONT_SIZE),
"gap", Set.of(Family.SPACE),
"line-height", Set.of(Family.LINE_HEIGHT),
- "width", Set.of(Family.GLYPH)
+ "width", Set.of(Family.GLYPH),
+ "padding-left", Set.of(Family.CONTROL_PADDING_X),
+ "padding-right", Set.of(Family.CONTROL_PADDING_X)
);
/** {@code width} is only a scale property on an SVG glyph; elsewhere it is a content measure. */
@@ -179,6 +188,14 @@ final class ChromeScaleScanner {
/** Matches an absolute length/size token. */
private static final Pattern ABSOLUTE = Pattern.compile("(?<![\\w.-])(\\d*\\.?\\d+)(px|rem)(?![\\w-])");
+ /**
+ * Matches a bare, unitless decimal. {@code line-height}'s scale step ({@code --jc-chrome-line-height},
+ * {@code 1.2}) is the only step value in the ladder with no {@code px}/{@code rem} unit, so this pattern is
+ * used only for the {@code line-height} property (see {@link #findDuplicatedStep}) - it must never widen
+ * matching for any other property.
+ */
+ private static final Pattern BARE_NUMBER = Pattern.compile("(?<![\\w.-])(\\d*\\.?\\d+)(?![\\w.%-])");
+
/** The steps this contract declares. */
static List<Step> scale() {
return SCALE;
@@ -278,15 +295,18 @@ final class ChromeScaleScanner {
* are violations either way; naming the apter one is what makes the message actionable.
*/
private static Step findDuplicatedStep(Decl d, Set<Family> families, List<Step> scale) {
- var literals = absoluteValues(d.value());
+ // line-height's step value is unitless, so it needs bare-number matching instead of the general
+ // px/rem literal matcher - scoped to this one property so no other property's matching widens (LD-2).
+ var lineHeight = "line-height".equals(d.property());
+ var literals = lineHeight ? bareNumberValues(d.value()) : absoluteValues(d.value());
if (literals.isEmpty())
return null;
for (var roleNamed : List.of(true, false)) {
for (var s : scale) {
if (!s.confirmed() || !families.contains(s.family()) || (s.family() == Family.SPACE) == roleNamed)
continue;
- var stepPx = toPx(s.value());
- if (stepPx != null && literals.contains(stepPx))
+ var stepValue = lineHeight ? bareValue(s.value()) : toPx(s.value());
+ if (stepValue != null && literals.contains(stepValue))
return s;
}
}
@@ -326,6 +346,24 @@ final class ChromeScaleScanner {
return m.find() ? px(Double.parseDouble(m.group(1)), m.group(2)) : null;
}
+ /**
+ * Every bare unitless number in a declaration's value, {@code var(...)} fallbacks excluded. Only ever called
+ * for the {@code line-height} property (see {@link #findDuplicatedStep}).
+ */
+ private static Set<Double> bareNumberValues(String value) {
+ var out = new LinkedHashSet<Double>();
+ var m = BARE_NUMBER.matcher(VAR_REF.matcher(value).replaceAll(" "));
+ while (m.find())
+ out.add(Double.parseDouble(m.group(1)));
+ return out;
+ }
+
+ /** The step-value counterpart to {@link #bareNumberValues}: a step's own value read as a bare number. */
+ private static Double bareValue(String value) {
+ var m = BARE_NUMBER.matcher(value);
+ return m.find() ? Double.parseDouble(m.group(1)) : null;
+ }
+
/** Normalises to px. {@code rem} resolves at the browser default because nothing here sets a root font-size. */
private static double px(double n, String unit) {
return "rem".equals(unit) ? n * 16.0 : n;
diff --git a/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ChromeScale_ContractScan_Test.java b/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ChromeScale_ContractScan_Test.java
index 61376231f0..0553a3e91b 100644
--- a/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ChromeScale_ContractScan_Test.java
+++ b/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ChromeScale_ContractScan_Test.java
@@ -152,4 +152,29 @@ class ChromeScale_ContractScan_Test extends TestBase {
assertTrue(ChromeScaleScanner.scan(".synthetic-cell { width: 16px; }").violations().isEmpty());
assertEquals(1, ChromeScaleScanner.scan(".synthetic-btn svg { width: 16px; }").violations().size());
}
+
+ /**
+ * {@code CONTROL_PADDING_X} was structurally unscored before this fix - no property mapped onto it in
+ * {@code CHECKED_PROPERTIES}, so a duplicated value could never be caught regardless of a step's
+ * {@code confirmed} flag. Exercises both newly-wired longhand properties.
+ */
+ @Test void b10_controlPaddingXCatchesADuplicatedValue() {
+ var r = ChromeScaleScanner.scan(".synthetic-control { padding-left: 10px; padding-right: 14px; }");
+ assertEquals(2, r.violations().size(), () -> "expected both padding-left and padding-right to be caught: "
+ + r.violations());
+ assertTrue(r.violations().get(0).contains("--jc-chrome-control-padding-x (10px)"), r.violations().get(0));
+ assertTrue(r.violations().get(1).contains("--jc-chrome-control-padding-x-wide (14px)"), r.violations().get(1));
+ }
+
+ /**
+ * {@code LINE_HEIGHT}'s only step value is unitless, so the general px/rem literal matcher could never bind
+ * to it before this fix - {@code line-height} was already mapped in {@code CHECKED_PROPERTIES}, but nothing
+ * could ever match. Proves the widened, {@code line-height}-scoped value-matching now catches it.
+ */
+ @Test void b11_lineHeightCatchesADuplicatedValue() {
+ var r = ChromeScaleScanner.scan(".synthetic-control { line-height: 1.2; }");
+ assertEquals(1, r.violations().size(), () -> "expected the duplicated line-height value to be caught: "
+ + r.violations());
+ assertTrue(r.violations().get(0).contains("--jc-chrome-line-height (1.2)"), r.violations().get(0));
+ }
}