/* Shared UI primitives + hooks */ const { useState, useEffect, useRef, useMemo, useCallback } = React; /* ---------- Icons (inline SVGs) ---------- */ const Icon = { Search: (p) => ( ), ChevronLeft: (p) => ( ), ChevronRight: (p) => ( ), ArrowRight: (p) => ( ), Upload: (p) => ( ), File: (p) => ( ), Logout: (p) => ( ), Check: (p) => ( ), X: (p) => ( ), }; /* ---------- Page-load delay (every nav) ---------- * Fires real chained httpbin.org/delay requests on every navigation so * the page-load duration is visible in the Network tab. httpbin caps * each /delay/N at 10 seconds, so longer loads are broken into chunks. * The final chunk is tagged `event=page-ready` — candidates can wait * for that specific request to know the page has finished loading. * A 5-second failsafe ensures we never stay stuck if the network is * blocked. */ function useArtificialDelay(key, ms = 700) { const [loading, setLoading] = useState(true); useEffect(() => { setLoading(true); if (ms <= 0) { setLoading(false); return; } const controller = new AbortController(); let cancelled = false; const failsafe = setTimeout(() => { if (!cancelled) setLoading(false); }, ms + 5000); (async () => { const totalChunks = Math.max(1, Math.ceil(ms / 10000)); let remaining = ms; for (let i = 1; i <= totalChunks; i++) { if (cancelled) return; const chunkMs = Math.min(remaining, 10000); const chunkSec = Math.max(1, Math.round(chunkMs / 1000)); const isLast = i === totalChunks; const event = isLast ? "page-ready" : "page-loading"; const url = `https://httpbin.org/delay/${chunkSec}` + `?event=${event}&chunk=${i}-of-${totalChunks}` + `&page=${encodeURIComponent(key)}`; try { await fetch(url, { signal: controller.signal, cache: "no-store" }); } catch (_) { // Aborted (navigated away) or offline — bail out; failsafe // timer above will release the loading state. return; } remaining -= chunkMs; } if (!cancelled) setLoading(false); })(); return () => { cancelled = true; controller.abort(); clearTimeout(failsafe); }; }, [key, ms]); return loading; } function PageLoader() { return (
); } function PageSkeleton({ kind = "topic" }) { if (kind === "home") { return (
{Array.from({ length: 9 }).map((_, i) => (
))}
); } return (
); } /* ---------- Test surface wrapper ---------- */ function TestSurface({ title = "Test surface", onReset, children }) { return (
{title}
{onReset && ( )}
{children}
); } /* ---------- Top bar ---------- */ function TopBar({ crumbs = [], user = "Candidate", onLogout, onNavHome }) { return (
Q QA Candidate Testing {crumbs.length > 0 && ( )}
Signed in as {user}
); } Object.assign(window, { Icon, useArtificialDelay, PageLoader, PageSkeleton, TestSurface, TopBar });