tests: in-run progress strip + cache-bust runner.js across files

* runner.js: shows a 30px fixed-top progress strip during runs (label
  "test x/total:", current suite/test name with ellipsis on the left,
  fixed 50%-wide progress fill on the right that turns danger-red on
  the first failure). The DOM node is created in loader.js so it appears
  immediately on shortcut press; runner.js only updates it. The fill
  width is fixed at flex:0 0 50% so it doesn't jiggle as the test name
  changes length. Suites now live on window._testSuites as a defensive
  fallback so two runner instances stay in sync.

* Cache invalidation: every test file's `from './runner.js'` is now
  `from './runner.js?v=2'`, and loader.js imports the runner with the
  same RUNNER_V suffix. Same URL → one module instance → suite
  registration and runAll see the same array. Bumping RUNNER_V (in
  loader.js + every test file in lock-step) is the way to force a
  re-fetch when runner.js changes.

* loader.js: ensureProgressBar() draws the strip via setProperty(...,
  'important') with z-index 2147483647 attached to documentElement so
  nothing in user CSS, scanline overlay (z 9999) or modals can occlude
  it. Auto-run path (?test=1) also pre-renders the strip.

chore: bump version to 0.9.1

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
thandClaude Opus 4.6 committed 2026-04-11 20:19:22 +02:00
1 parent f6cf7b4358
commit fe3399a44b
44 files changed
+132 -48

No files matched your search

+46 -1
View File
@@ -7,7 +7,13 @@
import { getCsrf } from '../../js/modules/util.js';
import { stopBackgroundSync, startBackgroundSync } from '../../js/modules/caldav.js';
const suites = [];
// Suites live on window so two runner.js instances (one cached and loaded
// by test files via bare imports, one fresh and loaded by loader.js with
// ?v=) can share registration. The cached runner provides describe/it/
// expect at module-load time; the fresh runner runs `runAll` against the
// shared array. Without this we'd see "0/0 passed" whenever the cached
// runner is older than the loader.
const suites = (window._testSuites = window._testSuites || []);
let currentSuite = null;
// ── Public API ──
@@ -200,11 +206,48 @@ async function restoreBackup() {
// ── Runner ──
// Fixed-top progress strip shown while tests are running. The main test
// panel is hidden during the run so the suite can interact with the UI;
// this strip gives the user a way to see how far along the run is.
//
// loader.js creates the DOM node when the user presses the shortcut so
// it appears immediately (loader.js is cache-busted per page load via
// the plugin manager's ?v=time()). The runner — which lives at a stable
// URL so suite registration is consistent — only updates the existing
// node. If runner.js is cached and stale, the placeholder still shows
// up so the user can tell something happened.
function getProgressBar() {
return document.getElementById('test-progress');
}
function updateProgress(current, total, name, failed) {
const bar = getProgressBar();
if (!bar) return;
const label = bar.querySelector('#test-progress-label');
const nameEl = bar.querySelector('#test-progress-name');
const fill = bar.querySelector('#test-progress-fill');
if (label) label.textContent = `test ${current}/${total}:`;
if (nameEl) nameEl.textContent = name;
if (fill) {
fill.style.width = (current / total * 100) + '%';
if (failed) fill.style.background = '#fb7185';
}
}
function hideProgress() {
getProgressBar()?.remove();
}
async function runAll() {
const results = [];
const total = suites.reduce((n, s) => n + s.tests.length, 0);
// Initial label before any test has started
updateProgress(0, total, 'starting…', false);
let done = 0;
let anyFailed = false;
for (const suite of suites) {
const suiteResult = { name: suite.name, tests: [] };
for (const test of suite.tests) {
done++;
updateProgress(done, total, `${suite.name} — ${test.name}`, anyFailed);
const entry = { name: test.name, status: 'pass', error: null, time: 0 };
const t0 = performance.now();
try {
@@ -214,6 +257,7 @@ async function runAll() {
} catch (err) {
entry.status = 'fail';
entry.error = err.message;
anyFailed = true;
// Always run afterEach on failure too
try { if (suite.afterEach) await suite.afterEach(); } catch {}
}
@@ -222,6 +266,7 @@ async function runAll() {
}
results.push(suiteResult);
}
hideProgress();
return results;
}