A local web application that generates BDD (Behaviour-Driven Development) coverage intelligence reports by cross-referencing three data sources:
- Azure DevOps - fetches all test cases matching your query via WIQL
- Jenkins REST API - auto-fetches the latest pipeline artifact files (Executed + Excluded) directly from Jenkins builds, no manual download needed
- GHE REST API - scans
.featurefiles directly from GitHub Enterprise repos via the Trees + Blobs API (no local clone required), or optionally from a local path
All through a browser UI, in under 60 seconds, with no scripting required.
- The Problem It Solves
- What the Report Tells You
- Time Savings
- How It Works
- Prerequisites
- Setup
- Running on a Custom Port
- Using the App
- Report Features
- Project Structure
- Troubleshooting
In large BDD projects - think 1,000+ automated test cases across dozens of feature files and two CI pipelines - answering basic coverage questions becomes surprisingly difficult:
| Question | Without this tool |
|---|---|
| Which TCs are actually running in the pipeline? | Open each TC in ADO, search pipeline logs by name/ID - one at a time |
Which TCs have a .feature file but never ran in any pipeline? |
Manually grep across hundreds of .feature files |
| Which TCs are completely uncovered? | No reliable way without scripting |
| What is our BDD coverage % right now? | Unknown unless someone compiles it manually |
| Who owns the most uncovered TCs? | Manual ADO query + cross-reference |
For a suite of 1,000 test cases, a manual coverage check takes 3–5 hours of tedious, error-prone work. Results go stale the moment the next pipeline run completes.
This tool answers all of those questions in under 60 seconds:
- Fetches pipeline artifact data automatically from Jenkins - no downloading, no uploading
- Runs the ADO WIQL query in bulk - no browsing test case by test case
- Scans
.featurefiles via GHE REST API - no local repo clone needed - Produces a full HTML report with coverage %, tables, platform filter, and breakdowns by module and assignee
- Re-runnable in one click whenever you need a fresh snapshot
| Metric | What it means |
|---|---|
| Total ADO Test Cases | All TCs matching your query (e.g. a specific Area Path) |
| General Pipeline | TCs whose TC ID was found in General pipeline execution logs |
| Upgrade Pipeline | TCs whose TC ID was found in Upgrade pipeline execution logs |
| Pipeline Excluded | TCs present in the pipeline excluded list (skipped intentionally) |
| Feature File Only | TCs tagged in a .feature file - but not yet running in any pipeline |
| Not Covered | TCs with no pipeline match and no feature file match |
| BDD Coverage % | (Covered / Total) x 100 |
The report also includes:
- Platform filter bar - filter all tables and counts by Android, Apple, Windows, Linux, or Chrome (searches TC title, ADO tags, and feature file path)
- Coverage by Solution - which modules / BDD sub-folders have the most coverage
- Not-covered by Assignee - who owns the most uncovered test cases
- Full searchable, scrollable tables for every category
| Task | Manual | With this tool |
|---|---|---|
| Get pipeline artifact files | Navigate to Jenkins build, download artifact zip, extract, locate .txt files |
1 click - Jenkins REST API fetches all sub-folders automatically |
| Check which TCs ran in pipeline | Open each TC in ADO, search logs - one at a time | Bulk cross-reference, all 1,000+ TCs in seconds |
| Fetch ADO test case details | Browse ADO area by area, apply filters manually | Single WIQL query fetches everything at once |
| Scan feature files for TC tags | grep -r TC_ across hundreds of files, tally manually |
GHE Trees API - entire repo tree in 1 call, blobs fetched 50 at a time |
| Calculate coverage % | Count covered TCs in a spreadsheet | Auto-calculated - shown instantly in the report |
| Generate a shareable report | Compile findings into a doc or spreadsheet by hand | Self-contained timestamped HTML file, one click to share |
| Re-run for a different area | Repeat the entire process from scratch | Change the Area Path, click Generate |
Typical time saved: 3–5 hours of manual lookup → under 60 seconds. Coverage % accuracy: manual estimates ±20% → tool-generated exact figure.
Screenshots coming soon
ADO REST API Jenkins REST API GHE REST API (or local path)
(WIQL query -> (auto-fetch latest Trees API: fetch full repo
fetch all TC SUCCESS build -> tree in 1 call ->
details, titles, download all parallel blob fetches
assignees, Analysis.*/Executed.txt (50 concurrent) ->
priority, state) + Excluded.txt) parse @TC-XXXXXX tags
| | |
+---------------------+-------------------------+
|
TC ID-based cross-reference
Priority: General > Upgrade > Excluded > Feature-only
|
+--------------+--------------+
Coverage % Timestamped HTML Report
breakdowns by (searchable tables, platform filter,
module + assignee self-contained, shareable)
| GHE Repos (default) | Local Path | |
|---|---|---|
| Requires repo clone on server | No | Yes |
| Works on any hosted VM | Yes | Only if repo is cloned there |
| Always scans latest code | Yes (pinned to branch HEAD) | Only if clone is up to date |
| Speed | ~2–50s (Trees API + 50 parallel blobs) | <1s (disk I/O) |
| Setup | GHE PAT entered in UI (required) | BDD_SCAN_ROOT in .env |
| Multiple repos | Yes - add as many as needed | One path only |
| If scan fails | Warning shown in report, continues without feature matching | Warning shown |
GHE scan flow:
GET /api/v3/repos/:owner/:repo/branches/:branch- resolve commit → tree SHA (1 call)- If a sub-path is specified, walk the tree segments to resolve that sub-folder's SHA (avoids root tree truncation for large repos)
GET /api/v3/repos/:owner/:repo/git/trees/:sha?recursive=1- full file tree from that SHA (1 call)- Filter to
.featureblobs GET /api/v3/repos/:owner/:repo/git/blobs/:shax N files - batched 50 at a time- Parse each file for
@TC-XXXXXXtags andTC_XXXXXXscenario names
Sub-path tip: For large repos, always set Sub-path to the BDD folder (e.g.
Bdd) - this resolves the tree from that sub-folder directly rather than the entire repo root, avoiding the GHE recursive tree size limit and dramatically reducing scan time.
Test Connection button in the UI calls POST /api/ghe-test which resolves the branch and counts .feature files for each configured repo - validates token + repo access before running the full report.
GHE PAT is required - enter it directly in the Step 3 UI field (read:repo scope). If no token is provided or the scan fails (e.g. wrong branch, network error), a warning is shown in the report and generation continues with ADO + pipeline data only.
Jenkins auto-detection logic:
- General build: latest SUCCESS build whose cause contains
Push event to branch master - Upgrade build: latest SUCCESS build triggered by the upstream project matching
JENKINS_UPGRADE_TRIGGER(configurable in.env) - All
Analysis.*sub-folders are fetched automatically (e.g.Analysis.General-Main,Analysis.General-Security,Analysis.Upgrade-CurrentBuild,Analysis.Upgrade-v1.0, ...)
TC ID matching logic:
- Pipeline artifact files contain one test method name per line, e.g.
Bdd.General.Features.LoginFeature.TC_1017008_SomeScenario - The tool extracts all numeric IDs from
TC_XXXXXXblocks - including combined ones likeTC_1017008_1017050SomeScenario(both IDs extracted) - Feature files are scanned for
@TC-XXXXXXtags andTC_XXXXXX_YYYYYYpatterns in scenario names - Duplicate priority when a TC ID appears in multiple sources: General executed → Upgrade executed → Excluded → Feature file only
- Node.js 18+ installed
- Azure DevOps access with a Personal Access Token (Work Items - Read)
- Jenkins network access -
JENKINS_URLreachable from this machine. If your Jenkins allows anonymous read, no credentials are needed. - GHE Personal Access Token with
read:reposcope - entered in the Step 3 UI field when running a report (optional but required for feature file matching)
git clone <repo-url>
cd bdd-coverage-app
npm installThere are two ways to configure the app — pick whichever suits you:
Open src/config/env.js and fill in the USER_CONFIG block at the top of the file:
const USER_CONFIG = {
// Azure DevOps
ADO_ORG: 'https://dev.azure.com/your-org',
ADO_PROJECT: 'YourProject',
ADO_PAT: 'your-pat-here',
// Jenkins
JENKINS_URL: 'https://your-jenkins.example.com',
JENKINS_JOB_PATH: 'job/your-pipeline/job/master',
JENKINS_UPGRADE_TRIGGER: 'Trigger-UpgradeTestExecution',
// GHE (optional)
GHE_HOST: '', // e.g. 'https://ghe.yourcompany.com'
GHE_TOKEN: '',
// Server
PORT: 3001, // ← change this if port 3001 is already in use
HOST: '0.0.0.0',
};This is the recommended approach for open-source / local use — one file, all settings, no extra config.
# Windows
copy .env.example .env
# macOS / Linux
cp .env.example .envOpen .env and fill in your values:
ADO_ORG=https://dev.azure.com/your-org
ADO_PROJECT=YourProject
ADO_PAT=your-personal-access-token-here
JENKINS_URL=https://your-jenkins.example.com
JENKINS_JOB_PATH=job/your-pipeline/job/master
PORT=3001
.envis listed in.gitignoreand is never committed..envvalues always overrideUSER_CONFIG.
- Open your ADO organisation in a browser
- Click your avatar (top-right) → Personal access tokens
- Click New Token
- Name it (e.g.
bdd-coverage-app), set expiry (90 days recommended) - Under Scopes, choose Custom defined and enable Work Items → Read
- Click Create and copy the token immediately - it is shown only once
- Paste it into
ADO_PAT(inUSER_CONFIGor.env)
npm startOpen http://localhost:3001 in your browser (or whatever port you configured). The server also prints your LAN IP on startup for access from other machines on the same network.
For auto-restart on code changes during development:
npm run dev
The app defaults to port 3001. If that port is already in use (e.g. another tool is running), change it in one place:
Option A — USER_CONFIG in src/config/env.js:
PORT: 3002, // ← pick any free portOption B — .env file:
PORT=3002Option C — inline when starting (no file changes needed):
# Windows PowerShell
$env:PORT=3002; npm start
# macOS / Linux
PORT=3002 npm startIf you need to run this app on a different port alongside another tool already on port 3000, just make sure each instance has a different PORT. Open a new terminal for each:
# Terminal 1 — existing tool stays untouched on port 3000
# (nothing to do — leave it running)
# Terminal 2 — this app on port 3001
cd bdd-coverage-app
npm start # reads PORT=3001 from .env or USER_CONFIGBoth servers run independently. Changing the port in .env or USER_CONFIG only affects this app — the other tool is completely unaffected.
Guided Builder (default): Build your query visually.
- Area Path - filter to a specific ADO area (optional; leave blank to query all)
- Title - filter by test case title with operator (Contains Words, Contains, etc.)
- Automation Status - defaults to Automated API Test (BDD); check additional statuses as needed
- State Filter - defaults to Exclude Closed; switch to No state filter to include all states
- Advanced Filters - expand for priority, assigned to, tags, iteration path, and order by
Raw WIQL: paste any WIQL query directly. Useful for multi-area-path queries or complex conditions.
Auto-fetch (recommended): Click Fetch Latest General Build, Fetch Latest Upgrade Build, or Fetch Both Pipelines. The server queries Jenkins, finds the latest successful build, and downloads all Analysis.* artifact files automatically.
Manual upload: Expand Upload files manually and drop .txt files into the appropriate zone:
- Left zone (General) - General pipeline Executed + Excluded
.txtfiles - Right zone (Upgrade) - Upgrade pipeline Executed + Excluded
.txtfiles
GHE Repos (default): Click + Add Repository to add a repo entry.
- The first click adds a blank card for you to fill in with your repo URL, branch, and sub-path
- Each subsequent click adds a blank card for you to fill in
- Enter the full GHE repo URL, branch, and optional sub-path (strongly recommended for large repos)
- Enter your GHE PAT (
read:reposcope) in the token field - required for scanning - Click Test Connection to verify access and count
.featurefiles per repo before generating - Multiple repos can be added - all are scanned and merged into one TC ID lookup
- Duplicate repo URLs are highlighted with a validation error
If no token is provided or the GHE scan fails, a warning is shown in the report and generation continues with ADO + pipeline data only - the feature file step is fully non-blocking.
Local Path: Enter the server-side absolute path to your .feature files folder. Only works if this app is running on a machine with the BDD repo cloned locally.
Leave both blank to report on ADO + pipeline data only.
Click Generate Report. The app:
- Runs the WIQL query against ADO and fetches all matching test case details
- Parses pipeline files and indexes every TC ID found
- (If configured) Scans GHE repos or local path for
.featurefiles and extracts TC ID tags - Cross-references all sources and generates a timestamped HTML report
- Opens the report automatically in a new browser tab
The generated report includes a Platform Filter bar at the top with buttons: All Platforms | Android | Apple | Windows | Linux | Chrome
Clicking a platform button filters all three tables (Covered, Not Covered, Feature File Only) to show only rows where the test case title, ADO tags, or feature file path contains that platform name. All summary cards (Total, Covered, General, Upgrade, Excluded, Feature File Only, Not Covered, Coverage %) and section counts update dynamically to reflect the filtered view. Clicking All Platforms resets everything.
Each table section has a live search box - type to filter rows by any column value. Search and platform filter work together.
A Download Report button in the report header saves the self-contained HTML file for sharing. No server is needed to view a saved report.
bdd-coverage-app/
|-- src/
| |-- server.js # Thin Express entry point - registers middleware, routes, starts server
| |-- config/
| | |-- env.js # Centralised environment config (single source of truth for all .env vars)
| | `-- ghe.js # GHE repo URL validation + host allow-list enforcement
| |-- middleware/
| | |-- auth.js # Entra ID (OIDC) Bearer token validation middleware
| | `-- upload.js # Multer config - memory storage, 50 MB limit
| |-- routes/
| | |-- auth.js # GET /api/auth-config, POST /api/preview-wiql
| | |-- report.js # POST /api/generate-report (orchestrates ADO + pipeline + GHE + report)
| | |-- jenkins.js # POST /api/jenkins-fetch
| | `-- ghe.js # POST /api/ghe-test
| `-- services/
| |-- adoClient.js # ADO REST API client - WIQL query + batched TC detail fetch
| |-- jenkinsClient.js # Jenkins REST API client - auto-detect latest build, download artifacts
| |-- pipelineParser.js # Parses pipeline .txt files, extracts TC IDs with priority rules
| |-- featureScanner.js # GHE Trees+Blobs API scanner + local filesystem scanner
| `-- reportGenerator.js # Generates the self-contained HTML coverage report
|-- public/
| `-- index.html # Browser UI - query builder, Jenkins fetch, GHE repo manager
|-- .env # Your local credentials - never committed (listed in .gitignore)
|-- .env.example # Template - copy to .env and fill in your values
|-- .gitignore # Excludes .env and node_modules from git
|-- .dockerignore # Excludes .env, node_modules, .git from Docker build context
|-- Dockerfile # Docker image definition - node:20-alpine, exposes port 3000
`-- package.json # Dependencies + start scripts (start: node src/server.js)
| Layer | Files | Responsibility |
|---|---|---|
| Entry | src/server.js |
Wires middleware + routes, starts the HTTP server |
| Config | src/config/env.js |
One place to read all env vars - no process.env scattered elsewhere |
| Config | src/config/ghe.js |
Validates GHE repo JSON and enforces GHE_HOST allow-list |
| Middleware | src/middleware/auth.js |
Validates Entra ID tokens (no-op when ENTRA_CLIENT_ID not set) |
| Middleware | src/middleware/upload.js |
Multer multipart file upload config |
| Routes | src/routes/report.js |
Orchestrates the full report pipeline (ADO -> parse -> GHE -> generate) |
| Routes | src/routes/jenkins.js |
Jenkins build auto-fetch endpoint |
| Routes | src/routes/ghe.js |
GHE connectivity test endpoint |
| Routes | src/routes/auth.js |
Auth config + WIQL preview endpoints |
| Services | src/services/adoClient.js |
ADO REST API - WIQL, batched work item fetch, 1000-item pagination |
| Services | src/services/jenkinsClient.js |
Jenkins - detect latest build, download Analysis.* artifacts |
| Services | src/services/pipelineParser.js |
Parse .txt lines, extract TC IDs, apply General > Upgrade priority |
| Services | src/services/featureScanner.js |
GHE Trees+Blobs API + local filesystem, parse @TC-XXXXXX tags |
| Services | src/services/reportGenerator.js |
Build self-contained HTML report with platform filter + tables |
Port 3000 already in use
# Windows PowerShell
Stop-Process -Id (Get-NetTCPConnection -LocalPort 3000).OwningProcess -Force
npm start# macOS / Linux
lsof -ti:3000 | xargs kill
npm start401 Unauthorized errors from ADO
Your PAT has expired or has insufficient permissions. Recreate it with Work Items → Read scope and update ADO_PAT in .env.
Jenkins fetch fails or returns "Jenkins not configured"
Ensure JENKINS_URL and JENKINS_JOB_PATH are set in .env. If your Jenkins allows anonymous read, no credentials are needed. Restart the server after editing .env.
Jenkins fetch finds no builds
The auto-detection scans the last 60 builds for General and 500 builds for Upgrade (Upgrade runs are infrequent). If no successful build is found, increase the limit parameter in src/services/jenkinsClient.js → findLatestSuccessBuild.
GHE scan fails with 404 "Not Found" on branch
The branch name is wrong for that repo - check the default branch name in GHE (some repos use main instead of master). Update the Branch field in the repo card and retry.
GHE scan fails - report still generates This is by design. GHE scanning is fully optional and non-blocking. A warning banner is shown in the report. The report still contains ADO + pipeline coverage data.
GHE tree truncated warning in report
The repo's root tree exceeds the GHE recursive tree size limit. Set a Sub-path (e.g. Bdd) in the repo card to scope the scan to just the BDD folder - this resolves the tree from that sub-folder's SHA directly and avoids truncation.
Unexpected token '<' error on Generate
The server returned an HTML error page instead of JSON - check the server console for the full error message.
Upgrade Pipeline shows 0 in the report When uploading manually, make sure Upgrade files go into the right zone (Upgrade). Both pipelines use the same filename - the zone determines attribution. When using Jenkins auto-fetch, the pipeline is detected automatically.
0 TC IDs from pipeline after uploading
Open one of the .txt files and confirm each line contains TC_ followed by digits (e.g. ...TC_1017008...). The file must follow the Namespace.FeatureClass.TC_XXXXXX_ScenarioName format.
Feature file scan returns 0 files (Local Path)
- Verify the path exists and is accessible from this machine
- Confirm the folder contains
.featurefiles (not.feature.csor other extensions) - Directories named
node_modules,.git,bin,objare automatically skipped - Ensure
BDD_SCAN_ROOTis set in.envif using the local path security restriction