aslakhellesoy-cucumber 0.3.93.1 → 0.3.94

Sign up to get free protection for your applications and to get access to all the features.
@@ -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