tests: snapshot and reset view state around runs
Tests assume a normal board — no super-view, no bucket filter, manual sort, no hides. When the runner fired with the app left in super-view or with filters on, selectors like `.card[data-id]` missed the `.superview-item` DOM and multi-select / due-date assertions read the wrong initial values. Snapshot the relevant bits of window.state before the run, force a clean baseline, then restore the user's view afterwards so opening the runner doesn't silently wipe their context. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
1d6499e5dd
commit
1a889ea435
1 file changed
+84
@@ -170,6 +170,71 @@ export function csrf() {
|
||||
return getCsrf();
|
||||
}
|
||||
|
||||
// Snapshot the view-affecting bits of window.state so we can force a
|
||||
// neutral baseline (normal board, no filter, manual sort, no hides)
|
||||
// for the test run and restore the user's view afterwards. Tests were
|
||||
// flaky when the runner fired while the board was in super-view: DOM
|
||||
// selectors like `.card[data-id]` don't match `.superview-item`, so
|
||||
// multi-select and due-date tests read wrong initial values.
|
||||
export function snapshotViewState() {
|
||||
const s = window.state;
|
||||
if (!s) return null;
|
||||
return {
|
||||
superView: s.superView,
|
||||
activeBucket: s.activeBucket,
|
||||
activeTags: [...(s.activeTags || [])],
|
||||
activeGroup: s.activeGroup,
|
||||
sortMode: s.sortMode,
|
||||
hideTermine: s.hideTermine,
|
||||
hideDone: s.hideDone,
|
||||
hideTagged: s.hideTagged,
|
||||
boardSplitOpen: s.boardSplitOpen,
|
||||
};
|
||||
}
|
||||
|
||||
export function resetViewState() {
|
||||
const s = window.state;
|
||||
if (!s) return;
|
||||
s.superView = false;
|
||||
s.activeBucket = 'all';
|
||||
s.activeTags = [];
|
||||
s.activeGroup = null;
|
||||
s.sortMode = 'manual';
|
||||
s.hideTermine = false;
|
||||
s.hideDone = false;
|
||||
s.hideTagged = false;
|
||||
s.boardSplitOpen = false;
|
||||
s.selectedIds?.clear?.();
|
||||
// Sync super-view toggle visuals with the reset state so tests that
|
||||
// inspect the button see the correct pose.
|
||||
const btn = document.getElementById('superview-toggle');
|
||||
const icon = btn?.querySelector('i');
|
||||
if (icon) icon.className = 'ph ph-list text-sm';
|
||||
btn?.classList.remove('active');
|
||||
window._renderBoard?.();
|
||||
}
|
||||
|
||||
export function restoreViewState(snap) {
|
||||
const s = window.state;
|
||||
if (!s || !snap) return;
|
||||
Object.assign(s, {
|
||||
superView: snap.superView,
|
||||
activeBucket: snap.activeBucket,
|
||||
activeTags: snap.activeTags,
|
||||
activeGroup: snap.activeGroup,
|
||||
sortMode: snap.sortMode,
|
||||
hideTermine: snap.hideTermine,
|
||||
hideDone: snap.hideDone,
|
||||
hideTagged: snap.hideTagged,
|
||||
boardSplitOpen: snap.boardSplitOpen,
|
||||
});
|
||||
const btn = document.getElementById('superview-toggle');
|
||||
const icon = btn?.querySelector('i');
|
||||
if (icon) icon.className = `ph ${snap.superView ? 'ph-squares-four' : 'ph-list'} text-sm`;
|
||||
btn?.classList.toggle('active', !!snap.superView);
|
||||
window._renderBoard?.();
|
||||
}
|
||||
|
||||
export async function closeAll() {
|
||||
document.getElementById('hterm')?.classList.add('hidden');
|
||||
for (const id of ['settings-overlay', 'help', 'shortcuts', 'search', 'calendar', 'gallery', 'detail', 'modal', 'tasks', 'dialog']) {
|
||||
@@ -452,6 +517,15 @@ async function run() {
|
||||
stopBackgroundSync();
|
||||
window._testRunning = true;
|
||||
|
||||
// 2b. Snapshot + reset view state. Tests assume a normal board with
|
||||
// no filters / super-view off / manual sort — if the user left the
|
||||
// app in super-view or with a bucket filter active, assertions on
|
||||
// .card[data-id], card count, etc. read wrong initial values.
|
||||
const viewSnap = snapshotViewState();
|
||||
await closeAll();
|
||||
resetViewState();
|
||||
await sleep(50);
|
||||
|
||||
// 3. Hide panel so tests run against visible UI
|
||||
panel.style.display = 'none';
|
||||
await sleep(100);
|
||||
@@ -476,6 +550,11 @@ async function run() {
|
||||
await window._reloadAll?.();
|
||||
await sleep(200);
|
||||
} catch {}
|
||||
// Re-reset the view in case a failing test left it in super-view,
|
||||
// a bucket filter, or a sorted mode — otherwise the retry runs
|
||||
// against the same wrong baseline that flaked it the first time.
|
||||
resetViewState();
|
||||
await sleep(50);
|
||||
const retryResults = await runAll(failedKeys);
|
||||
const retryMap = new Map();
|
||||
for (const sr of retryResults) {
|
||||
@@ -508,6 +587,11 @@ async function run() {
|
||||
await sleep(200);
|
||||
} catch {}
|
||||
|
||||
// 6b. Put the view back the way the user left it (super-view, sort,
|
||||
// filters, …) so launching the test runner doesn't silently wipe
|
||||
// their working context.
|
||||
restoreViewState(viewSnap);
|
||||
|
||||
// 7. Resume background sync (delay to avoid immediate re-sync creating duplicates)
|
||||
window._testRunning = false;
|
||||
setTimeout(() => startBackgroundSync(), 60000);
|
||||
|
||||
Reference in new issue
Block a user