fix(tests): agent-detail mobile padding — jsdom cannot compute an unparsed shorthand (#2910)

## The last failure in `app:backfill 1/4`

```
AgentDetailView mobile scroll regression (FN-4231)
  > adds mobile row gaps to the overview hero for long health and skills metadata (FN-7958)
AssertionError: expected '0' to be 'var(--space-md)'
```

**Not a style regression — the CSS is unchanged.**

jsdom does not substitute `var()`, and what it does *instead* changed at
the **27 → 29** bump (`4819c2634`): a directly-declared **longhand**
still echoes its raw text, while a **shorthand** fails to parse and
computes to the initial value. Same cause as the TaskCard failures fixed
in #2782.

**The asymmetry is visible three lines above the failure** — `rowGap`
and `columnGap` assert the same kind of token and still pass, because
they are declared as longhands. Only `padding` broke, which is why this
read as a one-property regression rather than a jsdom behaviour change.

## `paddingTop` does not rescue it

That was my first attempt, and it still returns `'0'` — measured, not
assumed. jsdom cannot derive a longhand from a shorthand it failed to
parse, so **computed style cannot answer this at all**.

## The fix

Assert the **declared rule**, which is the pattern this file already
uses for its desktop counterpart:

```ts
expect(loadAllAppCssBaseOnly()).toContain("padding: var(--space-md) calc(...);");
```

One difference that matters: the **full** sheet is needed rather than
the base-only one. This padding is a mobile override inside `@media
(max-width: 480px)` (`AgentDetailView.css:2129-2131`), and
`loadAllAppCssBaseOnly` strips at-rules by design — so the obvious copy
of the neighbouring assertion would have silently matched nothing.

## Evidence

| | result |
|---|---|
| the file | **7/7** (was 1 failed) |
| mutation: mobile card padding `md → xl` | **1 failed** |

The mutation is the important one here: a regex that merely found the
`@media` block would pass regardless. It tracks the actual declaration.

`pnpm lint` clean. Test-only; `AgentDetailView.css` restored clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-07-30 18:09:20 -07:00
committed by GitHub
parent 2811a4a2df
commit 85ca9fe461

View File

@@ -215,7 +215,30 @@ describe("AgentDetailView mobile scroll regression (FN-4231)", () => {
expect(metaStyle.flexWrap).toBe("wrap");
expect(metaStyle.rowGap).toBe("var(--space-lg)");
expect(metaStyle.columnGap).toBe("var(--space-sm)");
expect(cardStyle.padding).toBe("var(--space-md)");
/*
FNXC:AgentDetailMobile 2026-07-31-19:15:
DECLARED, NOT COMPUTED — jsdom cannot answer this one at all.
This read `getComputedStyle(card).padding` and expected the raw token text. jsdom does not
substitute `var()`, and what it does INSTEAD changed at the 27 -> 29 bump (4819c2634): a
directly-declared LONGHAND still echoes its raw text, but a SHORTHAND fails to parse and
computes to the initial value. Hence `expected '0' to be 'var(--space-md)'` with the CSS
untouched. Same cause as the TaskCard failures fixed in #2782.
The asymmetry is visible three lines up: `rowGap` and `columnGap` are declared as longhands and
still pass against the same kind of token. `paddingTop` does NOT rescue it — measured — because
jsdom cannot derive a longhand from a shorthand it failed to parse.
So the declared rule is asserted instead, which is what this file already does for the desktop
counterpart above (`expect(loadAllAppCssBaseOnly()).toContain("padding: ...")`). The full sheet
is needed rather than the base-only one: this padding is a MOBILE override inside
`@media (max-width: 480px)` (AgentDetailView.css:2129-2131), and `loadAllAppCssBaseOnly` strips
at-rules by design.
*/
expect(loadAllAppCss()).toContain("@media (max-width: 480px)");
expect(loadAllAppCss()).toMatch(
/@media \(max-width: 480px\)[\s\S]*?\.dashboard-summary-card\s*\{[^}]*padding:\s*var\(--space-md\);/,
);
expect(healthStyle.overflowWrap).toBe("anywhere");
expect(screen.getAllByText(/Paused: heartbeat-model-unavailable/).length).toBeGreaterThanOrEqual(1);
if (metadata.skills.length > 0) {