test(web): assert the timestamp occlusion the sidebar gutter fixes

The e2e measured the reserved band but never the symptom the change is
named for. Headless chromium defaults to an overlay scrollbar, which is
the configuration where a bar can cover row content at all, so the
scenario already ran in the right mode: against clean master the band is
0 and the bar covers 7px of the relative time.

Adds timeCoveredBy, the overlap between the relative time's right edge
and the range the bar occupies, taking the bar's width from the sheet
where it applies and from the UA's overlay width otherwise. Assuming 0
there would report no occlusion in precisely the state that has it.

Keeps the band assertion rather than replacing it: the two catch
different regressions. Removing only scrollbar-gutter leaves
timeCoveredBy at 0, because the bar is then 8px and the row's right
padding is also 8px, so it abuts the timestamp without covering it.
Removing the pseudo-element width as well is what produces the overlap.
Each was mutation-checked with the other assertions in its test silenced.

Records in the note that the gutter and the ::-webkit-scrollbar width are
jointly necessary against an overlay bar, measured by deleting each from
the live cascade with the other in force: either alone drops the band
from 8 to 0.
This commit is contained in:
Chinesezjc
2026-07-28 17:22:09 +08:00
parent 75ae2cb4eb
commit 17419aa6b9
4 changed files with 73 additions and 15 deletions
@@ -26,6 +26,8 @@ The track and the corner stay transparent, so the thumb reads against whatever s
`.list` declares `scrollbar-gutter: stable`, which keeps the bar beside the rows instead of on top of them. `stable` rather than `auto` because `auto` reserves the gutter only while the list actually overflows: expanding a workspace group would then shift every row horizontally at the moment it starts scrolling. The reservation is unconditional and the rows never move.
The gutter and the sheet's `::-webkit-scrollbar` width are jointly necessary against an overlay scrollbar, which is the configuration where the symptom exists at all. Measured on the running app by deleting each from the live cascade with the other left in force: either deletion alone takes the list's band from 8 to 0. The gutter states that space be reserved, and the pseudo-element width is what makes chromium treat the bar as occupying layout space rather than floating over the content. Neither half of this change is therefore optional for the reported bug, which is a second reason the two halves ship together.
## Alternatives considered
**Per-module `::-webkit-scrollbar` rules in each scrolling component sheet.** Rejected: the client has thirteen scrolling containers across nine packages, every one would carry the same block, and the fourteenth would ship unthemed with nothing failing. A skin driven by design tokens belongs in the package that owns the tokens.
@@ -65,6 +67,8 @@ Two chromium measurement limits shape what the e2e can assert. The gate makes ch
The gate itself has a negative control at the level it operates on: removing the `@supports` wrapper from the sheet, rebuilding `build:web`, and rerunning the e2e turns the `scrollbar-width: auto` assertion red with `thin`, which is the suppression the gate exists to prevent.
Headless chromium draws overlay scrollbars, so a reserved gutter there does not shrink `clientWidth`. The reservation shows up as a non-zero `offsetWidth - clientWidth` band on the list; client-area geometry alone does not demonstrate it, and an assertion comparing the time element's right edge against the client-area edge holds with and without the reservation, so it would pass or fail on the platform's scrollbar style rather than on the declaration under test.
Headless chromium draws overlay scrollbars, and that is the configuration in which the reported symptom exists, so the e2e reproduces the bug rather than approximating it: against clean master the list's band is 0 and the bar covers 7px of the relative time. A reserved gutter there does not shrink `clientWidth`, so an assertion comparing the time element's right edge against the client-area edge holds with and without the reservation and would pass or fail on the platform's scrollbar style rather than on the declaration under test. The two signals that do separate the states are the `offsetWidth - clientWidth` band and `timeCoveredBy`, the overlap measured against the bar's own width.
Both are asserted because each catches a different regression, established by mutating one declaration at a time with the other assertions in that test silenced. Removing only the gutter leaves `timeCoveredBy` at 0 — the bar is then 8px and the row's right padding is also 8px, so it abuts the timestamp without covering it — and the band assertion is what fails. Removing the pseudo-element width as well, which is the actual master state, produces the overlap, and `timeCoveredBy` fails at 7. A headed run under xvfb cannot show the symptom in either state, because chromium paints a classic space-consuming bar there and `clientWidth` already excludes it.
Verifying browser-visible plugin CSS needs a rebuild `pnpm run build:web` does not perform. `WorkspaceBrowser.module.css` never reaches `apps/web/dist`: ui-workspace loads as a runtime plugin and its CSS is inlined into `packages/client/ui-workspace/lib/client.js`, built by that package's own `bundle` script. A negative control that reruns only `build:web` therefore exercises a stale bundle and passes with the declaration removed, which reads as a vacuous test rather than as an invalid control. Rebuild with `pnpm --filter @deepseek-ai/dsh-client-ui-workspace run bundle`, confirm the artifact by grepping `lib/client.js` for the declaration, then `build:web`. No script in the web lane does this: `test:web` runs `build:web` alone, so every scroll-region or plugin-CSS change hits the same trap.