Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 70 additions & 6 deletions tests/browser/lobby.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1050,20 +1050,84 @@ lobbyTest(
);

lobbyTest(
"changing difficulty clears the previous random-pick exclusion",
"a drawn problem survives a difficulty change that still includes it",
async (page) => {
await page.addInitScript(() => {
Math.random = () => 0;
});
const before = await lobby(page);
await lobby(page);
await page.click("#random-problem");
assert.notEqual((await snapshot(page)).card, before.card);
await setLevel(page, "Hard", true);
await setLevel(page, "Hard", false);
assert.equal((await snapshot(page)).card, before.card);
const drawn = await snapshot(page);
const level = (await cardInfo(page, drawn.card)).level;

const others = ["Easy", "Medium", "Hard"].filter((name) => name !== level);
for (const other of others) {
await setLevel(page, other, true);
assert.equal(
(await snapshot(page)).card,
drawn.card,
`checking ${other} redrew a problem it did not hide`,
);
}
// Returning from the media preflight re-runs the recommendation, and the
// roll that drew this problem names a different one under the new filter.
await restore(page);
await awaitReady(page);
assert.equal(
(await snapshot(page)).card,
drawn.card,
"a kept problem moved when the page was restored",
);
await setLevel(page, others[0], false);
assert.equal(
(await snapshot(page)).card,
drawn.card,
`unchecking ${others[0]} redrew a problem it did not hide`,
);

await setLevel(page, level, false);
const redrawn = await snapshot(page);
assert.notEqual(redrawn.card, drawn.card, "a hidden problem stayed picked");
const info = await cardInfo(page, redrawn.card);
assert.equal(info.level, others[1]);
assert.equal(info.hidden, false);
},
);

// Widening the filter keeps the card on screen, but it was drawn from the
// reports in hand, so refreshed ones that pass it decide again. The usual order
// widens before leaving for the interview; the other widens inside the reload,
// while the reports in hand still belong to the page before the candidate left.
for (const [when, widenFirst] of [
["before the interview", true],
["while the history reloads", false],
]) {
lobbyTest(
`a drawn problem kept ${when} is redrawn from the history it comes back to`,
async (page) => {
const shown = (await lobby(page)).card;
const widen = async () => {
await setLevel(page, "Hard", true);
assert.equal((await snapshot(page)).card, shown);
};

if (widenFirst) await widen();
const release = holdHistory();
reports = [hired(shown)];
await restore(page);
if (!widenFirst) await widen();

release();
await awaitReady(page);
assert.notEqual(
(await snapshot(page)).card,
shown,
"a problem the refreshed history passed stayed recommended",
);
},
);
}

lobbyTest(
"Random problem waits for history on load and browser restore",
async (page) => {
Expand Down
30 changes: 25 additions & 5 deletions web/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ let reports = [];
/// The practice focus the share box currently refers to.
let sharedFocus = null;
let manualProblem = false;
/// The reports a drawn problem kept across a difficulty change was drawn from,
/// serialized, or null when nothing is kept. Not `manualProblem`: a hand pick
/// stands for good, while a kept draw stands only until the history it was
/// drawn from changes.
let keptDraw = null;
let manualDuration = false;
let manualDifficulty = false;
let historyReady = false;
Expand Down Expand Up @@ -176,6 +181,7 @@ for (const card of cards) {
nodes.randomProblem.addEventListener("click", () => {
if (!historyReady || accountUpdatePending() || deletingReports) return;
manualProblem = false;
keptDraw = null;
roll = Math.random();
avoidedProblem = problem?.id;
applyDifficulties();
Expand All @@ -192,11 +198,20 @@ for (const input of levels) {
}
manualDifficulty = true;
applyDifficulties();
// A pick made by hand survives a filter that still includes it. Only one
// that now hides it hands the choice back to the lobby.
if (manualProblem && selectedDifficulties().has(problem?.difficulty))
// A problem survives a filter that still includes it, whether it was
// picked by hand or drawn: the checkbox answered which levels to offer,
// not whether to throw away the problem on screen. Only a filter that now
// hides it hands the choice back to the lobby.
if (selectedDifficulties().has(problem?.difficulty)) {
// Kept, a drawn problem is no longer what `roll` draws under this
// filter, so the next `settle` would swap it. Held against the reports
// in hand, which while the history is still loading are the ones about
// to be replaced, so `settle` redraws then.
if (!manualProblem) keptDraw = JSON.stringify(reports);
return;
}
manualProblem = false;
keptDraw = null;
roll = Math.random();
avoidedProblem = undefined;
// Not before the reports are in. Recommending from an empty history here
Expand Down Expand Up @@ -535,6 +550,10 @@ function refreshHistory() {
/// describing history the page had already replaced.
function settle() {
historyReady = true;
// Refreshed reports that differ from the ones a kept draw came from may have
// just recorded it as passed, so the lobby draws again.
if (keptDraw !== null && keptDraw !== JSON.stringify(reports))
keptDraw = null;
// The level suggestion only applies when the candidate has not already said
// what they want. Moving their checkboxes would also hide the card they just
// picked.
Expand Down Expand Up @@ -880,8 +899,9 @@ function applyDifficulties() {
function recommend(note = "") {
// A candidate who picked a card has answered the question this line asks, so
// it stays answered. Naming a different problem here contradicted the card
// they had just selected.
if (manualProblem) return;
// they had just selected. A kept draw stands the same way until `settle`
// drops it.
if (manualProblem || keptDraw !== null) return;
const choice = pickProblem(
cards,
selectedDifficulties(),
Expand Down