Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions apps/web/src/components/usage/UsageLimitsPooled.browser.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<UsageLimitsPooled
presentations={presentations("2026-09-03T14:00:00Z", 0)}
now={Date.parse("2026-09-03T11:30:00Z")}
/>,
);
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());
Expand Down
69 changes: 63 additions & 6 deletions apps/web/test/fixtures/usage-limits-hub/README.md
Original file line number Diff line number Diff line change
@@ -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": <epoch ms>}`, 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.
Loading