Fix session loading: a no-op capture stop aborted the load (v2.4.1)

- `loadSession` begins by stopping a running capture; when there is nothing
  to stop the server answers 400 'No active capture' (or 403 with capture
  disabled) — and since the v2.3.1 `_fetch` status fix those answers throw, so
  the whole load was skipped and the UI alerted "Failed to load session":
  no session could be opened at all
- new `stopCaptureIfRunning()` swallows exactly that no-op answer; both
  `loadSession` and the capture button use it, and the button now resets its
  state outside the try so a tolerated stop still clears the UI
- the `_fetch` status check stays — real errors keep surfacing (an unknown
  session still throws 'Session not found')
- tests: the helper resolves on a 400 and still calls once; source pins that
  `loadSession` uses the tolerant helper (the regression guard) and that
  `stopCapture` resets the state
- v2.4.1; SW cache simtrace-analyser-v66
This commit is contained in:
catarrh committed 2026-10-17 21:25:48 +03:00
1 parent cad3999307
commit 407821ab3d
6 files changed
+48 -14

No files matched your search

+18 -10
View File
@@ -2546,15 +2546,22 @@ async function startCapture() {
}
}
async function stopCapture() {
// Stopping when nothing is capturing answers 400 ('No active capture'), and
// 403 when capture is disabled — both mean "there is no capture to stop",
// not a failure.
async function stopCaptureIfRunning() {
try {
await API.stopCapture();
STATE.captureActive = false;
STATE.currentSessionId = null;
STATE.sessionViewId = null;
updateCaptureButton();
updateStatus();
} catch (e) {}
} catch (e) { /* nothing to stop */ }
}
async function stopCapture() {
await stopCaptureIfRunning();
STATE.captureActive = false;
STATE.currentSessionId = null;
STATE.sessionViewId = null;
updateCaptureButton();
updateStatus();
}
function updateCaptureButton() {
@@ -2891,8 +2898,9 @@ async function confirmComprionImport() {
async function loadSession(id) {
if (STATE.captureActive && !confirm(t('Opening a session stops the active capture. Continue?'))) return;
try {
// Stop any active capture so it can't write into the session being viewed.
await API.stopCapture();
// Stop any active capture so it can't write into the session being
// viewed (a 400/403 means there is none — not an error).
await stopCaptureIfRunning();
STATE.captureActive = false;
STATE.currentSessionId = null;
stopPolling();
@@ -3164,7 +3172,7 @@ function onFilterChange() {
}
// ──────────────────── Versioning ────────────────────
const PWA_VERSION = '2.4.0';
const PWA_VERSION = '2.4.1';
const MIN_SERVER_VERSION = '2.3.0';
function parseVersion(v) {
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'simtrace-analyser-v65';
const CACHE = 'simtrace-analyser-v66';
const PRECACHE = [
'index.html',
+26
View File
@@ -498,3 +498,29 @@ test('Change details modal: header, summary, legend and both views', () => {
assert.match(el('file-diff-sub').textContent, /Record 2 \/ 5/);
assert.match(el('file-diff-body').innerHTML, /image is frozen/);
});
// ── Session loading must survive "no active capture" ───────────────────
eval(extractFunc(src, 'stopCaptureIfRunning'));
test('stopCaptureIfRunning tolerates "no active capture" (400)', async () => {
let calls = 0;
API.stopCapture = async () => {
calls++;
throw Object.assign(new Error('No active capture'), {status: 400});
};
await stopCaptureIfRunning(); // must resolve, not reject
assert.equal(calls, 1);
API.stopCapture = async () => { calls++; };
await stopCaptureIfRunning();
assert.equal(calls, 2);
});
test('loadSession stops a capture tolerantly', () => {
const body = extractFunc(src, 'loadSession');
assert.match(body, /await stopCaptureIfRunning\(\)/);
assert.doesNotMatch(body, /await API\.stopCapture\(\)/);
// The capture button also resets its state after a tolerated stop.
const stop = extractFunc(src, 'stopCapture');
assert.match(stop, /await stopCaptureIfRunning\(\)/);
assert.match(stop, /STATE\.captureActive = false;/);
});