Skip to content

OA Simulator

The OA (Online Assessment) Simulator reproduces the format real companies use for first-round screening — because the actual value to a student isn't just "practice questions," it's practicing under the exact structural constraints of a D.E. Shaw or Flipkart OA: fixed time budgets per section, no going back, mixed question types in one sitting.

The three routes

RouteComponentRole
/oa/buildOABuildercompose an exam: sections, question pools, timers, marking scheme
/oa/examOAExamthe timed attempt itself
/oa/resultOAResultpost-exam analytics, per-question review, Regrade with AI

Templates persist to users/{uid}/oaTemplates; a template can also be published to the world-readable publicOAs/{templateId} collection so one authored exam is attemptable by anyone. Attempts live in users/{uid}/oaAttempts.

What it reproduces

  • Configurable marking schemes — per-difficulty score tiers and negative marking, matching how real OAs actually score (verified against the real D.E. Shaw format: 3 sequential coding problems with individual timers, 10 technical MCQs, 10 aptitude, 0.25 negative marking, no backtracking).
  • Per-question and per-section timer modes with strict sequential flow — once a section's time is up, or you move to the next section, you cannot return. The transition is one-way and locked; NextSectionConfirm gates it explicitly so it's never accidental.
  • Mixed question types in one exam — a single attempt can pull from DSA (Judge0 runner), CS Fundamentals (AI-graded concept questions), and Aptitude (MCQ/numeric), because real OAs mix all three.

Timer integrity

The timer's source of truth is the server-recorded startedAt timestamp in Firestore, not client-side state. If the timer lived only in React state, a refresh — accidental or intentional — would reset it. Deriving elapsed time from a server timestamp on every load closes that gap, and Firebase Auth persistence is indexedDBLocalPersistence specifically so a refresh doesn't also log the user out mid-exam (see Authentication).

Per-question timers store their own started/locked markers on the attempt document, so even question-level countdowns survive a reload with no drift beyond network latency.

Anti-cheat

useAntiCheat + OAPermissionsGate detect tab-switching and enforce fullscreen during a timed attempt. This is a UX/integrity feature, not a security boundary — it's explicitly scoped as discouraging casual cheating during self-practice, not as a proctoring product. That's an honest distinction worth drawing: "how do you stop cheating" has a very different real answer for a practice tool than for a company's hiring pipeline.

Submission handling during the exam

Coding submissions inside an OA don't block the UI on judging. Each submit writes to users/{uid}/submissions with an oaAttemptId tag, and the background judging Worker picks it up on its cron, claims it with an updateTime precondition (so two Worker invocations can't double-judge), and calls the same Pages Function judging path the interactive runner uses — one judging implementation, not two. The student keeps moving; verdicts land on the attempt via onSnapshot. Details in Cloudflare Workers.

AI-graded questions

CS Fundamentals concept questions (evaluationMethod: ai) route through ConceptPracticeRunner, which grades free-text answers against the rubric authored in the question's <!--RUBRIC--> / <!--EXPECTED--> sections via functions/grade-concept.js. Because a single AI provider going down mid-exam would be a bad experience, grading goes through the nine-provider fallback chain (Gemini → Groq → Cerebras → Mistral → NVIDIA → GitHub Models → Cohere → Ollama → OpenRouter) — if one fails or rate-limits, the next is tried automatically, invisibly to the student. See AI Pipeline.

MCQ answers are graded deterministically — no model involved — the AI layer is only for free-text.

Result analytics & Regrade

OAResult re-fetches the full (heavy) body of each attempted question — via the same on-demand getQuestion() path used everywhere — to build topic-wise weak-area breakdowns after the exam ends, rather than carrying every question's full content through the exam session. The scoring itself is computed client-side by oaEngine.js#computeAttemptResult from the raw per-question records; the Worker judges, the result page scores.

The Regrade with AI button re-runs grading for AI-evaluated answers after the fact — useful when a provider degraded mid-exam or a rubric was updated — writing the fresh verdicts back onto the attempt.

Bugs this section has already paid for

  • undefined field writes — Firestore rejects documents containing undefined. Attempt writes now strip/default every optional field before setDoc; this was a live failure, not a lint nit.
  • Light-mode CSS conflicts — exam chrome was originally styled against dark tokens only; the fix moved every hardcoded color to the shared CSS variables.

Both are catalogued with their symptoms in Troubleshooting.

Ediky Workflow — internal engineering documentation.