mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-23 19:08:22 -06:00
fix(ui): recover authorized deep links after Control UI updates (#124328)
* fix(ui): refresh stale dashboard documents * test(ui): count recovery document requests
This commit is contained in:
committed by
GitHub
parent
e9d70a1169
commit
1fda883530
@@ -50,7 +50,10 @@ export function isStaleChunkImportError(error: unknown): boolean {
|
||||
}
|
||||
|
||||
function reloadControlUiDocument(): void {
|
||||
window.location.reload();
|
||||
const url = new URL(window.location.href);
|
||||
// The pre-app mount recovery strips this one-shot cache buster before bootstrap.
|
||||
url.searchParams.set("openclaw_mount_recovery", String(Date.now()));
|
||||
window.location.replace(url.href);
|
||||
}
|
||||
|
||||
function sessionStorageOrNull(): Pick<Storage, "getItem" | "setItem"> | null {
|
||||
|
||||
@@ -100,14 +100,14 @@ suite.define(() => {
|
||||
await route.abort("internetdisconnected");
|
||||
};
|
||||
await page.route(frenchLocaleModule, abortFrenchLocale);
|
||||
let navigationCount = 0;
|
||||
let documentRequestCount = 0;
|
||||
|
||||
try {
|
||||
const response = await page.goto(`${suite.server.baseUrl}settings/appearance`);
|
||||
expect(response?.status()).toBe(200);
|
||||
page.on("framenavigated", (frame) => {
|
||||
if (frame === page.mainFrame()) {
|
||||
navigationCount += 1;
|
||||
page.on("request", (request) => {
|
||||
if (request.resourceType() === "document") {
|
||||
documentRequestCount += 1;
|
||||
}
|
||||
});
|
||||
await page.locator(".settings-row__title", { hasText: "Language" }).waitFor();
|
||||
@@ -128,7 +128,7 @@ suite.define(() => {
|
||||
await page.locator(".settings-row__title", { hasText: "Language" }).waitFor();
|
||||
await page.locator(".settings-page").waitFor();
|
||||
expect(await documentMarker(page)).toBe("same-document");
|
||||
expect(navigationCount).toBe(0);
|
||||
expect(documentRequestCount).toBe(0);
|
||||
|
||||
await page.unroute(frenchLocaleModule, abortFrenchLocale);
|
||||
await reconnect(page, gateway);
|
||||
@@ -137,14 +137,14 @@ suite.define(() => {
|
||||
.locator(".settings-row__title", { hasText: "Langue" })
|
||||
.waitFor({ timeout: 10_000 });
|
||||
expect(await documentMarker(page)).toBeUndefined();
|
||||
expect(navigationCount).toBe(1);
|
||||
expect(documentRequestCount).toBe(1);
|
||||
expect(
|
||||
await page.evaluate(() =>
|
||||
sessionStorage.getItem("openclaw.controlUi.staleChunkReloadBuildId"),
|
||||
),
|
||||
).toBe("e2e");
|
||||
await page.waitForTimeout(500);
|
||||
expect(navigationCount).toBe(1);
|
||||
expect(documentRequestCount).toBe(1);
|
||||
expect(new URL(page.url()).pathname).toBe("/settings/appearance");
|
||||
} finally {
|
||||
await page.unroute(frenchLocaleModule, abortFrenchLocale);
|
||||
|
||||
@@ -59,6 +59,62 @@ async function closeContext(context: BrowserContext): Promise<void> {
|
||||
}
|
||||
|
||||
suite.define(() => {
|
||||
it("cache-busts stale-build recovery on a first dashboard navigation", async () => {
|
||||
const context = await suite.browser.newContext({
|
||||
serviceWorkers: "block",
|
||||
viewport: { height: 900, width: 1280 },
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const documentRequests: Array<{ fresh: boolean; pathname: string }> = [];
|
||||
const appOrigin = new URL(suite.server.baseUrl).origin;
|
||||
await page.route(`${appOrigin}/**`, async (route) => {
|
||||
const request = route.request();
|
||||
if (request.resourceType() === "document") {
|
||||
const url = new URL(request.url());
|
||||
documentRequests.push({
|
||||
fresh: url.searchParams.has("openclaw_mount_recovery"),
|
||||
pathname: url.pathname,
|
||||
});
|
||||
}
|
||||
await route.continue();
|
||||
});
|
||||
const gateway = await installMockGateway(page, {
|
||||
deferredMethods: ["connect"],
|
||||
sessionKey: "agent:example-agent:example-session",
|
||||
});
|
||||
const mismatch = {
|
||||
code: "UNAVAILABLE",
|
||||
message: "Control UI updated; reload this page to continue",
|
||||
details: {
|
||||
code: ConnectErrorDetailCodes.CONTROL_UI_BUILD_MISMATCH,
|
||||
gatewayBuildId: "replacement-build",
|
||||
reloadRequired: true,
|
||||
},
|
||||
retryable: false,
|
||||
};
|
||||
const target = new URL("dashboard/example-agent/example-session", suite.server.baseUrl);
|
||||
|
||||
try {
|
||||
await page.goto(target.href);
|
||||
await gateway.waitForRequest("connect");
|
||||
await gateway.rejectDeferred("connect", mismatch);
|
||||
|
||||
await expect.poll(() => documentRequests.length).toBe(2);
|
||||
await gateway.waitForRequest("connect");
|
||||
expect(documentRequests).toEqual([
|
||||
{ fresh: false, pathname: target.pathname },
|
||||
{ fresh: true, pathname: target.pathname },
|
||||
]);
|
||||
await gateway.resolveDeferred("connect");
|
||||
|
||||
await page.locator("openclaw-app-shell").waitFor();
|
||||
expect(await page.locator("openclaw-login-gate").count()).toBe(0);
|
||||
await expect.poll(() => page.url()).toBe(target.href);
|
||||
} finally {
|
||||
await closeContext(context);
|
||||
}
|
||||
});
|
||||
|
||||
it("reloads once for a build rejection, then keeps visible recovery guidance", async () => {
|
||||
const context = await suite.browser.newContext({ viewport: { height: 900, width: 1280 } });
|
||||
const page = await context.newPage();
|
||||
|
||||
@@ -100,10 +100,10 @@ suite.define(() => {
|
||||
path: path.join(artifactDir, "failure.png"),
|
||||
});
|
||||
|
||||
let navigationCount = 0;
|
||||
page.on("framenavigated", (frame) => {
|
||||
if (frame === page.mainFrame()) {
|
||||
navigationCount += 1;
|
||||
let documentRequestCount = 0;
|
||||
page.on("request", (request) => {
|
||||
if (request.resourceType() === "document") {
|
||||
documentRequestCount += 1;
|
||||
}
|
||||
});
|
||||
markDocumentReachable();
|
||||
@@ -112,13 +112,13 @@ suite.define(() => {
|
||||
await alert.waitFor();
|
||||
await page.waitForTimeout(500);
|
||||
expect(await alert.count()).toBe(1);
|
||||
expect(navigationCount).toBe(1);
|
||||
expect(documentRequestCount).toBe(1);
|
||||
|
||||
await alert.getByRole("button", { name: "Reload" }).click();
|
||||
await page.locator(".logbook").waitFor();
|
||||
expect(await alert.count()).toBe(0);
|
||||
expect(assetRequests).toBeGreaterThan(2);
|
||||
expect(navigationCount).toBe(2);
|
||||
expect(documentRequestCount).toBe(2);
|
||||
await gateway.waitForRequest("logbook.status");
|
||||
await page.screenshot({
|
||||
fullPage: true,
|
||||
|
||||
Reference in New Issue
Block a user