add quota to the main screen
CI / verify (pull_request) Failing after 8s

This commit is contained in:
2026-09-17 22:06:45 +02:00
parent 9494936160
commit df61d9a282
4 changed files with 51 additions and 28 deletions
+31 -9
View File
@@ -5,7 +5,8 @@ import { createSignal } from "solid-js";
import { build } from "../scripts/build.js";
import type { QuotaStatus } from "../src/quota.js";
type SlotRender = (input: { readonly sessionID: string }) => JSX.Element;
type SidebarSlotRender = (input: { readonly sessionID: string }) => JSX.Element;
type HomeSlotRender = (input: Record<string, never>) => JSX.Element;
const status = (plan: string, remaining: number): QuotaStatus => ({
plan,
@@ -30,9 +31,13 @@ test("built quota slot stays reactive across pending, empty, failure, update, an
);
const { default: plugin } = tui;
const pending = Promise.withResolvers<QuotaStatus | null>();
const responses: Array<() => Promise<QuotaStatus | null>> = [() => pending.promise];
const responses: Array<() => Promise<QuotaStatus | null>> = [
async () => status("home", 90),
() => pending.promise,
];
let statusCalls = 0;
let slotRender: SlotRender | undefined;
let sidebarRender: SidebarSlotRender | undefined;
let homeRender: HomeSlotRender | undefined;
let notifyUpdated: (() => void) | undefined;
const quota = {
@@ -52,8 +57,17 @@ test("built quota slot stays reactive across pending, empty, failure, update, an
const contextFixture = {
client: { rpc: () => quota },
ui: {
slot(claim: { render: SlotRender }) {
slotRender = claim.render;
slot(claim: {
append: "home.footer.status" | "sidebar.footer";
render: HomeSlotRender | SidebarSlotRender;
}) {
if (claim.append === "home.footer.status") {
// SAFETY: The home slot renderer receives the empty home slot input.
homeRender = claim.render as HomeSlotRender;
} else {
// SAFETY: The only other fixture path is the session-aware sidebar slot.
sidebarRender = claim.render as SidebarSlotRender;
}
return () => undefined;
},
},
@@ -63,10 +77,18 @@ test("built quota slot stays reactive across pending, empty, failure, update, an
// members implemented by this focused rendering fixture.
const context = Object.assign({} as Plugin.Context, contextFixture);
const cleanup = await plugin.setup(context);
if (!slotRender || !notifyUpdated) throw new Error("Plugin did not register its slot");
if (!sidebarRender || !homeRender || !notifyUpdated)
throw new Error("Plugin did not register its slots");
const renderHome = homeRender;
const home = await testRender(() => renderHome({}), { width: 70, height: 5 });
await home.waitForFrame((frame) => frame.includes("ChatGPT quota (home)"));
expect(home.captureCharFrame()).toContain("5h: 90% · due");
expect(statusCalls).toBe(1);
home.renderer.destroy();
const [sessionID, setSessionID] = createSignal("session-one");
const renderSlot = slotRender;
const renderSlot = sidebarRender;
const setup = await testRender(
() =>
renderSlot({
@@ -94,13 +116,13 @@ test("built quota slot stays reactive across pending, empty, failure, update, an
throw new Error("disconnected");
});
notifyUpdated();
await setup.waitFor(() => statusCalls === 3);
await setup.waitFor(() => statusCalls === 4);
expect(setup.captureCharFrame()).not.toContain("ChatGPT quota");
responses.push(async () => status("pro", 42));
setSessionID("session-two");
await setup.waitForFrame((frame) => frame.includes("ChatGPT quota (pro)"));
expect(statusCalls).toBe(4);
expect(statusCalls).toBe(5);
} finally {
setup.renderer.destroy();
if (cleanup) await cleanup();