Skip to content

Airamhh/Exam-Simulator

Repository files navigation

Exam Simulator

A full-featured certification exam simulator built with React and Express. Designed to replicate the experience of Microsoft (and other vendor) certification exams with realistic question types, domain-weighted scoring, timed sessions, AI-powered analysis, and comprehensive review capabilities.

License React Express SQLite TypeScript


Table of Contents


Features

Question Types

  • Single Choice — Select exactly one correct answer from a list of options.
  • Multiple Choice — Select one or more correct answers (with visual count hint).
  • Drag & Order — Drag items into the correct sequence (powered by @dnd-kit).
  • Drag & Match — Drag options to match them with their corresponding targets.
  • Hotspot — Dropdown-based questions where each box has its own set of choices (simulates image-hotspot style questions from real exams).

Exam Modes

Mode Description
Exam Simulates a real certification exam. Answers are revealed only after finishing.
Practice Check correctness of each question on-demand with the "Solve" button.
Review All Browse all questions with answers visible. Ideal for study sessions.

Domain-Based Realistic Exams

  • Official Domain Weights — Questions are distributed according to official Microsoft exam domain percentages (e.g. AZ-204: Compute 25–30%, Storage 15–20%, Security 20–25%, Monitor 15–20%, Integration 15–20%).
  • Realistic Mode Toggle — One click to generate an exam that mirrors the real certification structure.
  • Domain-Grouped Topic Filter — Topics are organized under their parent domain in a collapsible tree, with weight badges and question counts.
  • Weighted Scoring — Final score (0–1000) uses domain weights so weaknesses in high-weight domains are penalized proportionally.

Session Management

  • Timer — Configurable countdown timer with low-time warnings (visual + animation).
  • Pagination — Adjustable questions per page (1, 5, 10, 20, 50).
  • Question Flagging — Flag questions for review before finishing.
  • Per-Question Notes — Write personal notes on any question during the exam.
  • Session Persistence — Active sessions are saved to SQLite and survive page reloads.
  • Auto-save — Answers are debounced and persisted every 500ms.

Results & Analytics

  • Weighted Score Calculation — Scaled to 0–1000 using official domain weights (pass threshold: 700).
  • Domain Breakdown — Per-domain accuracy with weight badges and color-coded progress bars.
  • Topic Breakdown — Per-topic accuracy for granular analysis.
  • Time Analytics — Total time and average time per question.
  • Exam History — Full history with filter by passed/failed and sort by date.
  • Review Mode — Post-exam review with filters for correct, incorrect, and flagged questions.

AI-Powered Features

  • Gemini AI Explanation — Get AI-generated explanations for any question via Google Gemini.
  • Question Analysis — AI-powered analysis and validation of question quality and correctness.
  • Technology Tag Management — View and edit technology tag content with AI assistance.

Question Bank Management

  • JSON Import/Export — Import question banks from JSON files; export for backup or sharing.
  • Inline Editing — Edit question text, explanations, and correct answers directly in the UI.
  • Search & Filter — Search questions by text and filter by topic/domain within any bank.
  • Technology Tags — 24 technology tags with content descriptions, linked via a relation table.
  • Edit Audit Trail — All question edits are tracked in a separate question_edits table.

User Experience

  • Responsive Design — Sidebar navigation on desktop, hamburger menu on mobile.
  • Microsoft Fluent Design — Tailwind CSS theme mimicking Microsoft's design language (Segoe UI, Fluent colors).
  • Internationalization — Full i18n support with English and Spanish locales.
  • Keyboard Shortcuts — Navigate pages, flag questions, and more without a mouse.
  • Dark Mode Ready — Tailwind darkMode: 'class' is configured (theme tokens defined).

Architecture Overview

graph TB
    subgraph Browser["Browser (SPA)"]
        Pages["Pages<br/>Dashboard · ExamSetup · ExamSession<br/>ExamResults · ExamReview · History<br/>QuestionBank · Settings"]
        Hooks["Custom Hooks<br/>useExamSession · useTimer"]
        API["API Client — src/api.ts"]
        Pages --> Hooks --> API
    end

    subgraph Server["Express 5 Server (:3150)"]
        Entry["server/index.ts — CORS, JSON parser, dotenv"]
        Routes["server/routes.ts — REST API"]
        DB["server/db.ts — SQLite + auto-migration"]
        Gemini["server/gemini-prompt.ts — AI integration"]
        Entry --> Routes --> DB
        Routes --> Gemini
    end

    API -- "HTTP/JSON via Vite proxy<br/>/api/* → localhost:3150" --> Entry
    DB --> SQLite["exam-data.db (auto-generated)"]
Loading

