From c957d4fc9265c464cc1732e62ef07b2431becbc9 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Tue, 20 Oct 2026 18:42:02 +0000 Subject: [PATCH] test(web): pooled segments narrower than 6px drop the pace mark Also documents everything a rebuild of the Limits evidence needs in the hub fixture README. Co-Authored-By: Claude Opus 5.5 --- .../usage/UsageLimitsPooled.browser.test.tsx | 20 ++++++ .../test/fixtures/usage-limits-hub/README.md | 69 +++++++++++++++++-- 2 files changed, 83 insertions(+), 6 deletions(-) diff --git a/apps/web/src/components/usage/UsageLimitsPooled.browser.test.tsx b/apps/web/src/components/usage/UsageLimitsPooled.browser.test.tsx index b175bbd3b097..01cae793ae01 100644 --- a/apps/web/src/components/usage/UsageLimitsPooled.browser.test.tsx +++ b/apps/web/src/components/usage/UsageLimitsPooled.browser.test.tsx @@ -194,6 +194,26 @@ it("cuts the pace gap through the segment track and keeps the tick inside it", a expect(tick.right).toBeLessThanOrEqual(rect.right); }); +it("drops the pace gap and tick when a segment is narrower than 6px", async () => { + setup(); + await using _screen = await render( + , + ); + const segment = page.getByRole("button", { name: /^Alpha:/ }).element(); + const tick = () => segment.querySelector(':scope > span[style*="left"]'); + expect(tick()).not.toBeNull(); + // Pooled grid columns have no minimum width; squeeze the segment the way a crowded pool does. + await act(() => { + segment.style.width = "4.5px"; + }); + await expect.poll(tick).toBeNull(); + expect(getComputedStyle(segment).backgroundImage).toBe("none"); + expect(getComputedStyle(segment.querySelector(":scope > div > div")!).clipPath).toBe("none"); +}); + it("moves the one limit popover between windows on hover", async () => { setup(); await using screen = await render(initial()); diff --git a/apps/web/test/fixtures/usage-limits-hub/README.md b/apps/web/test/fixtures/usage-limits-hub/README.md index 7a7bc70fb44d..102f8a31e573 100644 --- a/apps/web/test/fixtures/usage-limits-hub/README.md +++ b/apps/web/test/fixtures/usage-limits-hub/README.md @@ -1,8 +1,65 @@ # Usage limits hub fixture -A fake CLIProxyAPI hub that serves the Limits evidence scenarios in `scenarios.json` -(A mixed, B one account per provider, C six long-named Codex accounts, L named accounts, -T forty accounts). Run `node hub.mjs state.json 18517` with `state.json` set to -`{"scenario": "A", "n0": }`, add `http://127.0.0.1:18517` with any management key -under Settings → Usage limit sources in a dev server, and freeze the browser clock at `n0`. -Edit `state.json` and press Refresh limits to switch scenarios. +A fake CLIProxyAPI hub that serves the Limits evidence scenarios in `scenarios.json`: + +| Scenario | Accounts | +| -------- | -------------------------------------------------------------- | +| A | mixed Codex and Claude accounts | +| B | one account per provider | +| C | six long-email Codex accounts | +| L | named accounts (no email, so segments show the auth-file name) | +| T | forty accounts, so pooled segments are 4px wide at 390px | + +Reset times are minute offsets from `n0`. + +## Rebuild a capture + +1. Start a dev server with empty provider homes, so local logins add no accounts: + + ```sh + CODEX_HOME=$(mktemp -d) CLAUDE_CONFIG_DIR=$(mktemp -d) vp run dev + ``` + +2. Write `state.json` as `{"scenario": "A", "n0": 1792515540000}`. That `n0` is 2026-10-20T16:59:00Z, the instant every published capture uses. Then start the hub: + + ```sh + node hub.mjs state.json 18517 + ``` + +3. Under Settings → Providers → Usage limit sources, add a hub with URL `http://127.0.0.1:18517`, any management key, and label `Team hub`. The composer panel prints that label on every account row. + +4. Freeze the browser clock at `n0` from the devtools console: + + ```js + const T = 1792515540000; + const R = Date; + window.Date = class extends R { + constructor(...a) { + super(...(a.length ? a : [T])); + } + static now() { + return T; + } + }; + ``` + + Then re-read the time: + - On the Usage page, select Cost and then Limits again. + - Open the composer panel with `/usage-limits` after freezing. + - The sidebar's relative timestamp picks up the frozen clock on its next minute tick. + +5. Viewports and themes: emulate `prefers-color-scheme` light and dark, and wait a few seconds after switching theme or size so transitions and overlay scrollbars settle. + + | Surface | Viewport | + | ----------------------------------------------- | ------------------------------------- | + | Usage page | 1440×900 and 390×1500 | + | Scenario T | 390×3100, so the page does not scroll | + | Composer panel, Codex thread (`gpt-6-astra`) | 1440×900 | + | Composer panel, Claude thread (Claude Opus 5.5) | 390×1300 | + +6. Edit `state.json` and press Refresh limits to switch scenarios. + +## Not from the fixture + +- **"Codex: Could not read limits."** That banner comes from the local Codex install, which is unauthenticated with an empty `CODEX_HOME`. +- **"Claude API Key" rows** in the Claude composer panel come from the local Claude install, not the fixture.