From 095e5ecfb33dac06eea25ac780dd7bd99c9e56cd Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Mon, 22 Jan 2024 22:03:10 +0100 Subject: [PATCH 01/21] refactor: EPMRPP-84613 Playwright tags are parsed to the RP's attributes and attached to the tests --- CHANGELOG.md | 3 +++ .../reporter/attributesReporting.spec.ts | 4 ++-- .../reporter/launchesReporting.spec.ts | 22 +++++++++---------- .../reporter/retriesReporting.spec.ts | 2 +- .../reporter/startSuiteTestReporting.spec.ts | 4 ++-- src/reporter.ts | 5 ++++- 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da3f011..fa72b48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## [5.1.7] - 2024-01-22 +### Changed +- The Playwright's tags are parsed to the RP's attributes and attached to the tests. ## [5.1.6] - 2023-12-19 ### Fixed diff --git a/src/__tests__/reporter/attributesReporting.spec.ts b/src/__tests__/reporter/attributesReporting.spec.ts index f316f6e..712db7c 100644 --- a/src/__tests__/reporter/attributesReporting.spec.ts +++ b/src/__tests__/reporter/attributesReporting.spec.ts @@ -37,7 +37,7 @@ describe('attributes reporting', () => { titlePath: () => ['', suiteName, 'testTitle'], }; - test('reporter.testItems should be updated with attributes', () => { + test('@smoke reporter.testItems should be updated with attributes', () => { reporter.testItems = new Map([['testItemId', { id: 'tempTestItemId', name: 'testTitle' }]]); // @ts-ignore reporter.addAttributes(attributes, testCase); @@ -47,7 +47,7 @@ describe('attributes reporting', () => { expect(reporter.testItems).toEqual(expectedTestItems); }); - test('reporter.suitesInfo should be with attributes', () => { + test('@smoke reporter.suitesInfo should be with attributes', () => { // @ts-ignore reporter.addAttributes(attributes, testCase, suiteName); const expectedSuitesInfo = new Map([ diff --git a/src/__tests__/reporter/launchesReporting.spec.ts b/src/__tests__/reporter/launchesReporting.spec.ts index 5ba06e7..5b0e1a3 100644 --- a/src/__tests__/reporter/launchesReporting.spec.ts +++ b/src/__tests__/reporter/launchesReporting.spec.ts @@ -37,12 +37,12 @@ describe('start launch', () => { beforeAll(() => reporter.onBegin()); - test('client.startLaunch should be called with corresponding params', () => { + test('@smoke client.startLaunch should be called with corresponding params', () => { expect(reporter.client.startLaunch).toHaveBeenCalledTimes(1); expect(reporter.client.startLaunch).toHaveBeenCalledWith(startLaunchObj); }); - test('reporter.launchId should be set', () => { + test('@smoke reporter.launchId should be set', () => { expect(reporter.launchId).toEqual('tempLaunchId'); }); }); @@ -64,12 +64,12 @@ describe('start launch', () => { beforeAll(() => reporter.onBegin()); - test('client.startLaunch should be called with corresponding params', () => { + test('@smoke client.startLaunch should be called with corresponding params', () => { expect(reporter.client.startLaunch).toHaveBeenCalledTimes(1); expect(reporter.client.startLaunch).toHaveBeenCalledWith(startLaunchObj); }); - test('reporter.launchId should be set', () => { + test('@smoke reporter.launchId should be set', () => { expect(reporter.launchId).toEqual('tempLaunchId'); }); }); @@ -92,12 +92,12 @@ describe('start launch', () => { beforeAll(() => reporter.onBegin()); - test('client.startLaunch should be called with corresponding params', () => { + test('@smoke client.startLaunch should be called with corresponding params', () => { expect(reporter.client.startLaunch).toHaveBeenCalledTimes(1); expect(reporter.client.startLaunch).toHaveBeenCalledWith(startLaunchObj); }); - test('reporter.launchId should be set', () => { + test('@smoke reporter.launchId should be set', () => { expect(reporter.launchId).toEqual('tempLaunchId'); }); }); @@ -125,12 +125,12 @@ describe('start launch', () => { delete process.env.RP_LAUNCH_ID; }); - test('client.startLaunch should be called with corresponding params', () => { + test('@smoke client.startLaunch should be called with corresponding params', () => { expect(reporter.client.startLaunch).toHaveBeenCalledTimes(1); expect(reporter.client.startLaunch).toHaveBeenCalledWith(startLaunchObj); }); - test('reporter.launchId should be set', () => { + test('@smoke reporter.launchId should be set', () => { expect(reporter.launchId).toEqual('tempLaunchId'); }); }); @@ -144,7 +144,7 @@ describe('finish launch', () => { beforeAll(() => reporter.onEnd()); - test('launch should be finished', () => { + test('@smoke launch should be finished', () => { expect(reporter.client.finishLaunch).toHaveBeenCalledTimes(1); expect(reporter.client.finishLaunch).toHaveBeenCalledWith('tempLaunchId', { endTime: mockedDate, @@ -164,7 +164,7 @@ describe('finish launch', () => { beforeAll(() => reporter.onEnd()); - test('launch finish request should not be sent', () => { + test('@smoke launch finish request should not be sent', () => { expect(reporter.client.finishLaunch).toHaveBeenCalledTimes(0); expect(reporter.isLaunchFinishSend).toBe(true); }); @@ -186,7 +186,7 @@ describe('finish launch', () => { delete process.env.RP_LAUNCH_ID; }); - test('launch finish request should not be sent', () => { + test('@smoke launch finish request should not be sent', () => { expect(reporter.client.finishLaunch).toHaveBeenCalledTimes(0); expect(reporter.isLaunchFinishSend).toBe(true); }); diff --git a/src/__tests__/reporter/retriesReporting.spec.ts b/src/__tests__/reporter/retriesReporting.spec.ts index 59a82ae..020d396 100644 --- a/src/__tests__/reporter/retriesReporting.spec.ts +++ b/src/__tests__/reporter/retriesReporting.spec.ts @@ -38,7 +38,7 @@ describe('retries reporting', () => { const spyStartTestItem = jest.spyOn(reporter.client, 'startTestItem'); - test('client.startTestItem should be called with retry=true params', () => { + test('@smoke client.startTestItem should be called with retry=true params', () => { const parentId = 'tempTestItemId'; const expectedTestObj: StartTestObjType = { startTime: reporter.client.helpers.now(), diff --git a/src/__tests__/reporter/startSuiteTestReporting.spec.ts b/src/__tests__/reporter/startSuiteTestReporting.spec.ts index 76d02bb..3dd8167 100644 --- a/src/__tests__/reporter/startSuiteTestReporting.spec.ts +++ b/src/__tests__/reporter/startSuiteTestReporting.spec.ts @@ -75,7 +75,7 @@ describe('start reporting suite/test', () => { jest.clearAllMocks(); }); - test('client.startTestItem should be called with corresponding params to report suites and test item', () => { + test('@smoke client.startTestItem should be called with corresponding params to report suites and test item', () => { const expectedSuites = new Map([ [ rootSuite, @@ -149,7 +149,7 @@ describe('start reporting suite/test', () => { expect(reporter.testItems).toEqual(expectedTestItems); }); - test('client.startTestItem should not be called in case of launch finish request have been send', () => { + test('@smoke client.startTestItem should not be called in case of launch finish request have been send', () => { reporter.isLaunchFinishSend = true; // @ts-ignore reporter.onTestBegin(testCase); diff --git a/src/reporter.ts b/src/reporter.ts index d90ca33..a756ef3 100644 --- a/src/reporter.ts +++ b/src/reporter.ts @@ -94,6 +94,8 @@ export class RPReporter implements Reporter { extendTestDescriptionWithLastError: true, ...config, launchId: process.env.RP_LAUNCH_ID || config.launchId, + // TODO: consider the "grep" and "grep-invert" options + }; this.suites = new Map(); this.suitesInfo = new Map(); @@ -375,8 +377,9 @@ export class RPReporter implements Reporter { !includePlaywrightProjectNameToCodeReference && playwrightProjectName, ); const { id: parentId } = parentSuiteObj; + const name = test.title.replace(/^@\w+\s/, ''); const startTestItem: StartTestObjType = { - name: test.title, + name, startTime: this.client.helpers.now(), type: TEST_ITEM_TYPES.STEP, codeRef, From c85fe55f2bfa72e069a793890c116bf97d375344 Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Fri, 2 Feb 2024 15:06:05 +0100 Subject: [PATCH 02/21] feat: unit-testing for tags --- CHANGELOG.md | 3 +- README.md | 219 ++++++++++-------- .../reporter/attributesReporting.spec.ts | 4 +- .../reporter/launchesReporting.spec.ts | 22 +- .../reporter/retriesReporting.spec.ts | 2 +- .../reporter/startSuiteTestReporting.spec.ts | 4 +- src/models/configs.ts | 2 + src/reporter.ts | 15 +- tests/example.spec.js | 49 ++++ 9 files changed, 206 insertions(+), 114 deletions(-) create mode 100644 tests/example.spec.js diff --git a/CHANGELOG.md b/CHANGELOG.md index fa72b48..7a47e68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,4 @@ -## [5.1.7] - 2024-01-22 -### Changed +### Added - The Playwright's tags are parsed to the RP's attributes and attached to the tests. ## [5.1.6] - 2023-12-19 diff --git a/README.md b/README.md index dc9f1ac..dd9ee21 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,14 @@ # @reportportal/agent-js-playwright Agent to integrate Playwright with ReportPortal. -* More about [Playwright](https://playwright.dev/) -* More about [ReportPortal](http://reportportal.io/) + +- More about [Playwright](https://playwright.dev/) +- More about [ReportPortal](http://reportportal.io/) ## Installation + Install the agent in your project: + ```cmd npm install --save-dev @reportportal/agent-js-playwright ``` @@ -13,37 +16,40 @@ npm install --save-dev @reportportal/agent-js-playwright ## Configuration **1.** Create `playwright.config.ts` or `*.config.js` file with reportportal configuration: -```typescript - import { PlaywrightTestConfig } from '@playwright/test'; - - const RPconfig = { - apiKey: '00000000-0000-0000-0000-000000000000', - endpoint: 'https://your.reportportal.server/api/v1', - project: 'Your reportportal project name', - launch: 'Your launch name', - attributes: [ - { - key: 'key', - value: 'value', - }, - { - value: 'value', - }, - ], - description: 'Your launch description', - }; - const config: PlaywrightTestConfig = { - reporter: [['@reportportal/agent-js-playwright', RPconfig]], - testDir: './tests', - }; - export default config; +```typescript +import { PlaywrightTestConfig } from '@playwright/test'; + +const RPconfig = { + apiKey: '00000000-0000-0000-0000-000000000000', + endpoint: 'https://your.reportportal.server/api/v1', + project: 'Your reportportal project name', + launch: 'Your launch name', + attributes: [ + { + key: 'key', + value: 'value', + }, + { + value: 'value', + }, + ], + description: 'Your launch description', + grep: 'fast', // to run only tests with 'fast' tag in the title + grepInvert: 'fast', // to exclude tests with 'fast' tag in the title +}; + +const config: PlaywrightTestConfig = { + reporter: [['@reportportal/agent-js-playwright', RPconfig]], + testDir: './tests', +}; +export default config; ``` The full list of available options presented below. | Option | Necessity | Default | Description | -|---------------------------------------------|------------|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ------------------------------------------- | ---------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | apiKey | Required | | User's reportportal token from which you want to send requests. It can be found on the profile page of this user. | | endpoint | Required | | URL of your server. For example 'https://server:8080/api/v1'. | | launch | Required | | Name of launch at creation. | @@ -52,27 +58,30 @@ The full list of available options presented below. | description | Optional | '' | Launch description. | | rerun | Optional | false | Enable [rerun](https://reportportal.io/docs/dev-guides/RerunDevelopersGuide) | | rerunOf | Optional | Not set | UUID of launch you want to rerun. If not specified, reportportal will update the latest launch with the same name | -| mode | Optional | 'DEFAULT' | Results will be submitted to Launches page
*'DEBUG'* - Results will be submitted to Debug page. | -| skippedIssue | Optional | true | reportportal provides feature to mark skipped tests as not 'To Investigate'.
Option could be equal boolean values:
*true* - skipped tests considered as issues and will be marked as 'To Investigate' on reportportal.
*false* - skipped tests will not be marked as 'To Investigate' on application. | +| mode | Optional | 'DEFAULT' | Results will be submitted to Launches page
_'DEBUG'_ - Results will be submitted to Debug page. | +| skippedIssue | Optional | true | reportportal provides feature to mark skipped tests as not 'To Investigate'.
Option could be equal boolean values:
_true_ - skipped tests considered as issues and will be marked as 'To Investigate' on reportportal.
_false_ - skipped tests will not be marked as 'To Investigate' on application. | | debug | Optional | false | This flag allows seeing the logs of the client-javascript. Useful for debugging. | -| launchId | Optional | Not set | The _ID_ of an already existing launch. The launch must be in 'IN_PROGRESS' status while the tests are running. Please note that if this _ID_ is provided, the launch will not be finished at the end of the run and must be finished separately. | +| launchId | Optional | Not set | The _ID_ of an already existing launch. The launch must be in 'IN*PROGRESS' status while the tests are running. Please note that if this \_ID* is provided, the launch will not be finished at the end of the run and must be finished separately. | | restClientConfig | Optional | Not set | The object with `agent` property for configure [http(s)](https://nodejs.org/api/https.html#https_https_request_url_options_callback) client, may contain other client options eg. [`timeout`](https://github.com/reportportal/client-javascript#timeout-30000ms-on-axios-requests).
Visit [client-javascript](https://github.com/reportportal/client-javascript) for more details. | | launchUuidPrint | Optional | false | Whether to print the current launch UUID. | | launchUuidPrintOutput | Optional | 'STDOUT' | Launch UUID printing output. Possible values: 'STDOUT', 'STDERR'. Works only if `launchUuidPrint` set to `true`. | | includeTestSteps | Optional | false | Allows you to see the test steps at the log level. | | includePlaywrightProjectNameToCodeReference | Optional | false | Includes Playwright project name to code reference. See [`testCaseId and codeRef calculation`](#setTestCaseId). It may be useful when you want to see the different history for the same test cases within different playwright projects. | -| extendTestDescriptionWithLastError | Optional | true | If set to `true` the latest error log will be attached to the test case description. | +| extendTestDescriptionWithLastError | Optional | true | If set to `true` the latest error log will be attached to the test case description. | +| grep | Optional | Not set | To run all the test cases contains a specific tag i.e. "@fast" | +| grepInvert | Optional | Not set | To run all the test cases not contains a specific tag i.e. "@fast" | | uploadVideo | Optional | true | Whether to attach the Playwright's [video](https://playwright.dev/docs/api/class-testoptions#test-options-video) to the test case. | | uploadTrace | Optional | true | Whether to attach the Playwright's [trace](https://playwright.dev/docs/api/class-testoptions#test-options-trace) to the test case. | | token | Deprecated | Not set | Use `apiKey` instead. | The following options can be overridden using ENVIRONMENT variables: -| Option | ENV variable | -|-------------|-----------------| -| launchId | RP_LAUNCH_ID | +| Option | ENV variable | +| -------- | ------------ | +| launchId | RP_LAUNCH_ID | **2.** Add script to `package.json` file: + ```json { "scripts": { @@ -103,11 +112,11 @@ test('basic test', async ({ page }, testInfo) => { }); ``` -*Note:* attachment path can be provided instead of body. +_Note:_ attachment path can be provided instead of body. As an alternative to this approach the [`ReportingAPI`](#log) methods can be used. -*Note:* [`ReportingAPI`](#log) methods will send attachments to ReportPortal right after their call, unlike attachments provided via `testInfo.attach` that will be reported only on the test item finish. +_Note:_ [`ReportingAPI`](#log) methods will send attachments to ReportPortal right after their call, unlike attachments provided via `testInfo.attach` that will be reported only on the test item finish. ### Logging @@ -132,6 +141,7 @@ As an alternative to this approach the [`ReportingAPI`](#log) methods can be use This reporter provides Reporting API to use it directly in tests to send some additional data to the report. To start using the `ReportingApi` in tests, just import it from `'@reportportal/agent-js-playwright'`: + ```javascript import { ReportingApi } from '@reportportal/agent-js-playwright'; ``` @@ -143,11 +153,13 @@ All ReportingApi methods have an optional _suite_ parameter.
If you want to add a data to the suite, you must pass the suite name as the last parameter. ##### addAttributes + Add attributes (tags) to the current test. Should be called inside of corresponding test.
`ReportingApi.addAttributes(attributes: Array, suite?: string);`
**required**: `attributes`
**optional**: `suite`
Example: + ```javascript test('should have the correct attributes', () => { ReportingApi.addAttributes([ @@ -164,12 +176,14 @@ test('should have the correct attributes', () => { ``` ##### setTestCaseId + Set test case id to the current test ([About test case id](https://reportportal.io/docs/Test-case-ID%3Ewhat-is-it-test-case-id)). Should be called inside of corresponding test.
`ReportingApi.setTestCaseId(id: string, suite?: string);`
**required**: `id`
**optional**: `suite`
If `testCaseId` not specified, it will be generated automatically based on [codeRef](https://reportportal.io/docs/Test-case-ID%3Ewhat-does-happen-if-you-do-not-report-items-with-test-case-id-).
Example: + ```javascript test('should have the correct testCaseId', () => { ReportingApi.setTestCaseId('itemTestCaseId'); @@ -178,14 +192,16 @@ test('should have the correct testCaseId', () => { ``` ##### log + Send logs to report portal for the current test. Should be called inside of corresponding test.
`ReportingApi.log(level: LOG_LEVELS, message: string, file?: Attachment, suite?: string);`
**required**: `level`, `message`
**optional**: `file`, `suite`
-where `level` can be one of the following: *TRACE*, *DEBUG*, *WARN*, *INFO*, *ERROR*, *FATAL*
+where `level` can be one of the following: _TRACE_, _DEBUG_, _WARN_, _INFO_, _ERROR_, _FATAL_
Example: + ```javascript -test('should contain logs with attachments',() => { +test('should contain logs with attachments', () => { const fileName = 'test.jpg'; const fileContent = fs.readFileSync(path.resolve(__dirname, './attachments', fileName)); const attachment = { @@ -200,6 +216,7 @@ test('should contain logs with attachments',() => { ``` ##### info, debug, warn, error, trace, fatal + Send logs with corresponding level to report portal for the current test. Should be called inside of corresponding test.
`ReportingApi.info(message: string, file?: Attachment, suite?: string);`
`ReportingApi.debug(message: string, file?: Attachment, suite?: string);`
@@ -210,26 +227,29 @@ Send logs with corresponding level to report portal for the current test. Should **required**: `message`
**optional**: `file`, `suite`
Example: + ```javascript test('should contain logs with attachments', () => { - ReportingApi.info('Log message'); - ReportingApi.debug('Log message'); - ReportingApi.warn('Log message'); - ReportingApi.error('Log message'); - ReportingApi.trace('Log message'); - ReportingApi.fatal('Log message'); - - expect(true).toBe(true); + ReportingApi.info('Log message'); + ReportingApi.debug('Log message'); + ReportingApi.warn('Log message'); + ReportingApi.error('Log message'); + ReportingApi.trace('Log message'); + ReportingApi.fatal('Log message'); + + expect(true).toBe(true); }); ``` ##### launchLog + Send logs to report portal for the current launch. Should be called inside of the any test or suite.
`ReportingApi.launchLog(level: LOG_LEVELS, message: string, file?: Attachment);`
**required**: `level`, `message`
**optional**: `file`
-where `level` can be one of the following: *TRACE*, *DEBUG*, *WARN*, *INFO*, *ERROR*, *FATAL*
+where `level` can be one of the following: _TRACE_, _DEBUG_, _WARN_, _INFO_, _ERROR_, _FATAL_
Example: + ```javascript test('should contain logs with attachments', async () => { const fileName = 'test.jpg'; @@ -246,6 +266,7 @@ test('should contain logs with attachments', async () => { ``` ##### launchInfo, launchDebug, launchWarn, launchError, launchTrace, launchFatal + Send logs with corresponding level to report portal for the current launch. Should be called inside of the any test or suite.
`ReportingApi.launchInfo(message: string, file?: Attachment);`
`ReportingApi.launchDebug(message: string, file?: Attachment);`
@@ -256,35 +277,39 @@ Send logs with corresponding level to report portal for the current launch. Shou **required**: `message`
**optional**: `file`
Example: + ```javascript test('should contain logs with attachments', () => { - ReportingApi.launchInfo('Log message'); - ReportingApi.launchDebug('Log message'); - ReportingApi.launchWarn('Log message'); - ReportingApi.launchError('Log message'); - ReportingApi.launchTrace('Log message'); - ReportingApi.launchFatal('Log message'); - - expect(true).toBe(true); + ReportingApi.launchInfo('Log message'); + ReportingApi.launchDebug('Log message'); + ReportingApi.launchWarn('Log message'); + ReportingApi.launchError('Log message'); + ReportingApi.launchTrace('Log message'); + ReportingApi.launchFatal('Log message'); + + expect(true).toBe(true); }); ``` ##### setStatus + Assign corresponding status to the current test item. Should be called inside of corresponding test.
`ReportingApi.setStatus(status: string, suite?: string);`
**required**: `status`
**optional**: `suite`
-where `status` must be one of the following: *passed*, *failed*, *stopped*, *skipped*, *interrupted*, *cancelled*
+where `status` must be one of the following: _passed_, _failed_, _stopped_, _skipped_, _interrupted_, _cancelled_
Example: + ```javascript test('should have status FAILED', () => { - ReportingApi.setStatus('failed'); - - expect(true).toBe(true); + ReportingApi.setStatus('failed'); + + expect(true).toBe(true); }); ``` ##### setStatusFailed, setStatusPassed, setStatusSkipped, setStatusStopped, setStatusInterrupted, setStatusCancelled + Assign corresponding status to the current test item. Should be called inside of corresponding test.
`ReportingApi.setStatusFailed(suite?: string);`
`ReportingApi.setStatusPassed(suite?: string);`
@@ -294,31 +319,35 @@ Assign corresponding status to the current test item. Should be called inside of `ReportingApi.setStatusCancelled(suite?: string);`
**optional**: `suite`
Example: + ```javascript test('should call ReportingApi to set statuses', () => { - ReportingAPI.setStatusFailed(); - ReportingAPI.setStatusPassed(); - ReportingAPI.setStatusSkipped(); - ReportingAPI.setStatusStopped(); - ReportingAPI.setStatusInterrupted(); - ReportingAPI.setStatusCancelled(); + ReportingAPI.setStatusFailed(); + ReportingAPI.setStatusPassed(); + ReportingAPI.setStatusSkipped(); + ReportingAPI.setStatusStopped(); + ReportingAPI.setStatusInterrupted(); + ReportingAPI.setStatusCancelled(); }); ``` ##### setLaunchStatus + Assign corresponding status to the current launch. Should be called inside of the any test or suite.
`ReportingApi.setLaunchStatus(status: string);`
**required**: `status`
-where `status` must be one of the following: *passed*, *failed*, *stopped*, *skipped*, *interrupted*, *cancelled*
+where `status` must be one of the following: _passed_, _failed_, _stopped_, _skipped_, _interrupted_, _cancelled_
Example: + ```javascript -test('launch should have status FAILED', () => { - ReportingApi.setLaunchStatus('failed'); - expect(true).toBe(true); +test('launch should have status FAILED', () => { + ReportingApi.setLaunchStatus('failed'); + expect(true).toBe(true); }); ``` ##### setLaunchStatusFailed, setLaunchStatusPassed, setLaunchStatusSkipped, setLaunchStatusStopped, setLaunchStatusInterrupted, setLaunchStatusCancelled + Assign corresponding status to the current test item. Should be called inside of the any test or suite.
`ReportingApi.setLaunchStatusFailed();`
`ReportingApi.setLaunchStatusPassed();`
@@ -327,29 +356,33 @@ Assign corresponding status to the current test item. Should be called inside of `ReportingApi.setLaunchStatusInterrupted();`
`ReportingApi.setLaunchStatusCancelled();`
Example: + ```javascript test('should call ReportingApi to set launch statuses', () => { - ReportingAPI.setLaunchStatusFailed(); - ReportingAPI.setLaunchStatusPassed(); - ReportingAPI.setLaunchStatusSkipped(); - ReportingAPI.setLaunchStatusStopped(); - ReportingAPI.setLaunchStatusInterrupted(); - ReportingAPI.setLaunchStatusCancelled(); + ReportingAPI.setLaunchStatusFailed(); + ReportingAPI.setLaunchStatusPassed(); + ReportingAPI.setLaunchStatusSkipped(); + ReportingAPI.setLaunchStatusStopped(); + ReportingAPI.setLaunchStatusInterrupted(); + ReportingAPI.setLaunchStatusCancelled(); }); ``` ### Integration with Sauce Labs -To integrate with Sauce Labs just add attributes for the test case: +To integrate with Sauce Labs just add attributes for the test case: ```javascript -[{ - "key": "SLID", - "value": "# of the job in Sauce Labs" -}, { - "key": "SLDC", - "value": "EU (your job region in Sauce Labs)" -}] +[ + { + key: 'SLID', + value: '# of the job in Sauce Labs', + }, + { + key: 'SLDC', + value: 'EU (your job region in Sauce Labs)', + }, +]; ``` ## Issues troubleshooting @@ -360,17 +393,19 @@ There is known issue that in some cases launches not finished as expected in Rep This may happen in case of error thrown from `before`/`beforeAll` hooks, retries enabled and `fullyParallel: false`. Associated with [#85](https://github.com/reportportal/agent-js-playwright/issues/85).
In this case as a workaround we suggest to use `.skip()` and `.fixme()` annotations inside the test body: -use +use + ```javascript - test('example fail', async ({}) => { - test.fixme(); - expect(1).toBeGreaterThan(2); - }); +test('example fail', async ({}) => { + test.fixme(); + expect(1).toBeGreaterThan(2); +}); ``` -instead of +instead of + ```javascript - test.fixme('example fail', async ({}) => { - expect(1).toBeGreaterThan(2); - }); +test.fixme('example fail', async ({}) => { + expect(1).toBeGreaterThan(2); +}); ``` diff --git a/src/__tests__/reporter/attributesReporting.spec.ts b/src/__tests__/reporter/attributesReporting.spec.ts index 712db7c..f316f6e 100644 --- a/src/__tests__/reporter/attributesReporting.spec.ts +++ b/src/__tests__/reporter/attributesReporting.spec.ts @@ -37,7 +37,7 @@ describe('attributes reporting', () => { titlePath: () => ['', suiteName, 'testTitle'], }; - test('@smoke reporter.testItems should be updated with attributes', () => { + test('reporter.testItems should be updated with attributes', () => { reporter.testItems = new Map([['testItemId', { id: 'tempTestItemId', name: 'testTitle' }]]); // @ts-ignore reporter.addAttributes(attributes, testCase); @@ -47,7 +47,7 @@ describe('attributes reporting', () => { expect(reporter.testItems).toEqual(expectedTestItems); }); - test('@smoke reporter.suitesInfo should be with attributes', () => { + test('reporter.suitesInfo should be with attributes', () => { // @ts-ignore reporter.addAttributes(attributes, testCase, suiteName); const expectedSuitesInfo = new Map([ diff --git a/src/__tests__/reporter/launchesReporting.spec.ts b/src/__tests__/reporter/launchesReporting.spec.ts index 5b0e1a3..5ba06e7 100644 --- a/src/__tests__/reporter/launchesReporting.spec.ts +++ b/src/__tests__/reporter/launchesReporting.spec.ts @@ -37,12 +37,12 @@ describe('start launch', () => { beforeAll(() => reporter.onBegin()); - test('@smoke client.startLaunch should be called with corresponding params', () => { + test('client.startLaunch should be called with corresponding params', () => { expect(reporter.client.startLaunch).toHaveBeenCalledTimes(1); expect(reporter.client.startLaunch).toHaveBeenCalledWith(startLaunchObj); }); - test('@smoke reporter.launchId should be set', () => { + test('reporter.launchId should be set', () => { expect(reporter.launchId).toEqual('tempLaunchId'); }); }); @@ -64,12 +64,12 @@ describe('start launch', () => { beforeAll(() => reporter.onBegin()); - test('@smoke client.startLaunch should be called with corresponding params', () => { + test('client.startLaunch should be called with corresponding params', () => { expect(reporter.client.startLaunch).toHaveBeenCalledTimes(1); expect(reporter.client.startLaunch).toHaveBeenCalledWith(startLaunchObj); }); - test('@smoke reporter.launchId should be set', () => { + test('reporter.launchId should be set', () => { expect(reporter.launchId).toEqual('tempLaunchId'); }); }); @@ -92,12 +92,12 @@ describe('start launch', () => { beforeAll(() => reporter.onBegin()); - test('@smoke client.startLaunch should be called with corresponding params', () => { + test('client.startLaunch should be called with corresponding params', () => { expect(reporter.client.startLaunch).toHaveBeenCalledTimes(1); expect(reporter.client.startLaunch).toHaveBeenCalledWith(startLaunchObj); }); - test('@smoke reporter.launchId should be set', () => { + test('reporter.launchId should be set', () => { expect(reporter.launchId).toEqual('tempLaunchId'); }); }); @@ -125,12 +125,12 @@ describe('start launch', () => { delete process.env.RP_LAUNCH_ID; }); - test('@smoke client.startLaunch should be called with corresponding params', () => { + test('client.startLaunch should be called with corresponding params', () => { expect(reporter.client.startLaunch).toHaveBeenCalledTimes(1); expect(reporter.client.startLaunch).toHaveBeenCalledWith(startLaunchObj); }); - test('@smoke reporter.launchId should be set', () => { + test('reporter.launchId should be set', () => { expect(reporter.launchId).toEqual('tempLaunchId'); }); }); @@ -144,7 +144,7 @@ describe('finish launch', () => { beforeAll(() => reporter.onEnd()); - test('@smoke launch should be finished', () => { + test('launch should be finished', () => { expect(reporter.client.finishLaunch).toHaveBeenCalledTimes(1); expect(reporter.client.finishLaunch).toHaveBeenCalledWith('tempLaunchId', { endTime: mockedDate, @@ -164,7 +164,7 @@ describe('finish launch', () => { beforeAll(() => reporter.onEnd()); - test('@smoke launch finish request should not be sent', () => { + test('launch finish request should not be sent', () => { expect(reporter.client.finishLaunch).toHaveBeenCalledTimes(0); expect(reporter.isLaunchFinishSend).toBe(true); }); @@ -186,7 +186,7 @@ describe('finish launch', () => { delete process.env.RP_LAUNCH_ID; }); - test('@smoke launch finish request should not be sent', () => { + test('launch finish request should not be sent', () => { expect(reporter.client.finishLaunch).toHaveBeenCalledTimes(0); expect(reporter.isLaunchFinishSend).toBe(true); }); diff --git a/src/__tests__/reporter/retriesReporting.spec.ts b/src/__tests__/reporter/retriesReporting.spec.ts index 020d396..59a82ae 100644 --- a/src/__tests__/reporter/retriesReporting.spec.ts +++ b/src/__tests__/reporter/retriesReporting.spec.ts @@ -38,7 +38,7 @@ describe('retries reporting', () => { const spyStartTestItem = jest.spyOn(reporter.client, 'startTestItem'); - test('@smoke client.startTestItem should be called with retry=true params', () => { + test('client.startTestItem should be called with retry=true params', () => { const parentId = 'tempTestItemId'; const expectedTestObj: StartTestObjType = { startTime: reporter.client.helpers.now(), diff --git a/src/__tests__/reporter/startSuiteTestReporting.spec.ts b/src/__tests__/reporter/startSuiteTestReporting.spec.ts index 3dd8167..76d02bb 100644 --- a/src/__tests__/reporter/startSuiteTestReporting.spec.ts +++ b/src/__tests__/reporter/startSuiteTestReporting.spec.ts @@ -75,7 +75,7 @@ describe('start reporting suite/test', () => { jest.clearAllMocks(); }); - test('@smoke client.startTestItem should be called with corresponding params to report suites and test item', () => { + test('client.startTestItem should be called with corresponding params to report suites and test item', () => { const expectedSuites = new Map([ [ rootSuite, @@ -149,7 +149,7 @@ describe('start reporting suite/test', () => { expect(reporter.testItems).toEqual(expectedTestItems); }); - test('@smoke client.startTestItem should not be called in case of launch finish request have been send', () => { + test('client.startTestItem should not be called in case of launch finish request have been send', () => { reporter.isLaunchFinishSend = true; // @ts-ignore reporter.onTestBegin(testCase); diff --git a/src/models/configs.ts b/src/models/configs.ts index 7c7066a..379cec5 100644 --- a/src/models/configs.ts +++ b/src/models/configs.ts @@ -56,4 +56,6 @@ export interface ReportPortalConfig extends ClientConfig, AttachmentsConfig { includeTestSteps?: boolean; includePlaywrightProjectNameToCodeReference?: boolean; extendTestDescriptionWithLastError?: boolean; + grep?: string; + grepInvert?: string; } diff --git a/src/reporter.ts b/src/reporter.ts index a756ef3..4e646b9 100644 --- a/src/reporter.ts +++ b/src/reporter.ts @@ -94,8 +94,6 @@ export class RPReporter implements Reporter { extendTestDescriptionWithLastError: true, ...config, launchId: process.env.RP_LAUNCH_ID || config.launchId, - // TODO: consider the "grep" and "grep-invert" options - }; this.suites = new Map(); this.suitesInfo = new Map(); @@ -284,6 +282,15 @@ export class RPReporter implements Reporter { mode: mode || LAUNCH_MODES.DEFAULT, id: launchId, }; + + // Extract grep and grepInvert from config and add as launch attributes + if (this.config.grep) { + startLaunchObj.attributes.push({ key: 'grep', value: this.config.grep }); + } + if (this.config.grepInvert) { + startLaunchObj.attributes.push({ key: 'grepInvert', value: this.config.grepInvert }); + } + const { tempId, promise } = this.client.startLaunch(startLaunchObj); this.addRequestToPromisesQueue(promise, 'Failed to start launch.'); this.launchId = tempId; @@ -377,9 +384,9 @@ export class RPReporter implements Reporter { !includePlaywrightProjectNameToCodeReference && playwrightProjectName, ); const { id: parentId } = parentSuiteObj; - const name = test.title.replace(/^@\w+\s/, ''); + const name = test.title.replace(/@\w+\s*/g, ''); const startTestItem: StartTestObjType = { - name, + name, startTime: this.client.helpers.now(), type: TEST_ITEM_TYPES.STEP, codeRef, diff --git a/tests/example.spec.js b/tests/example.spec.js new file mode 100644 index 0000000..99853e1 --- /dev/null +++ b/tests/example.spec.js @@ -0,0 +1,49 @@ +// @ts-check +const { test, expect } = require('@playwright/test'); +const { ReportingApi } = require('../src/reportingApi'); + +test('has title', async ({ page }) => { + await page.goto('https://playwright.dev/'); + + // Expect a title "to contain" a substring. + await expect(page).toHaveTitle(/Playwright/); +}); + +test('get started link', async ({ page }) => { + await page.goto('https://playwright.dev/'); + + // Click the get started link. + await page.getByRole('link', { name: 'Get started' }).click(); + + // Expects page to have a heading with the name of Installation. + await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible(); +}); + +test('has one tag at the end @single', async ({ page }) => { + ReportingApi.addAttributes([ + { + value: 'single', + }, + ]); + + await page.goto('https://playwright.dev/'); + await expect(page).toHaveTitle(/Playwright/); +}); + +test('@single has one tag at the beginning', async ({ page }) => { + ReportingApi.addAttributes([ + { + value: 'single', + }, + ]); + + await page.goto('https://playwright.dev/'); + await expect(page).toHaveTitle(/Playwright/); +}); + +test('has no tag', async ({ page }) => { + ReportingApi.addAttributes([]); + + await page.goto('https://playwright.dev/'); + await expect(page).toHaveTitle(/Playwright/); +}); \ No newline at end of file From 54a75a816a6b639f6dcd2a9a809496d11abd60fd Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Fri, 2 Feb 2024 15:14:29 +0100 Subject: [PATCH 03/21] feat: extract tags from title --- src/reporter.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/reporter.ts b/src/reporter.ts index 4e646b9..749e6b2 100644 --- a/src/reporter.ts +++ b/src/reporter.ts @@ -384,13 +384,14 @@ export class RPReporter implements Reporter { !includePlaywrightProjectNameToCodeReference && playwrightProjectName, ); const { id: parentId } = parentSuiteObj; - const name = test.title.replace(/@\w+\s*/g, ''); + const startTestItem: StartTestObjType = { - name, + name: this.#extractTagsFromTitle(test.title), startTime: this.client.helpers.now(), type: TEST_ITEM_TYPES.STEP, codeRef, retry: test.results?.length > 1, + attributes: this.#getAttributesFromTitle(test.title), }; const stepObj = this.client.startTestItem(startTestItem, this.launchId, parentId); this.addRequestToPromisesQueue(stepObj.promise, 'Failed to start test.'); @@ -645,4 +646,12 @@ export class RPReporter implements Reporter { printsToStdio(): boolean { return false; } + + #extractTagsFromTitle(title: string): string { + return title.match(/@\w+\s*/g)?.join('') || ''; + } + + #getAttributesFromTitle(title: string): Attribute[] { + return title.match(/@\w+\s*/g)?.map((tag) => ({ key: 'tag', value: tag.trim() })) || []; + } } From dc6206154d001d7b712ba598a840418036e4c829 Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Tue, 6 Feb 2024 20:48:31 +0100 Subject: [PATCH 04/21] refactor: use grep & grepInvert from pw config --- README.md | 2 -- .../reporter/startSuiteTestReporting.spec.ts | 33 +++++++++++++++++++ src/models/configs.ts | 2 -- src/reporter.ts | 19 +++++++---- tests/example.spec.js | 29 ---------------- 5 files changed, 46 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index dd9ee21..b568488 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,6 @@ const RPconfig = { }, ], description: 'Your launch description', - grep: 'fast', // to run only tests with 'fast' tag in the title - grepInvert: 'fast', // to exclude tests with 'fast' tag in the title }; const config: PlaywrightTestConfig = { diff --git a/src/__tests__/reporter/startSuiteTestReporting.spec.ts b/src/__tests__/reporter/startSuiteTestReporting.spec.ts index 76d02bb..76e99d5 100644 --- a/src/__tests__/reporter/startSuiteTestReporting.spec.ts +++ b/src/__tests__/reporter/startSuiteTestReporting.spec.ts @@ -20,6 +20,7 @@ import { RPClientMock } from '../mocks/RPClientMock'; import { StartTestObjType } from '../../models'; import { TEST_ITEM_TYPES } from '../../constants'; import path from 'path'; +import { TestCase } from '@playwright/test/reporter'; const rootSuite = 'tests/example.js'; const suiteName = 'suiteName'; @@ -158,4 +159,36 @@ describe('start reporting suite/test', () => { expect(reporter.suites).toEqual(new Map()); expect(reporter.testItems).toEqual(new Map()); }); + + test('@smoke has one tag at the beginning', () => { + const testId = 'testItemId'; + const testTitle = 'testTitle'; + const attributes = [{ value: 'smoke' }]; + const simpleTestCase = { + id: testId, + title: testTitle, + titlePath: () => [rootSuite, suiteName, testTitle], + }; + + reporter.onTestBegin(simpleTestCase); + expect(reporter.testItems).toEqual( + new Map([[testId, { id: 'tempTestItemId', name: testTitle, attributes }]]), + ); + }); + + test('has one tag at the end @manual', () => { + const testId = 'testItemId'; + const testTitle = 'testTitle'; + const attributes = [{ value: 'manual' }]; + const simpleTestCase = { + id: testId, + title: testTitle, + titlePath: () => [rootSuite, suiteName, testTitle], + }; + + reporter.onTestBegin(simpleTestCase); + expect(reporter.testItems).toEqual( + new Map([[testId, { id: 'tempTestItemId', name: testTitle, attributes }]]), + ); + }); }); diff --git a/src/models/configs.ts b/src/models/configs.ts index 379cec5..7c7066a 100644 --- a/src/models/configs.ts +++ b/src/models/configs.ts @@ -56,6 +56,4 @@ export interface ReportPortalConfig extends ClientConfig, AttachmentsConfig { includeTestSteps?: boolean; includePlaywrightProjectNameToCodeReference?: boolean; extendTestDescriptionWithLastError?: boolean; - grep?: string; - grepInvert?: string; } diff --git a/src/reporter.ts b/src/reporter.ts index 749e6b2..0d8052a 100644 --- a/src/reporter.ts +++ b/src/reporter.ts @@ -17,7 +17,13 @@ import RPClient from '@reportportal/client-javascript'; import stripAnsi from 'strip-ansi'; -import { Reporter, Suite as PWSuite, TestCase, TestResult } from '@playwright/test/reporter'; +import { + Reporter, + Suite as PWSuite, + TestCase, + TestResult, + FullConfig, +} from '@playwright/test/reporter'; import { Attribute, FinishTestItemObjType, @@ -266,7 +272,7 @@ export class RPReporter implements Reporter { }); } - onBegin(): void { + onBegin(config?: FullConfig): void { const { launch, description, attributes, skippedIssue, rerun, rerunOf, mode, launchId } = this.config; const systemAttributes: Attribute[] = getSystemAttributes(skippedIssue); @@ -284,11 +290,12 @@ export class RPReporter implements Reporter { }; // Extract grep and grepInvert from config and add as launch attributes - if (this.config.grep) { - startLaunchObj.attributes.push({ key: 'grep', value: this.config.grep }); + if (config?.grep) { + startLaunchObj.attributes.push({ key: 'grep', value: config.grep.toString() }); } - if (this.config.grepInvert) { - startLaunchObj.attributes.push({ key: 'grepInvert', value: this.config.grepInvert }); + + if (config?.grepInvert) { + startLaunchObj.attributes.push({ key: 'grepInvert', value: config.grepInvert.toString() }); } const { tempId, promise } = this.client.startLaunch(startLaunchObj); diff --git a/tests/example.spec.js b/tests/example.spec.js index 99853e1..8da530e 100644 --- a/tests/example.spec.js +++ b/tests/example.spec.js @@ -18,32 +18,3 @@ test('get started link', async ({ page }) => { // Expects page to have a heading with the name of Installation. await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible(); }); - -test('has one tag at the end @single', async ({ page }) => { - ReportingApi.addAttributes([ - { - value: 'single', - }, - ]); - - await page.goto('https://playwright.dev/'); - await expect(page).toHaveTitle(/Playwright/); -}); - -test('@single has one tag at the beginning', async ({ page }) => { - ReportingApi.addAttributes([ - { - value: 'single', - }, - ]); - - await page.goto('https://playwright.dev/'); - await expect(page).toHaveTitle(/Playwright/); -}); - -test('has no tag', async ({ page }) => { - ReportingApi.addAttributes([]); - - await page.goto('https://playwright.dev/'); - await expect(page).toHaveTitle(/Playwright/); -}); \ No newline at end of file From 5cb66dece44de350f388fea1214b232bcd325c38 Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Wed, 7 Feb 2024 14:50:28 +0100 Subject: [PATCH 05/21] refactor: update the tag tests --- .../reporter/startSuiteTestReporting.spec.ts | 24 ++++++++----------- src/reporter.ts | 7 +++++- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/__tests__/reporter/startSuiteTestReporting.spec.ts b/src/__tests__/reporter/startSuiteTestReporting.spec.ts index 76e99d5..85b2d4b 100644 --- a/src/__tests__/reporter/startSuiteTestReporting.spec.ts +++ b/src/__tests__/reporter/startSuiteTestReporting.spec.ts @@ -160,35 +160,31 @@ describe('start reporting suite/test', () => { expect(reporter.testItems).toEqual(new Map()); }); - test('@smoke has one tag at the beginning', () => { - const testId = 'testItemId'; - const testTitle = 'testTitle'; + test('@smoke client.startTestItem should be called with one tag at the beginning', () => { const attributes = [{ value: 'smoke' }]; const simpleTestCase = { - id: testId, - title: testTitle, - titlePath: () => [rootSuite, suiteName, testTitle], + id: 'testItemId', + title: 'testTitle', + titlePath: () => [rootSuite, suiteName, 'testTitle'], }; reporter.onTestBegin(simpleTestCase); expect(reporter.testItems).toEqual( - new Map([[testId, { id: 'tempTestItemId', name: testTitle, attributes }]]), + new Map([[simpleTestCase.id, { id: 'tempTestItemId', name: 'testTitle', attributes }]]), ); }); - test('has one tag at the end @manual', () => { - const testId = 'testItemId'; - const testTitle = 'testTitle'; + test('client.startTestItem should be called with one tag at the end @manual', () => { const attributes = [{ value: 'manual' }]; const simpleTestCase = { - id: testId, - title: testTitle, - titlePath: () => [rootSuite, suiteName, testTitle], + id: 'testItemId', + title: 'testTitle', + titlePath: () => [rootSuite, suiteName, 'testTitle'], }; reporter.onTestBegin(simpleTestCase); expect(reporter.testItems).toEqual( - new Map([[testId, { id: 'tempTestItemId', name: testTitle, attributes }]]), + new Map([[simpleTestCase.id, { id: 'tempTestItemId', name: 'testTitle', attributes }]]), ); }); }); diff --git a/src/reporter.ts b/src/reporter.ts index 0d8052a..458a070 100644 --- a/src/reporter.ts +++ b/src/reporter.ts @@ -655,7 +655,12 @@ export class RPReporter implements Reporter { } #extractTagsFromTitle(title: string): string { - return title.match(/@\w+\s*/g)?.join('') || ''; + return ( + title + .match(/@\w+\s*/g) + ?.join('') + .trim() || '' + ); } #getAttributesFromTitle(title: string): Attribute[] { From b5955074b8c000874724c6830c157b6f3b8fe69c Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Mon, 4 Mar 2024 06:47:56 +0100 Subject: [PATCH 06/21] feat: consider @key:value title attributes --- src/reporter.ts | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/reporter.ts b/src/reporter.ts index 458a070..18b4ec2 100644 --- a/src/reporter.ts +++ b/src/reporter.ts @@ -15,15 +15,6 @@ * */ -import RPClient from '@reportportal/client-javascript'; -import stripAnsi from 'strip-ansi'; -import { - Reporter, - Suite as PWSuite, - TestCase, - TestResult, - FullConfig, -} from '@playwright/test/reporter'; import { Attribute, FinishTestItemObjType, @@ -33,12 +24,19 @@ import { StartTestObjType, TestStepWithId, } from './models'; +import { + FullConfig, + Suite as PWSuite, + Reporter, + TestCase, + TestResult, +} from '@playwright/test/reporter'; import { LAUNCH_MODES, LOG_LEVELS, STATUSES, - TEST_ITEM_TYPES, TEST_ANNOTATION_TYPES, + TEST_ITEM_TYPES, TEST_OUTCOME_TYPES, } from './constants'; import { @@ -51,8 +49,11 @@ import { isFalse, promiseErrorHandler, } from './utils'; + import { EVENTS } from '@reportportal/client-javascript/lib/constants/events'; +import RPClient from '@reportportal/client-javascript'; import { randomUUID } from 'crypto'; +import stripAnsi from 'strip-ansi'; export interface TestItem { id: string; @@ -664,6 +665,16 @@ export class RPReporter implements Reporter { } #getAttributesFromTitle(title: string): Attribute[] { - return title.match(/@\w+\s*/g)?.map((tag) => ({ key: 'tag', value: tag.trim() })) || []; + const attributes = title.match(/@(\w+)(?::(\w+))?/g)?.map((tag) => { + const cleanedTag = tag.slice(1); + const parts = cleanedTag.split(':'); + + return { + key: parts.length === 2 ? parts[0] : '', + value: parts.length === 2 ? parts[1] : parts[0], + }; + }); + + return attributes || []; } } From 5756fa616beae6b599cc9ba789960000c3ef9954 Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Mon, 4 Mar 2024 06:57:56 +0100 Subject: [PATCH 07/21] style: format reporter.ts --- src/reporter.ts | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/reporter.ts b/src/reporter.ts index 18b4ec2..bf8d57e 100644 --- a/src/reporter.ts +++ b/src/reporter.ts @@ -15,6 +15,15 @@ * */ +import RPClient from '@reportportal/client-javascript'; +import stripAnsi from 'strip-ansi'; +import { + Reporter, + Suite as PWSuite, + TestCase, + TestResult, + FullConfig, +} from '@playwright/test/reporter'; import { Attribute, FinishTestItemObjType, @@ -24,19 +33,12 @@ import { StartTestObjType, TestStepWithId, } from './models'; -import { - FullConfig, - Suite as PWSuite, - Reporter, - TestCase, - TestResult, -} from '@playwright/test/reporter'; import { LAUNCH_MODES, LOG_LEVELS, STATUSES, - TEST_ANNOTATION_TYPES, TEST_ITEM_TYPES, + TEST_ANNOTATION_TYPES, TEST_OUTCOME_TYPES, } from './constants'; import { @@ -49,11 +51,8 @@ import { isFalse, promiseErrorHandler, } from './utils'; - import { EVENTS } from '@reportportal/client-javascript/lib/constants/events'; -import RPClient from '@reportportal/client-javascript'; import { randomUUID } from 'crypto'; -import stripAnsi from 'strip-ansi'; export interface TestItem { id: string; From 51e7139e622f9af9a90459ec7503ae46a18b7a9d Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Mon, 4 Mar 2024 07:05:49 +0100 Subject: [PATCH 08/21] test: fix client.startTestItem should be called with retry=true params --- src/__tests__/reporter/retriesReporting.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/__tests__/reporter/retriesReporting.spec.ts b/src/__tests__/reporter/retriesReporting.spec.ts index 59a82ae..3490c2f 100644 --- a/src/__tests__/reporter/retriesReporting.spec.ts +++ b/src/__tests__/reporter/retriesReporting.spec.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { RPReporter } from '../../reporter'; -import { mockConfig } from '../mocks/configMock'; import { RPClientMock } from '../mocks/RPClientMock'; +import { RPReporter } from '../../reporter'; import { StartTestObjType } from '../../models'; import { TEST_ITEM_TYPES } from '../../constants'; +import { mockConfig } from '../mocks/configMock'; describe('retries reporting', () => { const reporter = new RPReporter(mockConfig); @@ -41,7 +41,7 @@ describe('retries reporting', () => { test('client.startTestItem should be called with retry=true params', () => { const parentId = 'tempTestItemId'; const expectedTestObj: StartTestObjType = { - startTime: reporter.client.helpers.now(), + startTime: expect.any(Number), name: 'testTitle', type: TEST_ITEM_TYPES.STEP, codeRef: 'suiteName/testTitle', From 1abdab5963d849a6fb3e35dd72c3b4d9c718d97d Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Mon, 4 Mar 2024 07:45:18 +0100 Subject: [PATCH 09/21] test: update tags tests in startSuiteTestReporting.spec.ts --- .../reporter/startSuiteTestReporting.spec.ts | 58 ++++++++++++------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/src/__tests__/reporter/startSuiteTestReporting.spec.ts b/src/__tests__/reporter/startSuiteTestReporting.spec.ts index 85b2d4b..88da592 100644 --- a/src/__tests__/reporter/startSuiteTestReporting.spec.ts +++ b/src/__tests__/reporter/startSuiteTestReporting.spec.ts @@ -14,13 +14,13 @@ * limitations under the License. */ -import { RPReporter } from '../../reporter'; -import { mockConfig } from '../mocks/configMock'; import { RPClientMock } from '../mocks/RPClientMock'; +import { RPReporter } from '../../reporter'; import { StartTestObjType } from '../../models'; import { TEST_ITEM_TYPES } from '../../constants'; -import path from 'path'; import { TestCase } from '@playwright/test/reporter'; +import { mockConfig } from '../mocks/configMock'; +import path from 'path'; const rootSuite = 'tests/example.js'; const suiteName = 'suiteName'; @@ -160,31 +160,47 @@ describe('start reporting suite/test', () => { expect(reporter.testItems).toEqual(new Map()); }); - test('@smoke client.startTestItem should be called with one tag at the beginning', () => { - const attributes = [{ value: 'smoke' }]; - const simpleTestCase = { - id: 'testItemId', - title: 'testTitle', - titlePath: () => [rootSuite, suiteName, 'testTitle'], + test('@tag client.startTestItem should be called with one tag at the beginning', () => { + const expectedTestObj = { + startTime: reporter.client.helpers.now(), + name: 'testTitle', + type: TEST_ITEM_TYPES.STEP, + codeRef: `${rootSuite}/${suiteName}/testTitle`, + retry: false, + attributes: [{ value: 'tag' }], }; + const parentId = 'tempTestItemId'; - reporter.onTestBegin(simpleTestCase); - expect(reporter.testItems).toEqual( - new Map([[simpleTestCase.id, { id: 'tempTestItemId', name: 'testTitle', attributes }]]), + // @ts-ignore + reporter.onTestBegin(expectedTestObj); + + expect(spyStartTestItem).toHaveBeenNthCalledWith( + 1, + expectedTestObj, + reporter.launchId, + parentId, ); }); - test('client.startTestItem should be called with one tag at the end @manual', () => { - const attributes = [{ value: 'manual' }]; - const simpleTestCase = { - id: 'testItemId', - title: 'testTitle', - titlePath: () => [rootSuite, suiteName, 'testTitle'], + test('client.startTestItem should be called with one tag at the end @tag', () => { + const expectedTestObj = { + startTime: reporter.client.helpers.now(), + name: 'testTitle', + type: TEST_ITEM_TYPES.STEP, + codeRef: `${rootSuite}/${suiteName}/testTitle`, + retry: false, + attributes: [{ value: 'tag' }], }; + const parentId = 'tempTestItemId'; - reporter.onTestBegin(simpleTestCase); - expect(reporter.testItems).toEqual( - new Map([[simpleTestCase.id, { id: 'tempTestItemId', name: 'testTitle', attributes }]]), + // @ts-ignore + reporter.onTestBegin(expectedTestObj); + + expect(spyStartTestItem).toHaveBeenNthCalledWith( + 1, + expectedTestObj, + reporter.launchId, + parentId, ); }); }); From b681254d1a68f5231b312319d52886e0c70f690c Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Mon, 4 Mar 2024 07:49:33 +0100 Subject: [PATCH 10/21] test: update tags tests in startSuiteTestReporting.spec.ts --- src/__tests__/reporter/startSuiteTestReporting.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/__tests__/reporter/startSuiteTestReporting.spec.ts b/src/__tests__/reporter/startSuiteTestReporting.spec.ts index 88da592..24fdff1 100644 --- a/src/__tests__/reporter/startSuiteTestReporting.spec.ts +++ b/src/__tests__/reporter/startSuiteTestReporting.spec.ts @@ -18,7 +18,6 @@ import { RPClientMock } from '../mocks/RPClientMock'; import { RPReporter } from '../../reporter'; import { StartTestObjType } from '../../models'; import { TEST_ITEM_TYPES } from '../../constants'; -import { TestCase } from '@playwright/test/reporter'; import { mockConfig } from '../mocks/configMock'; import path from 'path'; From 37771b9c9162a09452bf2317b62c003287743081 Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Tue, 5 Mar 2024 16:43:08 +0100 Subject: [PATCH 11/21] test: fix the tests of the tags --- .../reporter/startSuiteTestReporting.spec.ts | 154 ++++++++++-------- src/reporter.ts | 33 ++-- tests/example.spec.js | 20 --- 3 files changed, 105 insertions(+), 102 deletions(-) delete mode 100644 tests/example.spec.js diff --git a/src/__tests__/reporter/startSuiteTestReporting.spec.ts b/src/__tests__/reporter/startSuiteTestReporting.spec.ts index 24fdff1..95c1fd9 100644 --- a/src/__tests__/reporter/startSuiteTestReporting.spec.ts +++ b/src/__tests__/reporter/startSuiteTestReporting.spec.ts @@ -27,6 +27,48 @@ const suiteName = 'suiteName'; describe('start reporting suite/test', () => { let reporter: RPReporter; let spyStartTestItem: jest.SpyInstance; + const expectedTestObj: StartTestObjType = { + name: 'testTitle', + type: TEST_ITEM_TYPES.STEP, + codeRef: 'tests/example.js/suiteName/testTitle', + retry: false, + }; + + const expectedParentSuiteObj: StartTestObjType = { + name: suiteName, + type: TEST_ITEM_TYPES.TEST, + codeRef: 'tests/example.js/suiteName', + }; + + const expectedRootParentSuiteObj: StartTestObjType = { + name: rootSuite, + type: TEST_ITEM_TYPES.SUITE, + codeRef: 'tests/example.js', + }; + + const expectedSuites = new Map([ + [ + rootSuite, + { + id: 'tempTestItemId', + name: rootSuite, + testInvocationsLeft: 1, + descendants: ['testItemId'], + }, + ], + [ + `${rootSuite}/${suiteName}`, + { + id: 'tempTestItemId', + name: suiteName, + descendants: ['testItemId'], + testInvocationsLeft: 1, + }, + ], + ]); + + const expectedTestItems = new Map([['testItemId', { id: 'tempTestItemId', name: 'testTitle' }]]); + const parentId = 'tempTestItemId'; const testCase = { title: 'testTitle', @@ -69,6 +111,10 @@ describe('start reporting suite/test', () => { reporter.launchId = 'tempLaunchId'; spyStartTestItem = jest.spyOn(reporter.client, 'startTestItem'); + + expectedTestObj.startTime = reporter.client.helpers.now(); + expectedParentSuiteObj.startTime = reporter.client.helpers.now(); + expectedRootParentSuiteObj.startTime = reporter.client.helpers.now(); }); afterEach(() => { @@ -76,50 +122,6 @@ describe('start reporting suite/test', () => { }); test('client.startTestItem should be called with corresponding params to report suites and test item', () => { - const expectedSuites = new Map([ - [ - rootSuite, - { - id: 'tempTestItemId', - name: rootSuite, - testInvocationsLeft: 1, - descendants: ['testItemId'], - }, - ], - [ - `${rootSuite}/${suiteName}`, - { - id: 'tempTestItemId', - name: suiteName, - descendants: ['testItemId'], - testInvocationsLeft: 1, - }, - ], - ]); - const expectedTestItems = new Map([ - ['testItemId', { id: 'tempTestItemId', name: 'testTitle' }], - ]); - const expectedRootParentSuiteObj: StartTestObjType = { - startTime: reporter.client.helpers.now(), - name: rootSuite, - type: TEST_ITEM_TYPES.SUITE, - codeRef: 'tests/example.js', - }; - const expectedParentSuiteObj: StartTestObjType = { - startTime: reporter.client.helpers.now(), - name: suiteName, - type: TEST_ITEM_TYPES.TEST, - codeRef: 'tests/example.js/suiteName', - }; - const expectedTestObj: StartTestObjType = { - startTime: reporter.client.helpers.now(), - name: 'testTitle', - type: TEST_ITEM_TYPES.STEP, - codeRef: 'tests/example.js/suiteName/testTitle', - retry: false, - }; - const parentId = 'tempTestItemId'; - // @ts-ignore reporter.onTestBegin(testCase); @@ -160,46 +162,62 @@ describe('start reporting suite/test', () => { }); test('@tag client.startTestItem should be called with one tag at the beginning', () => { - const expectedTestObj = { - startTime: reporter.client.helpers.now(), - name: 'testTitle', - type: TEST_ITEM_TYPES.STEP, - codeRef: `${rootSuite}/${suiteName}/testTitle`, - retry: false, - attributes: [{ value: 'tag' }], - }; - const parentId = 'tempTestItemId'; - // @ts-ignore - reporter.onTestBegin(expectedTestObj); + reporter.onTestBegin({ ...testCase, title: `@tag ${testCase.title}` }); + // the first call for the root suite start expect(spyStartTestItem).toHaveBeenNthCalledWith( 1, - expectedTestObj, + expectedRootParentSuiteObj, + reporter.launchId, + undefined, + ); + // the first call for the item parent suite start + expect(spyStartTestItem).toHaveBeenNthCalledWith( + 2, + expectedParentSuiteObj, reporter.launchId, parentId, ); + // the third call for the test item start + expect(reporter.client.startTestItem).toHaveBeenNthCalledWith( + 3, + { ...expectedTestObj, attributes: [{ value: 'tag' }] }, + reporter.launchId, + parentId, + ); + expect(spyStartTestItem).toHaveBeenCalledTimes(3); + expect(reporter.suites).toEqual(expectedSuites); + expect(reporter.testItems).toEqual(expectedTestItems); }); test('client.startTestItem should be called with one tag at the end @tag', () => { - const expectedTestObj = { - startTime: reporter.client.helpers.now(), - name: 'testTitle', - type: TEST_ITEM_TYPES.STEP, - codeRef: `${rootSuite}/${suiteName}/testTitle`, - retry: false, - attributes: [{ value: 'tag' }], - }; - const parentId = 'tempTestItemId'; - // @ts-ignore - reporter.onTestBegin(expectedTestObj); + reporter.onTestBegin({ ...testCase, title: `${testCase.title} @tag` }); + // the first call for the root suite start expect(spyStartTestItem).toHaveBeenNthCalledWith( 1, - expectedTestObj, + expectedRootParentSuiteObj, + reporter.launchId, + undefined, + ); + // the first call for the item parent suite start + expect(spyStartTestItem).toHaveBeenNthCalledWith( + 2, + expectedParentSuiteObj, + reporter.launchId, + parentId, + ); + // the third call for the test item start + expect(reporter.client.startTestItem).toHaveBeenNthCalledWith( + 3, + { ...expectedTestObj, attributes: [{ value: 'tag' }] }, reporter.launchId, parentId, ); + expect(spyStartTestItem).toHaveBeenCalledTimes(3); + expect(reporter.suites).toEqual(expectedSuites); + expect(reporter.testItems).toEqual(expectedTestItems); }); }); diff --git a/src/reporter.ts b/src/reporter.ts index bf8d57e..f29af0b 100644 --- a/src/reporter.ts +++ b/src/reporter.ts @@ -377,6 +377,12 @@ export class RPReporter implements Reporter { if (this.isLaunchFinishSend) { return; } + + const taggedTestTitle = test.title; + const untaggedTestTitle = this.#extractTagsFromTitle(test.title); + + test.title = untaggedTestTitle; + const playwrightProjectName = this.createSuites(test); const fullSuiteName = getCodeRef(test, test.parent.title); @@ -390,16 +396,20 @@ export class RPReporter implements Reporter { test.title, !includePlaywrightProjectNameToCodeReference && playwrightProjectName, ); + const { id: parentId } = parentSuiteObj; const startTestItem: StartTestObjType = { - name: this.#extractTagsFromTitle(test.title), + name: untaggedTestTitle, startTime: this.client.helpers.now(), type: TEST_ITEM_TYPES.STEP, codeRef, retry: test.results?.length > 1, - attributes: this.#getAttributesFromTitle(test.title), }; + + const attributes = this.#getAttributesFromTitle(taggedTestTitle); + if (attributes.length) startTestItem.attributes = attributes; + const stepObj = this.client.startTestItem(startTestItem, this.launchId, parentId); this.addRequestToPromisesQueue(stepObj.promise, 'Failed to start test.'); this.testItems.set(test.id, { @@ -655,23 +665,18 @@ export class RPReporter implements Reporter { } #extractTagsFromTitle(title: string): string { - return ( - title - .match(/@\w+\s*/g) - ?.join('') - .trim() || '' - ); + const tagRegex = /^@\w+\s*|\s*@\w+$/g; + const titleWithoutTags = title.replace(tagRegex, ''); + const trimmedTitle = titleWithoutTags.trim(); + + return trimmedTitle; } #getAttributesFromTitle(title: string): Attribute[] { const attributes = title.match(/@(\w+)(?::(\w+))?/g)?.map((tag) => { - const cleanedTag = tag.slice(1); - const parts = cleanedTag.split(':'); + const [key, value] = tag.slice(1).split(':'); - return { - key: parts.length === 2 ? parts[0] : '', - value: parts.length === 2 ? parts[1] : parts[0], - }; + return value ? { key, value } : { value: key }; }); return attributes || []; diff --git a/tests/example.spec.js b/tests/example.spec.js deleted file mode 100644 index 8da530e..0000000 --- a/tests/example.spec.js +++ /dev/null @@ -1,20 +0,0 @@ -// @ts-check -const { test, expect } = require('@playwright/test'); -const { ReportingApi } = require('../src/reportingApi'); - -test('has title', async ({ page }) => { - await page.goto('https://playwright.dev/'); - - // Expect a title "to contain" a substring. - await expect(page).toHaveTitle(/Playwright/); -}); - -test('get started link', async ({ page }) => { - await page.goto('https://playwright.dev/'); - - // Click the get started link. - await page.getByRole('link', { name: 'Get started' }).click(); - - // Expects page to have a heading with the name of Installation. - await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible(); -}); From 97102499afcde7bc3407fb99368b8aa3f576612e Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Wed, 6 Mar 2024 09:34:16 +0100 Subject: [PATCH 12/21] Update retriesReporting.spec.ts --- src/__tests__/reporter/retriesReporting.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__tests__/reporter/retriesReporting.spec.ts b/src/__tests__/reporter/retriesReporting.spec.ts index 3490c2f..4c8c6be 100644 --- a/src/__tests__/reporter/retriesReporting.spec.ts +++ b/src/__tests__/reporter/retriesReporting.spec.ts @@ -41,7 +41,7 @@ describe('retries reporting', () => { test('client.startTestItem should be called with retry=true params', () => { const parentId = 'tempTestItemId'; const expectedTestObj: StartTestObjType = { - startTime: expect.any(Number), + startTime: reporter.client.helpers.now(), name: 'testTitle', type: TEST_ITEM_TYPES.STEP, codeRef: 'suiteName/testTitle', From 6dbea24ea8a771b17f8efa48c47b8e40aa6650ef Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Wed, 6 Mar 2024 09:34:56 +0100 Subject: [PATCH 13/21] Update src/__tests__/reporter/startSuiteTestReporting.spec.ts Co-authored-by: Ilya --- src/__tests__/reporter/startSuiteTestReporting.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__tests__/reporter/startSuiteTestReporting.spec.ts b/src/__tests__/reporter/startSuiteTestReporting.spec.ts index 95c1fd9..bef05c9 100644 --- a/src/__tests__/reporter/startSuiteTestReporting.spec.ts +++ b/src/__tests__/reporter/startSuiteTestReporting.spec.ts @@ -161,7 +161,7 @@ describe('start reporting suite/test', () => { expect(reporter.testItems).toEqual(new Map()); }); - test('@tag client.startTestItem should be called with one tag at the beginning', () => { + test('client.startTestItem should be called with corresponding params while one tag provided at the beginning of the test case title', () => { // @ts-ignore reporter.onTestBegin({ ...testCase, title: `@tag ${testCase.title}` }); From cf466771ca719461f189abc88c1235b96d158c98 Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Wed, 6 Mar 2024 09:35:09 +0100 Subject: [PATCH 14/21] Update src/__tests__/reporter/startSuiteTestReporting.spec.ts Co-authored-by: Ilya --- src/__tests__/reporter/startSuiteTestReporting.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/__tests__/reporter/startSuiteTestReporting.spec.ts b/src/__tests__/reporter/startSuiteTestReporting.spec.ts index bef05c9..78ef9dd 100644 --- a/src/__tests__/reporter/startSuiteTestReporting.spec.ts +++ b/src/__tests__/reporter/startSuiteTestReporting.spec.ts @@ -191,7 +191,7 @@ describe('start reporting suite/test', () => { expect(reporter.testItems).toEqual(expectedTestItems); }); - test('client.startTestItem should be called with one tag at the end @tag', () => { + test('client.startTestItem should be called with corresponding params while one tag provided at the end of the test case title', () => { // @ts-ignore reporter.onTestBegin({ ...testCase, title: `${testCase.title} @tag` }); From 052015019d829f03001b5fcce6bb22d292e1cff3 Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Wed, 6 Mar 2024 10:54:11 +0100 Subject: [PATCH 15/21] doc: tag for test title in add attributes --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b568488..19c03b2 100644 --- a/README.md +++ b/README.md @@ -66,8 +66,6 @@ The full list of available options presented below. | includeTestSteps | Optional | false | Allows you to see the test steps at the log level. | | includePlaywrightProjectNameToCodeReference | Optional | false | Includes Playwright project name to code reference. See [`testCaseId and codeRef calculation`](#setTestCaseId). It may be useful when you want to see the different history for the same test cases within different playwright projects. | | extendTestDescriptionWithLastError | Optional | true | If set to `true` the latest error log will be attached to the test case description. | -| grep | Optional | Not set | To run all the test cases contains a specific tag i.e. "@fast" | -| grepInvert | Optional | Not set | To run all the test cases not contains a specific tag i.e. "@fast" | | uploadVideo | Optional | true | Whether to attach the Playwright's [video](https://playwright.dev/docs/api/class-testoptions#test-options-video) to the test case. | | uploadTrace | Optional | true | Whether to attach the Playwright's [trace](https://playwright.dev/docs/api/class-testoptions#test-options-trace) to the test case. | | token | Deprecated | Not set | Use `apiKey` instead. | @@ -173,6 +171,18 @@ test('should have the correct attributes', () => { }); ``` +You can now add test attributes in test titles using `@tag` notation. This provides a succinct way to include metadata in test reports. Any `@tag` in a jest test title will be used as an attribute in ReportPortal, but won't appear in the final test title on ReportPortal. + +Example: + +```javascript +test('should have a tag at the beginning of the test title', () => { + ReportingApi.addAttributes([{ key: 'title', value: 'tag' }]); + + expect(true).toBe(true); +}); +``` + ##### setTestCaseId Set test case id to the current test ([About test case id](https://reportportal.io/docs/Test-case-ID%3Ewhat-is-it-test-case-id)). Should be called inside of corresponding test.
From 41ad66797c21b4641a456f68b4c913da931e3e8a Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Wed, 6 Mar 2024 15:00:45 +0100 Subject: [PATCH 16/21] Update README.md Co-authored-by: Ilya --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 19c03b2..068a686 100644 --- a/README.md +++ b/README.md @@ -171,7 +171,7 @@ test('should have the correct attributes', () => { }); ``` -You can now add test attributes in test titles using `@tag` notation. This provides a succinct way to include metadata in test reports. Any `@tag` in a jest test title will be used as an attribute in ReportPortal, but won't appear in the final test title on ReportPortal. +You can now add test attributes in test titles using `@tag` notation. This provides a succinct way to include metadata in test reports. Any `@tag` in a playwright test title will be used as an attribute in ReportPortal, but won't appear in the final test title on ReportPortal. Example: From ee5559bb1b762f8a3db3d1eb2f810f6357d57471 Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Wed, 6 Mar 2024 15:00:53 +0100 Subject: [PATCH 17/21] Update README.md Co-authored-by: Ilya --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 068a686..54df5c3 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ You can now add test attributes in test titles using `@tag` notation. This provi Example: ```javascript -test('should have a tag at the beginning of the test title', () => { +test('@tag should have a tag at the beginning of the test title', () => { ReportingApi.addAttributes([{ key: 'title', value: 'tag' }]); expect(true).toBe(true); From 94fb6332180b88ab19ca1d66127f1f2205568501 Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Wed, 6 Mar 2024 15:01:05 +0100 Subject: [PATCH 18/21] Update README.md Co-authored-by: Ilya --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 54df5c3..5b1fb68 100644 --- a/README.md +++ b/README.md @@ -177,7 +177,6 @@ Example: ```javascript test('@tag should have a tag at the beginning of the test title', () => { - ReportingApi.addAttributes([{ key: 'title', value: 'tag' }]); expect(true).toBe(true); }); From f6955ca2b6a9af79dab30323b84d13cdf115dbef Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Wed, 6 Mar 2024 15:59:10 +0100 Subject: [PATCH 19/21] doc: grep options under Reporting section in README --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index 5b1fb68..d4d5e21 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,29 @@ When organizing tests, specify titles for `test.describe` blocks, as this is nec It is also required to specify playwright project names in `playwright.config.ts` when running the same tests in different playwright projects. +### Advanced Test Filtering with grep Options + +To enhance control over which test cases are executed, the `@reportportal/agent-js-playwright` integration supports the `grep` and `grepInvert` options. These options provide a flexible way to include or exclude specific tests based on tags, enabling targeted test runs. + +- `grep`: Use this option to run only the tests that contain a specific tag. For example, setting `grep` to "@fast" will execute tests tagged with "@fast", allowing for a focused test run on a subset of tests. This is particularly useful for categorizing tests based on criteria like execution speed, features, or stability. + +- `grepInvert`: Conversely, the `grepInvert` option runs all tests that do **not** contain a specific tag. If you want to exclude tests tagged with "@fast" from your test run, you can use `grepInvert` with the value "@fast". This option is valuable for filtering out tests that might not be relevant to a particular testing context, such as excluding long-running tests from a quick check. + +These options can be specified in the Playwright configuration file, enhancing test execution flexibility by focusing on tests relevant to specific criteria or excluding tests that do not meet the current testing needs. + +```markdown +**Example usage in `playwright.config.ts`:** + +```javascript +const config: PlaywrightTestConfig = { + grep: '@fast', // Only run tests tagged with @fast + grepInvert: '@slow', // Run tests except those tagged with @slow + reporter: [['@reportportal/agent-js-playwright', RPconfig]], + testDir: './tests', +}; +export default config; +``` + ### Attachments Attachments can be easily added during test run via `testInfo.attach` according to the Playwright [docs](https://playwright.dev/docs/api/class-testinfo#test-info-attach). From e672879f9c38d68f3fcab660c3897b78084b4755 Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Thu, 7 Mar 2024 13:07:21 +0100 Subject: [PATCH 20/21] doc: update advanced Test Filtering with grep Options in README --- README.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index d4d5e21..1ff0112 100644 --- a/README.md +++ b/README.md @@ -94,13 +94,9 @@ It is also required to specify playwright project names in `playwright.config.ts ### Advanced Test Filtering with grep Options -To enhance control over which test cases are executed, the `@reportportal/agent-js-playwright` integration supports the `grep` and `grepInvert` options. These options provide a flexible way to include or exclude specific tests based on tags, enabling targeted test runs. +The `@reportportal/agent-js-playwright` integration recognizes `grep` and `grepInvert` from Playwright for test filtering. These options, when used, are automatically attached as launch attributes in ReportPortal for targeted test execution. -- `grep`: Use this option to run only the tests that contain a specific tag. For example, setting `grep` to "@fast" will execute tests tagged with "@fast", allowing for a focused test run on a subset of tests. This is particularly useful for categorizing tests based on criteria like execution speed, features, or stability. - -- `grepInvert`: Conversely, the `grepInvert` option runs all tests that do **not** contain a specific tag. If you want to exclude tests tagged with "@fast" from your test run, you can use `grepInvert` with the value "@fast". This option is valuable for filtering out tests that might not be relevant to a particular testing context, such as excluding long-running tests from a quick check. - -These options can be specified in the Playwright configuration file, enhancing test execution flexibility by focusing on tests relevant to specific criteria or excluding tests that do not meet the current testing needs. +Refer to Playwright documentation for [`grep`](https://playwright.dev/docs/api/class-testconfig#test-config-grep) and [`grepInvert`](https://playwright.dev/docs/api/class-testconfig#test-config-grep-invert) to learn about their usage. ```markdown **Example usage in `playwright.config.ts`:** @@ -114,6 +110,7 @@ const config: PlaywrightTestConfig = { }; export default config; ``` +```` ### Attachments @@ -200,7 +197,6 @@ Example: ```javascript test('@tag should have a tag at the beginning of the test title', () => { - expect(true).toBe(true); }); ``` From c158a3a2f70b39dc99c360026c90ea8cc56f6a6b Mon Sep 17 00:00:00 2001 From: Abdelrhman Arnos Date: Thu, 7 Mar 2024 13:08:02 +0100 Subject: [PATCH 21/21] doc: rm markdown in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ff0112..d9f034a 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ The `@reportportal/agent-js-playwright` integration recognizes `grep` and `grepI Refer to Playwright documentation for [`grep`](https://playwright.dev/docs/api/class-testconfig#test-config-grep) and [`grepInvert`](https://playwright.dev/docs/api/class-testconfig#test-config-grep-invert) to learn about their usage. -```markdown +``` **Example usage in `playwright.config.ts`:** ```javascript