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
183 changes: 183 additions & 0 deletions tests/browser/candidate-cases.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -692,3 +692,186 @@ test("C stays unavailable until a function-style judge has loaded", async (t) =>
await new Promise((closed) => enabled.server.close(closed));
}
});

const CASES_KEY = "codetrial.candidateCases.chargeback-pair-match";

async function addCase(page, input) {
const count = await page.locator("#candidate-case-list > li").count();
await page.evaluate((value) => {
document.querySelector("#candidate-case-input").value = value;
document.querySelector("#candidate-case-add").click();
}, input);
await page.waitForFunction(
(n) => document.querySelector("#candidate-case-list").children.length === n,
count + 1,
);
const stored = await page.evaluate(
(k) => sessionStorage.getItem(k),
CASES_KEY,
);
assert.ok(stored?.includes(input));
}

test("ending an interview clears candidate cases from session storage", async (t) => {
if (!browser) return t.skip("playwright chromium unavailable");
const page = await browser.newPage();
try {
await page.goto(`${base}/interview.html?problem=chargeback-pair-match`, {
waitUntil: "domcontentloaded",
});
await candidateCasesReady(page);
await addCase(page, "[[2,7,11,15],9]");

await page.evaluate(() => document.querySelector("#end").click());
await page.waitForFunction(
() =>
sessionStorage.getItem(
"codetrial.candidateCases.chargeback-pair-match",
) === null,
);
} finally {
await page.close();
}
});

test("rendering a report clears candidate cases from session storage", async (t) => {
if (!browser) return t.skip("playwright chromium unavailable");
const page = await browser.newPage();
try {
await page.goto(`${base}/interview.html?problem=chargeback-pair-match`, {
waitUntil: "domcontentloaded",
});
await candidateCasesReady(page);
await addCase(page, "[[2,7,11,15],9]");

await page.evaluate(() => {
document.querySelector("#force-report").click();
});
await page.waitForFunction(
() =>
sessionStorage.getItem(
"codetrial.candidateCases.chargeback-pair-match",
) === null,
);
} finally {
await page.close();
}
});

test("in-flight candidate case additions are refused when interview leaves live", async (t) => {
if (!browser) return t.skip("playwright chromium unavailable");
const page = await browser.newPage();
let releaseJudge = () => {};
try {
releaseJudge = await holdJudge(page);
await page.goto(`${base}/interview.html?problem=chargeback-pair-match`, {
waitUntil: "domcontentloaded",
});
await page.waitForFunction(
() => !document.querySelector("#candidate-case-add").disabled,
);
await page.evaluate(() => {
document.querySelector("#candidate-case-input").value = "[[2,7,11,15],9]";
document.querySelector("#candidate-case-add").click();
document.querySelector("#end").click();
});
releaseJudge();
await page.waitForFunction(
() => !document.querySelector("#candidate-case-add").disabled,
);
const stored = await page.evaluate(() =>
sessionStorage.getItem("codetrial.candidateCases.chargeback-pair-match"),
);
assert.equal(stored, null);
const casesCount = await page.evaluate(
() => document.querySelector("#candidate-case-list").children.length,
);
assert.equal(casesCount, 0);
const status = await page.evaluate(
() => document.querySelector("#candidate-case-status").textContent,
);
assert.equal(status, "The interview has ended.");
} finally {
releaseJudge();
await page.close();
}
});

test("ending an interview empties the candidate case list", async (t) => {
if (!browser) return t.skip("playwright chromium unavailable");
const page = await browser.newPage();
try {
await page.goto(`${base}/interview.html?problem=chargeback-pair-match`, {
waitUntil: "domcontentloaded",
});
await candidateCasesReady(page);
await addCase(page, "[[2,7,11,15],9]");
await addCase(page, "[[1,2,3],5]");

await page.evaluate(() => document.querySelector("#end").click());
await page.waitForFunction(
() =>
document.querySelector("#candidate-case-list").children.length === 0,
);
const count = await page.locator("#candidate-case-list > li").count();
assert.equal(count, 0);
} finally {
await page.close();
}
});

