aslakhellesoy-cucumber 0.3.93.1 → 0.3.94

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,7 +39,6 @@ Given /^I am not running (?:.*) in the background$/ do
39
39
  # no-op
40
40
  end
41
41
 
42
-
43
42
  When /^I run cucumber (.*)$/ do |cucumber_opts|
44
43
  run "#{Cucumber::RUBY_BINARY} #{Cucumber::BINARY} --no-color #{cucumber_opts}"
45
44
  end
@@ -75,23 +74,17 @@ Then /^the output should be$/ do |text|
75
74
  last_stdout.should == text
76
75
  end
77
76
 
78
- Then /^"(.*)" should contain XML$/ do |file, xml|
79
- t = Tempfile.new('cucumber-junit')
80
- t.write(xml)
81
- t.flush
82
- t.close
83
- cmd = "diffxml #{t.path} #{file}"
84
- diff = `#{cmd}`
85
- if diff =~ /<delta>/m
86
- raise diff + "\nXML WAS:\n" + IO.read(file)
87
- end
77
+ Then /^"([^\"]*)" should contain$/ do |file, text|
78
+ strip_duration(IO.read(file)).should == text
88
79
  end
89
80
 
90
- Then /^"(.*)" should contain$/ do |file, text|
91
- strip_duration(IO.read(file)).should == text
81
+ Then /^"([^\"]*)" with junit duration "([^\"]*)" should contain$/ do |actual_file, duration_replacement, text|
82
+ actual = IO.read(actual_file)
83
+ actual = replace_junit_duration(actual, duration_replacement)
84
+ actual.should == text
92
85
  end
93
86
 
94
- Then /^"(.*)" should match$/ do |file, text|
87
+ Then /^"([^\"]*)" should match$/ do |file, text|
95
88
  IO.read(file).should =~ Regexp.new(text)
96
89
  end
97
90
 
@@ -119,7 +112,7 @@ Then /^STDERR should be empty$/ do
119
112
  last_stderr.should == ""
120
113
  end
121
114
 
122
- Then /^"(.*)" should exist$/ do |file|
115
+ Then /^"([^\"]*)" should exist$/ do |file|
123
116
  File.exists?(file).should be_true
124
117
  FileUtils.rm(file)
125
118
  end
@@ -50,6 +50,10 @@ class CucumberWorld
50
50
  s.gsub(/\d+m\d+\.\d+s/m, replacement)
51
51
  end
52
52
 
53
+ def replace_junit_duration(s, replacement)
54
+ s.gsub(/\d+\.\d\d+/m, replacement)
55
+ end
56
+
53
57
  def create_file(file_name, file_content)
54
58
  file_content.gsub!("CUCUMBER_LIB", "'#{cucumber_lib_dir}'") # Some files, such as Rakefiles need to use the lib dir
55
59
  in_current_dir do
@@ -118,8 +122,3 @@ end
118
122
  After do
119
123
  terminate_background_jobs
120
124
  end
121
-
122
- Before('@diffxml') do
123
- `diffxml --version`
124
- raise "Please install diffxml from http://diffxml.sourceforge.net/" if $? != 0
125
- end
@@ -44,6 +44,19 @@ module Cucumber
44
44
  "#{@file}:#{line}"
45
45
  end
46
46
 
47
+ def tag_count(tag)
48
+ if @tags.respond_to?(:count)
49
+ @tags.count(tag) # 1.9
50
+ else
51
+ @tags.select{|t| t == tag}.length # 1.8
52
+ end
53
+ end
54
+
55
+ def feature_and_children_tag_count(tag)
56
+ children_tag_count = @feature_elements.inject(0){|count, feature_element| count += feature_element.tag_count(tag)}
57
+ children_tag_count + tag_count(tag)
58
+ end
59
+
47
60
  def short_name
48
61
  first_line = name.split(/\n/)[0]
49
62
  if first_line =~ /#{language.keywords('feature', true)}:(.*)/
@@ -24,7 +24,7 @@ module Cucumber
24
24
  if @name.empty?
25
25
  [@keyword.jlength]
26
26
  else
27
- @name.split("\n").enum_for(:each_with_index).map do |line, line_number|
27
+ @name.split("\n").enum_for(:each_with_index).map do |line, line_number|
28
28
  line_number == 0 ? @keyword.jlength + line.jlength : line.jlength + Ast::Step::INDENT - 1 # We -1 as names which are not keyword lines are missing a space between keyword and name
