cucumber 0.3.93 → 0.3.94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +67 -38
- data/Manifest.txt +8 -0
- data/Rakefile +1 -1
- data/cucumber.yml +2 -2
- data/examples/python/features/fibonacci.feature +19 -0
- data/examples/python/features/step_definitions/fib_steps.rb +7 -0
- data/examples/python/features/support/env.rb +21 -0
- data/examples/python/lib/fib.py +7 -0
- data/examples/self_test/features/tags_sample.feature +17 -0
- data/examples/sinatra/README.textile +13 -0
- data/features/cucumber_cli.feature +85 -3
- data/features/custom_formatter.feature +2 -2
- data/features/html_formatter/a.html +1 -1
- data/features/junit_formatter.feature +19 -12
- data/features/step_definitions/cucumber_steps.rb +8 -15
- data/features/support/env.rb +4 -5
- data/gem_tasks/contributors.rake +4 -0
- data/lib/cucumber/ast/feature.rb +13 -0
- data/lib/cucumber/ast/feature_element.rb +8 -4
- data/lib/cucumber/ast/features.rb +6 -1
- data/lib/cucumber/ast/table.rb +13 -6
- data/lib/cucumber/ast/tags.rb +9 -1
- data/lib/cucumber/cli/configuration.rb +3 -15
- data/lib/cucumber/cli/main.rb +25 -8
- data/lib/cucumber/cli/options.rb +53 -31
- data/lib/cucumber/filter.rb +4 -3
- data/lib/cucumber/formatter/ansicolor.rb +42 -9
- data/lib/cucumber/formatter/console.rb +38 -6
- data/lib/cucumber/formatter/html.rb +2 -8
- data/lib/cucumber/formatter/junit.rb +65 -24
- data/lib/cucumber/formatter/ordered_xml_markup.rb +24 -0
- data/lib/cucumber/formatter/pretty.rb +9 -4
- data/lib/cucumber/formatter/progress.rb +7 -1
- data/lib/cucumber/rake/task.rb +3 -3
- data/lib/cucumber/step_mother.rb +3 -1
- data/lib/cucumber/version.rb +1 -1
- data/rails_generators/cucumber/templates/cucumber.rake +18 -6
- data/spec/cucumber/ast/scenario_outline_spec.rb +2 -2
- data/spec/cucumber/ast/table_spec.rb +5 -0
- data/spec/cucumber/cli/configuration_spec.rb +2 -1
- data/spec/cucumber/cli/options_spec.rb +11 -6
- metadata +10 -2
@@ -4,8 +4,8 @@ Feature: Custom Formatter
|
|
4
4
|
When I run cucumber --format Cucumber::Formatter::TagCloud features
|
5
5
|
Then it should fail with
|
6
6
|
"""
|
7
|
-
| after_file | background_tagged_before_on_outline | four | lots | one | three | two |
|
8
|
-
| 1 | 1 | 1 | 1 | 1 | 2 | 1 |
|
7
|
+
| after_file | background_tagged_before_on_outline | four | lots | one | sample_four | sample_one | sample_three | sample_two | three | two |
|
8
|
+
| 1 | 1 | 1 | 1 | 1 | 2 | 1 | 2 | 1 | 2 | 1 |
|
9
9
|
|
10
10
|
"""
|
11
11
|
|
@@ -178,6 +178,6 @@ features/search_sample.feature:10:in `Given failing without a table'</pre></li><
|
|
178
178
|
features/search_sample.feature:13:in `Given <state> without a table'</pre></td></tr></table></div></div><div class="scenario outline"><h3><span class="keyword">Scenario Outline:</span> <span class="val">Hantu Pisang match</span></h3><ol><li class="step skipped" id="features_search_sample_feature_19"><div><span class="keyword">Given</span> <span class="step val"><state> without a table</span></div></li></ol><div class="examples"><h4><span class="keyword">Examples:</span> <span class="val"></span></h4><table><tr id="row_21"><th class="val skipped_param" id="row_21_0">state</th></tr><tr id="row_22"><td class="val passed" id="row_22_0">passing</td></tr></table></div></div><div class="scenario outline"><h3><span class="keyword">Scenario Outline:</span> <span class="val">no match in name but in examples</span></h3><ol><li class="step skipped" id="features_search_sample_feature_25"><div><span class="keyword">Given</span> <span class="step val"><state> without a table</span></div></li></ol><div class="examples"><h4><span class="keyword">Examples:</span> <span class="val">Hantu Pisang</span></h4><table><tr id="row_27"><th class="val skipped_param" id="row_27_0">state</th></tr><tr id="row_28"><td class="val passed" id="row_28_0">passing</td></tr></table></div><div class="examples"><h4><span class="keyword">Examples:</span> <span class="val">Ignore me</span></h4><table><tr id="row_31"><th class="val skipped_param" id="row_31_0">state</th></tr><tr id="row_32"><td class="val failed" id="row_32_0">failing</td></tr><tr><td class="failed" colspan="1"><pre>FAIL (RuntimeError)
|
179
179
|
./features/step_definitions/sample_steps.rb:2:in `flunker'
|
180
180
|
./features/step_definitions/sample_steps.rb:16:in `/^failing without a table$/'
|
181
|
-
features/search_sample.feature:25:in `Given <state> without a table'</pre></td></tr></table></div></div></div><div class="feature"><span class="tag">@lots</span><h2><span class="val">Feature: Tons of cukes</span></h2><p class="narrative"></p><div class="scenario"><h3><span class="keyword">Scenario:</span> <span class="val">Lots and lots</span></h3><ol><li class="step passed" id="features_tons_of_cukes_feature_4"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step failed" id="features_tons_of_cukes_feature_5"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div><pre class="failed">We already have 2 cukes! (RuntimeError)
|
181
|
+
features/search_sample.feature:25:in `Given <state> without a table'</pre></td></tr></table></div></div></div><div class="feature"><span class="tag">@sample_one</span><h2><span class="val">Feature: Tag samples</span></h2><p class="narrative"></p><div class="scenario"><span class="tag">@sample_two</span> <span class="tag">@sample_four</span><h3><span class="keyword">Scenario:</span> <span class="val">Passing</span></h3><ol><li class="step undefined" id="features_tags_sample_feature_6"><div><span class="keyword">Given</span> <span class="step val">missing</span></div></li></ol></div><div class="scenario outline"><span class="tag">@sample_three</span><h3><span class="keyword">Scenario Outline:</span> <span class="val"></span></h3><ol><li class="step skipped" id="features_tags_sample_feature_10"><div><span class="keyword">Given</span> <span class="step val"><state></span></div></li></ol><div class="examples"><h4><span class="keyword">Examples:</span> <span class="val"></span></h4><table><tr id="row_12"><th class="val skipped_param" id="row_12_0">state</th></tr><tr id="row_13"><td class="val undefined" id="row_13_0">missing</td></tr></table></div></div><div class="scenario"><span class="tag">@sample_three</span> <span class="tag">@sample_four</span><h3><span class="keyword">Scenario:</span> <span class="val">Skipped</span></h3><ol><li class="step undefined" id="features_tags_sample_feature_17"><div><span class="keyword">Given</span> <span class="step val">missing</span></div></li></ol></div></div><div class="feature"><span class="tag">@lots</span><h2><span class="val">Feature: Tons of cukes</span></h2><p class="narrative"></p><div class="scenario"><h3><span class="keyword">Scenario:</span> <span class="val">Lots and lots</span></h3><ol><li class="step passed" id="features_tons_of_cukes_feature_4"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step failed" id="features_tons_of_cukes_feature_5"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div><pre class="failed">We already have 2 cukes! (RuntimeError)
|
182
182
|
./features/step_definitions/sample_steps.rb:28:in `/^'(.+)' cukes$/'
|
183
183
|
features/tons_of_cukes.feature:5:in `Given '2' cukes'</pre></li><li class="step skipped" id="features_tons_of_cukes_feature_6"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_7"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_8"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_9"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_10"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_11"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_12"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_13"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_14"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_15"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_16"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_17"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_18"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_19"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_20"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_21"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_22"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_23"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_24"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_25"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_26"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_27"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_28"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_29"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_30"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_31"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_32"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_33"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_34"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_35"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_36"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_37"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_38"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_39"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_40"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_41"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_42"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_43"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_44"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_45"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_46"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_47"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_48"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_49"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_50"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_51"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li><li class="step skipped" id="features_tons_of_cukes_feature_52"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">2</span>' cukes</span></div></li></ol></div></div><div class="feature"><h2><span class="val">Feature: undefined multiline args</span></h2><p class="narrative"></p><div class="scenario"><h3><span class="keyword">Scenario:</span> <span class="val">pystring</span></h3><ol><li class="step undefined" id="features_undefined_multiline_args_feature_4"><div><span class="keyword">Given</span> <span class="step val">a pystring</span></div><pre class="val"> example</pre></li></ol></div><div class="scenario"><h3><span class="keyword">Scenario:</span> <span class="val">table</span></h3><ol><li class="step undefined" id="features_undefined_multiline_args_feature_10"><div><span class="keyword">Given</span> <span class="step val">a table</span></div><table><tr id="row_11"><td class="val" id="row_11_0">table</td></tr><tr id="row_12"><td class="val" id="row_12_0">example</td></tr></table></li></ol></div></div><div class="duration">0m30.005s</div></div></body></html>
|
@@ -6,42 +6,49 @@ Feature: JUnit output formatter
|
|
6
6
|
Given I am in junit
|
7
7
|
And the tmp directory is empty
|
8
8
|
|
9
|
-
@mri186
|
9
|
+
@mri186
|
10
10
|
Scenario: one feature, one passing scenario, one failing scenario
|
11
11
|
When I run cucumber --format junit --out tmp/ features/one_passing_one_failing.feature
|
12
12
|
Then it should fail with
|
13
13
|
"""
|
14
14
|
|
15
15
|
"""
|
16
|
-
And "examples/junit/tmp/TEST-one_passing_one_failing.xml" should contain
|
16
|
+
And "examples/junit/tmp/TEST-one_passing_one_failing.xml" with junit duration "0.005" should contain
|
17
17
|
"""
|
18
18
|
<?xml version="1.0" encoding="UTF-8"?>
|
19
|
-
<testsuite errors="0"
|
20
|
-
<testcase
|
19
|
+
<testsuite errors="0" failures="1" name="One passing scenario, one failing scenario" tests="2" time="0.005">
|
20
|
+
<testcase classname="One passing scenario, one failing scenario.Passing" name="Passing" time="0.005">
|
21
21
|
</testcase>
|
22
|
-
<testcase
|
23
|
-
<failure message="
|
22
|
+
<testcase classname="One passing scenario, one failing scenario.Failing" name="Failing" time="0.005">
|
23
|
+
<failure message="failed Failing" type="failed">
|
24
|
+
Scenario: Failing
|
25
|
+
|
26
|
+
Given a failing scenario
|
27
|
+
|
28
|
+
Message:
|
24
29
|
(RuntimeError)
|
25
30
|
./features/step_definitions/steps.rb:6:in `/a failing scenario/'
|
26
31
|
features/one_passing_one_failing.feature:7:in `Given a failing scenario' </failure>
|
27
32
|
</testcase>
|
28
33
|
</testsuite>
|
29
|
-
|
34
|
+
|
30
35
|
"""
|
31
36
|
|
32
|
-
@mri186
|
37
|
+
@mri186
|
33
38
|
Scenario: pending step
|
34
39
|
When I run cucumber --format junit --out tmp/ features/pending.feature
|
35
40
|
Then it should pass with
|
36
41
|
"""
|
37
42
|
|
38
43
|
"""
|
39
|
-
And "examples/junit/tmp/TEST-pending.xml" should contain
|
44
|
+
And "examples/junit/tmp/TEST-pending.xml" with junit duration "0.009" should contain
|
40
45
|
"""
|
41
46
|
<?xml version="1.0" encoding="UTF-8"?>
|
42
|
-
<testsuite errors="0"
|
43
|
-
<testcase
|
44
|
-
<failure message="
|
47
|
+
<testsuite errors="0" failures="1" name="Pending step" tests="1" time="0.009">
|
48
|
+
<testcase classname="Pending step.Pending" name="Pending" time="0.009">
|
49
|
+
<failure message="pending Pending" type="pending">
|
50
|
+
Scenario: Pending
|
51
|
+
|
45
52
|
TODO (Cucumber::Pending)
|
46
53
|
./features/step_definitions/steps.rb:10:in `/a pending step/'
|
47
54
|
features/pending.feature:4:in `Given a pending step' </failure>
|
@@ -39,7 +39,6 @@ Given /^I am not running (?:.*) in the background$/ do
|
|
39
39
|
# no-op
|
40
40
|
end
|
41
41
|
|
42
|
-
|
43
42
|
When /^I run cucumber (.*)$/ do |cucumber_opts|
|
44
43
|
run "#{Cucumber::RUBY_BINARY} #{Cucumber::BINARY} --no-color #{cucumber_opts}"
|
45
44
|
end
|
@@ -75,23 +74,17 @@ Then /^the output should be$/ do |text|
|
|
75
74
|
last_stdout.should == text
|
76
75
|
end
|
77
76
|
|
78
|
-
Then /^"(
|
79
|
-
|
80
|
-
t.write(xml)
|
81
|
-
t.flush
|
82
|
-
t.close
|
83
|
-
cmd = "diffxml #{t.path} #{file}"
|
84
|
-
diff = `#{cmd}`
|
85
|
-
if diff =~ /<delta>/m
|
86
|
-
raise diff + "\nXML WAS:\n" + IO.read(file)
|
87
|
-
end
|
77
|
+
Then /^"([^\"]*)" should contain$/ do |file, text|
|
78
|
+
strip_duration(IO.read(file)).should == text
|
88
79
|
end
|
89
80
|
|
90
|
-
Then /^"(
|
91
|
-
|
81
|
+
Then /^"([^\"]*)" with junit duration "([^\"]*)" should contain$/ do |actual_file, duration_replacement, text|
|
82
|
+
actual = IO.read(actual_file)
|
83
|
+
actual = replace_junit_duration(actual, duration_replacement)
|
84
|
+
actual.should == text
|
92
85
|
end
|
93
86
|
|
94
|
-
Then /^"(
|
87
|
+
Then /^"([^\"]*)" should match$/ do |file, text|
|
95
88
|
IO.read(file).should =~ Regexp.new(text)
|
96
89
|
end
|
97
90
|
|
@@ -119,7 +112,7 @@ Then /^STDERR should be empty$/ do
|
|
119
112
|
last_stderr.should == ""
|
120
113
|
end
|
121
114
|
|
122
|
-
Then /^"(
|
115
|
+
Then /^"([^\"]*)" should exist$/ do |file|
|
123
116
|
File.exists?(file).should be_true
|
124
117
|
FileUtils.rm(file)
|
125
118
|
end
|
data/features/support/env.rb
CHANGED
@@ -50,6 +50,10 @@ class CucumberWorld
|
|
50
50
|
s.gsub(/\d+m\d+\.\d+s/m, replacement)
|
51
51
|
end
|
52
52
|
|
53
|
+
def replace_junit_duration(s, replacement)
|
54
|
+
s.gsub(/\d+\.\d\d+/m, replacement)
|
55
|
+
end
|
56
|
+
|
53
57
|
def create_file(file_name, file_content)
|
54
58
|
file_content.gsub!("CUCUMBER_LIB", "'#{cucumber_lib_dir}'") # Some files, such as Rakefiles need to use the lib dir
|
55
59
|
in_current_dir do
|
@@ -118,8 +122,3 @@ end
|
|
118
122
|
After do
|
119
123
|
terminate_background_jobs
|
120
124
|
end
|
121
|
-
|
122
|
-
Before('@diffxml') do
|
123
|
-
`diffxml --version`
|
124
|
-
raise "Please install diffxml from http://diffxml.sourceforge.net/" if $? != 0
|
125
|
-
end
|
data/lib/cucumber/ast/feature.rb
CHANGED
@@ -44,6 +44,19 @@ module Cucumber
|
|
44
44
|
"#{@file}:#{line}"
|
45
45
|
end
|
46
46
|
|
47
|
+
def tag_count(tag)
|
48
|
+
if @tags.respond_to?(:count)
|
49
|
+
@tags.count(tag) # 1.9
|
50
|
+
else
|
51
|
+
@tags.select{|t| t == tag}.length # 1.8
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def feature_and_children_tag_count(tag)
|
56
|
+
children_tag_count = @feature_elements.inject(0){|count, feature_element| count += feature_element.tag_count(tag)}
|
57
|
+
children_tag_count + tag_count(tag)
|
58
|
+
end
|
59
|
+
|
47
60
|
def short_name
|
48
61
|
first_line = name.split(/\n/)[0]
|
49
62
|
if first_line =~ /#{language.keywords('feature', true)}:(.*)/
|
@@ -24,7 +24,7 @@ module Cucumber
|
|
24
24
|
if @name.empty?
|
25
25
|
[@keyword.jlength]
|
26
26
|
else
|
27
|
-
@name.split("\n").enum_for(:each_with_index).map do |line, line_number|
|
27
|
+
@name.split("\n").enum_for(:each_with_index).map do |line, line_number|
|
28
28
|
line_number == 0 ? @keyword.jlength + line.jlength : line.jlength + Ast::Step::INDENT - 1 # We -1 as names which are not keyword lines are missing a space between keyword and name
|
29
29
|
end
|
30
30
|
end
|
@@ -32,7 +32,7 @@ module Cucumber
|
|
32
32
|
|
33
33
|
def matches_scenario_names?(scenario_name_regexps)
|
34
34
|
scenario_name_regexps.detect{|name| name =~ @name}
|
35
|
-
end
|
35
|
+
end
|
36
36
|
|
37
37
|
def backtrace_line(name = "#{@keyword} #{@name}", line = @line)
|
38
38
|
@feature.backtrace_line(name, line) if @feature
|
@@ -49,9 +49,13 @@ module Cucumber
|
|
49
49
|
def accept_hook?(hook)
|
50
50
|
@tags.accept_hook?(hook) || @feature.accept_hook?(hook)
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
|
+
def tag_count(tag)
|
54
|
+
@feature.tag_count(tag) == 0 ? @tags.count(tag) : @feature.tag_count(tag)
|
55
|
+
end
|
56
|
+
|
53
57
|
def language
|
54
58
|
@feature.language
|
55
59
|
end
|
56
|
-
|
60
|
+
end
|
57
61
|
end
|
data/lib/cucumber/ast/table.rb
CHANGED
@@ -126,23 +126,30 @@ module Cucumber
|
|
126
126
|
[:table, *cells_rows.map{|row| row.to_sexp}]
|
127
127
|
end
|
128
128
|
|
129
|
-
# Redefines the table headers. This makes it
|
130
|
-
#
|
129
|
+
# Redefines the table headers. This makes it possible to use
|
130
|
+
# prettier and more flexible header names in the features. The
|
131
|
+
# keys of +mappings+ are Strings or regular expressions
|
132
|
+
# (anything that responds to #=== will work) that may match
|
133
|
+
# column headings in the table. The values of +mappings+ are
|
134
|
+
# desired names for the columns.
|
135
|
+
#
|
136
|
+
# Example:
|
131
137
|
#
|
132
138
|
# | Phone Number | Address |
|
133
139
|
# | 123456 | xyz |
|
134
140
|
# | 345678 | abc |
|
135
141
|
#
|
136
|
-
# A StepDefinition receiving this table can then map the columns
|
142
|
+
# A StepDefinition receiving this table can then map the columns
|
143
|
+
# with both Regexp and String:
|
137
144
|
#
|
138
|
-
#
|
139
|
-
#
|
145
|
+
# table.map_headers!(/phone( number)?/i => :phone, 'Address' => :address)
|
146
|
+
# table.hashes
|
140
147
|
# # => [{:phone => '123456', :address => 'xyz'}, {:phone => '345678', :address => 'abc'}]
|
141
148
|
#
|
142
149
|
def map_headers!(mappings)
|
143
150
|
header_cells = cell_matrix[0]
|
144
151
|
mappings.each_pair do |pre, post|
|
145
|
-
header_cell = header_cells.detect{|cell| cell.value
|
152
|
+
header_cell = header_cells.detect{|cell| pre === cell.value}
|
146
153
|
header_cell.value = post
|
147
154
|
if @conversion_procs.has_key?(pre)
|
148
155
|
@conversion_procs[post] = @conversion_procs.delete(pre)
|
data/lib/cucumber/ast/tags.rb
CHANGED
@@ -10,7 +10,7 @@ module Cucumber
|
|
10
10
|
def self.strip_prefix(tag_name)
|
11
11
|
tag_name =~ /^@(.*)/ ? $1 : tag_name
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def initialize(line, tag_names)
|
15
15
|
@line, @tag_names = line, tag_names
|
16
16
|
end
|
@@ -26,6 +26,14 @@ module Cucumber
|
|
26
26
|
hook.matches_tag_names?(@tag_names)
|
27
27
|
end
|
28
28
|
|
29
|
+
def count(tag)
|
30
|
+
if @tag_names.respond_to?(:count)
|
31
|
+
@tag_names.count(tag) # 1.9
|
32
|
+
else
|
33
|
+
@tag_names.select{|t| t == tag}.length # 1.8
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
29
37
|
def to_sexp
|
30
38
|
@tag_names.map{|tag_name| [:tag, tag_name]}
|
31
39
|
end
|
@@ -7,18 +7,6 @@ module Cucumber
|
|
7
7
|
class ProfileNotFound < StandardError; end
|
8
8
|
|
9
9
|
class Configuration
|
10
|
-
BUILTIN_FORMATS = {
|
11
|
-
'html' => 'Cucumber::Formatter::Html',
|
12
|
-
'pretty' => 'Cucumber::Formatter::Pretty',
|
13
|
-
'profile' => 'Cucumber::Formatter::Profile',
|
14
|
-
'progress' => 'Cucumber::Formatter::Progress',
|
15
|
-
'rerun' => 'Cucumber::Formatter::Rerun',
|
16
|
-
'usage' => 'Cucumber::Formatter::Usage',
|
17
|
-
'junit' => 'Cucumber::Formatter::Junit',
|
18
|
-
'tag_cloud' => 'Cucumber::Formatter::TagCloud',
|
19
|
-
'steps' => 'Cucumber::Formatter::Steps'
|
20
|
-
}
|
21
|
-
|
22
10
|
attr_reader :options
|
23
11
|
|
24
12
|
def initialize(out_stream = STDOUT, error_stream = STDERR)
|
@@ -96,8 +84,8 @@ module Cucumber
|
|
96
84
|
end
|
97
85
|
|
98
86
|
def formatter_class(format)
|
99
|
-
if(builtin = BUILTIN_FORMATS[format])
|
100
|
-
constantize(builtin)
|
87
|
+
if(builtin = Options::BUILTIN_FORMATS[format])
|
88
|
+
constantize(builtin[0])
|
101
89
|
else
|
102
90
|
constantize(format)
|
103
91
|
end
|
@@ -180,7 +168,7 @@ module Cucumber
|
|
180
168
|
tr("-", "_").
|
181
169
|
downcase
|
182
170
|
end
|
183
|
-
|
171
|
+
|
184
172
|
end
|
185
173
|
|
186
174
|
end
|
data/lib/cucumber/cli/main.rb
CHANGED
@@ -11,6 +11,8 @@ require 'cucumber/cli/drb_client'
|
|
11
11
|
module Cucumber
|
12
12
|
module Cli
|
13
13
|
class Main
|
14
|
+
FAILURE = 1
|
15
|
+
|
14
16
|
class << self
|
15
17
|
def step_mother
|
16
18
|
@step_mother
|
@@ -32,7 +34,7 @@ module Cucumber
|
|
32
34
|
@out_stream = out_stream == STDOUT ? Formatter::ColorIO.new : out_stream
|
33
35
|
@error_stream = error_stream
|
34
36
|
end
|
35
|
-
|
37
|
+
|
36
38
|
def execute!(step_mother)
|
37
39
|
trap_interrupt
|
38
40
|
if configuration.drb?
|
@@ -56,17 +58,32 @@ module Cucumber
|
|
56
58
|
step_mother.visitor = visitor # Needed to support World#announce
|
57
59
|
visitor.visit_features(features)
|
58
60
|
|
59
|
-
failure = if
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
61
|
+
failure = if exceeded_tag_limts?(features)
|
62
|
+
FAILURE
|
63
|
+
elsif configuration.wip?
|
64
|
+
step_mother.scenarios(:passed).any?
|
65
|
+
else
|
66
|
+
step_mother.scenarios(:failed).any? ||
|
67
|
+
(configuration.strict? && step_mother.steps(:undefined).any?)
|
68
|
+
end
|
65
69
|
rescue ProfilesNotDefinedError, YmlLoadError, ProfileNotFound => e
|
66
70
|
@error_stream.puts e.message
|
67
71
|
true
|
68
72
|
end
|
69
73
|
|
74
|
+
def exceeded_tag_limts?(features)
|
75
|
+
exceeded = false
|
76
|
+
configuration.options[:include_tags].each do |tag, limit|
|
77
|
+
unless limit.nil?
|
78
|
+
tag_count = features.tag_count(tag)
|
79
|
+
if tag_count > limit.to_i
|
80
|
+
exceeded = true
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
exceeded
|
85
|
+
end
|
86
|
+
|
70
87
|
def load_plain_text_features
|
71
88
|
features = Ast::Features.new
|
72
89
|
|
@@ -85,7 +102,7 @@ module Cucumber
|
|
85
102
|
|
86
103
|
def configuration
|
87
104
|
return @configuration if @configuration
|
88
|
-
|
105
|
+
|
89
106
|
@configuration = Configuration.new(@out_stream, @error_stream)
|
90
107
|
@configuration.parse!(@args)
|
91
108
|
@configuration
|
data/lib/cucumber/cli/options.rb
CHANGED
@@ -4,16 +4,28 @@ module Cucumber
|
|
4
4
|
|
5
5
|
class Options
|
6
6
|
BUILTIN_FORMATS = {
|
7
|
-
'html' => 'Cucumber::Formatter::Html',
|
8
|
-
'pretty' => 'Cucumber::Formatter::Pretty',
|
9
|
-
'profile' => 'Cucumber::Formatter::Profile',
|
10
|
-
'progress' => 'Cucumber::Formatter::Progress',
|
11
|
-
'rerun' => 'Cucumber::Formatter::Rerun',
|
12
|
-
'usage' => 'Cucumber::Formatter::Usage',
|
13
|
-
'junit' => 'Cucumber::Formatter::Junit',
|
14
|
-
'tag_cloud' => 'Cucumber::Formatter::TagCloud',
|
15
|
-
'steps' => 'Cucumber::Formatter::Steps'
|
7
|
+
'html' => ['Cucumber::Formatter::Html', 'Generates a nice looking HTML report.'],
|
8
|
+
'pretty' => ['Cucumber::Formatter::Pretty', 'Prints the feature as is - in colours.'],
|
9
|
+
'profile' => ['Cucumber::Formatter::Profile', 'Prints the 10 slowest steps at the end.'],
|
10
|
+
'progress' => ['Cucumber::Formatter::Progress', 'Prints one character per scenario.'],
|
11
|
+
'rerun' => ['Cucumber::Formatter::Rerun', 'Prints failing files with line numbers.'],
|
12
|
+
'usage' => ['Cucumber::Formatter::Usage', 'Prints where step definitions are used.'],
|
13
|
+
'junit' => ['Cucumber::Formatter::Junit', 'Generates a report similar to Ant+JUnit.'],
|
14
|
+
'tag_cloud' => ['Cucumber::Formatter::TagCloud', 'Prints a tag cloud of tag usage.'],
|
15
|
+
'steps' => ['Cucumber::Formatter::Steps', 'Prints location of step definitions.']
|
16
16
|
}
|
17
|
+
max = BUILTIN_FORMATS.keys.map{|s| s.length}.max
|
18
|
+
FORMAT_HELP = (BUILTIN_FORMATS.keys.sort.map do |key|
|
19
|
+
" #{key}#{' ' * (max - key.length)} : #{BUILTIN_FORMATS[key][1]}"
|
20
|
+
end) + ["FORMAT can also be the fully qualified class name of",
|
21
|
+
"your own custom formatter. If the class isn't loaded,",
|
22
|
+
"Cucumber will attempt to require a file with a relative",
|
23
|
+
"file name that is the underscore name of the class name.",
|
24
|
+
"Example: --format Foo::BarZap -> Cucumber will look for",
|
25
|
+
"foo/bar_zap.rb. You can place the file with this relative",
|
26
|
+
"path underneath your features/support directory or anywhere",
|
27
|
+
"on Ruby's LOAD_PATH, for example in a Ruby gem."
|
28
|
+
]
|
17
29
|
DRB_FLAG = '--drb'
|
18
30
|
PROFILE_SHORT_FLAG = '-p'
|
19
31
|
PROFILE_LONG_FLAG = '--profile'
|
@@ -31,6 +43,7 @@ module Cucumber
|
|
31
43
|
@default_profile = options[:default_profile]
|
32
44
|
@skip_profile_information = options[:skip_profile_information]
|
33
45
|
@profiles = []
|
46
|
+
@overridden_paths = []
|
34
47
|
@options = default_options
|
35
48
|
end
|
36
49
|
|
@@ -54,7 +67,7 @@ module Cucumber
|
|
54
67
|
previous_flag_was_profile = true
|
55
68
|
next true
|
56
69
|
end
|
57
|
-
arg == DRB_FLAG
|
70
|
+
arg == DRB_FLAG || @overridden_paths.include?(arg)
|
58
71
|
end
|
59
72
|
)
|
60
73
|
end
|
@@ -96,16 +109,8 @@ module Cucumber
|
|
96
109
|
end
|
97
110
|
end
|
98
111
|
opts.on("-f FORMAT", "--format FORMAT",
|
99
|
-
"How to format features (Default: pretty)",
|
100
|
-
|
101
|
-
"FORMAT can also be the fully qualified class name of",
|
102
|
-
"your own custom formatter. If the class isn't loaded,",
|
103
|
-
"Cucumber will attempt to require a file with a relative",
|
104
|
-
"file name that is the underscore name of the class name.",
|
105
|
-
"Example: --format Foo::BarZap -> Cucumber will look for",
|
106
|
-
"foo/bar_zap.rb. You can place the file with this relative",
|
107
|
-
"path underneath your features/support directory or anywhere",
|
108
|
-
"on Ruby's LOAD_PATH, for example in a Ruby gem.") do |v|
|
112
|
+
"How to format features (Default: pretty). Available formats:",
|
113
|
+
*FORMAT_HELP) do |v|
|
109
114
|
@options[:formats] << [v, @out_stream]
|
110
115
|
end
|
111
116
|
opts.on("-o", "--out [FILE|DIR]",
|
@@ -118,12 +123,16 @@ module Cucumber
|
|
118
123
|
end
|
119
124
|
opts.on("-t TAGS", "--tags TAGS",
|
120
125
|
"Only execute the features or scenarios with the specified tags.",
|
121
|
-
"TAGS must be comma-separated without spaces.
|
122
|
-
"
|
123
|
-
"with
|
126
|
+
"TAGS must be comma-separated without spaces. They can be",
|
127
|
+
"specified with or without the @ prefix. Example: --tags dev\n",
|
128
|
+
"Negative tags: Prefix tags with ~ to exclude features or scenarios",
|
129
|
+
"having that tag.\n",
|
130
|
+
"Limit WIP: Positive tags can be given a threshold to limit the",
|
131
|
+
"number of occurrences. Example: --tags qa:3 will fail if there",
|
132
|
+
"are more than 3 occurrences of the @qa tag.") do |v|
|
124
133
|
include_tags, exclude_tags = *parse_tags(v)
|
125
|
-
@options[:include_tags]
|
126
|
-
@options[:exclude_tags]
|
134
|
+
@options[:include_tags].merge!(include_tags)
|
135
|
+
@options[:exclude_tags].merge!(exclude_tags)
|
127
136
|
end
|
128
137
|
opts.on("-n NAME", "--name NAME",
|
129
138
|
"Only execute the feature elements which match part of the given name.",
|
@@ -261,7 +270,16 @@ module Cucumber
|
|
261
270
|
# Strip @
|
262
271
|
includes = includes.map{|tag| Ast::Tags.strip_prefix(tag)}
|
263
272
|
excludes = excludes.map{|tag| Ast::Tags.strip_prefix(tag)}
|
264
|
-
[includes, excludes]
|
273
|
+
[parse_tag_limits(includes), parse_tag_limits(excludes)]
|
274
|
+
end
|
275
|
+
|
276
|
+
def parse_tag_limits(includes)
|
277
|
+
dict = {}
|
278
|
+
includes.each do |tag|
|
279
|
+
tag, limit = tag.split(':')
|
280
|
+
dict[tag] = limit.nil? ? limit : limit.to_i
|
281
|
+
end
|
282
|
+
dict
|
265
283
|
end
|
266
284
|
|
267
285
|
def disable_profile_loading?
|
@@ -298,10 +316,14 @@ module Cucumber
|
|
298
316
|
def reverse_merge(other_options)
|
299
317
|
@options = other_options.options.merge(@options)
|
300
318
|
@options[:require] += other_options[:require]
|
301
|
-
@options[:include_tags]
|
302
|
-
@options[:exclude_tags]
|
319
|
+
@options[:include_tags].merge! other_options[:include_tags]
|
320
|
+
@options[:exclude_tags].merge! other_options[:exclude_tags]
|
303
321
|
@options[:env_vars] = other_options[:env_vars].merge(@options[:env_vars])
|
304
|
-
|
322
|
+
if @options[:paths].empty?
|
323
|
+
@options[:paths] = other_options[:paths]
|
324
|
+
else
|
325
|
+
@overridden_paths += (other_options[:paths] - @options[:paths])
|
326
|
+
end
|
305
327
|
@options[:source] &= other_options[:source]
|
306
328
|
@options[:snippets] &= other_options[:snippets]
|
307
329
|
|
@@ -348,8 +370,8 @@ module Cucumber
|
|
348
370
|
:dry_run => false,
|
349
371
|
:formats => [],
|
350
372
|
:excludes => [],
|
351
|
-
:include_tags =>
|
352
|
-
:exclude_tags =>
|
373
|
+
:include_tags => {},
|
374
|
+
:exclude_tags => {},
|
353
375
|
:name_regexps => [],
|
354
376
|
:env_vars => {},
|
355
377
|
:diff_enabled => true
|