Skip to content

Commit 59bde8f

Browse files
authored
Merge pull request #300 from jia1022/hide-leetcode-line-unless-toggled
Hide LeetCode names in interviews by default
2 parents 2f1907b + 417da54 commit 59bde8f

6 files changed

Lines changed: 112 additions & 34 deletions

File tree

‎tests/browser/hide-examples.test.js‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
launchChromium,
1414
startStaticServer,
1515
} from "./source.js";
16+
import { SHOW_SOURCES_KEY } from "../../web/lib.js";
1617

1718
let browser = null;
1819
let server = null;
@@ -79,6 +80,54 @@ test("the worked examples can be hidden, and stay hidden on the next visit", asy
7980
}
8081
});
8182

83+
test("the interview hides the published problem name when the toggle is off", async (t) => {
84+
if (!browser) return t.skip("playwright chromium unavailable");
85+
const page = await browser.newPage();
86+
try {
87+
await page.goto(`${base}/interview.html?problem=chargeback-pair-match`, {
88+
waitUntil: "domcontentloaded",
89+
});
90+
await page.evaluate(
91+
(key) => localStorage.setItem(key, "0"),
92+
SHOW_SOURCES_KEY,
93+
);
94+
await page.reload({ waitUntil: "domcontentloaded" });
95+
await page
96+
.locator("#problem-panel .problem-detail")
97+
.waitFor({ state: "attached" });
98+
assert.doesNotMatch(
99+
await page.locator("#problem-panel").textContent(),
100+
/LeetCode:/,
101+
);
102+
} finally {
103+
await page.close();
104+
}
105+
});
106+
107+
test("the interview shows the published problem name when the toggle is on", async (t) => {
108+
if (!browser) return t.skip("playwright chromium unavailable");
109+
const page = await browser.newPage();
110+
try {
111+
await page.goto(`${base}/interview.html?problem=chargeback-pair-match`, {
112+
waitUntil: "domcontentloaded",
113+
});
114+
await page.evaluate(
115+
(key) => localStorage.setItem(key, "1"),
116+
SHOW_SOURCES_KEY,
117+
);
118+
await page.reload({ waitUntil: "domcontentloaded" });
119+
await page
120+
.locator("#problem-panel .problem-detail")
121+
.waitFor({ state: "attached" });
122+
assert.match(
123+
await page.locator("#problem-panel").textContent(),
124+
/LeetCode: Two Sum/,
125+
);
126+
} finally {
127+
await page.close();
128+
}
129+
});
130+
82131
// The Add a case placeholder is the judge's first input, a worked case in its
83132
// own right, so hiding the examples has to hide it too.
84133
test("hiding the examples also hides the Add a case placeholder", async (t) => {

‎tests/browser/render.test.js‎

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,17 +1105,20 @@ test("report markup marks a no-hire and an empty editor", () => {
11051105
});
11061106

11071107
test("problem markup escapes every field a problem carries", () => {
1108-
const body = problemMarkup({
1109-
source: "<script>t</script>",
1110-
brief: ["<script>s</script>"],
1111-
examples: [
1112-
{
1113-
input: "<script>i</script>",
1114-
output: "<script>o</script>",
1115-
explanation: "<script>x</script>",
1116-
},
1117-
],
1118-
});
1108+
const body = problemMarkup(
1109+
{
1110+
source: "<script>t</script>",
1111+
brief: ["<script>s</script>"],
1112+
examples: [
1113+
{
1114+
input: "<script>i</script>",
1115+
output: "<script>o</script>",
1116+
explanation: "<script>x</script>",
1117+
},
1118+
],
1119+
},
1120+
{ showSources: true },
1121+
);
11191122

11201123
assert.doesNotMatch(
11211124
body,
@@ -1143,19 +1146,39 @@ test("problem markup shows the scenario and leaves the limits to be asked for",
11431146
});
11441147

11451148
test("problem markup names the published title once, small, and only when it has one", () => {
1146-
const body = problemMarkup({
1147-
source: "Two Sum",
1148-
brief: ["Match the disputed charge."],
1149-
examples: [{ input: "a", output: "b" }],
1150-
});
1149+
const body = problemMarkup(
1150+
{
1151+
source: "Two Sum",
1152+
brief: ["Match the disputed charge."],
1153+
examples: [{ input: "a", output: "b" }],
1154+
},
1155+
{ showSources: true },
1156+
);
11511157
assert.equal(body.match(/Two Sum/g).length, 1);
11521158
assert.match(body, /<p class="problem-source">LeetCode: Two Sum<\/p>/);
11531159

1154-
const unnamed = problemMarkup({
1160+
const unnamed = problemMarkup(
1161+
{
1162+
brief: ["one"],
1163+
examples: [{ input: "a", output: "b" }],
1164+
},
1165+
{ showSources: true },
1166+
);
1167+
assert.doesNotMatch(unnamed, /problem-source|LeetCode|undefined/);
1168+
});
1169+
1170+
test("problem markup hides published titles by default", () => {
1171+
const problem = {
1172+
source: "Two Sum",
11551173
brief: ["one"],
11561174
examples: [{ input: "a", output: "b" }],
1157-
});
1158-
assert.doesNotMatch(unnamed, /problem-source|LeetCode|undefined/);
1175+
};
1176+
1177+
assert.doesNotMatch(problemMarkup(problem), /LeetCode:/);
1178+
assert.match(
1179+
problemMarkup(problem, { showSources: true }),
1180+
/LeetCode: Two Sum/,
1181+
);
11591182
});
11601183

11611184
test("problem markup omits the explanation line rather than printing undefined", () => {

‎web/app.js‎

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { FRAMEWORKS, codingLoop, interviewMode, languageLabel } from "./lib.js";
1+
import {
2+
FRAMEWORKS,
3+
SHOW_SOURCES_KEY,
4+
codingLoop,
5+
interviewMode,
6+
languageLabel,
7+
} from "./lib.js";
28
import {
39
clearReportHistory,
410
deleteReport,
@@ -156,13 +162,8 @@ for (const card of cards) mark(card.button, false);
156162
// The published names are off unless the candidate turns them on, and the
157163
// choice is theirs to keep between visits. A convenience, so storage that
158164
// throws (a private window, blocked site data) just means off.
159-
const SHOW_SOURCES_KEY = "codetrial.showProblemSources";
160165
const showSources = document.querySelector("#show-sources");
161-
try {
162-
showSources.checked = localStorage.getItem(SHOW_SOURCES_KEY) === "1";
163-
} catch {
164-
/* off */
165-
}
166+
showSources.checked = readStored(SHOW_SOURCES_KEY) === "1";
166167
// The published names are not in the page: they arrive with the map, fetched
167168
// the first time the candidate turns them on.
168169
const applySources = async () => {
@@ -185,11 +186,7 @@ const applySources = async () => {
185186
};
186187
showSources.addEventListener("change", () => {
187188
void applySources();
188-
try {
189-
localStorage.setItem(SHOW_SOURCES_KEY, showSources.checked ? "1" : "0");
190-
} catch {
191-
/* the page still shows what was chosen */
192-
}
189+
writeStored(SHOW_SOURCES_KEY, showSources.checked ? "1" : "0");
193190
});
194191
void applySources();
195192
const levels = [...document.querySelectorAll('[name="difficulty"]')];

‎web/interview.js‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import {
7676
roomInterviewer,
7777
sanitizeReport,
7878
sessionReport,
79+
SHOW_SOURCES_KEY,
7980
testPayload,
8081
timeWarningPayload,
8182
topics,
@@ -2036,6 +2037,7 @@ function renderProblem() {
20362037
recordStage();
20372038
nodes.problemPanel.innerHTML = problemMarkup(problem, {
20382039
examples: !nodes.hideExamples.checked,
2040+
showSources: readStored(SHOW_SOURCES_KEY) === "1",
20392041
});
20402042
}
20412043

‎web/lib.js‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ const HTML_ESCAPES = {
99
"'": "&#39;",
1010
};
1111

12+
export const SHOW_SOURCES_KEY = "codetrial.showProblemSources";
13+
1214
export function escapeHtml(value) {
1315
return String(value).replace(/[&<>"']/g, (char) => HTML_ESCAPES[char]);
1416
}

‎web/render.js‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,10 +205,15 @@ function sourceEventMarkup(event) {
205205
// No constraints section and no source statement: the page carries the scenario
206206
// the interview poses, and the limits and edge-case policies are what the
207207
// candidate asks Jim for, the way they would ask a person. The published title
208-
// is named once, small, so the problem can be found again after the interview.
208+
// is left out unless showSources is set, the lobby's "Show the LeetCode
209+
// problem" choice; then it is named once, small, so the problem can be found
210+
// again after the interview.
209211
// The worked examples can be left out, so the Example step starts from cases
210212
// the candidate proposes instead of ones already on screen.
211-
export function problemMarkup(problem, { examples = true } = {}) {
213+
export function problemMarkup(
214+
problem,
215+
{ examples = true, showSources = false } = {},
216+
) {
212217
const example = (example, index) => `
213218
<section>
214219
<h2>Example ${index + 1}</h2>
@@ -224,7 +229,7 @@ export function problemMarkup(problem, { examples = true } = {}) {
224229
return `
225230
<div class="problem-detail">
226231
<p class="interview-kicker">Interview exercise</p>
227-
${problem.source ? `<p class="problem-source">LeetCode: ${escapeHtml(problem.source)}</p>` : ""}
232+
${showSources && problem.source ? `<p class="problem-source">LeetCode: ${escapeHtml(problem.source)}</p>` : ""}
228233
${problem.requestedPage ? `<p class="muted small">The link asked for an exercise this bank does not have, so this is the default exercise.</p>` : ""}
229234
${problem.brief.map((text) => `<p>${escapeHtml(text)}</p>`).join("")}
230235
${

0 commit comments

Comments
 (0)