Skip to content

Commit

Permalink
Merge pull request #191 from reportportal/develop
Browse files Browse the repository at this point in the history
Release 5.3.0
  • Loading branch information
AliakseiLiasnitski authored May 7, 2024
2 parents 54d02d3 + 012936c commit 401e9f3
Show file tree
Hide file tree
Showing 17 changed files with 581 additions and 44 deletions.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1 +1 @@
* @AmsterGet
* @AmsterGet @AliakseiLiasnitski
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ jobs:
echo "patch" > ${{ env.versionFragmentFileName }}
git status
git add ${{ env.versionFileName }}
git add ${{ env.versionFragmentFileName }}
git commit -m "${{ needs.calculate-version.outputs.releaseVersion }} -> ${{ steps.bumpSnapshotVersion.outputs.next-version }}-SNAPSHOT"
git push origin develop
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
### Added
- `cucumberStepStart` and `cucumberStepEnd` commands for reporting `cypress-cucumber-preprocessor` scenario steps as nested steps in RP.
### Security
- Updated versions of vulnerable packages (@reportportal/client-javascript, glob).

## [5.2.0] - 2024-03-21
### Fixed
Expand Down
66 changes: 62 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ module.exports = defineConfig({
attributes: [
{
key: 'attributeKey',
value: 'attrbiuteValue',
value: 'attributeValue',
},
{
value: 'anotherAttrbiuteValue',
value: 'anotherAttributeValue',
},
],
},
Expand Down Expand Up @@ -85,10 +85,10 @@ Add the following options to cypress.json
"attributes": [
{
"key": "attributeKey",
"value": "attrbiuteValue"
"value": "attributeValue"
},
{
"value": "anotherAttrbiuteValue"
"value": "anotherAttributeValue"
}
]
}
Expand Down Expand Up @@ -437,6 +437,64 @@ jobs:

**Note:** The example provided for Cypress version <= 9. For Cypress version >= 10 usage of `cypress-io/github-action` may be changed.

## Cypress-cucumber-preprocessor execution

### Configuration:
Specify the options in the cypress.config.js:

```javascript
const { defineConfig } = require('cypress');
const createBundler = require('@bahmutov/cypress-esbuild-preprocessor');
const preprocessor = require('@badeball/cypress-cucumber-preprocessor');
const createEsbuildPlugin = require('@badeball/cypress-cucumber-preprocessor/esbuild').default;
const registerReportPortalPlugin = require('@reportportal/agent-js-cypress/lib/plugin');

module.exports = defineConfig({
reporter: '@reportportal/agent-js-cypress',
reporterOptions: {
endpoint: 'http://your-instance.com:8080/api/v1',
apiKey: 'reportportalApiKey',
launch: 'LAUNCH_NAME',
project: 'PROJECT_NAME',
description: 'LAUNCH_DESCRIPTION',
},
e2e: {
async setupNodeEvents(on, config) {
await preprocessor.addCucumberPreprocessorPlugin(on, config);
on(
'file:preprocessor',
createBundler({
plugins: [createEsbuildPlugin(config)],
}),
);
registerReportPortalPlugin(on, config);

return config;
},
specPattern: 'cypress/e2e/**/*.feature',
supportFile: 'cypress/support/e2e.js',
},
});
```

### Scenario steps
At the moment it is not possible to subscribe to start and end of scenario steps events. To solve the problem with displaying steps in the ReportPortal, the agent provides special commands: `cucumberStepStart`, `cucumberStepEnd`.
To work correctly, these commands must be called in the `BeforeStep`/`AfterStep` hooks.

```javascript
import { BeforeStep, AfterStep } from '@badeball/cypress-cucumber-preprocessor';

BeforeStep((step) => {
cy.cucumberStepStart(step);
});

AfterStep((step) => {
cy.cucumberStepEnd(step);
});
```

You can avoid duplicating this logic in each step definitions. Instead, add it to the `cypress/support/step_definitions.js` file and include the path to this file in the [stepDefinitions](https://github.com/badeball/cypress-cucumber-preprocessor/blob/master/docs/step-definitions.md) array (if necessary) within cucumber-preprocessor config. These hooks will be used for all step definitions.

# Copyright Notice

Licensed under the [Apache License v2.0](LICENSE)
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5.2.0
5.2.1-SNAPSHOT
5 changes: 5 additions & 0 deletions lib/commands/reportPortalCommands.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ declare global {
launchError(message: string, file?: RP_FILE): Chainable<any>;

launchFatal(message: string, file?: RP_FILE): Chainable<any>;
// Waiting for migrate to TypeScript
// Expected step: IStepHookParameter (https://github.com/badeball/cypress-cucumber-preprocessor/blob/055d8df6a62009c94057b0d894a30e142cb87b94/lib/public-member-types.ts#L39)
cucumberStepStart(step: any): Chainable<any>;

cucumberStepEnd(step: any): Chainable<any>;

setStatus(status: RP_STATUS, suiteTitle?: string): Chainable<void>;

Expand Down
11 changes: 11 additions & 0 deletions lib/commands/reportPortalCommands.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,17 @@ Cypress.Commands.add('launchFatal', (message, file) => {
});
});