29
29
  end
30
30
  end
@@ -32,7 +32,7 @@ module Cucumber
32
32
 
33
33
  def matches_scenario_names?(scenario_name_regexps)
34
34
  scenario_name_regexps.detect{|name| name =~ @name}
35
- end
35
+ end
36
36
 
37
37
  def backtrace_line(name = "#{@keyword} #{@name}", line = @line)
38
38
  @feature.backtrace_line(name, line) if @feature
@@ -49,9 +49,13 @@ module Cucumber
49
49
  def accept_hook?(hook)
50
50
  @tags.accept_hook?(hook) || @feature.accept_hook?(hook)
51
51
  end
52
-
52
+
53
+ def tag_count(tag)
54
+ @feature.tag_count(tag) == 0 ? @tags.count(tag) : @feature.tag_count(tag)
55
+ end
56
+
53
57
  def language
54
58
  @feature.language
55
59
  end
56
- end
60
+ end
57
61
  end
@@ -30,6 +30,11 @@ module Cucumber
30
30
  end
31
31
  @duration = Time.now - start
32
32
  end
33
+
34
+ def tag_count(tag)
35
+ @features.inject(0){|count, feature| count += feature.feature_and_children_tag_count(tag)}
36
+ end
37
+
33
38
  end
34
39
  end
35
- end
40
+ end
@@ -126,23 +126,30 @@ module Cucumber
126
126
  [:table, *cells_rows.map{|row| row.to_sexp}]
127
127
  end
128
128
 
129
- # Redefines the table headers. This makes it
130
- # possible to use prettier header names in the features. Example:
129
+ # Redefines the table headers. This makes it possible to use
130
+ # prettier and more flexible header names in the features. The
131
+ # keys of +mappings+ are Strings or regular expressions
132
+ # (anything that responds to #=== will work) that may match
133
+ # column headings in the table. The values of +mappings+ are
134
+ # desired names for the columns.
135
+ #
136
+ # Example:
131
137
  #
132
138
  # | Phone Number | Address |
133
139
  # | 123456 | xyz |
134
140
  # | 345678 | abc |
135
141
  #
136
- # A StepDefinition receiving this table can then map the columns:
142
+ # A StepDefinition receiving this table can then map the columns
143
+ # with both Regexp and String:
137
144
  #
138
- # mapped_table = table.map_columns('Phone Number' => :phone, 'Address' => :address)
139
- # hashes = mapped_table.hashes
145
+ # table.map_headers!(/phone( number)?/i => :phone, 'Address' => :address)
146
+ # table.hashes
140
147
  # # => [{:phone => '123456', :address => 'xyz'}, {:phone => '345678', :address => 'abc'}]
141
148
  #
142
149
  def map_headers!(mappings)
143
150
  header_cells = cell_matrix[0]
144
151
  mappings.each_pair do |pre, post|
145
- header_cell = header_cells.detect{|cell| cell.value == pre}
152
+ header_cell = header_cells.detect{|cell| pre === cell.value}
146
153
  header_cell.value = post
147
154
  if @conversion_procs.has_key?(pre)
148
155
  @conversion_procs[post] = @conversion_procs.delete(pre)
@@ -10,7 +10,7 @@ module Cucumber
10
10
  def self.strip_prefix(tag_name)
11
11
  tag_name =~ /^@(.*)/ ? $1 : tag_name
12
12
  end
13
-
13
+
14
14
  def initialize(line, tag_names)
15
15
  @line, @tag_names = line, tag_names
16
16
  end
@@ -26,6 +26,14 @@ module Cucumber
26
26
  hook.matches_tag_names?(@tag_names)
27
27
  end
28
28
 
29
+ def count(tag)
30
+ if @tag_names.respond_to?(:count)
31
+ @tag_names.count(tag) # 1.9
32
+ else
33
+ @tag_names.select{|t| t == tag}.length # 1.8
34
+ end
35
+ end
36
+
29
37
  def to_sexp
30
38
  @tag_names.map{|tag_name| [:tag, tag_name]}
31
39
  end
@@ -168,7 +168,7 @@ module Cucumber
168
168
  tr("-", "_").
169
169
  downcase
170
170
  end
171
-
171
+
172
172
  end
