Skip to content
Browse files

Integrate new core feature running through Cucumber.rb and Aruba

- Progress formatter is under development (issues #16, #17)
- core.feature first scenario is passing

Squashed commit of the following:

commit ed3db19c77f40107b3a2038ace8267d4cb642e85
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jul 5 23:46:07 2011 +0200

    Make progress formatter handle failing steps

commit 998dc26bc327876d23c4e7fdc2526a133659db99
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jul 5 23:00:52 2011 +0200

    Assert cucumber.js succeeds on passing scenario

commit 852c214c61c36077e70c0a11d8f4bf06ff685f0b
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jul 5 22:49:20 2011 +0200

    Integrate new core feature running through Cucumber.rb and Aruba

commit 75b82ed9317304a0ed9f3b8c56c3b2c0ae35caa9
Merge: 749512a b5ff2a7
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jul 5 21:15:56 2011 +0200

    Merge branch 'aruba-new-core-local' into 16-progress_formatter

commit 749512a4aa652dd8eac39e133427e2a26156bae1
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jul 5 18:59:51 2011 +0200

    Add a scenario for failing steps

    On hold because failing steps are not yet handled

commit 4bcd476544e4c094e9862fcf209c7a2446d63a8b
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jul 5 16:45:45 2011 +0200

    Handle multi-scenario features

commit b5ff2a7d12519a8434b14b4380a6662880bb798c
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jul 5 11:18:51 2011 +0200

    WIP

commit acecc9acf8c547bcaffd1e827247ad66c4562947
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jul 5 11:05:29 2011 +0200

    Fix CucumberJsMappings module name

commit afe3460dea101c2b6306a1ceb62a6354bb2773f8
Author: Julien Biezemans <jb@jbpros.com>
Date:   Wed Jun 29 00:16:55 2011 +0200

    Make first new core scenario pass with Aruba

    JS mappings are not semantically correct yet. e.g. cucumber.js cannot run specific scenarios at the moment; this prevents us from implementing run_scenario() the same way as within ruby mappings.

    To run it:
        $ ARUBA_REPORT_DIR=doc cucumber features/cucumber-features/core.feature:16 -r features ; open doc/features/cucumber-features/core.feature\:16.html

commit 87d27c739fea70fc4783737f35b6a6f137eb76f9
Author: Julien Biezemans <jb@jbpros.com>
Date:   Sun Jun 19 22:21:26 2011 +0200

    Count passed steps and scenarios in progress formatter (wip)

commit 15d749bac0f6956646df65b1fdf8e92cfbd62b8f
Author: Julien Biezemans <jb@jbpros.com>
Date:   Sun Jun 19 11:48:38 2011 +0200

    Add initial working progress formatter

commit dc3301a5148b8737cf838b7435e6aac8e797bc14
Author: Julien Biezemans <jb@jbpros.com>
Date:   Thu Jun 16 23:16:13 2011 +0200

    Handle step result (wip)

commit f5f6268714a6310cfeaf2c6ed9445c1921053eca
Merge: 0947988 8ae4c0e
Author: Julien Biezemans <jb@jbpros.com>
Date:   Wed Jun 15 00:37:15 2011 +0200

    Merge branch 'master' into 16-progress_formatter

commit 0947988a580714856b1d551ee9acdcc9ddc15a9f
Merge: 7f595b4 252634f
Author: Julien Biezemans <jb@jbpros.com>
Date:   Wed Jun 15 00:30:04 2011 +0200

    Merge branch 'master' into 16-progress_formatter

commit 7f595b48713b352042d2c382e19841957cd42b76
Author: Julien Biezemans <jb@jbpros.com>
Date:   Wed Jun 15 00:19:31 2011 +0200

    Add ProgressFormatter.listen()

commit a08b10b557e90e0f6ef3cdcb96558b7c45a6daf5
Merge: 0857a6c fc83930
Author: Julien Biezemans <jb@jbpros.com>
Date:   Tue Jun 14 19:20:56 2011 +0200

    Merge branch 'master' into 16-progress_formatter

    Conflicts:
    	lib/cucumber.js

commit 0857a6c
Author: Julien Biezemans <jb@jbpros.com>
Date:   Mon Jun 6 15:52:28 2011 +0200

    Stuck because of 'after' events fired too late -- on hold until refactoring is finished
  • Loading branch information...
1 parent 8ae4c0e commit f3b40fd824df932490c04130e29bd286187db9fc @jbpros jbpros committed Jul 5, 2011
View
3 .gitignore
@@ -2,3 +2,6 @@
node_modules
*~
.#*
+.rvmrc
+doc/
+tmp/
View
2 Gemfile
@@ -0,0 +1,2 @@
+source :rubygems
+gem "aruba", "0.4.3"
View
42 Gemfile.lock
@@ -0,0 +1,42 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ aruba (0.4.3)
+ bcat (>= 0.6.1)
+ childprocess (>= 0.1.9)
+ cucumber (>= 0.10.7)
+ rdiscount (>= 1.6.8)
+ rspec (>= 2.6.0)
+ bcat (0.6.1)
+ rack (~> 1.0)
+ builder (3.0.0)
+ childprocess (0.1.9)
+ ffi (~> 1.0.6)
+ cucumber (1.0.0)
+ builder (>= 2.1.2)
+ diff-lcs (>= 1.1.2)
+ gherkin (~> 2.4.1)
+ json (>= 1.4.6)
+ term-ansicolor (>= 1.0.5)
+ diff-lcs (1.1.2)
+ ffi (1.0.9)
+ gherkin (2.4.1)
+ json (>= 1.4.6)
+ json (1.5.3)
+ rack (1.3.0)
+ rdiscount (1.6.8)
+ rspec (2.6.0)
+ rspec-core (~> 2.6.0)
+ rspec-expectations (~> 2.6.0)
+ rspec-mocks (~> 2.6.0)
+ rspec-core (2.6.4)
+ rspec-expectations (2.6.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.6.0)
+ term-ansicolor (1.0.5)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ aruba (= 0.4.3)
View
23 README.md
@@ -32,15 +32,30 @@ The only dependency of cucumber.js is Gherkin:
## Run tests
-Specs:
+### Specs
$ cd spec
$ ../node_modules/.bin/jasmine-node .
-Features (yes, cucumber.js is eating itself):
+### Features
+
+Features run through cucumber.js have to be run one at a time for the moment. We are working on it :)
+
+#### Cucumber-features
+
+There is a common set of features shared between all cucumber implementations. Find more the [Github repository](http://github.com/cucumber/cucumber-features).
+
+Ruby and Bundler are required for this to work.
$ git submodule update --init
- $ ./cucumber.js features/cucumber-features/basic_feature_execution.feature
+ $ bundle
+ $ rm -rf doc; ARUBA_REPORT_DIR=doc cucumber features/cucumber-features/core.feature -r features
+ $ open doc/features/cucumber-features/*.html # might open a lot of files ;)
+
+#### Cucumber.js-specific features
+
$ ./cucumber.js features/asynchronous_step_definitions-issue_01.feature
-The features have to be run one at a time for the moment. Sorry for that, we're working on it :)
+#### Legacy cucumber.js core feature
+
+ $ ./cucumber.js features/cucumber-features-legacy/core.feature
View
17 cucumber.js
@@ -1,10 +1,11 @@
#!/usr/bin/env node
-var fs = require('fs');
-var Cucumber = require('./lib/cucumber');
-var supportCode = require('./features/step_definitions/cucumber_steps');
-var cucumber = Cucumber(fs.readFileSync(process.ARGV[2]), supportCode);
-var simpleListener = Cucumber.Debug.SimpleAstListener({logToConsole: true});
-cucumber.attachListener(simpleListener);
+var fs = require('fs');
+var Cucumber = require('./lib/cucumber');
+var supportCodePath = process.ARGV[3] ? process.cwd() + '/' + process.ARGV[3] : './features/step_definitions/cucumber_steps';
+var supportCode = require(supportCodePath);
+var cucumber = Cucumber(fs.readFileSync(process.ARGV[2]), supportCode);
+var progressFormatter = Cucumber.Listener.ProgressFormatter;
+cucumber.attachListener(progressFormatter());
cucumber.start(function() {
/*
Some "after" messages from the tree walker are still not processed when
@@ -13,10 +14,10 @@ cucumber.start(function() {
Cucumber.TreeWalker.broadcastMessagesBeforeAndAfterFunction() using no
callback and expecting the passed function to handle the possible
callback.
-
+
Fixing this is easy: broadcastMessagesBeforeAndAfterFunction() would
need a callback and run it AFTER broadcasting "after" messages.
-
+
Uncomment the following line to see that behaviour:
*/
// console.log("Done. But too soon, some dudes haven't finished doing their stuff just yet.");
2 features/cucumber-features
@@ -1 +1 @@
-Subproject commit ac303f2bdb493e2a23e00f8e5f93ca126055b883
+Subproject commit 5dcb569f6e2d8d4a6803893298d769a100a06b19
View
124 features/cucumber-features-legacy/core.feature
@@ -0,0 +1,124 @@
+Feature: Core feature elements execution
+ In order to have automated acceptance tests
+ As a developer
+ I want Cucumber to run core feature elements
+
+ Scenario: Simple flat steps
+ Given a step definition matching /^a step passes$/
+ When I run the following feature:
+ """
+ Feature: Simple flat steps
+ In order to execute features
+ As cucumber
+ I want to run features successfully
+
+ Scenario: Simple flat step
+ Given a step passes
+ When a step passes
+ Then a step passes
+ """
+ Then the feature should have run successfully
+
+ Scenario: Given, When, Then, And and But steps
+ Given a "Given" step definition matching /^a "Given" step passes$/
+ And a "When" step definition matching /^a "When" step passes$/
+ And a "Then" step definition matching /^a "Then" step passes$/
+ When I run the following feature:
+ """
+ Feature: Given, When, Then, And and But step execution
+ Scenario: All kinds of steps
+ Given a "Given" step passes
+ When a "When" step passes
+ Then a "Then" step passes
+
+ Scenario: All kinds of steps with And's and But's
+ Given a "Given" step passes
+ And a "Given" step passes
+ But a "Given" step passes
+ When a "When" step passes
+ And a "When" step passes
+ But a "When" step passes
+ Then a "Then" step passes
+ And a "Then" step passes
+ But a "Then" step passes
+ """
+ Then the feature should have run successfully
+
+ Scenario: Step definition body is executed
+ Given a step definition matching /^I call a watched step$/ counting its calls
+ And a step definition matching /^the watched step should have been called (\d+) times?$/ checking the number of step calls
+ When I run the following feature:
+ """
+ Feature: Step definition body execution
+ Scenario: Step definition body is executed once
+ When I call a watched step
+ Then the watched step should have been called 1 time
+
+ Scenario: Step definition body is executed several times
+ When I call a watched step
+ And I call a watched step
+ And I call a watched step
+ Then the watched step should have been called 3 times
+ """
+ Then the feature should have run successfully
+
+ Scenario: Steps accepting parameters
+ Given a step definition matching /^I call a step with "(.*)"$/ recording its parameters
+ And a step definition matching /^I call a step with "(.*)", "(.*)" and "(.*)"$/ recording its parameters
+ And a step definition matching /^the (\d+)(?:st|nd|rd) received parameter should be "(.*)"$/ checking a recorded parameter
+ When I run the following feature:
+ """
+ Feature: Steps receiving parameters
+ Scenario: Single-parameter step
+ When I call a step with "a parameter"
+ Then the 1st received parameter should be "a parameter"
+
+ Scenario: Three-parameter step
+ When I call a step with "one", "two" and "three"
+ Then the 1st received parameter should be "one"
+ And the 2nd received parameter should be "two"
+ And the 3rd received parameter should be "three"
+ """
+ Then the feature should have run successfully
+
+ Scenario: Steps accepting a DocString parameter
+ Given a step definition matching /^I call a step with the following text:$/ recording its parameters
+ And a step definition matching /^I call a step with "(.*)" and the following text:$/ recording its parameters
+ And a step definition matching /^the (\d+)(?:st|nd) received parameter should be "(.*)"$/ checking a recorded parameter
+ And a step definition matching /^the (\d+)(?:nd) received parameter should be:$/ checking a recorded parameter
+ When I run the following feature:
+ """
+ Feature: Steps receiving a DocString parameter
+ Scenario: One-liner DocString parameter
+ When I call a step with the following text:
+ \"\"\"
+ The cucumber (Cucumis sativus) is a widely cultivated plant in the gourd family Cucurbitaceae.
+ \"\"\"
+ Then the 1st received parameter should be "The cucumber (Cucumis sativus) is a widely cultivated plant in the gourd family Cucurbitaceae."
+
+ Scenario: Matching group and one-liner DocString
+ When I call a step with "Cucumber" and the following text:
+ \"\"\"
+ The cucumber (Cucumis sativus) is a widely cultivated plant in the gourd family Cucurbitaceae.
+ \"\"\"
+ Then the 1st received parameter should be "Cucumber"
+ And the 2nd received parameter should be "The cucumber (Cucumis sativus) is a widely cultivated plant in the gourd family Cucurbitaceae."
+
+ Scenario: Matching group and multiline DocString
+ When I call a step with "Cucumber" and the following text:
+ \"\"\"
+ cu·cum·ber |ˈkyoōˌkəmbər|
+ noun
+ 1. a long, green-skinned fruit with watery flesh, usually eaten raw in salads or pickled.
+ 2. the climbing plant of the gourd family that yields this fruit, native to the Chinese Himalayan region. It is widely cultivated but very rare in the wild. • Cucumis sativus, family Cucurbitaceae.
+ \"\"\"
+ Then the 1st received parameter should be "Cucumber"
+ And the 2nd received parameter should be:
+ \"\"\"
+ cu·cum·ber |ˈkyoōˌkəmbər|
+ noun
+ 1. a long, green-skinned fruit with watery flesh, usually eaten raw in salads or pickled.
+ 2. the climbing plant of the gourd family that yields this fruit, native to the Chinese Himalayan region. It is widely cultivated but very rare in the wild. • Cucumis sativus, family Cucurbitaceae.
+ \"\"\"
+ """
+ Then the feature should have run successfully
View
187 features/step_definitions/cucumber_js_mappings.rb
@@ -0,0 +1,187 @@
+# FIXME: Rename to CucumberJsMappings?
+module CucumberJsMappings
+ STEP_DEFINITIONS_FILE = "features/step_definitions/cucumber_steps.js"
+ FEATURE_FILE = "features/a_feature.feature"
+
+ def features_dir
+ 'features'
+ end
+
+ def run_scenario(scenario_name)
+ # FIXME: do not run the whole feature but only the scenario:
+ # run_simple "#{cucumber_bin} #{FEATURE_FILE} --name '#{scenario_name}'", false
+ append_to_file(STEP_DEFINITIONS_FILE, "};\nmodule.exports = stepDefinitions;")
+ run_simple "#{cucumber_bin} #{FEATURE_FILE} #{STEP_DEFINITIONS_FILE}", false
+ end
+
+# def run_feature
+# run_simple "#{cucumber_bin} #{FEATURE_FILE}", false
+# end
+
+ def cucumber_bin
+ File.expand_path(File.dirname(__FILE__) + '/../../cucumber.js')
+ end
+
+ def write_passing_mapping(step_name)
+ @mapping_count ||= 0
+ if @mapping_count == 0
+ step_definition = "var fs = require('fs');\nvar stepDefinitions = function() {\n"
+ else
+ step_definition = "\n"
+ end
+ erb = ERB.new(<<-EOF, nil, '-')
+ Given(/<%= step_name -%>/, function(callback){
+ fs.writeFileSync("<%= step_file(step_name) %>", "");
+ callback();
+ });
+EOF
+ @mapping_count += 1
+ step_definition += erb.result(binding)
+ append_to_file(STEP_DEFINITIONS_FILE, step_definition)
+ end
+
+# def write_pending_mapping(step_name)
+# erb = ERB.new(<<-EOF, nil, '-')
+# Given /<%= step_name -%>/ do
+# # ARUBA_IGNORE_START
+# File.open("<%= step_file(step_name) %>", "w")
+# # ARUBA_IGNORE_END
+# pending
+# end
+
+# EOF
+# append_to_file("features/step_definitions/some_stepdefs.rb", erb.result(binding))
+# end
+
+ def write_failing_mapping(step_name)
+ @mapping_count ||= 0
+ if @mapping_count == 0
+ step_definition = "var fs = require('fs');\nvar stepDefinitions = function() {\n"
+ else
+ step_definition = "\n"
+ end
+ erb = ERB.new(<<-EOF, nil, '-')
+ Given(/<%= step_name -%>/, function(callback){
+ fs.writeFileSync("<%= step_file(step_name) %>", "");
+ throw "Boom!";
+ callback();
+ });
+EOF
+ @mapping_count += 1
+ step_definition += erb.result(binding)
+ append_to_file(STEP_DEFINITIONS_FILE, step_definition)
+ end
+
+# def write_calculator_code
+# code = <<-EOF
+# # http://en.wikipedia.org/wiki/Reverse_Polish_notation
+# class RpnCalculator
+# def initialize
+# @stack = []
+# end
+
+# def push(arg)
+# if(%w{- + * /}.index(arg))
+# y, x = @stack.pop(2)
+# push(x.__send__(arg, y))
+# else
+# @stack.push(arg)
+# end
+# end
+
+# def PI
+# push(Math::PI)
+# end
+
+# def value
+# @stack[-1]
+# end
+# end
+# EOF
+# write_file("lib/rpn_calculator.rb", code)
+# end
+
+# def write_mappings_for_calculator
+# write_file("features/support/env.rb", "$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')\n")
+# mapping_code = <<-EOF
+# require 'rpn_calculator'
+
+# Given /^a calculator$/ do
+# @calc = RpnCalculator.new
+# end
+
+# When /^the calculator computes PI$/ do
+# @calc.PI
+# end
+
+# When /^the calculator adds up ([\\d\\.]+) and ([\\d\\.]+)$/ do |n1, n2|
+# @calc.push(n1.to_f)
+# @calc.push(n2.to_f)
+# @calc.push('+')
+# end
+
+# When /^the calculator adds up "([^"]*)" and "([^"]*)"$/ do |n1, n2|
+# @calc.push(n1.to_i)
+# @calc.push(n2.to_i)
+# @calc.push('+')
+# end
+
+# When /^the calculator adds up "([^"]*)", "([^"]*)" and "([^"]*)"$/ do |n1, n2, n3|
+# @calc.push(n1.to_i)
+# @calc.push(n2.to_i)
+# @calc.push(n3.to_i)
+# @calc.push('+')
+# @calc.push('+')
+# end
+
+# When /^the calculator adds up the following numbers:$/ do |numbers|
+# pushed = 0
+# numbers.split("\\n").each do |n|
+# @calc.push(n.to_i)
+# pushed +=1
+# @calc.push('+') if pushed > 1
+# end
+# end
+
+# Then /^the calculator returns PI$/ do
+# @calc.value.to_f.should be_within(0.00001).of(Math::PI)
+# end
+
+# Then /^the calculator returns "([^"]*)"$/ do |expected|
+# @calc.value.to_f.should be_within(0.00001).of(expected.to_f)
+# end
+
+# Then /^the calculator does not return ([\\d\\.]+)$/ do |unexpected|
+# @calc.value.to_f.should_not be_within(0.00001).of(unexpected.to_f)
+# end
+
+# EOF
+# write_file("features/step_definitions/calculator_mappings.rb", mapping_code)
+# end
+
+ def assert_passing_scenario
+ assert_partial_output("1 scenario(s) (1 passed)", all_output)
+ assert_success true
+ end
+
+ def assert_failing_scenario
+ assert_partial_output("1 scenario(s) (1 failed)", all_output)
+ assert_success false
+ end
+
+# def assert_pending_scenario
+# assert_partial_output("1 scenario (1 pending)", all_output)
+# assert_success true
+# end
+
+# def assert_undefined_scenario
+# assert_partial_output("1 scenario (1 undefined)", all_output)
+# assert_success true
+# end
+
+# def failed_output
+# "failed"
+# end
+end
+
+World(CucumberJsMappings)
View
94 features/step_definitions/cucumber_steps.js
@@ -10,11 +10,6 @@ var stepDefinitions = function() {
var _recordedStepParameters;
var _stepCallCount;
-
- // =======================================================
- // ===== Implementation-independent step definitions =====
- // =======================================================
-
// Creates a Given, When or Then step definition that does nothing and pass all the time.
//
// Matching groups:
@@ -124,26 +119,8 @@ var stepDefinitions = function() {
// Matching groups: none.
// Multiline parameter: the feature to execute.
When(/^I run the following feature:$/, function(featureSource, callback) {
- _listener = Cucumber.Debug.SimpleAstListener();
- _listener.beforeEachScenarioDo(function() {
- _stepDefs = [];
- _recordedStepParameters = [];
- _stepCallCount = 0;
- });
- var cucumber = Cucumber(featureSource, _getSupportCode);
- cucumber.attachListener(_listener);
- try {
- cucumber.start(function() {
- _finishedCuking = true;
- _featureSource = featureSource;
- callback();
- });
- } catch(error) {
- printInsideFeatureError(error);
- setTimeout(function() {
- throw(new Error("Step failed: Could not run the 'inside' feature successfully."));
- }, 10);
- };
+ _buildListener(Cucumber.Debug.SimpleAstListener);
+ _runFeature(featureSource, callback);
});
// Checks that the feature previously run succeeded.
@@ -154,17 +131,13 @@ var stepDefinitions = function() {
Then(/^the feature should have run successfully$/, function(callback) {
if (!_finishedCuking)
throw(new Error("Expected Cucumber to run the feature successfully."));
-
var actualOutput = _normalizeString(_listener.getLogs());
var expectedOutput = _normalizeString(_featureSource);
- if (!actualOutput.match(expectedOutput))
- throw(new Error("Expected listener to output the feature source.\n\n<<<<<<< EXPECTED\n" +
- expectedOutput + "\n======= ACTUAL:\n" + actualOutput + "\n>>>>>>>"));
-
+ if (actualOutput.indexOf(expectedOutput) == -1)
+ throw(UnexpectedOutputError(expectedOutput, actualOutput));
callback();
});
-
// =======================================================
// ======= Cucumber.js-specific step definitions =========
// =======================================================
@@ -177,11 +150,50 @@ var stepDefinitions = function() {
callback();
});
+ When(/^I run the following feature with the "progress" formatter:$/, function(featureSource, callback) {
+ _buildListener(Cucumber.Listener.ProgressFormatter);
+ _runFeature(featureSource, callback);
+ });
+
+ Then(/^the listener should output the following:$/, function(expectedOutput, callback) {
+ var actualOutput = _listener.getLogs();
+ var expectedOutput = expectedOutput;
+ if (actualOutput.indexOf(expectedOutput) == -1){
+ throw(UnexpectedOutputError(expectedOutput, actualOutput));
+ }
+ callback();
+ });
// =======================================================
// ===================== Helpers ========================
// =======================================================
+ function _runFeature(featureSource, callback) {
+ var cucumber = Cucumber(featureSource, _getSupportCode);
+ cucumber.attachListener(_listener);
+ try {
+ cucumber.start(function() {
+ _finishedCuking = true;
+ _featureSource = featureSource;
+ callback();
+ });
+ } catch(error) {
+ printInsideFeatureError(error);
+ setTimeout(function() {
+ throw(new Error("Step failed: Could not run the 'inside' feature successfully."));
+ }, 10);
+ };
+ };
+
+ function _buildListener(listenerConstructor) {
+ _listener = listenerConstructor({logToConsole: false});
+ _listener.beforeEachScenarioDo(function() {
+ _stepDefs = [];
+ _recordedStepParameters = [];
+ _stepCallCount = 0;
+ });
+ };
+
function _addStepDefinition(keyword, name, content) {
var _stepName = RegExp(name);
var stepDefinition;
@@ -209,15 +221,27 @@ var stepDefinitions = function() {
var util = require('util');
console.log("\n=================================================");
console.log("=== Error caught while running inside feature ===");
- console.log("=================================================\n");
- console.log(error.toString() + "\n\nFeature logs:");
- console.log("-------\n" + _listener.getLogs() + "\n" + error.stack + "\n-------");
- console.log("=================================================\n");
+ console.log("=================================================");
+ console.log(error.toString());
+ console.log("\n============== INSIDE FEATURE LOGS ==============");
+ console.log(_listener.getLogs());
+ console.log("\n===================== TRACE =====================");
+ console.log(error.stack);
+ console.log("==================================================\n");
};
function translateParameterOffsetToIndex(offset) {
return parseInt(offset) - 1;
};
+
+ function UnexpectedOutputError(expected, actual) {
+ return(new Error("Expected listener output is not met.\n\n<<<<<<< EXPECTED:\n" +
+ showSpacesOnString(expected) + "\n======= GOT:\n" + showSpacesOnString(actual) + "\n>>>>>>>"));
+ };
+
+ function showSpacesOnString(string) {
+ return string.replace(/ /g, '·').replace(/\n/g, "\n");
+ };
};
module.exports = stepDefinitions;
View
191 lib/cucumber.js
@@ -342,16 +342,33 @@ Cucumber.Ast.TreeWalker.Event = function(name, payload) {
},
replicateAsPreEvent: function replicateAsPreEvent() {
- var newName = Cucumber.Ast.TreeWalker.BEFORE_EVENT_NAME_PREFIX + name;
+ var newName = buildBeforeEventName(name);
return Cucumber.Ast.TreeWalker.Event(newName, payload);
},
replicateAsPostEvent: function replicateAsPostEvent() {
- var newName = Cucumber.Ast.TreeWalker.AFTER_EVENT_NAME_PREFIX + name;
+ var newName = buildAfterEventName(name);
return Cucumber.Ast.TreeWalker.Event(newName, payload);
- }
+ },
+
+ occuredOn: function occuredOn(eventName) {
+ return eventName == name;
+ },
+ occuredAfter: function occuredAfter(eventName) {
+ var afterEventName = buildAfterEventName(eventName);
+ return afterEventName == name;
+ }
};
+
+ function buildBeforeEventName(eventName) {
+ return Cucumber.Ast.TreeWalker.BEFORE_EVENT_NAME_PREFIX + eventName;
+ }
+
+ function buildAfterEventName(eventName) {
+ return Cucumber.Ast.TreeWalker.AFTER_EVENT_NAME_PREFIX + eventName;
+ }
+
return self;
};
@@ -419,10 +436,11 @@ Cucumber.SupportCode.StepDefinition = function(regexp, code) {
},
invoke: function invoke(stepName, docString, callback) {
- var parameters = self.buildInvocationParameters(stepName, docString, function() {
+ var codeCallback = function() {
var stepResult = Cucumber.Runtime.StepResult(true);
callback(stepResult);
- });
+ };
+ var parameters = self.buildInvocationParameters(stepName, docString, codeCallback);
code.apply(undefined, parameters);
},
@@ -441,7 +459,6 @@ Cucumber.SupportCode.StepDefinition = function(regexp, code) {
};
Cucumber.Runtime = {};
-
Cucumber.Runtime.StepResult = function(status) {
var self = {
isSuccessful: function isSuccessful() {
@@ -451,6 +468,126 @@ Cucumber.Runtime.StepResult = function(status) {
return self;
};
+Cucumber.Listener = {};
+Cucumber.Listener.ProgressFormatter = function(options) {
+ var beforeEachScenarioUserFunctions = Cucumber.Types.Collection();
+ var logs = "";
+ var passedScenarios = 0;
+ var passedSteps = 0;
+
+ if (!options)
+ options = {};
+ if (options['logToConsole'] == undefined)
+ options['logToConsole'] = true;
+
+ var self = {
+ beforeEachScenarioDo: function beforeEachScenarioDo(userFunction) {
+ beforeEachScenarioUserFunctions.add(userFunction);
+ },
+
+ log: function log(string) {
+ logs += string;
+ if (options['logToConsole'])
+ process.stdout.write(string);
+ },
+
+ getLogs: function getLogs() {
+ return logs;
+ },
+
+ hear: function hear(event, callback) {
+ if (self.hasHandlerForEvent(event)) {
+ var handler = self.getHandlerForEvent(event);
+ handler(event, callback);
+ } else {
+ callback();
+ }
+ },
+
+ hasHandlerForEvent: function hasHandlerForEvent(event) {
+ var handlerName = self.buildHandlerNameForEvent(event);
+ return self[handlerName] != undefined;
+ },
+
+ buildHandlerNameForEvent: function buildHandlerNameForEvent(event) {
+ var handlerName =
+ Cucumber.Listener.ProgressFormatter.EVENT_HANDLER_NAME_PREFIX +
+ event.getName() +
+ Cucumber.Listener.ProgressFormatter.EVENT_HANDLER_NAME_SUFFIX;
+ return handlerName;
+ },
+
+ getHandlerForEvent: function getHandlerForEvent(event) {
+ var eventHandlerName = self.buildHandlerNameForEvent(event);
+ return self[eventHandlerName];
+ },
+
+ handleStepResultEvent: function handleStepResultEvent(event, callback) {
+ var stepResult = event.getPayloadItem('stepResult');
+ if (stepResult.isSuccess()) {
+ self.countOnePassedStep();
+ self.log(Cucumber.Listener.ProgressFormatter.PASSING_STEP_CHARACTER);
+ } else {
+ self.countOneFailedStep();
+ self.log(Cucumber.Listener.ProgressFormatter.FAILED_STEP_CHARACTER);
+ }
+ callback();
+ },
+
+ handleAfterFeaturesEvent: function handleAfterFeaturesEvent(event, callback) {
+ self.logSummary();
+ callback();
+ },
+
+ handleAfterScenarioEvent: function handleAfterScenarioEvent(event, callback) {
+ self.countOnePassedScenario();
+ callback();
+ },
+
+ countOnePassedScenario: function countOnePassedScenario() {
+ passedScenarios++;
+ },
+
+ countOnePassedStep: function countOnePassedStep() {
+ passedSteps++;
+ },
+
+ countOneFailedStep: TODO("countOneFailedStep()"),
+
+ logSummary: function logSummary() {
+ self.log("\n\n");
+ var scenarioCount = self.getScenarioCount();
+ var passedScenarioCount = self.getPassedScenarioCount();
+ var stepCount = self.getStepCount();
+ var passedStepCount = self.getPassedStepCount();
+ self.log(scenarioCount + " scenario(s) (" + passedScenarioCount + " passed)\n");
+ self.log(stepCount + " step(s) (" + passedStepCount + " passed)");
+ self.log("\n");
+ },
+
+ getScenarioCount: function getScenarioCount() {
+ return self.getPassedScenarioCount();
+ },
+
+ getPassedScenarioCount: function getPassedScenarioCount(){
+ return passedScenarios;
+ },
+
+ getStepCount: function getStepCount() {
+ return self.getPassedStepCount();
+ },
+
+ getPassedStepCount: function getPassedStepCount() {
+ return passedSteps;
+ }
+ };
+ return self;
+};
+Cucumber.Listener.ProgressFormatter.PASSING_STEP_CHARACTER = '.';
+Cucumber.Listener.ProgressFormatter.FAILED_STEP_CHARACTER = 'F';
+Cucumber.Listener.ProgressFormatter.EVENT_HANDLER_NAME_PREFIX = 'handle';
+Cucumber.Listener.ProgressFormatter.EVENT_HANDLER_NAME_SUFFIX = 'Event';
+
Cucumber.Types = {};
Cucumber.Types.Collection = function() {
var items = new Array();
@@ -584,50 +721,10 @@ Cucumber.Debug.SimpleAstListener = function(options) {
return indented;
};
};
-Cucumber.Debug.SgmlAstListener = function() {
- var self = {
- hearBeforeFeatures: function hearBeforeFeatures() {
- console.log("<features>");
- },
-
- hearAfterFeatures: function hearAfterFeatures() {
- console.log("</features>");
- },
-
- hearBeforeFeature: function hearBeforeFeature() {
- console.log(" <feature>");
- },
-
- hearAfterFeature: function hearAfterFeature() {
- console.log(" </feature>");
- },
-
- hearBeforeScenario: function hearBeforeScenario() {
- console.log(" <scenario>");
- },
-
- hearAfterScenario: function hearAfterScenario() {
- console.log(" </scenario>");
- },
-
- hearBeforeStep: function hearBeforeStep() {
- console.log(" <step>");
- },
-
- hearStepResult: function hearStepResult(stepResult) {
- console.log(" <result success='" + (stepResult.isSuccessful() ? "true" : "false") + "'></result>");
- },
-
- hearAfterStep: function hearAfterStep() {
- console.log(" </step>");
- }
- };
- return self;
-};
if(typeof exports != 'undefined') { module.exports = Cucumber; }
if(typeof window != 'undefined') { for (var p in Cucumber) { window[p] = Cucumber[p]; } }
var TODO = function(description) {
- return function() { throw("IMPLEMENT ME: "+description) };
+ return function() { throw("IMPLEMENT ME: " + description); };
};
View
135 spec/cucumber/ast/tree_walker/event_spec.js
@@ -2,76 +2,107 @@ require('../../../support/spec_helper');
describe("Cucumber.Ast.TreeWalker.Event", function() {
var Cucumber = require('cucumber');
- var event, name, payload;
-
- beforeEach(function() {
- name = "SomeEvent";
- payloadItems = [
- createSpy("First payload item"),
- createSpy("Second payload item"),
- createSpy("Third payload item")
- ];
- payload = {
- firstItem: payloadItems[0],
- secondItem: payloadItems[1],
- thirdItem: payloadItems[2]
- };
- event = Cucumber.Ast.TreeWalker.Event(name, payload);
- });
- describe("getName()", function() {
- it("returns the name of the event", function() {
- expect(event.getName()).toBe(name);
- });
+ describe("non-instance method", function() {
});
- describe("replicateAsPreEvent()", function() {
- var preEvent;
+ describe("instance method", function() {
+ var event, name, payload;
beforeEach(function() {
- preEvent = createSpy("Pre-event (before)");
- spyOn(Cucumber.Ast.TreeWalker, 'Event').andReturn(preEvent);
+ name = "SomeEvent";
+ payloadItems = [
+ createSpy("First payload item"),
+ createSpy("Second payload item"),
+ createSpy("Third payload item")
+ ];
+ payload = {
+ firstItem: payloadItems[0],
+ secondItem: payloadItems[1],
+ thirdItem: payloadItems[2]
+ };
+ event = Cucumber.Ast.TreeWalker.Event(name, payload);
});
- it("creates a new event with the before prefix prepended to the event name and the same payload", function() {
- var newName = Cucumber.Ast.TreeWalker.BEFORE_EVENT_NAME_PREFIX + name;
- event.replicateAsPreEvent();
- expect(Cucumber.Ast.TreeWalker.Event).toHaveBeenCalledWith(newName, payload);
+ describe("getName()", function() {
+ it("returns the name of the event", function() {
+ expect(event.getName()).toBe(name);
+ });
});
- it("returns the new event", function() {
- expect(event.replicateAsPreEvent()).toBe(preEvent);
- });
- });
+ describe("replicateAsPreEvent()", function() {
+ var preEvent;
- describe("replicateAsPostEvent()", function() {
- var postEvent;
+ beforeEach(function() {
+ preEvent = createSpy("Pre-event (before)");
+ spyOn(Cucumber.Ast.TreeWalker, 'Event').andReturn(preEvent);
+ });
- beforeEach(function() {
- postEvent = createSpy("Post-event (after)");
- spyOn(Cucumber.Ast.TreeWalker, 'Event').andReturn(postEvent);
+ it("creates a new event with the before prefix prepended to the event name and the same payload", function() {
+ var newName = Cucumber.Ast.TreeWalker.BEFORE_EVENT_NAME_PREFIX + name;
+ event.replicateAsPreEvent();
+ expect(Cucumber.Ast.TreeWalker.Event).toHaveBeenCalledWith(newName, payload);
+ });
+
+ it("returns the new event", function() {
+ expect(event.replicateAsPreEvent()).toBe(preEvent);
+ });
});
- it("creates a new event with the after prefix prepended to the event name and the same payload", function() {
- var newName = Cucumber.Ast.TreeWalker.AFTER_EVENT_NAME_PREFIX + name;
- event.replicateAsPostEvent();
- expect(Cucumber.Ast.TreeWalker.Event).toHaveBeenCalledWith(newName, payload);
+ describe("replicateAsPostEvent()", function() {
+ var postEvent;
+
+ beforeEach(function() {
+ postEvent = createSpy("Post-event (after)");
+ spyOn(Cucumber.Ast.TreeWalker, 'Event').andReturn(postEvent);
+ });
+
+ it("creates a new event with the after prefix prepended to the event name and the same payload", function() {
+ var newName = Cucumber.Ast.TreeWalker.AFTER_EVENT_NAME_PREFIX + name;
+ event.replicateAsPostEvent();
+ expect(Cucumber.Ast.TreeWalker.Event).toHaveBeenCalledWith(newName, payload);
+ });
+
+ it("returns the new event", function() {
+ expect(event.replicateAsPostEvent()).toBe(postEvent);
+ });
});
- it("returns the new event", function() {
- expect(event.replicateAsPostEvent()).toBe(postEvent);
+ describe("getPayloadItem()", function() {
+ it("returns the requested item from the payload", function() {
+ expect(event.getPayloadItem('firstItem')).toBe(payloadItems[0]);
+ expect(event.getPayloadItem('secondItem')).toBe(payloadItems[1]);
+ expect(event.getPayloadItem('thirdItem')).toBe(payloadItems[2]);
+ });
+
+ it("returns undefined when the item does not exist in the payload", function() {
+ expect(event.getPayloadItem('unknownItem')).toBeUndefined();
+ });
});
- });
- describe("getPayloadItem()", function() {
- it("returns the requested item from the payload", function() {
- expect(event.getPayloadItem('firstItem')).toBe(payloadItems[0]);
- expect(event.getPayloadItem('secondItem')).toBe(payloadItems[1]);
- expect(event.getPayloadItem('thirdItem')).toBe(payloadItems[2]);
+ describe("occuredOn()", function() {
+ it("returns true when the received event name matches the actual event name", function() {
+ expect(event.occuredOn(name)).toBeTruthy();
+ });
+
+ it("returns false when the received event name does not match the actual event name", function() {
+ expect(event.occuredOn("SomeOtherEvent")).toBeFalsy();
+ });
});
- it("returns undefined when the item does not exist in the payload", function() {
- expect(event.getPayloadItem('unknownItem')).toBeUndefined();
+ describe("occuredAfter()", function() {
+ beforeEach(function() {
+ var afterName = Cucumber.Ast.TreeWalker.AFTER_EVENT_NAME_PREFIX + name;
+ event = Cucumber.Ast.TreeWalker.Event(afterName, payload);
+ });
+
+ it("returns true when the received event name prefixed by the 'after' keyword matches the actual event name", function() {
+ expect(event.occuredAfter(name)).toBeTruthy();
+ });
+
+ it("returns false when the received event name prefixed by the 'after' keyword does not match the actual event name", function() {
+ expect(event.occuredAfter('SomeOtherEvent')).toBeFalsy();
+ });
});
});
-});
+});
View
499 spec/cucumber/listener/progress_formatter_spec.js
@@ -0,0 +1,499 @@
+require('../../support/spec_helper');
+
+describe("Cucumber.Listener.ProgressFormatter", function() {
+ var Cucumber = require('cucumber');
+ var listener, beforeEachScenarioUserFunctions;
+
+ beforeEach(function() {
+ beforeEachScenarioUserFunctions = createSpy("User Functions to call before each scenario");
+ spyOn(Cucumber.Types, 'Collection').andReturn(beforeEachScenarioUserFunctions);
+ listener = Cucumber.Listener.ProgressFormatter();
+ });
+
+ describe("constructor", function() {
+ it("creates a new collection to store user functions to call before each scenario", function() {
+ expect(Cucumber.Types.Collection).toHaveBeenCalled();
+ });
+ });
+
+ describe("beforeEachScenarioDo()", function() {
+ beforeEach(function() {
+ spyOnStub(beforeEachScenarioUserFunctions, 'add');
+ });
+
+ it("adds the user function to the collection of 'before each scenario' user functions", function() {
+ var userFunction = createSpy("A user function to call before each scenario");
+ listener.beforeEachScenarioDo(userFunction);
+ expect(beforeEachScenarioUserFunctions.add).toHaveBeenCalledWith(userFunction);
+ });
+ });
+
+ describe("log()", function() {
+ var logged, alsoLogged, loggedBuffer;
+
+ beforeEach(function() {
+ logged = "this was logged";
+ alsoLogged = "this was also logged";
+ loggedBuffer = logged + alsoLogged;
+ spyOn(process.stdout, 'write');
+ });
+
+ it("records logged strings", function() {
+ listener.log(logged);
+ listener.log(alsoLogged);
+ expect(listener.getLogs()).toBe(loggedBuffer);
+ });
+
+ it("outputs the logged string to STDOUT by default", function() {
+ listener.log(logged);
+ expect(process.stdout.write).toHaveBeenCalledWith(logged);
+ });
+
+ describe("when asked to output to STDOUT", function() {
+ beforeEach(function() {
+ listener = Cucumber.Listener.ProgressFormatter({logToConsole: true});
+ });
+
+ it("outputs the logged string to STDOUT", function() {
+ listener.log(logged);
+ expect(process.stdout.write).toHaveBeenCalledWith(logged);
+ });
+ });
+
+ describe("when asked to not output to STDOUT", function() {
+ beforeEach(function() {
+ listener = Cucumber.Listener.ProgressFormatter({logToConsole: false});
+ });
+
+ it("does not output anything to STDOUT", function() {
+ listener.log(logged);
+ expect(process.stdout.write).not.toHaveBeenCalledWith(logged);
+ });
+ });
+ });
+
+ describe("getLogs()", function() {
+ it("returns the logged buffer", function() {
+ var logged = "this was logged";
+ var alsoLogged = "this was also logged";
+ var loggedBuffer = logged + alsoLogged;
+ spyOn(process.stdout, 'write'); // prevent actual output during spec execution
+ listener.log(logged);
+ listener.log(alsoLogged);
+ expect(listener.getLogs()).toBe(loggedBuffer);
+ });
+
+ it("returns an empty string when the listener did not log anything yet", function() {
+ expect(listener.getLogs()).toBe("");
+ });
+ });
+
+ describe("hear()", function() {
+ var event, callback;
+ var eventHandler;
+
+ beforeEach(function() {
+ event = createSpy("Event");
+ callback = createSpy("Callback");
+ spyOn(listener, 'hasHandlerForEvent');
+ spyOn(listener, 'getHandlerForEvent');
+ });
+
+ it("checks if there is a handler for the event", function() {
+ listener.hear(event, callback);
+ expect(listener.hasHandlerForEvent).toHaveBeenCalledWith(event);
+ });
+
+ describe("when there is a handler for that event", function() {
+ beforeEach(function() {
+ eventHandler = createSpy("Event handler (function)");
+ listener.hasHandlerForEvent.andReturn(true);
+ listener.getHandlerForEvent.andReturn(eventHandler);
+ });
+
+ it("gets the handler for that event", function() {
+ listener.hear(event, callback);
+ expect(listener.getHandlerForEvent).toHaveBeenCalledWith(event);
+ });
+
+ it("calls the handler with the event and the callback", function() {
+ listener.hear(event, callback);
+ expect(eventHandler).toHaveBeenCalledWith(event, callback);
+ });
+
+ it("does not callback", function() {
+ listener.hear(event, callback);
+ expect(callback).not.toHaveBeenCalled();
+ });
+ });
+
+ describe("when there are no handlers for that event", function() {
+ beforeEach(function() {
+ listener.hasHandlerForEvent.andReturn(false);
+ });
+
+ it("calls back", function() {
+ listener.hear(event, callback);
+ expect(callback).toHaveBeenCalled();
+ });
+
+ it("does not get the handler for the event", function() {
+ listener.hear(event, callback);
+ expect(listener.getHandlerForEvent).not.toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe("hasHandlerForEvent", function() {
+ var event, eventHandlerName, eventHandler;
+
+ beforeEach(function() {
+ event = createSpy("Event");
+ eventHandlerName = createSpy("event handler name");
+ spyOn(listener, 'buildHandlerNameForEvent').andReturn(eventHandlerName);
+ });
+
+ it("builds the name of the handler for that event", function() {
+ listener.hasHandlerForEvent(event);
+ expect(listener.buildHandlerNameForEvent).toHaveBeenCalledWith(event);
+ });
+
+ describe("when the handler exists", function() {
+ beforeEach(function() {
+ eventHandler = createSpy("event handler");
+ listener[eventHandlerName] = eventHandler;
+ });
+
+ it("returns true", function() {
+ expect(listener.hasHandlerForEvent(event)).toBeTruthy();
+ });
+ });
+
+ describe("when the handler does not exist", function() {
+ it("returns false", function() {
+ expect(listener.hasHandlerForEvent(event)).toBeFalsy();
+ });
+ });
+ });
+
+ describe("buildHandlerNameForEvent", function() {
+ var event, eventName;
+
+ beforeEach(function() {
+ eventName = "SomeEventName";
+ event = createSpyWithStubs("Event", {getName: eventName});
+ });
+
+ it("gets the name of the event", function() {
+ listener.buildHandlerNameForEvent(event);
+ expect(event.getName).toHaveBeenCalled();
+ });
+
+ it("returns the name of the event with prefix 'handle' and suffix 'Event'", function() {
+ expect(listener.buildHandlerNameForEvent(event)).toBe("handle" + eventName + "Event");
+ });
+ });
+
+ describe("getHandlerForEvent()", function() {
+ var event;
+ var eventHandlerName, eventHandler;
+
+ beforeEach(function() {
+ event = createSpy("event");
+ eventHandlerName = 'handleSomeEvent';
+ eventHandler = createSpy("event handler");
+ spyOn(listener, 'buildHandlerNameForEvent').andReturn(eventHandlerName);
+ });
+
+ it("gets the name of the handler for the event", function() {
+ listener.getHandlerForEvent(event);
+ expect(listener.buildHandlerNameForEvent).toHaveBeenCalledWith(event);
+ });
+
+ describe("when an event handler exists for the event", function() {
+ beforeEach(function() {
+ listener[eventHandlerName] = eventHandler;
+ });
+
+ it("returns the event handler", function() {
+ expect(listener.getHandlerForEvent(event)).toBe(eventHandler);
+ });
+ });
+
+ describe("when no event handlers exist for the event", function() {
+ it("returns nothing", function() {
+ expect(listener.getHandlerForEvent(event)).toBeUndefined();
+ });
+ });
+ });
+
+ describe("handleStepResultEvent", function() {
+ var event, callback, stepResult;
+
+ beforeEach(function() {
+ spyOn(listener, 'log');
+ stepResult = createSpyWithStubs("step result", {isSuccess: true});
+ event = createSpyWithStubs("event", {getPayloadItem: stepResult});
+ callback = createSpy("Callback");
+ spyOn(listener, 'countOnePassedStep');
+ });
+
+ it("gets the step result from the event payload", function() {
+ listener.handleStepResultEvent(event, callback);
+ expect(event.getPayloadItem).toHaveBeenCalledWith('stepResult');
+ });
+
+ it("asks the step result if the step passed", function() {
+ listener.handleStepResultEvent(event, callback);
+ expect(stepResult.isSuccess).toHaveBeenCalled();
+ });
+
+ describe("when the step passed", function() {
+ it("counts one more passed step", function() {
+ listener.handleStepResultEvent(event, callback);
+ expect(listener.countOnePassedStep).toHaveBeenCalled();
+ });
+
+ it("logs the passing step character", function() {
+ listener.handleStepResultEvent(event, callback);
+ expect(listener.log).toHaveBeenCalledWith(Cucumber.Listener.ProgressFormatter.PASSING_STEP_CHARACTER);
+ });
+ });
+
+ describe("when the step did not pass", function() {
+ beforeEach(function() {
+ stepResult.isSuccess.andReturn(false);
+ spyOnStub(stepResult, 'isSkipped');
+ spyOn(listener, 'countOneFailedStep');
+ });
+
+ it("does not count one more passed step", function() {
+ listener.handleStepResultEvent(event, callback);
+ expect(listener.countOnePassedStep).not.toHaveBeenCalled();
+ });
+
+ it("does not log the passing step character", function() {
+ listener.handleStepResultEvent(event, callback);
+ expect(listener.log).not.toHaveBeenCalledWith(Cucumber.Listener.ProgressFormatter.PASSING_STEP_CHARACTER);
+ });
+
+ it("counts one more failed step", function() {
+ listener.handleStepResultEvent(event, callback);
+ expect(listener.countOneFailedStep).toHaveBeenCalled();
+ });
+
+ it("logs the failed step character", function() {
+ listener.handleStepResultEvent(event, callback);
+ expect(listener.log).toHaveBeenCalledWith(Cucumber.Listener.ProgressFormatter.FAILED_STEP_CHARACTER);
+ });
+ });
+
+ it("calls back", function() {
+ listener.handleStepResultEvent(event, callback);
+ expect(callback).toHaveBeenCalled();
+ });
+ });
+
+ describe("handleAfterFeaturesEvent()", function() {
+ var features, callback;
+
+ beforeEach(function() {
+ event = createSpy("Event");
+ callback = createSpy("Callback");
+ spyOn(listener, "logSummary");
+ });
+
+ it("displays a summary", function() {
+ listener.handleAfterFeaturesEvent(event, callback);
+ expect(listener.logSummary).toHaveBeenCalled();
+ });
+
+ it("calls back", function() {
+ listener.handleAfterFeaturesEvent(event, callback);
+ expect(callback).toHaveBeenCalled();
+ });
+ });
+
+ describe("handleAfterScenarioEvent()", function() {
+ var event, callback;
+
+ beforeEach(function() {
+ event = createSpy("event");
+ callback = createSpy("callback");
+ spyOn(listener, 'countOnePassedScenario');
+ });
+
+ it("counts one more passed scenario", function() {
+ listener.handleAfterScenarioEvent(event, callback);
+ expect(listener.countOnePassedScenario).toHaveBeenCalled();
+ });
+
+ it("calls back", function() {
+ listener.handleAfterScenarioEvent(event, callback);
+ expect(callback).toHaveBeenCalled();
+ });
+ });
+
+ describe("logSummary", function() {
+ var scenarioCount, passedScenarioCount;
+ var stepCount;
+
+ beforeEach(function() {
+ scenarioCount = 12;
+ passedScenarioCount = 9;
+ stepCount = 27;
+ passedStepCount = 24;
+ spyOn(listener, 'log');
+ spyOn(listener, 'getScenarioCount').andReturn(scenarioCount);
+ spyOn(listener, 'getPassedScenarioCount').andReturn(passedScenarioCount);
+ spyOn(listener, 'getStepCount').andReturn(stepCount);
+ spyOn(listener, 'getPassedStepCount').andReturn(passedStepCount);
+ });
+
+ it("logs two line feeds", function() {
+ listener.logSummary();
+ expect(listener.log).toHaveBeenCalledWith("\n\n");
+ });
+
+ it("gets the number of scenarios", function() {
+ listener.logSummary();
+ expect(listener.getScenarioCount).toHaveBeenCalled();
+ });
+
+ it("gets the number of passed scenarios", function() {
+ listener.logSummary();
+ expect(listener.getPassedScenarioCount).toHaveBeenCalled();
+ });
+
+ it ("logs the number of scenarios and the number of passed scenarios", function() {
+ var string = scenarioCount + " scenario(s) (" + passedScenarioCount + " passed)\n";
+ listener.logSummary();
+ expect(listener.log).toHaveBeenCalledWith(string);
+ });
+
+ it("gets the number of steps", function() {
+ listener.logSummary();
+ expect(listener.getStepCount).toHaveBeenCalled();
+ });
+
+ it("gets the number of passed steps", function() {
+ listener.logSummary();
+ expect(listener.getPassedStepCount).toHaveBeenCalled();
+ });
+
+ it ("logs the number of steps and the number of passed steps", function() {
+ var string = stepCount + " step(s) (" + passedStepCount + " passed)";
+ listener.logSummary();
+ expect(listener.log).toHaveBeenCalledWith(string);
+ });
+
+ it("logs one line feed", function() {
+ listener.logSummary();
+ expect(listener.log).toHaveBeenCalledWith("\n");
+ });
+ });
+
+ describe("getScenarioCount()", function() {
+ var passedScenarioCount;
+
+ beforeEach(function() {
+ passedScenarioCount = 23;
+ spyOn(listener, 'getPassedScenarioCount').andReturn(passedScenarioCount);
+ });
+
+ it("gets the number of passed scenarios", function() {
+ listener.getScenarioCount();
+ expect(listener.getPassedScenarioCount).toHaveBeenCalled();
+ });
+
+ it("returns the number of passed scenarios", function() {
+ expect(listener.getScenarioCount()).toBe(passedScenarioCount);
+ });
+ });
+
+ describe("getStepCount()", function() {
+ var passedStepCount;
+
+ beforeEach(function() {
+ passedStepCount = 23;
+ spyOn(listener, 'getPassedStepCount').andReturn(passedStepCount);
+ });
+
+ it("gets the number of passed steps", function() {
+ listener.getStepCount();
+ expect(listener.getPassedStepCount).toHaveBeenCalled();
+ });
+
+ it("returns the number of passed steps", function() {
+ expect(listener.getStepCount()).toBe(passedStepCount);
+ });
+ });
+
+ describe("passed scenario counting", function() {
+ describe("countOnePassedScenario()", function() {
+ it("counts one more passed scenario", function() {
+ var beforeCountOne = listener.getPassedScenarioCount();
+ listener.countOnePassedScenario();
+ expect(listener.getPassedScenarioCount()).toBe(beforeCountOne + 1);
+ });
+ });
+
+ describe("getPassedScenarioCount()", function() {
+ it("returns 0 when no scenario passed", function() {
+ expect(listener.getPassedScenarioCount()).toBe(0);
+ });
+
+ it("returns 1 when one scenario passed", function() {
+ listener.countOnePassedScenario();
+ expect(listener.getPassedScenarioCount()).toBe(1);
+ });
+
+ it("returns 2 when two scenarios passed", function() {
+ listener.countOnePassedScenario();
+ listener.countOnePassedScenario();
+ expect(listener.getPassedScenarioCount()).toBe(2);
+ });
+
+ it("returns 3 when three scenarios passed", function() {
+ listener.countOnePassedScenario();
+ listener.countOnePassedScenario();
+ listener.countOnePassedScenario();
+ expect(listener.getPassedScenarioCount()).toBe(3);
+ });
+ });
+ });
+
+ describe("passed step counting", function() {
+ describe("countOnePassedStep()", function() {
+ it("counts one more passed step", function() {
+ var beforeCountOne = listener.getPassedStepCount();
+ listener.countOnePassedStep();
+ expect(listener.getPassedStepCount()).toBe(beforeCountOne + 1);
+ });
+ });
+
+ describe("getPassedStepCount()", function() {
+ it("returns 0 when no step passed", function() {
+ expect(listener.getPassedStepCount()).toBe(0);
+ });
+
+ it("returns 1 when one step passed", function() {
+ listener.countOnePassedStep();
+ expect(listener.getPassedStepCount()).toBe(1);
+ });
+
+ it("returns 2 when two steps passed", function() {
+ listener.countOnePassedStep();
+ listener.countOnePassedStep();
+ expect(listener.getPassedStepCount()).toBe(2);
+ });
+
+ it("returns 3 when three steps passed", function() {
+ listener.countOnePassedStep();
+ listener.countOnePassedStep();
+ listener.countOnePassedStep();
+ expect(listener.getPassedStepCount()).toBe(3);
+ });
+ });
+ });
+});

0 comments on commit f3b40fd

Please sign in to comment.
Something went wrong with that request. Please try again.