The frontend is a Vite-powered React SPA. During development, Vite proxies /api requests to the Express backend on port 3150. The backend uses better-sqlite3 with WAL mode for fast, concurrent-safe reads. The database is created automatically (empty, with schema) on first server startup.

See app-doc/architecture.md for detailed architecture documentation and app-doc/database.md for the full SQL schema.


Tech Stack

Layer Technology Version
Frontend Framework React 19
Routing React Router 7
Styling Tailwind CSS 3.4
Drag & Drop @dnd-kit/core + sortable 6.3 / 10.0
Icons Lucide React 0.468
Build Tool Vite 6
Language TypeScript 5.7
Backend Framework Express 5
Database SQLite (better-sqlite3) 12.9
AI Integration Google Gemini API
Server Runtime tsx 4.21
Testing Vitest
License GPL-3.0

Getting Started

Prerequisites

  • Node.js ≥ 18
  • npm ≥ 9

Installation

git clone https://github.com/BorjaUmpierrezMayor/Exam-Simulator.git
cd Exam-Simulator
npm install

Environment Setup

Copy the example environment file and fill in your values:

cp .env.example .env

Required variables:

  • GEMINI_API_KEY — Google Gemini API key (for AI explanations)
  • JWT_SECRET — Secret for JWT authentication tokens

Development

Start both the API server and the Vite dev server:

# Terminal 1 — API Server (port 3150)
npm run server

# Terminal 2 — Vite Dev Server (port 5173, proxies /api to 3150)
npm run dev

Or use the combined command (Windows):

npm run dev:all

Then open http://localhost:5173.

Production Build

npm run build
npm run preview

The production build outputs to dist/. The Express server must still run separately for the API.

Loading Your First Question Bank

  1. Open the app and navigate to Question Banks.
  2. Click Import JSON to load a question bank file (see Question Bank Format).
  3. Go to New Exam, configure your session, and start practicing.

The SQLite database starts empty on first run. The repository includes sample banks under public/exams/ that you can import from the UI.


Project Structure

Exam-Simulator/
├── index.html                  # HTML entry point
├── vite.config.ts              # Vite config with API proxy
├── tailwind.config.js          # Tailwind theme (Microsoft Fluent palette)
├── tsconfig.json               # TypeScript config (frontend)
├── tsconfig.server.json        # TypeScript config (server → dist-server/)
├── package.json                # Dependencies and scripts
├── .env.example                # Environment variable template
│
├── server/                     # Express API backend (TypeScript source)
│   ├── index.ts                # Server entry, CORS, middleware, dotenv
│   ├── routes.ts               # REST API routes (~1700+ lines)
│   ├── db.ts                   # SQLite connection, schema migration
│   ├── auth.ts                 # JWT authentication
│   ├── gemini-prompt.ts        # Gemini AI prompt construction
│   ├── question-schema.ts      # Question validation & normalization
│   └── tsconfig.json           # TypeScript config (server)
│
├── dist-server/                # Compiled server output (from tsconfig.server.json)
│
├── src/                        # React frontend
│   ├── main.tsx                # App entry, renders root
│   ├── App.tsx                 # Router + page layout
│   ├── api.ts                  # API client (fetch wrapper)
│   ├── store.ts                # LocalStorage persistence (legacy)
│   ├── types.ts                # TypeScript type definitions
│   ├── utils.ts                # Exam logic (grading, selection, domain weights)
│   ├── theme.ts                # Theme utilities
│   ├── index.css               # Tailwind directives + custom styles
│   │
│   ├── auth/                   # Authentication context & guards
│   │
│   ├── components/
│   │   ├── Layout.tsx          # App shell with sidebar navigation
│   │   ├── QuestionNav.tsx     # Question grid navigator
│   │   ├── TimerDisplay.tsx    # Timer with low-time warnings
│   │   ├── AiExplainDialog.tsx # Gemini AI explanation dialog
│   │   ├── GeminiAnalyzer.tsx  # AI-powered question analysis
│   │   ├── TechnologyTagDialog.tsx  # Tag content viewer/editor
│   │   └── questions/
│   │       ├── QuestionRenderer.tsx  # Question type dispatcher
│   │       ├── SingleChoice.tsx      # Radio button question
│   │       ├── MultipleChoice.tsx    # Checkbox question
│   │       ├── DragOrder.tsx         # Sortable drag question
│   │       ├── DragMatch.tsx         # Drag-to-target question
│   │       └── HotspotQuestion.tsx   # Dropdown-based hotspot
│   │
│   ├── hooks/
│   │   ├── useExamSession.ts   # Exam session state + API sync
│   │   └── useTimer.ts         # Countdown/countup timer hook
│   │
│   ├── i18n/
│   │   ├── index.tsx           # I18n context provider
│   │   ├── en.ts               # English translations (~260 keys)
│   │   └── es.ts               # Spanish translations (~260 keys)
│   │
│   ├── pages/
│   │   ├── Dashboard.tsx       # Home with stats and quick actions
│   │   ├── ExamSetup.tsx       # Exam configuration with domain tree
│   │   ├── ExamSession.tsx     # Active exam-taking view
│   │   ├── ExamResults.tsx     # Score, domain & topic breakdown
│   │   ├── ExamReview.tsx      # Question-by-question review
│   │   ├── History.tsx         # Exam history list
│   │   ├── QuestionBank.tsx    # Bank management UI
│   │   └── Settings.tsx        # App preferences
│   │
│   ├── test/                   # Vitest unit tests
│   └── utils/                  # Additional utility modules
│
├── app-doc/                    # Architecture and data model docs
│   ├── architecture.md
│   ├── data-model.md
│   ├── database.md
│   ├── technologies-overview.md
│   └── user-flows.md
│
├── scripts/                    # Data management & migration scripts
│   ├── deploy.ps1              # Deployment script
│   ├── migrate_id_to_int.cjs   # ID format migration
│   ├── populate_database.py    # Database population tool
│   ├── data-fix/               # Data correction scripts
│   ├── patch-data/             # Batch question patches
│   ├── pdf-extraction/         # PDF→question extraction tools
│   └── analysis/               # Data analysis scripts
│
├── public/exams/               # Bundled exam banks and assets
│   └── az-204/                 # AZ-204 exam bank + images
│
├── data/                       # Runtime SQLite directory (git-ignored)
└── test/                       # Postman API collections

