60 lines
3.9 KiB
JavaScript
60 lines
3.9 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { startFakeDecoder } from "./browser_fake_decoder.js";
|
|
|
|
export async function runDecoderReconnectSmoke({ evaluate, navigate, savedState, waitFor, pause, base }) {
|
|
const patchState = async patch => {
|
|
const current = await savedState();
|
|
Object.assign(current, patch);
|
|
const response = await fetch(base + "/api/state", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(current) });
|
|
assert.equal(response.ok, true);
|
|
};
|
|
const badgeOnline = () => evaluate('document.getElementById("connectionBadge")?.classList.contains("badge-online")');
|
|
|
|
const decoder = await startFakeDecoder();
|
|
try {
|
|
const seeded = await savedState();
|
|
await patchState({ settings: { ...seeded.settings, autoReconnect: true, wsUrl: `ws://127.0.0.1:${decoder.port}` } });
|
|
|
|
await navigate("events", "eventForm");
|
|
await waitFor(async () => await badgeOnline(), "decoder connects on page load");
|
|
assert.equal(decoder.activeConnections(), 1, "exactly one live connection after the initial connect");
|
|
assert.equal(decoder.totalConnections(), 1, "a clean initial connect makes exactly one connection attempt");
|
|
|
|
// Simulate a real network/decoder interruption: drop the TCP connection with a raw
|
|
// reset rather than a clean WS close handshake, the way a lost cable or a decoder
|
|
// power-cycle actually would - see src/decoder_runtime.js's wsClient.onclose.
|
|
decoder.dropConnections();
|
|
await waitFor(async () => (await badgeOnline()) === false, "badge shows offline once the drop is detected");
|
|
assert.equal(decoder.activeConnections(), 0, "no lingering connection right after the drop");
|
|
|
|
// decoder_runtime.js schedules exactly one reconnect attempt 2000ms after onclose.
|
|
await waitFor(async () => decoder.totalConnections() >= 2, "a reconnect attempt reaches the decoder socket");
|
|
await waitFor(async () => await badgeOnline(), "badge shows online again after auto-reconnect");
|
|
assert.equal(decoder.totalConnections(), 2, "auto-reconnect made exactly one new connection attempt, not several");
|
|
assert.equal(decoder.activeConnections(), 1, "auto-reconnect settles on exactly one live connection, not a duplicate");
|
|
console.log("PASS decoder auto-reconnects after a dropped socket onto exactly one connection");
|
|
|
|
// A second drop must not leave a stray reconnect timer running once autoReconnect is
|
|
// turned off - the retry loop should stop, not silently keep firing in the background.
|
|
// Toggling it through the real Settings form (not a raw state patch) matters here:
|
|
// a raw backend patch never reaches this already-loaded page's in-memory state, only
|
|
// a fresh navigation would, and that would also drop and reopen the live connection.
|
|
await navigate("settings", "settingsForm");
|
|
await waitFor(async () => await badgeOnline(), "decoder is connected on the settings page");
|
|
await evaluate('document.querySelector("#settingsForm [name=autoReconnect]").checked = false');
|
|
await evaluate('document.getElementById("settingsForm").requestSubmit()');
|
|
const connectionsBeforeDrop = decoder.totalConnections();
|
|
decoder.dropConnections();
|
|
await waitFor(async () => (await badgeOnline()) === false, "badge shows offline after the second drop");
|
|
await pause(3000);
|
|
assert.equal(decoder.totalConnections(), connectionsBeforeDrop, "no reconnect attempt is made once autoReconnect is disabled");
|
|
assert.equal(await badgeOnline(), false, "stays offline with autoReconnect disabled");
|
|
assert.equal(decoder.activeConnections(), 0, "no duplicate or stray connection appears while disabled");
|
|
console.log("PASS disabling auto-reconnect stops further reconnect attempts after a drop");
|
|
} finally {
|
|
const cleanup = await savedState();
|
|
await patchState({ settings: { ...cleanup.settings, autoReconnect: false } });
|
|
await decoder.close();
|
|
}
|
|
}
|