GitLab CI/CD Integration with QA Sphere
Automatically upload test results from your GitLab CI/CD pipelines to QA Sphere using the QAS CLI tool. This integration eliminates manual result entry and provides instant visibility into your automated test results.
What You'll Achieve
With this integration, every time your GitLab pipeline runs:
- Test results automatically upload to QA Sphere
- New test runs are created with pipeline information
- Tests are matched to existing QA Sphere test cases
- Pass/fail status, execution time, and screenshots are recorded
- Test history and trends are tracked over time
Prerequisites
Before starting, ensure you have:
- A GitLab project with automated tests (Playwright, Cypress, Jest, etc.)
- Tests configured to generate JUnit XML format results
- A QA Sphere account with Test Runner role or higher
- Test cases in QA Sphere with markers (e.g.,
BD-001,PRJ-123)
How It Works

- Your pipeline runs automated tests
- Tests generate JUnit XML results file
- QAS CLI tool reads the XML file
- CLI matches tests to QA Sphere cases using markers
- Results are uploaded and appear in QA Sphere
Setup Steps
Step 1: Create QA Sphere API Key
- Log into your QA Sphere account
- Click the gear icon ⚙️ in the top right → Settings
- Navigate to API Keys
- Click Create API Key
- Copy and save the key - you won't see it again!
Your API key format: t123.ak456.abc789xyz
Step 2: Configure GitLab Variables
Add these secrets to your GitLab project:
- Go to your GitLab project
- Navigate to Settings → CI/CD → Variables
- Click Add variable and create:
| Key | Value | Flags |
|---|---|---|
QAS_TOKEN | Your API key (e.g., t123.ak456.abc789xyz) | Protected, Masked |
QAS_URL | Your QA Sphere URL (e.g., https://company.eu1.qasphere.com) | Protected |
- Click Add variable to save
Never commit API keys to your repository. Always use GitLab CI/CD variables.
Step 3: Add Test Case Markers
Ensure your test names include QA Sphere markers in the format PROJECT-SEQUENCE.
These markers can be found in QA Sphere interface for each test case separately.

Playwright Example:
test('BD-001: User can login with valid credentials', async ({ page }) => {
await page.goto('https://example.com/login');
await page.fill('#username', '[email protected]');
await page.fill('#password', 'password123');
await page.click('#login-button');
await expect(page).toHaveURL('/dashboard');
});
test('BD-002: User sees error with invalid credentials', async ({ page }) => {
// test implementation
});
Cypress Example:
describe('Login Flow', () => {
it('BD-001: should login successfully with valid credentials', () => {
cy.visit('/login');
cy.get('#username').type('[email protected]');
cy.get('#password').type('password123');
cy.get('#login-button').click();
cy.url().should('include', '/dashboard');
});
});
Jest Example:
describe('API Tests', () => {
test('BD-015: GET /users returns user list', async () => {
const response = await fetch('/api/users');
expect(response.status).toBe(200);
const data = await response.json();
expect(data).toHaveLength(5);
});
});
Step 4: Configure Test Framework
Configure your test framework to generate JUnit XML output:
Playwright Configuration
// playwright.config.js
const { defineConfig } = require('@playwright/test');
module.exports = defineConfig({
testDir: './tests',
timeout: 30000,
// JUnit reporter for CI/CD
reporter: [
['list'], // Console output
['junit', { outputFile: 'junit-results/results.xml' }] // For QA Sphere
],
use: {
headless: true,
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
projects: [
{ name: 'chromium', use: { browserName: 'chromium' } },
{ name: 'firefox', use: { browserName: 'firefox' } },
{ name: 'webkit', use: { browserName: 'webkit' } },
],
});
Cypress Configuration
// cypress.config.js
const { defineConfig } = require('cypress');
module.exports = defineConfig({
e2e: {
reporter: 'cypress-multi-reporters',
reporterOptions: {
configFile: 'reporter-config.json'
}
}
});
// reporter-config.json
{
"reporterEnabled": "spec, mocha-junit-reporter",
"mochaJunitReporterReporterOptions": {
"mochaFile": "junit-results/results.xml"
}
}
Jest Configuration
// jest.config.js
module.exports = {
reporters: [
'default',
['jest-junit', {
outputDirectory: './junit-results',
outputName: 'results.xml',
classNameTemplate: '{classname}',
titleTemplate: '{title}'
}]
]
};
Step 5: Create GitLab Pipeline
Create or update .gitlab-ci.yml in your repository root:
For Playwright Projects
stages:
- test
- report
variables:
# Disable Husky git hooks in CI
HUSKY: 0
# Run Playwright tests
test:
stage: test
# IMPORTANT: Match image version to your @playwright/test package version
image: mcr.microsoft.com/playwright:v1.51.1-jammy
script:
- npm ci
- npx playwright test
artifacts:
when: always # Upload artifacts even if tests fail
paths:
- junit-results/
- test-results/
- playwright-report/
reports:
junit: junit-results/results.xml
expire_in: 1 week
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_COMMIT_BRANCH == "develop"
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# Upload results to QA Sphere
upload-to-qasphere:
stage: report
image: node:18
needs:
- job: test
artifacts: true
before_script:
- npm install -g qas-cli
script:
# Upload test results (automatically creates new run)
- qasphere junit-upload ./junit-results/results.xml
- echo "✅ Test results uploaded to QA Sphere"
when: always
allow_failure: true
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_COMMIT_BRANCH == "develop"
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
For Cypress Projects
stages:
- test
- report
test:
stage: test
image: cypress/browsers:node18.12.0-chrome107
script:
- npm ci
- npx cypress run
artifacts:
when: always
paths:
- junit-results/
- cypress/videos/
- cypress/screenshots/
reports:
junit: junit-results/results.xml
expire_in: 1 week
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_COMMIT_BRANCH == "develop"
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
upload-to-qasphere:
stage: report
image: node:18
needs:
- job: test
artifacts: true
before_script:
- npm install -g qas-cli
script:
- qasphere junit-upload ./junit-results/results.xml
- echo "✅ Results uploaded to QA Sphere"
when: always
allow_failure: true
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_COMMIT_BRANCH == "develop"
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
For Jest Projects
stages:
- test
- report
test:
stage: test
image: node:18
script:
- npm ci
- npm test
artifacts:
when: always
paths:
- junit-results/
- coverage/
reports:
junit: junit-results/results.xml
expire_in: 1 week
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_COMMIT_BRANCH == "develop"
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
upload-to-qasphere:
stage: report
image: node:18
needs:
- job: test
artifacts: true
before_script:
- npm install -g qas-cli
script:
- qasphere junit-upload ./junit-results/results.xml
- echo "✅ Results uploaded to QA Sphere"
when: always
allow_failure: true
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_COMMIT_BRANCH == "develop"
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
Step 6: Push and Verify
- Commit your changes:
git add .gitlab-ci.yml playwright.config.js # or your config files
git commit -m "Add GitLab CI/CD with QA Sphere integration"
git push origin main
-
Monitor the pipeline:
- Go to GitLab → Build → Pipelines
- Watch your pipeline execute
- Check both
testandupload-to-qaspherestages

-
Verify in QA Sphere:
- Log into QA Sphere
- Navigate to your project → Test Runs
- See the new run with your test results