test("candidate cases do not persist across new interview sessions in the same tab", async (t) => {
if (!browser) return t.skip("playwright chromium unavailable");
const page = await browser.newPage();
try {
await page.goto(`${base}/interview.html?problem=chargeback-pair-match`, {
waitUntil: "domcontentloaded",
});
await candidateCasesReady(page);
await addCase(page, "[[2,7,11,15],9]");

await page.goto(`${base}/`, { waitUntil: "domcontentloaded" });
await page.goto(`${base}/interview.html?problem=chargeback-pair-match`, {
waitUntil: "domcontentloaded",
});
await candidateCasesReady(page);

const count = await page.locator("#candidate-case-list > li").count();
assert.equal(count, 0);
const stored = await page.evaluate(
(k) => sessionStorage.getItem(k),
CASES_KEY,
);
assert.equal(stored, null);
} finally {
await page.close();
}
});

test("candidate cases are preserved on page reload", async (t) => {
if (!browser) return t.skip("playwright chromium unavailable");
const page = await browser.newPage();
try {
await page.goto(`${base}/interview.html?problem=chargeback-pair-match`, {
waitUntil: "domcontentloaded",
});
await candidateCasesReady(page);
await addCase(page, "[[2,7,11,15],9]");

await page.reload({ waitUntil: "domcontentloaded" });
await page.waitForFunction(
() =>
document.querySelector("#candidate-case-list").children.length === 1,
);

const count = await page.locator("#candidate-case-list > li").count();
assert.equal(count, 1);
const stored = await page.evaluate(
(k) => sessionStorage.getItem(k),
CASES_KEY,
);
assert.ok(stored?.includes("[[2,7,11,15],9]"));
} finally {
await page.close();
}
});
29 changes: 26 additions & 3 deletions web/interview.js
Original file line number Diff line number Diff line change
Expand Up @@ -2623,10 +2623,20 @@ async function runTests() {
}

async function initializeCandidateCases() {
const isReload =
performance.getEntriesByType("navigation")[0]?.type === "reload";
const activeSessionKey = "codetrial.activeSession";
const wasActive = Boolean(readStored(activeSessionKey, tabStorage));
writeStored(activeSessionKey, "1", tabStorage);

if (!isReload && wasActive) {
writeStored(candidateCaseStorageKey, "", tabStorage);
}
try {
const saved = JSON.parse(
readStored(candidateCaseStorageKey, tabStorage) || "[]",
);
const saved =
!isReload && wasActive
? []
: JSON.parse(readStored(candidateCaseStorageKey, tabStorage) || "[]");
state.candidateCases = Array.isArray(saved)
? saved.slice(0, CANDIDATE_CASE_LIMIT)
: [];
Expand Down Expand Up @@ -2675,6 +2685,10 @@ async function addCandidateCaseNow() {
}
try {
const spec = await loadJudge(problem.page);
if (state.phase !== "live") {
nodes.candidateCaseStatus.textContent = "The interview has ended.";
return "refused";
}
const input = parseCandidateCase(spec, nodes.candidateCaseInput.value);
const expectedText = nodes.candidateCaseExpected.value.trim();
const expected = expectedText ? JSON.parse(expectedText) : undefined;
Expand Down Expand Up @@ -2716,6 +2730,13 @@ function renderCandidateCases() {
}
}

function clearCandidateCases() {
writeStored(candidateCaseStorageKey, "", tabStorage);
state.candidateCases = [];
renderCandidateCases();
nodes.candidateCaseStatus.textContent = "The interview has ended.";
}

/// Remove a saved case by position. The results panel is reset rather than
/// kept: runBrowserTests labels candidate rows by the position they held at
/// run time, and a removal renumbers every later case, so a kept panel would
Expand Down Expand Up @@ -2920,6 +2941,7 @@ function endInterview(reason) {
clearTimeout(codePublishTimer);
codePublishTimer = null;
pendingLanguagePublish = null;
clearCandidateCases();
nodes.end.disabled = true;
nodes.ending.hidden = false;
// Only the interviewer answers `end_interview`, so a report is coming only
Expand Down Expand Up @@ -3113,6 +3135,7 @@ function renderReport() {
stopAvatar();
stopLocalMedia();
stopEndingClock();
clearCandidateCases();
nodes.ending.hidden = true;
nodes.report.hidden = false;
nodes.report.innerHTML = reportMarkup({
Expand Down