mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(ui): fence QR polling during cancellation
This commit is contained in:
@@ -88,6 +88,52 @@ describe("custodian QR wizard step", () => {
|
||||
await waitForFast(() => expect(page.textContent).toContain("Signal setup cancelled."));
|
||||
});
|
||||
|
||||
it("pauses QR polling while direct wizard cancellation settles", async () => {
|
||||
vi.useFakeTimers();
|
||||
const request = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(qrResult())
|
||||
.mockResolvedValueOnce(terminalResult("Signal setup cancelled."));
|
||||
const { page } = await mountPage(createContext(request).context);
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
|
||||
page.querySelector<HTMLButtonElement>(".custodian__wizard-cancel")?.click();
|
||||
await vi.advanceTimersByTimeAsync(2_000);
|
||||
await page.updateComplete;
|
||||
|
||||
expect(request).toHaveBeenCalledTimes(2);
|
||||
expect(request.mock.calls[1]?.[1]).toEqual({
|
||||
sessionId: SESSION_ID,
|
||||
wizardCancel: { stepId: "qr-step" },
|
||||
});
|
||||
expect(page.textContent).toContain("Signal setup cancelled.");
|
||||
});
|
||||
|
||||
it.each([
|
||||
["exit setup", (page: Awaited<ReturnType<typeof mountPage>>["page"]) => page.store.exitSetup()],
|
||||
[
|
||||
"open model setup",
|
||||
(page: Awaited<ReturnType<typeof mountPage>>["page"]) => page.store.openModelSetup(),
|
||||
],
|
||||
[
|
||||
"open channels",
|
||||
(page: Awaited<ReturnType<typeof mountPage>>["page"]) =>
|
||||
page.store.openChannelsFromOnboarding(),
|
||||
],
|
||||
])("stops QR polling and scrubs image bytes when users %s", async (_name, navigate) => {
|
||||
vi.useFakeTimers();
|
||||
const request = vi.fn().mockResolvedValueOnce(qrResult());
|
||||
const { page } = await mountPage(createContext(request).context);
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
|
||||
navigate(page);
|
||||
await vi.advanceTimersByTimeAsync(2_000);
|
||||
await page.updateComplete;
|
||||
|
||||
expect(request).toHaveBeenCalledOnce();
|
||||
expect(page.store.messages.some((message) => message.step?.qrDataUrl)).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps polling when acknowledgement advances directly to another QR", async () => {
|
||||
vi.useFakeTimers();
|
||||
const request = vi
|
||||
|
||||
@@ -375,7 +375,7 @@ export class CustodianSessionStore {
|
||||
const client = this.activeClient;
|
||||
if (
|
||||
!step ||
|
||||
!this.wizardInputPending ||
|
||||
(!this.wizardInputPending && !(this.wizardSettling && step.type === "qr")) ||
|
||||
!client ||
|
||||
!this.chatAvailable ||
|
||||
!this.wizardCancelAvailable ||
|
||||
@@ -385,12 +385,22 @@ export class CustodianSessionStore {
|
||||
this.emit();
|
||||
return;
|
||||
}
|
||||
const sessionContinuity = this.sessionContinuity;
|
||||
const settleQrCancellation =
|
||||
step.type === "qr"
|
||||
? this.qrScheduler.beginAcknowledgement(
|
||||
client,
|
||||
step.id,
|
||||
() => this.sessionContinuity === sessionContinuity && this.activeClient === client,
|
||||
)
|
||||
: undefined;
|
||||
void this.sendUserTurn(
|
||||
client,
|
||||
{ sessionId: this.sessionId, wizardCancel: { stepId: step.id } },
|
||||
t("custodian.cancel"),
|
||||
true,
|
||||
);
|
||||
() => settleQrCancellation?.("sent"),
|
||||
).then(settleQrCancellation);
|
||||
}
|
||||
|
||||
exitSetup(): void {
|
||||
@@ -401,6 +411,8 @@ export class CustodianSessionStore {
|
||||
}
|
||||
|
||||
private revokeNavigationAuthority(): void {
|
||||
this.qrScheduler.clear();
|
||||
this.messages = scrubCustodianQrSteps(this.messages);
|
||||
this.requestAbort?.abort();
|
||||
this.requestAbort = null;
|
||||
this.requestEpoch += 1;
|
||||
|
||||
Reference in New Issue
Block a user