diff --git a/lib/createTestFromScenario.js b/lib/createTestFromScenario.js index c30a78d1..ceeba49e 100644 --- a/lib/createTestFromScenario.js +++ b/lib/createTestFromScenario.js @@ -33,22 +33,42 @@ const runTest = (scenario, stepsToRun, rowData) => { Object.assign({}, step, { index }) ); - // eslint-disable-next-line func-names - it(scenario.name, function() { - const state = window.testState; - return cy - .then(() => state.onStartScenario(scenario, indexedSteps)) - .then(() => - resolveAndRunBeforeHooks.call(this, scenario.tags, state.feature.name) - ) - .then(() => - indexedSteps.forEach(step => stepTest.call(this, state, step, rowData)) - ) - .then(() => - resolveAndRunAfterHooks.call(this, scenario.tags, state.feature.name) - ) - .then(() => state.onFinishScenario(scenario)); - }); + // should we actually run this scenario + // or just mark it as skipped + if (scenario.shouldRun) { + // eslint-disable-next-line func-names + it(scenario.name, function() { + const state = window.testState; + return cy + .then(() => state.onStartScenario(scenario, indexedSteps)) + .then(() => + resolveAndRunBeforeHooks.call(this, scenario.tags, state.feature.name) + ) + .then(() => + indexedSteps.forEach(step => + stepTest.call(this, state, step, rowData) + ) + ) + .then(() => + resolveAndRunAfterHooks.call(this, scenario.tags, state.feature.name) + ) + .then(() => state.onFinishScenario(scenario)); + }); + } else { + // eslint-disable-next-line func-names,prefer-arrow-callback + it(scenario.name, function() { + // register this scenario with the cucumber data collector + // but don't run it + // Tell mocha this is a skipped test so it also shows correctly in Cypress + const state = window.testState; + cy.then(() => state.onStartScenario(scenario, indexedSteps)) + .then(() => state.onFinishScenario(scenario)) + // eslint-disable-next-line func-names + .then(function() { + return this.skip(); + }); + }); + } }; const cleanupFilename = s => s.split(".")[0]; @@ -64,7 +84,7 @@ const writeCucumberJsonFile = json => { }; const createTestFromScenarios = ( - scenariosToRun, + allScenarios, backgroundSection, testState ) => { @@ -87,7 +107,7 @@ const createTestFromScenarios = ( Cypress.on("fail", failHandler); }); - scenariosToRun.forEach(section => { + allScenarios.forEach(section => { if (section.examples) { section.examples.forEach(example => { const exampleValues = []; diff --git a/lib/createTestsFromFeature.js b/lib/createTestsFromFeature.js index a5bc69a4..caa42723 100644 --- a/lib/createTestsFromFeature.js +++ b/lib/createTestsFromFeature.js @@ -6,58 +6,37 @@ const createTestsFromFeature = (filePath, spec) => { const testState = new CucumberDataCollector(filePath, spec); const featureTags = testState.feature.tags; const hasEnvTags = !!getEnvTags(); - const sectionsWithTags = testState.feature.children.filter( - section => section.tags && section.tags.length + const anyFocused = + testState.feature.children.filter( + section => section.tags && section.tags.find(t => t.name === "@focus") + ).length > 0; + const backgroundSection = testState.feature.children.find( + section => section.type === "Background" + ); + const allScenarios = testState.feature.children.filter( + section => section.type !== "Background" ); - const sectionsWithTagsExist = sectionsWithTags.length > 0; - - let everythingShouldRun = false; - let featureShouldRun = false; - let taggedScenarioShouldRun = false; - let anyFocused = false; - if (hasEnvTags) { - featureShouldRun = shouldProceedCurrentStep(featureTags); - taggedScenarioShouldRun = testState.feature.children.some( - section => - section.tags && - section.tags.length && - shouldProceedCurrentStep(section.tags.concat(featureTags)) - ); - } else if (!sectionsWithTagsExist) { - everythingShouldRun = true; - } else { - anyFocused = sectionsWithTags.some(section => - section.tags.find(t => t.name === "@focus") - ); - if (anyFocused) { - taggedScenarioShouldRun = true; + const scenariosToRun = allScenarios.filter(section => { + let shouldRun; + // only just run focused if no env tags set + // https://github.com/TheBrainFamily/cypress-cucumber-example#smart-tagging + if (!hasEnvTags && anyFocused) { + shouldRun = section.tags.find(t => t.name === "@focus"); } else { - everythingShouldRun = true; + shouldRun = + !hasEnvTags || + shouldProceedCurrentStep(section.tags.concat(featureTags)); // Concat handles inheritance of tags from feature } - } - - // eslint-disable-next-line prefer-arrow-callback - if (everythingShouldRun || featureShouldRun || taggedScenarioShouldRun) { - const backgroundSection = testState.feature.children.find( - section => section.type === "Background" - ); - const otherSections = testState.feature.children.filter( - section => section.type !== "Background" - ); - const scenariosToRun = otherSections.filter(section => { - let shouldRun; - if (anyFocused) { - shouldRun = section.tags.find(t => t.name === "@focus"); - } else { - shouldRun = - everythingShouldRun || - shouldProceedCurrentStep(section.tags.concat(featureTags)); // Concat handles inheritance of tags from feature - } - return shouldRun; - }); - createTestFromScenarios(scenariosToRun, backgroundSection, testState); - } + return shouldRun; + }); + // create tests for all the scenarios + // but flag only the ones that should be run + scenariosToRun.forEach(section => { + // eslint-disable-next-line no-param-reassign + section.shouldRun = true; + }); + createTestFromScenarios(allScenarios, backgroundSection, testState); }; module.exports = { diff --git a/lib/cukejson/cucumberDataCollector.js b/lib/cukejson/cucumberDataCollector.js index b494d382..002621af 100644 --- a/lib/cukejson/cucumberDataCollector.js +++ b/lib/cukejson/cucumberDataCollector.js @@ -72,6 +72,14 @@ class CucumberDataCollector { status: statuses.UNDEFINED, duration: this.timeTaken() }; + } else if (err.constructor.name === "Pending") { + // cypress marks skipped mocha tests as pending + // https://github.com/cypress-io/cypress/issues/3092 + // don't record this error and mark the step as skipped + this.stepResults[this.currentStep] = { + status: statuses.SKIPPED, + duration: this.timeTaken() + }; } else { this.stepResults[this.currentStep] = { status: statuses.FAILED, @@ -122,9 +130,13 @@ class CucumberDataCollector { }); }; this.recordScenarioResult = scenario => { - this.runTests[scenario.name].result = this.anyStepsHaveFailed(scenario) - ? statuses.FAILED - : statuses.PASSED; + const allSkipped = this.areAllStepsSkipped(scenario.name); + const anyFailed = this.anyStepsHaveFailed(scenario.name); + if (allSkipped) this.runTests[scenario.name].result = statuses.SKIPPED; + else + this.runTests[scenario.name].result = anyFailed + ? statuses.FAILED + : statuses.PASSED; }; this.setStepToPending = step => { @@ -138,8 +150,11 @@ class CucumberDataCollector { }; }; - this.anyStepsHaveFailed = () => - Object.values(this.stepResults).find(e => e.status !== statuses.PASSED); + this.areAllStepsSkipped = name => + this.runTests[name].every(e => e.status === statuses.SKIPPED); + + this.anyStepsHaveFailed = name => + this.runTests[name].find(e => e.status === statuses.FAILED) !== undefined; } } diff --git a/lib/testHelpers/setupTestFramework.js b/lib/testHelpers/setupTestFramework.js index a636dbbf..d25311a0 100644 --- a/lib/testHelpers/setupTestFramework.js +++ b/lib/testHelpers/setupTestFramework.js @@ -3,6 +3,7 @@ global.expect = require("chai").expect; global.before = jest.fn(); global.after = jest.fn(); +global.skip = jest.fn(); window.Cypress = { env: jest.fn(),