Question Bank Format

Question banks are JSON files following this structure:

{
  "id": "my-exam",
  "title": "My Certification Exam",
  "description": "Practice questions for certification preparation",
  "passingScore": 700,
  "maxScore": 1000,
  "version": "1.0",
  "topics": ["Topic A", "Topic B"],
  "timeLimit": 90,
  "questions": [
    {
      "id": 1,
      "type": "single",
      "topic": "Topic A",
      "domain": "Develop Azure compute solutions (25–30%)",
      "tags": ["Azure Functions"],
      "text": "Which option is correct?",
      "code": "// optional code block",
      "options": [
        { "id": "a", "text": "Option A" },
        { "id": "b", "text": "Option B" }
      ],
      "correctAnswers": ["b"],
      "explanation": "Option B is correct because...",
      "references": ["https://docs.example.com/..."]
    }
  ]
}

Question Types

Type correctAnswers Format Extra Fields
single ["optionId"]
multiple ["optionId1", "optionId2"]
drag-order ["id1", "id2", "id3"] (ordered)
drag-match ["optionId:targetId", ...] targets: [{id, text}]
hotspot ["value1", "value2", ...] (parallel to targets) targets: [{id, label, choices}]

Exam Modes

Exam Mode

Simulates a real proctored exam. No answers are shown during the session. When finished, the exam is graded and a full results page is displayed with score, pass/fail status, domain breakdown, and topic breakdown.

Practice Mode

Identical interface to Exam Mode, but each question has a Solve button that reveals the correct answer, explanation, and references immediately. Ideal for active learning.

Review All Mode

All questions from the selected bank (or filtered topics) are loaded. Works like Practice Mode but defaults to the full question pool. Best for end-to-end study sessions.

Question Order — Review All offers two ordering options:

  • In Order (default) — Questions appear in ascending ID order (1, 2, 3…). Ideal for systematic study.
  • Random — Questions are shuffled. Useful for mixed review or spaced repetition.

Question Range — Optionally narrow the study session to a specific slice (e.g. questions 51–100) using the quick preset buttons or custom From/To inputs.


Domain-Based Exam Structure

The simulator supports official certification exam domain structures. For AZ-204:

Domain Weight Topics
Develop Azure compute solutions 25–30% Azure Functions, App Service (Web Apps, Containers), Container Apps & ACI, Docker & ACR, AKS & IaC
Develop for Azure storage 15–20% Azure Blob Storage, Azure Cosmos DB & Data Services
Implement Azure security 20–25% Microsoft Entra ID, App Service Security, Managed Identity, Key Vault & App Configuration
Monitor, troubleshoot, and optimize 15–20% Application Insights & Azure Monitor, App Service Monitoring, Azure Cache for Redis
Connect to and consume Azure services 15–20% Azure API Management, Azure Event Grid & Hubs, Service Bus & Queues, Logic Apps

Realistic Mode

