From 8605e62cb8e1c63c9436c53c1b782bb3030607e3 Mon Sep 17 00:00:00 2001 From: Mikhail Sidarkevich Date: Sat, 19 Mar 2022 21:53:10 +0200 Subject: [PATCH 1/2] EPMRPP-75498 || add nested steps reporting --- README.md | 42 ++++++++++++----- src/__tests__/onSuiteEnd.spec.ts | 79 ++++++++++++++++++++++++++++---- src/__tests__/utils.spec.ts | 22 ++++----- src/reporter.ts | 23 ++++++++-- src/utils.ts | 2 + 5 files changed, 132 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index ec096bb..a1716fc 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,8 @@ const config = { attributes: [{ key: 'key', value: 'value' }, { value: 'value' }], attachPicturesToLogs: false, rerun: false, - rerunOf: 'launchUuid of already existed launch', + rerunOf: 'launchUuid of already existed launch', + cucumberNestedSteps: false, skippedIssue: true, }; @@ -39,19 +40,36 @@ exports.config = { // ... }; ``` -| Parameter | Description | -| --------------------- | ----------------------------------------------------------------------------------------------------------------- | -| token | User's Report Portal token from which you want to send requests. It can be found on the profile page of this user.| -| endpoint | URL of your server. For example 'https://server:8080/api/v1'. | -| launch | Name of launch at creation. | -| project | The name of the project in which the launches will be created. | -| rerun | *Default: false.* Enable [rerun](https://github.com/reportportal/documentation/blob/master/src/md/src/DevGuides/rerun.md)| -| rerunOf | UUID of launch you want to rerun. If not specified, report portal will update the latest launch with the same name| -| mode | Launch mode. Allowable values *DEFAULT* (by default) or *DEBUG*.| -| attachPicturesToLogs | Automatically add screenshots| -| debug | This flag allows seeing the logs of the client-javascript. Useful for debugging.| +| Parameter | Description | +|----------------------|--------------------------------------------------------------------------------------------------------------------------| +| token | User's Report Portal token from which you want to send requests. It can be found on the profile page of this user. | +| endpoint | URL of your server. For example 'https://server:8080/api/v1'. | +| launch | Name of launch at creation. | +| project | The name of the project in which the launches will be created. | +| rerun | *Default: false.* Enable [rerun](https://github.com/reportportal/documentation/blob/master/src/md/src/DevGuides/rerun.md)| +| rerunOf | UUID of launch you want to rerun. If not specified, report portal will update the latest launch with the same name | +| mode | Launch mode. Allowable values *DEFAULT* (by default) or *DEBUG*. | +| attachPicturesToLogs | Automatically add screenshots | +| debug | This flag allows seeing the logs of the client-javascript. Useful for debugging. | +| cucumberNestedSteps | [Report your steps as logs](https://github.com/reportportal/agent-js-webdriverio#step-reporting-configuration) | | skippedIssue| *Default: true.* ReportPortal provides feature to mark skipped tests as not 'To Investigate' items on WS side.
Parameter could be equal boolean values:
*TRUE* - skipped tests considered as issues and will be marked as 'To Investigate' on Report Portal.
*FALSE* - skipped tests will not be marked as 'To Investigate' on application.| +## Step reporting configuration + +By default, this agent reports the following structure: + +- feature - SUITE +- scenario - TEST +- step - STEP + +You may change this behavior to report steps to the log level by enabling scenario-based reporting: + +- feature - TEST +- scenario - STEP +- step - log item + +To report your steps as logs, you need to pass an additional parameter to the agent config: `"cucumberNestedSteps": true` + ## Reporting This reporter provides Reporting API to use it directly in tests to send some additional data to the report. diff --git a/src/__tests__/onSuiteEnd.spec.ts b/src/__tests__/onSuiteEnd.spec.ts index 5c74ae1..51dd252 100644 --- a/src/__tests__/onSuiteEnd.spec.ts +++ b/src/__tests__/onSuiteEnd.spec.ts @@ -20,18 +20,81 @@ import { options } from './mocks/optionsMock'; import { RPClientMock } from './mocks/RPClientMock'; import { suiteId, suiteName } from './mocks/data'; import { getClientConfig } from '../utils'; +import { CUCUMBER_TYPE, RP_STATUSES } from '../constants'; describe('onSuiteEnd', () => { const reporter = new Reporter(options); - reporter['client'] = new RPClientMock(getClientConfig(options)); - reporter['tempLaunchId'] = 'tempLaunchId'; - reporter['storage'].addSuite({ id: suiteId, name: suiteName }); - it('client.finishTestItem should be called with corresponding params', () => { - reporter.onSuiteEnd(); + beforeEach(() => { + reporter['client'] = new RPClientMock(getClientConfig(options)); + reporter['storage'].addSuite({ id: suiteId, name: suiteName }); + }); + + describe('client.finishTestItem should be called with corresponding params', () => { + const suiteStats: any = { tests: [{ state: RP_STATUSES.PASSED }] }; + + it('test with basic config', () => { + reporter.onSuiteEnd(suiteStats); + + expect(reporter['client'].finishTestItem).toBeCalledTimes(1); + expect(reporter['client'].finishTestItem).toBeCalledWith(suiteId, {}); + expect(reporter['storage'].getCurrentSuite()).toEqual(null); + }); + + it('config with cucumberNestedSteps=true, no custom status', () => { + reporter['options'].cucumberNestedSteps = true; + + reporter.onSuiteEnd({ ...suiteStats, type: CUCUMBER_TYPE.SCENARIO }); + + expect(reporter['client'].finishTestItem).toBeCalledTimes(1); + expect(reporter['client'].finishTestItem).toBeCalledWith(suiteId, { + status: RP_STATUSES.PASSED, + }); + expect(reporter['storage'].getCurrentSuite()).toEqual(null); + }); + + it('config with cucumberNestedSteps=true, no custom status, all steps=passed', () => { + reporter['options'].cucumberNestedSteps = true; + + reporter.onSuiteEnd({ + ...suiteStats, + type: CUCUMBER_TYPE.SCENARIO, + }); + + expect(reporter['client'].finishTestItem).toBeCalledTimes(1); + expect(reporter['client'].finishTestItem).toBeCalledWith(suiteId, { + status: RP_STATUSES.PASSED, + }); + expect(reporter['storage'].getCurrentSuite()).toEqual(null); + }); + + it('config with cucumberNestedSteps=true, no custom status, some tests=failed', () => { + reporter['options'].cucumberNestedSteps = true; + + reporter.onSuiteEnd({ + ...suiteStats, + type: CUCUMBER_TYPE.SCENARIO, + tests: [...suiteStats.tests, { state: RP_STATUSES.FAILED }], + }); + + expect(reporter['client'].finishTestItem).toBeCalledTimes(1); + expect(reporter['client'].finishTestItem).toBeCalledWith(suiteId, { + status: RP_STATUSES.FAILED, + }); + expect(reporter['storage'].getCurrentSuite()).toEqual(null); + }); + + it('config with cucumberNestedSteps=true, with custom status', () => { + reporter['options'].cucumberNestedSteps = true; + reporter['storage'].addAdditionalSuiteData(suiteName, { status: RP_STATUSES.INFO }); + + reporter.onSuiteEnd(suiteStats); - expect(reporter['client'].finishTestItem).toBeCalledTimes(1); - expect(reporter['client'].finishTestItem).toBeCalledWith(suiteId, {}); - expect(reporter['storage'].getCurrentSuite()).toEqual(null); + expect(reporter['client'].finishTestItem).toBeCalledTimes(1); + expect(reporter['client'].finishTestItem).toBeCalledWith(suiteId, { + status: RP_STATUSES.INFO, + }); + expect(reporter['storage'].getCurrentSuite()).toEqual(null); + }); }); }); diff --git a/src/__tests__/utils.spec.ts b/src/__tests__/utils.spec.ts index d020e21..4dc3ebd 100644 --- a/src/__tests__/utils.spec.ts +++ b/src/__tests__/utils.spec.ts @@ -55,8 +55,7 @@ describe('utils', () => { }); it('getClientConfig with extended config', () => { - const extendedOptions = { - ...options, + const additionalOptions = { rerun: true, rerunOf: '00000000-0000-0000-0000-000000000000', skippedIssue: true, @@ -66,21 +65,18 @@ describe('utils', () => { restClientConfig: { agent: { keepAlive: true }, }, + cucumberNestedSteps: true, + }; + const extendedOptions = { + ...options, + ...additionalOptions, }; - const extendedRes = { + const expectedRes = { ...baseRes, - rerun: true, - rerunOf: '00000000-0000-0000-0000-000000000000', - skippedIssue: true, - mode: 'DEFAULT', - debug: true, - headers: { foo: 'bar' }, - restClientConfig: { - agent: { keepAlive: true }, - }, + ...additionalOptions, }; - expect(getClientConfig(extendedOptions)).toEqual(extendedRes); + expect(getClientConfig(extendedOptions)).toEqual(expectedRes); }); }); diff --git a/src/reporter.ts b/src/reporter.ts index 9893114..777065d 100644 --- a/src/reporter.ts +++ b/src/reporter.ts @@ -105,6 +105,9 @@ export class Reporter extends WDIOReporter { if (isCucumberFeature && suiteStats.description) { suiteDataRQ.description = suiteStats.description; } + if (this.options.cucumberNestedSteps) { + suiteDataRQ.type = isCucumberFeature ? TYPES.TEST : TYPES.STEP; + } const { tempId, promise } = this.client.startTestItem(suiteDataRQ, this.tempLaunchId, parentId); promiseErrorHandler(promise); const additionalData = this.storage.getAdditionalSuiteData(name); @@ -123,6 +126,7 @@ export class Reporter extends WDIOReporter { name, type: TYPES.STEP, codeRef, + ...(this.options.cucumberNestedSteps && { hasStats: false }), }; const { tempId, promise } = this.client.startTestItem( testItemDataRQ, @@ -181,10 +185,23 @@ export class Reporter extends WDIOReporter { this.storage.removeTest(id); } - onSuiteEnd(): void { + onSuiteEnd(suiteStats: SuiteStats): void { const { id, name } = this.storage.getCurrentSuite(); - const { status, attributes, description, testCaseId } = - this.storage.getAdditionalSuiteData(name); + const { + status: customStatus, + attributes, + description, + testCaseId, + } = this.storage.getAdditionalSuiteData(name); + let status = customStatus; + if (this.options.cucumberNestedSteps && suiteStats.type === CUCUMBER_TYPE.SCENARIO) { + const isAllStepsPassed = suiteStats.tests.every((test) => test.state === RP_STATUSES.PASSED); + status = customStatus + ? customStatus + : isAllStepsPassed + ? RP_STATUSES.PASSED + : RP_STATUSES.FAILED; + } const finishTestItemData = { ...(status && { status }), ...(attributes && { attributes }), diff --git a/src/utils.ts b/src/utils.ts index 0bfdc65..72b8ca8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -43,6 +43,7 @@ export const getClientConfig = (options: Partial): Config => debug, headers, restClientConfig, + cucumberNestedSteps, } = options; return { token, @@ -58,6 +59,7 @@ export const getClientConfig = (options: Partial): Config => ...(debug && { debug }), ...(headers && { headers }), ...(restClientConfig && { restClientConfig }), + ...(cucumberNestedSteps && { cucumberNestedSteps }), }; }; From 1ee99a6656c051ff05b97b813eb1c939d714121d Mon Sep 17 00:00:00 2001 From: Mikhail Sidarkevich Date: Mon, 21 Mar 2022 12:06:54 +0200 Subject: [PATCH 2/2] EPMRPP-75498 || code review fixes - 1 --- src/reporter.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/reporter.ts b/src/reporter.ts index 777065d..2c1da1b 100644 --- a/src/reporter.ts +++ b/src/reporter.ts @@ -196,11 +196,7 @@ export class Reporter extends WDIOReporter { let status = customStatus; if (this.options.cucumberNestedSteps && suiteStats.type === CUCUMBER_TYPE.SCENARIO) { const isAllStepsPassed = suiteStats.tests.every((test) => test.state === RP_STATUSES.PASSED); - status = customStatus - ? customStatus - : isAllStepsPassed - ? RP_STATUSES.PASSED - : RP_STATUSES.FAILED; + status = customStatus || (isAllStepsPassed ? RP_STATUSES.PASSED : RP_STATUSES.FAILED); } const finishTestItemData = { ...(status && { status }),