diff --git a/CHANGELOG.md b/CHANGELOG.md
index ce3e13d..00173fe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
### Added
- Add `@cucumber/cucumber` version 10 support. Addressed [155](https://github.com/reportportal/agent-js-cucumber/issues/155).
### Changed
+- **Breaking change** Drop support of cucumber <7. Addressed [153](https://github.com/reportportal/agent-js-cucumber/issues/153).
- **Breaking change** Drop support of Node.js 10. The version [5.2.3](https://github.com/reportportal/agent-js-cucumber/releases/tag/v5.2.3) is the latest that supports it.
- `@reportportal/client-javascript` bumped to version `5.1.0`.
diff --git a/README.md b/README.md
index 205e574..c7a8931 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,6 @@ Agent to integrate CucumberJS with ReportPortal.
* More about [ReportPortal](http://reportportal.io/)
This agent works well with cucumber versions from 7.x to 10.x.
-Documentation for legacy cucumber versions from 4.x to 6.x can be found [here](/modules/api/deprecated/README.md)
## Install agent to your project dir
diff --git a/jest.config.js b/jest.config.js
index 256824a..c2f3506 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -1,7 +1,7 @@
module.exports = {
moduleFileExtensions: ['js'],
testRegex: '/tests/.*\\.spec.(js)$',
- collectCoverageFrom: ['modules/**.js', '!api/deprecated'],
+ collectCoverageFrom: ['modules/**.js'],
coverageThreshold: {
global: {
branches: 80,
diff --git a/modules/api/current.js b/modules/api/current.js
deleted file mode 100644
index b4a41b0..0000000
--- a/modules/api/current.js
+++ /dev/null
@@ -1,565 +0,0 @@
-/*
- * Copyright 2022 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.
- */
-
-const stripAnsi = require('strip-ansi');
-const pjson = require('../../package.json');
-const utils = require('../utils');
-const {
- RP_EVENTS,
- RP_ENTITY_LAUNCH,
- LOG_LEVELS,
- STATUSES,
- CUCUMBER_MESSAGES,
- TEST_ITEM_TYPES,
-} = require('../constants');
-const Storage = require('../storage');
-
-module.exports = {
- init() {
- this.storage = new Storage();
- this.customLaunchStatus = null;
- this.codeRefIndexesMap = new Map();
-
- this.options.eventBroadcaster.on('envelope', (event) => {
- const [key] = Object.keys(event);
- switch (key) {
- case CUCUMBER_MESSAGES.GHERKIN_DOCUMENT:
- return this.onGherkinDocumentEvent(event[key]);
- case CUCUMBER_MESSAGES.PICKLE:
- return this.onPickleEvent(event[key]);
- case CUCUMBER_MESSAGES.HOOK:
- return this.onHookEvent(event[key]);
- case CUCUMBER_MESSAGES.TEST_RUN_STARTED:
- return this.onTestRunStartedEvent();
- case CUCUMBER_MESSAGES.TEST_CASE:
- return this.onTestCaseEvent(event[key]);
- case CUCUMBER_MESSAGES.TEST_CASE_STARTED:
- return this.onTestCaseStartedEvent(event[key]);
- case CUCUMBER_MESSAGES.TEST_STEP_STARTED:
- return this.onTestStepStartedEvent(event[key]);
- case CUCUMBER_MESSAGES.ATTACHMENT:
- return this.onTestStepAttachmentEvent(event[key]);
- case CUCUMBER_MESSAGES.TEST_STEP_FINISHED:
- return this.onTestStepFinishedEvent(event[key]);
- case CUCUMBER_MESSAGES.TEST_CASE_FINISHED:
- return this.onTestCaseFinishedEvent(event[key]);
- case CUCUMBER_MESSAGES.TEST_RUN_FINISHED:
- return this.onTestRunFinishedEvent(event[key]);
- default:
- return null;
- }
- });
- },
- onGherkinDocumentEvent(data) {
- this.storage.setDocument(data);
- this.storage.setAstNodesData(data, utils.findAstNodesData(data.feature.children));
- },
- onHookEvent(data) {
- const { id } = data;
- this.storage.setHook(id, data);
- },
- onPickleEvent(data) {
- this.storage.setPickle(data);
- },
- onTestRunStartedEvent() {
- const attributes = [
- ...(this.config.attributes || []),
- { key: 'agent', value: `${pjson.name}|${pjson.version}`, system: true },
- ];
- if (this.config.skippedIssue === false) {
- const skippedIssueAttribute = { key: 'skippedIssue', value: 'false', system: true };
- attributes.push(skippedIssueAttribute);
- }
- const startLaunchData = {
- name: this.config.launch,
- startTime: this.reportportal.helpers.now(),
- description: this.config.description || '',
- attributes,
- rerun: this.isRerun,
- rerunOf: this.rerunOf,
- ...(this.config.mode && { mode: this.config.mode }),
- };
- const { tempId } = this.reportportal.startLaunch(startLaunchData);
- this.storage.setLaunchTempId(tempId);
- },
- onTestCaseEvent(data) {
- const { id: testCaseId, pickleId, testSteps } = data;
- this.storage.setTestCase({ id: testCaseId, pickleId, testSteps });
-
- // prepare steps
- const stepsMap = {};
- testSteps.forEach((step, index) => {
- const { pickleStepId, id, hookId } = step;
-
- if (pickleStepId) {
- const { steps: stepsData } = this.storage.getPickle(pickleId);
- const stepData = stepsData.find((item) => item.id === pickleStepId);
- stepsMap[id] = { ...stepData, type: TEST_ITEM_TYPES.STEP };
- } else if (hookId) {
- const isBeforeHook = index === 0;
- const { name } = this.storage.getHook(hookId);
- stepsMap[id] = {
- text: name || (isBeforeHook ? 'Before' : 'After'),
- type: isBeforeHook ? TEST_ITEM_TYPES.BEFORE_TEST : TEST_ITEM_TYPES.AFTER_TEST,
- };
- }
- });
- this.storage.setSteps(testCaseId, stepsMap);
- },
- onTestCaseStartedEvent(data) {
- const { id, testCaseId, attempt } = data;
- this.storage.setTestCaseStartedId(id, testCaseId);
- const { pickleId, isRetry: isTestCaseRetried } = this.storage.getTestCase(testCaseId);
-
- const {
- uri: pickleFeatureUri,
- astNodeIds: [scenarioId, parametersId],
- } = this.storage.getPickle(pickleId);
- const currentFeatureUri = this.storage.getCurrentFeatureUri();
- const feature = this.storage.getFeature(pickleFeatureUri);
- const launchTempId = this.storage.getLaunchTempId();
- const isNeedToStartFeature = currentFeatureUri !== pickleFeatureUri;
-
- // start FEATURE if no currentFeatureUri or new feature
- // else finish old one
- const featureCodeRef = utils.formatCodeRef(pickleFeatureUri, feature.name);
- if (isNeedToStartFeature) {
- const isFirstFeatureInLaunch = currentFeatureUri === null;
- const suiteData = {
- name: `${feature.keyword}: ${feature.name}`,
- startTime: this.reportportal.helpers.now(),
- type: this.isScenarioBasedStatistics ? TEST_ITEM_TYPES.TEST : TEST_ITEM_TYPES.SUITE,
- description: (feature.description || '').trim(),
- attributes: utils.createAttributes(feature.tags),
- codeRef: featureCodeRef,
- };
-
- if (!isFirstFeatureInLaunch) {
- const previousFeatureTempId = this.storage.getFeatureTempId();
- this.reportportal.finishTestItem(previousFeatureTempId, {
- endTime: this.reportportal.helpers.now(),
- });
- }
-
- this.storage.setCurrentFeatureUri(pickleFeatureUri);
- const { tempId } = this.reportportal.startTestItem(suiteData, launchTempId, '');
- this.storage.setFeatureTempId(tempId);
- }
-
- // current feature node rule(this entity is for grouping several
- // scenarios in one logical block) || scenario
- const currentNode = utils.findNode(feature, scenarioId);
-
- let scenario;
- let ruleTempId;
- if (currentNode.rule) {
- ruleTempId = this.storage.getRuleTempId(currentNode.rule.id);
-
- if (!ruleTempId) {
- const { rule } = currentNode;
-
- const { name, description, tags, keyword, children = [], id: ruleId } = rule;
- const childrenIds = children.map((child) => child.scenario.id);
- const currentNodeCodeRef = utils.formatCodeRef(featureCodeRef, name);
- const testData = {
- startTime: this.reportportal.helpers.now(),
- type: this.isScenarioBasedStatistics ? TEST_ITEM_TYPES.TEST : TEST_ITEM_TYPES.SUITE,
- name: `${keyword}: ${name}`,
- description,
- attributes: utils.createAttributes(tags),
- codeRef: currentNodeCodeRef,
- };
- const parentId = this.storage.getFeatureTempId();
- const { tempId } = this.reportportal.startTestItem(testData, launchTempId, parentId);
- ruleTempId = tempId;
-
- scenario = utils.findScenario(rule, scenarioId);
-
- this.storage.setRuleTempId(ruleId, ruleTempId);
- this.storage.setRuleTempIdToTestCase(id, ruleTempId);
- this.storage.setRuleChildrenIds(ruleTempId, childrenIds);
- this.storage.setStartedRuleChildrenIds(ruleTempId, scenarioId);
- } else {
- this.storage.setRuleTempIdToTestCase(id, ruleTempId);
- this.storage.setStartedRuleChildrenIds(ruleTempId, scenarioId);
- scenario = utils.findScenario(currentNode.rule, scenarioId);
- }
- } else {
- scenario = currentNode.scenario;
- }
-
- let isRetry = isTestCaseRetried;
- if (attempt > 0) {
- isRetry = true;
- this.storage.updateTestCase(testCaseId, { isRetry });
-
- // do not show scenario with retry in RP
- if (!this.isScenarioBasedStatistics) return;
- }
-
- const { name: scenarioName } = scenario;
- const [keyword] = scenario.keyword.split(' ');
-
- const currentNodeCodeRef = utils.formatCodeRef(
- featureCodeRef,
- ruleTempId ? currentNode.rule.name : scenarioName,
- );
- const scenarioCodeRefIndexValue = this.codeRefIndexesMap.get(currentNodeCodeRef);
- this.codeRefIndexesMap.set(currentNodeCodeRef, (scenarioCodeRefIndexValue || 0) + 1);
- const name =
- scenarioCodeRefIndexValue && !isRetry
- ? `${scenarioName} [${scenarioCodeRefIndexValue}]`
- : scenarioName;
- const scenarioCodeRef =
- scenarioCodeRefIndexValue && !isRetry
- ? `${currentNodeCodeRef} [${scenarioCodeRefIndexValue}]`
- : currentNodeCodeRef;
-
- 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),
- codeRef: scenarioCodeRef,
- retry: this.isScenarioBasedStatistics && attempt > 0,
- };
-
- if (parametersId) {
- const [{ tableHeader, tableBody }] = scenario.examples;
- const params = utils.collectParams({ tableHeader, tableBody });
- Object.keys(params).forEach((paramKey) => {
- this.storage.setParameters(paramKey, params[paramKey]);
- });
- testData.parameters = this.storage.getParameters(parametersId);
- }
- const parentId = ruleTempId || this.storage.getFeatureTempId();
- const { tempId } = this.reportportal.startTestItem(testData, launchTempId, parentId);
- this.storage.setScenarioTempId(testCaseId, tempId);
- this.storage.updateTestCase(testCaseId, {
- codeRef: scenarioCodeRef,
- });
- },
- onTestStepStartedEvent(data) {
- const { testCaseStartedId, testStepId } = data;
- const testCaseId = this.storage.getTestCaseId(testCaseStartedId);
- const testCase = this.storage.getTestCase(testCaseId);
- const step = this.storage.getStep(testCaseId, testStepId);
-
- // start step
- if (step) {
- const currentFeatureUri = this.storage.getCurrentFeatureUri();
- const astNodesData = this.storage.getAstNodesData(currentFeatureUri);
-
- const { text: stepName, type, astNodeIds } = step;
- const keyword =
- astNodeIds && (astNodesData.find(({ id }) => astNodeIds.includes(id)) || {}).keyword;
-
- const codeRef = utils.formatCodeRef(testCase.codeRef, stepName);
- const stepCodeRefIndexValue = this.codeRefIndexesMap.get(codeRef);
- this.codeRefIndexesMap.set(codeRef, (stepCodeRefIndexValue || 0) + 1);
- const name =
- stepCodeRefIndexValue && !testCase.isRetry
- ? `${stepName} [${stepCodeRefIndexValue}]`
- : stepName;
-
- const stepData = {
- name: keyword ? `${keyword} ${name}` : name,
- startTime: this.reportportal.helpers.now(),
- type,
- codeRef,
- hasStats: !this.isScenarioBasedStatistics,
- retry: !this.isScenarioBasedStatistics && !!testCase.isRetry,
- };
-
- if (!this.isScenarioBasedStatistics && step.astNodeIds && step.astNodeIds.length > 1) {
- const { testSteps } = testCase;
- const testStep = testSteps.find((item) => item.id === testStepId);
- const argumentsMap = testStep.stepMatchArgumentsLists[0].stepMatchArguments.map((arg) =>
- arg.group.value.slice(1, -1),
- );
- const parametersId = step.astNodeIds[1];
- const params = this.storage.getParameters(parametersId);
- stepData.parameters = params.filter((param) => argumentsMap.includes(param.value));
- }
-
- const launchTempId = this.storage.getLaunchTempId();
- const parentId = this.storage.getScenarioTempId(testCaseId);
- const { tempId } = this.reportportal.startTestItem(stepData, launchTempId, parentId);
- this.storage.setStepTempId(testStepId, tempId);
- }
- },
- onTestStepAttachmentEvent(data) {
- if (data) {
- const { testStepId, testCaseStartedId } = data;
- const testCaseId = this.storage.getTestCaseId(testCaseStartedId);
- const step = this.storage.getStep(testCaseId, testStepId);
- const dataObj = utils.getJSON(data.body);
-
- switch (data.mediaType) {
- case RP_EVENTS.TEST_CASE_ID: {
- this.storage.updateStep(testCaseId, testStepId, dataObj);
- break;
- }
- case RP_EVENTS.ATTRIBUTES: {
- const savedAttributes = step.attributes || [];
- this.storage.updateStep(testCaseId, testStepId, {
- attributes: savedAttributes.concat(dataObj.attributes),
- });
- break;
- }
- case RP_EVENTS.DESCRIPTION: {
- const savedDescription = step.description || '';
- this.storage.updateStep(testCaseId, testStepId, {
- description: savedDescription
- ? `${savedDescription}
${dataObj.description}`
- : dataObj.description,
- });
- break;
- }
- case RP_EVENTS.STATUS: {
- if (dataObj.entity !== RP_ENTITY_LAUNCH) {
- this.storage.updateStep(testCaseId, testStepId, dataObj);
- } else {
- this.customLaunchStatus = dataObj.status;
- }
- break;
- }
- case 'text/plain': {
- const request = {
- time: this.reportportal.helpers.now(),
- };
- let tempStepId = this.storage.getStepTempId(testStepId);
-
- if (dataObj) {
- request.level = dataObj.level;
- request.message = dataObj.message;
- if (dataObj.entity === RP_ENTITY_LAUNCH) {
- tempStepId = this.storage.getLaunchTempId();
- }
- } else {
- request.level = LOG_LEVELS.DEBUG;
- request.message = data.body;
- }
- this.reportportal.sendLog(tempStepId, request);
- break;
- }
- default: {
- const fileName = 'file'; // TODO: generate human valuable file name here if possible
- const request = {
- time: this.reportportal.helpers.now(),
- level: LOG_LEVELS.INFO,
- message: fileName,
- file: {
- name: fileName,
- },
- };
- let tempStepId = 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();
- }
- }
- const fileObj = {
- name: fileName,
- type: data.mediaType,
- content: (dataObj && dataObj.data) || data.body,
- };
- this.reportportal.sendLog(tempStepId, request, fileObj);
- break;
- }
- }
- }
- },
- onTestStepFinishedEvent(data) {
- const { testCaseStartedId, testStepId, testStepResult } = data;
- const testCaseId = this.storage.getTestCaseId(testCaseStartedId);
- const step = this.storage.getStep(testCaseId, testStepId);
- const tempStepId = this.storage.getStepTempId(testStepId);
- let status;
-
- switch (testStepResult.status.toLowerCase()) {
- case STATUSES.PASSED: {
- status = STATUSES.PASSED;
- break;
- }
- case STATUSES.PENDING: {
- this.reportportal.sendLog(tempStepId, {
- time: this.reportportal.helpers.now(),
- level: 'WARN',
- message: "This step is marked as 'pending'",
- });
- status = STATUSES.FAILED;
- break;
- }
- case STATUSES.UNDEFINED: {
- this.reportportal.sendLog(tempStepId, {
- time: this.reportportal.helpers.now(),
- level: 'ERROR',
- message: 'There is no step definition found. Please verify and implement it.',
- });
- status = STATUSES.FAILED;
- break;
- }
- case STATUSES.AMBIGUOUS: {
- this.reportportal.sendLog(tempStepId, {
- time: this.reportportal.helpers.now(),
- level: 'ERROR',
- message: 'There are more than one step implementation. Please verify and reimplement it.',
- });
- status = STATUSES.FAILED;
- break;
- }
- case STATUSES.SKIPPED: {
- status = STATUSES.SKIPPED;
- break;
- }
- case STATUSES.FAILED: {
- status = STATUSES.FAILED;
- this.reportportal.sendLog(tempStepId, {
- time: this.reportportal.helpers.now(),
- level: 'ERROR',
- message: stripAnsi(testStepResult.message),
- });
-
- const isBrowserAvailable = 'browser' in global;
- const isTakeScreenshotOptionProvidedInRPConfig =
- this.config.takeScreenshot && this.config.takeScreenshot === 'onFailure';
-
- if (isBrowserAvailable && isTakeScreenshotOptionProvidedInRPConfig) {
- const currentFeatureUri = this.storage.getCurrentFeatureUri();
- const astNodesData = this.storage.getAstNodesData(currentFeatureUri);
- const screenshotName = utils.getScreenshotName(astNodesData, step.astNodeIds);
-
- const request = {
- time: this.reportportal.helpers.now(),
- level: 'ERROR',
- file: { name: screenshotName },
- message: screenshotName,
- };
-
- global.browser
- .takeScreenshot()
- .then((png) => {
- const screenshot = {
- name: screenshotName,
- type: 'image/png',
- content: png,
- };
- this.reportportal.sendLog(tempStepId, request, screenshot);
- })
- .catch((error) => {
- console.dir(error);
- });
- }
- break;
- }
- default:
- break;
- }
-
- if (step) {
- const { attributes, description = '', testCaseId: customTestCaseId } = step;
- status = step.status || status || testStepResult.status;
- const errorMessage =
- testStepResult.message && `\`\`\`error\n${stripAnsi(testStepResult.message)}\n\`\`\``;
- const descriptionToSend = errorMessage
- ? `${description}${description ? '\n' : ''}${errorMessage}`
- : description;
- const withoutIssue = status === STATUSES.SKIPPED && this.config.skippedIssue === false;
- this.reportportal.finishTestItem(tempStepId, {
- ...(status && { status }),
- ...(attributes && { attributes }),
- ...(descriptionToSend && { description: descriptionToSend }),
- ...(customTestCaseId && { testCaseId: customTestCaseId }),
- ...(withoutIssue && { issue: { issueType: 'NOT_ISSUE' } }),
- endTime: this.reportportal.helpers.now(),
- });
- }
-
- if (this.isScenarioBasedStatistics && status !== STATUSES.PASSED) {
- this.storage.updateTestCase(testCaseId, { status: STATUSES.FAILED });
- }
-
- this.storage.removeStepTempId(testStepId);
- },
- onTestCaseFinishedEvent({ testCaseStartedId, willBeRetried }) {
- const isNeedToFinishTestCase = !this.isScenarioBasedStatistics && willBeRetried;
-
- if (isNeedToFinishTestCase) {
- return;
- }
-
- const testCaseId = this.storage.getTestCaseId(testCaseStartedId);
- const testCase = this.storage.getTestCase(testCaseId);
- const scenarioTempId = this.storage.getScenarioTempId(testCaseId);
-
- this.reportportal.finishTestItem(scenarioTempId, {
- endTime: this.reportportal.helpers.now(),
- ...(this.isScenarioBasedStatistics && { status: testCase.status || STATUSES.PASSED }),
- });
-
- // finish RULE if it's exist and if it's last scenario
- const ruleTempId = this.storage.getRuleTempIdToTestCase(testCaseStartedId);
- const ruleChildrenIds = this.storage.getRuleChildrenIds(ruleTempId);
- const startedRuleChildrenIds = this.storage.getStartedRuleChildrenIds(ruleTempId);
- const isAllRuleChildrenStarted = utils.isAllRuleChildrenStarted(
- ruleChildrenIds,
- startedRuleChildrenIds,
- );
-
- if (ruleTempId && isAllRuleChildrenStarted) {
- this.reportportal.finishTestItem(ruleTempId, {
- endTime: this.reportportal.helpers.now(),
- });
-
- this.storage.removeRuleTempIdToTestCase(testCaseStartedId);
- this.storage.removeStartedRuleChildrenIds(ruleTempId);
- this.storage.removeRuleChildrenIds(ruleTempId);
- this.codeRefIndexesMap.clear();
- }
-
- if (!willBeRetried) {
- this.storage.removeTestCaseStartedId(testCaseStartedId);
- this.storage.removeSteps(testCaseId);
- this.storage.removeTestCase(testCaseId);
- this.storage.removeScenarioTempId(testCaseStartedId);
- }
- },
- onTestRunFinishedEvent() {
- const featureTempId = this.storage.getFeatureTempId();
- this.reportportal.finishTestItem(featureTempId, {
- endTime: this.reportportal.helpers.now(),
- });
-
- const launchId = this.storage.getLaunchTempId();
- this.reportportal.getPromiseFinishAllItems(launchId).then(() => {
- this.reportportal.finishLaunch(launchId, {
- ...(this.customLaunchStatus && { status: this.customLaunchStatus }),
- });
- this.storage.setLaunchTempId(null);
- this.storage.setCurrentFeatureUri(null);
- this.storage.setFeatureTempId(null);
- this.customLaunchStatus = null;
- });
- },
-};
diff --git a/modules/api/deprecated/README.md b/modules/api/deprecated/README.md
deleted file mode 100644
index de0c9d0..0000000
--- a/modules/api/deprecated/README.md
+++ /dev/null
@@ -1,316 +0,0 @@
-## THIS IS DOCUMENTATION FOR DEPRECATED VERSION CUCUMBER FRAMEWORK
PLEASE USE CUCUMBER VERSION FROM 7 OR HIGHER
-
-# agent-js-cucumber
-
-Agent for integration CucumberJS with ReportPortal.
-* More about [CucumberJS](https://cucumber.io/docs/installation/javascript/)
-* More about [ReportPortal](http://reportportal.io/)
-
-This agent works well with cucumber versions from 4.x to 6.x inclusive.
-
-## Install agent to your project dir
-
-```cmd
-npm install --save-dev @reportportal/agent-js-cucumber
-```
-
-1. Make sure that you required glue code correctly. It is important to make Cucumber see support code.
- For example:
- Let's say you have project structure like this below
-
- ```
- my-project
- L features
- L step_definitions
- L steps.js
- L support
- L hooks.js
- L world.js
- L package.json
- ```
-
- #### Note
-
- Protractor and Cucumber have their own **timeouts** .
- When protractror start main process that lauches cucumber it would have different timeouts if there not the same they would wait for scripts different time.
- If cucumbers's timeout less then protractor's it would through wrong exeption.
- For example if page that has been loaded and hasn't got angular, the next error would be thrown : `Error: function timed out after 10000 milliseconds . . .` . Instead of protractor's :
- `Error: Error while running testForAngular: asynchronous script timeout: result was not received in 4 seconds . . .` .
- So it must be handled manually by setting cucumbers's timeout greater then protractor's is at the hooks.js. For example if you set up protractor's timeout 9000 miliseconds , so cucumber must be at least 1 second greater = 10000 miliseconds. Example :
-
- ```javascript
- var { setDefaultTimeout } = require('cucumber');
-
- setDefaultTimeout(10000);
- ```
-
-2. Create Report Portal configuration file
- For example `./rpConfig.json`
-
- In example below `${text}` - is used as placeholder for your data. This data you must get from ReportPortal profile.
-
- ```json
- {
- "token": "${rp.token}",
- "endpoint": "${rp.endpoint}/api/v1",
- "launch": "${rp.launch}",
- "project": "${rp.your_project}",
- "takeScreenshot": "onFailure",
- "description": "Awesome launch description.",
- "attributes": [
- {
- "key": "launchAttributeKey",
- "value": "launchAttributeValue"
- }
- ],
- "mode": "DEFAULT",
- "debug": false,
- "restClientConfig": {
- "timeout": 0
- }
- }
- ```
-
- `takeScreenshot` - if this option is defined then framework will take screenshot with _protractor or webdriver_ API if step has failed
- `mode` - Launch mode. Allowable values *DEFAULT* (by default) or *DEBUG*.
- `debug` - this flag allows seeing the logs of the `client-javascript`. Useful for debugging.
- `restClientConfig` (optional) - The object with `agent` property for configure [http(s)](https://nodejs.org/api/https.html#https_https_request_url_options_callback) client, may contain other client options eg. `timeout`.
-
-3. Create Report Portal formatter in a new js file, for example `reportPortalFormatter.js`:
-
- ```javascript
- const { createRPFormatterClass } = require('@reportportal/agent-js-cucumber');
- const config = require('./rpConfig.json');
-
- module.exports = createRPFormatterClass(config);
- ```
-
-4. Import RPWorld (provides API for logging and data attaching) into /features/step_definitions/support/world.js
-
- ```javascript
- let { setWorldConstructor } = require('cucumber');
- let { RPWorld } = require('@reportportal/agent-js-cucumber');
- setWorldConstructor(RPWorld);
- ```
-
- If you have other world constructors it must be used with the RPWorld as shown below
-
- ```javascript
- class CustomWorld extends RPWorld {
- constructor(...args) {
- super(...args);
-
- /*
- * any driver container must be named 'browser', because reporter could be used with cucumber
- * and protractor. And protractor has global object browser which contains all web-driver methods
- */
- global.browser = new seleniumWebdriver.Builder().forBrowser('chrome').build();
- }
- }
-
- setWorldConstructor(CustomWorld);
- ```
-
- It will allow you send logs and screenshots to RP directly from step definitions.
- **All this logs would be attached to test data and could be viewed at the Report Portal**.
- Also you will be able to specify additional info for test items (e.g. description, attributes, testCaseId, status).
- See [API](#api) section for more information.
-
-5. Run cucumber-js
-
- `cucumber-js -f ./reportPortalFormatter.js`
-
- More info in the [examples](https://github.com/reportportal/examples-js/tree/master/example-cucumber) repository.
-
-### TODO parallel launch
-
-## Rerun
-
-To report [rerun](https://github.com/reportportal/documentation/blob/master/src/md/src/DevGuides/rerun.md) to the report portal you need to specify the following options to the config file:
-
-- rerun - to enable rerun
-- rerunOf - UUID of launch you want to rerun. If not specified, report portal will update the latest launch with the same name
-
-Example:
-
-```json
- "rerun": true,
- "rerunOf": "f68f39f9-279c-4e8d-ac38-1216dffcc59c"
-```
-
-## 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: `"scenarioBasedStatistics": true`
-
-```json
-{
- "scenarioBasedStatistics": true
-}
-```
-
-This will report your your steps with logs to a log level without creating statistics for every step.
-
-## Reporting skipped cucumber steps as failed
-
-By default, cucumber marks steps which follow a failed step as `skipped`.
-When `scenarioBasedStatistics` is set to `false` (the default behavior)
-Report Portal reports these steps as failures to investigate.
-
-To change this behavior and instead mark skipped steps which follow a failed step as `cancelled`,
-you need to add an additional parameter to the agent config: `"reportSkippedCucumberStepsOnFailedTest": false`
-
-```json
-{
- "reportSkippedCucumberStepsOnFailedTest": false
-}
-```
-
-Steps which are marked as `skipped` that do not follow a failed step will continue to mark the step and the scenario as `skipped`.
-
-## API
-
-### Attachments
-
-Attachments are being reported as logs. You can either just attach a file using cucumber's `this.attach` or specify log level and message:
-
-```javascript
-this.attach(
- JSON.stringify({
- message: `Attachment with ${type}`,
- level: 'INFO',
- data: data.toString('base64'),
- }),
- type,
-);
-```
-To send attachment to the launch just specify `entity: 'launch'` property.
-
-Also `this.screenshot` 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.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.
-Handler will parse attachments and send corresponding log to the step item.
-
-### Logs
-
-To report logs to the **items** you can use the next methods:
-
-```javascript
-Then(/^I should see my new task in the list$/, function() {
- this.info('This is Info Level log');
- this.debug('This is Debug Level log');
- this.error('This is Error Level log');
- this.warn('This is Warn Level log');
- this.trace('This is Trace Level log');
- this.fatal('This is Fatal Level log');
-});
-```
-
-To report logs to the **launch** you can use the next methods:
-
-```javascript
-Then(/^I should see my new task in the list$/, function() {
- this.launchInfo('This is Info Level log');
- this.launchDebug('This is Debug Level log');
- this.launchError('This is Error Level log');
- this.launchWarn('This is Warn Level log');
- this.launchTrace('This is Trace Level log');
- this.launchFatal('This is Fatal Level log');
-});
-```
-
-### Attributes
-
-Attributes for features and scenarios are parsed from @tags as `@key:value` pair.
-
-To add attributes to the 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' }]);
-});
-```
-
-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:
-
-```javascript
-Then(/^I should see my new task in the list$/, function() {
- this.addDescription('Test item description.');
-});
-```
-
-The description will be concatenated.
-
-### TestCaseId
-
-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');
-});
-```
-
-### Statuses
-
-The user can set the status of the item/launch directly depending on some conditions or behavior.
-It will take precedence over the actual completed status.
-
-To set status to the **item** you can use the next methods:
-
-```javascript
-Then(/^I should see my new task in the list$/, function() {
- this.setStatusPassed();
- this.setStatusFailed();
- this.setStatusSkipped();
- this.setStatusStopped();
- this.setStatusInterrupted();
- this.setStatusCancelled();
-});
-```
-
-To set status to the **item** you can use the next methods:
-
-```javascript
-Then(/^I should see my new task in the list$/, function() {
- this.setLaunchStatusPassed();
- this.setLaunchStatusFailed();
- this.setLaunchStatusSkipped();
- this.setLaunchStatusStopped();
- this.setLaunchStatusInterrupted();
- this.setLaunchStatusCancelled();
-});
-```
-
-# Copyright Notice
-
-Licensed under the [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0.html)
-license (see the LICENSE.txt file).
diff --git a/modules/api/deprecated/context.js b/modules/api/deprecated/context.js
deleted file mode 100644
index 19c8eb1..0000000
--- a/modules/api/deprecated/context.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-const { STATUSES } = require('../../constants');
-
-class Context {
- constructor() {
- this.initContext();
- }
-
- initContext() {
- this.outlineRow = 0;
- this.scenarioStatus = STATUSES.FAILED;
- this.forcedIssue = null;
- this.currentFeatureUri = null;
- this.scenarioId = null;
- this.stepId = null;
- this.stepStatus = STATUSES.FAILED;
- this.launchId = null;
- this.background = null;
- this.failedScenarios = {};
- this.lastScenarioDescription = null;
- this.scenario = null;
- this.step = null;
- this.stepSourceLocation = null;
- this.stepDefinitions = null;
- this.stepDefinition = null;
- this.itemsParams = {};
- }
-
- getFileName() {
- const fileName = this.stepDefinition
- ? `Failed at step definition line:${this.stepDefinition.line}`
- : 'UNDEFINED STEP';
-
- return fileName;
- }
-
- findStep(event) {
- let stepObj = null;
- const stepDefinition = this.stepDefinitions.steps[event.index];
-
- if (stepDefinition.hookType) {
- stepObj = { keyword: stepDefinition.hookType };
- } else {
- this.scenario.steps.forEach((step) => {
- if (
- stepDefinition.sourceLocation.uri === event.testCase.sourceLocation.uri &&
- stepDefinition.sourceLocation.line === step.location.line
- ) {
- stepObj = step;
- }
- });
-
- if (this.background) {
- this.background.steps.forEach((step) => {
- if (
- stepDefinition.sourceLocation.uri === event.testCase.sourceLocation.uri &&
- stepDefinition.sourceLocation.line === step.location.line
- ) {
- stepObj = step;
- }
- });
- }
- }
- return stepObj;
- }
-
- incrementFailedScenariosCount(uri) {
- this.failedScenarios[uri] = this.failedScenarios[uri] ? this.failedScenarios[uri] + 1 : 1;
- }
-
- resetContext() {
- this.initContext();
- }
-}
-
-module.exports = Context;
diff --git a/modules/api/deprecated/cucumber-reportportal-formatter.test.js b/modules/api/deprecated/cucumber-reportportal-formatter.test.js
deleted file mode 100644
index be8c477..0000000
--- a/modules/api/deprecated/cucumber-reportportal-formatter.test.js
+++ /dev/null
@@ -1,888 +0,0 @@
-/* These tests need to be rewritten to reflect the refactoring of the agent. Plans for version 5.0.1. */
-const { createRPFormatterClass } = require('../../index');
-const {
- ContextMock,
- DocumentsStorageMock,
- RPClientMock,
- getDefaultConfig,
- mockedDate,
-} = require('../../../tests/mocks');
-const itemFinders = require('./itemFinders');
-const utils = require('./utils');
-const { AFTER_HOOK_URI_TO_SKIP, STATUSES } = require('../../constants');
-
-const featureMock = {
- description: 'feature description',
- keyword: 'ft',
- name: 'feature',
- tags: ['@feature:value'],
- children: [],
-};
-
-describe('Create ReportPortal formatter class', function() {
- let FormatterClass;
- let formatter;
-
- beforeEach(() => {
- const config = getDefaultConfig();
-
- FormatterClass = createRPFormatterClass(config);
-
- formatter = new FormatterClass({
- parsedArgvOptions: {},
- eventBroadcaster: {
- on: () => {},
- },
- });
-
- formatter.contextState = new ContextMock();
- formatter.documentsStorage = new DocumentsStorageMock();
- formatter.reportportal = new RPClientMock();
- formatter.attributesConf = [];
- });
-
- afterEach(() => {
- jest.clearAllMocks();
- });
-
- describe('onGherkinDocument', () => {
- const documentEvent = {
- uri: 'mockUri',
- document: 'any',
- pickle: null,
- };
-
- test('should call cacheDocument method from documents storage to cache the document', function() {
- const spyCacheDocument = jest.spyOn(formatter.documentsStorage, 'cacheDocument');
-
- formatter.onGherkinDocument(documentEvent);
-
- expect(spyCacheDocument).toHaveBeenCalledWith(documentEvent);
- });
-
- test('should call startLaunch method from RPClient if not launchId in the config', function() {
- const launchStartObj = {
- name: 'LauncherName',
- startTime: mockedDate,
- description: 'Launch description',
- attributes: [],
- rerun: undefined,
- rerunOf: undefined,
- };
-
- const spyStartLaunch = jest.spyOn(formatter.reportportal, 'startLaunch');
-
- formatter.onGherkinDocument(documentEvent);
-
- expect(spyStartLaunch).toHaveBeenCalledWith(launchStartObj);
- expect(formatter.contextState.context.launchId).toBe('tempLaunchId');
- });
-
- test('should not call startLaunch method from RPClient if launchId exists in the config', function() {
- formatter.contextState.context.launchId = 'tempLaunchId';
- const spyStartLaunch = jest.spyOn(formatter.reportportal, 'startLaunch');
-
- formatter.onGherkinDocument(documentEvent);
-
- expect(spyStartLaunch).toHaveBeenCalledTimes(0);
- });
- });
-
- describe('onPickleAccepted', () => {
- const uriMock = 'featureUri';
- const documentEvent = {
- uri: uriMock,
- document: 'any',
- pickle: null,
- };
-
- beforeAll(() => {
- jest.spyOn(itemFinders, 'findFeature').mockImplementation(() => featureMock);
- jest.spyOn(itemFinders, 'findBackground').mockReturnValue(null);
- jest.spyOn(utils, 'getUri').mockReturnValue(uriMock);
- jest.spyOn(utils, 'createAttribute').mockReturnValue([{ key: 'feature', value: 'value' }]);
- });
-
- beforeEach(() => {
- formatter.documentsStorage.pickleDocuments[uriMock] = {};
- });
-
- test('should call isAcceptedPickleCached method from documents storage with event', function() {
- const spyIsAcceptedPickleCached = jest.spyOn(
- formatter.documentsStorage,
- 'isAcceptedPickleCached',
- );
-
- formatter.onPickleAccepted(documentEvent);
-
- expect(spyIsAcceptedPickleCached).toHaveBeenCalledWith(documentEvent);
- });
-
- test('should call cacheAcceptedPickle method from documents storage if pickle not cached', function() {
- const spyCacheAcceptedPickle = jest.spyOn(formatter.documentsStorage, 'cacheAcceptedPickle');
-
- formatter.onPickleAccepted(documentEvent);
-
- expect(spyCacheAcceptedPickle).toHaveBeenCalledWith(documentEvent);
- });
-
- test('should not call cacheAcceptedPickle method from documents storage if pickle cached', function() {
- jest.spyOn(formatter.documentsStorage, 'isAcceptedPickleCached').mockReturnValue(true);
-
- const spyCacheAcceptedPickle = jest.spyOn(formatter.documentsStorage, 'cacheAcceptedPickle');
-
- formatter.onPickleAccepted(documentEvent);
-
- expect(spyCacheAcceptedPickle).toHaveBeenCalledTimes(0);
- });
- });
-
- describe('onTestCasePrepared', () => {
- test('should set stepDefinitions and isBeforeHook for the context', function() {
- const event = {
- data: 'any',
- };
-
- formatter.onTestCasePrepared(event);
-
- expect(formatter.contextState.context.stepDefinitions).toEqual(event);
- expect(formatter.contextState.context.isBeforeHook).toBe(true);
- });
- });
-
- describe('onTestCaseStarted', () => {
- const uriMock = 'featureUri';
- const documentEvent = {
- uri: uriMock,
- document: 'any',
- name: 'lol',
- keyword: 'loc',
- description: 'description',
- sourceLocation: {
- uri: uriMock,
- },
- };
- const pickleTags = [
- {
- name: '@feature:value',
- location: {
- line: 1,
- column: 1,
- },
- },
- ];
- let spyFindScenario;
- let spyGetUri;
- let spyCreateAttributes;
- let spyCreateTagComparator;
-
- beforeAll(() => {
- spyFindScenario = jest
- .spyOn(itemFinders, 'findScenario')
- .mockImplementation(() => featureMock);
- spyGetUri = jest.spyOn(utils, 'getUri').mockReturnValue(uriMock);
- spyCreateAttributes = jest
- .spyOn(utils, 'createAttributes')
- .mockReturnValue([{ key: 'feature', value: 'value' }]);
- spyCreateTagComparator = jest
- .spyOn(utils, 'createTagComparator')
- .mockReturnValue(() => false);
- });
-
- beforeEach(() => {
- formatter.documentsStorage.pickleDocuments[uriMock] = {
- featureId: 'featureId',
- tags: pickleTags,
- };
- formatter.documentsStorage.gherkinDocuments[uriMock] = {
- feature: {
- tags: [],
- },
- };
- });
-
- test('should call findScenario with gherkinDocuments & sourceLocation', function() {
- formatter.onTestCaseStarted(documentEvent);
-
- expect(spyFindScenario).toHaveBeenCalledWith(
- formatter.documentsStorage.gherkinDocuments,
- documentEvent.sourceLocation,
- );
- });
-
- test('should call getUri with uri from event sourceLocation', function() {
- formatter.onTestCaseStarted(documentEvent);
-
- expect(spyGetUri).toHaveBeenCalledWith(documentEvent.sourceLocation.uri);
- });
-
- test('should call createTagComparator with pickle tag value', function() {
- formatter.onTestCaseStarted(documentEvent);
-
- expect(spyCreateTagComparator).toHaveBeenCalledWith(pickleTags[0]);
- });
-
- test('should call createAttributes with pickleTags', function() {
- formatter.onTestCaseStarted(documentEvent);
-
- expect(spyCreateAttributes).toHaveBeenCalledWith(pickleTags);
- });
-
- test('should call startTestItem method from RPClient if isScenarioBasedStatistics is true', function() {
- formatter.isScenarioBasedStatistics = true;
-
- const itemStartObj = {
- name: 'ft: feature',
- type: 'STEP',
- startTime: mockedDate,
- description: 'feature description',
- attributes: [{ key: 'feature', value: 'value' }],
- retry: false,
- };
-
- formatter.contextState.context.launchId = 'tempLaunchId';
- const spyStartTestItem = jest.spyOn(formatter.reportportal, 'startTestItem');
-
- formatter.onTestCaseStarted(documentEvent);
-
- expect(spyStartTestItem).toHaveBeenCalledWith(itemStartObj, 'tempLaunchId', 'featureId');
- });
-
- test('should call startTestItem method from RPClient if isScenarioBasedStatistics is false and attemptNumber from event <=2', function() {
- formatter.isScenarioBasedStatistics = false;
-
- const itemStartObj = {
- name: 'ft: feature',
- type: 'TEST',
- startTime: mockedDate,
- description: 'feature description',
- attributes: [{ key: 'feature', value: 'value' }],
- retry: false,
- };
-
- formatter.contextState.context.launchId = 'tempLaunchId';
-
- const spyStartTestItem = jest.spyOn(formatter.reportportal, 'startTestItem');
-
- formatter.onTestCaseStarted({
- ...documentEvent,
- attemptNumber: 1,
- });
-
- expect(spyStartTestItem).toHaveBeenCalledWith(itemStartObj, 'tempLaunchId', 'featureId');
- });
-
- test('should not call startTestItem method from RPClient if isScenarioBasedStatistics is false and attemptNumber from event >=2', function() {
- formatter.isScenarioBasedStatistics = false;
-
- const spyStartTestItem = jest.spyOn(formatter.reportportal, 'startTestItem');
-
- formatter.onTestCaseStarted({
- ...documentEvent,
- attemptNumber: 2,
- });
-
- expect(spyStartTestItem).toHaveBeenCalledTimes(0);
- });
- });
-
- describe('onTestStepStarted', () => {
- const stepMock = {
- keyword: 'stepExample',
- };
- const stepDefinitionMock = {
- name: 'stepDefinition',
- };
- const event = {
- index: 0,
- testCase: {
- attemptNumber: 1,
- },
- };
-
- let spyFindStep;
- let spyFindStepDefinition;
- let spyGetStepType;
-
- beforeAll(() => {
- spyFindStepDefinition = jest
- .spyOn(itemFinders, 'findStepDefinition')
- .mockImplementation(() => stepDefinitionMock);
- spyGetStepType = jest.spyOn(utils, 'getStepType').mockReturnValue('STEP');
- });
-
- beforeEach(() => {
- spyFindStep = jest
- .spyOn(formatter.contextState, 'findStep')
- .mockImplementation(() => stepMock);
- formatter.contextState.context.stepDefinitions = {
- steps: [
- {
- sourceLocation: {},
- },
- ],
- };
- });
-
- test('should call findStep function to find step for context', function() {
- formatter.onTestStepStarted(event);
-
- expect(spyFindStep).toHaveBeenCalledWith(event);
- expect(formatter.contextState.context.step).toEqual(stepMock);
- });
-
- test('should call findStepDefinition function to find step definition for context', function() {
- formatter.onTestStepStarted(event);
-
- expect(spyFindStepDefinition).toHaveBeenCalledWith(formatter.contextState.context, event);
- expect(formatter.contextState.context.stepDefinition).toEqual(stepDefinitionMock);
- });
-
- test('should call getStepType function to get type for step', function() {
- formatter.onTestStepStarted(event);
-
- expect(spyGetStepType).toHaveBeenCalledWith(stepMock.keyword);
- });
-
- test('should call startTestItem method from RPClient', function() {
- formatter.contextState.context.launchId = 'launchId';
- formatter.contextState.context.scenarioId = 'scenarioId';
- formatter.isScenarioBasedStatistics = false;
-
- const itemStartObj = {
- name: stepMock.keyword,
- type: 'STEP',
- startTime: mockedDate,
- description: '',
- hasStats: true,
- retry: false,
- };
-
- const spyStartTestItem = jest.spyOn(formatter.reportportal, 'startTestItem');
-
- formatter.onTestStepStarted(event);
-
- expect(spyStartTestItem).toHaveBeenCalledWith(itemStartObj, 'launchId', 'scenarioId');
- expect(formatter.contextState.context.stepId).toBe('testItemId');
- });
-
- test('should not call startTestItem method and stop function execution', function() {
- formatter.contextState.context.stepDefinitions = {
- steps: [
- {
- actionLocation: {
- uri: `uri: ${AFTER_HOOK_URI_TO_SKIP}`,
- },
- },
- ],
- };
-
- const spyStartTestItem = jest.spyOn(formatter.reportportal, 'startTestItem');
-
- formatter.onTestStepStarted(event);
-
- expect(spyFindStep).toHaveBeenCalledTimes(0);
- expect(spyFindStepDefinition).toHaveBeenCalledTimes(0);
- expect(spyGetStepType).toHaveBeenCalledTimes(0);
- expect(spyStartTestItem).toHaveBeenCalledTimes(0);
- });
- });
-
- describe('onTestStepFinished', () => {
- const event = {
- result: {
- status: 'passed',
- },
- testCase: {
- attemptNumber: 1,
- sourceLocation: { uri: 'testCaseUri' },
- },
- };
-
- let spyGetFileName;
- let spyCountFailedScenarios;
-
- beforeEach(() => {
- spyGetFileName = jest.spyOn(formatter.contextState, 'getFileName');
- spyCountFailedScenarios = jest
- .spyOn(formatter.contextState, 'countFailedScenarios')
- .mockImplementation(() => {});
- formatter.contextState.context.stepSourceLocation = { sourceLocation: {} };
- });
-
- test('should call spyGetFileName to get name for screenshot', function() {
- formatter.onTestStepFinished(event);
-
- expect(spyGetFileName).toHaveBeenCalledTimes(1);
- });
-
- test('should set passed status for step and scenario in case of passed result status', function() {
- formatter.onTestStepFinished(event);
-
- expect(formatter.contextState.context.stepStatus).toBe(STATUSES.PASSED);
- expect(formatter.contextState.context.scenarioStatus).toBe(STATUSES.PASSED);
- });
-
- test('should call sendLog method from RPClient with WARN level in case of pending result status', function() {
- event.result.status = STATUSES.PENDING;
- formatter.contextState.context.stepId = 'stepId';
- formatter.onTestStepFinished(event);
-
- const spySendLog = jest.spyOn(formatter.reportportal, 'sendLog');
-
- expect(spySendLog).toHaveBeenCalledWith('stepId', {
- time: mockedDate,
- level: 'WARN',
- message: "This step is marked as 'pending'",
- });
- });
-
- test('should set not_implemented status for step and failed for scenario in case of pending result status', function() {
- event.result.status = STATUSES.PENDING;
- formatter.contextState.context.stepId = 'stepId';
- formatter.onTestStepFinished(event);
-
- expect(formatter.contextState.context.stepStatus).toBe(STATUSES.NOT_IMPLEMENTED);
- expect(formatter.contextState.context.scenarioStatus).toBe(STATUSES.FAILED);
- expect(spyCountFailedScenarios).toHaveBeenCalledWith(event.testCase.sourceLocation.uri);
- });
-
- test('should call sendLog method from RPClient with ERROR level in case of undefined result status', function() {
- event.result.status = STATUSES.UNDEFINED;
- formatter.contextState.context.stepId = 'stepId';
- formatter.onTestStepFinished(event);
-
- const spySendLog = jest.spyOn(formatter.reportportal, 'sendLog');
-
- expect(spySendLog).toHaveBeenCalledWith('stepId', {
- time: mockedDate,
- level: 'ERROR',
- message: 'There is no step definition found. Please verify and implement it.',
- });
- });
-
- test('should set not_found status for step and failed for scenario in case of undefined result status', function() {
- event.result.status = STATUSES.UNDEFINED;
- formatter.contextState.context.stepId = 'stepId';
- formatter.onTestStepFinished(event);
-
- expect(formatter.contextState.context.stepStatus).toBe(STATUSES.NOT_FOUND);
- expect(formatter.contextState.context.scenarioStatus).toBe(STATUSES.FAILED);
- expect(spyCountFailedScenarios).toHaveBeenCalledWith(event.testCase.sourceLocation.uri);
- });
-
- test('should call sendLog method from RPClient with ERROR level in case of ambiguous result status', function() {
- event.result.status = STATUSES.AMBIGUOUS;
- formatter.contextState.context.stepId = 'stepId';
- formatter.onTestStepFinished(event);
-
- const spySendLog = jest.spyOn(formatter.reportportal, 'sendLog');
-
- expect(spySendLog).toHaveBeenCalledWith('stepId', {
- time: mockedDate,
- level: 'ERROR',
- message: 'There are more than one step implementation. Please verify and reimplement it.',
- });
- });
-
- test('should set not_found status for step and failed for scenario in case of ambiguous result status', function() {
- event.result.status = STATUSES.AMBIGUOUS;
- formatter.contextState.context.stepId = 'stepId';
- formatter.onTestStepFinished(event);
-
- expect(formatter.contextState.context.stepStatus).toBe(STATUSES.NOT_FOUND);
- expect(formatter.contextState.context.scenarioStatus).toBe(STATUSES.FAILED);
- expect(spyCountFailedScenarios).toHaveBeenCalledWith(event.testCase.sourceLocation.uri);
- });
-
- test('should set skipped status for step in case of skipped result status', function() {
- event.result.status = STATUSES.SKIPPED;
- formatter.onTestStepFinished(event);
-
- expect(formatter.contextState.context.stepStatus).toBe(STATUSES.SKIPPED);
- });
-
- test('should set skipped status for scenario if it was failed in case of skipped result status', function() {
- event.result.status = STATUSES.SKIPPED;
- formatter.contextState.context.scenarioStatus = STATUSES.FAILED;
- formatter.onTestStepFinished(event);
-
- expect(formatter.contextState.context.scenarioStatus).toBe(STATUSES.SKIPPED);
- });
-
- test('should call sendLog method from RPClient with ERROR level in case of failed result status', function() {
- event.result.status = STATUSES.FAILED;
- event.result.exception = 255;
- const stepDefinitionMock = {
- uri: 'stepDefinition',
- };
-
- formatter.contextState.context.stepDefinition = stepDefinitionMock;
- formatter.contextState.context.stepId = 'stepId';
- formatter.onTestStepFinished(event);
-
- const spySendLog = jest.spyOn(formatter.reportportal, 'sendLog');
-
- expect(spySendLog).toHaveBeenCalledWith('stepId', {
- time: mockedDate,
- level: 'ERROR',
- message: `${stepDefinitionMock.uri}\n 255`,
- });
- });
-
- test('should set failed status for step in case of failed result status', function() {
- event.result.status = STATUSES.FAILED;
- formatter.contextState.context.stepDefinition = {
- uri: 'stepDefinition',
- };
-
- formatter.onTestStepFinished(event);
-
- expect(formatter.contextState.context.stepStatus).toBe(STATUSES.FAILED);
- expect(spyCountFailedScenarios).toHaveBeenCalledWith(event.testCase.sourceLocation.uri);
- });
-
- test('should call finishTestItem method from RPClient', function() {
- event.result.status = STATUSES.PASSED;
- formatter.contextState.context.stepId = 'stepId';
-
- const itemFinishObj = {
- status: STATUSES.PASSED,
- endTime: mockedDate,
- };
-
- const spyStartTestItem = jest.spyOn(formatter.reportportal, 'finishTestItem');
-
- formatter.onTestStepFinished(event);
-
- expect(spyStartTestItem).toHaveBeenCalledWith('stepId', itemFinishObj);
- });
-
- test('should call finishTestItem method from RPClient with ab001 issue in case of not_found step status', function() {
- event.result.status = STATUSES.UNDEFINED;
- formatter.contextState.context.stepId = 'stepId';
-
- const itemFinishObj = {
- status: STATUSES.FAILED,
- endTime: mockedDate,
- issue: {
- issueType: 'ab001',
- comment: 'STEP DEFINITION WAS NOT FOUND',
- },
- };
-
- const spyStartTestItem = jest.spyOn(formatter.reportportal, 'finishTestItem');
-
- formatter.onTestStepFinished(event);
-
- expect(spyStartTestItem).toHaveBeenCalledWith('stepId', itemFinishObj);
- });
-
- test('should call finishTestItem method from RPClient with ti001 issue in case of not_implemented step status', function() {
- event.result.status = STATUSES.PENDING;
- formatter.contextState.context.stepId = 'stepId';
-
- const itemFinishObj = {
- status: STATUSES.SKIPPED,
- endTime: mockedDate,
- issue: {
- issueType: 'ti001',
- comment: 'STEP IS PENDING IMPLEMENTATION',
- },
- };
-
- const spyStartTestItem = jest.spyOn(formatter.reportportal, 'finishTestItem');
-
- formatter.onTestStepFinished(event);
-
- expect(spyStartTestItem).toHaveBeenCalledWith('stepId', itemFinishObj);
- });
-
- test('should not call finishTestItem method and stop function execution', function() {
- formatter.contextState.context.stepSourceLocation = {
- actionLocation: {
- uri: `uri: ${AFTER_HOOK_URI_TO_SKIP}`,
- },
- };
-
- const spyFinishTestItem = jest.spyOn(formatter.reportportal, 'finishTestItem');
- const spySendLog = jest.spyOn(formatter.reportportal, 'sendLog');
-
- formatter.onTestStepFinished(event);
-
- expect(spyGetFileName).toHaveBeenCalledTimes(0);
- expect(spyCountFailedScenarios).toHaveBeenCalledTimes(0);
- expect(spySendLog).toHaveBeenCalledTimes(0);
- expect(spyFinishTestItem).toHaveBeenCalledTimes(0);
- });
- });
-
- describe('onTestStepAttachment', () => {
- const mockFileObj = {
- level: 'INFO',
- message: 'file',
- data: 'string',
- };
- const event = {
- data: [
- {
- item: 'text',
- },
- ],
- };
-
- const spyGetJSON = jest.spyOn(utils, 'getJSON');
- let spyGetFileName;
-
- beforeEach(() => {
- spyGetFileName = jest
- .spyOn(formatter.contextState, 'getFileName')
- .mockImplementation(() => 'fileName');
-
- formatter.contextState.context.stepStatus = STATUSES.PASSED;
- formatter.contextState.context.stepId = 'stepId';
- });
-
- test('should call spyGetFileName to get name for file', function() {
- event.media = {
- type: 'text/plain',
- };
- spyGetJSON.mockImplementationOnce(() => mockFileObj);
- formatter.onTestStepAttachment(event);
-
- expect(spyGetFileName).toHaveBeenCalledTimes(1);
- });
-
- test('should call sendLog method from RPClient to send log with attachment for text media type', function() {
- event.media = {
- type: 'text/plain',
- };
- spyGetJSON.mockImplementationOnce(() => mockFileObj);
-
- const request = {
- level: mockFileObj.level,
- message: mockFileObj.message,
- time: mockedDate,
- };
-
- const spySendLog = jest.spyOn(formatter.reportportal, 'sendLog');
-
- formatter.onTestStepAttachment(event);
-
- expect(spySendLog).toHaveBeenCalledWith('stepId', request);
- });
-
- test('should call sendLog method from RPClient with DEBUG level log in case of invalid json data for text media type', function() {
- event.media = {
- type: 'text/plain',
- };
- spyGetJSON.mockImplementationOnce(() => false);
-
- const request = {
- level: 'DEBUG',
- message: event.data,
- time: mockedDate,
- };
-
- const spySendLog = jest.spyOn(formatter.reportportal, 'sendLog');
-
- formatter.onTestStepAttachment(event);
-
- expect(spySendLog).toHaveBeenCalledWith('stepId', request);
- });
-
- test('should call sendLog method from RPClient to send log with attachment for other media type', function() {
- event.media = {
- type: 'other',
- };
- spyGetJSON.mockImplementationOnce(() => mockFileObj);
-
- const request = {
- level: mockFileObj.level,
- message: mockFileObj.message,
- time: mockedDate,
- file: {
- name: mockFileObj.message,
- },
- };
- const fileObj = {
- name: 'fileName',
- type: 'other',
- content: mockFileObj.data,
- };
-
- const spySendLog = jest.spyOn(formatter.reportportal, 'sendLog');
-
- formatter.onTestStepAttachment(event);
-
- expect(spySendLog).toHaveBeenCalledWith('stepId', request, fileObj);
- });
-
- test('should call sendLog method from RPClient with default parameters in case of invalid json data for other media type', function() {
- event.media = {
- type: 'other',
- };
- spyGetJSON.mockImplementationOnce(() => false);
-
- const request = {
- level: 'DEBUG',
- message: 'fileName',
- time: mockedDate,
- file: {
- name: 'fileName',
- },
- };
- const fileObj = {
- name: 'fileName',
- type: 'other',
- content: event.data,
- };
-
- const spySendLog = jest.spyOn(formatter.reportportal, 'sendLog');
-
- formatter.onTestStepAttachment(event);
-
- expect(spySendLog).toHaveBeenCalledWith('stepId', request, fileObj);
- });
- });
-
- describe('onTestCaseFinished', () => {
- const itemUri = 'itemUri';
- const event = {
- sourceLocation: {
- uri: itemUri,
- },
- result: {
- status: STATUSES.PASSED,
- },
- };
-
- let spyFinishTestItem;
-
- beforeEach(() => {
- spyFinishTestItem = jest.spyOn(formatter.reportportal, 'finishTestItem');
-
- formatter.isScenarioBasedStatistics = false;
- formatter.contextState.context.scenarioId = 'scenarioId';
- formatter.contextState.context.scenariosCount[itemUri] = {
- done: 0,
- total: 2,
- };
-
- formatter.documentsStorage.pickleDocuments[itemUri] = {
- featureId: 'featureId',
- };
- });
-
- test('should call finishTestItem method from RPClient to finish test item', function() {
- formatter.onTestCaseFinished(event);
-
- const itemFinishObj = {
- status: STATUSES.PASSED,
- endTime: mockedDate,
- };
-
- expect(spyFinishTestItem).toHaveBeenCalledWith('scenarioId', itemFinishObj);
- expect(formatter.contextState.context.scenarioStatus).toBe(STATUSES.FAILED);
- expect(formatter.contextState.context.scenarioId).toBe(null);
- });
-
- test('should call finishTestItem method from RPClient and finish test item with failed status in case of result status not passed', function() {
- event.result.status = STATUSES.SKIPPED;
- formatter.onTestCaseFinished(event);
-
- const itemFinishObj = {
- status: STATUSES.FAILED,
- endTime: mockedDate,
- };
-
- expect(spyFinishTestItem).toHaveBeenCalledWith('scenarioId', itemFinishObj);
- expect(formatter.contextState.context.scenarioStatus).toBe(STATUSES.FAILED);
- expect(formatter.contextState.context.scenarioId).toBe(null);
- });
-
- test('should increase scenariosCount done in context', function() {
- event.result.status = STATUSES.PASSED;
- formatter.onTestCaseFinished(event);
-
- expect(formatter.contextState.context.scenariosCount[itemUri].done).toBe(1);
- });
-
- test('should call finishTestItem method from RPClient twice in case of finishing the item from suite', function() {
- formatter.contextState.context.scenariosCount[itemUri].total = 1;
- formatter.contextState.context.failedScenarios[itemUri] = 0;
-
- event.result.status = STATUSES.PASSED;
- formatter.onTestCaseFinished(event);
-
- const finishItemObj = {
- status: STATUSES.PASSED,
- endTime: mockedDate,
- };
-
- expect(spyFinishTestItem).toHaveBeenCalledTimes(2);
- expect(spyFinishTestItem).toHaveBeenNthCalledWith(2, 'featureId', finishItemObj);
- });
-
- test('should call finishTestItem method from RPClient second time with failed status in case of failed scenarios in suite', function() {
- formatter.contextState.context.scenariosCount[itemUri].total = 1;
- formatter.contextState.context.failedScenarios[itemUri] = 1;
-
- event.result.status = STATUSES.PASSED;
- formatter.onTestCaseFinished(event);
-
- const finishItemObj = {
- status: STATUSES.FAILED,
- endTime: mockedDate,
- };
-
- expect(spyFinishTestItem).toHaveBeenCalledTimes(2);
- expect(spyFinishTestItem).toHaveBeenNthCalledWith(2, 'featureId', finishItemObj);
- });
-
- test('should not call finishTestItem method and stop function execution', function() {
- formatter.isScenarioBasedStatistics = false;
- event.result.retried = true;
-
- formatter.onTestCaseFinished(event);
-
- expect(spyFinishTestItem).toHaveBeenCalledTimes(0);
- });
- });
-
- describe('onTestRunFinished', () => {
- let spyGetPromiseFinishAllItems;
- let spyFinishLaunch;
-
- beforeEach(() => {
- spyGetPromiseFinishAllItems = jest.spyOn(formatter.reportportal, 'getPromiseFinishAllItems');
- spyFinishLaunch = jest.spyOn(formatter.reportportal, 'finishLaunch');
- });
-
- test('should call getPromiseFinishAllItems method from RPClient & should not call finishLaunch method for empty launch id', function() {
- formatter.contextState.context.launchId = null;
-
- formatter.onTestRunFinished();
-
- expect(spyGetPromiseFinishAllItems).toHaveBeenCalledWith(null);
- expect(spyFinishLaunch).toHaveBeenCalledTimes(0);
- });
-
- test('should call getPromiseFinishAllItems method from RPClient and should finish launch with corresponding id', async function() {
- formatter.contextState.context.launchId = 'launchId';
- await formatter.onTestRunFinished();
-
- expect(spyGetPromiseFinishAllItems).toHaveBeenCalledWith('launchId');
- expect(spyFinishLaunch).toHaveBeenCalledWith('launchId', { endTime: mockedDate });
- });
-
- test('should call resetContext method from context after finishing launch', async function() {
- formatter.contextState.context.launchId = 'launchId';
- const spyResetContext = jest.spyOn(formatter.contextState, 'resetContext');
-
- await formatter.onTestRunFinished();
-
- expect(spyResetContext).toHaveBeenCalledTimes(1);
- });
- });
-});
diff --git a/modules/api/deprecated/documents-storage.js b/modules/api/deprecated/documents-storage.js
deleted file mode 100644
index 2600bba..0000000
--- a/modules/api/deprecated/documents-storage.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-class DocumentsStorage {
- constructor() {
- this.gherkinDocuments = {};
- this.featureData = {};
- }
-
- cacheDocument(gherkinDocument) {
- this.gherkinDocuments[gherkinDocument.uri] = gherkinDocument.document;
- }
-
- createCachedFeature(uri) {
- this.featureData[uri] = {};
- }
-
- isFeatureDataCached(uri) {
- return !!this.featureData[uri];
- }
-}
-
-module.exports = DocumentsStorage;
diff --git a/modules/api/deprecated/index.js b/modules/api/deprecated/index.js
deleted file mode 100644
index 565a689..0000000
--- a/modules/api/deprecated/index.js
+++ /dev/null
@@ -1,568 +0,0 @@
-/*
- * Copyright 2022 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.
- */
-
-const stripAnsi = require('strip-ansi');
-const Table = require('cli-table3');
-const utils = require('./utils');
-const itemFinders = require('./itemFinders');
-const {
- STATUSES,
- AFTER_HOOK_URI_TO_SKIP,
- TABLE_CONFIG,
- RP_EVENTS,
- RP_ENTITY_LAUNCH,
- LOG_LEVELS,
- CUCUMBER_EVENTS,
-} = require('../../constants');
-const Context = require('./context');
-const DocumentStorage = require('./documents-storage');
-const pjson = require('../../../package.json');
-
-module.exports = {
- init() {
- this.context = new Context();
- this.documentsStorage = new DocumentStorage();
-
- this.options.eventBroadcaster.on(CUCUMBER_EVENTS.GHERKIN_DOCUMENT, (event) =>
- this.onGherkinDocument(event),
- );
- this.options.eventBroadcaster.on(CUCUMBER_EVENTS.PICKLE_ACCEPTED, (event) =>
- this.onPickleAccepted(event),
- );
- this.options.eventBroadcaster.on(CUCUMBER_EVENTS.TEST_CASE_PREPARED, (event) =>
- this.onTestCasePrepared(event),
- );
- this.options.eventBroadcaster.on(CUCUMBER_EVENTS.TEST_CASE_STARTED, (event) =>
- this.onTestCaseStarted(event),
- );
- this.options.eventBroadcaster.on(CUCUMBER_EVENTS.TEST_STEP_STARTED, (event) =>
- this.onTestStepStarted(event),
- );
- this.options.eventBroadcaster.on(CUCUMBER_EVENTS.TEST_STEP_FINISHED, (event) =>
- this.onTestStepFinished(event),
- );
- this.options.eventBroadcaster.on(CUCUMBER_EVENTS.TEST_STEP_ATTACHMENT, (event) =>
- this.onTestStepAttachment(event),
- );
- this.options.eventBroadcaster.on(CUCUMBER_EVENTS.TEST_CASE_FINISHED, (event) =>
- this.onTestCaseFinished(event),
- );
- this.options.eventBroadcaster.on(CUCUMBER_EVENTS.TEST_RUN_FINISHED, (event) =>
- this.onTestRunFinished(event),
- );
- },
- onGherkinDocument(event) {
- this.documentsStorage.cacheDocument(event);
-
- // BeforeFeatures
- if (!this.context.launchId) {
- const launch = this.reportportal.startLaunch({
- name: this.config.launch,
- startTime: this.reportportal.helpers.now(),
- description: !this.config.description ? '' : this.config.description,
- attributes: [
- ...(this.config.attributes || []),
- { key: 'agent', value: `${pjson.name}|${pjson.version}`, system: true },
- ],
- rerun: this.isRerun,
- rerunOf: this.rerunOf,
- });
- this.context.launchId = launch.tempId;
- }
- },
- onPickleAccepted(event) {
- const featureUri = utils.getUri(event.uri);
- if (!this.documentsStorage.isFeatureDataCached(featureUri)) {
- this.documentsStorage.createCachedFeature(featureUri);
-
- const feature = this.documentsStorage.featureData[featureUri];
- const featureDocument = itemFinders.findFeature(
- this.documentsStorage.gherkinDocuments,
- event,
- );
- feature.description = featureDocument.description || featureUri;
- const { name } = featureDocument;
- feature.name = name;
- feature.itemAttributes = utils.createAttributes(featureDocument.tags);
- }
- },
- onTestCasePrepared(event) {
- const featureUri = utils.getUri(event.sourceLocation.uri);
- const feature = this.documentsStorage.featureData[featureUri];
- // If this is the first scenario in the feature, start the feature in RP
- if (!feature.featureId) {
- feature.featureId = this.reportportal.startTestItem(
- {
- name: feature.name,
- startTime: this.reportportal.helpers.now(),
- type: this.isScenarioBasedStatistics ? 'TEST' : 'SUITE',
- codeRef: utils.formatCodeRef(featureUri, feature.name),
- description: feature.description,
- attributes: feature.itemAttributes,
- },
- this.context.launchId,
- ).tempId;
- }
- // If this is the first feature in the run, set the currentFeatureUri
- if (!this.context.currentFeatureUri) {
- this.context.currentFeatureUri = featureUri;
- }
- // If this is a new feature, finish the previous feature in RP.
- // does not work for the final feature in the run. that is finished in onTestRunFinished
- if (this.context.currentFeatureUri !== featureUri) {
- const previousFeature = this.documentsStorage.featureData[this.context.currentFeatureUri];
- // If this is a new feature, finish the previous feature
- this.reportportal.finishTestItem(previousFeature.featureId, {
- status: previousFeature.featureStatus,
- endTime: this.reportportal.helpers.now(),
- });
- // Now that the previous feature is finished, assign the new current feature
- this.context.currentFeatureUri = featureUri;
- }
- this.context.stepDefinitions = event;
- let hookType = 'Before';
- this.context.stepDefinitions.steps.forEach((step) => {
- if (step.sourceLocation) {
- hookType = 'After';
- return;
- }
- // eslint-disable-next-line no-param-reassign
- step.hookType = hookType;
- });
- },
- onTestCaseStarted(event) {
- const featureDocument = itemFinders.findFeature(
- this.documentsStorage.gherkinDocuments,
- event.sourceLocation,
- );
- this.context.scenario = itemFinders.findScenario(
- this.documentsStorage.gherkinDocuments,
- event.sourceLocation,
- );
- this.context.scenarioStatus = STATUSES.STARTED;
- this.context.background = itemFinders.findBackground(featureDocument);
- const featureTags = featureDocument.tags;
- const keyword = this.context.scenario.keyword
- ? this.context.scenario.keyword
- : this.context.scenario.type;
- let name = [keyword, this.context.scenario.name].join(': ');
- const eventTags = this.context.scenario.tags
- ? this.context.scenario.tags.filter(
- (tag) => !featureTags.find(utils.createTagComparator(tag)),
- )
- : [];
- const itemAttributes = utils.createAttributes(eventTags);
- const description =
- this.context.scenario.description ||
- [utils.getUri(event.sourceLocation.uri), event.sourceLocation.line].join(':');
- const { featureId } = this.documentsStorage.featureData[event.sourceLocation.uri];
-
- if (this.context.lastScenarioDescription !== name) {
- this.context.lastScenarioDescription = name;
- this.context.outlineRow = 0;
- } else if (event.attemptNumber < 2) {
- this.context.outlineRow += 1;
- name += ` [${this.context.outlineRow}]`;
- }
-
- // BeforeScenario
- if (this.isScenarioBasedStatistics || event.attemptNumber < 2) {
- this.context.scenarioId = this.reportportal.startTestItem(
- {
- name,
- startTime: this.reportportal.helpers.now(),
- type: this.isScenarioBasedStatistics ? 'STEP' : 'TEST',
- description,
- codeRef: utils.formatCodeRef(event.sourceLocation.uri, name),
- parameters: this.context.scenario.parameters,
- attributes: itemAttributes,
- retry: this.isScenarioBasedStatistics && event.attemptNumber > 1,
- },
- this.context.launchId,
- featureId,
- ).tempId;
- }
- },
- onTestStepStarted(event) {
- this.context.stepStatus = STATUSES.FAILED;
- this.context.stepId = null;
-
- this.context.stepSourceLocation = this.context.stepDefinitions.steps[event.index];
-
- // skip After Hook added by protractor-cucumber-framework
- if (
- !this.context.stepSourceLocation.sourceLocation &&
- this.context.stepSourceLocation.actionLocation.uri.includes(AFTER_HOOK_URI_TO_SKIP)
- )
- return;
-
- this.context.step = this.context.findStep(event);
- this.context.stepDefinition = itemFinders.findStepDefinition(this.context, event);
-
- let description;
- let name = this.context.step.text
- ? `${this.context.step.keyword} ${this.context.step.text}`
- : this.context.step.keyword;
-
- if (this.context.step.argument) {
- let stepArguments;
- if (this.context.step.argument.content) {
- stepArguments = `"""\n${this.context.step.argument.content}\n"""`;
- }
-
- if (this.context.step.argument.rows) {
- const rows = this.context.step.argument.rows.map((row) =>
- row.cells.map((cell) => {
- // Added an if statement to only replace step parameters if this is a Scenario Outline
- let tempStepValue = cell.value;
- if (this.context.scenario.parameters) {
- this.context.scenario.parameters.forEach((parameter) => {
- if (cell.value.includes(`<${parameter.key}>`)) {
- tempStepValue = utils.replaceParameter(
- cell.value,
- parameter.key,
- parameter.value,
- );
- }
- });
- }
- return tempStepValue;
- }),
- );
- const datatable = new Table(TABLE_CONFIG);
- datatable.push(...rows);
- stepArguments = datatable.toString();
- }
- if (this.isScenarioBasedStatistics) {
- name += `\n${stepArguments}`;
- } else {
- description = stepArguments;
- }
- }
-
- let type = 'STEP';
- let isHook = false;
- if (this.context.step.keyword === 'Before') {
- type = 'BEFORE_TEST';
- isHook = true;
- } else if (this.context.step.keyword === 'After') {
- type = 'AFTER_TEST';
- isHook = true;
- }
-
- // hooks are described in cucumber's library core
- const codeRef =
- this.context.stepDefinition && !isHook
- ? utils.formatCodeRef(this.context.stepDefinition.uri, name)
- : undefined;
-
- this.context.stepId = this.reportportal.startTestItem(
- {
- name,
- description,
- startTime: this.reportportal.helpers.now(),
- type,
- codeRef,
- parameters: this.context.step.parameters,
- hasStats: !this.isScenarioBasedStatistics,
- retry: !this.isScenarioBasedStatistics && event.testCase.attemptNumber > 1,
- },
- this.context.launchId,
- this.context.scenarioId,
- ).tempId;
- },
- onTestStepFinished(event) {
- // skip After Hook added by protractor-cucumber-framework
- if (
- !this.context.stepSourceLocation.sourceLocation &&
- this.context.stepSourceLocation.actionLocation.uri.includes(AFTER_HOOK_URI_TO_SKIP)
- )
- return;
-
- // StepResult
- const sceenshotName = this.context.getFileName();
-
- switch (event.result.status) {
- case STATUSES.PASSED: {
- this.context.stepStatus = STATUSES.PASSED;
- if (this.context.scenarioStatus !== STATUSES.FAILED) {
- this.context.scenarioStatus = STATUSES.PASSED;
- }
- break;
- }
- case STATUSES.PENDING: {
- this.reportportal.sendLog(this.context.stepId, {
- time: this.reportportal.helpers.now(),
- level: 'WARN',
- message: "This step is marked as 'pending'",
- });
- this.context.stepStatus = STATUSES.NOT_IMPLEMENTED;
- this.context.scenarioStatus = STATUSES.FAILED;
- this.context.incrementFailedScenariosCount(event.testCase.sourceLocation.uri);
- break;
- }
- case STATUSES.UNDEFINED: {
- this.reportportal.sendLog(this.context.stepId, {
- time: this.reportportal.helpers.now(),
- level: 'ERROR',
- message: 'There is no step definition found. Please verify and implement it.',
- });
- this.context.stepStatus = STATUSES.NOT_FOUND;
- this.context.scenarioStatus = STATUSES.FAILED;
- this.context.incrementFailedScenariosCount(event.testCase.sourceLocation.uri);
- break;
- }
- case STATUSES.AMBIGUOUS: {
- this.reportportal.sendLog(this.context.stepId, {
- time: this.reportportal.helpers.now(),
- level: 'ERROR',
- message: 'There are more than one step implementation. Please verify and reimplement it.',
- });
- this.context.stepStatus = STATUSES.NOT_FOUND;
- this.context.scenarioStatus = STATUSES.FAILED;
- this.context.incrementFailedScenariosCount(event.testCase.sourceLocation.uri);
- break;
- }
- case STATUSES.SKIPPED: {
- this.context.stepStatus = STATUSES.SKIPPED;
- if (this.context.scenarioStatus === STATUSES.FAILED) {
- this.context.scenarioStatus = STATUSES.SKIPPED;
- }
-
- if (
- this.context.scenarioStatus === STATUSES.STARTED ||
- this.context.scenarioStatus === STATUSES.PASSED
- ) {
- this.context.scenarioStatus = STATUSES.SKIPPED;
- } else {
- this.context.scenarioStatus = STATUSES.FAILED;
- if (
- // eslint-disable-next-line no-prototype-builtins
- this.config.hasOwnProperty('reportSkippedCucumberStepsOnFailedTest') &&
- !this.config.reportSkippedCucumberStepsOnFailedTest
- ) {
- this.context.stepStatus = STATUSES.CANCELLED;
- }
- }
-
- break;
- }
- case STATUSES.FAILED: {
- this.context.stepStatus = STATUSES.FAILED;
- this.context.scenarioStatus = STATUSES.FAILED;
- this.context.incrementFailedScenariosCount(event.testCase.sourceLocation.uri);
- const errorMessage = stripAnsi(
- `${this.context.stepDefinition.uri}\n ${event.result.exception.toString()}`,
- );
- this.reportportal.sendLog(this.context.stepId, {
- time: this.reportportal.helpers.now(),
- level: 'ERROR',
- message: errorMessage,
- });
- if (
- global.browser &&
- this.config.takeScreenshot &&
- this.config.takeScreenshot === 'onFailure'
- ) {
- const request = {
- time: this.reportportal.helpers.now(),
- level: 'ERROR',
- file: { name: sceenshotName },
- message: sceenshotName,
- };
- global.browser.takeScreenshot().then((png) => {
- const fileObj = {
- name: sceenshotName,
- type: 'image/png',
- content: png,
- };
- this.reportportal.sendLog(this.context.stepId, request, fileObj);
- });
- }
- break;
- }
- default:
- break;
- }
-
- const itemParams = this.context.itemsParams[this.context.stepId];
-
- // AfterStep
- const request = {
- status: this.context.stepStatus,
- endTime: this.reportportal.helpers.now(),
- ...itemParams,
- };
- if (request.status === STATUSES.NOT_FOUND) {
- request.status = STATUSES.FAILED;
- request.issue = {
- issueType: 'ab001',
- comment: 'STEP DEFINITION WAS NOT FOUND',
- };
- } else if (request.status === STATUSES.NOT_IMPLEMENTED) {
- request.status = STATUSES.SKIPPED;
- request.issue = {
- issueType: 'ti001',
- comment: 'STEP IS PENDING IMPLEMENTATION',
- };
- }
-
- this.reportportal.finishTestItem(this.context.stepId, request);
- },
-
- updateItemParams(id, newParams) {
- this.context.itemsParams[id] = {
- ...this.context.itemsParams[id],
- ...newParams,
- };
- },
- getItemParams(id) {
- return this.context.itemsParams[id] || {};
- },
- onTestStepAttachment(event) {
- const fileName = this.context.getFileName();
- if (
- event.data &&
- event.data.length &&
- (this.context.stepStatus === STATUSES.PASSED || this.context.stepStatus === STATUSES.FAILED)
- ) {
- const dataObj = utils.getJSON(event.data);
- let itemId = this.context.stepId;
-
- switch (event.media.type) {
- case RP_EVENTS.TEST_CASE_ID: {
- this.updateItemParams(itemId, { testCaseId: dataObj.testCaseId });
- break;
- }
- case RP_EVENTS.ATTRIBUTES: {
- const savedAttributes = this.getItemParams(itemId).attributes || [];
- this.updateItemParams(itemId, {
- attributes: savedAttributes.concat(dataObj.attributes),
- });
- break;
- }
- case RP_EVENTS.DESCRIPTION: {
- const savedDescription = this.getItemParams(itemId).description || '';
- this.updateItemParams(itemId, {
- description: savedDescription
- ? `${savedDescription}
${dataObj.description}`
- : dataObj.description,
- });
- break;
- }
- case RP_EVENTS.STATUS: {
- if (dataObj.entity !== RP_ENTITY_LAUNCH) {
- this.updateItemParams(itemId, {
- status: dataObj.status,
- });
- } else {
- this.context.launchStatus = dataObj.status;
- }
- break;
- }
- case 'text/plain': {
- const request = {
- time: this.reportportal.helpers.now(),
- };
- if (dataObj) {
- request.level = dataObj.level;
- request.message = dataObj.message;
- if (dataObj.entity === RP_ENTITY_LAUNCH) {
- itemId = this.context.launchId;
- }
- } else {
- request.level = LOG_LEVELS.DEBUG;
- request.message = event.data;
- }
- this.reportportal.sendLog(itemId, request);
- break;
- }
- default: {
- const request = {
- time: this.reportportal.helpers.now(),
- level:
- this.context.stepStatus === STATUSES.PASSED ? LOG_LEVELS.DEBUG : LOG_LEVELS.ERROR,
- message: fileName,
- file: {
- name: fileName,
- },
- };
- if (dataObj) {
- request.level = dataObj.level;
- request.message = dataObj.message;
- request.file.name = dataObj.message;
- if (dataObj.entity === RP_ENTITY_LAUNCH) {
- itemId = this.context.launchId;
- }
- }
- const fileObj = {
- name: fileName,
- type: event.media.type,
- content: (dataObj && dataObj.data) || event.data,
- };
- this.reportportal.sendLog(itemId, request, fileObj);
- break;
- }
- }
- }
- },
- onTestCaseFinished(event) {
- if (!this.isScenarioBasedStatistics && event.result.retried) {
- return;
- }
- const isFailed = event.result.status !== STATUSES.PASSED;
- // ScenarioResult
- this.reportportal.finishTestItem(this.context.scenarioId, {
- status: isFailed ? STATUSES.FAILED : STATUSES.PASSED,
- endTime: this.reportportal.helpers.now(),
- });
- this.context.scenarioId = null;
- const featureUri = event.sourceLocation.uri;
-
- this.documentsStorage.featureData[featureUri].featureStatus =
- this.context.failedScenarios[featureUri] > 0 ? STATUSES.FAILED : STATUSES.PASSED;
- },
- onTestRunFinished(event) {
- // Finish the final feature in the run
- const finalFeature = this.documentsStorage.featureData[this.context.currentFeatureUri];
- this.reportportal.finishTestItem(finalFeature.featureId, {
- status: finalFeature.featureStatus,
- endTime: this.reportportal.helpers.now(),
- });
- // AfterFeatures
- const promise = this.reportportal.getPromiseFinishAllItems(this.context.launchId);
- return promise.then(() => {
- if (this.context.launchId) {
- const finishLaunchRQ = {
- endTime: this.reportportal.helpers.now(),
- status: event.result.success ? STATUSES.PASSED : STATUSES.FAILED,
- };
-
- if (this.context.launchStatus) {
- finishLaunchRQ.status = this.context.launchStatus;
- }
-
- const launchFinishPromise = this.reportportal.finishLaunch(
- this.context.launchId,
- finishLaunchRQ,
- ).promise;
- launchFinishPromise.then(() => {
- this.context.resetContext();
- });
- }
- });
- },
-};
diff --git a/modules/api/deprecated/itemFinders.js b/modules/api/deprecated/itemFinders.js
deleted file mode 100644
index e7aa021..0000000
--- a/modules/api/deprecated/itemFinders.js
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-const utils = require('./utils');
-
-function createSteps(header, row, steps) {
- return steps.map((step) => {
- const modified = { ...step, parameters: [] };
-
- header.cells.forEach((variable, index) => {
- const isParameterPresents = modified.text.indexOf(`<${variable.value}>`) !== -1;
- modified.text = utils.replaceParameter(modified.text, variable.value, row.cells[index].value);
-
- if (isParameterPresents) {
- modified.parameters.push({ key: variable.value, value: row.cells[index].value });
- }
- });
-
- return modified;
- });
-}
-
-function createScenarioFromOutlineExample(outline, example, row) {
- const parameters = utils.getParameters(example.tableHeader, row);
- let outlineName = outline.name;
-
- parameters.forEach((param) => {
- outlineName = utils.replaceParameter(outlineName, param.key, param.value);
- });
-
- return {
- type: 'Scenario',
- tags: example.tags,
- location: row.location,
- keyword: 'Scenario',
- name: outlineName,
- steps: createSteps(example.tableHeader, row, outline.steps),
- parameters,
- description: outline.description,
- };
-}
-
-function createScenarioFromOutline(outline, location) {
- let foundRow;
- const foundExample = outline.examples.find((example) => {
- foundRow = example.tableBody.find((row) => row.location.line === location.line);
-
- return !!foundRow;
- });
-
- if (!foundRow) return null;
-
- return createScenarioFromOutlineExample(outline, foundExample, foundRow);
-}
-
-function findOutlineScenario(outlines, location) {
- return outlines
- .map((child) => createScenarioFromOutline(child, location))
- .find((outline) => !!outline);
-}
-
-function findBackground(feature) {
- return feature.children ? feature.children.find((child) => child.type === 'Background') : null;
-}
-
-function findFeature(documents, location) {
- return documents[location.uri].feature;
-}
-
-function findScenario(documents, location) {
- const { children } = findFeature(documents, location);
- const scenario = children.find(
- (child) => child.type === 'Scenario' && child.location.line === location.line,
- );
- if (scenario) {
- return scenario;
- }
-
- const outlines = children.filter((child) => child.type === 'ScenarioOutline');
- return findOutlineScenario(outlines, location);
-}
-
-function findStepDefinition(context, event) {
- return context.stepDefinitions.steps[event.index].actionLocation;
-}
-
-module.exports = {
- findBackground,
- findFeature,
- findScenario,
- findStepDefinition,
-};
diff --git a/modules/api/deprecated/utils.js b/modules/api/deprecated/utils.js
deleted file mode 100644
index a3459a4..0000000
--- a/modules/api/deprecated/utils.js
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2022 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.
- */
-
-const path = require('path');
-const commonUtils = require('../../utils');
-
-const getUri = (uri) => uri.replace(process.cwd() + path.sep, '');
-
-const createTagComparator = (tagA) => (tagB) =>
- tagB.name === tagA.name &&
- tagB.location.line === tagA.location.line &&
- tagB.location.column === tagA.location.column;
-
-const getParameters = (header, body) => {
- const keys = header ? header.cells.map((cell) => cell.value) : [];
-
- if (Array.isArray(body)) {
- return body.reduce((acc, item) => {
- const params = item.cells.map((cell, index) => ({
- key: keys[index],
- value: cell.value,
- }));
-
- return acc.concat(params);
- }, []);
- }
-
- return body.cells.map((cell, index) => ({
- key: keys[index],
- value: cell.value,
- }));
-};
-
-function replaceParameter(originalString, name, value) {
- return originalString.replace(new RegExp(`<${name}>`, 'g'), value);
-}
-
-const getStepType = (keyword) => {
- let type;
-
- switch (keyword) {
- case 'Before':
- type = 'BEFORE_TEST';
- break;
- case 'After':
- type = 'AFTER_TEST';
- break;
- default:
- type = 'STEP';
- break;
- }
-
- return type;
-};
-
-module.exports = {
- createTagComparator,
- getUri,
- createAttributes: commonUtils.createAttributes,
- getJSON: commonUtils.getJSON,
- getStepType,
- getParameters,
- formatCodeRef: commonUtils.formatCodeRef,
- replaceParameter,
-};
diff --git a/modules/constants.js b/modules/constants.js
index 2d7461a..eb8fd5e 100644
--- a/modules/constants.js
+++ b/modules/constants.js
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-const AFTER_HOOK_URI_TO_SKIP = 'protractor-cucumber-framework';
const STATUSES = {
PASSED: 'passed',
FAILED: 'failed',
@@ -84,36 +83,19 @@ const RP_EVENTS = {
STATUS: 'rp/status',
};
-// @see https://github.com/Automattic/cli-table#custom-styles
-const TABLE_CONFIG = {
- chars: {
- top: '',
- 'top-left': '',
- 'top-mid': '',
- 'top-right': '',
- mid: '',
- 'left-mid': '',
- 'mid-mid': '',
- 'right-mid': '',
- bottom: '',
- 'bottom-left': '',
- 'bottom-mid': '',
- 'bottom-right': '',
- },
- style: {
- head: [],
- border: [],
- },
+const TEST_STEP_FINISHED_RP_MESSAGES = {
+ PENDING: "This step is marked as 'pending'",
+ UNDEFINED: 'There is no step definition found. Please verify and implement it.',
+ AMBIGUOUS: 'There are more than one step implementation. Please verify and reimplement it.',
};
module.exports = {
- AFTER_HOOK_URI_TO_SKIP,
RP_ENTITY_LAUNCH,
STATUSES,
LOG_LEVELS,
CUCUMBER_EVENTS,
RP_EVENTS,
- TABLE_CONFIG,
CUCUMBER_MESSAGES,
+ TEST_STEP_FINISHED_RP_MESSAGES,
TEST_ITEM_TYPES,
};
diff --git a/modules/cucumber-reportportal-formatter.js b/modules/cucumber-reportportal-formatter.js
index d7baeb5..c9cc852 100644
--- a/modules/cucumber-reportportal-formatter.js
+++ b/modules/cucumber-reportportal-formatter.js
@@ -15,27 +15,26 @@
*/
const ReportPortalClient = require('@reportportal/client-javascript');
+const { Formatter } = require('@cucumber/cucumber');
+const stripAnsi = require('strip-ansi');
const utils = require('./utils');
const pjson = require('../package.json');
+const {
+ RP_EVENTS,
+ RP_ENTITY_LAUNCH,
+ LOG_LEVELS,
+ STATUSES,
+ CUCUMBER_MESSAGES,
+ TEST_STEP_FINISHED_RP_MESSAGES,
+ TEST_ITEM_TYPES,
+} = require('./constants');
+const Storage = require('./storage');
-const createRPFormatterClass = (config) => {
- let Formatter;
- let module;
- try {
- // eslint-disable-next-line global-require
- Formatter = require('@cucumber/cucumber').Formatter;
- // eslint-disable-next-line global-require
- module = require('./api/current');
- } catch (e) {
- // eslint-disable-next-line global-require
- Formatter = require('cucumber').Formatter;
- // eslint-disable-next-line global-require
- module = require('./api/deprecated');
- }
-
- return class CucumberReportPortalFormatter extends Formatter {
+const createRPFormatterClass = (config) =>
+ class CucumberReportPortalFormatter extends Formatter {
constructor(options) {
super(options);
+
this.options = options;
this.config = config;
this.reportportal = new ReportPortalClient(config, {
@@ -49,12 +48,552 @@ const createRPFormatterClass = (config) => {
typeof this.config.scenarioBasedStatistics === 'boolean'
? this.config.scenarioBasedStatistics
: false;
+ this.storage = new Storage();
+ this.customLaunchStatus = null;
+ this.codeRefIndexesMap = new Map();
+
+ this.options.eventBroadcaster.on('envelope', this.eventHandler);
+ }
+
+ eventHandler(event) {
+ const [key] = Object.keys(event);
+ switch (key) {
+ case CUCUMBER_MESSAGES.GHERKIN_DOCUMENT:
+ return this.onGherkinDocumentEvent(event[key]);
+ case CUCUMBER_MESSAGES.PICKLE:
+ return this.onPickleEvent(event[key]);
+ case CUCUMBER_MESSAGES.HOOK:
+ return this.onHookEvent(event[key]);
+ case CUCUMBER_MESSAGES.TEST_RUN_STARTED:
+ return this.onTestRunStartedEvent();
+ case CUCUMBER_MESSAGES.TEST_CASE:
+ return this.onTestCaseEvent(event[key]);
+ case CUCUMBER_MESSAGES.TEST_CASE_STARTED:
+ return this.onTestCaseStartedEvent(event[key]);
+ case CUCUMBER_MESSAGES.TEST_STEP_STARTED:
+ return this.onTestStepStartedEvent(event[key]);
+ case CUCUMBER_MESSAGES.ATTACHMENT:
+ return this.onTestStepAttachmentEvent(event[key]);
+ case CUCUMBER_MESSAGES.TEST_STEP_FINISHED:
+ return this.onTestStepFinishedEvent(event[key]);
+ case CUCUMBER_MESSAGES.TEST_CASE_FINISHED:
+ return this.onTestCaseFinishedEvent(event[key]);
+ case CUCUMBER_MESSAGES.TEST_RUN_FINISHED:
+ return this.onTestRunFinishedEvent(event[key]);
+ default:
+ return null;
+ }
+ }
+
+ onGherkinDocumentEvent(data) {
+ this.storage.setDocument(data);
+ this.storage.setAstNodesData(data, utils.findAstNodesData(data.feature.children));
+ }
+
+ onHookEvent(data) {
+ const { id } = data;
+ this.storage.setHook(id, data);
+ }
+
+ onPickleEvent(data) {
+ this.storage.setPickle(data);
+ }
+
+ onTestRunStartedEvent() {
+ const attributes = [
+ ...(this.config.attributes || []),
+ { key: 'agent', value: `${pjson.name}|${pjson.version}`, system: true },
+ ];
+ if (this.config.skippedIssue === false) {
+ const skippedIssueAttribute = { key: 'skippedIssue', value: 'false', system: true };
+ attributes.push(skippedIssueAttribute);
+ }
+ const startLaunchData = {
+ name: this.config.launch,
+ startTime: this.reportportal.helpers.now(),
+ description: this.config.description || '',
+ attributes,
+ rerun: this.isRerun,
+ rerunOf: this.rerunOf,
+ ...(this.config.mode && { mode: this.config.mode }),
+ };
+ const { tempId } = this.reportportal.startLaunch(startLaunchData);
+ this.storage.setLaunchTempId(tempId);
+ }
+
+ onTestCaseEvent(data) {
+ const { id: testCaseId, pickleId, testSteps } = data;
+ this.storage.setTestCase({ id: testCaseId, pickleId, testSteps });
+
+ // prepare steps
+ const stepsMap = {};
+ testSteps.forEach((step, index) => {
+ const { pickleStepId, id, hookId } = step;
+
+ if (pickleStepId) {
+ const { steps: stepsData } = this.storage.getPickle(pickleId);
+ const stepData = stepsData.find((item) => item.id === pickleStepId);
+ stepsMap[id] = { ...stepData, type: TEST_ITEM_TYPES.STEP };
+ } else if (hookId) {
+ const isBeforeHook = index === 0;
+ const { name } = this.storage.getHook(hookId);
+ stepsMap[id] = {
+ text: name || (isBeforeHook ? 'Before' : 'After'),
+ type: isBeforeHook ? TEST_ITEM_TYPES.BEFORE_TEST : TEST_ITEM_TYPES.AFTER_TEST,
+ };
+ }
+ });
+ this.storage.setSteps(testCaseId, stepsMap);
+ }
+
+ onTestCaseStartedEvent(data) {
+ const { id, testCaseId, attempt } = data;
+ this.storage.setTestCaseStartedId(id, testCaseId);
+ const { pickleId, isRetry: isTestCaseRetried } = this.storage.getTestCase(testCaseId);
+
+ const {
+ uri: pickleFeatureUri,
+ astNodeIds: [scenarioId, parametersId],
+ } = this.storage.getPickle(pickleId);
+ const currentFeatureUri = this.storage.getCurrentFeatureUri();
+ const feature = this.storage.getFeature(pickleFeatureUri);
+ const launchTempId = this.storage.getLaunchTempId();
+ const isNeedToStartFeature = currentFeatureUri !== pickleFeatureUri;
+
+ // start FEATURE if no currentFeatureUri or new feature
+ // else finish old one
+ const featureCodeRef = utils.formatCodeRef(pickleFeatureUri, feature.name);
+ if (isNeedToStartFeature) {
+ const isFirstFeatureInLaunch = currentFeatureUri === null;
+ const suiteData = {
+ name: `${feature.keyword}: ${feature.name}`,
+ startTime: this.reportportal.helpers.now(),
+ type: this.isScenarioBasedStatistics ? TEST_ITEM_TYPES.TEST : TEST_ITEM_TYPES.SUITE,
+ description: (feature.description || '').trim(),
+ attributes: utils.createAttributes(feature.tags),
+ codeRef: featureCodeRef,
+ };
+
+ if (!isFirstFeatureInLaunch) {
+ const previousFeatureTempId = this.storage.getFeatureTempId();
+ this.reportportal.finishTestItem(previousFeatureTempId, {
+ endTime: this.reportportal.helpers.now(),
+ });
+ }
+
+ this.storage.setCurrentFeatureUri(pickleFeatureUri);
+ const { tempId } = this.reportportal.startTestItem(suiteData, launchTempId, '');
+ this.storage.setFeatureTempId(tempId);
+ }
+
+ // current feature node rule(this entity is for grouping several
+ // scenarios in one logical block) || scenario
+ const currentNode = utils.findNode(feature, scenarioId);
+
+ let scenario;
+ let ruleTempId;
+ if (currentNode.rule) {
+ ruleTempId = this.storage.getRuleTempId(currentNode.rule.id);
+
+ if (!ruleTempId) {
+ const { rule } = currentNode;
+
+ const { name, description, tags, keyword, children = [], id: ruleId } = rule;
+ const childrenIds = children.map((child) => child.scenario.id);
+ const currentNodeCodeRef = utils.formatCodeRef(featureCodeRef, name);
+ const testData = {
+ startTime: this.reportportal.helpers.now(),
+ type: this.isScenarioBasedStatistics ? TEST_ITEM_TYPES.TEST : TEST_ITEM_TYPES.SUITE,
+ name: `${keyword}: ${name}`,
+ description,
+ attributes: utils.createAttributes(tags),
+ codeRef: currentNodeCodeRef,
+ };
+ const parentId = this.storage.getFeatureTempId();
+ const { tempId } = this.reportportal.startTestItem(testData, launchTempId, parentId);
+ ruleTempId = tempId;
+
+ scenario = utils.findScenario(rule, scenarioId);
+
+ this.storage.setRuleTempId(ruleId, ruleTempId);
+ this.storage.setRuleTempIdToTestCase(id, ruleTempId);
+ this.storage.setRuleChildrenIds(ruleTempId, childrenIds);
+ this.storage.setStartedRuleChildrenIds(ruleTempId, scenarioId);
+ } else {
+ this.storage.setRuleTempIdToTestCase(id, ruleTempId);
+ this.storage.setStartedRuleChildrenIds(ruleTempId, scenarioId);
+ scenario = utils.findScenario(currentNode.rule, scenarioId);
+ }
+ } else {
+ scenario = currentNode.scenario;
+ }
+
+ let isRetry = isTestCaseRetried;
+ if (attempt > 0) {
+ isRetry = true;
+ this.storage.updateTestCase(testCaseId, { isRetry });
+
+ // do not show scenario with retry in RP
+ if (!this.isScenarioBasedStatistics) return;
+ }
+
+ const { name: scenarioName } = scenario;
+ const [keyword] = scenario.keyword.split(' ');
+
+ const currentNodeCodeRef = utils.formatCodeRef(
+ featureCodeRef,
+ ruleTempId ? currentNode.rule.name : scenarioName,
+ );
+ const scenarioCodeRefIndexValue = this.codeRefIndexesMap.get(currentNodeCodeRef);
+ this.codeRefIndexesMap.set(currentNodeCodeRef, (scenarioCodeRefIndexValue || 0) + 1);
+ const name =
+ scenarioCodeRefIndexValue && !isRetry
+ ? `${scenarioName} [${scenarioCodeRefIndexValue}]`
+ : scenarioName;
+ const scenarioCodeRef =
+ scenarioCodeRefIndexValue && !isRetry
+ ? `${currentNodeCodeRef} [${scenarioCodeRefIndexValue}]`
+ : currentNodeCodeRef;
+
+ 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),
+ codeRef: scenarioCodeRef,
+ retry: this.isScenarioBasedStatistics && attempt > 0,
+ };
+
+ if (parametersId) {
+ const [{ tableHeader, tableBody }] = scenario.examples;
+ const params = utils.collectParams({ tableHeader, tableBody });
+ Object.keys(params).forEach((paramKey) => {
+ this.storage.setParameters(paramKey, params[paramKey]);
+ });
+ testData.parameters = this.storage.getParameters(parametersId);
+ }
+ const parentId = ruleTempId || this.storage.getFeatureTempId();
+ const { tempId } = this.reportportal.startTestItem(testData, launchTempId, parentId);
+ this.storage.setScenarioTempId(testCaseId, tempId);
+ this.storage.updateTestCase(testCaseId, {
+ codeRef: scenarioCodeRef,
+ });
+ }
+
+ onTestStepStartedEvent(data) {
+ const { testCaseStartedId, testStepId } = data;
+ const testCaseId = this.storage.getTestCaseId(testCaseStartedId);
+ const testCase = this.storage.getTestCase(testCaseId);
+ const step = this.storage.getStep(testCaseId, testStepId);
+
+ // start step
+ if (step) {
+ const currentFeatureUri = this.storage.getCurrentFeatureUri();
+ const astNodesData = this.storage.getAstNodesData(currentFeatureUri);
+
+ const { text: stepName, type, astNodeIds } = step;
+ const keyword =
+ astNodeIds && (astNodesData.find(({ id }) => astNodeIds.includes(id)) || {}).keyword;
+
+ const codeRef = utils.formatCodeRef(testCase.codeRef, stepName);
+ const stepCodeRefIndexValue = this.codeRefIndexesMap.get(codeRef);
+ this.codeRefIndexesMap.set(codeRef, (stepCodeRefIndexValue || 0) + 1);
+ const name =
+ stepCodeRefIndexValue && !testCase.isRetry
+ ? `${stepName} [${stepCodeRefIndexValue}]`
+ : stepName;
+
+ const stepData = {
+ name: keyword ? `${keyword} ${name}` : name,
+ startTime: this.reportportal.helpers.now(),
+ type,
+ codeRef,
+ hasStats: !this.isScenarioBasedStatistics,
+ retry: !this.isScenarioBasedStatistics && !!testCase.isRetry,
+ };
+
+ if (!this.isScenarioBasedStatistics && step.astNodeIds && step.astNodeIds.length > 1) {
+ const { testSteps } = testCase;
+ const testStep = testSteps.find((item) => item.id === testStepId);
+ const argumentsMap = testStep.stepMatchArgumentsLists[0].stepMatchArguments.map((arg) =>
+ arg.group.value.slice(1, -1),
+ );
+ const parametersId = step.astNodeIds[1];
+ const params = this.storage.getParameters(parametersId);
+ stepData.parameters = params.filter((param) => argumentsMap.includes(param.value));
+ }
+
+ const launchTempId = this.storage.getLaunchTempId();
+ const parentId = this.storage.getScenarioTempId(testCaseId);
+ const { tempId } = this.reportportal.startTestItem(stepData, launchTempId, parentId);
+ this.storage.setStepTempId(testStepId, tempId);
+ }
+ }
+
+ onTestStepAttachmentEvent(data) {
+ if (data) {
+ const { testStepId, testCaseStartedId } = data;
+ const testCaseId = this.storage.getTestCaseId(testCaseStartedId);
+ const step = this.storage.getStep(testCaseId, testStepId);
+ const dataObj = utils.getJSON(data.body);
+
+ switch (data.mediaType) {
+ case RP_EVENTS.TEST_CASE_ID: {
+ this.storage.updateStep(testCaseId, testStepId, dataObj);
+ break;
+ }
+ case RP_EVENTS.ATTRIBUTES: {
+ const savedAttributes = step.attributes || [];
+ this.storage.updateStep(testCaseId, testStepId, {
+ attributes: savedAttributes.concat(dataObj.attributes),
+ });
+ break;
+ }
+ case RP_EVENTS.DESCRIPTION: {
+ const savedDescription = step.description || '';
+ this.storage.updateStep(testCaseId, testStepId, {
+ description: savedDescription
+ ? `${savedDescription}
${dataObj.description}`
+ : dataObj.description,
+ });
+ break;
+ }
+ case RP_EVENTS.STATUS: {
+ if (dataObj.entity !== RP_ENTITY_LAUNCH) {
+ this.storage.updateStep(testCaseId, testStepId, dataObj);
+ } else {
+ this.customLaunchStatus = dataObj.status;
+ }
+ break;
+ }
+ case 'text/plain': {
+ const request = {
+ time: this.reportportal.helpers.now(),
+ };
+ let tempStepId = this.storage.getStepTempId(testStepId);
+
+ if (dataObj) {
+ request.level = dataObj.level;
+ request.message = dataObj.message;
+ if (dataObj.entity === RP_ENTITY_LAUNCH) {
+ tempStepId = this.storage.getLaunchTempId();
+ }
+ } else {
+ request.level = LOG_LEVELS.DEBUG;
+ request.message = data.body;
+ }
+ this.reportportal.sendLog(tempStepId, request);
+ break;
+ }
+ default: {
+ const fileName = 'file'; // TODO: generate human valuable file name here if possible
+ const request = {
+ time: this.reportportal.helpers.now(),
+ level: LOG_LEVELS.INFO,
+ message: fileName,
+ file: {
+ name: fileName,
+ },
+ };
+ let tempStepId = 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();
+ }
+ }
+ const fileObj = {
+ name: fileName,
+ type: data.mediaType,
+ content: (dataObj && dataObj.data) || data.body,
+ };
+ this.reportportal.sendLog(tempStepId, request, fileObj);
+ break;
+ }
+ }
+ }
+ }
+
+ onTestStepFinishedEvent(data) {
+ const { testCaseStartedId, testStepId, testStepResult } = data;
+ const testCaseId = this.storage.getTestCaseId(testCaseStartedId);
+ const step = this.storage.getStep(testCaseId, testStepId);
+ const tempStepId = this.storage.getStepTempId(testStepId);
+ let status;
+
+ switch (testStepResult.status.toLowerCase()) {
+ case STATUSES.PASSED: {
+ status = STATUSES.PASSED;
+ break;
+ }
+ case STATUSES.PENDING: {
+ this.reportportal.sendLog(tempStepId, {
+ time: this.reportportal.helpers.now(),
+ level: LOG_LEVELS.WARN,
+ message: TEST_STEP_FINISHED_RP_MESSAGES.PENDING,
+ });
+ status = STATUSES.FAILED;
+ break;
+ }
+ case STATUSES.UNDEFINED: {
+ this.reportportal.sendLog(tempStepId, {
+ time: this.reportportal.helpers.now(),
+ level: LOG_LEVELS.ERROR,
+ message: TEST_STEP_FINISHED_RP_MESSAGES.UNDEFINED,
+ });
+ status = STATUSES.FAILED;
+ break;
+ }
+ case STATUSES.AMBIGUOUS: {
+ this.reportportal.sendLog(tempStepId, {
+ time: this.reportportal.helpers.now(),
+ level: LOG_LEVELS.ERROR,
+ message: TEST_STEP_FINISHED_RP_MESSAGES.AMBIGUOUS,
+ });
+ status = STATUSES.FAILED;
+ break;
+ }
+ case STATUSES.SKIPPED: {
+ status = STATUSES.SKIPPED;
+ break;
+ }
+ case STATUSES.FAILED: {
+ status = STATUSES.FAILED;
+ this.reportportal.sendLog(tempStepId, {
+ time: this.reportportal.helpers.now(),
+ level: LOG_LEVELS.ERROR,
+ message: stripAnsi(testStepResult.message),
+ });
+
+ const isBrowserAvailable = 'browser' in global;
+ const isTakeScreenshotOptionProvidedInRPConfig =
+ this.config.takeScreenshot && this.config.takeScreenshot === 'onFailure';
+
+ if (isBrowserAvailable && isTakeScreenshotOptionProvidedInRPConfig) {
+ const currentFeatureUri = this.storage.getCurrentFeatureUri();
+ const astNodesData = this.storage.getAstNodesData(currentFeatureUri);
+ const screenshotName = utils.getScreenshotName(astNodesData, step.astNodeIds);
- utils.bindToClass(module, this);
+ const request = {
+ time: this.reportportal.helpers.now(),
+ level: LOG_LEVELS.ERROR,
+ file: { name: screenshotName },
+ message: screenshotName,
+ };
- this.init();
+ global.browser
+ .takeScreenshot()
+ .then((png) => {
+ const screenshot = {
+ name: screenshotName,
+ type: 'image/png',
+ content: png,
+ };
+ this.reportportal.sendLog(tempStepId, request, screenshot);
+ })
+ .catch((error) => {
+ console.dir(error);
+ });
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (step) {
+ const { attributes, description = '', testCaseId: customTestCaseId } = step;
+ status = step.status || status || testStepResult.status;
+ const errorMessage =
+ testStepResult.message && `\`\`\`error\n${stripAnsi(testStepResult.message)}\n\`\`\``;
+ const descriptionToSend = errorMessage
+ ? `${description}${description ? '\n' : ''}${errorMessage}`
+ : description;
+ const withoutIssue = status === STATUSES.SKIPPED && this.config.skippedIssue === false;
+ this.reportportal.finishTestItem(tempStepId, {
+ ...(status && { status }),
+ ...(attributes && { attributes }),
+ ...(descriptionToSend && { description: descriptionToSend }),
+ ...(customTestCaseId && { testCaseId: customTestCaseId }),
+ ...(withoutIssue && { issue: { issueType: 'NOT_ISSUE' } }),
+ endTime: this.reportportal.helpers.now(),
+ });
+ }
+
+ if (this.isScenarioBasedStatistics && status !== STATUSES.PASSED) {
+ this.storage.updateTestCase(testCaseId, { status: STATUSES.FAILED });
+ }
+
+ this.storage.removeStepTempId(testStepId);
+ }
+
+ onTestCaseFinishedEvent({ testCaseStartedId, willBeRetried }) {
+ const isNeedToFinishTestCase = !this.isScenarioBasedStatistics && willBeRetried;
+
+ if (isNeedToFinishTestCase) {
+ return;
+ }
+
+ const testCaseId = this.storage.getTestCaseId(testCaseStartedId);
+ const testCase = this.storage.getTestCase(testCaseId);
+ const scenarioTempId = this.storage.getScenarioTempId(testCaseId);
+
+ this.reportportal.finishTestItem(scenarioTempId, {
+ endTime: this.reportportal.helpers.now(),
+ ...(this.isScenarioBasedStatistics && { status: testCase.status || STATUSES.PASSED }),
+ });
+
+ // finish RULE if it's exist and if it's last scenario
+ const ruleTempId = this.storage.getRuleTempIdToTestCase(testCaseStartedId);
+ const ruleChildrenIds = this.storage.getRuleChildrenIds(ruleTempId);
+ const startedRuleChildrenIds = this.storage.getStartedRuleChildrenIds(ruleTempId);
+ const isAllRuleChildrenStarted = utils.isAllRuleChildrenStarted(
+ ruleChildrenIds,
+ startedRuleChildrenIds,
+ );
+
+ if (ruleTempId && isAllRuleChildrenStarted) {
+ this.reportportal.finishTestItem(ruleTempId, {
+ endTime: this.reportportal.helpers.now(),
+ });
+
+ this.storage.removeRuleTempIdToTestCase(testCaseStartedId);
+ this.storage.removeStartedRuleChildrenIds(ruleTempId);
+ this.storage.removeRuleChildrenIds(ruleTempId);
+ this.codeRefIndexesMap.clear();
+ }
+
+ if (!willBeRetried) {
+ this.storage.removeTestCaseStartedId(testCaseStartedId);
+ this.storage.removeSteps(testCaseId);
+ this.storage.removeTestCase(testCaseId);
+ this.storage.removeScenarioTempId(testCaseStartedId);
+ }
+ }
+
+ onTestRunFinishedEvent() {
+ const featureTempId = this.storage.getFeatureTempId();
+ this.reportportal.finishTestItem(featureTempId, {
+ endTime: this.reportportal.helpers.now(),
+ });
+
+ const launchId = this.storage.getLaunchTempId();
+ this.reportportal.getPromiseFinishAllItems(launchId).then(() => {
+ this.reportportal.finishLaunch(launchId, {
+ ...(this.customLaunchStatus && { status: this.customLaunchStatus }),
+ });
+ this.storage.setLaunchTempId(null);
+ this.storage.setCurrentFeatureUri(null);
+ this.storage.setFeatureTempId(null);
+ this.customLaunchStatus = null;
+ });
}
};
-};
module.exports = { createRPFormatterClass };
diff --git a/modules/utils.js b/modules/utils.js
index d394511..bd755ca 100644
--- a/modules/utils.js
+++ b/modules/utils.js
@@ -69,23 +69,10 @@ const isAllRuleChildrenStarted = (ruleChildrenIds, startedRuleChildrenIds) =>
ruleChildrenIds.every((childId) => startedRuleChildrenIds.has(childId));
const findScenario = (node, searchId) => {
- const children = node.children.find((child) => {
- if (child.scenario) {
- return child.scenario.id === searchId;
- }
- return null;
- });
+ const children = node.children.find((child) => child.scenario && child.scenario.id === searchId);
return children.scenario;
};
-const bindToClass = (module, thisClass) => {
- const that = thisClass;
- Object.entries(module).forEach((method) => {
- const [key, value] = method;
- that[key] = value.bind(that);
- });
-};
-
const collectParams = ({ tableHeader, tableBody }) => {
const { cells: headerCells } = tableHeader;
return tableBody.reduce((map, row) => {
@@ -134,7 +121,6 @@ module.exports = {
findNode,
findScenario,
isAllRuleChildrenStarted,
- bindToClass,
collectParams,
findAstNodesData,
getScreenshotName,
diff --git a/package-lock.json b/package-lock.json
index 56c1211..a1d153e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,6 @@
"license": "Apache-2.0",
"dependencies": {
"@reportportal/client-javascript": "^5.1.0",
- "cli-table3": "^0.6.3",
"strip-ansi": "^6.0.1"
},
"devDependencies": {
@@ -30,8 +29,7 @@
"node": ">=12.x"
},
"peerDependencies": {
- "@cucumber/cucumber": "7.x - 10.x",
- "cucumber": "4.x - 6.x"
+ "@cucumber/cucumber": "7.x - 10.x"
}
},
"../client-javascript": {
@@ -689,19 +687,6 @@
"@babel/core": "^7.0.0-0"
}
},
- "node_modules/@babel/runtime-corejs3": {
- "version": "7.23.8",
- "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.23.8.tgz",
- "integrity": "sha512-2ZzmcDugdm0/YQKFVYsXiwUN7USPX8PM7cytpb4PFl87fM+qYPSvTZX//8tyeJB1j0YDmafBJEbl5f8NfLyuKw==",
- "peer": true,
- "dependencies": {
- "core-js-pure": "^3.30.2",
- "regenerator-runtime": "^0.14.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@babel/template": {
"version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
@@ -770,6 +755,7 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
"integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+ "dev": true,
"optional": true,
"engines": {
"node": ">=0.1.90"
@@ -1985,7 +1971,8 @@
"node_modules/any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
- "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+ "dev": true
},
"node_modules/anymatch": {
"version": "3.1.3",
@@ -2095,19 +2082,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/assert-plus": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
- "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
- "peer": true,
- "engines": {
- "node": ">=0.8"
- }
- },
"node_modules/assertion-error-formatter": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/assertion-error-formatter/-/assertion-error-formatter-3.0.0.tgz",
"integrity": "sha512-6YyAVLrEze0kQ7CmJfUgrLHb+Y7XghmL2Ie7ijVa2Y9ynP3LV+VDiwFk62Dn0qtqbmY0BT0ss6p1xxpiF2PYbQ==",
+ "dev": true,
"dependencies": {
"diff": "^4.0.1",
"pad-right": "^0.2.2",
@@ -2273,18 +2252,6 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
- "node_modules/becke-ch--regex--s0-0-v1--base--pl--lib": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/becke-ch--regex--s0-0-v1--base--pl--lib/-/becke-ch--regex--s0-0-v1--base--pl--lib-1.4.0.tgz",
- "integrity": "sha512-FnWonOyaw7Vivg5nIkrUll9HSS5TjFbyuURAiDssuL6VxrBe3ERzudRxOcWRhZYlP89UArMDikz7SapRPQpmZQ==",
- "peer": true
- },
- "node_modules/bluebird": {
- "version": "3.7.2",
- "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
- "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
- "peer": true
- },
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -2484,6 +2451,7 @@
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz",
"integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==",
+ "dev": true,
"dependencies": {
"string-width": "^4.2.0"
},
@@ -2542,15 +2510,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
- "node_modules/colors": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
- "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
- "peer": true,
- "engines": {
- "node": ">=0.1.90"
- }
- },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -2588,23 +2547,6 @@
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"dev": true
},
- "node_modules/core-js-pure": {
- "version": "3.35.0",
- "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.35.0.tgz",
- "integrity": "sha512-f+eRYmkou59uh7BPcyJ8MC76DiGhspj1KMxVIcF24tzP8NA9HVa1uC7BTW2tgx7E1QVCzDzsgp7kArrzhlz8Ew==",
- "hasInstallScript": true,
- "peer": true,
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/core-js"
- }
- },
- "node_modules/core-util-is": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
- "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
- "peer": true
- },
"node_modules/create-jest": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz",
@@ -2640,149 +2582,6 @@
"node": ">= 8"
}
},
- "node_modules/cucumber": {
- "version": "6.0.7",
- "resolved": "https://registry.npmjs.org/cucumber/-/cucumber-6.0.7.tgz",
- "integrity": "sha512-pN3AgWxHx8rOi+wOlqjASNETOjf3TgeyqhMNLQam7nSTXgQzju1oAmXkleRQRcXvpVvejcDHiZBLFSfBkqbYpA==",
- "deprecated": "Cucumber is publishing new releases under @cucumber/cucumber",
- "peer": true,
- "dependencies": {
- "assertion-error-formatter": "^3.0.0",
- "bluebird": "^3.4.1",
- "cli-table3": "^0.5.1",
- "colors": "^1.1.2",
- "commander": "^3.0.1",
- "cucumber-expressions": "^8.1.0",
- "cucumber-tag-expressions": "^2.0.2",
- "duration": "^0.2.1",
- "escape-string-regexp": "^2.0.0",
- "figures": "^3.0.0",
- "gherkin": "5.0.0",
- "glob": "^7.1.3",
- "indent-string": "^4.0.0",
- "is-generator": "^1.0.2",
- "is-stream": "^2.0.0",
- "knuth-shuffle-seeded": "^1.0.6",
- "lodash": "^4.17.14",
- "mz": "^2.4.0",
- "progress": "^2.0.0",
- "resolve": "^1.3.3",
- "serialize-error": "^4.1.0",
- "stack-chain": "^2.0.0",
- "stacktrace-js": "^2.0.0",
- "string-argv": "^0.3.0",
- "title-case": "^2.1.1",
- "util-arity": "^1.0.2",
- "verror": "^1.9.0"
- },
- "bin": {
- "cucumber-js": "bin/cucumber-js"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/cucumber-expressions": {
- "version": "8.3.0",
- "resolved": "https://registry.npmjs.org/cucumber-expressions/-/cucumber-expressions-8.3.0.tgz",
- "integrity": "sha512-cP2ya0EiorwXBC7Ll7Cj7NELYbasNv9Ty42L4u7sso9KruWemWG1ZiTq4PMqir3SNDSrbykoqI5wZgMbLEDjLQ==",
- "deprecated": "This package is now published under @cucumber/cucumber-expressions",
- "peer": true,
- "dependencies": {
- "becke-ch--regex--s0-0-v1--base--pl--lib": "^1.4.0",
- "xregexp": "^4.2.4"
- }
- },
- "node_modules/cucumber-tag-expressions": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/cucumber-tag-expressions/-/cucumber-tag-expressions-2.0.3.tgz",
- "integrity": "sha512-+x5j1IfZrBtbvYHuoUX0rl4nUGxaey6Do9sM0CABmZfDCcWXuuRm1fQeCaklIYQgOFHQ6xOHvDSdkMHHpni6tQ==",
- "peer": true
- },
- "node_modules/cucumber/node_modules/ansi-regex": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
- "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
- "peer": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/cucumber/node_modules/cli-table3": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz",
- "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==",
- "peer": true,
- "dependencies": {
- "object-assign": "^4.1.0",
- "string-width": "^2.1.1"
- },
- "engines": {
- "node": ">=6"
- },
- "optionalDependencies": {
- "colors": "^1.1.2"
- }
- },
- "node_modules/cucumber/node_modules/commander": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz",
- "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==",
- "peer": true
- },
- "node_modules/cucumber/node_modules/escape-string-regexp": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
- "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
- "peer": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/cucumber/node_modules/is-fullwidth-code-point": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
- "peer": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/cucumber/node_modules/string-width": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
- "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
- "peer": true,
- "dependencies": {
- "is-fullwidth-code-point": "^2.0.0",
- "strip-ansi": "^4.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/cucumber/node_modules/strip-ansi": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
- "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
- "peer": true,
- "dependencies": {
- "ansi-regex": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/d": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
- "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
- "peer": true,
- "dependencies": {
- "es5-ext": "^0.10.50",
- "type": "^1.0.1"
- }
- },
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -2881,6 +2680,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true,
"engines": {
"node": ">=0.3.1"
}
@@ -2906,16 +2706,6 @@
"node": ">=6.0.0"
}
},
- "node_modules/duration": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/duration/-/duration-0.2.2.tgz",
- "integrity": "sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg==",
- "peer": true,
- "dependencies": {
- "d": "1",
- "es5-ext": "~0.10.46"
- }
- },
"node_modules/eastasianwidth": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@@ -2943,7 +2733,8 @@
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
},
"node_modules/error-ex": {
"version": "1.3.2",
@@ -2958,6 +2749,7 @@
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz",
"integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==",
+ "dev": true,
"dependencies": {
"stackframe": "^1.3.4"
}
@@ -3055,42 +2847,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/es5-ext": {
- "version": "0.10.62",
- "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
- "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
- "hasInstallScript": true,
- "peer": true,
- "dependencies": {
- "es6-iterator": "^2.0.3",
- "es6-symbol": "^3.1.3",
- "next-tick": "^1.1.0"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/es6-iterator": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
- "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
- "peer": true,
- "dependencies": {
- "d": "1",
- "es5-ext": "^0.10.35",
- "es6-symbol": "^3.1.1"
- }
- },
- "node_modules/es6-symbol": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
- "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
- "peer": true,
- "dependencies": {
- "d": "^1.0.1",
- "ext": "^1.1.2"
- }
- },
"node_modules/escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@@ -3104,6 +2860,7 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
"engines": {
"node": ">=0.8.0"
}
@@ -3569,30 +3326,6 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/ext": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
- "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==",
- "peer": true,
- "dependencies": {
- "type": "^2.7.2"
- }
- },
- "node_modules/ext/node_modules/type": {
- "version": "2.7.2",
- "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
- "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==",
- "peer": true
- },
- "node_modules/extsprintf": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz",
- "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==",
- "engines": [
- "node >=0.6.0"
- ],
- "peer": true
- },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -3639,6 +3372,7 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
"integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+ "dev": true,
"dependencies": {
"escape-string-regexp": "^1.0.5"
},
@@ -3801,6 +3535,7 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -3902,16 +3637,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/gherkin": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/gherkin/-/gherkin-5.0.0.tgz",
- "integrity": "sha512-Y+93z2Nh+TNIKuKEf+6M0FQrX/z0Yv9C2LFfc5NlcGJWRrrTeI/jOg2374y1FOw6ZYQ3RgJBezRkli7CLDubDA==",
- "deprecated": "This package is now published under @cucumber/gherkin",
- "peer": true,
- "bin": {
- "gherkin-javascript": "bin/gherkin"
- }
- },
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -4106,6 +3831,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
+ "dev": true,
"dependencies": {
"function-bind": "^1.1.2"
},
@@ -4200,6 +3926,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "dev": true,
"engines": {
"node": ">=8"
}
@@ -4304,6 +4031,7 @@
"version": "2.13.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
"integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
+ "dev": true,
"dependencies": {
"hasown": "^2.0.0"
},
@@ -4339,16 +4067,11 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
"engines": {
"node": ">=8"
}
},
- "node_modules/is-generator": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-generator/-/is-generator-1.0.3.tgz",
- "integrity": "sha512-G56jBpbJeg7ds83HW1LuShNs8J73Fv3CPz/bmROHOHlnKkN8sWb9ujiagjmxxMUywftgq48HlBZELKKqFLk0oA==",
- "peer": true
- },
"node_modules/is-generator-fn": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
@@ -4474,6 +4197,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
"engines": {
"node": ">=8"
},
@@ -5308,6 +5032,7 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/knuth-shuffle-seeded/-/knuth-shuffle-seeded-1.0.6.tgz",
"integrity": "sha512-9pFH0SplrfyKyojCLxZfMcvkhf5hH0d+UwR9nTVJ/DDQJGuzcXjTwB7TP7sDfehSudlGGaOLblmEWqv04ERVWg==",
+ "dev": true,
"dependencies": {
"seed-random": "~2.2.0"
}
@@ -5355,12 +5080,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/lodash": {
- "version": "4.17.21",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
- "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
- "peer": true
- },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -5528,6 +5247,7 @@
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dev": true,
"dependencies": {
"any-promise": "^1.0.0",
"object-assign": "^4.0.1",
@@ -5540,12 +5260,6 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true
},
- "node_modules/next-tick": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
- "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==",
- "peer": true
- },
"node_modules/no-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
@@ -5614,6 +5328,7 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -5768,6 +5483,7 @@
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/pad-right/-/pad-right-0.2.2.tgz",
"integrity": "sha512-4cy8M95ioIGolCoMmm2cMntGR1lPLEbOMzOKu8bzjuJP6JpzEMQcDHmh7hHLYGgob+nKe1YHFMaG4V59HQa89g==",
+ "dev": true,
"dependencies": {
"repeat-string": "^1.5.2"
},
@@ -5834,7 +5550,8 @@
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
},
"node_modules/path-scurry": {
"version": "1.10.1",
@@ -6018,6 +5735,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true,
"engines": {
"node": ">=0.4.0"
}
@@ -6205,12 +5923,6 @@
"integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==",
"dev": true
},
- "node_modules/regenerator-runtime": {
- "version": "0.14.1",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
- "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
- "peer": true
- },
"node_modules/regexp-match-indices": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz",
@@ -6262,6 +5974,7 @@
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
"integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==",
+ "dev": true,
"engines": {
"node": ">=0.10"
}
@@ -6279,6 +5992,7 @@
"version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+ "dev": true,
"dependencies": {
"is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
@@ -6416,7 +6130,8 @@
"node_modules/seed-random": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz",
- "integrity": "sha512-34EQV6AAHQGhoc0tn/96a9Fsi6v2xdqe/dMUwljGRaFOzR3EgRmECvD0O8vi8X+/uQ50LGHfkNu/Eue5TPKZkQ=="
+ "integrity": "sha512-34EQV6AAHQGhoc0tn/96a9Fsi6v2xdqe/dMUwljGRaFOzR3EgRmECvD0O8vi8X+/uQ50LGHfkNu/Eue5TPKZkQ==",
+ "dev": true
},
"node_modules/semver": {
"version": "7.5.3",
@@ -6433,27 +6148,6 @@
"node": ">=10"
}
},
- "node_modules/serialize-error": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-4.1.0.tgz",
- "integrity": "sha512-5j9GgyGsP9vV9Uj1S0lDCvlsd+gc2LEPVK7HHHte7IyPwOD4lVQFeaX143gx3U5AnoCi+wbcb3mvaxVysjpxEw==",
- "peer": true,
- "dependencies": {
- "type-fest": "^0.3.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/serialize-error/node_modules/type-fest": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz",
- "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==",
- "peer": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/set-function-length": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
@@ -6596,21 +6290,6 @@
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"dev": true
},
- "node_modules/stack-chain": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-2.0.0.tgz",
- "integrity": "sha512-GGrHXePi305aW7XQweYZZwiRwR7Js3MWoK/EHzzB9ROdc75nCnjSJVi21rdAGxFl+yCx2L2qdfl5y7NO4lTyqg==",
- "peer": true
- },
- "node_modules/stack-generator": {
- "version": "2.0.10",
- "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz",
- "integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==",
- "peer": true,
- "dependencies": {
- "stackframe": "^1.3.4"
- }
- },
"node_modules/stack-utils": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
@@ -6635,46 +6314,8 @@
"node_modules/stackframe": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
- "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="
- },
- "node_modules/stacktrace-gps": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz",
- "integrity": "sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==",
- "peer": true,
- "dependencies": {
- "source-map": "0.5.6",
- "stackframe": "^1.3.4"
- }
- },
- "node_modules/stacktrace-gps/node_modules/source-map": {
- "version": "0.5.6",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
- "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==",
- "peer": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/stacktrace-js": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz",
- "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==",
- "peer": true,
- "dependencies": {
- "error-stack-parser": "^2.0.6",
- "stack-generator": "^2.0.5",
- "stacktrace-gps": "^3.0.4"
- }
- },
- "node_modules/string-argv": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
- "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
- "peer": true,
- "engines": {
- "node": ">=0.6.19"
- }
+ "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==",
+ "dev": true
},
"node_modules/string-length": {
"version": "4.0.2",
@@ -6693,6 +6334,7 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -6852,6 +6494,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
"engines": {
"node": ">= 0.4"
},
@@ -6883,6 +6526,7 @@
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "dev": true,
"dependencies": {
"any-promise": "^1.0.0"
}
@@ -6891,6 +6535,7 @@
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "dev": true,
"dependencies": {
"thenify": ">= 3.1.0 < 4"
},
@@ -6904,31 +6549,6 @@
"integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==",
"dev": true
},
- "node_modules/title-case": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz",
- "integrity": "sha512-EkJoZ2O3zdCz3zJsYCsxyq2OC5hrxR9mfdd5I+w8h/tmFfeOxJ+vvkxsKxdmN0WtS9zLdHEgfgVOiMVgv+Po4Q==",
- "peer": true,
- "dependencies": {
- "no-case": "^2.2.0",
- "upper-case": "^1.0.3"
- }
- },
- "node_modules/title-case/node_modules/lower-case": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
- "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==",
- "peer": true
- },
- "node_modules/title-case/node_modules/no-case": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
- "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
- "peer": true,
- "dependencies": {
- "lower-case": "^1.1.1"
- }
- },
"node_modules/tmp": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
@@ -6992,12 +6612,6 @@
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
"dev": true
},
- "node_modules/type": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
- "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
- "peer": true
- },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -7152,12 +6766,6 @@
"browserslist": ">= 4.21.0"
}
},
- "node_modules/upper-case": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
- "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==",
- "peer": true
- },
"node_modules/upper-case-first": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz",
@@ -7179,7 +6787,8 @@
"node_modules/util-arity": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/util-arity/-/util-arity-1.1.0.tgz",
- "integrity": "sha512-kkyIsXKwemfSy8ZEoaIz06ApApnWsk5hQO0vLjZS6UkBiGiW++Jsyb8vSBoc0WKlffGoGs5yYy/j5pp8zckrFA=="
+ "integrity": "sha512-kkyIsXKwemfSy8ZEoaIz06ApApnWsk5hQO0vLjZS6UkBiGiW++Jsyb8vSBoc0WKlffGoGs5yYy/j5pp8zckrFA==",
+ "dev": true
},
"node_modules/uuid": {
"version": "9.0.0",
@@ -7214,20 +6823,6 @@
"spdx-expression-parse": "^3.0.0"
}
},
- "node_modules/verror": {
- "version": "1.10.1",
- "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
- "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==",
- "peer": true,
- "dependencies": {
- "assert-plus": "^1.0.0",
- "core-util-is": "1.0.2",
- "extsprintf": "^1.2.0"
- },
- "engines": {
- "node": ">=0.6.0"
- }
- },
"node_modules/walker": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
@@ -7349,15 +6944,6 @@
"node": ">=8.0"
}
},
- "node_modules/xregexp": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.4.1.tgz",
- "integrity": "sha512-2u9HwfadaJaY9zHtRRnH6BY6CQVNQKkYm3oLtC9gJXXzfsbACg5X5e4EZZGVAH+YIfa+QA9lsFQTTe3HURF3ag==",
- "peer": true,
- "dependencies": {
- "@babel/runtime-corejs3": "^7.12.1"
- }
- },
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
diff --git a/package.json b/package.json
index 0a4cb17..44b836e 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,6 @@
},
"dependencies": {
"@reportportal/client-javascript": "^5.1.0",
- "cli-table3": "^0.6.3",
"strip-ansi": "^6.0.1"
},
"engines": {
@@ -31,8 +30,7 @@
"prettier": "^2.8.8"
},
"peerDependencies": {
- "@cucumber/cucumber": "7.x - 10.x",
- "cucumber": "4.x - 6.x"
+ "@cucumber/cucumber": "7.x - 10.x"
},
"repository": {
"type": "git",
diff --git a/tests/cucumber-reportportal-formatter.spec.js b/tests/cucumber-reportportal-formatter.spec.js
index 464adcf..90648cf 100644
--- a/tests/cucumber-reportportal-formatter.spec.js
+++ b/tests/cucumber-reportportal-formatter.spec.js
@@ -21,6 +21,11 @@ const {
gherkinDocument,
uri,
pickle,
+ pickleWithParameters,
+ testCaseWithParameters,
+ ruleId,
+ ruleTempId,
+ hook,
testCase,
testCaseStarted,
testCaseId,
@@ -34,7 +39,15 @@ const {
scenario,
step,
} = require('./data');
-const { STATUSES, TEST_ITEM_TYPES } = require('../modules/constants');
+const {
+ STATUSES,
+ TEST_ITEM_TYPES,
+ CUCUMBER_MESSAGES,
+ RP_EVENTS,
+ RP_ENTITY_LAUNCH,
+ TEST_STEP_FINISHED_RP_MESSAGES,
+ LOG_LEVELS,
+} = require('../modules/constants');
describe('cucumber-reportportal-formatter', () => {
const config = getDefaultConfig();
@@ -55,6 +68,113 @@ describe('cucumber-reportportal-formatter', () => {
formatter.codeRefIndexesMap.clear();
});
+ describe('eventHandler', () => {
+ it('onGherkinDocumentEvent should be called', () => {
+ const spyOnGherkinDocumentEvent = jest
+ .spyOn(formatter, 'onGherkinDocumentEvent')
+ .mockImplementationOnce(() => {});
+ formatter.eventHandler({ [CUCUMBER_MESSAGES.GHERKIN_DOCUMENT]: gherkinDocument });
+
+ expect(spyOnGherkinDocumentEvent).toBeCalledWith(gherkinDocument);
+ });
+
+ it('onPickleEvent should be called', () => {
+ const spyOnPickleEvent = jest
+ .spyOn(formatter, 'onPickleEvent')
+ .mockImplementationOnce(() => {});
+ formatter.eventHandler({ [CUCUMBER_MESSAGES.PICKLE]: pickle });
+
+ expect(spyOnPickleEvent).toBeCalledWith(pickle);
+ });
+
+ it('onHookEvent should be called', () => {
+ const spyOnHookEvent = jest.spyOn(formatter, 'onHookEvent').mockImplementationOnce(() => {});
+ formatter.eventHandler({ [CUCUMBER_MESSAGES.HOOK]: hook });
+
+ expect(spyOnHookEvent).toBeCalledWith(hook);
+ });
+
+ it('onTestRunStartedEvent should be called', () => {
+ const spyOnTestRunStartedEvent = jest
+ .spyOn(formatter, 'onTestRunStartedEvent')
+ .mockImplementationOnce(() => {});
+ formatter.eventHandler({ [CUCUMBER_MESSAGES.TEST_RUN_STARTED]: {} });
+
+ expect(spyOnTestRunStartedEvent).toBeCalled();
+ });
+
+ it('onTestCaseEvent should be called', () => {
+ const spyOnTestCaseEvent = jest
+ .spyOn(formatter, 'onTestCaseEvent')
+ .mockImplementationOnce(() => {});
+ formatter.eventHandler({ [CUCUMBER_MESSAGES.TEST_CASE]: testCase });
+
+ expect(spyOnTestCaseEvent).toBeCalledWith(testCase);
+ });
+
+ it('onTestCaseStartedEvent should be called', () => {
+ const spyOnTestCaseStartedEvent = jest
+ .spyOn(formatter, 'onTestCaseStartedEvent')
+ .mockImplementationOnce(() => {});
+ formatter.eventHandler({ [CUCUMBER_MESSAGES.TEST_CASE_STARTED]: testCaseStarted });
+
+ expect(spyOnTestCaseStartedEvent).toBeCalledWith(testCaseStarted);
+ });
+
+ it('onTestStepStartedEvent should be called', () => {
+ const spyOnTestStepStartedEvent = jest
+ .spyOn(formatter, 'onTestStepStartedEvent')
+ .mockImplementationOnce(() => {});
+ formatter.eventHandler({ [CUCUMBER_MESSAGES.TEST_STEP_STARTED]: testStepStarted });
+
+ expect(spyOnTestStepStartedEvent).toBeCalledWith(testStepStarted);
+ });
+
+ it('onTestStepAttachmentEvent should be called', () => {
+ const testData = { id: 'test' };
+ const spyOnTestStepAttachmentEvent = jest
+ .spyOn(formatter, 'onTestStepAttachmentEvent')
+ .mockImplementationOnce(() => {});
+ formatter.eventHandler({ [CUCUMBER_MESSAGES.ATTACHMENT]: testData });
+
+ expect(spyOnTestStepAttachmentEvent).toBeCalledWith(testData);
+ });
+
+ it('onTestStepFinishedEvent should be called', () => {
+ const spyOnTestStepFinishedEvent = jest
+ .spyOn(formatter, 'onTestStepFinishedEvent')
+ .mockImplementationOnce(() => {});
+ formatter.eventHandler({ [CUCUMBER_MESSAGES.TEST_STEP_FINISHED]: testStepFinished });
+
+ expect(spyOnTestStepFinishedEvent).toBeCalledWith(testStepFinished);
+ });
+
+ it('onTestCaseFinishedEvent should be called', () => {
+ const spyOnTestCaseFinishedEvent = jest
+ .spyOn(formatter, 'onTestCaseFinishedEvent')
+ .mockImplementationOnce(() => {});
+ formatter.eventHandler({ [CUCUMBER_MESSAGES.TEST_CASE_FINISHED]: testCaseFinished });
+
+ expect(spyOnTestCaseFinishedEvent).toBeCalledWith(testCaseFinished);
+ });
+
+ it('onTestRunFinishedEvent should be called', () => {
+ const testData = { id: 'test' };
+ const spyOnTestRunFinishedEvent = jest
+ .spyOn(formatter, 'onTestRunFinishedEvent')
+ .mockImplementationOnce(() => {});
+ formatter.eventHandler({ [CUCUMBER_MESSAGES.TEST_RUN_FINISHED]: testData });
+
+ expect(spyOnTestRunFinishedEvent).toBeCalledWith(testData);
+ });
+
+ it('should return null if an unexpected event is received', () => {
+ const result = formatter.eventHandler({ 'unexpected-event': {} });
+
+ expect(result).toBeNull();
+ });
+ });
+
describe('onGherkinDocumentEvent', () => {
beforeEach(() => {
formatter.onGherkinDocumentEvent(gherkinDocument);
@@ -68,6 +188,14 @@ describe('cucumber-reportportal-formatter', () => {
});
});
+ describe('onHookEvent', () => {
+ it('should set hook to storage', () => {
+ formatter.onHookEvent(hook);
+
+ expect(formatter.storage.getHook(hook.id)).toBe(hook);
+ });
+ });
+
describe('onPickleEvent', () => {
it('should set pickle to storage', () => {
formatter.onPickleEvent(pickle);
@@ -82,6 +210,24 @@ describe('cucumber-reportportal-formatter', () => {
expect(formatter.storage.getLaunchTempId()).toBe('tempLaunchId');
});
+
+ it('startLaunch should be called with skippedIssue attribute', () => {
+ const tempId = 'test-temp-id';
+ const spyStartLaunch = jest.spyOn(formatter.reportportal, 'startLaunch');
+ const { skippedIssue } = formatter.config;
+
+ formatter.config.skippedIssue = false;
+ formatter.onTestRunStartedEvent();
+ formatter.config.skippedIssue = skippedIssue;
+
+ expect(spyStartLaunch).toHaveBeenCalledWith(
+ expect.objectContaining({
+ attributes: expect.arrayContaining([
+ { key: 'skippedIssue', value: 'false', system: true },
+ ]),
+ }),
+ );
+ });
});
describe('onTestCaseEvent', () => {
@@ -97,12 +243,25 @@ describe('cucumber-reportportal-formatter', () => {
expect(formatter.storage.getStep(testCase.id, testStepId)).toEqual(expectedRes);
});
+
+ it('should set Before Hook to storage under testCaseId', () => {
+ const data = { ...testCase, testSteps: [{ hookId: hook.id, id: testStepId }] };
+ formatter.onGherkinDocumentEvent(gherkinDocument);
+ formatter.storage.setHook(hook.id, hook);
+
+ formatter.onTestCaseEvent(data);
+
+ expect(formatter.storage.getStep(testCase.id, testStepId)).toEqual({
+ text: hook.name,
+ type: TEST_ITEM_TYPES.BEFORE_TEST,
+ });
+ });
});
describe('onTestCaseStartedEvent', () => {
beforeEach(() => {
formatter.onGherkinDocumentEvent(gherkinDocument);
- formatter.onPickleEvent(pickle);
+ formatter.onPickleEvent(pickleWithParameters);
formatter.onTestRunStartedEvent();
formatter.onTestCaseEvent(testCase);
});
@@ -180,19 +339,33 @@ describe('cucumber-reportportal-formatter', () => {
type: 'TEST',
codeRef: `${uri}/${feature.name}/${scenario.name}`,
retry: false,
+ parameters: [
+ {
+ key: scenario.examples[0].tableHeader.cells[0].value,
+ value: scenario.examples[0].tableBody[0].cells[0].value,
+ },
+ ],
},
'tempLaunchId',
'testItemId',
);
});
+
+ it('should set isRetry for test case in storage if attempt > 0', () => {
+ const spyStartTestItem = jest.spyOn(formatter.reportportal, 'startTestItem');
+
+ formatter.onTestCaseStartedEvent({ ...testCaseStarted, attempt: 1 });
+
+ expect(formatter.storage.getTestCase(testCaseId).isRetry).toEqual(true);
+ });
});
describe('onTestStepStartedEvent', () => {
beforeEach(() => {
formatter.onGherkinDocumentEvent(gherkinDocument);
- formatter.onPickleEvent(pickle);
+ formatter.onPickleEvent(pickleWithParameters);
formatter.onTestRunStartedEvent();
- formatter.onTestCaseEvent(testCase);
+ formatter.onTestCaseEvent(testCaseWithParameters);
formatter.onTestCaseStartedEvent(testCaseStarted);
});
@@ -207,6 +380,12 @@ describe('cucumber-reportportal-formatter', () => {
startTime: mockedDate,
type: 'STEP',
codeRef: `${uri}/${feature.name}/${scenario.name}/${step.text}`,
+ parameters: [
+ {
+ key: scenario.examples[0].tableHeader.cells[0].value,
+ value: scenario.examples[0].tableBody[0].cells[0].value,
+ },
+ ],
hasStats: true,
retry: false,
},
@@ -216,6 +395,248 @@ describe('cucumber-reportportal-formatter', () => {
});
});
+ describe('onTestStepAttachmentEvent', () => {
+ const launchTempId = 'launch-temp-id';
+ const stepTempId = 'step-temp-id';
+ const body = {
+ entity: 'test-entity',
+ status: 'test-status',
+ description: 'test-description',
+ level: 'test-level',
+ message: 'test-message',
+ data: 'test-data',
+ attributes: ['attributes1', 'attributes2', 'attributes3'],
+ };
+
+ beforeEach(() => {
+ formatter.storage.setTestCaseStartedId(testCaseStartedId, testCase.id);
+ formatter.storage.setSteps(testCase.id, {
+ [testStepId]: {},
+ });
+ });
+
+ afterEach(() => {
+ formatter.storage.removeTestCaseStartedId(testCaseStartedId);
+ formatter.storage.removeSteps(testCase.id);
+ });
+
+ it('should update step in storage', () => {
+ const data = {
+ mediaType: RP_EVENTS.TEST_CASE_ID,
+ testStepId,
+ testCaseStartedId,
+ body: JSON.stringify(body),
+ };
+
+ formatter.onTestStepAttachmentEvent(data);
+
+ expect(formatter.storage.getStep(testCaseId, testStepId)).toEqual(body);
+ });
+
+ it('should update step attributes in storage', () => {
+ const data = {
+ mediaType: RP_EVENTS.ATTRIBUTES,
+ testStepId,
+ testCaseStartedId,
+ body: JSON.stringify(body),
+ };
+
+ formatter.onTestStepAttachmentEvent(data);
+
+ expect(formatter.storage.getStep(testCaseId, testStepId)).toEqual({
+ attributes: body.attributes,
+ });
+ });
+
+ it('should update step description in storage', () => {
+ const data = {
+ mediaType: RP_EVENTS.DESCRIPTION,
+ testStepId,
+ testCaseStartedId,
+ body: JSON.stringify(body),
+ };
+
+ formatter.onTestStepAttachmentEvent(data);
+
+ expect(formatter.storage.getStep(testCaseId, testStepId)).toEqual({
+ description: body.description,
+ });
+ });
+
+ it('should update step description in storage', () => {
+ const data = {
+ mediaType: RP_EVENTS.DESCRIPTION,
+ testStepId,
+ testCaseStartedId,
+ body: JSON.stringify(body),
+ };
+
+ formatter.onTestStepAttachmentEvent(data);
+
+ expect(formatter.storage.getStep(testCaseId, testStepId)).toEqual({
+ description: body.description,
+ });
+ });
+
+ describe('Status event handling', () => {
+ it('should update step in storage', () => {
+ const data = {
+ mediaType: RP_EVENTS.STATUS,
+ testStepId,
+ testCaseStartedId,
+ body: JSON.stringify(body),
+ };
+
+ formatter.onTestStepAttachmentEvent(data);
+
+ expect(formatter.storage.getStep(testCaseId, testStepId)).toEqual(body);
+ });
+
+ it('should set status in customLaunchStatus field', () => {
+ const status = 'start';
+ const data = {
+ mediaType: RP_EVENTS.STATUS,
+ testStepId,
+ testCaseStartedId,
+ body: JSON.stringify({ ...body, entity: RP_ENTITY_LAUNCH, status }),
+ };
+
+ formatter.onTestStepAttachmentEvent(data);
+
+ expect(formatter.customLaunchStatus).toEqual(status);
+ });
+ });
+
+ describe('text/plain event handling', () => {
+ it('sendLog should be called', () => {
+ jest.spyOn(formatter.storage, 'getStepTempId').mockImplementationOnce(() => stepTempId);
+ const spySendLog = jest.spyOn(formatter.reportportal, 'sendLog');
+
+ const data = {
+ mediaType: 'text/plain',
+ testStepId,
+ testCaseStartedId,
+ body: JSON.stringify(body),
+ };
+
+ formatter.onTestStepAttachmentEvent(data);
+
+ expect(spySendLog).toHaveBeenCalledWith(stepTempId, {
+ time: mockedDate,
+ level: body.level,
+ message: body.message,
+ });
+ });
+
+ it('sendLog should be called with launch temp id', () => {
+ jest.spyOn(formatter.storage, 'getLaunchTempId').mockImplementationOnce(() => launchTempId);
+ const spySendLog = jest.spyOn(formatter.reportportal, 'sendLog');
+
+ const data = {
+ mediaType: 'text/plain',
+ testStepId,
+ testCaseStartedId,
+ body: JSON.stringify({ ...body, entity: RP_ENTITY_LAUNCH }),
+ };
+
+ formatter.onTestStepAttachmentEvent(data);
+
+ expect(spySendLog).toHaveBeenCalledWith(launchTempId, {
+ time: mockedDate,
+ level: body.level,
+ message: body.message,
+ });
+ });
+
+ it('sendLog should be called with body like message and debug level', () => {
+ jest.spyOn(formatter.storage, 'getStepTempId').mockImplementationOnce(() => stepTempId);
+ const spySendLog = jest.spyOn(formatter.reportportal, 'sendLog');
+
+ const data = {
+ mediaType: 'text/plain',
+ testStepId,
+ testCaseStartedId,
+ body: 'not-json-body',
+ };
+
+ formatter.onTestStepAttachmentEvent(data);
+
+ expect(spySendLog).toHaveBeenCalledWith(stepTempId, {
+ time: mockedDate,
+ level: LOG_LEVELS.DEBUG,
+ message: data.body,
+ });
+ });
+ });
+
+ describe('default handling', () => {
+ const fileName = 'file';
+ const mediaType = 'unexpected-media-type';
+
+ it('sendLog should be called', () => {
+ jest.spyOn(formatter.storage, 'getStepTempId').mockImplementationOnce(() => stepTempId);
+ const spySendLog = jest.spyOn(formatter.reportportal, 'sendLog');
+
+ const data = {
+ mediaType,
+ testStepId,
+ testCaseStartedId,
+ body: JSON.stringify(body),
+ };
+
+ formatter.onTestStepAttachmentEvent(data);
+
+ expect(spySendLog).toHaveBeenCalledWith(
+ stepTempId,
+ {
+ time: mockedDate,
+ level: body.level,
+ message: body.message,
+ file: {
+ name: body.message,
+ },
+ },
+ {
+ name: fileName,
+ type: mediaType,
+ content: body.data,
+ },
+ );
+ });
+
+ it('sendLog should be called with launch temp id', () => {
+ jest.spyOn(formatter.storage, 'getLaunchTempId').mockImplementationOnce(() => launchTempId);
+ const spySendLog = jest.spyOn(formatter.reportportal, 'sendLog');
+
+ const data = {
+ mediaType,
+ testStepId,
+ testCaseStartedId,
+ body: JSON.stringify({ ...body, entity: RP_ENTITY_LAUNCH }),
+ };
+
+ formatter.onTestStepAttachmentEvent(data);
+
+ expect(spySendLog).toHaveBeenCalledWith(
+ launchTempId,
+ {
+ time: mockedDate,
+ level: body.level,
+ message: body.message,
+ file: {
+ name: body.message,
+ },
+ },
+ {
+ name: fileName,
+ type: mediaType,
+ content: body.data,
+ },
+ );
+ });
+ });
+ });
+
describe('onTestStepFinishedEvent', () => {
beforeEach(() => {
formatter.onGherkinDocumentEvent(gherkinDocument);
@@ -226,23 +647,153 @@ describe('cucumber-reportportal-formatter', () => {
formatter.onTestStepStartedEvent(testStepStarted);
});
- it('finishTestItem should be called, clean storage', () => {
- const spyFinishTestItem = jest.spyOn(formatter.reportportal, 'finishTestItem');
+ describe('finishsed with failed status', () => {
+ const originBrowser = global.browser;
+ const png = 'base64-data';
- formatter.onTestStepFinishedEvent(testStepFinished);
+ beforeAll(() => {
+ global.browser = {
+ takeScreenshot: jest.fn().mockImplementation(() => Promise.resolve(png)),
+ };
+ });
- expect(spyFinishTestItem).toBeCalledWith('testItemId', {
- endTime: mockedDate,
- status: STATUSES.FAILED,
- description: '```error\nerror message\n```',
+ afterAll(() => {
+ global.browser = originBrowser;
+ });
+
+ it('finishTestItem should be called with failed status, clean storage', () => {
+ const spyFinishTestItem = jest.spyOn(formatter.reportportal, 'finishTestItem');
+
+ formatter.onTestStepFinishedEvent(testStepFinished);
+
+ expect(spyFinishTestItem).toBeCalledWith('testItemId', {
+ endTime: mockedDate,
+ status: STATUSES.FAILED,
+ description: '```error\nerror message\n```',
+ });
+ expect(formatter.storage.getStepTempId(testStepStarted.testStepId)).toBeUndefined();
});
+
+ it('should make screenshot if takeScreenshot === onFailure', () => {
+ const originTakeScreenshot = formatter.config.takeScreenshot;
+
+ const spyTakeScreenshot = jest.spyOn(global.browser, 'takeScreenshot');
+
+ formatter.config.takeScreenshot = 'onFailure';
+ formatter.onTestStepFinishedEvent(testStepFinished);
+ formatter.config.takeScreenshot = originTakeScreenshot;
+
+ expect(spyTakeScreenshot).toBeCalled();
+ });
+ });
+
+ it('finishTestItem should be called with passed status, clean storage', () => {
+ const spyfinishTestItem = jest.spyOn(formatter.reportportal, 'finishTestItem');
+
+ formatter.onTestStepFinishedEvent({
+ ...testStepFinished,
+ testStepResult: { ...testStepFinished.testStepResult, status: STATUSES.PASSED },
+ });
+
+ expect(spyfinishTestItem).toBeCalledWith(
+ 'testItemId',
+ expect.objectContaining({
+ status: STATUSES.PASSED,
+ }),
+ );
expect(formatter.storage.getStepTempId(testStepStarted.testStepId)).toBeUndefined();
});
+
+ it('finishTestItem should be called with unexpected status', () => {
+ const spyfinishTestItem = jest.spyOn(formatter.reportportal, 'finishTestItem');
+
+ formatter.onTestStepFinishedEvent({
+ ...testStepFinished,
+ testStepResult: { ...testStepFinished.testStepResult, status: 'unexpected-status' },
+ });
+
+ expect(spyfinishTestItem).toBeCalledWith(
+ 'testItemId',
+ expect.objectContaining({
+ status: 'unexpected-status',
+ }),
+ );
+ });
+
+ it('finishTestItem should be called with NOT_ISSUE type if SKIPPED status and skippedIssue set to false', () => {
+ const spyfinishTestItem = jest.spyOn(formatter.reportportal, 'finishTestItem');
+
+ formatter.config.skippedIssue = false;
+ formatter.onTestStepFinishedEvent({
+ ...testStepFinished,
+ testStepResult: { ...testStepFinished.testStepResult, status: STATUSES.SKIPPED },
+ });
+
+ expect(spyfinishTestItem).toBeCalledWith(
+ 'testItemId',
+ expect.objectContaining({ status: STATUSES.SKIPPED, issue: { issueType: 'NOT_ISSUE' } }),
+ );
+ });
+
+ it('should set failed status for test case in storage if isScenarioBasedStatistics === true', () => {
+ const originIsScenarioBasedStatistics = formatter.isScenarioBasedStatistics;
+ formatter.isScenarioBasedStatistics = true;
+
+ formatter.onTestStepFinishedEvent(testStepFinished);
+ formatter.isScenarioBasedStatistics = originIsScenarioBasedStatistics;
+
+ expect(formatter.storage.getTestCase(testCaseId).status).toEqual(STATUSES.FAILED);
+ });
+
+ it('sendLog should be called with pending message in case of corresponding status received', () => {
+ const spySendLog = jest.spyOn(formatter.reportportal, 'sendLog');
+
+ formatter.onTestStepFinishedEvent({
+ ...testStepFinished,
+ testStepResult: { ...testStepFinished.testStepResult, status: STATUSES.PENDING },
+ });
+
+ expect(spySendLog).toBeCalledWith('testItemId', {
+ time: mockedDate,
+ level: LOG_LEVELS.WARN,
+ message: TEST_STEP_FINISHED_RP_MESSAGES.PENDING,
+ });
+ });
+
+ it('sendLog should be called with undefined message in case of corresponding status received', () => {
+ const spySendLog = jest.spyOn(formatter.reportportal, 'sendLog');
+
+ formatter.onTestStepFinishedEvent({
+ ...testStepFinished,
+ testStepResult: { ...testStepFinished.testStepResult, status: STATUSES.UNDEFINED },
+ });
+
+ expect(spySendLog).toBeCalledWith('testItemId', {
+ time: mockedDate,
+ level: LOG_LEVELS.ERROR,
+ message: TEST_STEP_FINISHED_RP_MESSAGES.UNDEFINED,
+ });
+ });
+
+ it('sendLog should be called with ambiguous message in case of corresponding status received', () => {
+ const spySendLog = jest.spyOn(formatter.reportportal, 'sendLog');
+
+ formatter.onTestStepFinishedEvent({
+ ...testStepFinished,
+ testStepResult: { ...testStepFinished.testStepResult, status: STATUSES.AMBIGUOUS },
+ });
+
+ expect(spySendLog).toBeCalledWith('testItemId', {
+ time: mockedDate,
+ level: LOG_LEVELS.ERROR,
+ message: TEST_STEP_FINISHED_RP_MESSAGES.AMBIGUOUS,
+ });
+ });
});
describe('onTestCaseFinishedEvent', () => {
beforeEach(() => {
- formatter.onGherkinDocumentEvent(gherkinDocument);
+ formatter.onGherkinDocumentEvent(gherkinDocumentWithRule);
formatter.onPickleEvent(pickle);
formatter.onTestRunStartedEvent();
formatter.onTestCaseEvent(testCase);
diff --git a/tests/data.js b/tests/data.js
index e19dea1..086312e 100644
--- a/tests/data.js
+++ b/tests/data.js
@@ -17,12 +17,19 @@
const launchTempId = 'tempId';
const uri = 'features/statuses/statuses.feature';
const scenarioId = '1957ea93-e4de-4895-86e8-acb857b5b069';
+const parameters = 'row-cell-id';
const ruleId = '2034eaf4-f7hv-8234-55l4-njk687k3k423';
const scenario = {
id: scenarioId,
name: 'scenario name',
keyword: 'Scenario',
steps: [{ id: 'scenarioStepsId', keyword: 'Then', location: { line: 7, column: 5 } }],
+ examples: [
+ {
+ tableHeader: { cells: [{ value: 'header-cells-value1' }] },
+ tableBody: [{ id: parameters, cells: [{ value: 'row-cell-value1' }] }],
+ },
+ ],
};
const feature = {
keyword: 'Feature',
@@ -51,6 +58,12 @@ const step = {
argument: undefined,
astNodeIds: [],
};
+const stepWithParameters = {
+ id: stepId,
+ text: 'I put "true"',
+ argument: undefined,
+ astNodeIds: [scenarioId, parameters],
+};
const pickleId = 'c544ae8c-f080-41be-a612-f3000ac46565';
const pickle = {
id: pickleId,
@@ -59,6 +72,13 @@ const pickle = {
name: 'Given and expected value are equal',
steps: [step],
};
+const pickleWithParameters = {
+ id: pickleId,
+ uri: 'features/statuses/statuses.feature',
+ astNodeIds: [scenarioId, parameters],
+ name: 'Given and expected value are equal',
+ steps: [stepWithParameters],
+};
const hookId = '2b9c9732-acbd-4fa0-8408-42875800d92e';
const hook = {
id: hookId,
@@ -76,6 +96,24 @@ const testCase = {
},
],
};
+const testCaseWithParameters = {
+ pickleId,
+ id: testCaseId,
+ testSteps: [
+ {
+ id: testStepId,
+ pickleStepId: stepId,
+ stepMatchArgumentsLists: [
+ {
+ stepMatchArguments: [
+ { group: { value: '-header-cells-value1-' } },
+ { group: { value: '-row-cell-value1-' } },
+ ],
+ },
+ ],
+ },
+ ],
+};
const testCaseStartedId = '57e2a70f-a001-4774-97fa-1e6d5fdb293b';
const testCaseStarted = {
testCaseId,
@@ -119,6 +157,8 @@ module.exports = {
pickleId,
uri,
pickle,
+ pickleWithParameters,
+ testCaseWithParameters,
hookId,
hook,
testCase,
diff --git a/tests/mocks.js b/tests/mocks.js
index 50788e9..8311130 100644
--- a/tests/mocks.js
+++ b/tests/mocks.js
@@ -36,7 +36,7 @@ class RPClientMock {
}
const getDefaultConfig = () => ({
- token: '00000000-0000-0000-0000-000000000000',
+ apiKey: '00000000-0000-0000-0000-000000000000',
endpoint: 'https://reportportal.server/api/v1',
project: 'ProjectName',
launch: 'LauncherName',
diff --git a/tests/utils.spec.js b/tests/utils.spec.js
index 325e9b9..9bed7d8 100644
--- a/tests/utils.spec.js
+++ b/tests/utils.spec.js
@@ -98,20 +98,6 @@ describe('utils', () => {
expect(utils.findScenario(node, scenarioId)).toBe(expectedRes);
});
- it('bindToClass should add method to class', () => {
- const module = {
- newMethod() {},
- };
- class Test {
- constructor() {
- utils.bindToClass(module, this);
- }
- }
- const instance = new Test();
-
- expect(instance.newMethod).toBeTruthy();
- });
-
it('collectParams should create map from tableHeader & tableBody', () => {
const tableHeader = {
cells: [{ value: 'parameterKey' }],