Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,21 @@ const config: PlaywrightTestConfig = {
}
```

### Optional: Ignore failed retries
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please move this section before/after custom field


To avoid duplicate "blocked" test results in Zephyr (especially if the test passes after a retry), you can enable the following option:

```ts
reporter: [['playwright-zephyr', {
host: 'https://jira.your-company-domain.com/',
authorizationToken: 'SVSdrtwgDSA312342--',
projectKey: 'JARV',
ignoreFailedRetries: true, // ← Add this to ignore intermediate retry failures
}]]
```

When enabled, only the first successful or skipped retry will be reported. Failed retries are skipped unless the test ultimately fails all attempts.

If you want to use **Cloud** reporter, you need to specify `cloud` option:

```typescript
Expand Down
5 changes: 5 additions & 0 deletions src/cloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ export default class ZephyrReporter implements Reporter {
private projectKey!: string;
private testCaseKeyPattern = /\[(.*?)\]/;
private options: ZephyrOptions;
private readonly ignoreFailedRetries: boolean;

constructor(options: ZephyrOptions) {
this.options = validateOptions(options);
this.ignoreFailedRetries = this.options.ignoreFailedRetries || false;
}

async onBegin() {
Expand All @@ -27,6 +29,9 @@ export default class ZephyrReporter implements Reporter {
}

onTestEnd(test: TestCase, result: TestResult) {
if (this.ignoreFailedRetries && test.retries !== result.retry && result.status !== 'passed') {
return;
}
if (test.title.match(this.testCaseKeyPattern) && test.title.match(this.testCaseKeyPattern)!.length > 1) {
const [, testCaseId] = test.title.match(this.testCaseKeyPattern)!;
const testCaseKey = `${this.projectKey}-${testCaseId}`;
Expand Down
1 change: 1 addition & 0 deletions src/convert-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export interface ZephyrOptions extends AxiosRequestConfig {
projectKey: string;
testCycle?: ZephyrTestCycle;
nodeInternalTlsRejectUnauthorized?: '0' | '1'; // NODE_TLS_REJECT_UNAUTHORIZED
ignoreFailedRetries?: boolean;
}

export type ZephyrStatus = 'Passed' | 'Failed' | 'Blocked' | 'Not Executed' | 'In Progress';
Expand Down
5 changes: 5 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ class ZephyrReporter implements Reporter {
private projectKey!: string;
private testCaseKeyPattern = /\[(.*?)\]/;
private options: ZephyrOptions;
private readonly ignoreFailedRetries: boolean;
environment: string | undefined;

constructor(options: ZephyrOptions) {
this.options = options;
this.ignoreFailedRetries = options.ignoreFailedRetries || false;
}

async onBegin() {
Expand All @@ -32,6 +34,9 @@ class ZephyrReporter implements Reporter {
}

onTestEnd(test: TestCase, result: TestResult) {
if (this.ignoreFailedRetries && test.retries !== result.retry && result.status !== 'passed') {
return;
}
if (test.title.match(this.testCaseKeyPattern) && test.title.match(this.testCaseKeyPattern)!.length > 1) {
const [, projectName] = test.titlePath();
const [, testCaseId] = test.title.match(this.testCaseKeyPattern)!;
Expand Down
5 changes: 5 additions & 0 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ class ZephyrReporter implements Reporter {
private projectKey!: string;
private testCaseKeyPattern = /\[(.*?)\]/;
private options: ZephyrOptions;
private readonly ignoreFailedRetries: boolean;
environment: string | undefined;

constructor(options: ZephyrOptions) {
this.options = options;
this.ignoreFailedRetries = options.ignoreFailedRetries || false;
}

async onBegin() {
Expand All @@ -32,6 +34,9 @@ class ZephyrReporter implements Reporter {
}

onTestEnd(test: TestCase, result: TestResult) {
if (this.ignoreFailedRetries && test.retries !== result.retry && result.status !== 'passed') {
return;
}
if (test.title.match(this.testCaseKeyPattern) && test.title.match(this.testCaseKeyPattern)!.length > 1) {
const [, projectName] = test.titlePath();
const [, testCaseId] = test.title.match(this.testCaseKeyPattern)!;
Expand Down
18 changes: 18 additions & 0 deletions tests/ignore-retries.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { test, expect } from '@playwright/test';

/**
* This test suite simulates a test that passes after a retry.
* Make sure "ignoreFailedRetries" is set to true in the Playwright config
* before running this test to properly validate reporter behavior.
*/
test.describe.configure({ retries: 2 });

test('[C70] should pass after a retry and be reported once', async ({ page }, testInfo) => {
// Simulate a failure on first attempt
if (testInfo.retry === 0) {
expect(false).toBeTruthy();
}

await page.goto('https://playwright.dev');
await expect(page).toHaveTitle(/Playwright/);
});
1 change: 1 addition & 0 deletions types/zephyr.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface ZephyrOptions extends AxiosRequestConfig {
authorizationToken?: string;
projectKey: string;
environment?: string;
ignoreFailedRetries?: boolean;
}

export type ZephyrStatus = 'Pass' | 'Fail' | 'Blocked' | 'Not Executed' | 'In Progress';
Expand Down