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
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// @vitest-environment jsdom

import { cleanup, render, screen } from "@testing-library/react";
import { TooltipProvider } from "@bb/shared-ui/tooltip";
import { afterEach, describe, expect, it } from "vitest";
import { Usage } from "./UsageLimitsSettingsSection.stories";

afterEach(cleanup);

describe("settings/Settings Page/Usage story", () => {
it("renders the usage state gallery", () => {
render(
<TooltipProvider>
<Usage />
</TooltipProvider>,
);

expect(screen.getByText("complete usage")).toBeDefined();
expect(screen.getAllByText("Fable").length).toBeGreaterThan(0);
expect(screen.getByText("authentication")).toBeDefined();
expect(screen.getByText("provider responses")).toBeDefined();
expect(screen.getByText("loading")).toBeDefined();
expect(screen.getByText("request failed")).toBeDefined();
expect(screen.getByText("multiple machines")).toBeDefined();
expect(
screen.getByRole("button", { name: "Usage limits machine" }),
).toBeDefined();

const providerHeadingIds = screen
.getAllByRole("region")
.map((region) => region.getAttribute("aria-labelledby"));
expect(new Set(providerHeadingIds).size).toBe(providerHeadingIds.length);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
import { useState, type ReactNode } from "react";
import type { Host } from "@bb/domain";
import { StoryCard, StoryRow } from "../../../.ladle/story-card";
import {
UsageLimitsSettingsSectionContent,
type UsageLimitsSettingsSectionContentProps,
} from "./UsageLimitsSettingsSection";

export default {
title: "settings/Settings Page",
};

type Usage = UsageLimitsSettingsSectionContentProps["usage"];

const noop = () => {};

function futureIso(minutesFromNow: number): string {
return new Date(Date.now() + minutesFromNow * 60_000).toISOString();
}

const HEALTHY_USAGE: Usage = {
codex: {
status: "ok",
accountEmail: "sawyer@example.com",
planLabel: "Pro",
windows: [
{
label: "Weekly usage limit",
usedPercent: 8,
resetsAt: futureIso(5 * 24 * 60),
},
],
},
claudeCode: {
status: "ok",
accountEmail: "sawyer@example.com",
planLabel: "Max (20x)",
windows: [
{
label: "Current session",
usedPercent: 53,
resetsAt: futureIso(187),
},
{
label: "All models",
usedPercent: 25,
resetsAt: futureIso(67),
},
{
label: "Fable",
usedPercent: 48,
resetsAt: futureIso(67),
},
],
},
cursor: {
status: "ok",
accountEmail: "sawyer@example.com",
planLabel: "Pro",
windows: [
{
label: "Plan usage",
usedPercent: 72,
resetsAt: futureIso(14 * 24 * 60),
},
{
label: "On-demand spend",
usedPercent: 25,
resetsAt: futureIso(14 * 24 * 60),
cost: { usedUsdCents: 1_250, limitUsdCents: 5_000 },
},
],
},
};

const AUTH_USAGE: Usage = {
codex: { status: "unauthenticated" },
claudeCode: { status: "expired" },
cursor: { status: "not_installed" },
};

const EMPTY_AND_ERROR_USAGE: Usage = {
codex: {
status: "ok",
accountEmail: null,
planLabel: "Team",
windows: [],
},
claudeCode: {
status: "error",
message: "Claude usage is temporarily unavailable.",
},
cursor: { status: "not_installed" },
};

const HOSTS: Host[] = [
{
id: "host-macbook",
name: "MacBook Pro",
type: "persistent",
status: "connected",
lastSeenAt: 1_700_000_000_000,
maxPermissionMode: "full",
lastRejectedProtocolVersion: null,
createdAt: 1,
updatedAt: 2,
},
{
id: "host-studio",
name: "Mac Studio",
type: "persistent",
status: "connected",
lastSeenAt: 1_700_000_000_000,
maxPermissionMode: "full",
lastRejectedProtocolVersion: null,
createdAt: 1,
updatedAt: 2,
},
{
id: "host-build",
name: "Build machine",
type: "persistent",
status: "disconnected",
lastSeenAt: 1_700_000_000_000,
maxPermissionMode: "full",
lastRejectedProtocolVersion: null,
createdAt: 1,
updatedAt: 2,
},
];

function Stage({ children }: { children: ReactNode }) {
return <div className="w-full min-w-0 max-w-3xl">{children}</div>;
}

type UsagePreviewProps = Pick<UsageLimitsSettingsSectionContentProps, "usage"> &
Partial<
Pick<
UsageLimitsSettingsSectionContentProps,
| "hosts"
| "isError"
| "isFetching"
| "isLoading"
| "onSelectHost"
| "selectedHostId"
>
>;

function UsagePreview({
usage,
hosts,
isError = false,
isFetching = false,
isLoading = false,
onSelectHost,
selectedHostId,
}: UsagePreviewProps) {
return (
<Stage>
<UsageLimitsSettingsSectionContent
usage={usage}
isLoading={isLoading}
isError={isError}
isFetching={isFetching}
onRefresh={noop}
hosts={hosts}
selectedHostId={selectedHostId}
onSelectHost={onSelectHost}
/>
</Stage>
);
}

function MultipleMachinesPreview() {
const [selectedHostId, setSelectedHostId] = useState(HOSTS[0]?.id ?? null);

return (
<UsagePreview
usage={HEALTHY_USAGE}
hosts={HOSTS}
selectedHostId={selectedHostId}
onSelectHost={setSelectedHostId}
/>
);
}

export function Usage() {
return (
<StoryCard labelWidth="170px">
<StoryRow
label="complete usage"
hint="Provider plans, Claude Fable, and Cursor on-demand spend"
>
<UsagePreview usage={HEALTHY_USAGE} />
</StoryRow>
<StoryRow
label="authentication"
hint="Signed out, expired, and uninstalled providers"
>
<UsagePreview usage={AUTH_USAGE} />
</StoryRow>
<StoryRow
label="provider responses"
hint="An empty plan beside a provider-specific error"
>
<UsagePreview usage={EMPTY_AND_ERROR_USAGE} />
</StoryRow>
<StoryRow label="loading" hint="Initial request with no cached data">
<UsagePreview usage={{}} isLoading />
</StoryRow>
<StoryRow
label="request failed"
hint="The selected machine could not return usage"
>
<UsagePreview usage={{}} isError />
</StoryRow>
<StoryRow
label="multiple machines"
hint="Connected machines are selectable; disconnected ones are disabled"
>
<MultipleMachinesPreview />
</StoryRow>
</StoryCard>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ describe("UsageLimitsSettingsSectionContent", () => {
});

expect(screen.getByRole("heading", { name: "Cursor" })).toBeDefined();
expect(screen.getByRole("region", { name: "Cursor" })).toBeDefined();
expect(screen.getByText("cursor@example.com")).toBeDefined();
expect(screen.getByText("Plan usage")).toBeDefined();
expect(screen.getByText("50% used")).toBeDefined();
Expand All @@ -86,6 +87,20 @@ describe("UsageLimitsSettingsSectionContent", () => {
expect(screen.getByRole("heading", { name: "Codex" })).toBeDefined();
});

it("keeps states without usage bars with the provider heading", () => {
renderContent({
usage: { codex: { status: "unauthenticated" } },
isLoading: false,
isError: false,
isFetching: false,
onRefresh: vi.fn(),
});

const heading = screen.getByRole("heading", { name: "Codex" });
const status = screen.getByText(/Run `codex` to sign in/u);
expect(heading.parentElement?.contains(status)).toBe(true);
});

it("selects which connected machine supplies usage", () => {
const onSelectHost = vi.fn();
renderContent({
Expand Down
Loading
Loading