square-cucumber 0.3.12.2 → 0.3.93.1
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 +117 -4
- data/Manifest.txt +11 -0
- data/Rakefile +1 -1
- data/config/hoe.rb +3 -2
- data/cucumber.yml +2 -2
- data/examples/i18n/ko/features/addition.feature +5 -5
- data/examples/i18n/ko/features/step_definitons/calculator_steps.rb +1 -1
- data/examples/i18n/no/features/step_definitons/kalkulator_steps.rb +1 -1
- data/examples/i18n/pt/features/adicao.feature +4 -4
- data/examples/self_test/features/support/env.rb +2 -1
- data/examples/sinatra/features/support/env.rb +7 -1
- data/examples/steps_library/features/step_definitions/steps_lib1.rb +8 -0
- data/examples/steps_library/features/step_definitions/steps_lib2.rb +8 -0
- data/examples/tickets/features/step_definitons/tickets_steps.rb +15 -0
- data/examples/tickets/features/table_diffing.feature +13 -0
- data/examples/watir/features/step_definitons/search_steps.rb +5 -1
- data/features/cucumber_cli_diff_disabled.feature +2 -1
- data/features/html_formatter/a.html +5 -7
- data/features/junit_formatter.feature +21 -14
- data/features/profiles.feature +99 -0
- data/features/rake_task.feature +28 -0
- data/features/step_definitions/cucumber_steps.rb +28 -15
- data/features/steps_formatter.feature +25 -0
- data/features/support/env.rb +9 -5
- data/features/table_diffing.feature +45 -0
- data/features/unicode_table.feature +35 -0
- data/features/work_in_progress.feature +1 -0
- data/gem_tasks/contributors.rake +4 -0
- data/lib/cucumber/ast/background.rb +1 -0
- data/lib/cucumber/ast/comment.rb +1 -0
- data/lib/cucumber/ast/examples.rb +1 -0
- data/lib/cucumber/ast/feature.rb +10 -0
- data/lib/cucumber/ast/features.rb +6 -1
- data/lib/cucumber/ast/outline_table.rb +4 -1
- data/lib/cucumber/ast/py_string.rb +1 -1
- data/lib/cucumber/ast/scenario.rb +1 -0
- data/lib/cucumber/ast/scenario_outline.rb +2 -0
- data/lib/cucumber/ast/step.rb +5 -1
- data/lib/cucumber/ast/step_collection.rb +1 -0
- data/lib/cucumber/ast/step_invocation.rb +1 -0
- data/lib/cucumber/ast/table.rb +306 -52
- data/lib/cucumber/ast/tags.rb +1 -0
- data/lib/cucumber/ast/visitor.rb +2 -1
- data/lib/cucumber/cli/configuration.rb +28 -278
- data/lib/cucumber/cli/drb_client.rb +3 -1
- data/lib/cucumber/cli/language_help_formatter.rb +9 -7
- data/lib/cucumber/cli/main.rb +16 -2
- data/lib/cucumber/cli/options.rb +370 -0
- data/lib/cucumber/cli/profile_loader.rb +65 -0
- data/lib/cucumber/core_ext/instance_exec.rb +8 -5
- data/lib/cucumber/feature_file.rb +7 -1
- data/lib/cucumber/filter.rb +2 -2
- data/lib/cucumber/formatter/ansicolor.rb +42 -9
- data/lib/cucumber/formatter/console.rb +1 -1
- data/lib/cucumber/formatter/html.rb +12 -10
- data/lib/cucumber/formatter/junit.rb +63 -26
- data/lib/cucumber/formatter/pretty.rb +20 -5
- data/lib/cucumber/formatter/progress.rb +1 -1
- data/lib/cucumber/formatter/steps.rb +49 -0
- data/lib/cucumber/languages.yml +6 -6
- data/lib/cucumber/parser/feature.rb +90 -63
- data/lib/cucumber/parser/feature.tt +28 -1
- data/lib/cucumber/parser/i18n/language.rb +12 -5
- data/lib/cucumber/parser/table.rb +25 -25
- data/lib/cucumber/rake/task.rb +9 -3
- data/lib/cucumber/step_definition.rb +1 -1
- data/lib/cucumber/step_match.rb +1 -1
- data/lib/cucumber/step_mother.rb +3 -1
- data/lib/cucumber/version.rb +2 -2
- data/lib/cucumber/webrat/table_locator.rb +66 -0
- data/rails_generators/cucumber/cucumber_generator.rb +5 -1
- data/rails_generators/cucumber/templates/cucumber +3 -2
- data/rails_generators/cucumber/templates/cucumber.rake +18 -6
- data/rails_generators/cucumber/templates/cucumber_environment.rb +7 -4
- data/rails_generators/cucumber/templates/env.rb +1 -0
- data/rails_generators/cucumber/templates/spork_env.rb +1 -0
- data/rails_generators/cucumber/templates/webrat_steps.rb +22 -0
- data/rails_generators/feature/templates/feature.erb +1 -1
- data/rails_generators/feature/templates/steps.erb +2 -8
- data/spec/cucumber/ast/table_spec.rb +169 -0
- data/spec/cucumber/cli/configuration_spec.rb +144 -101
- data/spec/cucumber/cli/main_spec.rb +14 -5
- data/spec/cucumber/cli/options_spec.rb +311 -0
- data/spec/cucumber/cli/profile_loader_spec.rb +10 -0
- data/spec/cucumber/core_ext/proc_spec.rb +16 -2
- data/spec/cucumber/formatter/html_spec.rb +18 -0
- data/spec/cucumber/formatter/progress_spec.rb +2 -2
- data/spec/cucumber/parser/table_parser_spec.rb +1 -1
- data/spec/spec.opts +3 -1
- metadata +18 -4
- data/lib/cucumber/webrat/mechanize_world.rb +0 -82
data/lib/cucumber/ast/feature.rb
CHANGED
@@ -16,6 +16,7 @@ module Cucumber
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def accept(visitor)
|
19
|
+
return if $cucumber_interrupted
|
19
20
|
visitor.visit_comment(@comment) unless @comment.empty?
|
20
21
|
visitor.visit_tags(@tags)
|
21
22
|
visitor.visit_feature_name(@name)
|
@@ -43,6 +44,15 @@ module Cucumber
|
|
43
44
|
"#{@file}:#{line}"
|
44
45
|
end
|
45
46
|
|
47
|
+
def short_name
|
48
|
+
first_line = name.split(/\n/)[0]
|
49
|
+
if first_line =~ /#{language.keywords('feature', true)}:(.*)/
|
50
|
+
$1.strip
|
51
|
+
else
|
52
|
+
first_line
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
46
56
|
def to_sexp
|
47
57
|
sexp = [:feature, @file, @name]
|
48
58
|
comment = @comment.to_sexp
|
@@ -9,6 +9,10 @@ module Cucumber
|
|
9
9
|
@features = []
|
10
10
|
end
|
11
11
|
|
12
|
+
def [](index)
|
13
|
+
@features[index]
|
14
|
+
end
|
15
|
+
|
12
16
|
def each(&proc)
|
13
17
|
@features.each(&proc)
|
14
18
|
end
|
@@ -19,8 +23,9 @@ module Cucumber
|
|
19
23
|
end
|
20
24
|
|
21
25
|
def accept(visitor)
|
26
|
+
return if $cucumber_interrupted
|
22
27
|
start = Time.now
|
23
|
-
|
28
|
+
self.each do |feature|
|
24
29
|
visitor.visit_feature(feature)
|
25
30
|
end
|
26
31
|
@duration = Time.now - start
|
@@ -9,6 +9,7 @@ module Cucumber
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def accept(visitor)
|
12
|
+
return if $cucumber_interrupted
|
12
13
|
cells_rows.each_with_index do |row, n|
|
13
14
|
if(visitor.options[:expand])
|
14
15
|
row.accept(visitor)
|
@@ -44,7 +45,8 @@ module Cucumber
|
|
44
45
|
end
|
45
46
|
|
46
47
|
class ExampleRow < Cells
|
47
|
-
|
48
|
+
attr_reader :scenario_outline # https://rspec.lighthouseapp.com/projects/16211/tickets/342
|
49
|
+
|
48
50
|
def create_step_invocations!(scenario_outline)
|
49
51
|
@scenario_outline = scenario_outline
|
50
52
|
@step_invocations = scenario_outline.step_invocations(self)
|
@@ -57,6 +59,7 @@ module Cucumber
|
|
57
59
|
end
|
58
60
|
|
59
61
|
def accept(visitor)
|
62
|
+
return if $cucumber_interrupted
|
60
63
|
visitor.options[:expand] ? accept_expand(visitor) : accept_plain(visitor)
|
61
64
|
end
|
62
65
|
|
@@ -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
|
@@ -32,6 +31,7 @@ module Cucumber
|
|
32
31
|
end
|
33
32
|
|
34
33
|
def accept(visitor)
|
34
|
+
return if $cucumber_interrupted
|
35
35
|
visitor.visit_py_string(to_s)
|
36
36
|
end
|
37
37
|
|
@@ -21,6 +21,7 @@ module Cucumber
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def accept(visitor)
|
24
|
+
return if $cucumber_interrupted
|
24
25
|
visitor.visit_comment(@comment) unless @comment.empty?
|
25
26
|
visitor.visit_tags(@tags)
|
26
27
|
visitor.visit_scenario_name(@keyword, @name, file_colon_line(@line), source_indent(first_line_length))
|
@@ -5,6 +5,7 @@ module Cucumber
|
|
5
5
|
|
6
6
|
module ExamplesArray
|
7
7
|
def accept(visitor)
|
8
|
+
return if $cucumber_interrupted
|
8
9
|
each do |examples|
|
9
10
|
visitor.visit_examples(examples)
|
10
11
|
end
|
@@ -37,6 +38,7 @@ module Cucumber
|
|
37
38
|
end
|
38
39
|
|
39
40
|
def accept(visitor)
|
41
|
+
return if $cucumber_interrupted
|
40
42
|
visitor.visit_comment(@comment) unless @comment.empty?
|
41
43
|
visitor.visit_tags(@tags)
|
42
44
|
visitor.visit_scenario_name(@keyword, @name, file_colon_line(@line), source_indent(first_line_length))
|
data/lib/cucumber/ast/step.rb
CHANGED
@@ -33,6 +33,7 @@ module Cucumber
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def accept(visitor)
|
36
|
+
return if $cucumber_interrupted
|
36
37
|
# The only time a Step is visited is when it is in a ScenarioOutline.
|
37
38
|
# Otherwise it's always StepInvocation that gets visited instead.
|
38
39
|
visit_step_result(visitor, first_match(visitor), @multiline_arg, :skipped, nil, nil)
|
@@ -85,8 +86,11 @@ module Cucumber
|
|
85
86
|
private
|
86
87
|
|
87
88
|
def matched_cells(cells)
|
89
|
+
col_index = 0
|
88
90
|
cells.select do |cell|
|
89
|
-
|
91
|
+
header_cell = cell.table.header_cell(col_index)
|
92
|
+
col_index += 1
|
93
|
+
delimited = delimited(header_cell.value)
|
90
94
|
@name.index(delimited) || (@multiline_arg && @multiline_arg.has_text?(delimited))
|
91
95
|
end
|
92
96
|
end
|
data/lib/cucumber/ast/table.rb
CHANGED
@@ -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,
|
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
|
-
|
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(
|
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(
|
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
|
-
#
|
95
|
+
# gets converted into the following:
|
92
96
|
#
|
93
97
|
# [['a', 'b], ['c', 'd']]
|
94
98
|
#
|
95
99
|
def raw
|
96
|
-
|
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
|
-
|
109
|
+
raw[1..-1]
|
102
110
|
end
|
103
111
|
|
104
112
|
def each_cells_row(&proc)
|
@@ -106,6 +114,7 @@ module Cucumber
|
|
106
114
|
end
|
107
115
|
|
108
116
|
def accept(visitor)
|
117
|
+
return if $cucumber_interrupted
|
109
118
|
cells_rows.each do |row|
|
110
119
|
visitor.visit_table_row(row)
|
111
120
|
end
|
@@ -117,19 +126,38 @@ module Cucumber
|
|
117
126
|
[:table, *cells_rows.map{|row| row.to_sexp}]
|
118
127
|
end
|
119
128
|
|
120
|
-
#
|
121
|
-
#
|
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:
|
122
137
|
#
|
123
138
|
# | Phone Number | Address |
|
124
139
|
# | 123456 | xyz |
|
125
140
|
# | 345678 | abc |
|
126
141
|
#
|
127
|
-
# 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:
|
128
144
|
#
|
129
|
-
#
|
130
|
-
#
|
145
|
+
# table.map_headers!(/phone( number)?/i => :phone, 'Address' => :address)
|
146
|
+
# table.hashes
|
131
147
|
# # => [{:phone => '123456', :address => 'xyz'}, {:phone => '345678', :address => 'abc'}]
|
132
148
|
#
|
149
|
+
def map_headers!(mappings)
|
150
|
+
header_cells = cell_matrix[0]
|
151
|
+
mappings.each_pair do |pre, post|
|
152
|
+
header_cell = header_cells.detect{|cell| pre === cell.value}
|
153
|
+
header_cell.value = post
|
154
|
+
if @conversion_procs.has_key?(pre)
|
155
|
+
@conversion_procs[post] = @conversion_procs.delete(pre)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Returns a new Table where the headers are redefined. See #map_headers!
|
133
161
|
def map_headers(mappings)
|
134
162
|
table = self.dup
|
135
163
|
table.map_headers!(mappings)
|
@@ -153,11 +181,111 @@ module Cucumber
|
|
153
181
|
@conversion_procs[column_name] = conversion_proc
|
154
182
|
end
|
155
183
|
|
184
|
+
# Compares +other_table+ to self. If +other_table+ contains columns
|
185
|
+
# and/or rows that are not in self, new columns/rows are added at the
|
186
|
+
# relevant positions, marking the cells in those rows/columns as
|
187
|
+
# <tt>surplus</tt>. Likewise, if +other_table+ lacks columns and/or
|
188
|
+
# rows that are present in self, these are marked as <tt>missing</tt>.
|
189
|
+
#
|
190
|
+
# <tt>surplus</tt> and <tt>missing</tt> cells are recognised by formatters
|
191
|
+
# and displayed so that it's easy to read the differences.
|
192
|
+
#
|
193
|
+
# Cells that are different, but <em>look</em> identical (for example the
|
194
|
+
# boolean true and the string "true") are converted to their Object#inspect
|
195
|
+
# representation and preceded with (i) - to make it easier to identify
|
196
|
+
# where the difference actually is.
|
197
|
+
#
|
198
|
+
# Since all tables that are passed to StepDefinitions always have String
|
199
|
+
# objects in their cells, you may want to use #map_column! before calling
|
200
|
+
# #diff!. You can use #map_column! on either of the tables.
|
201
|
+
#
|
202
|
+
# An exception is raised if there are missing rows or columns, or
|
203
|
+
# surplus rows. An error is <em>not</em> raised for surplus columns.
|
204
|
+
# Whether to raise or not raise can be changed by setting values in
|
205
|
+
# +options+ to true or false:
|
206
|
+
#
|
207
|
+
# * <tt>missing_row</tt>: Raise on missing rows (defaults to true)
|
208
|
+
# * <tt>surplus_row</tt>: Raise on surplus rows (defaults to true)
|
209
|
+
# * <tt>missing_col</tt>: Raise on missing columns (defaults to true)
|
210
|
+
# * <tt>surplus_col</tt>: Raise on surplus columns (defaults to false)
|
211
|
+
#
|
212
|
+
# The +other_table+ argument can be another Table, an Array of Array or
|
213
|
+
# an Array of Hash (similar to the structure returned by #hashes).
|
214
|
+
#
|
215
|
+
# Calling this method is particularly useful in <tt>Then</tt> steps that take
|
216
|
+
# a Table argument, if you want to compare that table to some actual values.
|
217
|
+
#
|
218
|
+
def diff!(other_table, options={})
|
219
|
+
options = {:missing_row => true, :surplus_row => true, :missing_col => true, :surplus_col => false}.merge(options)
|
220
|
+
|
221
|
+
other_table = ensure_table(other_table)
|
222
|
+
other_table.convert_columns!
|
223
|
+
ensure_green!
|
224
|
+
|
225
|
+
original_width = cell_matrix[0].length
|
226
|
+
other_table_cell_matrix = pad!(other_table.cell_matrix)
|
227
|
+
padded_width = cell_matrix[0].length
|
228
|
+
|
229
|
+
missing_col = cell_matrix[0].detect{|cell| cell.status == :undefined}
|
230
|
+
surplus_col = padded_width > original_width
|
231
|
+
|
232
|
+
require_diff_lcs
|
233
|
+
cell_matrix.extend(Diff::LCS)
|
234
|
+
convert_columns!
|
235
|
+
changes = cell_matrix.diff(other_table_cell_matrix).flatten
|
236
|
+
|
237
|
+
inserted = 0
|
238
|
+
missing = 0
|
239
|
+
|
240
|
+
row_indices = Array.new(other_table_cell_matrix.length) {|n| n}
|
241
|
+
|
242
|
+
last_change = nil
|
243
|
+
missing_row_pos = nil
|
244
|
+
insert_row_pos = nil
|
245
|
+
|
246
|
+
changes.each do |change|
|
247
|
+
if(change.action == '-')
|
248
|
+
missing_row_pos = change.position + inserted
|
249
|
+
cell_matrix[missing_row_pos].each{|cell| cell.status = :undefined}
|
250
|
+
row_indices.insert(missing_row_pos, nil)
|
251
|
+
missing += 1
|
252
|
+
else # '+'
|
253
|
+
insert_row_pos = change.position + missing
|
254
|
+
inserted_row = change.element
|
255
|
+
inserted_row.each{|cell| cell.status = :comment}
|
256
|
+
cell_matrix.insert(insert_row_pos, inserted_row)
|
257
|
+
row_indices[insert_row_pos] = nil
|
258
|
+
inspect_rows(cell_matrix[missing_row_pos], inserted_row) if last_change && last_change.action == '-'
|
259
|
+
inserted += 1
|
260
|
+
end
|
261
|
+
last_change = change
|
262
|
+
end
|
263
|
+
|
264
|
+
other_table_cell_matrix.each_with_index do |other_row, i|
|
265
|
+
row_index = row_indices.index(i)
|
266
|
+
row = cell_matrix[row_index] if row_index
|
267
|
+
if row
|
268
|
+
(original_width..padded_width).each do |col_index|
|
269
|
+
surplus_cell = other_row[col_index]
|
270
|
+
row[col_index].value = surplus_cell.value if row[col_index]
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
clear_cache!
|
276
|
+
should_raise =
|
277
|
+
missing_row_pos && options[:missing_row] ||
|
278
|
+
insert_row_pos && options[:surplus_row] ||
|
279
|
+
missing_col && options[:missing_col] ||
|
280
|
+
surplus_col && options[:surplus_col]
|
281
|
+
raise 'Tables were not identical' if should_raise
|
282
|
+
end
|
283
|
+
|
156
284
|
def to_hash(cells) #:nodoc:
|
157
285
|
hash = Hash.new do |hash, key|
|
158
286
|
hash[key.to_s] if key.is_a?(Symbol)
|
159
287
|
end
|
160
|
-
|
288
|
+
raw[0].each_with_index do |column_name, column_index|
|
161
289
|
value = @conversion_procs[column_name].call(cells.value(column_index))
|
162
290
|
hash[column_name] = value
|
163
291
|
end
|
@@ -169,11 +297,11 @@ module Cucumber
|
|
169
297
|
end
|
170
298
|
|
171
299
|
def verify_column(column_name)
|
172
|
-
raise %{The column named "#{column_name}" does not exist} unless
|
300
|
+
raise %{The column named "#{column_name}" does not exist} unless raw[0].include?(column_name)
|
173
301
|
end
|
174
302
|
|
175
303
|
def verify_table_width(width)
|
176
|
-
raise %{The table must have exactly #{width} columns} unless
|
304
|
+
raise %{The table must have exactly #{width} columns} unless raw[0].size == width
|
177
305
|
end
|
178
306
|
|
179
307
|
def arguments_replaced(arguments) #:nodoc:
|
@@ -198,33 +326,88 @@ module Cucumber
|
|
198
326
|
def cells_rows
|
199
327
|
@rows ||= cell_matrix.map do |cell_row|
|
200
328
|
@cells_class.new(self, cell_row)
|
201
|
-
end
|
329
|
+
end
|
202
330
|
end
|
203
331
|
|
204
332
|
def headers
|
205
|
-
|
333
|
+
raw.first
|
206
334
|
end
|
207
335
|
|
208
336
|
def header_cell(col)
|
209
337
|
cells_rows[0][col]
|
210
338
|
end
|
211
339
|
|
340
|
+
def cell_matrix
|
341
|
+
@cell_matrix
|
342
|
+
end
|
343
|
+
|
344
|
+
def col_width(col)
|
345
|
+
columns[col].__send__(:width)
|
346
|
+
end
|
347
|
+
|
348
|
+
def to_s(options = {})
|
349
|
+
options = {:color => true, :indent => 2, :prefixes => TO_S_PREFIXES}.merge(options)
|
350
|
+
io = StringIO.new
|
351
|
+
|
352
|
+
c = Term::ANSIColor.coloring?
|
353
|
+
Term::ANSIColor.coloring = options[:color]
|
354
|
+
f = Formatter::Pretty.new(nil, io, options)
|
355
|
+
f.instance_variable_set('@indent', options[:indent])
|
356
|
+
f.visit_multiline_arg(self)
|
357
|
+
Term::ANSIColor.coloring = c
|
358
|
+
|
359
|
+
io.rewind
|
360
|
+
s = "\n" + io.read + (" " * (options[:indent] - 2))
|
361
|
+
s
|
362
|
+
end
|
363
|
+
|
364
|
+
private
|
365
|
+
|
366
|
+
TO_S_PREFIXES = Hash.new(' ')
|
367
|
+
TO_S_PREFIXES[:comment] = ['(+) ']
|
368
|
+
TO_S_PREFIXES[:undefined] = ['(-) ']
|
369
|
+
|
212
370
|
protected
|
213
371
|
|
214
|
-
def
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
372
|
+
def inspect_rows(missing_row, inserted_row)
|
373
|
+
missing_row.each_with_index do |missing_cell, col|
|
374
|
+
inserted_cell = inserted_row[col]
|
375
|
+
if(missing_cell.value != inserted_cell.value && (missing_cell.value.to_s == inserted_cell.value.to_s))
|
376
|
+
missing_cell.inspect!
|
377
|
+
inserted_cell.inspect!
|
220
378
|
end
|
221
379
|
end
|
222
380
|
end
|
223
381
|
|
224
|
-
|
382
|
+
def create_cell_matrix(raw)
|
383
|
+
@cell_matrix = raw.map do |raw_row|
|
384
|
+
line = raw_row.line rescue -1
|
385
|
+
raw_row.map do |raw_cell|
|
386
|
+
new_cell(raw_cell, line)
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
225
390
|
|
226
|
-
def
|
227
|
-
|
391
|
+
def convert_columns!
|
392
|
+
cell_matrix.transpose.each do |col|
|
393
|
+
conversion_proc = @conversion_procs[col[0].value]
|
394
|
+
col[1..-1].each do |cell|
|
395
|
+
cell.value = conversion_proc.call(cell.value)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
def require_diff_lcs
|
401
|
+
begin
|
402
|
+
require 'diff/lcs'
|
403
|
+
rescue LoadError => e
|
404
|
+
e.message << "\n Please gem install diff-lcs\n"
|
405
|
+
raise e
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
def clear_cache!
|
410
|
+
@hashes = @rows_hash = @rows = @columns = nil
|
228
411
|
end
|
229
412
|
|
230
413
|
def columns
|
@@ -233,17 +416,78 @@ module Cucumber
|
|
233
416
|
end.freeze
|
234
417
|
end
|
235
418
|
|
236
|
-
def
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
419
|
+
def new_cell(raw_cell, line)
|
420
|
+
@cell_class.new(raw_cell, self, line)
|
421
|
+
end
|
422
|
+
|
423
|
+
# Pads our own cell_matrix and returns a cell matrix of same
|
424
|
+
# column width that can be used for diffing
|
425
|
+
def pad!(other_cell_matrix)
|
426
|
+
clear_cache!
|
427
|
+
cols = cell_matrix.transpose
|
428
|
+
unmapped_cols = other_cell_matrix.transpose
|
429
|
+
|
430
|
+
mapped_cols = []
|
431
|
+
|
432
|
+
cols.each_with_index do |col, col_index|
|
433
|
+
header = col[0]
|
434
|
+
candidate_cols, unmapped_cols = unmapped_cols.partition do |other_col|
|
435
|
+
other_col[0] == header
|
245
436
|
end
|
246
|
-
|
437
|
+
raise "More than one column has the header #{header}" if candidate_cols.size > 2
|
438
|
+
|
439
|
+
other_padded_col = if candidate_cols.size == 1
|
440
|
+
# Found a matching column
|
441
|
+
candidate_cols[0]
|
442
|
+
else
|
443
|
+
mark_as_missing(cols[col_index])
|
444
|
+
(0...other_cell_matrix.length).map do |row|
|
445
|
+
val = row == 0 ? header.value : nil
|
446
|
+
SurplusCell.new(val, self, -1)
|
447
|
+
end
|
448
|
+
end
|
449
|
+
mapped_cols.insert(col_index, other_padded_col)
|
450
|
+
end
|
451
|
+
|
452
|
+
unmapped_cols.each_with_index do |col, col_index|
|
453
|
+
empty_col = (0...cell_matrix.length).map do |row|
|
454
|
+
SurplusCell.new(nil, self, -1)
|
455
|
+
end
|
456
|
+
cols << empty_col
|
457
|
+
end
|
458
|
+
|
459
|
+
@cell_matrix = cols.transpose
|
460
|
+
(mapped_cols + unmapped_cols).transpose
|
461
|
+
end
|
462
|
+
|
463
|
+
def ensure_table(table_or_array)
|
464
|
+
return table_or_array if Table === table_or_array
|
465
|
+
table_or_array = hashes_to_array(table_or_array) if Hash === table_or_array[0]
|
466
|
+
table_or_array = enumerable_to_array(table_or_array) unless Array == table_or_array[0]
|
467
|
+
Table.new(table_or_array)
|
468
|
+
end
|
469
|
+
|
470
|
+
def hashes_to_array(hashes)
|
471
|
+
header = hashes[0].keys
|
472
|
+
[header] + hashes.map{|hash| header.map{|key| hash[key]}}
|
473
|
+
end
|
474
|
+
|
475
|
+
def enumerable_to_array(rows)
|
476
|
+
rows.map{|row| row.map{|cell| cell}}
|
477
|
+
end
|
478
|
+
|
479
|
+
def ensure_green!
|
480
|
+
each_cell{|cell| cell.status = :passed}
|
481
|
+
end
|
482
|
+
|
483
|
+
def each_cell(&proc)
|
484
|
+
cell_matrix.each{|row| row.each(&proc)}
|
485
|
+
end
|
486
|
+
|
487
|
+
def mark_as_missing(col)
|
488
|
+
col.each do |cell|
|
489
|
+
cell.status = :undefined
|
490
|
+
end
|
247
491
|
end
|
248
492
|
|
249
493
|
# Represents a row of cells or columns of cells
|
@@ -256,6 +500,7 @@ module Cucumber
|
|
256
500
|
end
|
257
501
|
|
258
502
|
def accept(visitor)
|
503
|
+
return if $cucumber_interrupted
|
259
504
|
each do |cell|
|
260
505
|
visitor.visit_table_cell(cell)
|
261
506
|
end
|
@@ -303,30 +548,39 @@ module Cucumber
|
|
303
548
|
end
|
304
549
|
|
305
550
|
class Cell
|
306
|
-
attr_reader :
|
307
|
-
|
551
|
+
attr_reader :line, :table
|
552
|
+
attr_accessor :status, :value
|
308
553
|
|
309
|
-
def initialize(value, table,
|
310
|
-
@value, @table, @
|
554
|
+
def initialize(value, table, line)
|
555
|
+
@value, @table, @line = value, table, line
|
311
556
|
end
|
312
557
|
|
313
558
|
def accept(visitor)
|
314
|
-
|
559
|
+
return if $cucumber_interrupted
|
560
|
+
visitor.visit_table_cell_value(value, status)
|
561
|
+
end
|
562
|
+
|
563
|
+
def inspect!
|
564
|
+
@value = "(i) #{value.inspect}"
|
315
565
|
end
|
316
566
|
|
317
|
-
def
|
318
|
-
|
567
|
+
def ==(o)
|
568
|
+
SurplusCell === o || value == o.value
|
319
569
|
end
|
320
570
|
|
321
571
|
# For testing only
|
322
572
|
def to_sexp #:nodoc:
|
323
573
|
[:cell, @value]
|
324
574
|
end
|
575
|
+
end
|
576
|
+
|
577
|
+
class SurplusCell < Cell
|
578
|
+
def status
|
579
|
+
:comment
|
580
|
+
end
|
325
581
|
|
326
|
-
|
327
|
-
|
328
|
-
def col_width
|
329
|
-
@col_width ||= @table.__send__(:col_width, @col).freeze
|
582
|
+
def ==(o)
|
583
|
+
true
|
330
584
|
end
|
331
585
|
end
|
332
586
|
end
|