QA Testing
Previewops can run an automated QA analysis on any pull request with a single comment. You get a ready-to-read report posted directly in the PR — no CI configuration required.
Quick start
Run on demand
/qa-previewops
Previewops queues the analysis and posts a confirmation comment immediately. The full report appears in the PR within a few minutes. If a preview environment is already live for this PR, HTTP smoke tests run against it automatically.
Run against a specific URL
/qa-previewops --url=https://pr-42-preview.example.com
Use --url to target a preview that was deployed outside of Previewops, or to override the auto-detected URL.
What's in the report
The report is always posted as a PR comment and is organised into sections:
📁 Test coverage
Available on all plans — no AI key required.
Previewops inspects up to 300 changed files and maps each source file to its expected test counterpart. The result is a table showing which files are covered (a test file was found), uncovered (no test file could be matched), and — for covered files — the name of the matched test file.
Supported languages:
| Language | Convention used |
|---|---|
| TypeScript / JavaScript | src/X/Y.ts → src/__tests__/X/Y.test.ts or .spec.ts; .mjs / .cjs variants supported |
| Python | auth.py → test_auth.py or auth_test.py |
| Go | handler.go → handler_test.go (same directory) |
| Ruby | auth.rb → auth_spec.rb or auth_test.rb |
| Java | Auth.java → AuthTest.java or AuthTests.java |
| Playwright | .spec.ts / .spec.js files are recognised as test files |
| Cypress | .cy.ts / .cy.js files are recognised as test files |
A fuzzy fallback handles cases where the test file name doesn't exactly mirror the source file name — for example, provider subdirectories (aws-ecs/index.ts matched to ecs.test.ts) or cross-root test layouts (Java src/main/ ↔ src/test/, Ruby app/ ↔ spec/).
Files that are not mappable are silently excluded from the coverage table. This includes: configuration files (*.config.js, tsconfig*.json, .eslintrc.*, etc.), migrations, Markdown, JSON, and files under build-output or static-asset directories (public/, dist/, build/, out/, .next/).
📝 Code review
Available on Pro, Enterprise, and Custom plans, and on Premium BYOC with the Custom LLM add-on activated.
An AI model reviews the full PR diff and produces:
- A 1–3 sentence summary of what changed.
- A table of flagged issues, each rated high / medium / low severity.
The review covers any language in the diff.
🧪 HTTP smoke tests
Available on Pro, Enterprise, and Custom plans, and on Premium BYOC with the Custom LLM add-on activated. Requires a live preview URL.
The AI generates 3–6 HTTP test scenarios based on the PR diff, then executes them against the preview. Each scenario specifies a method, path, and expected status code. Results are shown in a pass/fail table.
If the AI determines the diff contains no HTTP endpoints (e.g. the PR only touches test files, configuration, or documentation), the HTTP test section is omitted automatically.
If no preview URL is available (neither via --url nor an active preview for this PR), the HTTP test section is omitted.
⚠️ Regression risks
Available on Pro, Enterprise, and Custom plans, and on Premium BYOC with the Custom LLM add-on activated.
The AI identifies areas of the codebase that may regress based on what the PR touches — for example, auth changes raising risk for all authenticated routes. Each risk is rated high / medium / low with a brief explanation.
Plan comparison
| Feature | Free | Premium BYOC | Pro | Enterprise | Custom |
|---|---|---|---|---|---|
| Test coverage mapping | ✅ | ✅ | ✅ | ✅ | ✅ |
| AI code review | ❌ | ✅ ¹ | ✅ | ✅ | ✅ |
| HTTP smoke tests | ❌ | ✅ ¹ | ✅ | ✅ | ✅ |
| Regression risk analysis | ❌ | ✅ ¹ | ✅ | ✅ | ✅ |
¹ Premium BYOC AI features require the Custom LLM add-on. Activate it in the dashboard and store your
LLM_PROVIDER,LLM_API_KEY, andLLM_MODELcredentials. Without it, the command runs and returns test coverage, but AI analysis is skipped and an add-on activation prompt is shown in the report. See add-ons.md. You may also setLLM_MAX_PATCH_CHARS(integer, 100–10000) to control how many characters of each file's diff are sent to the model — useful for large files with many route definitions.
Free plan users will see an upgrade prompt in the report where the AI sections would appear.
Example report
> 🕵️ QA Report for PR #42
📋 **2/3** files covered · **1** issue · **3/3** HTTP tests passed · **2** risks
### 📁 Test Coverage — 2/3 files covered
| File | Status | Test file |
|---|---|---|
| `src/utils/auth.ts` | ✅ covered | `auth.test.ts` |
| `src/utils/rate-limit.ts` | ✅ covered | `rate-limit.test.ts` |
| `src/deployer/index.ts` | ❌ uncovered | |
**2/3** source files have corresponding test files.
---
### 📝 Code Review — 1 issue
The changes introduce a new rate-limiting layer in the auth utility and refactor
the deploy orchestrator to support concurrent provider calls.
| Severity | Issue |
|---|---|
| 🟡 medium | `rateLimiter` is not reset between test runs — consider clearing state in teardown. |
---
### 🧪 HTTP Test Scenarios — 3/3 passed
| Scenario | Status | Details |
|---|---|---|
| GET /api/health | ✅ pass | HTTP 200 |
| POST /api/deploy | ✅ pass | HTTP 202 |
| GET /api/unknown-route | ✅ pass | HTTP 404 (expected 404) |
---
### ⚠️ Regression Risks — 2 areas
| Area | Risk | Reason |
|---|---|---|
| Auth middleware | 🟡 medium | Rate-limiting logic now wraps all authenticated routes. |
| Deploy orchestrator | 🔴 high | Concurrent provider path is new and lacks tests. |
---
<details>
<summary>🔧 Test Setup</summary>
- **Preview:** [https://pr-42-preview.fly.dev](https://pr-42-preview.fly.dev)
- **Model:** gpt-4o
- **Files analyzed:** 3
</details>
Options
| Option | Type | Example | Description |
|---|---|---|---|
--url |
https://... |
--url=https://pr-42.example.com |
Explicitly set the preview URL for HTTP smoke tests. When omitted, Previewops auto-detects the active preview for this PR. Private/internal IP addresses are always rejected. |
--provider |
string | --provider=render |
Target the preview deployed to this provider. Useful when multiple active previews exist for the same PR. Aliases are supported: lightsail → aws-lightsail, ecs → aws-ecs, azure → azure-container-apps. If no matching preview is found, the AI analysis still runs but HTTP smoke tests are skipped. |
--env |
string | --env=staging |
Target the preview with this environment name (set via --env when originally deployed). Lowercase alphanumeric and hyphens, max 20 characters. |
--coverage-only |
flag | --coverage-only |
Skip AI analysis entirely — only run test coverage mapping. No AI credits are consumed. Useful for a quick coverage check without triggering billing. |
--commit |
SHA | --commit=abc1234 |
Analyse the file tree at a specific commit instead of the PR head. Accepts a short (7-char) or full (40-char) SHA. The SHA must belong to this PR's commit history — Previewops validates it before proceeding. The target commit is noted in the 🔧 Test Setup footer. |
--generate-drafts |
flag | --generate-drafts |
Generate Playwright draft test scripts for the repo's active QA scenarios and save them in the QA Hub for review. Requires the QA Hub add-on and at least one active scenario. See Playwright draft generation below. |
--draft-scope |
repo | all |
--draft-scope=all |
Scope of scenarios to consider for draft generation. repo (default) includes scenarios scoped to the current repo plus global (unscoped) scenarios. all includes scenarios from all repos under the organisation. Only applies when --generate-drafts is active. |
--draft-max |
integer (1–25) | --draft-max=10 |
Maximum number of drafts to generate per run. Default: 5. Only applies when --generate-drafts is active. |
--draft-scenario-ids |
comma-separated IDs | --draft-scenario-ids=id1,id2 |
Restrict draft generation to specific scenario IDs. When provided, only the listed scenarios are candidates (after scope filtering). |
--draft-categories |
comma-separated | --draft-categories=smoke,e2e |
Restrict draft generation to scenarios matching one of the listed categories (smoke, regression, e2e, accessibility, visual). |
--draft-priorities |
comma-separated | --draft-priorities=P0,P1 |
Restrict draft generation to scenarios with the listed priorities (P0, P1, P2). |
--draft-no-preview |
flag | --draft-no-preview |
Allow draft generation even when no live preview URL is available. The generated script includes a PREVIEW_URL_PLACEHOLDER comment that must be replaced before running. See No-preview mode below. |
Playwright draft generation
Available on Premium BYOC, Pro, Enterprise, and Custom plans with the QA Hub add-on activated.
When you pass --generate-drafts, Previewops uses the LLM to write a Playwright .spec.ts script for each of the repo's active QA scenarios, then saves the drafts in the QA Hub under Generated Tests with status draft.
Requirements
- QA Hub add-on enabled for your organisation.
- A live preview URL (auto-detected or supplied via
--url). - At least one active QA scenario defined for the repo in the dashboard.
- An LLM configured (same requirement as AI code review).
How it works
- Scenarios are sorted P0-first. Up to 5 are drafted per run (override with
--draft-max). - An idempotency check runs for each scenario — if a draft already exists for this scenario at the current commit SHA, it is skipped to avoid duplicates.
- The LLM generates a Playwright script following stability best practices:
data-testidlocators,waitForLoadState()instead ofwaitForTimeout(), and a 30-second cold-start timeout on the firstgoto()call for preview environments. - Each generated script is saved as
status: draftin the QA Hub. No script is executed automatically. - The QA report footer shows how many drafts were generated with a link to the QA Hub.
Selecting which scenarios to draft
By default, Previewops drafts up to 5 scenarios scoped to the current repo (plus global/unscoped scenarios), sorted P0-first. Use the selector flags to narrow or change this:
# Only draft P0 smoke scenarios, up to 10
/qa-previewops --generate-drafts --draft-priorities=P0 --draft-categories=smoke --draft-max=10
# Draft a specific pair of scenarios by ID
/qa-previewops --generate-drafts --draft-scenario-ids=uuid1,uuid2
# Include scenarios from all repos in the org (not just this repo)
/qa-previewops --generate-drafts --draft-scope=all --draft-max=8
All selector flags are optional and additive — you can combine any of them. The default behaviour (5 drafts, repo scope, all categories and priorities) is preserved when no selector flags are provided.
No-preview mode
By default, draft generation requires a live preview URL because the LLM uses the URL to generate accurate goto() calls and assertions. When no preview is available, you can still generate structural drafts:
/qa-previewops --generate-drafts --draft-no-preview --draft-priorities=P0
Scripts generated in no-preview mode contain https://PREVIEW_URL_PLACEHOLDER as the target URL. Replace this value before running the test. The Generated Tests card in the QA Hub shows a warning badge on scripts generated without a preview.
Note: No-preview mode is useful for seeding your test library before a preview environment has been deployed, but scripts should be reviewed and the placeholder replaced before they are approved for automated runs.
Reviewing and approving drafts
Open the QA Hub in the dashboard (QA → Generated Tests). Each draft can be:
- Approved — moves to
awaiting-approvaland is queued for a test run against the preview URL. - Rejected — removed from the queue; re-running
/qa-previewops --generate-draftson a new commit generates a fresh draft. - Edited — the script is shown in a code editor before approval.
Controlling draft generation via .previewops.yaml
The qa.generateDrafts field lets you set a default without requiring the CLI flag each time:
qa:
# 'auto' (default): uses the org-level dashboard setting.
# true: always generate drafts when the billing gate is met.
# false: never generate drafts — overrides the CLI flag and org setting.
generateDrafts: auto
The full precedence order (highest wins):
qa.generateDrafts: falsein.previewops.yaml→ always off (cannot be overridden by the CLI flag)--generate-draftsCLI flag → on (subject to billing gate)qa.generateDrafts: truein.previewops.yaml→ on (subject to billing gate)- Org-level dashboard setting → on/off (subject to billing gate)
- Default → off
Who can trigger the command
Any collaborator with Write, Maintain, or Admin permission on the repository. Comments from users with read-only access are silently ignored.
Frequently asked questions
Does QA testing require a deployed preview?
No. The test coverage mapping and AI code review run on the PR diff — no live URL needed. HTTP smoke tests require a preview URL, but if one isn't available that section is simply omitted.
What happens if I run /qa-previewops again without pushing a new commit?
Previewops detects the existing QA comment for that commit and updates it in place rather than posting a new one. You won't get duplicate comments.
Does QA post a commit status / Check Run?
Yes — after posting the report, Previewops creates a GitHub Check Run named previewops / QA on the PR head commit. The check shows failure if any high-severity issue was found, and success otherwise.
I have multiple active previews for this PR. Which one does QA test against?
By default, Previewops uses the most recently deployed active preview for the PR. To target a specific one, use --provider=<key> (e.g. --provider=render) or --env=<name> to match by environment name. You can also combine both flags. If no preview matching your filter is found, the AI analysis and coverage report still run — only the HTTP smoke test section is skipped.
Which AI model is used?
The model name is shown in the 🔧 Test Setup collapsible at the bottom of every report. For Pro, Enterprise, and Custom plan users, the model is determined by the operator's Previewops configuration. For Premium BYOC users with the Custom LLM add-on, it reflects the exact model you selected when saving your credentials — for example gpt-4-turbo or claude-sonnet-4-5. Contact your team's Previewops administrator or see plans.md to learn more.
Why is my file shown as uncovered when I know a test exists?
The mapping uses the test files that exist at the PR head. If your test file was added in the same PR and is already committed, it should be detected. If the test file uses an unusual naming convention or lives in an unexpected location, it may fall through the fuzzy fallback. Primary mapping conventions are listed in the Test coverage table above.
What happens if the AI is temporarily unavailable?
Previewops retries the AI call automatically before falling back. If all attempts fail, the code review section shows a short error message and the rest of the report (coverage, HTTP tests if applicable) is still posted.
Is the diff sent to a third-party AI service?
Yes — when AI features are enabled, the PR diff is sent to the configured LLM provider (OpenAI or Anthropic). File patches are truncated to 2 000 characters each by default (operators can raise this up to 10 000 via LLM_MAX_PATCH_CHARS), and no repository access tokens or credentials are included in the payload. Self-hosted operators control which provider is used. Contact your Previewops administrator for details.
Related
- qa-hub.md — Dashboard-based Playwright test library, suite management, and runs against preview URLs (Pro / Enterprise / Custom)
- commands.md — Full command reference including
/qa-previewopsoptions - plans.md — Plan comparison and AI feature access
- performance-testing.md — Run k6 load tests against previews with
/perf-previewops