feat(e2e): add smart test caching and error toast catching

- results-db.ts: TestPassCache with consecutivePasses counter,
  recordTestResult(), shouldSkip(threshold=3), resetCache()
- wdio.conf.js: afterTest hook catches unexpected .toast.error/.load-error
  elements, records results to smart cache. FULL_RESCAN=1 bypasses caching
This commit is contained in:
Hibryda 2026-03-18 05:16:49 +01:00
parent 0803dc3844
commit 46f51d7941
2 changed files with 120 additions and 3 deletions

View file

@ -4,6 +4,7 @@ import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { rmSync, existsSync } from 'node:fs';
import { createTestFixture } from './infra/fixtures.ts';
import { getResultsDb } from './infra/results-db.ts';
const __dirname = dirname(fileURLToPath(import.meta.url));
const projectRoot = resolve(__dirname, '../..');
@ -251,6 +252,57 @@ export const config = {
console.log('App identity verified: Agent Orchestrator connected.');
},
/**
* Smart test caching: skip tests that have passed consecutively N times.
* Set FULL_RESCAN=1 to bypass caching and run all tests.
*/
beforeTest(test) {
// Reset expected errors for this test
browser.__expectedErrors = [];
if (process.env.FULL_RESCAN) return;
const db = getResultsDb();
const specFile = test.file?.replace(/.*specs\//, '') ?? '';
if (db.shouldSkip(specFile, test.title)) {
const stats = db.getCacheStats();
console.log(`⏭ Skipping (3+ consecutive passes): ${test.title} [${stats.skippable}/${stats.total} skippable]`);
this.skip();
}
},
/**
* After each test: check for unexpected error toasts in the DOM,
* then record the result in the pass cache.
*/
async afterTest(test, _context, { passed }) {
// 1. Check for unexpected error toasts
let unexpected = [];
try {
const errors = await browser.execute(() => {
const toasts = [...document.querySelectorAll('.toast-error, .load-error')];
return toasts.map(t => t.textContent?.trim()).filter(Boolean);
});
const expected = browser.__expectedErrors || [];
unexpected = errors.filter(e => !expected.some(exp => e.includes(exp)));
if (unexpected.length > 0 && passed) {
throw new Error(
`Unexpected error toast(s) during "${test.title}": ${unexpected.join('; ')}`
);
}
} catch (e) {
// Re-throw toast errors, swallow browser.execute failures (e.g., session closed)
if (e.message?.includes('Unexpected error toast')) throw e;
}
// 2. Record result in pass cache
const db = getResultsDb();
const specFile = test.file?.replace(/.*specs\//, '') ?? '';
db.recordTestResult(specFile, test.title, passed && unexpected.length === 0);
},
/**
* Kill tauri-driver after the test run.
*/