QA Lens Docs

QA Lens Architecture

QA Lens turns static test reports into local, explainable triage intelligence.

---

Overview

QA Lens is structured as a pipeline with clearly separated concerns:

Report on disk
     │
     ▼
┌─────────────┐
│  Detection  │  (parsers/detector.py)
│             │  Identifies report type from file/folder signatures
└──────┬──────┘
       │
       ▼
┌─────────────┐
│   Parser    │  (parsers/)
│             │  Extent, Allure, JUnit, TestNG, Playwright, Cypress
└──────┬──────┘
       │  models.TestRun (normalized)
       ▼
┌─────────────┐
│  Analyzers  │  (analyzers/)
│             │  Signatures → Categorization → Clustering → Flaky → Decision
└──────┬──────┘
       │  models.AnalysisSummary
       ▼
┌─────────────┐
│  Database   │  (db/)
│             │  SQLite via schema.py — runs, tests, failures, artifacts
└──────┬──────┘
       │
       ▼
┌─────────────┐
│   Server    │  (server/)
│             │  FastAPI — REST API + packaged React UI
└─────────────┘

CLI outputs (JSON, Markdown, console) are produced directly from AnalysisSummary without going through the database, for one-shot commands like qalens summarize and qalens clusters.

---

Key Design Decisions

1. Normalize First, Analyze Second

Parsers only extract. They produce a TestRun canonical model and stop. Analyzers only analyze. They receive a TestRun and produce AnalysisSummary. These concerns are strictly separated so that:

  • A new parser does not require changes to any analyzer.
  • Analyzers are testable against hand-crafted TestRun objects.
  • The same analyzers work across all report formats.

2. Explainability is Non-Negotiable

Every Insight must include:

  • category — the classification
  • confidence — a float in [0, 1]
  • explanation — a human-readable reason
  • evidence — a list of concrete signals that drove the classification

There are no black-box verdicts.

3. Local-First, Opt-In External

Core analysis, ingestion, and the web UI run entirely on the local machine with no telemetry. External network calls are opt-in only:

  • GitHub OAuth — optional team authentication (QALENS_AUTH_MODE=github)
  • Cloud LLM providers — require explicit allow_external = true in config

4. Plugin Architecture

Each major layer has a defined abstract base or protocol:

  • BaseParser — for custom report parsers
  • Categorization rules — additive, pluggable functions
  • Output writers — implement BaseWriter

---

Module Map

src/qalens/parsers/

ModuleResponsibility
detector.pyIdentifies report type from path/file signatures
extent.pyExtracts data from Extent HTML reports (v4, v5)
allure.pyExtracts data from Allure HTML reports (v2)
junit.pyParses JUnit-compatible XML (testsuite / testsuites)
testng.pyParses TestNG XML (testng-results.xml)
playwright.pyParses Playwright JSON reports and JSON-backed HTML folders
cypress.pyParses Cypress/Mocha JSON and Mochawesome-style output

src/qalens/models/

ModuleKey Types
run.pyTestRun, RunMetadata
test_case.pyTestCaseResult, StepResult
failure.pyFailureInfo
insight.pyInsight, FailureCluster, AnalysisSummary
attachment.pyAttachment
warnings.pyExtractionWarning

src/qalens/analyzers/

ModuleResponsibility
signatures.pyStack trace normalization, signature generation
categorizer.pyRule-based failure categorization
clustering.pyDeterministic + optional fuzzy failure grouping
flaky.pyFlaky scoring with historical context
decision.pyDecision summary, trend intelligence, and prioritized actions
incidents.pyCross-run incident tracking and trend detection

src/qalens/db/

ModuleResponsibility
schema.pySQLite schema, table definitions, default DB path

src/qalens/artifacts/

ModuleResponsibility
config.pyArtifact ingestion policy (mode, caps, compression settings)
storage.pyImage byte storage, SHA-256 deduplication, ArtifactStore interface

src/qalens/llm/

