133 lines
5.8 KiB
JavaScript
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; });
|