Skip to content

Commit

Permalink
EPMRPP-78272 || Implement attaching data using Playwright testInfo.at…
Browse files Browse the repository at this point in the history
…tach (#123)

* EPMRPP-78272 || Attach additional data via testInfo.attach

* EPMRPP-78272 || Fix failed tests

* EPMRPP-78272 || Code review fixes - 2

* EPMRPP-78272 || Increase test coverage

* EPMRPP-78272 || Code review fixes - 2

- Remove unused interface
- Reuse ReportingApi.setStatus method

* EPMRPP-78272 || Code review fixes - 3
  • Loading branch information
Bam6ycha authored and AmsterGet committed Dec 18, 2023
1 parent 7fb8d79 commit 6742009
Show file tree
Hide file tree
Showing 19 changed files with 817 additions and 65 deletions.
5 changes: 3 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module",
"project": "./tsconfig.eslint.json"
"project": "./tsconfig.eslint.json"
},
"rules": {
"no-plusplus": 0,
Expand All @@ -33,6 +33,7 @@
"@typescript-eslint/ban-ts-comment": 0,
"@typescript-eslint/no-implied-eval": 0,
"dot-notation": 0,
"@typescript-eslint/naming-convention": 0
"@typescript-eslint/naming-convention": 0,
"consistent-return": "warn"
}
}
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Ignore all md files
*.md
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### Added
- `extendTestDescriptionWithLastError` option to the RP config to have the ability to attach the last error log
- `ReportingApi` from `@reportportal/agent-js-playwright/promises` methods (***addAttributes, setDescription, setTestCaseId, setStatus***, and all methods for setting custom statuses for test or suite) now using ***testInfo.attach*** method to attach custom data to test case.

## [5.1.4] - 2023-10-05
## Changed
Expand Down
58 changes: 34 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ 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'`:
To start using the `ReportingApi` in tests, just import it from `'@reportportal/agent-js-playwright/promises'`:
```javascript
import { ReportingApi } from '@reportportal/agent-js-playwright';
import { ReportingApi } from '@reportportal/agent-js-playwright/promises'
```

#### Reporting API methods
Expand All @@ -144,13 +144,15 @@ If you want to add a data to the suite, you must pass the suite name as the last

