context: a window opened on the Context tab loads it at once, not at the refresh's first tick five seconds on

context.js comes after app.js, and app.js shows ?view=usage and reads the
state before context.js is there: when /api/state answered first, both
paths called window.loadContext while it was undefined, the TypeError went
to the status line, and the tab stayed empty until the 5s refresh (found
as context-window's webkit de 1100px flake, 2 of 6 runs). The calls are
optional now, and context.js loads the tab itself when the page opened on
it, as plugins.js and library.js do. The new test holds context.js until
the state has answered: it fails on both engines without the fix and
passes with it; context-window passes 22/22, the case that flaked 10/10.
This commit is contained in:
yetone committed 2026-10-08 12:53:27 +08:00
1 parent b5a7d3999b
commit fd29653d4e
3 files changed
+33 -2

No files matched your search

+2 -2
View File
@@ -12443,7 +12443,7 @@ async function loadUsage(asked) {
if (asked) loadQuotas(true);
if (usageTab === "sessions") return loadSessions();
if (usageTab === "requests") return loadLedger();
if (usageTab === "context") return window.loadContext();
if (usageTab === "context") return window.loadContext?.();
renderUsageLoading();
if (!asked) loadQuotas();
const p = period, read = ++usageRead;
@@ -20205,7 +20205,7 @@ function refreshUsage(now = false) {
// the newest page takes the requests as they come; an older one stays put
if (ledger && (want || !ledOffset)) await loadLedger(true);
} else if (usageTab === "context") {
await window.loadContext();
await window.loadContext?.();
} else {
// the reader asking reads the allowances afresh, a Claude account's by
// running Claude Code's own /usage (the backend runs it at most once in 30s)
+3
View File
@@ -616,4 +616,7 @@
}
window.loadContext = loadContext;
window.renderContext = renderContext;
// opened on the Context tab (?view=usage): app.js showed it, and may have
// read the state, before this file was here to load it
if (view === "usage" && usageTab === "context") loadContext().catch((e) => status(e.message, "err"));
})();
@@ -180,3 +180,31 @@ for (const engine of (process.env.BROWSER ? [process.env.BROWSER] : ["chromium",
}
}
}
// context.js comes after app.js, which shows the page and reads the state
// before it is there: the state answered first, the tab still loads at once
// rather than at the refresh's first tick, five seconds on
for (const engine of (process.env.BROWSER ? [process.env.BROWSER] : ["chromium", "webkit"])) {
test(`${engine}: the Context tab loads when context.js arrives after the state`, async (t) => {
const browser = await (engine === "webkit" ? webkit.launch() : chromium.launch({ channel: "chromium" }));
t.after(() => browser.close());
const page = await (await browser.newContext({ viewport: { width: 1100, height: 900 } })).newPage();
const errors = [], asked = [];
page.on("pageerror", (e) => errors.push(e.message));
const base = serve("en", asked);
let answered;
const stateDone = new Promise((res) => { answered = res; });
await page.route("**/*", async (r) => {
const p = new URL(r.request().url()).pathname;
if (p === "/context.js") await stateDone.then(() => new Promise((res) => setTimeout(res, 150)));
await base(r);
if (p === "/api/state") answered();
});
await page.addInitScript(() => { try { localStorage.clear(); localStorage.setItem("magpie.usageTab", "context"); } catch {} });
await page.goto("http://magpie.test/?view=usage");
await page.locator(".ctx-agent", { hasText: "Codex" }).waitFor({ timeout: 2500 });
assert.equal(asked[0], "7");
assert.doesNotMatch(await page.locator("#status").innerText(), /loadContext|not a function/);
assert.deepEqual(errors, []);
});
}