Make the race deterministic
A test that waits 10 ms and hopes A finishes after B may be flaky. Control completion directly with deferred promises. The fake intentionally ignores abort so you prove the generation guard itself.
import test from 'node:test';
import assert from 'node:assert/strict';
import { createCatalogueLoader } from '../public/ui/catalogue-loader.js';
function deferred() {
let resolve, reject;
const promise = new Promise((yes, no) => { resolve = yes; reject = no; });
return { promise, resolve, reject };
}
test('an older result cannot replace the newest intention', async () => {
const first = deferred();
const second = deferred();
const states = [];
let calls = 0;
const loader = createCatalogueLoader(
() => (++calls === 1 ? first.promise : second.promise),
state => states.push(state)
);
const a = loader.load('/slow');
const b = loader.load('/new');
second.resolve([{ id: 'new' }]);
await b;
first.resolve([{ id: 'old' }]);
await a;
assert.equal(states.at(-1).products[0].id, 'new');
assert.equal(states.filter(state => state.phase === 'ready').length, 1);
});
This loader unit test uses minimal products because catalogue schema validation has its own tests. Add a second test where the older promise rejects after the newer one succeeds. Add a disposal test: after dispose(), a late result must publish nothing further.
Tests at different boundaries
Pure tests prove policies and controller sequencing. They do not prove your HTML IDs match the renderer. Adapter tests prove response handling. They do not prove the real server exposes the correct route. Keep those limits explicit.
Run a real local HTTP check against your server for GET success, empty, 503, unknown route, malformed POST, valid quote, revision conflict and request replay. Check both status and payload; "the request returned something" is not an assertion.
Browser acceptance run
Start from a cleared draft key, then complete these scenarios:
- Enter quantity 2 and submit: premium total 324.00.
- Make quantity invalid: no old total presented as current; corrective text is associated with the field.
- Save valid inputs, reload, and restore them.
- Load a slow catalogue, then an empty one: no late stale list.
- Confirm the labelled server fixture and retry the same request: one revision change.
- Complete the form with keyboard navigation at a narrow viewport.
Record expected and observed results. If you use a browser automation library, choose assertions about visible behavior and request outcomes, not internal function names. Manual evidence is acceptable for this course when its scope is stated honestly.
Review question
Would a screenshot catch a duplicate submission, a discarded draft, or an outdated response? Usually not. Keep screenshots for layout evidence and behavioral checks for behavior. Your final report must say which category each piece of evidence covers.