##### addAttributes
Add attributes (tags) to the current test. Should be called inside of corresponding test.<br/>
`ReportingApi.addAttributes(attributes: Array<Attribute>, suite?: string);`<br/>
`ReportingApi.addAttributes(attributes: Array<Attribute>, suite?: string):Promise<void>;`<br/>
**required**: `attributes`<br/>
**optional**: `suite`<br/>
Example:
```javascript
test('should have the correct attributes', () => {
ReportingApi.addAttributes([
import { ReportingApi } from '@reportportal/agent-js-playwright/promises'

test('should have the correct attributes', async () => {
await ReportingApi.addAttributes([
{
key: 'testKey',
value: 'testValue',
Expand All @@ -165,14 +167,16 @@ 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.<br/>
`ReportingApi.setTestCaseId(id: string, suite?: string);`<br/>
`ReportingApi.setTestCaseId(id: string, suite?: string):Promise<void>;`<br/>
**required**: `id`<br/>
**optional**: `suite`<br/>
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-).<br/>
Example:
```javascript
test('should have the correct testCaseId', () => {
ReportingApi.setTestCaseId('itemTestCaseId');
import { ReportingApi } from '@reportportal/agent-js-playwright/promises'

test('should have the correct testCaseId', async () => {
await ReportingApi.setTestCaseId('itemTestCaseId');
expect(true).toBe(true);
});
```
Expand Down Expand Up @@ -271,37 +275,43 @@ test('should contain logs with attachments', () => {

##### setStatus
Assign corresponding status to the current test item. Should be called inside of corresponding test.<br/>
`ReportingApi.setStatus(status: string, suite?: string);`<br/>
`ReportingApi.setStatus(status: string, suite?: string):Promise<void>;`<br/>
**required**: `status`<br/>
**optional**: `suite`<br/>
where `status` must be one of the following: *passed*, *failed*, *stopped*, *skipped*, *interrupted*, *cancelled*<br/>
Example:
```javascript
test('should have status FAILED', () => {
ReportingApi.setStatus('failed');
import { ReportingApi } from '@reportportal/agent-js-playwright/promises'

test('should have status FAILED', async () => {
await 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.<br/>
`ReportingApi.setStatusFailed(suite?: string);`<br/>
`ReportingApi.setStatusPassed(suite?: string);`<br/>
`ReportingApi.setStatusSkipped(suite?: string);`<br/>
`ReportingApi.setStatusStopped(suite?: string);`<br/>
`ReportingApi.setStatusInterrupted(suite?: string);`<br/>
`ReportingApi.setStatusCancelled(suite?: string);`<br/>
`ReportingApi.setStatusFailed(suite?: string):Promise<void>;`<br/>
`ReportingApi.setStatusPassed(suite?: string):Promise<void>;`<br/>
`ReportingApi.setStatusSkipped(suite?: string):Promise<void>;`<br/>
`ReportingApi.setStatusStopped(suite?: string):Promise<void>;`<br/>
`ReportingApi.setStatusInterrupted(suite?: string):Promise<void>;`<br/>
`ReportingApi.setStatusCancelled(suite?: string):Promise<void>;`<br/>
**optional**: `suite`<br/>
Example:
```javascript
test('should call ReportingApi to set statuses', () => {
ReportingAPI.setStatusFailed();
ReportingAPI.setStatusPassed();
ReportingAPI.setStatusSkipped();
ReportingAPI.setStatusStopped();
ReportingAPI.setStatusInterrupted();
ReportingAPI.setStatusCancelled();
import { ReportingApi } from '@reportportal/agent-js-playwright/promises'

test('should call ReportingApi to set statuses', async () => {
await Promise.all[
ReportingAPI.setStatusFailed(),
ReportingAPI.setStatusPassed(),
ReportingAPI.setStatusSkipped(),
ReportingAPI.setStatusStopped(),
ReportingAPI.setStatusInterrupted(),
ReportingAPI.setStatusCancelled(),
]
});
```

Expand Down
25 changes: 23 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,29 @@
"name": "@reportportal/agent-js-playwright",
"version": "5.1.4",
"description": "Agent to integrate Playwright with ReportPortal.",
"main": "build/index.js",
"types": "build/index.d.ts",
"exports": {
".": {
"node": "./build/index.js",
"import": "./build/index.js",
"require": "./build/index.js",
"default": "./build/index.js"
},
"./promises": {
"node": "./build/promises/index.js",
"default": "./build/promises/index.js",
"require": "./build/promises/index.js",
"import": "./build/promises/index.js"
}
},
"typesVersions": {
"*": {
"promises": [
"./build/promises/index.d.ts"
]
}
},
"types": "./build/index.d.ts",
"main": "./build/index.js",
"scripts": {
"build": "npm run clean && tsc",
"clean": "rimraf ./build",
Expand Down
192 changes: 192 additions & 0 deletions src/__tests__/promises/reportingApi.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* Copyright 2021 EPAM Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

import { ReportingApi } from '../../promises';
import * as utils from '../../utils';
import { LOG_LEVELS, STATUSES } from '../../constants';

const reportingApiStatusMethods = [
{ method: 'setStatusPassed', status: 'passed' },
{ method: 'setStatusFailed', status: 'failed' },
{ method: 'setStatusSkipped', status: 'skipped' },
{ method: 'setStatusStopped', status: 'stopped' },
{ method: 'setStatusInterrupted', status: 'interrupted' },
{ method: 'setStatusCancelled', status: 'cancelled' },
{ method: 'setStatusInfo', status: 'info' },
{ method: 'setStatusWarn', status: 'warn' },
];

const reportingApiLaunchStatusMethods = [
{ method: 'setLaunchStatusPassed', status: 'passed' },
{ method: 'setLaunchStatusFailed', status: 'failed' },
{ method: 'setLaunchStatusSkipped', status: 'skipped' },
{ method: 'setLaunchStatusStopped', status: 'stopped' },
{ method: 'setLaunchStatusInterrupted', status: 'interrupted' },
{ method: 'setLaunchStatusCancelled', status: 'cancelled' },
{ method: 'setLaunchStatusInfo', status: 'info' },
{ method: 'setLaunchStatusWarn', status: 'warn' },
];

const reportingApiLogMethods = [
{ method: 'trace', level: 'TRACE' },
{ method: 'debug', level: 'DEBUG' },
{ method: 'info', level: 'INFO' },
{ method: 'warn', level: 'WARN' },
{ method: 'error', level: 'ERROR' },
{ method: 'fatal', level: 'FATAL' },
];

const reportingApiLaunchLogMethods = [
{ method: 'launchTrace', level: 'TRACE' },
{ method: 'launchDebug', level: 'DEBUG' },
{ method: 'launchInfo', level: 'INFO' },
{ method: 'launchWarn', level: 'WARN' },
{ method: 'launchError', level: 'ERROR' },
{ method: 'launchFatal', level: 'FATAL' },
];

describe('reportingApi', () => {
beforeEach(() => {
jest.clearAllMocks();
});

test('addAttributes should call sendEventToReporter with params', () => {
const attrs = [
{
key: 'key',
value: 'value',
},
];
const suite = 'suite';
const spySendEventToReporter = jest.spyOn(utils, 'sendEventToReporter');
ReportingApi.addAttributes(attrs, suite);

expect(spySendEventToReporter).toHaveBeenCalledTimes(1);
});

test('setDescription should call sendEventToReporter with params', () => {
const description = 'description';
const suite = 'suite';
const spySendEventToReporter = jest.spyOn(utils, 'sendEventToReporter');
ReportingApi.setDescription(description, suite);

expect(spySendEventToReporter).toHaveBeenCalledTimes(1);
});

test('setTestCaseId should call sendEventToReporter with params', () => {
const testCaseId = 'TestCaseIdForTheSuite';
const suite = 'suite';
const spySendEventToReporter = jest.spyOn(utils, 'sendEventToReporter');
ReportingApi.setTestCaseId(testCaseId, suite);

expect(spySendEventToReporter).toHaveBeenCalledTimes(1);
});

describe('Item status reporting', () => {
reportingApiStatusMethods.map(({ method, status }) => {
test(`${method} should call sendEventToReporter with ${status} status`, () => {
const suite = 'suite';
const spySendEventToReporter = jest.spyOn(utils, 'sendEventToReporter');
// @ts-ignore
ReportingApi[method](suite);

expect(spySendEventToReporter).toHaveBeenCalledTimes(1);
});
});

test('ReportingApi.setStatus should call sendEventToReporter with provided status', () => {
const suite = 'suite';
const spySendEventToReporter = jest.spyOn(utils, 'sendEventToReporter');
ReportingApi.setStatus(STATUSES.PASSED, suite);

expect(spySendEventToReporter).toHaveBeenCalledTimes(1);
});
});

describe('Launch status reporting', () => {
reportingApiLaunchStatusMethods.map(({ method, status }) => {
test(`${method} should call sendEventToReporter with ${status} status`, () => {
const spySendEventToReporter = jest.spyOn(utils, 'sendEventToReporter');
// @ts-ignore
ReportingApi[method]();

expect(spySendEventToReporter).toHaveBeenCalledTimes(1);
});
});

test('ReportingApi.setLaunchStatus should call sendEventToReporter with provided status', () => {
const spySendEventToReporter = jest.spyOn(utils, 'sendEventToReporter');
const status = 'PASSED';
ReportingApi.setLaunchStatus(status);

expect(spySendEventToReporter).toHaveBeenCalledTimes(1);
});
});

describe('Logs reporting', () => {
const file = {
name: 'filename',
type: 'image/png',
content: Buffer.from([1, 2, 3, 4, 5, 6, 7]).toString('base64'),
};
const suite = 'suite';

reportingApiLogMethods.map(({ method, level }) => {
test(`${method} should call ReportingApi.log with ${level} level`, () => {
const spyLogFunc = jest.spyOn(ReportingApi, 'log');

// @ts-ignore
ReportingApi[method]('message', file, suite);

expect(spyLogFunc).toHaveBeenCalledTimes(1);
});
});

test('ReportingApi.log should call sendEventToReporter with params', () => {
const spySendEventToReporter = jest.spyOn(utils, 'sendEventToReporter');
ReportingApi.log(LOG_LEVELS.INFO, 'message', file, suite);

expect(spySendEventToReporter).toHaveBeenCalledTimes(1);
});
});

describe('Launch logs reporting', () => {
const file = {
name: 'filename',
type: 'image/png',
content: Buffer.from([1, 2, 3, 4, 5, 6, 7]).toString('base64'),
};

reportingApiLaunchLogMethods.map(({ method, level }) => {
test(`${method} should call ReportingApi.launchLog with ${level} level`, () => {
const spyLogFunc = jest.spyOn(ReportingApi, 'launchLog');

// @ts-ignore
ReportingApi[method]('message', file);

expect(spyLogFunc).toHaveBeenCalledTimes(1);
});
});

test('ReportingApi.launchLog should call sendEventToReporter with params', () => {
const spySendEventToReporter = jest.spyOn(utils, 'sendEventToReporter');
ReportingApi.launchLog(LOG_LEVELS.INFO, 'message', file);

expect(spySendEventToReporter).toHaveBeenCalledTimes(1);
});
});
});
2 changes: 2 additions & 0 deletions src/__tests__/reporter/finishSuiteReporting.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ describe('finish suites on finish all of their children', () => {
const testCase = {
title: 'testTitle',
id: 'testItemId',
//@ts-ignore
results: [{ attachments: [] }],
parent: {
title: rootSuite,
project: () => ({ name: rootSuite }),
Expand Down
Loading

0 comments on commit 6742009

Please sign in to comment.