173
173
 
174
174
  end
@@ -11,6 +11,8 @@ require 'cucumber/cli/drb_client'
11
11
  module Cucumber
12
12
  module Cli
13
13
  class Main
14
+ FAILURE = 1
15
+
14
16
  class << self
15
17
  def step_mother
16
18
  @step_mother
@@ -32,7 +34,7 @@ module Cucumber
32
34
  @out_stream = out_stream == STDOUT ? Formatter::ColorIO.new : out_stream
33
35
  @error_stream = error_stream
34
36
  end
35
-
37
+
36
38
  def execute!(step_mother)
37
39
  trap_interrupt
38
40
  if configuration.drb?
@@ -56,17 +58,32 @@ module Cucumber
56
58
  step_mother.visitor = visitor # Needed to support World#announce
57
59
  visitor.visit_features(features)
58
60
 
59
- failure = if configuration.wip?
60
- step_mother.scenarios(:passed).any?
61
- else
62
- step_mother.scenarios(:failed).any? ||
63
- (configuration.strict? && step_mother.steps(:undefined).any?)
64
- end
61
+ failure = if exceeded_tag_limts?(features)
62
+ FAILURE
63
+ elsif configuration.wip?
64
+ step_mother.scenarios(:passed).any?
65
+ else
66
+ step_mother.scenarios(:failed).any? ||
67
+ (configuration.strict? && step_mother.steps(:undefined).any?)
68
+ end
65
69
  rescue ProfilesNotDefinedError, YmlLoadError, ProfileNotFound => e
66
70
  @error_stream.puts e.message
67
71
  true
68
72
  end
69
73
 
74
+ def exceeded_tag_limts?(features)
75
+ exceeded = false
76
+ configuration.options[:include_tags].each do |tag, limit|
77
+ unless limit.nil?
78
+ tag_count = features.tag_count(tag)
79
+ if tag_count > limit.to_i
80
+ exceeded = true
81
+ end
82
+ end
83
+ end
84
+ exceeded
85
+ end
86
+
70
87
  def load_plain_text_features
71
88
  features = Ast::Features.new
72
89
 
@@ -85,7 +102,7 @@ module Cucumber
85
102
 
86
103
  def configuration
87
104
  return @configuration if @configuration
88
-
105
+
89
106
  @configuration = Configuration.new(@out_stream, @error_stream)
90
107
  @configuration.parse!(@args)
91
108
  @configuration
@@ -43,6 +43,7 @@ module Cucumber
43
43
  @default_profile = options[:default_profile]
44
44
  @skip_profile_information = options[:skip_profile_information]
45
45
  @profiles = []
46
+ @overridden_paths = []
46
47
  @options = default_options
47
48
  end
48
49
 
@@ -66,7 +67,7 @@ module Cucumber
66
67
  previous_flag_was_profile = true
67
68
  next true
68
69
  end
69
- arg == DRB_FLAG
70
+ arg == DRB_FLAG || @overridden_paths.include?(arg)
70
71
  end
71
72
  )
72
73
  end
@@ -122,12 +123,16 @@ module Cucumber
122
123
  end
123
124
  opts.on("-t TAGS", "--tags TAGS",
124
125
  "Only execute the features or scenarios with the specified tags.",
125
- "TAGS must be comma-separated without spaces. Prefix tags with ~ to",
126
- "exclude features or scenarios having that tag. Tags can be specified",
127
- "with or without the @ prefix.") do |v|
126
+ "TAGS must be comma-separated without spaces. They can be",
127
+ "specified with or without the @ prefix. Example: --tags dev\n",
128
+ "Negative tags: Prefix tags with ~ to exclude features or scenarios",
129
+ "having that tag.\n",
130
+ "Limit WIP: Positive tags can be given a threshold to limit the",
131
+ "number of occurrences. Example: --tags qa:3 will fail if there",
132
+ "are more than 3 occurrences of the @qa tag.") do |v|
128
133
  include_tags, exclude_tags = *parse_tags(v)
129
- @options[:include_tags] += include_tags
130
- @options[:exclude_tags] += exclude_tags
134
+ @options[:include_tags].merge!(include_tags)
135
+ @options[:exclude_tags].merge!(exclude_tags)
131
136
  end
132
137
  opts.on("-n NAME", "--name NAME",
133
138
  "Only execute the feature elements which match part of the given name.",
@@ -265,7 +270,16 @@ module Cucumber
265
270
  # Strip @
266
271
  includes = includes.map{|tag| Ast::Tags.strip_prefix(tag)}
267
272
  excludes = excludes.map{|tag| Ast::Tags.strip_prefix(tag)}
268
- [includes, excludes]
273
+ [parse_tag_limits(includes), parse_tag_limits(excludes)]
274
+ end
275
+
276
+ def parse_tag_limits(includes)
277
+ dict = {}
278
+ includes.each do |tag|
279
+ tag, limit = tag.split(':')
280
+ dict[tag] = limit.nil? ? limit : limit.to_i
281
+ end
282
+ dict
269
283
  end
270
284
 
271
285
  def disable_profile_loading?
@@ -302,10 +316,14 @@ module Cucumber
302
316
  def reverse_merge(other_options)
303
317
  @options = other_options.options.merge(@options)
304
318
  @options[:require] += other_options[:require]
305
- @options[:include_tags] += other_options[:include_tags]
306
- @options[:exclude_tags] += other_options[:exclude_tags]
319
+ @options[:include_tags].merge! other_options[:include_tags]
320
+ @options[:exclude_tags].merge! other_options[:exclude_tags]
307
321
  @options[:env_vars] = other_options[:env_vars].merge(@options[:env_vars])
308
- @options[:paths] = other_options[:paths] if @options[:paths].empty?
322
+ if @options[:paths].empty?
323
+ @options[:paths] = other_options[:paths]
324
+ else
325
+ @overridden_paths += (other_options[:paths] - @options[:paths])
326
+ end
309
327
  @options[:source] &= other_options[:source]
310
328
  @options[:snippets] &= other_options[:snippets]
311
329
 
@@ -352,8 +370,8 @@ module Cucumber
352
370
  :dry_run => false,
353
371
  :formats => [],
354
372
  :excludes => [],
355
- :include_tags => [],
356
- :exclude_tags => [],
373
+ :include_tags => {},
374
+ :exclude_tags => {},
357
375
  :name_regexps => [],
358
376
  :env_vars => {},
359
377
  :diff_enabled => true
@@ -2,8 +2,9 @@ module Cucumber
2
2
  class Filter
3
3
  def initialize(lines, options)
4
4
  @lines = lines
5
- @include_tags = options[:include_tags] || []
6
- @exclude_tags = options[:exclude_tags] || []
5
+
6
+ @include_tags = options[:include_tags] ? options[:include_tags].keys : []
7
+ @exclude_tags = options[:exclude_tags] ? options[:exclude_tags].keys : []
7
8
  @name_regexps = options[:name_regexps] || []
8
9
  end
9
10
 
@@ -14,7 +15,7 @@ module Cucumber
14
15
  end
15
16
 
16
17
  def accept_example?(syntax_node, outline)
17
- (at_line?(syntax_node) || outline_at_line?(outline)) &&
18
+ (at_line?(syntax_node) || outline_at_line?(outline)) &&
18
19
  (matches_names?(syntax_node) || outline_matches_names?(outline))
19
20
  end
20
21
 
@@ -60,15 +60,6 @@ module Cucumber
60
60
  module ANSIColor
61
61
  include Term::ANSIColor
62
62
 
63
- # Not supported in Term::ANSIColor
64
- def grey(m)
65
- if ::Term::ANSIColor.coloring?
66
- "\e[90m#{m}\e[0m"
67
- else
68
- m
69
- end
70
- end
71
-
72
63
  ALIASES = Hash.new do |h,k|
73
64
  if k.to_s =~ /(.*)_param/
74
65
  h[$1] + ',bold'
@@ -105,6 +96,48 @@ module Cucumber
105
96
  eval(code)
106
97
  end
107
98
  end
99
+
100
+ def self.define_grey
101
+ begin
102
+ gem 'genki-ruby-terminfo'
103
+ require 'terminfo'
104
+ puts TermInfo.default_object.tigetnum("colors")
105
+ case TermInfo.default_object.tigetnum("colors")
106
+ when 0
107
+ raise "Your terminal doesn't support colours"
108
+ when 1
109
+ ::Term::ANSIColor.coloring = false
110
+ alias grey white
111
+ when 2..8
112
+ alias grey white
113
+ else
114
+ define_real_grey
115
+ end
116
+ rescue Exception => e
117
+ if e.class.name == 'TermInfo::TermInfoError'
118
+ STDERR.puts "*** WARNING ***"
119
+ STDERR.puts "You have the genki-ruby-terminfo gem installed, but you haven't set your TERM variable."
120
+ STDERR.puts "Try setting it to TERM=xterm-256color to get grey colour in output"
121
+ STDERR.puts "\n"
122
+ alias grey white
123
+ else
124
+ define_real_grey
125
+ end
126
+ end
127
+ end
128
+
129
+ def self.define_real_grey
130
+ def grey(m)
131
+ if ::Term::ANSIColor.coloring?
132
+ "\e[90m#{m}\e[0m"
133
+ else
134
+ m
135
+ end
136
+ end
137
+ end
138
+
139
+ define_grey
140
+
108
141
  end
109
142
  end
110
143
  end
@@ -6,7 +6,7 @@ module Cucumber
6
6
  module Console
7
7
  extend ANSIColor
8
8
  include Duration
9
-
9
+
10
10
  FORMATS = Hash.new{|hash, format| hash[format] = method(format).to_proc}
11
11
 
12
12
  def format_step(keyword, step_match, status, source_indent)
@@ -59,9 +59,9 @@ module Cucumber
59
59
  end
60
60
 
61
61
  def print_stats(features)
62
-
62
+
63
63
  @failures = step_mother.scenarios(:failed).select { |s| s.is_a?(Cucumber::Ast::Scenario) }
64
-
64
+
65
65
  if !@failures.empty?
66
66
  @io.puts format_string("Failing Scenarios:", :failed)
67
67
  @failures.each do |failure|
@@ -70,7 +70,7 @@ module Cucumber
70
70
  end
71
71
  @io.puts
72
72
  end
73
-
73
+
74
74
  @io.print dump_count(step_mother.scenarios.length, "scenario")
75
75
  print_status_counts{|status| step_mother.scenarios(status)}
76
76
 
@@ -81,7 +81,7 @@ module Cucumber
81
81
 
82
82
  @io.flush
83
83
  end
84
-
84
+
85
85
  def print_exception(e, status, indent)
86
86
  @io.puts(format_string("#{e.message} (#{e.class})\n#{e.backtrace.join("\n")}".indent(indent), status))
87
87
  end
@@ -114,6 +114,29 @@ module Cucumber
114
114
  end
115
115
  end
116
116
 
117
+ def print_tag_limit_warnings(options)
118
+ return unless tag_limit_breached?(options, @tag_occurences)
119
+ @io.puts
120
+ @io.puts format_string("Failed due to exceeding the tag limit", :failed)
121
+ options[:include_tags].each do |tag_name, limit|
122
+ tag_frequnecy = @tag_occurences[tag_name].size
123
+ if limit && tag_frequnecy > limit
124
+ @io.puts format_string("@#{tag_name} occurred:#{tag_frequnecy} limit:#{limit}", :failed)
125
+ @tag_occurences[tag_name].each {|location| @io.puts format_string(" #{location}", :failed)}
126
+ @io.flush
127
+ end
128
+ end
129
+ end
130
+
131
+ def record_tag_occurrences(feature_element, options)
132
+ @tag_occurences ||= Hash.new{|k,v| k[v] = []}
133
+ options[:include_tags].each do |tag_name, limit|
134
+ if feature_element.tag_count(tag_name) > 0
135
+ @tag_occurences[tag_name] << feature_element.file_colon_line
136
+ end
137
+ end
138
+ end
139
+
117
140
  def announce(announcement)
118
141
  @io.puts
119
142
  @io.puts(format_string(announcement, :tag))
@@ -144,6 +167,15 @@ module Cucumber
144
167
  raise "No format for #{key.inspect}: #{FORMATS.inspect}" if fmt.nil?
145
168
  fmt
146
169
  end
170
+
171
+ def tag_limit_breached?(options, tag_occurences)
172
+ return if tag_occurences.nil?
173
+ tag_limit_breached = false
174
+ options[:include_tags].each do |tag_name, limit|
175
+ tag_limit_breached ||= limit && (tag_occurences[tag_name].size > limit)
176
+ end
177
+ tag_limit_breached
178
+ end
147
179
  end
148
180
  end
149
- end
181
+ end