Feature: Environment Hooks # The following scenario is a regression test for special "around" hooks which # deserve a bit more of attention. Scenario: Tagged around hook with untagged scenario Given an around hook tagged with "@foo" When Cucumber executes a scenario with no tags Then the hook is not fired Scenario: Hooks are steps Given a file named "features/a.feature" with: """ Feature: some feature Scenario: I've declared one step and it is passing Given This step is passing """ And a file named "features/step_definitions/cucumber_steps.js" with: """ var cucumberSteps = function() { this.Given(/^This step is passing$/, function(callback) { callback(); }); }; module.exports = cucumberSteps; """ And a file named "features/support/hooks.js" with: """ var hooks = function () { this.Before(function(callback) { callback(); }); this.After(function(callback) { callback(); }); this.Around(function(runScenario) { runScenario(function(callback) { callback(); }); }); }; module.exports = hooks; """ When I run `cucumber.js -f json` Then it outputs this json: """ [ { "id": "some-feature", "name": "some feature", "description": "", "line": 1, "keyword": "Feature", "uri": "/features/a.feature", "elements": [ { "name": "I've declared one step and it is passing", "id": "some-feature;i've-declared-one-step-and-it-is-passing", "line": 3, "keyword": "Scenario", "description": "", "type": "scenario", "steps": [ { "keyword": "Around ", "hidden": true, "result": { "duration": "", "status": "passed" }, "match": {} }, { "keyword": "Before ", "hidden": true, "result": { "duration": "", "status": "passed" }, "match": {} }, { "name": "This step is passing", "line": 4, "keyword": "Given ", "result": { "duration": "", "status": "passed" }, "match": {} }, { "keyword": "After ", "hidden": true, "result": { "duration": "", "status": "passed" }, "match": {} }, { "keyword": "Around ", "hidden": true, "result": { "duration": "", "status": "passed" }, "match": {} } ] } ] } ] """ Scenario Outline: Failing around hook (pre scenario) fails the scenario Given a file named "features/a.feature" with: """ Feature: some feature Scenario: I've declared one step and it is passing Given This step is passing """ And a file named "features/step_definitions/cucumber_steps.js" with: """ var cucumberSteps = function() { this.Given(/^This step is passing$/, function(callback) { callback(); }); }; module.exports = cucumberSteps; """ And a file named "features/support/hooks.js" with: """ var hooks = function () { this.Around(function(runScenario) { }); }; module.exports = hooks; """ When I run `cucumber.js -f json` Then it outputs this json: """ [ { "id": "some-feature", "name": "some feature", "description": "", "line": 1, "keyword": "Feature", "uri": "/features/a.feature", "elements": [ { "name": "I've declared one step and it is passing", "id": "some-feature;i've-declared-one-step-and-it-is-passing", "line": 3, "keyword": "Scenario", "description": "", "type": "scenario", "steps": [ { "keyword": "Around ", "hidden": true, "result": { "error_message": "", "duration": "", "status": "failed" }, "match": {} }, { "name": "This step is passing", "line": 4, "keyword": "Given ", "result": { "status": "skipped" }, "match": {} }, { "keyword": "Around ", "hidden": true, "result": { "duration": "", "status": "passed" }, "match": {} } ] } ] } ] """ Examples: | fail_approach | | runScenario('Fail', function(callback) { callback(); }); | | runScenario.fail(); | Scenario Outline: Failing around hook (post scenario) fails the scenario Given a file named "features/a.feature" with: """ Feature: some feature Scenario: I've declared one step and it is passing Given This step is passing """ And a file named "features/step_definitions/cucumber_steps.js" with: """ var cucumberSteps = function() { this.Given(/^This step is passing$/, function(callback) { callback(); }); }; module.exports = cucumberSteps; """ And a file named "features/support/hooks.js" with: """ var hooks = function () { this.Around(function(runScenario) { // no-op runScenario(function(callback) { }); }); }; module.exports = hooks; """ When I run `cucumber.js -f json` Then it outputs this json: """ [ { "id": "some-feature", "name": "some feature", "description": "", "line": 1, "keyword": "Feature", "uri": "/features/a.feature", "elements": [ { "name": "I've declared one step and it is passing", "id": "some-feature;i've-declared-one-step-and-it-is-passing", "line": 3, "keyword": "Scenario", "description": "", "type": "scenario", "steps": [ { "keyword": "Around ", "hidden": true, "result": { "duration": "", "status": "passed" }, "match": {} }, { "name": "This step is passing", "line": 4, "keyword": "Given ", "result": { "duration": "", "status": "passed" }, "match": {} }, { "keyword": "Around ", "hidden": true, "result": { "error_message": "", "duration": "", "status": "failed" }, "match": {} } ] } ] } ] """ Examples: | fail_approach | | callback('Fail'); | | callback.fail(); | Scenario Outline: Failing before hook fails the scenario Given a file named "features/a.feature" with: """ Feature: some feature Scenario: I've declared one step and it is passing Given This step is passing """ And a file named "features/step_definitions/cucumber_steps.js" with: """ var cucumberSteps = function() { this.Given(/^This step is passing$/, function(callback) { callback(); }); }; module.exports = cucumberSteps; """ And a file named "features/support/hooks.js" with: """ var hooks = function () { this.Before(function(callback) { }); }; module.exports = hooks; """ When I run `cucumber.js -f json` Then it outputs this json: """ [ { "id": "some-feature", "name": "some feature", "description": "", "line": 1, "keyword": "Feature", "uri": "/features/a.feature", "elements": [ { "name": "I've declared one step and it is passing", "id": "some-feature;i've-declared-one-step-and-it-is-passing", "line": 3, "keyword": "Scenario", "description": "", "type": "scenario", "steps": [ { "keyword": "Before ", "hidden": true, "result": { "error_message": "", "duration": "", "status": "failed" }, "match": {} }, { "name": "This step is passing", "line": 4, "keyword": "Given ", "result": { "status": "skipped" }, "match": {} } ] } ] } ] """ Examples: | fail_approach | | callback('Fail'); | | callback.fail(); | Scenario Outline: Failing after hook fails the scenario Given a file named "features/a.feature" with: """ Feature: some feature Scenario: I've declared one step and it is passing Given This step is passing """ And a file named "features/step_definitions/cucumber_steps.js" with: """ var cucumberSteps = function() { this.Given(/^This step is passing$/, function(callback) { callback(); }); }; module.exports = cucumberSteps; """ And a file named "features/support/hooks.js" with: """ var hooks = function () { this.After(function(callback) { }); }; module.exports = hooks; """ When I run `cucumber.js -f json` Then it outputs this json: """ [ { "id": "some-feature", "name": "some feature", "description": "", "line": 1, "keyword": "Feature", "uri": "/features/a.feature", "elements": [ { "name": "I've declared one step and it is passing", "id": "some-feature;i've-declared-one-step-and-it-is-passing", "line": 3, "keyword": "Scenario", "description": "", "type": "scenario", "steps": [ { "name": "This step is passing", "line": 4, "keyword": "Given ", "result": { "duration": "", "status": "passed" }, "match": {} }, { "keyword": "After ", "hidden": true, "result": { "error_message": "", "duration": "", "status": "failed" }, "match": {} } ] } ] } ] """ Examples: | fail_approach | | callback('Fail'); | | callback.fail(); | Scenario: Hooks still execute after a failure Given a file named "features/a.feature" with: """ Feature: some feature Scenario: I've declared one step and it is passing Given This step is passing """ And a file named "features/step_definitions/cucumber_steps.js" with: """ var cucumberSteps = function() { this.Given(/^This step is passing$/, function(callback) { callback(); }); }; module.exports = cucumberSteps; """ And a file named "features/support/hooks.js" with: """ var hooks = function () { this.Around(function(scenario, runScenario) { runScenario("fail", function(callback) { callback(); }); }); this.Around(function(scenario, runScenario) { runScenario(function(callback) { callback(); }); }); this.Before(function(scenario, callback) { callback(); }); this.After(function(scenario, callback) { callback(); }); }; module.exports = hooks; """ When I run `cucumber.js -f json` Then it outputs this json: """ [ { "id": "some-feature", "name": "some feature", "description": "", "line": 1, "keyword": "Feature", "uri": "/features/a.feature", "elements": [ { "name": "I've declared one step and it is passing", "id": "some-feature;i've-declared-one-step-and-it-is-passing", "line": 3, "keyword": "Scenario", "description": "", "type": "scenario", "steps": [ { "keyword": "Around ", "hidden": true, "result": { "error_message": "", "duration": "", "status": "failed" }, "match": {} }, { "keyword": "Around ", "hidden": true, "result": { "duration": "", "status": "passed" }, "match": {} }, { "keyword": "Before ", "hidden": true, "result": { "duration": "", "status": "passed" }, "match": {} }, { "name": "This step is passing", "line": 4, "keyword": "Given ", "result": { "status": "skipped" }, "match": {} }, { "keyword": "After ", "hidden": true, "result": { "duration": "", "status": "passed" }, "match": {} }, { "keyword": "Around ", "hidden": true, "result": { "duration": "", "status": "passed" }, "match": {} }, { "keyword": "Around ", "hidden": true, "result": { "duration": "", "status": "passed" }, "match": {} } ] } ] } ] """ Scenario: World is this in hooks Given a file named "features/a.feature" with: """ Feature: some feature Scenario: I've declared one step and it is passing Given This step is passing """ And a file named "features/step_definitions/cucumber_steps.js" with: """ var cucumberSteps = function() { this.Given(/^This step is passing$/, function(callback) { callback(); }); }; module.exports = cucumberSteps; """ And a file named "features/support/world.js" with: """ var WorldConstructor = function WorldConstructor(callback) { var world = { isWorld: function() { return true; } }; callback(world); // tell Cucumber we're finished and to use our world object instead of 'this' }; module.exports.World = WorldConstructor; """ And a file named "features/support/hooks.js" with: """ var hooks = function () { this.World = require("../support/world.js").World; this.Before(function(callback) { var world = this; if (!world.isWorld()) callback("Expected this to be world"); else callback(); }); this.After(function(callback) { var world = this; if (!world.isWorld()) callback("Expected this to be world"); else callback(); }); this.Around(function(runScenario) { var world = this; var error; if (!world.isWorld()) error = "Expected this to be world"; else error = null; runScenario(error, function(callback) { var world = this; var error; if (!world.isWorld()) error = "Expected this to be world"; else error = null; callback(error); }); }); }; module.exports = hooks; """ When I run `cucumber.js -f json` Then it outputs this json: """ [ { "id": "some-feature", "name": "some feature", "description": "", "line": 1, "keyword": "Feature", "uri": "/features/a.feature", "elements": [ { "name": "I've declared one step and it is passing", "id": "some-feature;i've-declared-one-step-and-it-is-passing", "line": 3, "keyword": "Scenario", "description": "", "type": "scenario", "steps": [ { "keyword": "Around ", "hidden": true, "result": { "duration": "", "status": "passed" }, "match": {} }, { "keyword": "Before ", "hidden": true, "result": { "duration": "", "status": "passed" }, "match": {} }, { "name": "This step is passing", "line": 4, "keyword": "Given ", "result": { "duration": "", "status": "passed" }, "match": {} }, { "keyword": "After ", "hidden": true, "result": { "duration": "", "status": "passed" }, "match": {} }, { "keyword": "Around ", "hidden": true, "result": { "duration": "", "status": "passed" }, "match": {} } ] } ] } ] """