ModuleResponsibility
config.pyLLM provider config, config.toml load/save, provider defaults
prompts.pyPrompt builders for each LLM feature
client.pyLLM routing and provider client abstraction
deterministic_answers.pyRule-based answers that bypass the LLM

src/qalens/server/

ModuleResponsibility
app.pyFastAPI app factory, middleware (CSP, auth), health + auth endpoints
auth.pyAuth modes (none / token / GitHub OAuth), session signing, admin access
routes_runs.pyRun listing, test results, failure detail
routes_analysis.pyFailure categorization and cluster API
routes_evidence.pyScreenshot and artifact evidence API
routes_llm.pyLLM-powered chat and question-answering
routes_compare.pyRun comparison and history API
routes_settings.pyRuntime settings read/write (admin only)
routes_report.pyShareable HTML/Markdown/JSON report export
static/Compiled React + Vite frontend assets

src/qalens/reports/

ModuleResponsibility
builder.pyBuild deterministic report payloads from SQLite data
model.pyTyped report export models
renderers.pyRender standalone HTML, Markdown, and JSON exports

src/qalens/ownership.py

Owner mapping — resolves test/suite/feature/tag patterns to team names from a owners.toml file provided at ingestion time.

src/qalens/api/

ModuleResponsibility
library.pyPublic Python API: QALensClient

src/qalens/utils/

ModuleResponsibility
fs.pyFile system helpers (find, resolve, walk)
text.pyText cleaning and regex utilities

---

Authentication

QA Lens supports three auth modes configured via environment variables:

ModeEnv varsHow it works
noneNo login required; intended for localhost
tokenQALENS_AUTH_TOKENBearer token sent in Authorization header; stored in browser session storage
githubQALENS_AUTH_MODE, QALENS_GITHUB_CLIENT_ID, QALENS_GITHUB_CLIENT_SECRET, QALENS_SESSION_SECRETGitHub OAuth flow; session signed with HMAC-SHA256 and stored in an HttpOnly cookie

Admin access to the Settings panel is controlled separately by QALENS_ADMIN_GITHUB_USERS. OAuth state is stored server-side (in-process dict with 10-minute TTL) to avoid cookie-domain mismatches in local development.

---

Data Flow in Detail

Parser
  → extracts raw HTML/JSON/XML
  → builds TestCaseResult objects for each test
  → attaches StepResult, FailureInfo, Attachment lists
  → emits ExtractionWarning for missing/malformed fields
  → wraps everything in TestRun

Analyzers (sequential pipeline)
  1. SignatureEngine
       → normalize stack traces per failed TestCaseResult
       → generate a stable failure_signature
  2. Categorizer
       → evaluate each failure against heuristic rules
       → assign Insight(category, confidence, explanation, evidence)
  3. ClusterEngine
       → group failures by signature (deterministic)
       → optionally refine with TF-IDF proximity
       → produce FailureCluster list
  4. FlakyScorer
       → compare with historical runs from the DB
       → compute flaky_score per test
  5. Decision/report builders
       → aggregate statistics
       → produce AnalysisSummary

Persistence (qalens ingest)
  → AnalysisSummary + TestRun written to SQLite via db/schema.py
  → Screenshot artifacts stored separately (metadata-only by default)

Server (qalens serve)
  → FastAPI reads from SQLite on each API request
  → React frontend communicates exclusively via /api/* endpoints
  → Auth middleware validates every request before it reaches a route handler

---

Extension Points

See plugin-guide.md for details on:

  • Adding a parser for a new report format
  • Adding custom categorization rules
  • Adding a custom output writer

---

Dependency Graph

cli/            → api/library.py + db/ + server/
api/library.py  → parsers/ + analyzers/ + reports/
server/         → db/ + auth + llm/ + reports/ + ownership
parsers/        → models/ + utils/
analyzers/      → models/ + utils/
reports/        → db/ + models/
llm/            → db/ + security
models/         → pydantic (external)

No circular dependencies are permitted. models/ must not import from parsers/, analyzers/, or reports/.