Control UI: clear queued connect timeout on stop (#57338)

Merged via squash.

Prepared head SHA: a359fe8367
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
Gustavo Madeira Santana
2026-03-29 20:54:21 -04:00
committed by GitHub
parent fb81e3fc7f
commit b952e404fa
3 changed files with 44 additions and 4 deletions

View File

@@ -92,6 +92,16 @@ type ConnectFrame = {
};
};
function stubWindowGlobals(storage?: ReturnType<typeof createStorageMock>) {
vi.stubGlobal("window", {
location: { href: "http://127.0.0.1:18789/" },
localStorage: storage,
setTimeout: (handler: (...args: unknown[]) => void, timeout?: number, ...args: unknown[]) =>
globalThis.setTimeout(() => handler(...args), timeout),
clearTimeout: (timeoutId: number | undefined) => globalThis.clearTimeout(timeoutId),
});
}
function getLatestWebSocket(): MockWebSocket {
const ws = wsInstances.at(-1);
if (!ws) {
@@ -182,10 +192,7 @@ describe("GatewayBrowserClient", () => {
});
vi.stubGlobal("localStorage", storage);
Object.defineProperty(window, "localStorage", {
configurable: true,
value: storage,
});
stubWindowGlobals(storage);
localStorage.clear();
vi.stubGlobal("WebSocket", MockWebSocket);
@@ -377,6 +384,26 @@ describe("GatewayBrowserClient", () => {
vi.useRealTimers();
});
it("cancels a queued connect send when stopped before the timeout fires", async () => {
vi.useFakeTimers();
const client = new GatewayBrowserClient({
url: "ws://127.0.0.1:18789",
token: "shared-auth-token",
});
client.start();
const ws = getLatestWebSocket();
ws.emitOpen();
client.stop();
await vi.advanceTimersByTimeAsync(750);
expect(ws.sent).toHaveLength(0);
vi.useRealTimers();
});
it("does not auto-reconnect on AUTH_TOKEN_MISSING", async () => {
vi.useFakeTimers();
localStorage.clear();
@@ -408,6 +435,14 @@ describe("GatewayBrowserClient", () => {
});
describe("shouldRetryWithDeviceToken", () => {
beforeEach(() => {
stubWindowGlobals();
});
afterEach(() => {
vi.unstubAllGlobals();
});
it("allows a bounded retry for trusted loopback endpoints", () => {
expect(
shouldRetryWithDeviceToken({

View File

@@ -294,6 +294,10 @@ export class GatewayBrowserClient {
stop() {
this.closed = true;
if (this.connectTimer !== null) {
window.clearTimeout(this.connectTimer);
this.connectTimer = null;
}
this.ws?.close();
this.ws = null;
this.pendingConnectError = undefined;