Qase JavaScript Commons
This module is an SDK for developing test reporters for Qase TMS.
It's using qase-api-client and qase-api-v2-client as API clients, and all Qase reporters are, in turn,
using this package.
You should use it if you're developing your own test reporter for a special-purpose framework.
To report results from tests using a popular framework or test runner, don't install this module directly and use the corresponding reporter module instead:
Installation
npm install qase-javascript-commons
Configuration
Qase JS Reporters can be configured in multiple ways:
- using a config file
qase.config.json - using environment variables
All configuration options are listed in the table below:
| Description | Config file | Environment variable | Default value | Required | Possible values |
|---|---|---|---|---|---|
| Common | |||||
| Mode of reporter | mode |
QASE_MODE |
off |
No | testops, testops_multi, report, off |
| Fallback mode of reporter | fallback |
QASE_FALLBACK |
off |
No | testops, testops_multi, report, off |
| Environment | environment |
QASE_ENVIRONMENT |
undefined | No | Any string |
| Root suite | rootSuite |
QASE_ROOT_SUITE |
undefined | No | Any string |
| Enable debug logs | debug |
QASE_DEBUG |
False |
No | True, False |
Enable capture logs from stdout and stderr |
testops.defect |
QASE_CAPTURE_LOGS |
False |
No | True, False |
Map test result statuses to different values (format: fromStatus=toStatus) |
statusMapping |
QASE_STATUS_MAPPING |
undefined | No | Object mapping statuses (e.g., {"invalid": "failed", "skipped": "passed"}) |
| Logging configuration | |||||
| Enable/disable console output for reporter logs | logging.console |
QASE_LOGGING_CONSOLE |
True |
No | True, False |
| Enable/disable file output for reporter logs | logging.file |
QASE_LOGGING_FILE |
Same as debug setting |
No | True, False |
| Qase Report configuration | |||||
| Driver used for report mode | report.driver |
QASE_REPORT_DRIVER |
local |
No | local |
| Path to save the report | report.connection.path |
QASE_REPORT_CONNECTION_PATH |
./build/qase-report |
||
| Local report format | report.connection.format |
QASE_REPORT_CONNECTION_FORMAT |
json |
json, jsonp |
|
| Qase TestOps configuration | |||||
| Token for API access | testops.api.token |
QASE_TESTOPS_API_TOKEN |
undefined | Yes | Any string |
Qase API host. Set eu.qase.io for the EU cluster. Enterprise users can give a host (example.qase.io, resolved as https://api-example.qase.io) or a full base URL used verbatim (https://qase.internal, http://localhost:8080) |
testops.api.host |
QASE_TESTOPS_API_HOST |
qase.io |
No | Host name or base URL |
| Qase enterprise environment | testops.api.enterprise |
QASE_TESTOPS_API_ENTERPRISE |
False |
No | True, False |
| Timeout of a single API request, in seconds | testops.api.timeout |
QASE_TESTOPS_API_TIMEOUT |
30 |
No | Any positive integer |
| How many extra attempts a failed result upload gets | testops.api.retries |
QASE_TESTOPS_API_RETRIES |
3 |
No | Any non-negative integer |
First backoff step between result upload attempts, in seconds (doubles per attempt, capped at 30s; a Retry-After header wins over it) |
testops.api.retryBackoff |
QASE_TESTOPS_API_RETRY_BACKOFF |
1 |
No | Any positive number |
Code of your project, which you can take from the URL: https://app.qase.io/project/DEMOTR - DEMOTR is the project code |
testops.project |
QASE_TESTOPS_PROJECT |
undefined | Yes | Any string |
| Qase test run ID | testops.run.id |
QASE_TESTOPS_RUN_ID |
undefined | No | Any integer |
| Qase test run title | testops.run.title |
QASE_TESTOPS_RUN_TITLE |
Automated run <Current date and time> |
No | Any string |
| Qase test run description | testops.run.description |
QASE_TESTOPS_RUN_DESCRIPTION |
<Framework name> automated run |
No | Any string |
| Qase test run complete | testops.run.complete |
QASE_TESTOPS_RUN_COMPLETE |
True |
True, False |
|
| Array of tags to be added to the test run | testops.run.tags |
QASE_TESTOPS_RUN_TAGS |
[] |
No | Array of strings |
| External link to associate with test run (e.g., Jira ticket) | testops.run.externalLink |
QASE_TESTOPS_RUN_EXTERNAL_LINK |
undefined | No | JSON object with type (jiraCloud or jiraServer) and link (e.g., PROJ-123) |
| Qase test plan ID | testops.plan.id |
QASE_TESTOPS_PLAN_ID |
undefined | No | Any integer |
| Size of batch for sending test results | testops.batch.size |
QASE_TESTOPS_BATCH_SIZE |
200 |
No | 1–200 |
| Enable defects for failed test cases | testops.defect |
QASE_TESTOPS_DEFECT |
False |
No | True, False |
| Enable/disable attachment uploads | testops.uploadAttachments |
QASE_TESTOPS_UPLOAD_ATTACHMENTS |
true |
No | True, False |
| How many attachment batches are uploaded at the same time | testops.attachments.concurrency |
QASE_TESTOPS_ATTACHMENTS_CONCURRENCY |
4 |
No | Integer, clamped to 1..16 |
| Timeout of a single attachment upload request, in seconds | testops.attachments.timeout |
QASE_TESTOPS_ATTACHMENTS_TIMEOUT |
120 |
No | Any positive integer |
| Filter test results by status (comma-separated list of statuses to exclude from reporting) | testops.statusFilter |
QASE_TESTOPS_STATUS_FILTER |
undefined | No | Array of strings (passed, failed, skipped, invalid) |
Configuration values to create/find in groups (format: group1=value1,group2=value2) |
testops.configurations.values |
QASE_TESTOPS_CONFIGURATIONS_VALUES |
undefined | No | Comma-separated key=value pairs |
| Create configuration groups if they don't exist | testops.configurations.createIfNotExists |
QASE_TESTOPS_CONFIGURATIONS_CREATE_IF_NOT_EXISTS |
false |
No | True, False |
| Enable public report link generation and display after test run completion | testops.showPublicReportLink |
QASE_TESTOPS_SHOW_PUBLIC_REPORT_LINK |
False |
No | True, False |
| Qase TestOps Multi-Project configuration | |||||
| Default project code for tests without explicit project mapping | testops_multi.default_project |
N/A (use config file) | First project in projects |
No | Any string (must match one of projects[].code) |
| Array of project configurations | testops_multi.projects |
N/A (use config file) | [] |
Yes** | Array of objects |
| Project code | testops_multi.projects[].code |
N/A | undefined | Yes** | Any string |
| Project-specific test run title | testops_multi.projects[].run.title |
N/A | Same as single-project default | No | Any string |
| Project-specific test run description | testops_multi.projects[].run.description |
N/A | Same as single-project default | No | Any string |
| Project-specific test run complete | testops_multi.projects[].run.complete |
N/A | true |
No | true, false |
| Project-specific test run ID | testops_multi.projects[].run.id |
N/A | undefined | No | Any integer |
| Project-specific test plan ID | testops_multi.projects[].plan.id |
N/A | undefined | No | Any integer |
| Project-specific environment | testops_multi.projects[].environment |
N/A | Uses global environment if not set |
No | Any string |
** Required when using testops_multi mode
Example qase.config.json config
{
"mode": "testops",
"fallback": "report",
"debug": false,
"environment": "local",
"captureLogs": false,
"statusMapping": {
"invalid": "failed",
"skipped": "passed"
},
"logging": {
"console": true,
"file": true
},
"report": {
"driver": "local",
"connection": {
"local": {
"path": "./build/qase-report",
"format": "json"
}
}
},
"testops": {
"api": {
"token": "<token>",
"host": "qase.io",
"timeout": 30,
"retries": 3,
"retryBackoff": 1
},
"run": {
"title": "Regress run",
"description": "Regress run description",
"complete": true,
"tags": ["tag1", "tag2"],
"externalLink": {
"type": "jiraCloud",
"link": "PROJ-123"
}
},
"defect": false,
"project": "<project_code>",
"uploadAttachments": true,
"showPublicReportLink": true,
"statusFilter": ["passed", "skipped"],
"batch": {
"size": 100
},
"attachments": {
"concurrency": 4,
"timeout": 120
},
"configurations": {
"values": [
{
"name": "group1",
"value": "value1"
},
{
"name": "group2",
"value": "value2"
}
],
"createIfNotExists": true
}
}
}
Multi-Project Configuration (testops_multi mode)
Example qase.config.json for multi-project reporting:
{
"mode": "testops_multi",
"fallback": "report",
"debug": false,
"environment": "local",
"logging": {
"console": true,
"file": false
},
"report": {
"driver": "local",
"connection": {
"local": {
"path": "./build/qase-report",
"format": "json"
}
}
},
"testops": {
"api": {
"token": "<token>",
"host": "qase.io"
},
"batch": { "size": 100 },
"showPublicReportLink": true
},
"testops_multi": {
"default_project": "PROJ1",
"projects": [
{
"code": "PROJ1",
"run": {
"title": "PROJ1 Multi-Project Run",
"description": "Test run for PROJ1 project",
"complete": true
},
"plan": { "id": 1 },
"environment": "staging"
},
{
"code": "PROJ2",
"run": {
"title": "PROJ2 Multi-Project Run",
"description": "Test run for PROJ2 project",
"complete": true
},
"environment": "production"
}
]
}
}
Environment Variables Example
# Set external link for Jira Cloud
export QASE_TESTOPS_RUN_EXTERNAL_LINK='{"type":"jiraCloud","link":"PROJ-123"}'
# Set external link for Jira Server
export QASE_TESTOPS_RUN_EXTERNAL_LINK='{"type":"jiraServer","link":"PROJ-456"}'
# Filter out test results with specific statuses
export QASE_TESTOPS_STATUS_FILTER="passed,failed"
# Map test result statuses
export QASE_STATUS_MAPPING="invalid=failed,skipped=passed"
# Logging configuration
export QASE_LOGGING_CONSOLE=false # Disable console output
export QASE_LOGGING_FILE=true # Enable file output
# Enable public report link generation
export QASE_TESTOPS_SHOW_PUBLIC_REPORT_LINK=true
Multi-Project Support
The multi-project feature allows you to send test results to multiple Qase projects simultaneously, with different test case IDs for each project. This is useful when:
- You need to report the same test to different projects
- Different projects track the same functionality with different test case IDs
- You want to maintain separate test runs for different environments or teams
How It Works
- Configure multiple projects in the
testops_multi.projectsarray - Each project can have its own run configuration (title, description, plan, environment)
- Use framework-specific helpers or markers to map test cases to projects (see table below)
- Tests without explicit project mapping are sent to
default_project(or the first project). Results without any case ID are also sent to the default project without linking to a test case.
Framework-Specific Documentation
For detailed framework-specific documentation on multi-project support, see:
- Cypress Multi-Project Guide —
qase.projects(mapping, it(...)), title markers - Playwright Multi-Project Guide —
qase.projects(mapping),qase.projectsTitle(), annotations - Jest Multi-Project Guide —
qase.projects(mapping, name) - Vitest Multi-Project Guide —
addQaseProjects(name, mapping) - Mocha Multi-Project Guide —
qase.projects(mapping, name) - WDIO Multi-Project Guide —
qase.projects(mapping, name) - CucumberJS Multi-Project Guide — tags
@qaseid.PROJ(ids)in feature files - Newman — comments in test script
- TestCafe —
qase.projects(mapping).create()intest.meta()
Example Usage
For runnable examples, see the multi-project examples directory.
Configuration
Set mode to testops_multi and add a testops_multi section with default_project and projects:
{
"mode": "testops_multi",
"testops": {
"api": { "token": "<token>", "host": "qase.io" },
"batch": { "size": 100 }
},
"testops_multi": {
"default_project": "PROJ1",
"projects": [
{
"code": "PROJ1",
"run": { "title": "Project 1 Run", "complete": true },
"plan": { "id": 1 },
"environment": "staging"
},
{
"code": "PROJ2",
"run": { "title": "Project 2 Run", "id": 123, "complete": true },
"environment": "production"
}
]
}
}
Test case mapping
For each result you can set testops_project_mapping (or use the helpers on TestResultType):
testops_project_mapping:Record<string, number[]>— project code → list of test case IDs.- Backward compatibility: if
testops_project_mappingis empty or missing, the result usestestops_idand is sent todefault_project(or the first project).
How to specify projects and IDs in tests
Use the syntax below only when mode is testops_multi. For single-project mode (testops), keep using the usual single-project syntax (e.g. (Qase ID: 123) or qase(1, 'name')).
Use the helper methods below so project codes and IDs are formatted correctly (same idea as qase(id, name) for single-project).
Vitest
- Single project:
addQaseId(name, caseIds)→"name (Qase ID: 1,2)". - Multi-project:
addQaseProjects(name, mapping)→"name (Qase PROJ1: 1,2) (Qase PROJ2: 3)".
import { addQaseId, addQaseProjects } from 'vitest-qase-reporter/vitest';
// Single project (legacy)
it(addQaseId('login flow', [100]), async () => { ... });
// Multi-project — use helper so title is formatted correctly
it(addQaseProjects('login flow', { PROJ1: [100], PROJ2: [200] }), async () => { ... });
// Multiple IDs per project
it(addQaseProjects('checkout', { PROJ1: [10, 11], PROJ2: [20] }), async () => { ... });
Jest
- Single project:
qase(caseId, name). - Multi-project:
qase.projects(mapping, name).
const { qase } = require('jest-qase-reporter');
// Single project
test(qase(100, 'login flow'), () => { ... });
// Multi-project
test(qase.projects({ PROJ1: [100], PROJ2: [200] }, 'login flow'), () => { ... });
Cypress
- Single project:
qase(caseId, test)(mutates test title) or title string(Qase ID: 100). - Multi-project:
qase.projects(mapping, name)returns the title string; use it as the first argument toit().
const { qase } = require('cypress-qase-reporter');
// Single project
it(qase(100, 'login flow'), () => { ... }); // if qase receives test
it('login flow (Qase ID: 100)', () => { ... });
// Multi-project — use helper
it(qase.projects({ PROJ1: [100], PROJ2: [200] }, 'login flow'), () => { ... });
Tags (Cucumber/BDD): @qaseid.PROJ1(100) @qaseid.PROJ2(200).
Playwright
- Single project:
qase(caseId, name)orqase.id([1, 2])in test. - Multi-project:
qase.projects(mapping)inside the test (metadata, not title).
const { test } = require('@playwright/test');
const { qase } = require('playwright-qase-reporter');
// Multi-project: set mapping inside the test
test('login and checkout', async ({ page }) => {
qase.projects({ PROJ1: [1, 2], PROJ2: [3] });
await page.goto('/login');
// ...
});
// Single project
test(qase(1, 'single project test'), async ({ page }) => { ... });
Mocha
- Single project:
qase(caseId, name). - Multi-project:
qase.projects(mapping, name).
const { qase } = require('mocha-qase-reporter');
it(qase(100, 'login flow'), function () { ... });
it(qase.projects({ PROJ1: [100], PROJ2: [200] }, 'login flow'), function () { ... });
WDIO (WebdriverIO)
- Single project:
qase(qaseId, name). - Multi-project:
qase.projects(mapping, name).
const { qase } = require('wdio-qase-reporter');
it(qase(1, 'should work'), async () => { ... });
it(qase.projects({ PROJ1: [100], PROJ2: [200] }, 'Login flow'), async () => { ... });
You can still type markers manually in the title (e.g. (Qase PROJ1: 100) (Qase PROJ2: 200)); the helpers avoid typos and keep the format consistent.
Summary
| Framework | Single project (legacy) | Multi-project (project: IDs in title or API) |
|---|---|---|
| Vitest | (Qase ID: 123) in title |
(Qase PROJ1: 123) (Qase PROJ2: 456) in title |
| Jest | (Qase ID: 123) in title |
(Qase PROJ1: 123) (Qase PROJ2: 456) in title |
| Cypress | (Qase ID: 123) or @qaseid(123) |
(Qase PROJ1: 123) (Qase PROJ2: 456) or @qaseid.PROJ1(123) @qaseid.PROJ2(456) |
| Playwright | qase(1, 'name') or metadata |
qase.projects({ PROJ1: [1, 2], PROJ2: [3] }) in test |
| Mocha | (Qase ID: 123) in title |
(Qase PROJ1: 123) (Qase PROJ2: 456) in title |
| WDIO | (Qase ID: 123) in title |
(Qase PROJ1: 123) (Qase PROJ2: 456) in title |
- Title format:
(Qase PROJECT_CODE: id1,id2,...)— space afterQase, project code (letters, numbers, underscores), colon, then one or more IDs separated by commas. Several projects = several such blocks in one title. - Cypress tags:
@qaseid.PROJECT_CODE(id1,id2,...). - Playwright:
qase.projects({ PROJECT_CODE: [id1, id2, ...], ... }); project codes must matchtestops_multi.projects[].code.
Tests that do not specify any multi-project mapping are sent to default_project (or the first project) using their usual single-project ID, if any.