aslakhellesoy-cucumber 0.3.11.6 → 0.3.11.200907091518

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/History.txt +49 -1
  2. data/Manifest.txt +3 -0
  3. data/examples/i18n/no/features/step_definitons/kalkulator_steps.rb +1 -1
  4. data/examples/self_test/features/support/env.rb +2 -1
  5. data/examples/steps_library/features/step_definitions/steps_lib1.rb +8 -0
  6. data/examples/steps_library/features/step_definitions/steps_lib2.rb +8 -0
  7. data/examples/tickets/features/step_definitons/tickets_steps.rb +15 -0
  8. data/features/html_formatter/a.html +4 -4
  9. data/features/rake_task.feature +28 -0
  10. data/features/steps_formatter.feature +25 -0
  11. data/features/support/env.rb +5 -0
  12. data/lib/cucumber/ast/outline_table.rb +2 -1
  13. data/lib/cucumber/ast/py_string.rb +0 -1
  14. data/lib/cucumber/ast/step.rb +4 -1
  15. data/lib/cucumber/ast/table.rb +286 -48
  16. data/lib/cucumber/ast/visitor.rb +2 -1
  17. data/lib/cucumber/cli/configuration.rb +8 -2
  18. data/lib/cucumber/cli/language_help_formatter.rb +9 -7
  19. data/lib/cucumber/feature_file.rb +7 -1
  20. data/lib/cucumber/filter.rb +2 -2
  21. data/lib/cucumber/formatter/html.rb +1 -1
  22. data/lib/cucumber/formatter/pretty.rb +20 -5
  23. data/lib/cucumber/formatter/progress.rb +1 -1
  24. data/lib/cucumber/formatter/steps.rb +49 -0
  25. data/lib/cucumber/parser/feature.rb +27 -0
  26. data/lib/cucumber/parser/i18n/language.rb +8 -5
  27. data/lib/cucumber/rake/task.rb +6 -0
  28. data/lib/cucumber/step_match.rb +1 -1
  29. data/lib/cucumber/version.rb +1 -1
  30. data/rails_generators/cucumber/templates/env.rb +1 -0
  31. data/rails_generators/feature/templates/feature.erb +1 -1
  32. data/rails_generators/feature/templates/steps.erb +2 -8
  33. data/spec/cucumber/ast/table_spec.rb +144 -0
  34. data/spec/cucumber/cli/configuration_spec.rb +13 -0
  35. data/spec/cucumber/cli/main_spec.rb +0 -1
  36. data/spec/cucumber/formatter/progress_spec.rb +2 -2
  37. metadata +8 -4
@@ -1,12 +1,18 @@
1
1
  == 0.3.12 (In Git)
2
2
 
3
- The Egality release
3
+ The Hot summer release
4
+
5
+ This is a hot summer in Norway, and Cucumbers are growing in abundance. To celebrate this we give you
6
+ a new release with some hot new features:
7
+
8
+ === Egality
4
9
 
5
10
  English is not the world's most spoken language, so why should Cucumber force non-English speakers to use the
6
11
  --language flag? As of this release you're no longer forced to do that. Instead, you can add a comment header
7
12
  to your .feature files:
8
13
 
9
14
  # language: fr
15
+ # Cucumber understands that this is French
10
16
  Fonctionnalité: Trou de boulette
11
17
 
12
18
  If you don't have that header, Cucumber will work as before - using whatever you specified with --language,
@@ -14,6 +20,40 @@ or default to English if no --language option was specified. A nice side effect
14
20
  have features in several languages side by side and run them in the same cucumber. (Not recommended unless
15
21
  you want to take polyglot programming to an extreme level).
16
22
 
23
+ === Table diffing
24
+
25
+ When you pass a table as an argument to your Then steps you often want to compare that table
26
+ to some actual values. In previous releases you had to iterate over the table's values and manually
27
+ compare each row using cell.should equal('foo') or assert_equal('foo', cell). If a discrepancy was found
28
+ you'd get an error and then you were done.
29
+
30
+ With this release you have a much more powerful way to compare expected tables with actual data. An
31
+ Ast::Table object now has a new #diff!(table) method that you can invoke in your step definitions
32
+ that take table arguments. If the table you pass in is different from the expected table (from your
33
+ plain text step), Cucumber will print the difference for each of the row or column and fail your step.
34
+
35
+ The Table#diff! method expects an Array of Array, Array of Hash (similar to what you'd get from table#hashes)
36
+ or simply another Ast::Table object.
37
+
38
+ As an extra bonus we provide you with a new Webrat method that you can use to transform the data from
39
+ an HTML table into an Array of Array, so that you can easily compare the contents of your HTML table to
40
+ expected data passed to a step:
41
+
42
+ Then /^I should see the following cukes:$/ do |expected_cukes_table|
43
+ expected_cukes_table.diff!(table_at('#cuke_table').to_a)
44
+ end
45
+
46
+ You can do the same trick to compare data from a Rails ActiveRecord table (although this is not a
47
+ recommended practice - your Then steps should compare against what users *see*, not what's in the
48
+ database):
49
+
50
+ # This requires that you use the column names in the header of the plain text expected table
51
+ Then /^I should have the following cukes in the database:$/ do |expected_cukes_table|
52
+ expected_cukes_table.diff!(Cuke.find(:all).map(&attributes), :coldiff => false) # Don't care about extra columns
53
+ end
54
+
55
+ === Environment variables
56
+
17
57
  Another useful new feature is the ability to define environment variables on Cucumber's command line (just
18
58
  like you can with Rake). Example:
19
59
 
@@ -25,6 +65,8 @@ for example enabling your super hack that validates all HTTP responses for XHTML
25
65
  This release also has several bugfixes related to --format and Before/Adter hooks.
26
66
 
27
67
  === Bugfixes
