When E2E is flaky, the worst workflow is this: someone changes code to skip a scenario, pushes, waits for CI, then changes it back later. It’s slow, noisy, and it pollutes git history.
So we built Jira Skipper.
Idea: control skipping from a single Jira ticket description. No code changes needed. Update Jira, rerun pipeline, done.
How it works
- We keep one ticket as the “source of truth”, for example
QE-1234 - The ticket description contains scenario identifiers, one per line
- Global setup fetches the description before tests run
- We store it in an env var,
process.env.SCENARIOS, as JSON - Your tests (or a wrapper) read that list and skip matching scenarios
This is the core helper:
export const jiraSkipperTicketId = 'QE-1234';
export const jiraSkipper = async (): Promise<void> => {
try {
const requestContext = await request.newContext();
const jiraService = new JiraService(requestContext);
const ticketDetails = await jiraService.getTicketDetails(jiraSkipperTicketId);
process.env.SCENARIOS = JSON.stringify(ticketDetails.split('\n'));
const content =
`${colors.fgCyan}[INFO]${colors.reset} Checking JIRA Ticket ${colors.fgYellow}${jiraSkipperTicketId}${colors.reset} for scenarios to skip\n` +
`${colors.fgCyan}[INFO]${colors.reset} Fetching description for JIRA ticket: ${colors.fgYellow}${jiraSkipperTicketId}${colors.reset}\n` +
`${colors.fgGreen}[SUCCESS]${colors.reset} JIRA_SKIPPER is set. Setting up event listeners for scenario skipping.`;
console.log(createBox('📋 JIRA Configuration', content.trim()));
await requestContext.dispose();
} catch {
const errorContent = `${colors.fgRed}[WARNING]${colors.reset} No valid JIRA ticket ID found or unable to connect to JIRA`;
console.log(createBox('📋 JIRA Configuration', errorContent));
}
};
And it runs in globalSetup:
await clearTestOutputs();
await jiraSkipper();
logBaseUrl();
Fresh project setup guide (Playwright + TypeScript)
1) Create the project
mkdir pw-jira-skipper
cd pw-jira-skipper
npm init -y
npm i -D @playwright/test typescript ts-node
npx playwright install
Create a basic TS config:
npx tsc --init
Make sure these are set in tsconfig.json (keep it simple):
"module": "NodeNext""moduleResolution": "NodeNext""target": "ES2022""strict": true
2) Add Playwright config with globalSetup
Create playwright.config.ts:
import {defineConfig} from '@playwright/test';
export default defineConfig({
testDir: './tests',
globalSetup: './test-support/setup/globalSetup',
});
Create test-support/setup/globalSetup.ts:
import {jiraSkipper} from '../helpers/JiraSkipper';
const globalSetup = async (): Promise<void> => {
await jiraSkipper();
};
export default globalSetup;
3) Add the Jira Skipper helper
Create test-support/helpers/JiraSkipper.ts and paste your helper.
You’ll also need:
JiraService(fetch ticket description via Jira REST API)colors+createBox(optional, just for nice logs)
If you want a minimal version with no fancy logging, keep it dead simple:
- fetch description
- split by
\n - set
process.env.SCENARIOS
4) Make tests actually skip based on the list
This is the missing piece most people forget. The skipper only loads data. You still need to apply it.
Simple pattern: tag tests with an ID and skip if ID is in the Jira list.
Example tests/example.spec.ts:
import {test, expect} from '@playwright/test';
const getSkipList = (): string[] => {
const raw = process.env.SCENARIOS;
if (!raw) return [];
try {
return JSON.parse(raw) as string[];
} catch {
return [];
}
};
const shouldSkip = (id: string): boolean => {
const list = getSkipList().map((x) => x.trim()).filter(Boolean);
return list.includes(id);
};
test('checkout flow [SCN-1234]', async ({page}) => {
test.skip(shouldSkip('SCN-1234'), 'Skipped by Jira Skipper');
await page.goto('https://example.com');
await expect(page).toHaveTitle(/Example/);
});
Now Jira controls skipping. No commits.
5) Define the Jira ticket format
In Jira ticket QE-1234 description:
SCN-1234
SCN-8890
SCN-4455
One per line, nothing fancy. Keep it boring.
6) CI usage
- Update ticket description
- Rerun pipeline
- The same codebase behaves differently, based on Jira
Why this is worth it
- no “temporary skip” PRs
- less noise in git history
- faster reaction during incidents
- skips are visible and auditable in Jira
- ownership is clear, whoever edits Jira owns the skip decision