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
@@ -64,6 +64,7 @@ module Cucumber
64
64
  end
65
65
 
66
66
  def visit_outline_table(outline_table)
67
+ @table = outline_table
67
68
  outline_table.accept(self)
68
69
  end
69
70
 
@@ -105,7 +106,7 @@ module Cucumber
105
106
  table_cell.accept(self)
106
107
  end
107
108
 
108
- def visit_table_cell_value(value, width, status)
109
+ def visit_table_cell_value(value, status)
109
110
  end
110
111
 
111
112
  def announce(announcement)
@@ -102,7 +102,9 @@ module Cucumber
102
102
  "TAGS must be comma-separated without spaces. Prefix tags with ~ to",
103
103
  "exclude features or scenarios having that tag. Tags can be specified",
104
104
  "with or without the @ prefix.") do |v|
105
- @options[:include_tags], @options[:exclude_tags] = *parse_tags(v)
105
+ include_tags, exclude_tags = *parse_tags(v)
106
+ @options[:include_tags] += include_tags
107
+ @options[:exclude_tags] += exclude_tags
106
108
  end
107
109
  opts.on("-n NAME", "--name NAME",
108
110
  "Only execute the feature elements which match part of the given name.",
@@ -268,7 +270,7 @@ module Cucumber
268
270
  end
269
271
 
270
272
  def files_to_require
271
- requires = @options[:require] || feature_dirs
273
+ requires = @options[:require] || require_dirs
272
274
  files = requires.map do |path|
273
275
  path = path.gsub(/\\/, '/') # In case we're on windows. Globs don't work with backslashes.
274
276
  path = path.gsub(/\/$/, '') # Strip trailing slash.
@@ -310,6 +312,10 @@ module Cucumber
310
312
  @paths.map { |f| File.directory?(f) ? f : File.dirname(f) }.uniq
311
313
  end
312
314
 
315
+ def require_dirs
316
+ feature_dirs+Dir['vendor/{gems,plugins}/*/cucumber']
317
+ end
318
+
313
319
  def constantize(camel_cased_word)
314
320
  begin
315
321
  names = camel_cased_word.split('::')
@@ -1,4 +1,5 @@
1
1
  require 'cucumber/formatter/pretty'
2
+ require 'cucumber/parser/i18n/language'
2
3
 
3
4
  module Cucumber
4
5
  module Cli
@@ -18,15 +19,16 @@ http://wiki.github.com/aslakhellesoy/cucumber/spoken-languages
18
19
  [lang, Cucumber::LANGUAGES[lang]['name'], Cucumber::LANGUAGES[lang]['native']]
19
20
  end
20
21
  table = Ast::Table.new(raw)
21
- new(nil, io, {:check_lang=>true}, '').visit_multiline_arg(table)
22
+ new(nil, io, {:check_lang=>true}).visit_multiline_arg(table)
22
23
  end
23
24
 
24
25
  def self.list_keywords(io, lang)
25
- raw = Cucumber::KEYWORD_KEYS.map do |key|
26
- [key, Cucumber::LANGUAGES[lang][key]]
26
+ language = Parser::I18n::Language[lang]
27
+ raw = Parser::I18n::Language::KEYWORD_KEYS.map do |key|
28
+ [key, language.keywords(key)]
27
29
  end
28
30
  table = Ast::Table.new(raw)
29
- new(nil, io, {:incomplete => Cucumber.language_incomplete?(lang)}, '').visit_multiline_arg(table)
31
+ new(nil, io, {:incomplete => language.incomplete?}).visit_multiline_arg(table)
30
32
  end
31
33
 
32
34
  def visit_multiline_arg(table)
@@ -41,10 +43,10 @@ http://wiki.github.com/aslakhellesoy/cucumber/spoken-languages
41
43
  super
42
44
  end
43
45
 
44
- def visit_table_cell_value(value, width, status)
46
+ def visit_table_cell_value(value, status)
45
47
  if @col == 1
46
48
  if(@options[:check_lang])
47
- @incomplete = Cucumber.language_incomplete?(value)
49
+ @incomplete = Parser::I18n::Language[value].incomplete?
48
50
  end
49
51
  status = :comment
50
52
  elsif @incomplete
@@ -52,7 +54,7 @@ http://wiki.github.com/aslakhellesoy/cucumber/spoken-languages
52
54
  end
53
55
 
54
56
  @col += 1
55
- super(value, width, status)
57
+ super(value, status)
56
58
  end
57
59
  end
58
60
  end
@@ -31,7 +31,13 @@ module Cucumber
31
31
  require 'open-uri'
32
32
  open(@path).read
33
33
  else
34
- File.open(@path, Cucumber.file_mode('r')).read
34
+ begin
35
+ File.open(@path, Cucumber.file_mode('r')).read
36
+ rescue Errno::EACCES => e
37
+ p = File.expand_path(@path)
38
+ e.message << "\nCouldn't open #{p}"
39
+ raise e
40
+ end
35
41
  end
36
42
  end
37
43
 
@@ -32,7 +32,7 @@ module Cucumber
32
32
  end
33
33
 
34
34
  def included_by_tags?(syntax_node)
35
- @include_tags.empty? || syntax_node.has_tags?(@include_tags)
35
+ @include_tags.empty? || syntax_node.has_all_tags?(@include_tags)
36
36
  end
37
37
 
38
38
  def excluded_by_tags?(syntax_node)
@@ -47,4 +47,4 @@ module Cucumber
47
47
  @name_regexps.nil? || @name_regexps.empty? || @name_regexps.detect{|name_regexp| syntax_node.matches_name?(name_regexp)}
48
48
  end
49
49
  end
50
- end
50
+ end
@@ -214,7 +214,7 @@ module Cucumber
214
214
  @outline_row += 1 if @outline_row
215
215
  end
216
216
 
217
- def visit_table_cell_value(value, width, status)
217
+ def visit_table_cell_value(value, status)
218
218
  cell_type = @outline_row == 0 ? :th : :td
219
219
  attributes = {:id => "#{@row_id}_#{@col_index}", :class => 'val'}
220
220
  attributes[:class] += " #{status}" if status
@@ -13,13 +13,13 @@ module Cucumber
13
13
  include Console
14
14
  attr_writer :indent
15
15
 
16
- def initialize(step_mother, io, options, delim='|')
16
+ def initialize(step_mother, io, options)
17
17
  super(step_mother)
18
18
  @io = io
19
19
  @options = options
20
- @delim = delim
21
20
  @exceptions = []
22
21
  @indent = 0
22
+ @prefixes = options[:prefixes] || {}
23
23
  end
24
24
 
25
25
  def visit_features(features)
@@ -145,6 +145,7 @@ module Cucumber
145
145
 
146
146
  def visit_multiline_arg(multiline_arg)
147
147
  return if @options[:no_multiline]
148
+ @table = multiline_arg
148
149
  super
149
150
  end
150
151
 
@@ -154,7 +155,8 @@ module Cucumber
154
155
  end
155
156
 
156
157
  def visit_table_row(table_row)
157
- @io.print @delim.indent(@indent)
158
+ @col_index = 0
159
+ @io.print ' |'.indent(@indent-2)
158
160
  super
159
161
  @io.puts
160
162
  if table_row.exception && !@exceptions.index(table_row.exception)
@@ -169,13 +171,26 @@ module Cucumber
169
171
  @io.flush
170
172
  end
171
173
 
172
- def visit_table_cell_value(value, width, status)
174
+ def visit_table_cell(cell)
175
+ super
176
+ @col_index += 1
177
+ end
178
+
179
+ def visit_table_cell_value(value, status)
173
180
  status ||= @status || :passed
174
- @io.print(' ' + format_string((value.to_s || '').ljust(width), status) + ::Term::ANSIColor.reset(" #{@delim}"))
181
+ width = @table.col_width(@col_index)
182
+ cell_text = value.to_s || ''
183
+ padded = cell_text + (' ' * (width - cell_text.jlength))
184
+ prefix = cell_prefix(status)
185
+ @io.print(' ' + format_string("#{prefix}#{padded}", status) + ::Term::ANSIColor.reset(" |"))
175
186
  @io.flush
176
187
  end
177
188
 
178
189
  private
190
+
191
+ def cell_prefix(status)
192
+ @prefixes[status]
193
+ end
179
194
 
180
195
  def print_summary(features)
181
196
  print_stats(features)
@@ -23,7 +23,7 @@ module Cucumber
23
23
  @status = status
24
24
  end
25
25
 
26
- def visit_table_cell_value(value, width, status)
26
+ def visit_table_cell_value(value, status)
27
27
  status ||= @status
28
28
  progress(status) unless table_header_cell?(status)
29
29
  end
@@ -0,0 +1,49 @@
1
+ module Cucumber
2
+ module Formatter
3
+ class Steps < Ast::Visitor
4
+
5
+ def initialize(step_mother, io, options)
6
+ super(step_mother)
7
+ @io = io
8
+ @options = options
9
+ @step_definition_files = collect_steps(step_mother)
10
+ end
11
+
12
+ def visit_features(features)
13
+ print_summary
14
+ end
15
+
16
+ private
17
+
18
+ def print_summary
19
+ count = 0
20
+ @step_definition_files.keys.sort.each do |step_definition_file|
21
+ @io.puts step_definition_file
22
+
23
+ sources = @step_definition_files[step_definition_file]
24
+ source_indent = source_indent(sources)
25
+ sources.sort.each do |file_colon_line, regexp|
26
+ @io.print "#{regexp}".indent(2)
27
+ @io.print " # #{file_colon_line}".indent(source_indent - regexp.size)
28
+ @io.puts
29
+ end
30
+ @io.puts
31
+ count += sources.size
32
+ end
33
+ @io.puts "#{count} step definition(s) in #{@step_definition_files.size} source file(s)."
34
+ end
35
+
36
+ def collect_steps(step_mother)
37
+ step_mother.step_definitions.inject({}) do |step_definitions, step_definition|
38
+ step_definitions[step_definition.file] ||= []
39
+ step_definitions[step_definition.file] << [ step_definition.file_colon_line, step_definition.regexp.inspect ]
40
+ step_definitions
41
+ end
42
+ end
43
+
44
+ def source_indent(sources)
45
+ sources.map { |file_colon_line, regexp| regexp.size }.max + 1
46
+ end
47
+ end
48
+ end
49
+ end
@@ -55,6 +55,10 @@ module Cucumber
55
55
  def has_tags?(tag_names)
56
56
  tags.has_tags?(tag_names)
57
57
  end
58
+
59
+ def has_all_tags?(tag_names)
60
+ tags.has_all_tags?(tag_names)
61
+ end
58
62
 
59
63
  def build(filter)
60
64
  if(filter.nil? || feature_elements.accept?(filter) || (!bg.empty? && filter.accept?(bg)))
@@ -211,6 +215,10 @@ module Cucumber
211
215
  ts.elements.detect{|e| e.tag.line == line}
212
216
  end
213
217
 
218
+ def has_all_tags?(tags)
219
+ (tags & tag_names) == tags
220
+ end
221
+
214
222
  def has_tags?(tags)
215
223
  (tag_names & tags).any?
216
224
  end
@@ -491,6 +499,11 @@ module Cucumber
491
499
  feature_tags.has_tags?(tag_names)
492
500
  end
493
501
 
502
+ def has_all_tags?(tag_names)
503
+ feature_tags = self.parent.tags
504
+ feature_tags.has_all_tags?(tag_names)
505
+ end
506
+
494
507
  def build
495
508
  Ast::Background.new(
496
509
  comment.build,
@@ -688,6 +701,11 @@ module Cucumber
688
701
  tags.has_tags?(tag_names) || feature_tags.has_tags?(tag_names)
689
702
  end
690
703
 
704
+ def has_all_tags?(tag_names)
705
+ feature_tags = self.parent.parent.tags
706
+ tags.has_all_tags?(tag_names) || feature_tags.has_all_tags?(tag_names)
707
+ end
708
+
691
709
  def matches_name?(regexp_to_match)
692
710
  name.build =~ regexp_to_match
693
711
  end
@@ -826,6 +844,11 @@ module Cucumber
826
844
  tags.has_tags?(tag_names) || feature_tags.has_tags?(tag_names)
827
845
  end
828
846
 
847
+ def has_all_tags?(tag_names)
848
+ feature_tags = self.parent.parent.tags
849
+ tags.has_all_tags?(tag_names) || feature_tags.has_all_tags?(tag_names)
850
+ end
851
+
829
852
  def matches_name?(regexp_to_match)
830
853
  outline_matches_name?(regexp_to_match) || examples_sections.matches_name?(regexp_to_match)
831
854
  end
@@ -1162,6 +1185,10 @@ module Cucumber
1162
1185
  true
1163
1186
  end
1164
1187
 
1188
+ def has_all_tags?(tag_names)
1189
+ true
1190
+ end
1191
+
1165
1192
  def outline_at_line?(line)
1166
1193
  true
1167
1194
  end
@@ -32,34 +32,37 @@ module Cucumber
32
32
 
33
33
  alias_step_definitions(Cucumber::LANGUAGES['en'])
34
34
 
35
- attr_reader :parser
36
-
37
35
  def initialize(lang)
38
36
  @keywords = Cucumber::LANGUAGES[lang]
39
37
  raise "Language not supported: #{lang.inspect}" if @keywords.nil?
40
38
  @keywords['grammar_name'] = @keywords['name'].gsub(/\s/, '')
39
+ end
40
+
41
+ def parser
42
+ return @parser if @parser
41
43
  i18n_tt = File.expand_path(File.dirname(__FILE__) + '/../i18n.tt')
42
44
  template = File.open(i18n_tt, Cucumber.file_mode('r')).read
43
45
  erb = ERB.new(template)
44
46
  grammar = erb.result(binding)
45
47
  Treetop.load_from_string(grammar)
46
- @parser = Parser::I18n.const_get("#{@keywords['grammar_name']}Parser").new
47
48
  self.class.alias_step_definitions(@keywords)
49
+ @parser = Parser::I18n.const_get("#{@keywords['grammar_name']}Parser").new
48
50
  end
49
51
 
50
52
  def parse(source, path, filter)
51
- feature = @parser.parse_or_fail(source, path, filter)
53
+ feature = parser.parse_or_fail(source, path, filter)
52
54
  feature.language = self if feature
53
55
  feature
54
56
  end
55
57
 
56
58
  def keywords(key, raw=false)
57
59
  return @keywords[key] if raw
60
+ return nil unless @keywords[key]
58
61
  values = @keywords[key].split('|')
59
62
  values.map{|value| "'#{value}'"}.join(" / ")
60
63
  end
61
64
 
62
- def language_incomplete?
65
+ def incomplete?
63
66
  KEYWORD_KEYS.detect{|key| @keywords[key].nil?}
64
67
  end
65
68
 
@@ -194,6 +194,7 @@ module Cucumber
194
194
  result = []
195
195
  result += feature_list.to_a if feature_list
196
196
  result += FileList[feature_pattern].to_a if feature_pattern
197
+ result = make_command_line_safe(result)
197
198
  FileList[result]
198
199
  end
199
200
  end
@@ -208,6 +209,11 @@ module Cucumber
208
209
  FileList[result]
209
210
  end
210
211
  end
212
+
213
+ private
214
+ def make_command_line_safe(list)
215
+ list.map{|string| string.gsub(' ', '\ ')}
216
+ end
211
217
  end
212
218
 
213
219
  class FeatureTask < Task
@@ -34,7 +34,7 @@ module Cucumber
34
34
  end
35
35
 
36
36
  class NoStepMatch
37
- attr_reader :step_definition
37
+ attr_reader :step_definition, :name
38
38
 
39
39
  def initialize(step, name)
40
40
  @step = step
@@ -3,7 +3,7 @@ module Cucumber #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 3
5
5
  TINY = 11
6
- PATCH = 6 # Set to nil for official release
6
+ PATCH = 200907091518 # Set to nil for official release
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, PATCH].compact.join('.')
9
9
  end
@@ -23,4 +23,5 @@ end
23
23
 
24
24
  require 'cucumber/rails/rspec'
25
25
  require 'webrat/core/matchers'
26
+ require 'cucumber/webrat/table_locator' # Lets you do table.diff!(table_at('#my_table').to_a)
26
27
  <% end -%>
@@ -25,7 +25,7 @@ Feature: Manage <%= plural_name %>
25
25
  <% end -%>
26
26
  When I delete the 3rd <%= singular_name %>
27
27
  Then I should see the following <%= plural_name %>:
28
- |<%= named_args.map(&:name).join('|') %>|
28
+ |<%= named_args.map{|arg| arg.name.humanize}.join('|') %>|
29
29
  <% [1,2,4].each do |n| -%>
30
30
  |<%= named_args.map{|arg| arg.value(n)}.join('|') %>|
31
31
  <% end -%>
@@ -9,12 +9,6 @@ When /^I delete the (\d+)(?:st|nd|rd|th) <%= singular_name %>$/ do |pos|
9
9
  end
10
10
  end
11
11
 
12
- Then /^I should see the following <%= plural_name %>:$/ do |<%= plural_name %>|
13
- <%= plural_name %>.rows.each_with_index do |row, i|
14
- row.each_with_index do |cell, j|
15
- response.should have_selector("table > tr:nth-child(#{i+2}) > td:nth-child(#{j+1})") { |td|
16
- td.inner_text.should == cell
17
- }
18
- end
19
- end
12
+ Then /^I should see the following <%= plural_name %>:$/ do |expected_<%= plural_name %>_table|
13
+ expected_<%= plural_name %>_table.diff!(table_at('table').to_a)
20
14
  end
@@ -168,6 +168,150 @@ module Cucumber
168
168
 
169
169
  end
170
170
 
171
+ describe "diff!" do
172
+ it "should detect a complex diff" do
173
+ t1 = table(%{
174
+ | 1 | 22 | 333 | 4444 |
175
+ | 55555 | 666666 | 7777777 | 88888888 |
176
+ | 999999999 | 0000000000 | 01010101010 | 121212121212 |
177
+ | 4000 | ABC | DEF | 50000 |
178
+ })
179
+
180
+ t2 = table(%{
181
+ | a | 4444 | 1 |
182
+ | bb | 88888888 | 55555 |
183
+ | ccc | xxxxxxxx | 999999999 |
184
+ | dddd | 4000 | 300 |
185
+ | e | 50000 | 4000 |
186
+ })
187
+ lambda{t1.diff!(t2)}.should raise_error
188
+ t1.to_s(:indent => 12, :color => false).should == %{
189
+ | 1 | (-) 22 | (-) 333 | 4444 | (+) a |
190
+ | 55555 | (-) 666666 | (-) 7777777 | 88888888 | (+) bb |
191
+ | (-) 999999999 | (-) 0000000000 | (-) 01010101010 | (-) 121212121212 | (+) |
192
+ | (+) 999999999 | (+) | (+) | (+) xxxxxxxx | (+) ccc |
193
+ | (+) 300 | (+) | (+) | (+) 4000 | (+) dddd |
194
+ | 4000 | (-) ABC | (-) DEF | 50000 | (+) e |
195
+ }
196
+ end
197
+
198
+ it "should not change table when diffed with identical" do
199
+ t = table(%{
200
+ |a|b|c|
201
+ |d|e|f|
202
+ |g|h|i|
203
+ })
204
+ t.diff!(t.dup)
205
+ t.to_s(:indent => 12, :color => false).should == %{
206
+ | a | b | c |
207
+ | d | e | f |
208
+ | g | h | i |
209
+ }
210
+ end
211
+
212
+ it "should inspect missing and surplus cells" do
213
+ t1 = Table.new([
214
+ ['name', 'male', 'lastname', 'swedish'],
215
+ ['aslak', 'true', 'hellesøy', 'false']
216
+ ])
217
+ t2 = Table.new([
218
+ ['name', 'male', 'lastname', 'swedish'],
219
+ ['aslak', true, 'hellesøy', false]
220
+ ])
221
+ lambda{t1.diff!(t2)}.should raise_error
222
+ t1.to_s(:indent => 12, :color => false).should == %{
223
+ | name | male | lastname | swedish |
224
+ | (-) aslak | (-) (i) "true" | (-) hellesøy | (-) (i) "false" |
225
+ | (+) aslak | (+) (i) true | (+) hellesøy | (+) (i) false |
226
+ }
227
+ end
228
+
229
+ it "should allow column mapping before diffing" do
230
+ t1 = Table.new([
231
+ ['name', 'male'],
232
+ ['aslak', 'true']
233
+ ])
234
+ t1.map_column!('male') { |m| m == 'true' }
235
+ t2 = Table.new([
236
+ ['name', 'male'],
237
+ ['aslak', true]
238
+ ])
239
+ t1.diff!(t2)
240
+ t1.to_s(:indent => 12, :color => false).should == %{
241
+ | name | male |
242
+ | aslak | true |
243
+ }
244
+ end
245
+
246
+ it "should allow header mapping before diffing" do
247
+ t1 = Table.new([
248
+ ['Name', 'Male'],
249
+ ['aslak', 'true']
250
+ ])
251
+ t1.map_headers!('Name' => 'name', 'Male' => 'male')
252
+ t1.map_column!('male') { |m| m == 'true' }
253
+ t2 = Table.new([
254
+ ['name', 'male'],
255
+ ['aslak', true]
256
+ ])
257
+ t1.diff!(t2)
258
+ t1.to_s(:indent => 12, :color => false).should == %{
259
+ | name | male |
260
+ | aslak | true |
261
+ }
262
+ end
263
+
264
+ describe "raising" do
265
+ before do
266
+ @t = table(%{
267
+ | a | b |
268
+ | c | d |
269
+ })
270
+ end
271
+
272
+ it "should raise on missing rows" do
273
+ t = table(%{
274
+ | a | b |
275
+ })
276
+ lambda { @t.dup.diff!(t) }.should raise_error
277
+ lambda { @t.dup.diff!(t, :missing_row => false) }.should_not raise_error
278
+ end
279
+
280
+ it "should raise on surplus rows" do
281
+ t = table(%{
282
+ | a | b |
283
+ | c | d |
284
+ | e | f |
285
+ })
286
+ lambda { @t.dup.diff!(t) }.should raise_error
287
+ lambda { @t.dup.diff!(t, :surplus_row => false) }.should_not raise_error
288
+ end
289
+
290
+ it "should raise on missing columns" do
291
+ t = table(%{
292
+ | a |
293
+ | c |
294
+ })
295
+ lambda { @t.dup.diff!(t) }.should raise_error
296
+ lambda { @t.dup.diff!(t, :missing_col => false) }.should_not raise_error
297
+ end
298
+
299
+ it "should not raise on surplus columns" do
300
+ t = table(%{
301
+ | a | b | x |
302
+ | c | d | y |
303
+ })
304
+ lambda { @t.dup.diff!(t) }.should_not raise_error
305
+ lambda { @t.dup.diff!(t, :surplus_col => true) }.should raise_error
306
+ end
307
+ end
308
+
309
+ def table(text, file=nil, line_offset=0)
310
+ @table_parser ||= Parser::TableParser.new
311
+ @table_parser.parse_or_fail(text.strip, file, line_offset)
312
+ end
313
+ end
314
+
171
315
  it "should convert to sexp" do
172
316
  @table.to_sexp.should ==
173
317
  [:table,