When Realistic Mode is enabled in the exam setup:

  1. Questions are distributed proportionally across domains by weight
  2. A visual breakdown shows how many questions each domain will contribute
  3. Scoring uses weighted domain averages (a 70% in Compute weighs more than 70% in Integration)

Topic Filter Tree

The topic filter displays topics grouped under their parent domain with:

  • Collapsible domain sections
  • Domain-level select/deselect all
  • Weight percentage badges
  • Question counts per topic and domain
  • Search filter across all topics

API Reference

All endpoints are prefixed with /api.

Banks

Method Endpoint Description
GET /banks List all question banks (with questions)
GET /banks/:id Get a single bank with questions
POST /banks Create or update a bank (upsert)
DELETE /banks/:id Delete a bank and its questions

Questions

Method Endpoint Description
GET /banks/:bankId/questions List questions (supports topic, domain, limit, offset, fromId, toId params)
GET /banks/:bankId/questions/:questionId Get one question by id
POST /banks/:bankId/questions Create/upsert a question
POST /banks/:bankId/questions/:questionId Create/upsert a question by explicit id
PATCH /banks/:bankId/questions/:questionId Partially update fields including domain, topic, tags

Domains & Topics

Method Endpoint Description
GET /banks/:bankId/domains List domains with question counts
GET /banks/:bankId/topics List topics with domain and question counts

Sessions

Method Endpoint Description
GET /sessions List active (in-progress) sessions
GET /sessions/:id Get session with full question data
POST /sessions Create a new session
PUT /sessions/:id Update session (answers, page, status)
DELETE /sessions/:id Delete a session

Results

Method Endpoint Description
GET /results List all exam results
GET /results/:id Get result with full question data
POST /results Save a graded exam result
DELETE /results/:id Delete a single result
DELETE /results Clear all results

Tags

Method Endpoint Description
GET /tags List all technology tags
GET /tags/:id Get tag details with content
PATCH /tags/:id Update tag content

Question Edits

Method Endpoint Description
GET /edits?bankId= List edits (optionally filtered by bank)
POST /edits Save an edit (also applies it to the question)

Settings

Method Endpoint Description
GET /settings Get all settings as key-value pairs
PUT /settings Update settings (upsert)

Testing Collections

Postman collections are available in test/:

  • test/questions-crud.collection.jsonGET (all, batch by ID range, batch by limit/offset), POST, and PATCH requests for questions.
  • test/questions-patch-scenarios.collection.json — focused partial-update scenarios, including explanation-only updates.

Both collections use these variables:

  • baseUrl (default http://localhost:3150/api)
  • bankId (default az-204)
  • questionId (default 1)

Internationalization

The app supports multiple locales via a React context-based i18n system with fully typed translation keys.

Locale File Keys Status
English src/i18n/en.ts ~260 Complete
Spanish src/i18n/es.ts ~260 Complete

The locale is persisted in localStorage under the key app-locale. To add a new language:

  1. Create src/i18n/{code}.ts mirroring the structure of en.ts.
  2. Import it in src/i18n/index.tsx and add it to the dictionaries and LOCALE_LABELS objects.
  3. The TranslationKey type is auto-derived from en.ts — no manual type updates needed.

Keyboard Shortcuts

Key Action
or N Next page
or P Previous page
F Flag/unflag first question on current page

Shortcuts are disabled when the focus is in an input or textarea field.


Configuration

App Settings (via UI)

Setting Default Description
Questions per page 10 Default pagination size
Questions per exam 50 Default number of questions
Default time limit None Optional countdown timer
Keyboard shortcuts Shown Toggle shortcut display
Language English UI language

Environment Variables (.env)

Variable Default Description
PORT 3150 Express server port
GEMINI_API_KEY Google Gemini API key for AI features
GEMINI_MODEL gemini-2.0-flash Gemini model to use
JWT_SECRET Secret for JWT token signing
DB_PATH ./data/exam-data.db SQLite database file path

Vite Dev Server

Variable Default Description
Vite Port 5173 Dev server port (Vite default)
API Proxy /api → :3150 Configured in vite.config.ts

Database Schema

Key tables:

  • banks — Exam bank metadata (title, passing score, version)
  • questions — All questions with type, topic, domain, tags (JSON), text (Markdown), options, correct_answers
  • question_tags — Many-to-many relation linking questions to technology tags
  • tags — Technology tag definitions with name, slug, content_md
  • sessions — Active exam sessions with answers and progress
  • results — Completed exam results with scores
  • question_edits — Audit trail for all question modifications
  • settings — Key-value app settings

See app-doc/database.md for the full SQL schema.


License

This project is licensed under the GNU General Public License v3.0.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors