/* All working test components. Each exports a key in window.TestComponents */
const TestComponents = {};
/* ---------- Text Input ---------- */
TestComponents["text-input"] = function TextInputTest({ resetKey }) {
const [value, setValue] = useState("");
const [label, setLabel] = useState("Button");
useEffect(() => { setValue(""); setLabel("Button"); }, [resetKey]);
return (
setValue(e.target.value)}
/>
setLabel(value || "Button")}
style={{ width: "100%" }}
>
{label}
);
};
/* ---------- Client Side Delay ---------- */
TestComponents["client-side-delay"] = function ClientDelayTest({ resetKey }) {
const [phase, setPhase] = useState("idle"); // idle | running | done
useEffect(() => { setPhase("idle"); }, [resetKey]);
const start = () => {
setPhase("running");
setTimeout(() => setPhase("done"), 5000);
};
return (
{phase === "running" ? <> Computing…> : "Run heavy computation"}
{phase === "done" && (
Computation complete · 42 ops finished
)}
);
};
/* ---------- AJAX Data (real fetch — visible in DevTools Network) ---------- */
TestComponents["ajax-data"] = function AjaxDataTest({ resetKey }) {
const [phase, setPhase] = useState("idle"); // idle | loading | done | error
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect(() => { setPhase("idle"); setData(null); setError(null); }, [resetKey]);
const fetchData = async () => {
setPhase("loading");
setError(null);
setData(null);
try {
// POST a fake records payload to httpbin's delay endpoint. The
// server holds the response for ~3 seconds and then echoes the
// body back under `json`, giving us:
// • a real, slow network request candidates can intercept
// • a real records array in the response they can count.
const RECORD_COUNT = 24;
const records = Array.from({ length: RECORD_COUNT }, (_, i) => ({
id: i + 1,
name: `Record #${i + 1}`,
value: Math.floor(Math.random() * 1000),
}));
const res = await fetch("https://httpbin.org/delay/3", {
method: "POST",
headers: { "Content-Type": "application/json" },
cache: "no-store",
body: JSON.stringify({ records }),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = await res.json();
// httpbin's /delay endpoint sometimes echoes the body under `json`
// (already parsed) and sometimes only as a raw string in `data`.
// Try both so the record count always reflects what the server
// actually returned.
let returned = [];
if (Array.isArray(json?.json?.records)) {
returned = json.json.records;
} else if (typeof json?.data === "string" && json.data.length) {
try {
const parsed = JSON.parse(json.data);
if (Array.isArray(parsed?.records)) returned = parsed.records;
} catch (_) { /* leave returned as [] */ }
}
setData({ count: returned.length, records: returned });
setPhase("done");
} catch (e) {
setError(e.message || "Network error");
setPhase("error");
}
};
return (
{phase === "loading" ? <> Fetching…> : "Fetch data"}
{phase === "done" && data && (
✓ Loaded {data.count} records · 200 OK
)}
{phase === "error" && (
✗ Request failed: {error}
)}
);
};
/* ---------- Scrollbars ---------- */
TestComponents["scrollbars"] = function ScrollbarsTest({ resetKey }) {
const [clicked, setClicked] = useState(false);
const containerRef = useRef(null);
useEffect(() => {
setClicked(false);
if (containerRef.current) containerRef.current.scrollTo(0, 0);
}, [resetKey]);
return (
setClicked(true)}
>
Click me
{clicked && (
✓ Target reached and clicked.
)}
);
};
/* ---------- Dynamic Table ---------- */
TestComponents["dynamic-table"] = function DynamicTableTest({ resetKey }) {
const rows = [
{ Name: "Chrome", CPU: "12.3%", Memory: "1.2 GB", Network: "245 KB/s", Battery: "—" },
{ Name: "Firefox", CPU: "8.1%", Memory: "896 MB", Network: "112 KB/s", Battery: "—" },
{ Name: "Safari", CPU: "3.4%", Memory: "612 MB", Network: "0 KB/s", Battery: "—" },
{ Name: "Edge", CPU: "5.8%", Memory: "742 MB", Network: "82 KB/s", Battery: "—" },
{ Name: "Slack", CPU: "2.1%", Memory: "412 MB", Network: "14 KB/s", Battery: "—" },
];
const columnOrders = useMemo(() => [
["Name", "CPU", "Memory", "Network", "Battery"],
["Name", "Memory", "CPU", "Battery", "Network"],
["Name", "Battery", "Network", "CPU", "Memory"],
["Name", "Network", "CPU", "Battery", "Memory"],
], []);
const [orderIdx, setOrderIdx] = useState(0);
useEffect(() => {
setOrderIdx((i) => (i + 1) % columnOrders.length);
}, [resetKey, columnOrders.length]);
const cols = columnOrders[orderIdx];
const chrome = rows.find((r) => r.Name === "Chrome");
return (
Chrome CPU: {chrome.CPU}
{cols.map((c) => (
{c}
))}
{rows.map((r) => (
{cols.map((c) => (
{r[c]}
))}
))}
);
};
/* ---------- Progress Bar (no visible % label) ---------- */
TestComponents["progress-bar"] = function ProgressBarTest({ resetKey }) {
const [pct, setPct] = useState(0);
const [running, setRunning] = useState(false);
const tickRef = useRef(null);
useEffect(() => {
setPct(0);
setRunning(false);
if (tickRef.current) clearInterval(tickRef.current);
}, [resetKey]);
useEffect(() => () => { if (tickRef.current) clearInterval(tickRef.current); }, []);
const start = () => {
if (running || pct >= 100) return;
setRunning(true);
tickRef.current = setInterval(() => {
setPct((p) => {
if (p >= 100) { clearInterval(tickRef.current); return 100; }
return p + 1;
});
}, 100);
};
const stop = () => {
setRunning(false);
if (tickRef.current) clearInterval(tickRef.current);
};
const finishedAt75 = !running && pct === 75;
return (
= 100}>Start
Stop
{!running && pct > 0 && (
{finishedAt75 ? `✓ Stopped at exactly 75%.` : `✗ Stopped at ${pct}%. Target: 75%.`}
)}
);
};
/* ---------- Visibility ---------- */
TestComponents["visibility"] = function VisibilityTest({ resetKey }) {
const [hidden, setHidden] = useState(null);
useEffect(() => { setHidden(null); }, [resetKey]);
const targetStyles = {
null: {},
display: { display: "none" },
visibility: { visibility: "hidden" },
opacity: { opacity: 0, pointerEvents: "none" },
offscreen: { position: "absolute", left: "-9999px" },
"zero-size": { width: 0, height: 0, padding: 0, overflow: "hidden", border: 0 },
covered: {},
};
const techniques = [
{ key: "display", label: "Hide A" },
{ key: "visibility", label: "Hide B" },
{ key: "opacity", label: "Hide C" },
{ key: "offscreen", label: "Hide D" },
{ key: "zero-size", label: "Hide E" },
{ key: "covered", label: "Hide F" },
];
return (
I am the target
{hidden === "covered" && (
)}
{techniques.map((t) => (
setHidden(t.key)}
>
{t.label}
))}
setHidden(null)}>
Reset target
);
};
/* ---------- Overlapped Element (must click first) ---------- */
TestComponents["overlapped-element"] = function OverlappedTest({ resetKey }) {
const [email, setEmail] = useState("");
const [clickedField, setClickedField] = useState(false);
const [warn, setWarn] = useState(false);
const ref = useRef(null);
useEffect(() => {
setEmail(""); setClickedField(false); setWarn(false);
if (ref.current) ref.current.scrollTo(0, 0);
}, [resetKey]);
// Capture typing-without-click as failure: track keydown when not focused via click
const onKeyDown = (e) => {
if (!clickedField) {
e.preventDefault();
setWarn(true);
}
};
return (
{warn && (
✗ Typed without clicking the field first. Click the email field first, then type.
)}
{!warn && email && (
✓ Email captured: {email}
)}
);
};
/* ---------- Shadow DOM ---------- */
TestComponents["shadow-dom"] = function ShadowDomTest({ resetKey }) {
const hostRef = useRef(null);
const [outerEcho, setOuterEcho] = useState("");
useEffect(() => {
const host = hostRef.current;
if (!host) return;
let root = host.shadowRoot;
if (!root) root = host.attachShadow({ mode: "open" });
root.innerHTML = `
`;
const input = root.querySelector('[data-testid="shadow-input"]');
const submit = root.querySelector('[data-testid="shadow-submit"]');
const result = root.querySelector('[data-testid="shadow-result"]');
const handler = () => {
const v = input.value;
result.style.display = v ? "block" : "none";
result.textContent = v ? `✓ Submitted: ${v}` : "";
setOuterEcho(v);
};
submit.addEventListener("click", handler);
return () => submit.removeEventListener("click", handler);
}, [resetKey]);
return (
{outerEcho && (
Outer page received: {outerEcho}
)}
);
};
/* ---------- File Upload ---------- */
TestComponents["file-upload"] = function FileUploadTest({ resetKey }) {
const [files, setFiles] = useState([]);
const [dragging, setDragging] = useState(false);
const inputRef = useRef(null);
useEffect(() => { setFiles([]); if (inputRef.current) inputRef.current.value = ""; }, [resetKey]);
const addFiles = (list) => {
const next = Array.from(list).map((f) => ({ name: f.name, size: f.size, type: f.type || "application/octet-stream" }));
setFiles((prev) => [...prev, ...next]);
};
return (
{ e.preventDefault(); setDragging(true); }}
onDragLeave={() => setDragging(false)}
onDrop={(e) => { e.preventDefault(); setDragging(false); addFiles(e.dataTransfer.files); }}
>
Drop files here or click to browse
Any file type, any size
addFiles(e.target.files)}
/>
{files.length > 0 && (
{files.map((f, i) => (
{f.name}
{(f.size/1024).toFixed(1)} KB
))}
)}
);
};
/* ---------- Mystery Button (was Frames; no iframe hint anywhere on the page) ---------- */
TestComponents["mystery-button"] = function MysteryButtonTest({ resetKey }) {
const [counter, setCounter] = useState(0);
useEffect(() => { setCounter(0); }, [resetKey]);
const frameHtml = useMemo(() => {
return `
Click me from the parent
(button must be clicked to win)
`;
}, []);
useEffect(() => {
const onMsg = (e) => {
if (e.data?.type === "mystery-click") setCounter((n) => n + 1);
};
window.addEventListener("message", onMsg);
return () => window.removeEventListener("message", onMsg);
}, []);
const [k, setK] = useState(0);
useEffect(() => { setK((x) => x + 1); }, [resetKey]);
return (
0 ? "success" : "")}
data-testid="outer-counter"
data-count={counter}
>
Counter: {counter}
);
};
/* ---------- Disabled Input ---------- */
TestComponents["disabled-input"] = function DisabledInputTest({ resetKey }) {
const [enabled, setEnabled] = useState(false);
const [activating, setActivating] = useState(false);
const [value, setValue] = useState("");
useEffect(() => { setEnabled(false); setActivating(false); setValue(""); }, [resetKey]);
const activate = () => {
setActivating(true);
setTimeout(() => { setEnabled(true); setActivating(false); }, 4000);
};
return (
{activating ? <> Unlocking input…> :
enabled ? "Input unlocked" : "Activate input"}
setValue(e.target.value)}
/>
{enabled && value && (
✓ Captured: {value}
)}
);
};
/* ---------- Chart Interaction (Google Charts) ---------- */
TestComponents["chart-interaction"] = function ChartInteractionTest({ resetKey }) {
// Categories + expected values shown to the candidate
const data = useMemo(
() => [
{ category: "Mon", expected: 124 },
{ category: "Tue", expected: 218 },
{ category: "Wed", expected: 167 },
{ category: "Thu", expected: 305 },
{ category: "Fri", expected: 280 },
{ category: "Sat", expected: 92 },
{ category: "Sun", expected: 148 },
],
[]
);
const chartRef = useRef(null);
const [hoveredIdx, setHoveredIdx] = useState(null);
useEffect(() => {
// Load Google Charts once
const ensure = () => new Promise((resolve) => {
if (window.google && window.google.charts) return resolve();
const existing = document.querySelector('script[data-gcharts]');
if (existing) {
existing.addEventListener("load", resolve);
return;
}
const s = document.createElement("script");
s.src = "https://www.gstatic.com/charts/loader.js";
s.dataset.gcharts = "1";
s.onload = resolve;
document.head.appendChild(s);
});
let chart;
let cancelled = false;
ensure().then(() => {
if (cancelled) return;
window.google.charts.load("current", { packages: ["corechart"] });
window.google.charts.setOnLoadCallback(() => {
if (cancelled || !chartRef.current) return;
const dt = new window.google.visualization.DataTable();
dt.addColumn("string", "Day");
dt.addColumn("number", "Visitors");
dt.addRows(data.map((d) => [d.category, d.expected]));
chart = new window.google.visualization.ColumnChart(chartRef.current);
const styles = getComputedStyle(document.documentElement);
const accent = styles.getPropertyValue("--accent").trim() || "#4f46e5";
const fg = styles.getPropertyValue("--fg").trim() || "#09090b";
const muted = styles.getPropertyValue("--fg-muted").trim() || "#52525b";
const border = styles.getPropertyValue("--border").trim() || "#e4e4e7";
chart.draw(dt, {
backgroundColor: "transparent",
colors: [accent],
chartArea: { left: 56, top: 24, right: 16, bottom: 40, width: "100%", height: "100%" },
legend: { position: "none" },
tooltip: { isHtml: true, trigger: "focus" },
hAxis: { textStyle: { color: muted, fontSize: 12 } },
vAxis: { textStyle: { color: muted, fontSize: 12 }, gridlines: { color: border }, baselineColor: border },
height: 280,
animation: { startup: true, duration: 400, easing: "out" },
bar: { groupWidth: "60%" },
fontName: "Geist, ui-sans-serif, system-ui, sans-serif",
});
window.google.visualization.events.addListener(chart, "onmouseover", (e) => {
if (typeof e.row === "number") setHoveredIdx(e.row);
});
window.google.visualization.events.addListener(chart, "onmouseout", () => {
setHoveredIdx(null);
});
});
});
return () => { cancelled = true; if (chart) chart.clearChart && chart.clearChart(); };
}, [resetKey, data]);
return (
Expected values
{data.map((d, i) => (
{d.category}
{d.expected}
))}
{hoveredIdx != null
? <>Hovering {data[hoveredIdx].category} · value {data[hoveredIdx].expected} >
: <>Hover any column to read its value.>}
);
};
/* ---------- Auto Wait (strict: exactly one click at the right moment) ---------- */
TestComponents["auto-wait"] = function AutoWaitTest({ resetKey }) {
// stages: 0 idle, 1 visible+disabled (Loading), 2 enabled but Almost, 3 ready (Click me now)
const [stage, setStage] = useState(0);
const [clickCount, setClickCount] = useState(0);
const [verdict, setVerdict] = useState(null); // "success" | "early" | "extra"
const [log, setLog] = useState([]);
const timers = useRef([]);
useEffect(() => {
timers.current.forEach((t) => clearTimeout(t));
timers.current = [];
setStage(0); setClickCount(0); setVerdict(null); setLog([]);
}, [resetKey]);
useEffect(() => () => { timers.current.forEach((t) => clearTimeout(t)); }, []);
const pushLog = (t) => setLog((l) => [...l, `[${new Date().toLocaleTimeString()}] ${t}`]);
const start = () => {
timers.current.forEach((t) => clearTimeout(t));
timers.current = [];
setStage(0); setClickCount(0); setVerdict(null); setLog([]);
pushLog("sequence started");
timers.current.push(setTimeout(() => { setStage(1); pushLog("target rendered (Loading…)"); }, 2000));
timers.current.push(setTimeout(() => { setStage(2); pushLog("target enabled (Almost…)"); }, 4000));
timers.current.push(setTimeout(() => { setStage(3); pushLog("target ready (Click me now)"); }, 6000));
};
const onTargetClick = () => {
setClickCount((c) => {
const next = c + 1;
if (next === 1) {
if (stage < 3) {
setVerdict("early");
pushLog(`✗ clicked too early (stage ${stage})`);
} else {
setVerdict("success");
pushLog("✓ clicked at the right moment");
}
} else {
setVerdict("extra");
pushLog(`✗ extra click registered (total ${next})`);
}
return next;
});
};
const targetLabel =
stage === 0 ? "—" :
stage === 1 ? "Loading…" :
stage === 2 ? "Almost…" :
"Click me now";
return (
Start sequence
{stage >= 1 && (
= 3 ? "" : "btn-secondary")}
data-testid="target"
data-stage={stage}
disabled={stage < 2}
onClick={onTargetClick}
>
{targetLabel}
)}
{log.length > 0 && (
)}
{verdict === "success" && (
✓ Target clicked exactly once at the right moment.
)}
{verdict === "early" && (
✗ Clicked the target before it was ready. Wait for the label to read "Click me now".
)}
{verdict === "extra" && (
✗ Target was clicked more than once ({clickCount} total). It must be clicked exactly once.
)}
);
};
window.TestComponents = TestComponents;