From fffa07aa8c2695c7d7755eee15260b2a102b4fc0 Mon Sep 17 00:00:00 2001 From: Dzmitry Kosarau Date: Tue, 10 Oct 2023 17:30:51 +0500 Subject: [PATCH 1/6] EPMRPP-78272 || Attach additional data via testInfo.attach --- .eslintrc | 5 +- CHANGELOG.md | 2 + README.md | 63 ++++++++ .../reporter/finishTestItemReporting.spec.ts | 93 +++++++++++- src/__tests__/utils.spec.ts | 137 ++++++++++++++++++ src/constants/index.ts | 1 + src/constants/testInfo.ts | 23 +++ src/index.ts | 1 + src/models/reporting.ts | 14 ++ src/reporter.ts | 20 ++- src/utils.ts | 41 +++++- 11 files changed, 390 insertions(+), 10 deletions(-) create mode 100644 src/constants/testInfo.ts diff --git a/.eslintrc b/.eslintrc index dbb4ec4..a61a91f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -18,7 +18,7 @@ "parserOptions": { "ecmaVersion": 2018, "sourceType": "module", - "project": "./tsconfig.eslint.json" + "project": "./tsconfig.eslint.json" }, "rules": { "no-plusplus": 0, @@ -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" } } diff --git a/CHANGELOG.md b/CHANGELOG.md index f0ca94b..fe1e124 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +### Added +- Add ability to provide `attributes`,`status`,`description`,`testCaseId` for test using `testInfo.attach` ## [5.1.4] - 2023-10-05 ## Changed diff --git a/README.md b/README.md index 444e6ac..ca66321 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,69 @@ test('basic test', async ({ page }, testInfo) => { }); ``` +Also, we can attach `attributes`,`status`,`description`,`testCaseId` + +`Description` +```typescript +import { test, expect } from '@playwright/test'; +import { RPTestInfo } from '@reportportal/agent-js-playwright' + +test('basic test', async ({ page }, testInfo) => { + await testInfo.attach(RPTestInfo.description, { body: 'Description', contentType: 'plain/text' }); + + expect(true).toBe(true); +}); +``` +Description provided this way will be merged with description provided by `ReportingAPI`. + +`Attributes` +```typescript +import { test, expect } from '@playwright/test'; +import { RPTestInfo } from '@reportportal/agent-js-playwright' + +test('basic test', async ({ page }, testInfo) => { + await testInfo.attach(RPTestInfo.status, { body: JSON.stringify([ + { + key: 'testKey', + value: 'testValue', + }, + { + value: 'testValueTwo', + } + ]), + contentType: 'application/json' }); + + expect(true).toBe(true); +}); +``` +Attributes provided this way will be merged with Attributes provided by `ReportingAPI`. + +`Status` +```typescript +import { test, expect } from '@playwright/test'; +import { RPTestInfo } from '@reportportal/agent-js-playwright' + +test('basic test', async ({ page }, testInfo) => { + await testInfo.attach(RPTestInfo.status, { body: 'passed', contentType:'text/plain'}) + + expect(true).toBe(true); +}); +``` +Status provided this way will be replaced by status provided by `ReportingAPI`. You can provide as many statuses as you want, but only the last will be applied. + +`testCaseId` +```typescript +import { test, expect } from '@playwright/test'; +import { RPTestInfo } from '@reportportal/agent-js-playwright' + +test('basic test', async ({ page }, testInfo) => { + await testInfo.attach(RPTestInfo.testCaseId, { body: 'testCaseId', contentType:'text/plain'}) + + expect(true).toBe(true); +}); +``` +TestCaseId provided this way will be replaced by testCaseId provided by `ReportingAPI`. + *Note:* attachment path can be provided instead of body. As an alternative to this approach the [`ReportingAPI`](#log) methods can be used. diff --git a/src/__tests__/reporter/finishTestItemReporting.spec.ts b/src/__tests__/reporter/finishTestItemReporting.spec.ts index 46ed03a..8911e6c 100644 --- a/src/__tests__/reporter/finishTestItemReporting.spec.ts +++ b/src/__tests__/reporter/finishTestItemReporting.spec.ts @@ -18,7 +18,7 @@ import { RPReporter } from '../../reporter'; import { mockConfig } from '../mocks/configMock'; import { RPClientMock } from '../mocks/RPClientMock'; import { FinishTestItemObjType } from '../../models'; -import { STATUSES } from '../../constants'; +import { RPTestInfo, STATUSES } from '../../constants'; const rootSuite = 'rootSuite'; const suiteName = 'suiteName'; @@ -27,6 +27,8 @@ describe('finish test reporting', () => { const testCase = { title: 'testTitle', id: 'testItemId', + //@ts-ignore + results: [{ attachments: [] }], parent: { title: rootSuite, project: () => ({ name: rootSuite }), @@ -189,4 +191,93 @@ describe('finish test reporting', () => { expect(reporter.client.finishTestItem).toHaveBeenCalledWith('1214r1', finishStepObject); }); + + test('client.finishTestItem should call reporter.client.finishTestItem with correct values', async () => { + const result = { status: 'passed' }; + + reporter.testItems = new Map([ + [ + `${testCase.id}`, + { + id: 'testItemId', + name: 'name', + description: 'savedTestItemDescription', + status: STATUSES.PASSED, + attributes: [ + { value: 'savedTestItemAttrValue' }, + { key: 'savedTestItemAttrKey', value: 'savedTestItemAttrValue', system: false }, + ], + }, + ], + ]); + + await reporter.onTestEnd( + { + ...testCase, + outcome: () => 'expected', + results: [ + // @ts-ignore + { + attachments: [ + { + name: RPTestInfo.attributes, + contentType: 'application/json', + body: Buffer.from( + JSON.stringify([ + { key: 'key1', value: 'value1', system: false }, + { key: 'key2', value: 'value2', system: false }, + ]), + ), + }, + { + name: RPTestInfo.description, + contentType: 'plain/text', + body: Buffer.from('Description'), + }, + { + name: RPTestInfo.status, + contentType: 'plain/text', + body: Buffer.from('skipped'), + }, + { + name: RPTestInfo.status, + contentType: 'plain/text', + body: Buffer.from('interrupted'), + }, + { + name: RPTestInfo.testCaseId, + contentType: 'plain/text', + body: Buffer.from('testCaseId'), + }, + { + name: 'notAllowedField', + contentType: 'plain/text', + body: Buffer.from('notAllowedValue'), + }, + ], + }, + ], + }, + result, + ); + + const finishStepObject: FinishTestItemObjType = { + endTime: reporter.client.helpers.now(), + status: STATUSES.PASSED, + attributes: [ + { key: 'key1', value: 'value1', system: false }, + { key: 'key2', value: 'value2', system: false }, + { value: 'savedTestItemAttrValue' }, + { key: 'savedTestItemAttrKey', value: 'savedTestItemAttrValue', system: false }, + ], + description: 'savedTestItemDescription\nDescription', + testCaseId: 'testCaseId', + }; + + expect(reporter.client.finishTestItem).toHaveBeenNthCalledWith( + 1, + 'testItemId', + finishStepObject, + ); + }); }); diff --git a/src/__tests__/utils.spec.ts b/src/__tests__/utils.spec.ts index 72addaf..95b5f8e 100644 --- a/src/__tests__/utils.spec.ts +++ b/src/__tests__/utils.spec.ts @@ -27,6 +27,8 @@ import { getAttachments, isErrorLog, calculateRpStatus, + getAdditionalInfo, + getDescription, } from '../utils'; import fs from 'fs'; import path from 'path'; @@ -35,7 +37,10 @@ import { TestOutcome, BASIC_ATTACHMENT_CONTENT_TYPES, BASIC_ATTACHMENT_NAMES, + RPTestInfo, } from '../constants'; +import { TestCase } from '@playwright/test/reporter'; +import { TestAdditionalInfo } from '../models/reporting'; describe('testing utils', () => { test('isFalse', () => { @@ -418,4 +423,136 @@ describe('testing utils', () => { expect(status).toBe(STATUSES.PASSED); }); }); + + describe('getAdditionalInfo', () => { + test('Should collect only allowed fields', () => { + const testCase = { + results: [ + { + attachments: [ + { + name: RPTestInfo.attributes, + contentType: 'application/json', + body: Buffer.from( + JSON.stringify([ + { key: 'key1', value: 'value1', system: true }, + { key: 'key2', value: 'value2', system: true }, + ]), + ), + }, + { + name: RPTestInfo.description, + contentType: 'plain/text', + body: Buffer.from('Description'), + }, + { + name: RPTestInfo.status, + contentType: 'plain/text', + body: Buffer.from('skipped'), + }, + { + name: RPTestInfo.status, + contentType: 'plain/text', + body: Buffer.from('failed'), + }, + { + name: RPTestInfo.testCaseId, + contentType: 'plain/text', + body: Buffer.from('testCaseId'), + }, + { + name: 'notAllowedField', + contentType: 'plain/text', + body: Buffer.from('notAllowedValue'), + }, + ], + }, + ], + }; + + const expectedResult: TestAdditionalInfo = { + attributes: [ + { key: 'key1', value: 'value1', system: true }, + { key: 'key2', value: 'value2', system: true }, + ], + description: 'Description', + status: 'failed', + testCaseId: 'testCaseId', + }; + + const additionalInfo = getAdditionalInfo(testCase as TestCase); + + expect(additionalInfo).toEqual(expectedResult); + }); + + test('Should recover from error in case if JSON is not valid', () => { + const testCase = { + results: [ + { + attachments: [ + { + name: RPTestInfo.attributes, + contentType: 'application/json', + body: Buffer.from( + `{ key: 'key1', value: 'value1', system: true }, + { key: 'key2', value: 'value2', system: true }`, + ), + }, + { + name: RPTestInfo.description, + contentType: 'plain/text', + body: Buffer.from('Description'), + }, + { + name: RPTestInfo.status, + contentType: 'plain/text', + body: Buffer.from('skipped'), + }, + ], + }, + ], + }; + + const expectedResult: TestAdditionalInfo = { + attributes: [], + description: 'Description', + status: 'skipped', + testCaseId: '', + }; + + const error = new Error('Unexpected token k in JSON at position 2'); + + console.error = jest.fn(); + + const additionalInfo = getAdditionalInfo(testCase as TestCase); + + expect(console.error).toBeCalledTimes(1); + expect(console.error).toHaveBeenCalledWith(error.message); + expect(additionalInfo).toEqual(expectedResult); + }); + }); + + describe('getDescription', () => { + test('It should return one description joined by \n', () => { + const description1 = 'description1'; + const description2 = 'description2'; + + const expectedResult = 'description1\ndescription2'; + + const description = getDescription(description1, description2); + + expect(expectedResult).toBe(description); + }); + + test('It should return one description joined by \n in case when empty string was provided', () => { + const description1 = 'description1'; + const description2 = ''; + + const expectedResult = 'description1'; + + const description = getDescription(description1, description2); + + expect(expectedResult).toBe(description); + }); + }); }); diff --git a/src/constants/index.ts b/src/constants/index.ts index 7eee66e..3c9d315 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -19,6 +19,7 @@ export { LAUNCH_MODES } from './launchModes'; export { TEST_ITEM_TYPES } from './testItemTypes'; export { STATUSES } from './statuses'; export { LOG_LEVELS } from './logLevels'; +export { RPTestInfo } from './testInfo'; export { TestAnnotation, TestOutcome, diff --git a/src/constants/testInfo.ts b/src/constants/testInfo.ts new file mode 100644 index 0000000..ba121c7 --- /dev/null +++ b/src/constants/testInfo.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2023 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. + * + */ + +export const RPTestInfo = { + status: 'status', + attributes: 'attributes', + description: 'description', + testCaseId: 'testCaseId', +} as const; diff --git a/src/index.ts b/src/index.ts index ce7e3dc..25da34e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,6 @@ import { RPReporter } from './reporter'; export { ReportingApi } from './reportingApi'; export { LOG_LEVELS, STATUSES } from './constants'; +export { RPTestInfo } from './constants'; export default RPReporter; diff --git a/src/models/reporting.ts b/src/models/reporting.ts index c2021ae..572d05b 100644 --- a/src/models/reporting.ts +++ b/src/models/reporting.ts @@ -66,3 +66,17 @@ export interface LogRQ { export interface TestStepWithId extends TestStep { id: string; } + +export interface TestInfoAttachment { + name: string; + contentType?: string; + body?: string | Buffer; + path?: string; +} + +export interface TestAdditionalInfo { + status?: string; + attributes?: Attribute[]; + description?: string; + testCaseId?: string; +} diff --git a/src/reporter.ts b/src/reporter.ts index 152be5a..cb26f19 100644 --- a/src/reporter.ts +++ b/src/reporter.ts @@ -37,9 +37,11 @@ import { } from './constants'; import { calculateRpStatus, + getAdditionalInfo, getAgentInfo, getAttachments, getCodeRef, + getDescription, getSystemAttributes, isErrorLog, isFalse, @@ -452,6 +454,9 @@ export class RPReporter implements Reporter { if (!savedTestItem) { return Promise.resolve(); } + + const additionalInfo = getAdditionalInfo(test); + const { id: testItemId, attributes, @@ -460,9 +465,12 @@ export class RPReporter implements Reporter { status: predefinedStatus, } = savedTestItem; let withoutIssue; - let testDescription = description; + let testDescription = getDescription(description, additionalInfo.description); + const calculatedStatus = calculateRpStatus(test.outcome(), result.status, test.annotations); - const status = predefinedStatus || calculatedStatus; + const status = predefinedStatus || additionalInfo.status || calculatedStatus; + const calculatedTestCaseId = testCaseId || additionalInfo.testCaseId; + const mergedAttributes = additionalInfo.attributes.concat(attributes ?? []); if (status === STATUSES.SKIPPED) { withoutIssue = isFalse(this.config.skippedIssue); } @@ -489,7 +497,9 @@ export class RPReporter implements Reporter { level: LOG_LEVELS.ERROR, message: stacktrace, }); - testDescription = (description || '').concat(`\n\`\`\`error\n${stacktrace}\n\`\`\``); + testDescription = getDescription(description, additionalInfo.description).concat( + `\n\`\`\`error\n${stacktrace}\n\`\`\``, + ); } [...this.nestedSteps.entries()].forEach(([key, value]) => { @@ -511,9 +521,9 @@ export class RPReporter implements Reporter { endTime: this.client.helpers.now(), status, ...(withoutIssue && { issue: { issueType: 'NOT_ISSUE' } }), - ...(attributes && { attributes }), + ...(mergedAttributes.length !== 0 && { attributes: mergedAttributes }), ...(testDescription && { description: testDescription }), - ...(testCaseId && { testCaseId }), + ...(calculatedTestCaseId && { testCaseId: calculatedTestCaseId }), }; const { promise } = this.client.finishTestItem(testItemId, finishTestItemObj); diff --git a/src/utils.ts b/src/utils.ts index 1a29c85..68eb07e 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -29,7 +29,9 @@ import { BASIC_ATTACHMENT_CONTENT_TYPES, TEST_ANNOTATION_TYPES, TEST_OUTCOME_TYPES, + RPTestInfo, } from './constants'; +import { TestAdditionalInfo } from './models/reporting'; const fsPromises = fs.promises; @@ -123,13 +125,13 @@ export const getAttachments = async ( fileContent = body; } else { if (!fs.existsSync(attachmentPath)) { - return; + return undefined; } fileContent = await fsPromises.readFile(attachmentPath); } } catch (e) { console.error(e); - return; + return undefined; } return { @@ -175,3 +177,38 @@ export const calculateRpStatus = ( return calculatedStatus; }; + +export const getAdditionalInfo = (test: TestCase): TestAdditionalInfo => { + const initialValue: TestAdditionalInfo = { + attributes: [], + description: '', + testCaseId: '', + status: '', + }; + + return test.results.reduce( + (additionalInfo, { attachments = [] }) => + Object.assign( + additionalInfo, + attachments.reduce((acc, { name, body }) => { + if (name in RPTestInfo) { + try { + const value = body.toString(); + + return Object.assign(acc, { + [name]: name === RPTestInfo.attributes ? JSON.parse(value) : value, + }); + } catch (error: unknown) { + console.error((error as Error).message); + } + } + + return acc; + }, initialValue), + ), + initialValue, + ); +}; + +export const getDescription = (...descriptions: string[]): string => + descriptions.filter(Boolean).join('\n'); From 9ec793d02556a1e7c63f4b22b79ac988ea833a78 Mon Sep 17 00:00:00 2001 From: Dzmitry Kosarau Date: Tue, 10 Oct 2023 17:40:39 +0500 Subject: [PATCH 2/6] EPMRPP-78272 || Fix failed tests --- src/__tests__/reporter/finishSuiteReporting.spec.ts | 2 ++ src/__tests__/reporter/logReporting.spec.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/__tests__/reporter/finishSuiteReporting.spec.ts b/src/__tests__/reporter/finishSuiteReporting.spec.ts index c136818..44c0cc9 100644 --- a/src/__tests__/reporter/finishSuiteReporting.spec.ts +++ b/src/__tests__/reporter/finishSuiteReporting.spec.ts @@ -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 }), diff --git a/src/__tests__/reporter/logReporting.spec.ts b/src/__tests__/reporter/logReporting.spec.ts index dc5bbaa..b0ea2dd 100644 --- a/src/__tests__/reporter/logReporting.spec.ts +++ b/src/__tests__/reporter/logReporting.spec.ts @@ -110,6 +110,7 @@ describe('logs reporting', () => { const testCase = { title: 'testTitle', id: 'testItemId', + results: [{ attachments: [{}] }], parent: { title: playwrightProjectName, location: 'tests/example.js', From e0425601310f2be5b3c323a221fb664818a4c922 Mon Sep 17 00:00:00 2001 From: Dzmitry Kosarau Date: Sat, 14 Oct 2023 17:16:49 +0500 Subject: [PATCH 3/6] EPMRPP-78272 || Code review fixes - 2 --- .prettierignore | 2 + CHANGELOG.md | 2 +- README.md | 137 +++++------ package.json | 25 ++- .../reporter/finishTestItemReporting.spec.ts | 75 +++++-- src/__tests__/utils.spec.ts | 25 --- src/index.ts | 2 +- src/promises/index.ts | 1 + src/promises/reportingApi.ts | 212 ++++++++++++++++++ src/reporter.ts | 27 +-- src/reportingApi.ts | 123 ++++++++-- src/utils.ts | 3 - 12 files changed, 473 insertions(+), 161 deletions(-) create mode 100644 .prettierignore create mode 100644 src/promises/index.ts create mode 100644 src/promises/reportingApi.ts diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..60700ed --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +# Ignore all md files +*.md diff --git a/CHANGELOG.md b/CHANGELOG.md index fe1e124..e59c72c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ ### Added -- Add ability to provide `attributes`,`status`,`description`,`testCaseId` for test using `testInfo.attach` +- `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 diff --git a/README.md b/README.md index ca66321..1750852 100644 --- a/README.md +++ b/README.md @@ -102,69 +102,6 @@ test('basic test', async ({ page }, testInfo) => { }); ``` -Also, we can attach `attributes`,`status`,`description`,`testCaseId` - -`Description` -```typescript -import { test, expect } from '@playwright/test'; -import { RPTestInfo } from '@reportportal/agent-js-playwright' - -test('basic test', async ({ page }, testInfo) => { - await testInfo.attach(RPTestInfo.description, { body: 'Description', contentType: 'plain/text' }); - - expect(true).toBe(true); -}); -``` -Description provided this way will be merged with description provided by `ReportingAPI`. - -`Attributes` -```typescript -import { test, expect } from '@playwright/test'; -import { RPTestInfo } from '@reportportal/agent-js-playwright' - -test('basic test', async ({ page }, testInfo) => { - await testInfo.attach(RPTestInfo.status, { body: JSON.stringify([ - { - key: 'testKey', - value: 'testValue', - }, - { - value: 'testValueTwo', - } - ]), - contentType: 'application/json' }); - - expect(true).toBe(true); -}); -``` -Attributes provided this way will be merged with Attributes provided by `ReportingAPI`. - -`Status` -```typescript -import { test, expect } from '@playwright/test'; -import { RPTestInfo } from '@reportportal/agent-js-playwright' - -test('basic test', async ({ page }, testInfo) => { - await testInfo.attach(RPTestInfo.status, { body: 'passed', contentType:'text/plain'}) - - expect(true).toBe(true); -}); -``` -Status provided this way will be replaced by status provided by `ReportingAPI`. You can provide as many statuses as you want, but only the last will be applied. - -`testCaseId` -```typescript -import { test, expect } from '@playwright/test'; -import { RPTestInfo } from '@reportportal/agent-js-playwright' - -test('basic test', async ({ page }, testInfo) => { - await testInfo.attach(RPTestInfo.testCaseId, { body: 'testCaseId', contentType:'text/plain'}) - - expect(true).toBe(true); -}); -``` -TestCaseId provided this way will be replaced by testCaseId provided by `ReportingAPI`. - *Note:* attachment path can be provided instead of body. As an alternative to this approach the [`ReportingAPI`](#log) methods can be used. @@ -193,9 +130,11 @@ 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'` or `'@reportportal/agent-js-playwright/promises'`: ```javascript import { ReportingApi } from '@reportportal/agent-js-playwright'; +// or +import { ReportingApi } from '@reportportal/agent-js-playwright/promises' ``` #### Reporting API methods @@ -225,6 +164,27 @@ test('should have the correct attributes', () => { }); ``` +`ReportingApi.addAttributes(attributes: Array, suite?: string):Promise;`
+**required**: `attributes`
+**optional**: `suite`
+Example: +```javascript +import { ReportingApi } from '@reportportal/agent-js-playwright/promises' + +test('should have the correct attributes', async () => { + await ReportingApi.addAttributes([ + { + key: 'testKey', + value: 'testValue', + }, + { + value: 'testValueTwo', + }, + ]); + 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.
`ReportingApi.setTestCaseId(id: string, suite?: string);`
@@ -239,6 +199,19 @@ test('should have the correct testCaseId', () => { }); ``` +`ReportingApi.setTestCaseId(id: string, suite?: string):Promise;`
+**required**: `id`
+**optional**: `suite`
+Example: +```javascript +import { ReportingApi } from '@reportportal/agent-js-playwright/promises' + +test('should have the correct testCaseId', async () => { + await ReportingApi.setTestCaseId('itemTestCaseId'); + expect(true).toBe(true); +}); +``` + ##### 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);`
@@ -346,6 +319,19 @@ test('should have status FAILED', () => { }); ``` +`ReportingApi.setStatus(status: string, suite?: string):Promise;`
+**required**: `status`
+**optional**: `suite`
+```javascript +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.
`ReportingApi.setStatusFailed(suite?: string);`
@@ -365,6 +351,29 @@ test('should call ReportingApi to set statuses', () => { ReportingAPI.setStatusInterrupted(); ReportingAPI.setStatusCancelled(); }); + +``` +`ReportingApi.setStatusFailed(suite?: string):Promise;`
+`ReportingApi.setStatusPassed(suite?: string):Promise;`
+`ReportingApi.setStatusSkipped(suite?: string):Promise;`
+`ReportingApi.setStatusStopped(suite?: string):Promise;`
+`ReportingApi.setStatusInterrupted(suite?: string):Promise;`
+`ReportingApi.setStatusCancelled(suite?: string):Promise;`
+**optional**: `suite`
+Example: +```javascript +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() + ] +}); ``` ##### setLaunchStatus diff --git a/package.json b/package.json index b822edd..51beab7 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/__tests__/reporter/finishTestItemReporting.spec.ts b/src/__tests__/reporter/finishTestItemReporting.spec.ts index 8911e6c..2e1da07 100644 --- a/src/__tests__/reporter/finishTestItemReporting.spec.ts +++ b/src/__tests__/reporter/finishTestItemReporting.spec.ts @@ -101,8 +101,30 @@ describe('finish test reporting', () => { description: 'description', }; - // @ts-ignore - await reporter.onTestEnd({ ...testCase, outcome: () => 'expected' }, result); + await reporter.onTestEnd( + { + ...testCase, + outcome: () => 'expected', + results: [ + // @ts-ignore + { + attachments: [ + { + name: RPTestInfo.attributes, + body: Buffer.from(JSON.stringify([{ key: 'key', value: 'value' }])), + contentType: 'application/json', + }, + { + name: RPTestInfo.description, + body: Buffer.from('description'), + contentType: 'text/plain', + }, + ], + }, + ], + }, + result, + ); expect(reporter.client.finishTestItem).toHaveBeenCalledTimes(3); expect(reporter.client.finishTestItem).toHaveBeenNthCalledWith( @@ -126,7 +148,30 @@ describe('finish test reporting', () => { issue: { issueType: 'NOT_ISSUE' }, }; // @ts-ignore - await reporter.onTestEnd({ ...testCase, outcome: () => 'skipped' }, result); + await reporter.onTestEnd( + { + ...testCase, + outcome: () => 'skipped', + results: [ + // @ts-ignore + { + attachments: [ + { + name: RPTestInfo.attributes, + body: Buffer.from(JSON.stringify([{ key: 'key', value: 'value' }])), + contentType: 'application/json', + }, + { + name: RPTestInfo.description, + body: Buffer.from('description'), + contentType: 'text/plain', + }, + ], + }, + ], + }, + result, + ); expect(reporter.client.finishTestItem).toHaveBeenCalledTimes(3); expect(reporter.client.finishTestItem).toHaveBeenNthCalledWith( @@ -195,22 +240,6 @@ describe('finish test reporting', () => { test('client.finishTestItem should call reporter.client.finishTestItem with correct values', async () => { const result = { status: 'passed' }; - reporter.testItems = new Map([ - [ - `${testCase.id}`, - { - id: 'testItemId', - name: 'name', - description: 'savedTestItemDescription', - status: STATUSES.PASSED, - attributes: [ - { value: 'savedTestItemAttrValue' }, - { key: 'savedTestItemAttrKey', value: 'savedTestItemAttrValue', system: false }, - ], - }, - ], - ]); - await reporter.onTestEnd( { ...testCase, @@ -263,20 +292,18 @@ describe('finish test reporting', () => { const finishStepObject: FinishTestItemObjType = { endTime: reporter.client.helpers.now(), - status: STATUSES.PASSED, + status: STATUSES.INTERRUPTED, attributes: [ { key: 'key1', value: 'value1', system: false }, { key: 'key2', value: 'value2', system: false }, - { value: 'savedTestItemAttrValue' }, - { key: 'savedTestItemAttrKey', value: 'savedTestItemAttrValue', system: false }, ], - description: 'savedTestItemDescription\nDescription', + description: 'Description', testCaseId: 'testCaseId', }; expect(reporter.client.finishTestItem).toHaveBeenNthCalledWith( 1, - 'testItemId', + 'tempTestItemId', finishStepObject, ); }); diff --git a/src/__tests__/utils.spec.ts b/src/__tests__/utils.spec.ts index 95b5f8e..c3f4036 100644 --- a/src/__tests__/utils.spec.ts +++ b/src/__tests__/utils.spec.ts @@ -28,7 +28,6 @@ import { isErrorLog, calculateRpStatus, getAdditionalInfo, - getDescription, } from '../utils'; import fs from 'fs'; import path from 'path'; @@ -531,28 +530,4 @@ describe('testing utils', () => { expect(additionalInfo).toEqual(expectedResult); }); }); - - describe('getDescription', () => { - test('It should return one description joined by \n', () => { - const description1 = 'description1'; - const description2 = 'description2'; - - const expectedResult = 'description1\ndescription2'; - - const description = getDescription(description1, description2); - - expect(expectedResult).toBe(description); - }); - - test('It should return one description joined by \n in case when empty string was provided', () => { - const description1 = 'description1'; - const description2 = ''; - - const expectedResult = 'description1'; - - const description = getDescription(description1, description2); - - expect(expectedResult).toBe(description); - }); - }); }); diff --git a/src/index.ts b/src/index.ts index 25da34e..8e49b49 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ import { RPReporter } from './reporter'; export { ReportingApi } from './reportingApi'; export { LOG_LEVELS, STATUSES } from './constants'; -export { RPTestInfo } from './constants'; +export { ReportPortalConfig } from './models'; export default RPReporter; diff --git a/src/promises/index.ts b/src/promises/index.ts new file mode 100644 index 0000000..206037a --- /dev/null +++ b/src/promises/index.ts @@ -0,0 +1 @@ +export { ReportingApi } from './reportingApi'; diff --git a/src/promises/reportingApi.ts b/src/promises/reportingApi.ts new file mode 100644 index 0000000..9a0608d --- /dev/null +++ b/src/promises/reportingApi.ts @@ -0,0 +1,212 @@ +/* + * 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 { EVENTS } from '@reportportal/client-javascript/lib/constants/events'; +import { sendEventToReporter } from '../utils'; +import { Attribute } from '../models'; +import { STATUSES, LOG_LEVELS, RPTestInfo } from '../constants'; +import { Attachment } from '../models/reporting'; +import { test } from '@playwright/test'; + +export const ReportingApi = { + addAttributes: (attrs: Attribute[], suite?: string): Promise => { + if (suite) { + sendEventToReporter(EVENTS.ADD_ATTRIBUTES, attrs, suite); + + return Promise.resolve(); + } + + return test.info().attach(RPTestInfo.attributes, { + body: JSON.stringify(attrs), + contentType: 'application/json', + }); + }, + + setDescription: (description: string, suite?: string): Promise => { + if (suite) { + sendEventToReporter(EVENTS.SET_DESCRIPTION, description, suite); + + return Promise.resolve(); + } + + return test.info().attach(RPTestInfo.description, { + body: description, + contentType: 'text/plain', + }); + }, + setTestCaseId: (testCaseId: string, suite?: string): Promise => { + if (suite) { + sendEventToReporter(EVENTS.SET_TEST_CASE_ID, testCaseId, suite); + + return Promise.resolve(); + } + + return test.info().attach(RPTestInfo.testCaseId, { + body: testCaseId, + contentType: 'text/plain', + }); + }, + setStatus: (status: keyof typeof STATUSES, suite?: string): Promise => { + if (suite) { + sendEventToReporter(EVENTS.SET_STATUS, status, suite); + + return Promise.resolve(); + } + + return test.info().attach(RPTestInfo.status, { + body: status, + contentType: 'text/plain', + }); + }, + setStatusPassed: (suite?: string): Promise => { + if (suite) { + sendEventToReporter(EVENTS.SET_STATUS, STATUSES.PASSED, suite); + + return Promise.resolve(); + } + + return test.info().attach(RPTestInfo.status, { + body: STATUSES.PASSED, + contentType: 'text/plain', + }); + }, + setStatusFailed: (suite?: string): Promise => { + if (suite) { + sendEventToReporter(EVENTS.SET_STATUS, STATUSES.FAILED, suite); + + return Promise.resolve(); + } + + return test + .info() + .attach(RPTestInfo.status, { body: STATUSES.FAILED, contentType: 'text/plain' }); + }, + setStatusSkipped: (suite?: string): Promise => { + if (suite) { + sendEventToReporter(EVENTS.SET_STATUS, STATUSES.SKIPPED, suite); + + return Promise.resolve(); + } + + return test + .info() + .attach(RPTestInfo.status, { body: STATUSES.SKIPPED, contentType: 'text/plain' }); + }, + setStatusStopped: (suite?: string): Promise => { + if (suite) { + sendEventToReporter(EVENTS.SET_STATUS, STATUSES.STOPPED, suite); + + return Promise.resolve(); + } + + return test + .info() + .attach(RPTestInfo.status, { body: STATUSES.STOPPED, contentType: 'text/plain' }); + }, + setStatusInterrupted: (suite?: string): Promise => { + if (suite) { + sendEventToReporter(EVENTS.SET_STATUS, STATUSES.INTERRUPTED, suite); + + return Promise.resolve(); + } + + return test + .info() + .attach(RPTestInfo.status, { body: STATUSES.INTERRUPTED, contentType: 'text/plain' }); + }, + setStatusCancelled: (suite?: string): Promise => { + if (suite) { + sendEventToReporter(EVENTS.SET_STATUS, STATUSES.CANCELLED, suite); + + return Promise.resolve(); + } + + return test + .info() + .attach(RPTestInfo.status, { body: STATUSES.CANCELLED, contentType: 'text/plain' }); + }, + setStatusInfo: (suite?: string): Promise => { + if (suite) { + sendEventToReporter(EVENTS.SET_STATUS, STATUSES.INFO, suite); + + return Promise.resolve(); + } + + return test + .info() + .attach(RPTestInfo.status, { body: STATUSES.INFO, contentType: 'text/plain' }); + }, + setStatusWarn: (suite?: string): Promise => { + if (suite) { + sendEventToReporter(EVENTS.SET_STATUS, STATUSES.WARN, suite); + + return Promise.resolve(); + } + + return test + .info() + .attach(RPTestInfo.status, { body: STATUSES.WARN, contentType: 'text/plain' }); + }, + + setLaunchStatus: (status: keyof typeof STATUSES): void => + sendEventToReporter(EVENTS.SET_LAUNCH_STATUS, status), + setLaunchStatusPassed: (): void => sendEventToReporter(EVENTS.SET_LAUNCH_STATUS, STATUSES.PASSED), + setLaunchStatusFailed: (): void => sendEventToReporter(EVENTS.SET_LAUNCH_STATUS, STATUSES.FAILED), + setLaunchStatusSkipped: (): void => + sendEventToReporter(EVENTS.SET_LAUNCH_STATUS, STATUSES.SKIPPED), + setLaunchStatusStopped: (): void => + sendEventToReporter(EVENTS.SET_LAUNCH_STATUS, STATUSES.STOPPED), + setLaunchStatusInterrupted: (): void => + sendEventToReporter(EVENTS.SET_LAUNCH_STATUS, STATUSES.INTERRUPTED), + setLaunchStatusCancelled: (): void => + sendEventToReporter(EVENTS.SET_LAUNCH_STATUS, STATUSES.CANCELLED), + setLaunchStatusInfo: (): void => sendEventToReporter(EVENTS.SET_LAUNCH_STATUS, STATUSES.INFO), + setLaunchStatusWarn: (): void => sendEventToReporter(EVENTS.SET_LAUNCH_STATUS, STATUSES.WARN), + + log: ( + level: LOG_LEVELS = LOG_LEVELS.INFO, + message = '', + file?: Attachment, + suite?: string, + ): void => sendEventToReporter(EVENTS.ADD_LOG, { level, message, file }, suite), + launchLog: (level: LOG_LEVELS = LOG_LEVELS.INFO, message = '', file?: Attachment): void => + sendEventToReporter(EVENTS.ADD_LAUNCH_LOG, { level, message, file }), + trace: (message: string, file?: Attachment, suite?: string): void => + ReportingApi.log(LOG_LEVELS.TRACE, message, file, suite), + debug: (message: string, file?: Attachment, suite?: string): void => + ReportingApi.log(LOG_LEVELS.DEBUG, message, file, suite), + info: (message: string, file?: Attachment, suite?: string): void => + ReportingApi.log(LOG_LEVELS.INFO, message, file, suite), + warn: (message: string, file?: Attachment, suite?: string): void => + ReportingApi.log(LOG_LEVELS.WARN, message, file, suite), + error: (message: string, file?: Attachment, suite?: string): void => + ReportingApi.log(LOG_LEVELS.ERROR, message, file, suite), + fatal: (message: string, file?: Attachment, suite?: string): void => + ReportingApi.log(LOG_LEVELS.FATAL, message, file, suite), + launchTrace: (message: string, file?: Attachment): void => + ReportingApi.launchLog(LOG_LEVELS.TRACE, message, file), + launchDebug: (message: string, file?: Attachment): void => + ReportingApi.launchLog(LOG_LEVELS.DEBUG, message, file), + launchInfo: (message: string, file?: Attachment): void => + ReportingApi.launchLog(LOG_LEVELS.INFO, message, file), + launchWarn: (message: string, file?: Attachment): void => + ReportingApi.launchLog(LOG_LEVELS.WARN, message, file), + launchError: (message: string, file?: Attachment): void => + ReportingApi.launchLog(LOG_LEVELS.ERROR, message, file), + launchFatal: (message: string, file?: Attachment): void => + ReportingApi.launchLog(LOG_LEVELS.FATAL, message, file), +}; diff --git a/src/reporter.ts b/src/reporter.ts index cb26f19..19b4bd0 100644 --- a/src/reporter.ts +++ b/src/reporter.ts @@ -41,7 +41,6 @@ import { getAgentInfo, getAttachments, getCodeRef, - getDescription, getSystemAttributes, isErrorLog, isFalse, @@ -346,8 +345,6 @@ export class RPReporter implements Reporter { ...(status && { status }), ...(logs && { logs }), // TODO: may be send it on suite start }); - - this.suitesInfo.delete(currentSuiteTitle); } return projectName; @@ -455,22 +452,20 @@ export class RPReporter implements Reporter { return Promise.resolve(); } - const additionalInfo = getAdditionalInfo(test); - const { - id: testItemId, attributes, description, - testCaseId, status: predefinedStatus, - } = savedTestItem; + testCaseId, + } = getAdditionalInfo(test); + + const { id: testItemId } = savedTestItem; let withoutIssue; - let testDescription = getDescription(description, additionalInfo.description); + let testDescription = description; const calculatedStatus = calculateRpStatus(test.outcome(), result.status, test.annotations); - const status = predefinedStatus || additionalInfo.status || calculatedStatus; - const calculatedTestCaseId = testCaseId || additionalInfo.testCaseId; - const mergedAttributes = additionalInfo.attributes.concat(attributes ?? []); + const status = predefinedStatus || calculatedStatus; + if (status === STATUSES.SKIPPED) { withoutIssue = isFalse(this.config.skippedIssue); } @@ -497,9 +492,7 @@ export class RPReporter implements Reporter { level: LOG_LEVELS.ERROR, message: stacktrace, }); - testDescription = getDescription(description, additionalInfo.description).concat( - `\n\`\`\`error\n${stacktrace}\n\`\`\``, - ); + testDescription = description.concat(`\n\`\`\`error\n${stacktrace}\n\`\`\``); } [...this.nestedSteps.entries()].forEach(([key, value]) => { @@ -521,9 +514,9 @@ export class RPReporter implements Reporter { endTime: this.client.helpers.now(), status, ...(withoutIssue && { issue: { issueType: 'NOT_ISSUE' } }), - ...(mergedAttributes.length !== 0 && { attributes: mergedAttributes }), + ...(attributes.length !== 0 && { attributes }), ...(testDescription && { description: testDescription }), - ...(calculatedTestCaseId && { testCaseId: calculatedTestCaseId }), + ...(testCaseId && { testCaseId }), }; const { promise } = this.client.finishTestItem(testItemId, finishTestItemObj); diff --git a/src/reportingApi.ts b/src/reportingApi.ts index e36ccd9..32d6cf1 100644 --- a/src/reportingApi.ts +++ b/src/reportingApi.ts @@ -21,31 +21,106 @@ import { Attribute } from './models'; import { STATUSES, LOG_LEVELS } from './constants'; import { Attachment } from './models/reporting'; +const getDepreciationMessage = (methodName: string): string => + `This method is deprecated. Use ${methodName} from @reportportal/agent-js-playwright/promises instead`; + export const ReportingApi = { - addAttributes: (attrs: Attribute[], suite?: string): void => - sendEventToReporter(EVENTS.ADD_ATTRIBUTES, attrs, suite), - setDescription: (description: string, suite?: string): void => - sendEventToReporter(EVENTS.SET_DESCRIPTION, description, suite), - setTestCaseId: (testCaseId: string, suite?: string): void => - sendEventToReporter(EVENTS.SET_TEST_CASE_ID, testCaseId, suite), - setStatus: (status: keyof typeof STATUSES, suite?: string): void => - sendEventToReporter(EVENTS.SET_STATUS, status, suite), - setStatusPassed: (suite?: string): void => - sendEventToReporter(EVENTS.SET_STATUS, STATUSES.PASSED, suite), - setStatusFailed: (suite?: string): void => - sendEventToReporter(EVENTS.SET_STATUS, STATUSES.FAILED, suite), - setStatusSkipped: (suite?: string): void => - sendEventToReporter(EVENTS.SET_STATUS, STATUSES.SKIPPED, suite), - setStatusStopped: (suite?: string): void => - sendEventToReporter(EVENTS.SET_STATUS, STATUSES.STOPPED, suite), - setStatusInterrupted: (suite?: string): void => - sendEventToReporter(EVENTS.SET_STATUS, STATUSES.INTERRUPTED, suite), - setStatusCancelled: (suite?: string): void => - sendEventToReporter(EVENTS.SET_STATUS, STATUSES.CANCELLED, suite), - setStatusInfo: (suite?: string): void => - sendEventToReporter(EVENTS.SET_STATUS, STATUSES.INFO, suite), - setStatusWarn: (suite?: string): void => - sendEventToReporter(EVENTS.SET_STATUS, STATUSES.WARN, suite), + /** + * @deprecated Use `addAttributes` from `@reportportal/agent-js-playwright/promises` instead + */ + addAttributes: (attrs: Attribute[], suite?: string): void => { + console.warn(getDepreciationMessage('addAttributes')); + sendEventToReporter(EVENTS.ADD_ATTRIBUTES, attrs, suite); + }, + + /** + * @deprecated Use `setDescription` from `@reportportal/agent-js-playwright/promises` instead + */ + setDescription: (description: string, suite?: string): void => { + console.warn(getDepreciationMessage('setDescription')); + sendEventToReporter(EVENTS.SET_DESCRIPTION, description, suite); + }, + + /** + * @deprecated Use `setTestCaseId` from `@reportportal/agent-js-playwright/promises` instead + */ + setTestCaseId: (testCaseId: string, suite?: string): void => { + console.warn(getDepreciationMessage('setTestCaseId')); + sendEventToReporter(EVENTS.SET_TEST_CASE_ID, testCaseId, suite); + }, + + /** + * @deprecated Use `setStatus` from `@reportportal/agent-js-playwright/promises` instead + */ + setStatus: (status: keyof typeof STATUSES, suite?: string): void => { + console.warn(getDepreciationMessage('setStatus')); + sendEventToReporter(EVENTS.SET_STATUS, status, suite); + }, + + /** + * @deprecated Use `setStatusPassed` from `@reportportal/agent-js-playwright/promises` instead + */ + setStatusPassed: (suite?: string): void => { + console.warn(getDepreciationMessage('setStatusPassed')); + sendEventToReporter(EVENTS.SET_STATUS, STATUSES.PASSED, suite); + }, + + /** + * @deprecated Use `setStatusFailed` from `@reportportal/agent-js-playwright/promises` instead + */ + setStatusFailed: (suite?: string): void => { + console.warn(getDepreciationMessage('setStatusPassed')); + sendEventToReporter(EVENTS.SET_STATUS, STATUSES.FAILED, suite); + }, + + /** + * @deprecated Use `setStatusSkipped` from `@reportportal/agent-js-playwright/promises` instead + */ + setStatusSkipped: (suite?: string): void => { + console.warn(getDepreciationMessage('setStatusSkipped')); + sendEventToReporter(EVENTS.SET_STATUS, STATUSES.SKIPPED, suite); + }, + + /** + * @deprecated Use `setStatusStopped` from `@reportportal/agent-js-playwright/promises` instead + */ + setStatusStopped: (suite?: string): void => { + console.warn(getDepreciationMessage('setStatusStopped')); + sendEventToReporter(EVENTS.SET_STATUS, STATUSES.STOPPED, suite); + }, + + /** + * @deprecated Use `setStatusInterrupted` from `@reportportal/agent-js-playwright/promises` instead + */ + setStatusInterrupted: (suite?: string): void => { + console.warn(getDepreciationMessage('setStatusInterrupted')); + sendEventToReporter(EVENTS.SET_STATUS, STATUSES.INTERRUPTED, suite); + }, + + /** + * @deprecated Use `setStatusCancelled` from `@reportportal/agent-js-playwright/promises` instead + */ + setStatusCancelled: (suite?: string): void => { + console.warn(getDepreciationMessage('setStatusCancelled')); + sendEventToReporter(EVENTS.SET_STATUS, STATUSES.CANCELLED, suite); + }, + + /** + * @deprecated Use `setStatusInfo` from `@reportportal/agent-js-playwright/promises` instead + */ + setStatusInfo: (suite?: string): void => { + console.warn(getDepreciationMessage('setStatusInfo')); + sendEventToReporter(EVENTS.SET_STATUS, STATUSES.INFO, suite); + }, + + /** + * @deprecated Use `setStatusWarn` from `@reportportal/agent-js-playwright/promises` instead + */ + setStatusWarn: (suite?: string): void => { + console.warn(getDepreciationMessage('setStatusWarn')); + sendEventToReporter(EVENTS.SET_STATUS, STATUSES.WARN, suite); + }, + setLaunchStatus: (status: keyof typeof STATUSES): void => sendEventToReporter(EVENTS.SET_LAUNCH_STATUS, status), setLaunchStatusPassed: (): void => sendEventToReporter(EVENTS.SET_LAUNCH_STATUS, STATUSES.PASSED), diff --git a/src/utils.ts b/src/utils.ts index 68eb07e..ede7415 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -209,6 +209,3 @@ export const getAdditionalInfo = (test: TestCase): TestAdditionalInfo => { initialValue, ); }; - -export const getDescription = (...descriptions: string[]): string => - descriptions.filter(Boolean).join('\n'); From 69829ef682a433e65abae581f7d26391b68efd87 Mon Sep 17 00:00:00 2001 From: Dzmitry Kosarau Date: Sun, 15 Oct 2023 12:33:44 +0500 Subject: [PATCH 4/6] EPMRPP-78272 || Increase test coverage --- src/__tests__/promises/reportingApi.spec.ts | 193 ++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 src/__tests__/promises/reportingApi.spec.ts diff --git a/src/__tests__/promises/reportingApi.spec.ts b/src/__tests__/promises/reportingApi.spec.ts new file mode 100644 index 0000000..516845d --- /dev/null +++ b/src/__tests__/promises/reportingApi.spec.ts @@ -0,0 +1,193 @@ +/* + * 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 } 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 status = 'PASSED'; + const spySendEventToReporter = jest.spyOn(utils, 'sendEventToReporter'); + ReportingApi.setStatus(status, 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); + }); + }); +}); From 29050d4f702f8ab0aae2238a0a998a3fd548a811 Mon Sep 17 00:00:00 2001 From: Dzmitry Kosarau Date: Mon, 16 Oct 2023 20:38:12 +0500 Subject: [PATCH 5/6] EPMRPP-78272 || Code review fixes - 2 - Remove unused interface - Reuse ReportingApi.setStatus method --- README.md | 2 +- src/__tests__/promises/reportingApi.spec.ts | 5 +- src/models/reporting.ts | 7 -- src/promises/reportingApi.ts | 105 +++----------------- 4 files changed, 18 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index 1750852..a23ebdf 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,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'` or `'@reportportal/agent-js-playwright/promises'`: +To start using the `ReportingApi` in tests, just import it from `'@reportportal/agent-js-playwright/promises'`: ```javascript import { ReportingApi } from '@reportportal/agent-js-playwright'; // or diff --git a/src/__tests__/promises/reportingApi.spec.ts b/src/__tests__/promises/reportingApi.spec.ts index 516845d..b2b44db 100644 --- a/src/__tests__/promises/reportingApi.spec.ts +++ b/src/__tests__/promises/reportingApi.spec.ts @@ -17,7 +17,7 @@ import { ReportingApi } from '../../promises'; import * as utils from '../../utils'; -import { LOG_LEVELS } from '../../constants'; +import { LOG_LEVELS, STATUSES } from '../../constants'; const reportingApiStatusMethods = [ { method: 'setStatusPassed', status: 'passed' }, @@ -110,9 +110,8 @@ describe('reportingApi', () => { test('ReportingApi.setStatus should call sendEventToReporter with provided status', () => { const suite = 'suite'; - const status = 'PASSED'; const spySendEventToReporter = jest.spyOn(utils, 'sendEventToReporter'); - ReportingApi.setStatus(status, suite); + ReportingApi.setStatus(STATUSES.PASSED, suite); expect(spySendEventToReporter).toHaveBeenCalledTimes(1); }); diff --git a/src/models/reporting.ts b/src/models/reporting.ts index 572d05b..3a60729 100644 --- a/src/models/reporting.ts +++ b/src/models/reporting.ts @@ -67,13 +67,6 @@ export interface TestStepWithId extends TestStep { id: string; } -export interface TestInfoAttachment { - name: string; - contentType?: string; - body?: string | Buffer; - path?: string; -} - export interface TestAdditionalInfo { status?: string; attributes?: Attribute[]; diff --git a/src/promises/reportingApi.ts b/src/promises/reportingApi.ts index 9a0608d..0df6b5f 100644 --- a/src/promises/reportingApi.ts +++ b/src/promises/reportingApi.ts @@ -60,7 +60,7 @@ export const ReportingApi = { contentType: 'text/plain', }); }, - setStatus: (status: keyof typeof STATUSES, suite?: string): Promise => { + setStatus: (status: STATUSES, suite?: string): Promise => { if (suite) { sendEventToReporter(EVENTS.SET_STATUS, status, suite); @@ -72,95 +72,20 @@ export const ReportingApi = { contentType: 'text/plain', }); }, - setStatusPassed: (suite?: string): Promise => { - if (suite) { - sendEventToReporter(EVENTS.SET_STATUS, STATUSES.PASSED, suite); - - return Promise.resolve(); - } - - return test.info().attach(RPTestInfo.status, { - body: STATUSES.PASSED, - contentType: 'text/plain', - }); - }, - setStatusFailed: (suite?: string): Promise => { - if (suite) { - sendEventToReporter(EVENTS.SET_STATUS, STATUSES.FAILED, suite); - - return Promise.resolve(); - } - - return test - .info() - .attach(RPTestInfo.status, { body: STATUSES.FAILED, contentType: 'text/plain' }); - }, - setStatusSkipped: (suite?: string): Promise => { - if (suite) { - sendEventToReporter(EVENTS.SET_STATUS, STATUSES.SKIPPED, suite); - - return Promise.resolve(); - } - - return test - .info() - .attach(RPTestInfo.status, { body: STATUSES.SKIPPED, contentType: 'text/plain' }); - }, - setStatusStopped: (suite?: string): Promise => { - if (suite) { - sendEventToReporter(EVENTS.SET_STATUS, STATUSES.STOPPED, suite); - - return Promise.resolve(); - } - - return test - .info() - .attach(RPTestInfo.status, { body: STATUSES.STOPPED, contentType: 'text/plain' }); - }, - setStatusInterrupted: (suite?: string): Promise => { - if (suite) { - sendEventToReporter(EVENTS.SET_STATUS, STATUSES.INTERRUPTED, suite); - - return Promise.resolve(); - } - - return test - .info() - .attach(RPTestInfo.status, { body: STATUSES.INTERRUPTED, contentType: 'text/plain' }); - }, - setStatusCancelled: (suite?: string): Promise => { - if (suite) { - sendEventToReporter(EVENTS.SET_STATUS, STATUSES.CANCELLED, suite); - - return Promise.resolve(); - } - - return test - .info() - .attach(RPTestInfo.status, { body: STATUSES.CANCELLED, contentType: 'text/plain' }); - }, - setStatusInfo: (suite?: string): Promise => { - if (suite) { - sendEventToReporter(EVENTS.SET_STATUS, STATUSES.INFO, suite); - - return Promise.resolve(); - } - - return test - .info() - .attach(RPTestInfo.status, { body: STATUSES.INFO, contentType: 'text/plain' }); - }, - setStatusWarn: (suite?: string): Promise => { - if (suite) { - sendEventToReporter(EVENTS.SET_STATUS, STATUSES.WARN, suite); - - return Promise.resolve(); - } - - return test - .info() - .attach(RPTestInfo.status, { body: STATUSES.WARN, contentType: 'text/plain' }); - }, + setStatusPassed: (suite?: string): Promise => + ReportingApi.setStatus(STATUSES.PASSED, suite), + setStatusFailed: (suite?: string): Promise => + ReportingApi.setStatus(STATUSES.FAILED, suite), + setStatusSkipped: (suite?: string): Promise => + ReportingApi.setStatus(STATUSES.SKIPPED, suite), + setStatusStopped: (suite?: string): Promise => + ReportingApi.setStatus(STATUSES.STOPPED, suite), + setStatusInterrupted: (suite?: string): Promise => + ReportingApi.setStatus(STATUSES.INTERRUPTED, suite), + setStatusCancelled: (suite?: string): Promise => + ReportingApi.setStatus(STATUSES.CANCELLED, suite), + setStatusInfo: (suite?: string): Promise => ReportingApi.setStatus(STATUSES.INFO, suite), + setStatusWarn: (suite?: string): Promise => ReportingApi.setStatus(STATUSES.WARN, suite), setLaunchStatus: (status: keyof typeof STATUSES): void => sendEventToReporter(EVENTS.SET_LAUNCH_STATUS, status), From ee554d70fb94b7cc59017f80f35902c88ddb7c68 Mon Sep 17 00:00:00 2001 From: Dzmitry Kosarau Date: Tue, 17 Oct 2023 12:07:04 +0500 Subject: [PATCH 6/6] EPMRPP-78272 || Code review fixes - 3 --- README.md | 70 ++----------------- .../reporter/finishTestItemReporting.spec.ts | 28 ++++---- src/__tests__/utils.spec.ts | 18 ++--- src/constants/index.ts | 2 +- src/constants/testInfo.ts | 21 ++++-- src/promises/reportingApi.ts | 12 ++-- src/reporter.ts | 15 ++-- src/utils.ts | 8 ++- 8 files changed, 61 insertions(+), 113 deletions(-) diff --git a/README.md b/README.md index a23ebdf..5adf8bd 100644 --- a/README.md +++ b/README.md @@ -132,8 +132,6 @@ This reporter provides Reporting API to use it directly in tests to send some ad To start using the `ReportingApi` in tests, just import it from `'@reportportal/agent-js-playwright/promises'`: ```javascript -import { ReportingApi } from '@reportportal/agent-js-playwright'; -// or import { ReportingApi } from '@reportportal/agent-js-playwright/promises' ``` @@ -145,25 +143,6 @@ 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.
-`ReportingApi.addAttributes(attributes: Array, suite?: string);`
-**required**: `attributes`
-**optional**: `suite`
-Example: -```javascript -test('should have the correct attributes', () => { - ReportingApi.addAttributes([ - { - key: 'testKey', - value: 'testValue', - }, - { - value: 'testValueTwo', - }, - ]); - expect(true).toBe(true); -}); -``` - `ReportingApi.addAttributes(attributes: Array, suite?: string):Promise;`
**required**: `attributes`
**optional**: `suite`
@@ -187,21 +166,10 @@ test('should have the correct attributes', async () => { ##### 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'); - expect(true).toBe(true); -}); -``` - `ReportingApi.setTestCaseId(id: string, suite?: string):Promise;`
**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 import { ReportingApi } from '@reportportal/agent-js-playwright/promises' @@ -306,23 +274,12 @@ test('should contain logs with attachments', () => { ##### setStatus Assign corresponding status to the current test item. Should be called inside of corresponding test.
-`ReportingApi.setStatus(status: string, suite?: string);`
+`ReportingApi.setStatus(status: string, suite?: string):Promise;`
**required**: `status`
**optional**: `suite`
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(status: string, suite?: string):Promise;`
-**required**: `status`
-**optional**: `suite`
-```javascript import { ReportingApi } from '@reportportal/agent-js-playwright/promises' test('should have status FAILED', async () => { @@ -334,25 +291,6 @@ test('should have status FAILED', async () => { ##### 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);`
-`ReportingApi.setStatusSkipped(suite?: string);`
-`ReportingApi.setStatusStopped(suite?: string);`
-`ReportingApi.setStatusInterrupted(suite?: string);`
-`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(suite?: string):Promise;`
`ReportingApi.setStatusPassed(suite?: string):Promise;`
`ReportingApi.setStatusSkipped(suite?: string):Promise;`
@@ -366,12 +304,12 @@ import { ReportingApi } from '@reportportal/agent-js-playwright/promises' test('should call ReportingApi to set statuses', async () => { await Promise.all[ - ReportingAPI.setStatusFailed(); + ReportingAPI.setStatusFailed(), ReportingAPI.setStatusPassed(), ReportingAPI.setStatusSkipped(), ReportingAPI.setStatusStopped(), ReportingAPI.setStatusInterrupted(), - ReportingAPI.setStatusCancelled() + ReportingAPI.setStatusCancelled(), ] }); ``` diff --git a/src/__tests__/reporter/finishTestItemReporting.spec.ts b/src/__tests__/reporter/finishTestItemReporting.spec.ts index 2e1da07..1eb821f 100644 --- a/src/__tests__/reporter/finishTestItemReporting.spec.ts +++ b/src/__tests__/reporter/finishTestItemReporting.spec.ts @@ -14,11 +14,12 @@ * limitations under the License. */ +import { EVENTS } from '@reportportal/client-javascript/lib/constants/events'; import { RPReporter } from '../../reporter'; import { mockConfig } from '../mocks/configMock'; import { RPClientMock } from '../mocks/RPClientMock'; import { FinishTestItemObjType } from '../../models'; -import { RPTestInfo, STATUSES } from '../../constants'; +import { STATUSES } from '../../constants'; const rootSuite = 'rootSuite'; const suiteName = 'suiteName'; @@ -110,12 +111,12 @@ describe('finish test reporting', () => { { attachments: [ { - name: RPTestInfo.attributes, + name: EVENTS.ADD_ATTRIBUTES, body: Buffer.from(JSON.stringify([{ key: 'key', value: 'value' }])), contentType: 'application/json', }, { - name: RPTestInfo.description, + name: EVENTS.SET_DESCRIPTION, body: Buffer.from('description'), contentType: 'text/plain', }, @@ -157,12 +158,12 @@ describe('finish test reporting', () => { { attachments: [ { - name: RPTestInfo.attributes, + name: EVENTS.ADD_ATTRIBUTES, body: Buffer.from(JSON.stringify([{ key: 'key', value: 'value' }])), contentType: 'application/json', }, { - name: RPTestInfo.description, + name: EVENTS.SET_DESCRIPTION, body: Buffer.from('description'), contentType: 'text/plain', }, @@ -249,7 +250,7 @@ describe('finish test reporting', () => { { attachments: [ { - name: RPTestInfo.attributes, + name: EVENTS.ADD_ATTRIBUTES, contentType: 'application/json', body: Buffer.from( JSON.stringify([ @@ -259,22 +260,22 @@ describe('finish test reporting', () => { ), }, { - name: RPTestInfo.description, + name: EVENTS.SET_DESCRIPTION, contentType: 'plain/text', body: Buffer.from('Description'), }, { - name: RPTestInfo.status, + name: EVENTS.SET_STATUS, contentType: 'plain/text', body: Buffer.from('skipped'), }, { - name: RPTestInfo.status, + name: EVENTS.SET_STATUS, contentType: 'plain/text', body: Buffer.from('interrupted'), }, { - name: RPTestInfo.testCaseId, + name: EVENTS.SET_TEST_CASE_ID, contentType: 'plain/text', body: Buffer.from('testCaseId'), }, @@ -293,11 +294,8 @@ describe('finish test reporting', () => { const finishStepObject: FinishTestItemObjType = { endTime: reporter.client.helpers.now(), status: STATUSES.INTERRUPTED, - attributes: [ - { key: 'key1', value: 'value1', system: false }, - { key: 'key2', value: 'value2', system: false }, - ], - description: 'Description', + attributes: [{ key: 'key', value: 'value' }], + description: 'description', testCaseId: 'testCaseId', }; diff --git a/src/__tests__/utils.spec.ts b/src/__tests__/utils.spec.ts index c3f4036..f416d58 100644 --- a/src/__tests__/utils.spec.ts +++ b/src/__tests__/utils.spec.ts @@ -15,6 +15,7 @@ * */ +import { EVENTS } from '@reportportal/client-javascript/lib/constants/events'; // @ts-ignore import { name as pjsonName, version as pjsonVersion } from '../../package.json'; import { @@ -36,7 +37,6 @@ import { TestOutcome, BASIC_ATTACHMENT_CONTENT_TYPES, BASIC_ATTACHMENT_NAMES, - RPTestInfo, } from '../constants'; import { TestCase } from '@playwright/test/reporter'; import { TestAdditionalInfo } from '../models/reporting'; @@ -430,7 +430,7 @@ describe('testing utils', () => { { attachments: [ { - name: RPTestInfo.attributes, + name: EVENTS.ADD_ATTRIBUTES, contentType: 'application/json', body: Buffer.from( JSON.stringify([ @@ -440,22 +440,22 @@ describe('testing utils', () => { ), }, { - name: RPTestInfo.description, + name: EVENTS.SET_DESCRIPTION, contentType: 'plain/text', body: Buffer.from('Description'), }, { - name: RPTestInfo.status, + name: EVENTS.SET_STATUS, contentType: 'plain/text', body: Buffer.from('skipped'), }, { - name: RPTestInfo.status, + name: EVENTS.SET_STATUS, contentType: 'plain/text', body: Buffer.from('failed'), }, { - name: RPTestInfo.testCaseId, + name: EVENTS.SET_TEST_CASE_ID, contentType: 'plain/text', body: Buffer.from('testCaseId'), }, @@ -490,7 +490,7 @@ describe('testing utils', () => { { attachments: [ { - name: RPTestInfo.attributes, + name: EVENTS.ADD_ATTRIBUTES, contentType: 'application/json', body: Buffer.from( `{ key: 'key1', value: 'value1', system: true }, @@ -498,12 +498,12 @@ describe('testing utils', () => { ), }, { - name: RPTestInfo.description, + name: EVENTS.SET_DESCRIPTION, contentType: 'plain/text', body: Buffer.from('Description'), }, { - name: RPTestInfo.status, + name: EVENTS.SET_STATUS, contentType: 'plain/text', body: Buffer.from('skipped'), }, diff --git a/src/constants/index.ts b/src/constants/index.ts index 3c9d315..37a8201 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -19,7 +19,7 @@ export { LAUNCH_MODES } from './launchModes'; export { TEST_ITEM_TYPES } from './testItemTypes'; export { STATUSES } from './statuses'; export { LOG_LEVELS } from './logLevels'; -export { RPTestInfo } from './testInfo'; +export { RPTestInfo, RpEventsToAdditionalInfoMap } from './testInfo'; export { TestAnnotation, TestOutcome, diff --git a/src/constants/testInfo.ts b/src/constants/testInfo.ts index ba121c7..adb3ee8 100644 --- a/src/constants/testInfo.ts +++ b/src/constants/testInfo.ts @@ -15,9 +15,18 @@ * */ -export const RPTestInfo = { - status: 'status', - attributes: 'attributes', - description: 'description', - testCaseId: 'testCaseId', -} as const; +import { EVENTS } from '@reportportal/client-javascript/lib/constants/events'; + +export enum RPTestInfo { + STATUS = 'status', + ATTRIBUTES = 'attributes', + DESCRIPTION = 'description', + TEST_CASE_ID = 'testCaseId', +} + +export const RpEventsToAdditionalInfoMap = { + [EVENTS.ADD_ATTRIBUTES]: RPTestInfo.ATTRIBUTES, + [EVENTS.SET_DESCRIPTION]: RPTestInfo.DESCRIPTION, + [EVENTS.SET_TEST_CASE_ID]: RPTestInfo.TEST_CASE_ID, + [EVENTS.SET_STATUS]: RPTestInfo.STATUS, +}; diff --git a/src/promises/reportingApi.ts b/src/promises/reportingApi.ts index 0df6b5f..c1fb134 100644 --- a/src/promises/reportingApi.ts +++ b/src/promises/reportingApi.ts @@ -1,5 +1,5 @@ /* - * Copyright 2021 EPAM Systems + * Copyright 2023 EPAM Systems * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ import { EVENTS } from '@reportportal/client-javascript/lib/constants/events'; import { sendEventToReporter } from '../utils'; import { Attribute } from '../models'; -import { STATUSES, LOG_LEVELS, RPTestInfo } from '../constants'; +import { STATUSES, LOG_LEVELS } from '../constants'; import { Attachment } from '../models/reporting'; import { test } from '@playwright/test'; @@ -30,7 +30,7 @@ export const ReportingApi = { return Promise.resolve(); } - return test.info().attach(RPTestInfo.attributes, { + return test.info().attach(EVENTS.ADD_ATTRIBUTES, { body: JSON.stringify(attrs), contentType: 'application/json', }); @@ -43,7 +43,7 @@ export const ReportingApi = { return Promise.resolve(); } - return test.info().attach(RPTestInfo.description, { + return test.info().attach(EVENTS.SET_DESCRIPTION, { body: description, contentType: 'text/plain', }); @@ -55,7 +55,7 @@ export const ReportingApi = { return Promise.resolve(); } - return test.info().attach(RPTestInfo.testCaseId, { + return test.info().attach(EVENTS.SET_TEST_CASE_ID, { body: testCaseId, contentType: 'text/plain', }); @@ -67,7 +67,7 @@ export const ReportingApi = { return Promise.resolve(); } - return test.info().attach(RPTestInfo.status, { + return test.info().attach(EVENTS.SET_STATUS, { body: status, contentType: 'text/plain', }); diff --git a/src/reporter.ts b/src/reporter.ts index 19b4bd0..0437792 100644 --- a/src/reporter.ts +++ b/src/reporter.ts @@ -452,14 +452,15 @@ export class RPReporter implements Reporter { return Promise.resolve(); } - const { - attributes, - description, - status: predefinedStatus, - testCaseId, - } = getAdditionalInfo(test); + const additionalInfo = getAdditionalInfo(test); - const { id: testItemId } = savedTestItem; + const { + id: testItemId, + attributes = additionalInfo.attributes, + description = additionalInfo.description, + testCaseId = additionalInfo.testCaseId, + status: predefinedStatus = additionalInfo.status, + } = savedTestItem; let withoutIssue; let testDescription = description; diff --git a/src/utils.ts b/src/utils.ts index ede7415..f15b648 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -15,6 +15,7 @@ * */ +import { EVENTS } from '@reportportal/client-javascript/lib/constants/events'; import { TestCase, TestStatus, TestResult } from '@playwright/test/reporter'; import fs from 'fs'; import path from 'path'; @@ -29,7 +30,7 @@ import { BASIC_ATTACHMENT_CONTENT_TYPES, TEST_ANNOTATION_TYPES, TEST_OUTCOME_TYPES, - RPTestInfo, + RpEventsToAdditionalInfoMap, } from './constants'; import { TestAdditionalInfo } from './models/reporting'; @@ -191,12 +192,13 @@ export const getAdditionalInfo = (test: TestCase): TestAdditionalInfo => { Object.assign( additionalInfo, attachments.reduce((acc, { name, body }) => { - if (name in RPTestInfo) { + if (name in RpEventsToAdditionalInfoMap) { try { const value = body.toString(); return Object.assign(acc, { - [name]: name === RPTestInfo.attributes ? JSON.parse(value) : value, + [RpEventsToAdditionalInfoMap[name]]: + name === EVENTS.ADD_ATTRIBUTES ? JSON.parse(value) : value, }); } catch (error: unknown) { console.error((error as Error).message);