/**
* Cucumber Scenario's steps commands
*/
Cypress.Commands.add('cucumberStepStart', (step) => {
cy.task('rp_cucumberStepStart', step);
});

Cypress.Commands.add('cucumberStepEnd', (step) => {
cy.task('rp_cucumberStepEnd', step);
});

/**
* Attributes command
*/
Expand Down
9 changes: 9 additions & 0 deletions lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,21 @@ const reporterEvents = {
SCREENSHOT: 'screenshot',
SET_STATUS: 'setStatus',
SET_LAUNCH_STATUS: 'setLaunchStatus',
CUCUMBER_STEP_START: 'cucumberStepStart',
CUCUMBER_STEP_END: 'cucumberStepEnd',
};

const cucumberKeywordMap = {
Outcome: 'Then',
Action: 'When',
Context: 'Given',
};

module.exports = {
testItemStatuses,
logLevels,
entityType,
hookTypesMap,
cucumberKeywordMap,
reporterEvents,
};
8 changes: 8 additions & 0 deletions lib/cypressReporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ class CypressReporter extends Mocha.reporters.Base {
this.worker.send({ event: reporterEvents.SET_STATUS, statusInfo });
const setLaunchStatusListener = (statusInfo) =>
this.worker.send({ event: reporterEvents.SET_LAUNCH_STATUS, statusInfo });
const cucumberStepStartListener = (step) =>
this.worker.send({ event: reporterEvents.CUCUMBER_STEP_START, step });
const cucumberStepEndListener = (step) =>
this.worker.send({ event: reporterEvents.CUCUMBER_STEP_END, step });

startIPCServer(
(server) => {
Expand All @@ -93,6 +97,8 @@ class CypressReporter extends Mocha.reporters.Base {
server.on(IPC_EVENTS.SCREENSHOT, screenshotListener);
server.on(IPC_EVENTS.SET_STATUS, setStatusListener);
server.on(IPC_EVENTS.SET_LAUNCH_STATUS, setLaunchStatusListener);
server.on(IPC_EVENTS.CUCUMBER_STEP_START, cucumberStepStartListener);
server.on(IPC_EVENTS.CUCUMBER_STEP_END, cucumberStepEndListener);
},
(server) => {
server.off(IPC_EVENTS.CONFIG, '*');
Expand All @@ -104,6 +110,8 @@ class CypressReporter extends Mocha.reporters.Base {
server.off(IPC_EVENTS.SCREENSHOT, '*');
server.off(IPC_EVENTS.SET_STATUS, '*');
server.off(IPC_EVENTS.SET_LAUNCH_STATUS, '*');
server.off(IPC_EVENTS.CUCUMBER_STEP_START, '*');
server.off(IPC_EVENTS.CUCUMBER_STEP_END, '*');
},
);
CypressReporter.worker = this.worker;
Expand Down
2 changes: 2 additions & 0 deletions lib/ipcEvents.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const IPC_EVENTS = {
SCREENSHOT: 'screenshot',
SET_STATUS: 'setStatus',
SET_LAUNCH_STATUS: 'setLaunchStatus',
CUCUMBER_STEP_START: 'cucumberStepStart',
CUCUMBER_STEP_END: 'cucumberStepEnd',
};

module.exports = { IPC_EVENTS };
8 changes: 8 additions & 0 deletions lib/plugin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ const registerReportPortalPlugin = (on, config, callbacks) => {
ipc.of.reportportal.emit(IPC_EVENTS.SET_LAUNCH_STATUS, statusInfo);
return null;
},
rp_cucumberStepStart(step) {
ipc.of.reportportal.emit(IPC_EVENTS.CUCUMBER_STEP_START, step);
return null;
},
rp_cucumberStepEnd(step) {
ipc.of.reportportal.emit(IPC_EVENTS.CUCUMBER_STEP_END, step);
return null;
},
});

on('after:screenshot', (screenshotInfo) => {
Expand Down
103 changes: 100 additions & 3 deletions lib/reporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@

const RPClient = require('@reportportal/client-javascript');

const { entityType, logLevels, testItemStatuses } = require('./constants');
const { entityType, logLevels, testItemStatuses, cucumberKeywordMap } = require('./constants');
const {
getScreenshotAttachment,
getTestStartObject,
getTestEndObject,
getHookStartObject,
getAgentInfo,
getCodeRef,
} = require('./utils');

const { createMergeLaunchLockFile, deleteMergeLaunchLockFile } = require('./mergeLaunchesUtils');
Expand Down Expand Up @@ -55,6 +56,7 @@ class Reporter {
this.suiteTestCaseIds = new Map();
this.pendingTestsIds = [];
this.suiteStatuses = new Map();
this.cucumberSteps = new Map();
}

resetCurrentTestFinishParams() {
Expand Down Expand Up @@ -137,7 +139,12 @@ class Reporter {
);
promiseErrorHandler(promise, 'Fail to start test');
this.testItemIds.set(test.id, tempId);
this.currentTestTempInfo = { tempId, startTime: startTestObj.startTime };
this.currentTestTempInfo = {
tempId,
codeRef: test.codeRef,
startTime: startTestObj.startTime,
cucumberStepIds: new Set(),
};
if (this.pendingTestsIds.includes(test.id)) {
this.testEnd(test);
}
Expand All @@ -161,6 +168,7 @@ class Reporter {
testId = this.testItemIds.get(test.id);
}
this.sendLogOnFinishFailedItem(test, testId);
this.finishFailedStep(test);
const testInfo = Object.assign({}, test, this.currentTestFinishParams);
const finishTestItemPromise = this.client.finishTestItem(
testId,
Expand All @@ -181,6 +189,75 @@ class Reporter {
}
}

cucumberStepStart(data) {
const { testStepId, pickleStep } = data;
const parent = this.currentTestTempInfo;

if (!parent) return;

const keyword = cucumberKeywordMap[pickleStep.type];
const stepName = pickleStep.text;
const codeRef = getCodeRef([stepName], parent.codeRef);

const stepData = {
name: keyword ? `${keyword} ${stepName}` : stepName,
startTime: this.client.helpers.now(),
type: entityType.STEP,
codeRef,
hasStats: false,
};

const { tempId, promise } = this.client.startTestItem(
stepData,
this.tempLaunchId,
parent.tempId,
);
promiseErrorHandler(promise, 'Fail to start step');
this.cucumberSteps.set(testStepId, { tempId, tempParentId: parent.tempId, testStepId });
parent.cucumberStepIds.add(testStepId);
}

finishFailedStep(test) {
if (test.status === FAILED) {
const step = this.getCurrentCucumberStep();

if (!step) return;

this.cucumberStepEnd({
testStepId: step.testStepId,
testStepResult: {
status: testItemStatuses.FAILED,
message: test.err.stack,
},
});
}
}

cucumberStepEnd(data) {
const { testStepId, testStepResult = { status: testItemStatuses.PASSED } } = data;
const step = this.cucumberSteps.get(testStepId);

if (!step) return;

if (testStepResult.status === testItemStatuses.FAILED) {
this.sendLog(step.tempId, {
time: this.client.helpers.now(),
level: logLevels.ERROR,
message: testStepResult.message,
});
}

this.client.finishTestItem(step.tempId, {
status: testStepResult.status,
endTime: this.client.helpers.now(),
});

this.cucumberSteps.delete(testStepId);
if (this.currentTestTempInfo) {
this.currentTestTempInfo.cucumberStepIds.delete(testStepId);
}
}

hookStart(hook) {
const hookStartObject = getHookStartObject(hook);
switch (hookStartObject.type) {
Expand Down Expand Up @@ -227,6 +304,24 @@ class Reporter {
return currentSuiteInfo && currentSuiteInfo.tempId;
}

getCurrentCucumberStep() {
if (this.currentTestTempInfo && this.currentTestTempInfo.cucumberStepIds.size > 0) {
const testStepId = Array.from(this.currentTestTempInfo.cucumberStepIds.values())[
this.currentTestTempInfo.cucumberStepIds.size - 1
];

return this.cucumberSteps.get(testStepId);
}

return null;
}

getCurrentCucumberStepId() {
const step = this.getCurrentCucumberStep();

return step && step.tempId;
}

sendLog(tempId, { level, message = '', file }) {
return this.client.sendLog(
tempId,
Expand All @@ -241,7 +336,9 @@ class Reporter {

sendLogToCurrentItem(log) {
const tempItemId =
(this.currentTestTempInfo && this.currentTestTempInfo.tempId) || this.getCurrentSuiteId();
this.getCurrentCucumberStepId() ||
(this.currentTestTempInfo && this.currentTestTempInfo.tempId) ||
this.getCurrentSuiteId();
if (tempItemId) {
const promise = this.sendLog(tempItemId, log);
promiseErrorHandler(promise, 'Fail to send log to current item');
Expand Down
6 changes: 6 additions & 0 deletions lib/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ process.on('message', (message) => {
case reporterEvents.SET_LAUNCH_STATUS:
reporter.setLaunchStatus(message.statusInfo);
break;
case reporterEvents.CUCUMBER_STEP_START:
reporter.cucumberStepStart(message.step);
break;
case reporterEvents.CUCUMBER_STEP_END:
reporter.cucumberStepEnd(message.step);
break;
default:
break;
}
Expand Down
Loading

0 comments on commit 401e9f3

Please sign in to comment.