68
+ * Make it possible to write non-localized step definitions (#377 Aslak Hellesøy)
69
+ * Table cells containing unicode are rendered incorrectly (#386 Stefan Kanev)
28
70
  * Before and After hooks run after everything is finished whn there are 2+ --format options (#371 Aslak Hellesøy)
29
71
  * When using --out and two --format the first is not delivered inline with execution of features (#361 Aslak Hellesøy)
30
72
  * Profile Formatter broken (#370 Aslak Hellesøy)
@@ -33,15 +75,21 @@ This release also has several bugfixes related to --format and Before/Adter hook
33
75
  * rake gems no longer lists cucumber as a [F]ramework gem (David Chelimsky)
34
76
  * CLI issues correct exit code when using --drb. Requires Spork version >= 0.5.1. (#355 Ben Mabey)
35
77
  * Make sure script/generate cucumber --spork uses the cucumber Rails environment (Philippe Lafoucrière)
78
+ * Fixed bug with rake task raising errors with feature files with spaces (#380 Joseph Wilk)
36
79
 
37
80
  === New Features
81
+ * Access to scenario outline name from After hook scenario parameter (#342 Aslak Hellesøy)
82
+ * Allow multiple --tags switches to be passed
83
+ * Load step definitions from vendored gems and plugins (#388 Mike Burns)
38
84
  * New --format steps formatter. Variant of the usage formatter that lists available step definitions (Demetrius Nunes)
39
85
  * Possibility to specify scenario language as part of the .feature file (#345 Aslak Hellesøy)
40
86
  * Support specifying environment variables using foo=bar syntax on command line or in profiles (#362 Bryan Helmkamp)
41
87
  * Display failing scenarios at the end of pretty format to make it easier for people to play them back (#360 Ryan Bigg)
42
88
 
43
89
  === Changed Features
90
+ * With --tags, positive tags are &&'ed while negative tags are ||'ed (John Wilger)
44
91
  * The data returned from Table#hashes and similar methods are frozen. Dup if you need to modify. (Aslak Hellesøy)
92
+ * Visitor.visit_table_cell_value(value, col_width, status) is now visitor.visit_table_cell_value(value, status)
45
93
 
46
94
  == 0.3.11 2009-06-05
47
95
 
@@ -239,6 +239,7 @@ examples/tickets/features/step_definitons/246_steps.rb
239
239
  examples/tickets/features/step_definitons/248_steps.rb
240
240
  examples/tickets/features/step_definitons/scenario_outline_steps.rb
241
241
  examples/tickets/features/step_definitons/tickets_steps.rb
242
+ examples/tickets/features/table_diffing.feature
242
243
  examples/tickets/features/tickets.feature
243
244
  examples/watir/README.textile
244
245
  examples/watir/Rakefile
@@ -268,6 +269,7 @@ features/step_definitions/cucumber_steps.rb
268
269
  features/step_definitions/extra_steps.rb
269
270
  features/steps_formatter.feature
270
271
  features/support/env.rb
272
+ features/table_diffing.feature
271
273
  features/usage.feature
272
274
  features/work_in_progress.feature
273
275
  gem_tasks/deployment.rake
@@ -349,6 +351,7 @@ lib/cucumber/step_definition.rb
349
351
  lib/cucumber/step_match.rb
350
352
  lib/cucumber/step_mother.rb
351
353
  lib/cucumber/version.rb
354
+ lib/cucumber/webrat/table_locator.rb
352
355
  lib/cucumber/world.rb
353
356
  rails_generators/cucumber/USAGE
354
357
  rails_generators/cucumber/cucumber_generator.rb
@@ -4,7 +4,7 @@ Before do
4
4
  @calc = Kalkulator.new
5
5
  end
6
6
 
7
- Gitt /at jeg har tastet inn (\d+)/ do |n|
7
+ Given /at jeg har tastet inn (\d+)/ do |n|
8
8
  @calc.push n.to_i
9
9
  end
10
10
 
@@ -1,4 +1,5 @@
1
1
  require 'spec/expectations'
2
+ $KCODE = 'u' unless Cucumber::RUBY_1_9
2
3
 
3
4
  Before('@not_used') do
4
5
  raise "Should never run"
@@ -14,4 +15,4 @@ end
14
15
 
15
16
  After('@background_tagged_before_on_outline') do
16
17
  @cukes.should == '888'
17
- end
18
+ end
@@ -0,0 +1,8 @@
1
+ Given /^I defined a first step$/ do
2
+ end
3
+
4
+ When /^I define a second step$/ do
5
+ end
6
+
7
+ Then /^I should also have a third step$/ do
8
+ end
@@ -0,0 +1,8 @@
1
+ Given /^I defined a step 4$/ do
2
+ end
3
+
4
+ When /^I create a step 5$/ do
5
+ end
6
+
7
+ Then /^I should be too tired for step 6$/ do
8
+ end
@@ -64,3 +64,18 @@ end
64
64
  Then /^I should get a no method error for 'backtrace_line'$/ do
65
65
  pending
66
66
  end
67
+
68
+ Then /the table should be different with table:/ do |expected|
69
+ expected.diff!(table(%{
70
+ | b | c | a | d |
71
+ | KASHA | AIIT | BOOYA | X |
72
+ | four | five | three | Y |
73
+ }), :coldiff => true)
74
+ end
75
+
76
+ Then /the table should be different with array:/ do |expected|
77
+ expected.diff!([
78
+ {'a' => 'BOOYA', 'b' => 'KASHA'},
79
+ {'a' => 'three', 'b' => 'four'},
80
+ ])
81
+ end
@@ -142,7 +142,7 @@ features/background/failing_background_after_success.feature:5:in `And '10' glob
142
142
  I sleep all night and I test all day</pre></li></ol></div></div><div class="feature"><h2><span class="val">Feature: Passing background sample</span></h2><p class="narrative"></p><div class="background"><h3><span class="keyword">Background:</span> <span class="val"></span></h3><ol><li class="step passed" id="features_background_passing_background_feature_4"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">10</span>' cukes</span></div></li></ol></div><div class="scenario"><h3><span class="keyword">Scenario:</span> <span class="val">passing background</span></h3><ol><li class="step passed" id="features_background_passing_background_feature_7"><div><span class="keyword">Then</span> <span class="step val">I should have '<span class="param">10</span>' cukes</span></div></li></ol></div><div class="scenario"><h3><span class="keyword">Scenario:</span> <span class="val">another passing background</span></h3><ol><li class="step passed" id="features_background_passing_background_feature_10"><div><span class="keyword">Then</span> <span class="step val">I should have '<span class="param">10</span>' cukes</span></div></li></ol></div></div><div class="feature"><h2><span class="val">Feature: Pending background sample</span></h2><p class="narrative"></p><div class="background"><h3><span class="keyword">Background:</span> <span class="val"></span></h3><ol><li class="step undefined" id="features_background_pending_background_feature_4"><div><span class="keyword">Given</span> <span class="step val">pending</span></div></li></ol></div><div class="scenario"><h3><span class="keyword">Scenario:</span> <span class="val">pending background</span></h3><ol><li class="step skipped" id="features_background_pending_background_feature_7"><div><span class="keyword">Then</span> <span class="step val">I should have '<span class="param">10</span>' cukes</span></div></li></ol></div><div class="scenario"><h3><span class="keyword">Scenario:</span> <span class="val">another pending background</span></h3><ol><li class="step skipped" id="features_background_pending_background_feature_10"><div><span class="keyword">Then</span> <span class="step val">I should have '<span class="param">10</span>' cukes</span></div></li></ol></div></div><div class="feature"><h2><span class="val">Feature: Failing background with scenario outlines sample</span></h2><p class="narrative"></p><div class="background"><h3><span class="keyword">Background:</span> <span class="val"></span></h3><ol><li class="step failed" id="features_background_scenario_outline_failing_background_feature_4"><div><span class="keyword">Given</span> <span class="step val">failing without a table</span></div><pre class="failed">FAIL (RuntimeError)
143
143
  ./features/step_definitions/sample_steps.rb:2:in `flunker'
144
144
  ./features/step_definitions/sample_steps.rb:16:in `/^failing without a table$/'
145
- features/background/scenario_outline_failing_background.feature:4:in `Given failing without a table'</pre></li></ol></div><div class="scenario outline"><h3><span class="keyword">Scenario Outline:</span> <span class="val">failing background</span></h3><ol><li class="step skipped" id="features_background_scenario_outline_failing_background_feature_7"><div><span class="keyword">Then</span> <span class="step val">I should have '&lt;count&gt;' cukes</span></div></li></ol><div class="examples"><h4><span class="keyword">Examples:</span> <span class="val"></span></h4><table><tr id="row_9"><th class="val skipped_param" id="row_9_0">count</th></tr><tr id="row_10"><td class="val skipped" id="row_10_0">10</td></tr><tr><td class="failed" colspan="1"><pre>FAIL (RuntimeError)
145
+ features/background/scenario_outline_failing_background.feature:4:in `Given failing without a table'</pre></li></ol></div><div class="scenario outline"><h3><span class="keyword">Scenario Outline:</span> <span class="val">failing background</span></h3><ol><li class="step skipped" id="features_background_scenario_outline_failing_background_feature_7"><div><span class="keyword">Then</span> <span class="step val">I should have '&lt;count&gt;' cukes</span></div></li></ol><div class="examples"><h4><span class="keyword">Examples:</span> <span class="val"></span></h4><table><tr id="row_9"><th class="val skipped_param" id="row_9_0">count</th></tr><tr id="row_10"><td class="val skipped" id="row_10_0">10</td></tr><tr><td colspan="1" class="failed"><pre>FAIL (RuntimeError)
146
146
  ./features/step_definitions/sample_steps.rb:2:in `flunker'
147
147
  ./features/step_definitions/sample_steps.rb:16:in `/^failing without a table$/'
148
148
  features/background/scenario_outline_failing_background.feature:4:in `Given failing without a table'</pre></td></tr></table></div></div><div class="scenario outline"><h3><span class="keyword">Scenario Outline:</span> <span class="val">another failing background</span></h3><ol><li class="step skipped" id="features_background_scenario_outline_failing_background_feature_13"><div><span class="keyword">Then</span> <span class="step val">I should have '&lt;count&gt;' cukes</span></div></li></ol><div class="examples"><h4><span class="keyword">Examples:</span> <span class="val"></span></h4><table><tr id="row_15"><th class="val skipped_param" id="row_15_0">count</th></tr><tr id="row_16"><td class="val skipped" id="row_16_0">10</td></tr></table></div></div></div><div class="feature"><h2><span class="val">Feature: Passing background with scenario outlines sample</span></h2><p class="narrative"></p><div class="background"><h3><span class="keyword">Background:</span> <span class="val"></span></h3><ol><li class="step passed" id="features_background_scenario_outline_passing_background_feature_4"><div><span class="keyword">Given</span> <span class="step val">'<span class="param">10</span>' cukes</span></div></li></ol></div><div class="scenario outline"><h3><span class="keyword">Scenario Outline:</span> <span class="val">passing background</span></h3><ol><li class="step skipped" id="features_background_scenario_outline_passing_background_feature_7"><div><span class="keyword">Then</span> <span class="step val">I should have '&lt;count&gt;' cukes</span></div></li></ol><div class="examples"><h4><span class="keyword">Examples:</span> <span class="val"></span></h4><table><tr id="row_9"><th class="val skipped_param" id="row_9_0">count</th></tr><tr id="row_10"><td class="val passed" id="row_10_0">10</td></tr></table></div></div><div class="scenario outline"><h3><span class="keyword">Scenario Outline:</span> <span class="val">another passing background</span></h3><ol><li class="step skipped" id="features_background_scenario_outline_passing_background_feature_13"><div><span class="keyword">Then</span> <span class="step val">I should have '&lt;count&gt;' cukes</span></div></li></ol><div class="examples"><h4><span class="keyword">Examples:</span> <span class="val"></span></h4><table><tr id="row_15"><th class="val skipped_param" id="row_15_0">count</th></tr><tr id="row_16"><td class="val passed" id="row_16_0">10</td></tr></table></div></div></div><div class="feature"><h2><span class="val">Feature: Calling undefined step</span></h2><p class="narrative"></p><div class="scenario"><h3><span class="keyword">Scenario:</span> <span class="val">Call directly</span></h3><ol><li class="step undefined" id="features_call_undefined_step_from_step_def_feature_4"><div><span class="keyword">Given</span> <span class="step val">a step definition that calls an undefined step</span></div><pre class="undefined">Undefined step: "this does not exist" (Cucumber::Undefined)
@@ -165,7 +165,7 @@ yawn</span></h3><ol><li class="step passed" id="features_multiline_name_feature_
165
165
  which goes on and on and on for three lines
166
166
  yawn</span></h3><ol><li class="step skipped" id="features_multiline_name_feature_16"><div><span class="keyword">Given</span> <span class="step val">&lt;state&gt; 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_18"><th class="val skipped_param" id="row_18_0">state</th></tr><tr id="row_19"><td class="val passed" id="row_19_0">passing</td></tr></table></div></div><div class="scenario outline"><h3><span class="keyword">Scenario Outline:</span> <span class="val">name</span></h3><ol><li class="step skipped" id="features_multiline_name_feature_22"><div><span class="keyword">Given</span> <span class="step val">&lt;state&gt; without a table</span></div></li></ol><div class="examples"><h4><span class="keyword">Examples:</span> <span class="val">I'm a multiline name
167
167
  which goes on and on and on for three lines
168
- yawn</span></h4><table><tr id="row_26"><th class="val skipped_param" id="row_26_0">state</th></tr><tr id="row_27"><td class="val passed" id="row_27_0">passing</td></tr></table></div></div></div><div class="feature"><h2><span class="val">Feature: Outline Sample</span></h2><p class="narrative"></p><div class="scenario"><h3><span class="keyword">Scenario:</span> <span class="val">I have no steps</span></h3><ol></ol></div><div class="scenario outline"><h3><span class="keyword">Scenario Outline:</span> <span class="val">Test state</span></h3><ol><li class="step skipped" id="features_outline_sample_feature_6"><div><span class="keyword">Given</span> <span class="step val">&lt;state&gt; without a table</span></div></li><li class="step skipped" id="features_outline_sample_feature_7"><div><span class="keyword">Given</span> <span class="step val">&lt;other_state&gt; without a table</span></div></li></ol><div class="examples"><h4><span class="keyword">Examples:</span> <span class="val">Rainbow colours</span></h4><table><tr id="row_9"><th class="val skipped_param" id="row_9_0">state</th><th class="val skipped_param" id="row_9_1">other_state</th></tr><tr id="row_10"><td class="val undefined" id="row_10_0">missing</td><td class="val skipped" id="row_10_1">passing</td></tr><tr id="row_11"><td class="val passed" id="row_11_0">passing</td><td class="val passed" id="row_11_1">passing</td></tr><tr id="row_12"><td class="val failed" id="row_12_0">failing</td><td class="val skipped" id="row_12_1">passing</td></tr><tr><td class="failed" colspan="2"><pre>FAIL (RuntimeError)
168
+ yawn</span></h4><table><tr id="row_26"><th class="val skipped_param" id="row_26_0">state</th></tr><tr id="row_27"><td class="val passed" id="row_27_0">passing</td></tr></table></div></div></div><div class="feature"><h2><span class="val">Feature: Outline Sample</span></h2><p class="narrative"></p><div class="scenario"><h3><span class="keyword">Scenario:</span> <span class="val">I have no steps</span></h3><ol></ol></div><div class="scenario outline"><h3><span class="keyword">Scenario Outline:</span> <span class="val">Test state</span></h3><ol><li class="step skipped" id="features_outline_sample_feature_6"><div><span class="keyword">Given</span> <span class="step val">&lt;state&gt; without a table</span></div></li><li class="step skipped" id="features_outline_sample_feature_7"><div><span class="keyword">Given</span> <span class="step val">&lt;other_state&gt; without a table</span></div></li></ol><div class="examples"><h4><span class="keyword">Examples:</span> <span class="val">Rainbow colours</span></h4><table><tr id="row_9"><th class="val skipped_param" id="row_9_0">state</th><th class="val skipped_param" id="row_9_1">other_state</th></tr><tr id="row_10"><td class="val undefined" id="row_10_0">missing</td><td class="val skipped" id="row_10_1">passing</td></tr><tr id="row_11"><td class="val passed" id="row_11_0">passing</td><td class="val passed" id="row_11_1">passing</td></tr><tr id="row_12"><td class="val failed" id="row_12_0">failing</td><td class="val skipped" id="row_12_1">passing</td></tr><tr><td colspan="2" class="failed"><pre>FAIL (RuntimeError)
169
169
  ./features/step_definitions/sample_steps.rb:2:in `flunker'
170
170
  ./features/step_definitions/sample_steps.rb:16:in `/^failing without a table$/'
171
171
  features/outline_sample.feature:6:in `Given <state> without a table'</pre></td></tr></table></div><div class="examples"><h4><span class="keyword">Examples:</span> <span class="val">Only passing</span></h4><table><tr id="row_14"><th class="val skipped_param" id="row_14_0">state</th><th class="val skipped_param" id="row_14_1">other_state</th></tr><tr id="row_15"><td class="val passed" id="row_15_0">passing</td><td class="val passed" id="row_15_1">passing</td></tr></table></div></div></div><div class="feature"><pre class="comment"># Feature comment<br/></pre><span class="tag">@one</span><h2><span class="val">Feature: Sample</span></h2><p class="narrative"></p><div class="scenario"><span class="tag">@two</span><span class="tag">@three</span><h3><span class="keyword">Scenario:</span> <span class="val">Missing</span></h3><ol><li class="step undefined" id="features_sample_feature_7"><div><span class="keyword">Given</span> <span class="step val">missing</span></div></li></ol></div><div class="scenario"><pre class="comment"># Scenario comment<br/></pre><span class="tag">@three</span><h3><span class="keyword">Scenario:</span> <span class="val">Passing</span></h3><ol><li class="step passed" id="features_sample_feature_12"><div><span class="keyword">Given</span> <span class="step val">passing</span></div><table><tr id="row_13"><td class="val" id="row_13_0">a</td><td class="val" id="row_13_1">b</td></tr><tr id="row_14"><td class="val" id="row_14_0">c</td><td class="val" id="row_14_1">d</td></tr></table></li></ol></div><div class="scenario"><span class="tag">@four</span><h3><span class="keyword">Scenario:</span> <span class="val">Failing</span></h3><ol><li class="step failed" id="features_sample_feature_18"><div><span class="keyword">Given</span> <span class="step val">failing</span></div><pre class="val"> hello</pre><pre class="failed">FAIL (RuntimeError)
@@ -174,10 +174,10 @@ features/outline_sample.feature:6:in `Given <state> without a table'</pre></td><
174
174
  features/sample.feature:18:in `Given failing'</pre></li></ol></div></div><div class="feature"><h2><span class="val">Feature: search examples</span></h2><p class="narrative"></p><div class="background"><h3><span class="keyword">Background:</span> <span class="val">Hantu Pisang background match</span></h3><ol><li class="step passed" id="features_search_sample_feature_4"><div><span class="keyword">Given</span> <span class="step val">passing without a table</span></div></li></ol></div><div class="scenario"><h3><span class="keyword">Scenario:</span> <span class="val">should match Hantu Pisang</span></h3><ol><li class="step passed" id="features_search_sample_feature_7"><div><span class="keyword">Given</span> <span class="step val">passing without a table</span></div></li></ol></div><div class="scenario"><h3><span class="keyword">Scenario:</span> <span class="val">Ignore me</span></h3><ol><li class="step failed" id="features_search_sample_feature_10"><div><span class="keyword">Given</span> <span class="step val">failing without a table</span></div><pre class="failed">FAIL (RuntimeError)
175
175
  ./features/step_definitions/sample_steps.rb:2:in `flunker'
176
176
  ./features/step_definitions/sample_steps.rb:16:in `/^failing without a table$/'
177
- features/search_sample.feature:10:in `Given failing without a table'</pre></li></ol></div><div class="scenario outline"><h3><span class="keyword">Scenario Outline:</span> <span class="val">Ignore me</span></h3><ol><li class="step skipped" id="features_search_sample_feature_13"><div><span class="keyword">Given</span> <span class="step val">&lt;state&gt; 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_15"><th class="val skipped_param" id="row_15_0">state</th></tr><tr id="row_16"><td class="val failed" id="row_16_0">failing</td></tr><tr><td class="failed" colspan="1"><pre>FAIL (RuntimeError)
177
+ features/search_sample.feature:10:in `Given failing without a table'</pre></li></ol></div><div class="scenario outline"><h3><span class="keyword">Scenario Outline:</span> <span class="val">Ignore me</span></h3><ol><li class="step skipped" id="features_search_sample_feature_13"><div><span class="keyword">Given</span> <span class="step val">&lt;state&gt; 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_15"><th class="val skipped_param" id="row_15_0">state</th></tr><tr id="row_16"><td class="val failed" id="row_16_0">failing</td></tr><tr><td colspan="1" class="failed"><pre>FAIL (RuntimeError)
178
178
  ./features/step_definitions/sample_steps.rb:2:in `flunker'
179
179
  ./features/step_definitions/sample_steps.rb:16:in `/^failing without a table$/'
180
- 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">&lt;state&gt; 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">&lt;state&gt; 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)
180
+ 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">&lt;state&gt; 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">&lt;state&gt; 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 colspan="1" class="failed"><pre>FAIL (RuntimeError)
181
181
  ./features/step_definitions/sample_steps.rb:2:in `flunker'
182
182
  ./features/step_definitions/sample_steps.rb:16:in `/^failing without a table$/'
183
183
  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)
@@ -148,3 +148,31 @@ Feature: Rake task
148
148
  """
149
149
  * features/support/dont_require_me.rb
150
150
  """
151
+
152
+ Scenario: feature files with spaces
153
+ Given a file named "features/spaces are nasty.feature" with:
154
+ """
155
+ Feature: The futures green
156
+
157
+ Scenario: Orange
158
+ Given this is missing
159
+ """
160
+ And a file named "Rakefile" with:
161
+ """
162
+ $LOAD_PATH.unshift(CUCUMBER_LIB)
163
+ require 'cucumber/rake/task'
164
+
165
+ Cucumber::Rake::Task.new(:features) do |t|
166
+ t.cucumber_opts = %w{--quiet --no-color}
167
+ end
168
+ """
169
+ When I run rake features
170
+ Then it should pass
171
+ And the output should contain
172
+ """
173
+ Feature: The futures green
174
+
175
+ Scenario: Orange
176
+ Given this is missing
177
+
178
+ """
@@ -0,0 +1,25 @@
1
+ Feature: --formatter steps option - Steps Formatter
2
+ In order to easily see which steps are already defined,
3
+ specially when using 3rd party steps libraries,
4
+ Cucumber should show the available steps in a user-friendly format
5
+
6
+ Background:
7
+ Given I am in steps_library
8
+
9
+ Scenario: Printing steps
10
+ When I run cucumber -f steps features
11
+ Then it should pass with
12
+ """
13
+ features/step_definitions/steps_lib1.rb
14
+ /^I defined a first step$/ # features/step_definitions/steps_lib1.rb:1
15
+ /^I define a second step$/ # features/step_definitions/steps_lib1.rb:4
16
+ /^I should also have a third step$/ # features/step_definitions/steps_lib1.rb:7
17
+
18
+ features/step_definitions/steps_lib2.rb
19
+ /^I defined a step 4$/ # features/step_definitions/steps_lib2.rb:1
20
+ /^I create a step 5$/ # features/step_definitions/steps_lib2.rb:4
21
+ /^I should be too tired for step 6$/ # features/step_definitions/steps_lib2.rb:7
22
+
23
+ 6 step definition(s) in 2 source file(s).
24
+
25
+ """
@@ -70,6 +70,11 @@ class CucumberWorld
70
70
  stderr_file.close
71
71
  in_current_dir do
72
72
  @last_stdout = `#{command} 2> #{stderr_file.path}`
73
+ mode = Cucumber::RUBY_1_9 ? {:external_encoding=>"UTF-8"} : 'r'
74
+ IO.popen("#{command} 2> #{stderr_file.path}", mode) do |io|
75
+ @last_stdout = io.read
76
+ end
77
+
73
78
  @last_exit_status = $?.exitstatus
74
79
  end
75
80
  @last_stderr = IO.read(stderr_file.path)
@@ -44,7 +44,8 @@ module Cucumber
44
44
  end
45
45
 
46
46
  class ExampleRow < Cells
47
-
47
+ attr_reader :scenario_outline # https://rspec.lighthouseapp.com/projects/16211/tickets/342
48
+
48
49
  def create_step_invocations!(scenario_outline)
49
50
  @scenario_outline = scenario_outline
50
51
  @step_invocations = scenario_outline.step_invocations(self)
@@ -17,7 +17,6 @@ module Cucumber
17
17
  # Note how the indentation from the source is stripped away.
18
18
  #
19
19
  class PyString
20
-
21
20
  def self.default_arg_name
22
21
  "string"
23
22
  end
@@ -85,8 +85,11 @@ module Cucumber
85
85
  private
86
86
 
87
87
  def matched_cells(cells)
88
+ col_index = 0
88
89
  cells.select do |cell|
89
- delimited = delimited(cell.header_cell.value)
90
+ header_cell = cell.table.header_cell(col_index)
91
+ col_index += 1
92
+ delimited = delimited(header_cell.value)
90
93
  @name.index(delimited) || (@multiline_arg && @multiline_arg.has_text?(delimited))
91
94
  end
92
95
  end
@@ -7,7 +7,9 @@ module Cucumber
7
7
  #
8
8
  # This gets parsed into a Table holding the values <tt>[['a', 'b'], ['c', 'd']]</tt>
9
9
  #
10
- class Table
10
+ class Table
11
+ include Enumerable
12
+
11
13
  NULL_CONVERSIONS = Hash.new(lambda{ |cell_value| cell_value }).freeze
12
14
 
13
15
  attr_accessor :file
@@ -16,18 +18,19 @@ module Cucumber
16
18
  "table"
17
19
  end
18
20
 
19
- def initialize(raw, conversions = NULL_CONVERSIONS.dup)
20
- # Verify that it's square
21
- raw.transpose
22
- @raw = raw
21
+ def initialize(raw, conversion_procs = NULL_CONVERSIONS.dup)
23
22
  @cells_class = Cells
24
23
  @cell_class = Cell
25
- @conversion_procs = conversions
24
+
25
+ # Verify that it's square
26
+ transposed = raw.transpose
27
+ create_cell_matrix(raw)
28
+ @conversion_procs = conversion_procs
26
29
  end
27
30
 
28
31
  # Creates a copy of this table, inheriting the column mappings.
29
32
  def dup
30
- self.class.new(@raw.dup, @conversion_procs.dup)
33
+ self.class.new(raw.dup, @conversion_procs.dup)
31
34
  end
32
35
 
33
36
  # Returns a new, transposed table. Example:
@@ -42,7 +45,7 @@ module Cucumber
42
45
  # | 4 | 2 |
43
46
  #
44
47
  def transpose
45
- self.class.new(@raw.transpose, @conversion_procs.dup)
48
+ self.class.new(raw.transpose, @conversion_procs.dup)
46
49
  end
47
50
 
48
51
  # Converts this table into an Array of Hash where the keys of each
@@ -78,8 +81,9 @@ module Cucumber
78
81
  # The table must be exactly two columns wide
79
82
  #
80
83
  def rows_hash
84
+ return @rows_hash if @rows_hash
81
85
  verify_table_width(2)
82
- @rows_hash = self.transpose.hashes[0]
86
+ @rows_hash = self.transpose.hashes[0].freeze
83
87
  end
84
88
 
85
89
  # Gets the raw data of this table. For example, a Table built from
@@ -88,17 +92,21 @@ module Cucumber
88
92
  # | a | b |
89
93
  # | c | d |
90
94
  #
91
- # Get converted into the following:
95
+ # gets converted into the following:
92
96
  #
93
97
  # [['a', 'b], ['c', 'd']]
94
98
  #
95
99
  def raw
96
- @raw
100
+ cell_matrix.map do |row|
101
+ row.map do |cell|
102
+ cell.value
103
+ end
104
+ end
97
105
  end
98
106
 
99
107
  # Same as #raw, but skips the first (header) row
100
108
  def rows
101
- @raw[1..-1]
109
+ raw[1..-1]
102
110
  end
103
111
 
104
112
  def each_cells_row(&proc)
@@ -117,7 +125,7 @@ module Cucumber
117
125
  [:table, *cells_rows.map{|row| row.to_sexp}]
118
126
  end
119
127
 
120
- # Returns a new Table where the headers are redefined. This makes it
128
+ # Redefines the table headers. This makes it
121
129
  # possible to use prettier header names in the features. Example:
122
130
  #
123
131
  # | Phone Number | Address |
@@ -130,6 +138,18 @@ module Cucumber
130
138
  # hashes = mapped_table.hashes
131
139
  # # => [{:phone => '123456', :address => 'xyz'}, {:phone => '345678', :address => 'abc'}]
132
140
  #
141
+ def map_headers!(mappings)
142
+ header_cells = cell_matrix[0]
143
+ mappings.each_pair do |pre, post|
144
+ header_cell = header_cells.detect{|cell| cell.value == pre}
145
+ header_cell.value = post
146
+ if @conversion_procs.has_key?(pre)
147
+ @conversion_procs[post] = @conversion_procs.delete(pre)
148
+ end
149
+ end
150
+ end
151
+
152
+ # Returns a new Table where the headers are redefined. See #map_headers!
133
153
  def map_headers(mappings)
134
154
  table = self.dup
135
155
  table.map_headers!(mappings)
@@ -153,11 +173,110 @@ module Cucumber
153
173
  @conversion_procs[column_name] = conversion_proc
154
174
  end
155
175
 
176
+ # Compares +other_table+ to self. If +other_table+ contains columns
177
+ # and/or rows that are not in self, new columns/rows are added at the
178
+ # relevant positions, marking the cells in those rows/columns as
179
+ # <tt>surplus</tt>. Likewise, if +other_table+ lacks columns and/or
180
+ # rows that are present in self, these are marked as <tt>missing</tt>.
181
+ #
182
+ # <tt>surplus</tt> and <tt>missing</tt> cells are recognised by formatters
183
+ # and displayed so that it's easy to read the differences.
184
+ #
185
+ # Cells that are different, but <em>look</em> identical (for example the
186
+ # boolean true and the string "true") are converted to their Object#inspect
187
+ # representation and preceded with (i) - to make it easier to identify
188
+ # where the difference actually is.
189
+ #
190
+ # Since all tables that are passed to StepDefinitions always have String
191
+ # objects in their cells, you may want to use #map_column! before calling
192
+ # #diff!
193
+ #
194
+ # An exception is raised if there are missing rows or columns, or
195
+ # surplus rows. An error is <em>not</em> raised for surplus columns.
196
+ # Whether to raise or not raise can be changed by setting values in
197
+ # +options+ to true or false:
198
+ #
199
+ # * <tt>missing_row</tt>: Raise on missing rows (defaults to true)
200
+ # * <tt>surplus_row</tt>: Raise on surplus rows (defaults to true)
201
+ # * <tt>missing_col</tt>: Raise on missing columns (defaults to true)
202
+ # * <tt>surplus_col</tt>: Raise on surplus columns (defaults to false)
203
+ #
204
+ # The +other_table+ argument can be another Table, an Array of Array or
205
+ # an Array of Hash (similar to the structure returned by #hashes).
206
+ #
207
+ # Calling this method is particularly useful in <tt>Then</tt> steps that take
208
+ # a Table argument, if you want to compare that table to some actual values.
209
+ #
210
+ def diff!(other_table, options={})
211
+ options = {:missing_row => true, :surplus_row => true, :missing_col => true, :surplus_col => false}.merge(options)
212
+
213
+ other_table = ensure_table(other_table)
214
+ ensure_green!
215
+
216
+ original_width = cell_matrix[0].length
217
+ other_table_cell_matrix = pad!(other_table.cell_matrix)
218
+ padded_width = cell_matrix[0].length
219
+
220
+ missing_col = cell_matrix[0].detect{|cell| cell.status == :undefined}
221
+ surplus_col = padded_width > original_width
222
+
223
+ require_diff_lcs
224
+ cell_matrix.extend(Diff::LCS)
225
+ convert_columns!
226
+ changes = cell_matrix.diff(other_table_cell_matrix).flatten
227
+
228
+ inserted = 0
229
+ missing = 0
230
+
231
+ row_indices = Array.new(other_table_cell_matrix.length) {|n| n}
232
+
233
+ last_change = nil
234
+ missing_row_pos = nil
235
+ insert_row_pos = nil
236
+
237
+ changes.each do |change|
238
+ if(change.action == '-')
239
+ missing_row_pos = change.position + inserted
240
+ cell_matrix[missing_row_pos].each{|cell| cell.status = :undefined}
241
+ row_indices.insert(missing_row_pos, nil)
242
+ missing += 1
243
+ else # '+'
244
+ insert_row_pos = change.position + missing
245
+ inserted_row = change.element
246
+ inserted_row.each{|cell| cell.status = :comment}
247
+ cell_matrix.insert(insert_row_pos, inserted_row)
248
+ row_indices[insert_row_pos] = nil
249
+ inspect_rows(cell_matrix[missing_row_pos], inserted_row) if last_change && last_change.action == '-'
250
+ inserted += 1
251
+ end
252
+ last_change = change
253
+ end
254
+
255
+ other_table_cell_matrix.each_with_index do |other_row, i|
256
+ row_index = row_indices.index(i)
257
+ row = cell_matrix[row_index] if row_index
258
+ if row
259
+ (original_width..padded_width).each do |col_index|
260
+ surplus_cell = other_row[col_index]
261
+ row[col_index].value = surplus_cell.value if row[col_index]
262
+ end
263
+ end
264
+ end
265
+
266
+ clear_cache!
267
+ should_raise =
268
+ missing_row_pos && options[:missing_row] ||
269
+ insert_row_pos && options[:surplus_row] ||
270
+ missing_col && options[:missing_col] ||
271
+ surplus_col && options[:surplus_col]
272
+ raise 'Tables were not identical' if should_raise
273
+ end
274
+
156
275
  def to_hash(cells) #:nodoc:
157
276
  hash = Hash.new do |hash, key|
158
277
  hash[key.to_s] if key.is_a?(Symbol)
159
278
  end
160
- @raw[0].each_with_index do |column_name, column_index|
279
+ raw[0].each_with_index do |column_name, column_index|
161
280
  value = @conversion_procs[column_name].call(cells.value(column_index))
162
281
  hash[column_name] = value
163
282
  end
@@ -169,11 +288,11 @@ module Cucumber
169
288
  end
170
289
 
171
290
  def verify_column(column_name)
172
- raise %{The column named "#{column_name}" does not exist} unless @raw[0].include?(column_name)
291
+ raise %{The column named "#{column_name}" does not exist} unless raw[0].include?(column_name)
173
292
  end
174
293
 
175
294
  def verify_table_width(width)
176
- raise %{The table must have exactly #{width} columns} unless @raw[0].size == width
295
+ raise %{The table must have exactly #{width} columns} unless raw[0].size == width
177
296
  end
178
297
 
179
298
  def arguments_replaced(arguments) #:nodoc:
@@ -198,33 +317,88 @@ module Cucumber
198
317
  def cells_rows
199
318
  @rows ||= cell_matrix.map do |cell_row|
200
319
  @cells_class.new(self, cell_row)
201
- end.freeze
320
+ end
202
321
  end
203
322
 
204
323
  def headers
205
- @raw.first
324
+ raw.first
206
325
  end
207
326
 
208
327
  def header_cell(col)
209
328
  cells_rows[0][col]
210
329
  end
211
330
 
331
+ def cell_matrix
332
+ @cell_matrix
333
+ end
334
+
335
+ def col_width(col)
336
+ columns[col].__send__(:width)
337
+ end
338
+
339
+ def to_s(options = {})
340
+ options = {:color => true, :indent => 2, :prefixes => TO_S_PREFIXES}.merge(options)
341
+ io = StringIO.new
342
+
343
+ c = Term::ANSIColor.coloring?
344
+ Term::ANSIColor.coloring = options[:color]
345
+ f = Formatter::Pretty.new(nil, io, options)
346
+ f.instance_variable_set('@indent', options[:indent])
347
+ f.visit_multiline_arg(self)
348
+ Term::ANSIColor.coloring = c
349
+
350
+ io.rewind
351
+ s = "\n" + io.read + (" " * (options[:indent] - 2))
352
+ s
353
+ end
354
+
355
+ private
356
+
357
+ TO_S_PREFIXES = Hash.new(' ')
358
+ TO_S_PREFIXES[:comment] = ['(+) ']
359
+ TO_S_PREFIXES[:undefined] = ['(-) ']
360
+
212
361
  protected
213
362
 
214
- def map_headers!(mappings)
215
- headers = @raw[0]
216
- mappings.each_pair do |pre, post|
217
- headers[headers.index(pre)] = post
218
- if @conversion_procs.has_key?(pre)
219
- @conversion_procs[post] = @conversion_procs.delete(pre)
363
+ def inspect_rows(missing_row, inserted_row)
364
+ missing_row.each_with_index do |missing_cell, col|
365
+ inserted_cell = inserted_row[col]
366
+ if(missing_cell.value != inserted_cell.value && (missing_cell.value.to_s == inserted_cell.value.to_s))
367
+ missing_cell.inspect!
368
+ inserted_cell.inspect!
220
369
  end
221
370
  end
222
371
  end
223
372
 
224
- private
373
+ def create_cell_matrix(raw)
374
+ @cell_matrix = raw.map do |raw_row|
375
+ line = raw_row.line rescue -1
376
+ raw_row.map do |raw_cell|
377
+ new_cell(raw_cell, line)
378
+ end
379
+ end
380
+ end
225
381
 
226
- def col_width(col)
227
- columns[col].__send__(:width)
382
+ def convert_columns!
383
+ cell_matrix.transpose.each do |col|
384
+ conversion_proc = @conversion_procs[col[0].value]
385
+ col[1..-1].each do |cell|
386
+ cell.value = conversion_proc.call(cell.value)
387
+ end
388
+ end
389
+ end
390
+
391
+ def require_diff_lcs
392
+ begin
393
+ require 'diff/lcs'
394
+ rescue LoadError => e
395
+ e.message << "\n Please gem install diff-lcs\n"
396
+ raise e
397
+ end
398
+ end
399
+
400
+ def clear_cache!
401
+ @hashes = @rows_hash = @rows = @columns = nil
228
402
  end
229
403
 
230
404
  def columns
@@ -233,17 +407,73 @@ module Cucumber
233
407
  end.freeze
234
408
  end
235
409
 
236
- def cell_matrix
237
- row = -1
238
- @cell_matrix ||= @raw.map do |raw_row|
239
- line = raw_row.line rescue -1
240
- row += 1
241
- col = -1
242
- raw_row.map do |raw_cell|
243
- col += 1
244
- @cell_class.new(raw_cell, self, row, col, line)
410
+ def new_cell(raw_cell, line)
411
+ @cell_class.new(raw_cell, self, line)
412
+ end
413
+
414
+ # Pads our own cell_matrix and returns a cell matrix of same
415
+ # column width that can be used for diffing
416
+ def pad!(other_cell_matrix)
417
+ clear_cache!
418
+ cols = cell_matrix.transpose
419
+ unmapped_cols = other_cell_matrix.transpose
420
+
421
+ mapped_cols = []
422
+
423
+ cols.each_with_index do |col, col_index|
424
+ header = col[0]
425
+ candidate_cols, unmapped_cols = unmapped_cols.partition do |other_col|
426
+ other_col[0] == header
245
427
  end
246
- end.freeze
428
+ raise "More than one column has the header #{header}" if candidate_cols.size > 2
429
+
430
+ other_padded_col = if candidate_cols.size == 1
431
+ # Found a matching column
432
+ candidate_cols[0]
433
+ else
434
+ mark_as_missing(cols[col_index])
435
+ (0...other_cell_matrix.length).map do |row|
436
+ val = row == 0 ? header.value : nil
437
+ SurplusCell.new(val, self, -1)
438
+ end
439
+ end
440
+ mapped_cols.insert(col_index, other_padded_col)
441
+ end
442
+
443
+ unmapped_cols.each_with_index do |col, col_index|
444
+ empty_col = (0...cell_matrix.length).map do |row|
445
+ SurplusCell.new(nil, self, -1)
446
+ end
447
+ cols << empty_col
448
+ end
449
+
450
+ @cell_matrix = cols.transpose
451
+ (mapped_cols + unmapped_cols).transpose
452
+ end
453
+
454
+ def ensure_table(table_or_array)
455
+ return table_or_array if Table === table_or_array
456
+ table_or_array = hashes_to_array(table_or_array) if Hash === table_or_array[0]
457
+ Table.new(table_or_array)
458
+ end
459
+
460
+ def hashes_to_array(hashes)
461
+ header = hashes[0].keys
462
+ [header] + hashes.map{|hash| header.map{|key| hash[key]}}
463
+ end
464
+
465
+ def ensure_green!
466
+ each_cell{|cell| cell.status = :passed}
467
+ end
468
+
469
+ def each_cell(&proc)
470
+ cell_matrix.each{|row| row.each(&proc)}
471
+ end
472
+
473
+ def mark_as_missing(col)
474
+ col.each do |cell|
475
+ cell.status = :undefined
476
+ end
247
477
  end
248
478
 
249
479
  # Represents a row of cells or columns of cells
@@ -303,30 +533,38 @@ module Cucumber
303
533
  end
304
534
 
305
535
  class Cell
306
- attr_reader :value, :line
307
- attr_writer :status
536
+ attr_reader :line, :table
537
+ attr_accessor :status, :value
308
538
 
309
- def initialize(value, table, row, col, line)
310
- @value, @table, @row, @col, @line = value, table, row, col, line
539
+ def initialize(value, table, line)
540
+ @value, @table, @line = value, table, line
311
541
  end
312
542
 
313
543
  def accept(visitor)
314
- visitor.visit_table_cell_value(@value, col_width, @status)
544
+ visitor.visit_table_cell_value(value, status)
545
+ end
546
+
547
+ def inspect!
548
+ @value = "(i) #{value.inspect}"
315
549
  end
316
550
 
317
- def header_cell
318
- @table.header_cell(@col)
551
+ def ==(o)
552
+ SurplusCell === o || value == o.value
319
553
  end
320
554
 
321
555
  # For testing only
322
556
  def to_sexp #:nodoc:
323
557
  [:cell, @value]
324
558
  end
559
+ end
560
+
561
+ class SurplusCell < Cell
562
+ def status
563
+ :comment
564
+ end
325
565
 
326
- private
327
-
328
- def col_width
329
- @col_width ||= @table.__send__(:col_width, @col).freeze
566
+ def ==(o)
567
+ true
330
568
  end
331
569
  end
332
570
  end