Skip to content

Commit

Permalink
EPMRPP-89455 || Add dynamic data support for scenarios (#166)
Browse files Browse the repository at this point in the history
  • Loading branch information
AliakseiLiasnitski authored May 30, 2024
1 parent f5b2af5 commit 44deb8c
Show file tree
Hide file tree
Showing 9 changed files with 478 additions and 95 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
### Added
- New API methods for scenario
### Fixed
- Reporting of feature (suite) for parallel execution [#142](https://github.com/reportportal/agent-js-cucumber/issues/142).

Expand Down
67 changes: 61 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,20 +168,23 @@ this.attach(
```
To send attachment to the launch just specify `entity: 'launch'` property.
Also `this.screenshot` and `this.launchScreenshot` methods can be used to take screenshots.
Also `this.screenshot`, `this.scenarioScreenshot` and `this.launchScreenshot` methods can be used to take screenshots.
```javascript
Then(/^I should see my new task in the list$/, function(callback) {
this.screenshot('This screenshot')
.then(() => callback())
.catch((err) => callback(err));
this.scenarioScreenshot('This is screenshot for scenario')
.then(() => callback())
.catch((err) => callback(err));
this.launchScreenshot('This is screenshot for launch')
.then(() => callback())
.catch((err) => callback(err));
});
```
`screenshot`/`launchScreenshot` function return promise fulfilled after `screenshot` is taken and image added to attachments.
`screenshot`/`scenarioScreenshot`/`launchScreenshot` function return promise fulfilled after `screenshot` is taken and image added to attachments.
Handler will parse attachments and send corresponding log to the step item.
### Logs
Expand All @@ -199,6 +202,19 @@ Then(/^I should see my new task in the list$/, function() {
});
```
To report logs to the **scenario** you can use the next methods:
```javascript
Then(/^I should see my new task in the list$/, function() {
this.scenarioInfo('This is Info Level log');
this.scenarioDebug('This is Debug Level log');
this.scenarioError('This is Error Level log');
this.scenarioWarn('This is Warn Level log');
this.scenarioTrace('This is Trace Level log');
this.scenarioFatal('This is Fatal Level log');
});
```
To report logs to the **launch** you can use the next methods:
```javascript
Expand All @@ -216,40 +232,64 @@ Then(/^I should see my new task in the list$/, function() {
Attributes for features and scenarios are parsed from @tags as `@key:value` pair.
To add attributes to the step items you can use the next method:
To add attributes to the **step items** you can use the next method:
```javascript
Then(/^I should see my new task in the list$/, function() {
this.addAttributes([{ key: 'agent', value: 'cucumber' }]);
});
```
To add attributes to the **scenario** you can use the next method:
```javascript
Then(/^I should see my new task in the list$/, function() {
this.addScenarioAttributes([{ key: 'agent', value: 'cucumber' }]);
});
```
The attributes will be concatenated.
### Description
Description for features and scenarios are parsed from their definition.
To add description to the items you can use the following method:
To add description to the **items** you can use the following method:
```javascript
Then(/^I should see my new task in the list$/, function() {
this.addDescription('Test item description.');
});
```
To add description to the **scenario** you can use the following method:
```javascript
Then(/^I should see my new task in the list$/, function() {
this.addScenarioDescription('Scenario description.');
});
```
The description will be concatenated.
### TestCaseId
To set test case id to the items you can use the following method:
To set test case id to the **items** you can use the following method:
```javascript
Then(/^I should see my new task in the list$/, function() {
this.setTestCaseId('itemTestCaseId');
});
```
To set test case id to the **scenario** you can use the following method:
```javascript
Then(/^I should see my new task in the list$/, function() {
this.setScenarioTestCaseId('scenarioTestCaseId');
});
```
### Statuses
The user can set the status of the item/launch directly depending on some conditions or behavior.
Expand All @@ -268,7 +308,22 @@ Then(/^I should see my new task in the list$/, function() {
});
```
To set status to the **item** you can use the next methods:
To set status to the **scenario** you can use the next methods:
```javascript
Then(/^I should see my new task in the list$/, function() {
this.setScenarioStatusPassed();
this.setScenarioStatusFailed();
this.setScenarioStatusSkipped();
this.setScenarioStatusStopped();
this.setScenarioStatusInterrupted();
this.setScenarioStatusCancelled();
this.setScenarioStatusInfo();
this.setScenarioStatusWarn();
});
```
To set status to the **launch** you can use the next methods:
```javascript
Then(/^I should see my new task in the list$/, function() {
Expand Down
20 changes: 14 additions & 6 deletions modules/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ const LOG_LEVELS = {
WARN: 'WARN',
};

const RP_ENTITY_LAUNCH = 'launch';
const RP_ENTITIES = {
LAUNCH: 'LAUNCH',
SCENARIO: 'SCENARIO',
};

const CUCUMBER_EVENTS = {
GHERKIN_DOCUMENT: 'gherkin-document',
Expand Down Expand Up @@ -77,10 +80,15 @@ const TEST_ITEM_TYPES = {
};

const RP_EVENTS = {
TEST_CASE_ID: 'rp/testCaseId',
ATTRIBUTES: 'rp/attributes',
DESCRIPTION: 'rp/description',
STATUS: 'rp/status',
STEP_TEST_CASE_ID: 'rp/step/testCaseId',
STEP_ATTRIBUTES: 'rp/step/attributes',
STEP_DESCRIPTION: 'rp/step/description',
STEP_STATUS: 'rp/step/status',
SCENARIO_TEST_CASE_ID: 'rp/scenario/testCaseId',
SCENARIO_ATTRIBUTES: 'rp/scenario/attributes',
SCENARIO_DESCRIPTION: 'rp/scenario/description',
SCENARIO_STATUS: 'rp/scenario/status',
LAUNCH_STATUS: 'rp/launch/status',
};

const TEST_STEP_FINISHED_RP_MESSAGES = {
Expand All @@ -90,7 +98,7 @@ const TEST_STEP_FINISHED_RP_MESSAGES = {
};

module.exports = {
RP_ENTITY_LAUNCH,
RP_ENTITIES,
STATUSES,
LOG_LEVELS,
CUCUMBER_EVENTS,
Expand Down
91 changes: 67 additions & 24 deletions modules/cucumber-reportportal-formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const utils = require('./utils');
const pjson = require('../package.json');
const {
RP_EVENTS,
RP_ENTITY_LAUNCH,
RP_ENTITIES,
LOG_LEVELS,
STATUSES,
CUCUMBER_MESSAGES,
Expand Down Expand Up @@ -255,13 +255,13 @@ const createRPFormatterClass = (config) =>
scenarioCodeRefIndexValue && !isRetry
? `${currentNodeCodeRef} [${scenarioCodeRefIndexValue}]`
: currentNodeCodeRef;

const scenarioAttributes = utils.createAttributes(scenario.tags);
const testData = {
startTime: this.reportportal.helpers.now(),
type: this.isScenarioBasedStatistics ? TEST_ITEM_TYPES.STEP : TEST_ITEM_TYPES.TEST,
name: `${keyword}: ${name}`,
description: scenario.description,
attributes: utils.createAttributes(scenario.tags),
attributes: scenarioAttributes,
codeRef: scenarioCodeRef,
retry: this.isScenarioBasedStatistics && attempt > 0,
};
Expand All @@ -276,7 +276,11 @@ const createRPFormatterClass = (config) =>
}
const parentId = ruleTempId || this.storage.getFeatureTempId(pickleFeatureUri);
const { tempId } = this.reportportal.startTestItem(testData, launchTempId, parentId);
this.storage.setScenarioTempId(testCaseId, tempId);
this.storage.setScenario(testCaseId, {
tempId,
description: scenario.description,
attributes: scenarioAttributes,
});
this.storage.updateTestCase(testCaseId, {
codeRef: scenarioCodeRef,
});
Expand Down Expand Up @@ -336,22 +340,24 @@ const createRPFormatterClass = (config) =>
if (data) {
const { testStepId, testCaseStartedId } = data;
const testCaseId = this.storage.getTestCaseId(testCaseStartedId);
const scenario = this.storage.getScenario(testCaseId) || {};
const step = this.storage.getStep(testCaseId, testStepId);
const dataObj = utils.getJSON(data.body);

switch (data.mediaType) {
case RP_EVENTS.TEST_CASE_ID: {
case RP_EVENTS.STEP_TEST_CASE_ID:
case RP_EVENTS.STEP_STATUS: {
this.storage.updateStep(testCaseId, testStepId, dataObj);
break;
}
case RP_EVENTS.ATTRIBUTES: {
case RP_EVENTS.STEP_ATTRIBUTES: {
const savedAttributes = step.attributes || [];
this.storage.updateStep(testCaseId, testStepId, {
attributes: savedAttributes.concat(dataObj.attributes),
});
break;
}
case RP_EVENTS.DESCRIPTION: {
case RP_EVENTS.STEP_DESCRIPTION: {
const savedDescription = step.description || '';
this.storage.updateStep(testCaseId, testStepId, {
description: savedDescription
Expand All @@ -360,31 +366,54 @@ const createRPFormatterClass = (config) =>
});
break;
}
case RP_EVENTS.STATUS: {
if (dataObj.entity !== RP_ENTITY_LAUNCH) {
this.storage.updateStep(testCaseId, testStepId, dataObj);
} else {
this.customLaunchStatus = dataObj.status;
}

case RP_EVENTS.SCENARIO_TEST_CASE_ID:
case RP_EVENTS.SCENARIO_STATUS: {
this.storage.updateScenario(testCaseId, dataObj);
break;
}
case RP_EVENTS.SCENARIO_ATTRIBUTES: {
const savedAttributes = scenario.attributes || [];
this.storage.updateScenario(testCaseId, {
attributes: savedAttributes.concat(dataObj.attributes),
});
break;
}
case RP_EVENTS.SCENARIO_DESCRIPTION: {
const savedDescription = scenario.description || '';
this.storage.updateScenario(testCaseId, {
description: savedDescription
? `${savedDescription}<br/>${dataObj.description}`
: dataObj.description,
});
break;
}

case RP_EVENTS.LAUNCH_STATUS: {
this.customLaunchStatus = dataObj.status;
break;
}

case 'text/plain': {
const request = {
time: this.reportportal.helpers.now(),
};
let tempStepId = this.storage.getStepTempId(testStepId);
let tempId = this.storage.getStepTempId(testStepId);

if (dataObj) {
request.level = dataObj.level;
request.message = dataObj.message;
if (dataObj.entity === RP_ENTITY_LAUNCH) {
tempStepId = this.storage.getLaunchTempId();

if (dataObj.entity === RP_ENTITIES.LAUNCH) {
tempId = this.storage.getLaunchTempId();
} else if (dataObj.entity === RP_ENTITIES.SCENARIO) {
tempId = this.storage.getScenarioTempId(testCaseId);
}
} else {
request.level = LOG_LEVELS.DEBUG;
request.message = data.body;
}
this.reportportal.sendLog(tempStepId, request);
this.reportportal.sendLog(tempId, request);
break;
}
default: {
Expand All @@ -397,24 +426,27 @@ const createRPFormatterClass = (config) =>
name: fileName,
},
};
let tempStepId = this.storage.getStepTempId(testStepId);
let tempId = this.storage.getStepTempId(testStepId);

if (dataObj) {
if (dataObj.level) {
request.level = dataObj.level;
}
request.message = dataObj.message;
request.file.name = dataObj.message;
if (dataObj.entity === RP_ENTITY_LAUNCH) {
tempStepId = this.storage.getLaunchTempId();

if (dataObj.entity === RP_ENTITIES.LAUNCH) {
tempId = this.storage.getLaunchTempId();
} else if (dataObj.entity === RP_ENTITIES.SCENARIO) {
tempId = this.storage.getScenarioTempId(testCaseId);
}
}
const fileObj = {
name: fileName,
type: data.mediaType,
content: (dataObj && dataObj.data) || data.body,
};
this.reportportal.sendLog(tempStepId, request, fileObj);
this.reportportal.sendLog(tempId, request, fileObj);
break;
}
}
Expand Down Expand Up @@ -544,11 +576,22 @@ const createRPFormatterClass = (config) =>

const testCaseId = this.storage.getTestCaseId(testCaseStartedId);
const testCase = this.storage.getTestCase(testCaseId);
const scenarioTempId = this.storage.getScenarioTempId(testCaseId);
const {
tempId: scenarioTempId,
status: scenarioStatus,
testCaseId: customTestCaseId,
attributes,
description,
} = this.storage.getScenario(testCaseId);

this.reportportal.finishTestItem(scenarioTempId, {
endTime: this.reportportal.helpers.now(),
...(this.isScenarioBasedStatistics && { status: testCase.status || STATUSES.PASSED }),
...(this.isScenarioBasedStatistics && {
status: scenarioStatus || testCase.status || STATUSES.PASSED,
}),
...(this.isScenarioBasedStatistics && customTestCaseId && { testCaseId: customTestCaseId }),
...(attributes && { attributes }),
...(description && { description }),
});

// finish RULE if it's exist and if it's last scenario
Expand All @@ -575,7 +618,7 @@ const createRPFormatterClass = (config) =>
this.storage.removeTestCaseStartedId(testCaseStartedId);
this.storage.removeSteps(testCaseId);
this.storage.removeTestCase(testCaseId);
this.storage.removeScenarioTempId(testCaseStartedId);
this.storage.removeScenario(testCaseStartedId);
}

const { uri: pickleFeatureUri } = this.storage.getPickle(testCase.pickleId);
Expand Down
Loading

0 comments on commit 44deb8c

Please sign in to comment.