Files
hockey_new/tests/roster_first_selection.cjs

133 lines
5.8 KiB
JavaScript

/* Actual menu functions, deterministic local responses; no browser/network I/O. */
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const assert = require('node:assert/strict');
const support = fs.readFileSync(path.join(__dirname, 'match_selection_frontend.cjs'), 'utf8');
const boundary = support.indexOf('const appNames=');
assert.ok(boundary > 0, 'Menu harness boundary missing');
const sandbox = { require, __dirname, AbortController, URLSearchParams, performance };
vm.createContext(sandbox);
vm.runInContext(support.slice(0, boundary) + '\nthis.rosterSupport={menuHarness,details,compact,defer,tick,copy};', sandbox);
const { menuHarness, details, compact, defer, tick, copy } = sandbox.rosterSupport;
function empty(id, { finished = false, loaded = false } = {}) {
const value = details(id, 1, { finished });
value.game.home.players = [];
value.game.away.players = [];
value.game.referees = [];
value.details.loaded = loaded;
if (!loaded) value.details.synced_at = null;
return value;
}
const cases = {};
cases.first_selection_unchanged_live_hydrates = async () => {
const h = menuHarness({ initialId: '', finished: false, onRequest: url => {
if (url.includes('/games/B/details?')) return empty('B');
if (url.includes('/games/B/details/sync')) return Promise.reject(Error('temporary details timeout'));
if (url.includes('/games/B/live')) return { ...details('B', 2, { finished: false }), changed: false };
} });
h.state.selectedGame = null;
h.state.selectedMatchDetails = null;
h.state.games.items = [compact('B', { finished: false })];
await h.c.openGame('B');
await tick();
assert.equal(h.state.selectedGameId, 'B');
assert.equal(h.state.selectedGame.home.players.length, 0);
await h.c.pollSelectedGame();
assert.equal(h.state.selectedGame.home.players[0]?.id, 'B-home-v2', 'First selection discarded a complete unchanged live roster');
assert.equal(h.data().hockey.away.players[0]?.id, 'B-away-v2');
assert.equal(h.events.at(-1).detail.game.external_id, 'B');
assert.equal(h.state.selectedMatchDetails.loaded, true);
};
cases.finished_empty_loaded_cache_repairs_once = async () => {
const h = menuHarness({ initialId: '', onRequest: url => {
if (url.includes('/games/B/details?')) return empty('B', { finished: true, loaded: true });
if (url.includes('/games/B/details/sync')) return details('B', 2);
} });
h.state.selectedGame = null;
h.state.selectedMatchDetails = null;
h.state.games.items = [compact('B')];
await h.c.openGame('B');
await tick();
assert.equal(h.state.selectedGame.home.players[0]?.id, 'B-home-v2', 'An empty finished cache was mistaken for a complete roster');
assert.equal(h.data().hockey.away.players[0]?.id, 'B-away-v2');
assert.equal(h.requests.filter(row => row.url.includes('/details/sync')).length, 1);
assert.equal(h.timers.size, 0, 'A finished repaired match started endless polling');
};
cases.finished_complete_cache_remains_offline = async () => {
const h = menuHarness({ initialId: '' });
h.state.selectedGame = null;
h.state.selectedMatchDetails = null;
await h.c.openGame('B');
await tick();
assert.equal(h.data().hockey.home.players[0]?.id, 'B-home-v1');
assert.equal(h.requests.filter(row => row.url.includes('/details/sync') || row.url.includes('/live')).length, 0);
assert.equal(h.timers.size, 0);
};
cases.unchanged_newer_details_preserve_new_score = async () => {
const h = menuHarness({ finished: false, onRequest: url => {
if (url.includes('/live')) {
const value = details('A', 2, { finished: false });
value.game.home.score = 7;
value.game.away.score = 6;
return { ...value, changed: false };
}
} });
await h.c.pollSelectedGame();
assert.equal(h.state.selectedGame.home.players[0]?.id, 'A-home-v2');
assert.equal(h.data().hockey.home.score, 7);
assert.equal(h.data().hockey.away.score, 6);
};
cases.unchanged_incomplete_live_preserves_loaded_roster = async () => {
const h = menuHarness({ finished: false, onRequest: url => {
if (url.includes('/live')) return { ...empty('A'), changed: false };
} });
await h.c.pollSelectedGame();
assert.equal(h.state.selectedGame.home.players[0]?.id, 'A-home-v1');
assert.equal(h.data().hockey.away.players[0]?.id, 'A-away-v1');
assert.equal(h.state.selectedMatchDetails.loaded, true);
};
cases.restore_empty_finished_cache_repairs = async () => {
const h = menuHarness({ onRequest: url => {
if (url.includes('/games/A/details?')) return empty('A', { finished: true, loaded: true });
if (url.includes('/games/A/details/sync')) return details('A', 2);
} });
h.state.selectedGame = null;
h.state.selectedMatchDetails = null;
await h.c.restoreSelection();
await tick();
assert.equal(h.data().hockey.home.players[0]?.id, 'A-home-v2');
assert.equal(h.requests.filter(row => row.url.includes('/details/sync')).length, 1);
assert.equal(h.timers.size, 0);
};
cases.late_unchanged_roster_cannot_cross_selection = async () => {
const pending = defer();
const h = menuHarness({ finished: false, onRequest: url => url.includes('/live') ? pending.promise : undefined });
const work = h.c.pollSelectedGame();
h.c.stopSelectedGamePolling();
h.state.gameOpenGeneration += 1;
h.state.selectedGameId = 'B';
h.state.selectedGame = details('B', 3).game;
h.state.selectedMatchDetails = details('B', 3).details;
const count = h.events.length;
pending.resolve({ ...details('A', 4, { finished: false }), changed: false });
await work;
assert.equal(h.state.selectedGame.home.players[0].id, 'B-home-v3');
assert.equal(h.events.length, count);
};
(async () => {
const name = process.argv[2];
assert.ok(cases[name], 'Unknown case ' + name);
await cases[name]();
console.log('PASS first roster selection: ' + name);
})().catch(error => { console.error(error); process.exitCode = 1; });