gurke 2.4.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +4 -0
  3. data/bin/gurke +2 -0
  4. data/features/gurke.feature +2 -5
  5. data/features/gurke.rb +2 -1
  6. data/features/gurke/backtrace_filtering.feature +3 -4
  7. data/features/gurke/context_in_hooks.feature +1 -2
  8. data/features/gurke/filter_by_tags.feature +5 -6
  9. data/features/gurke/include_by_tags.feature +1 -2
  10. data/features/gurke/other_reporter.feature +3 -4
  11. data/features/gurke/pending_steps.feature +1 -4
  12. data/features/gurke/run_specific_directories.feature +4 -5
  13. data/features/gurke/run_specific_scenarios.feature +5 -6
  14. data/features/gurke/step_specific_definitions.feature +1 -4
  15. data/features/support/steps/cli_steps.rb +17 -8
  16. data/features/support/steps/file_steps.rb +2 -9
  17. data/gurke.gemspec +10 -9
  18. data/lib/gurke.rb +3 -0
  19. data/lib/gurke/background.rb +2 -0
  20. data/lib/gurke/builder.rb +10 -11
  21. data/lib/gurke/capybara.rb +2 -0
  22. data/lib/gurke/cli.rb +35 -27
  23. data/lib/gurke/configuration.rb +9 -6
  24. data/lib/gurke/dsl.rb +9 -5
  25. data/lib/gurke/feature.rb +6 -8
  26. data/lib/gurke/feature_list.rb +11 -7
  27. data/lib/gurke/reporter.rb +41 -34
  28. data/lib/gurke/reporters/compact_reporter.rb +137 -0
  29. data/lib/gurke/reporters/default_reporter.rb +57 -16
  30. data/lib/gurke/reporters/null_reporter.rb +4 -2
  31. data/lib/gurke/reporters/team_city_reporter.rb +11 -13
  32. data/lib/gurke/rspec.rb +2 -0
  33. data/lib/gurke/run_list.rb +2 -0
  34. data/lib/gurke/runner.rb +12 -6
  35. data/lib/gurke/scenario.rb +41 -2
  36. data/lib/gurke/step.rb +24 -14
  37. data/lib/gurke/step_definition.rb +3 -1
  38. data/lib/gurke/steps.rb +3 -1
  39. data/lib/gurke/tag.rb +5 -1
  40. data/lib/gurke/version.rb +5 -3
  41. data/spec/gurke/feature_list_spec.rb +4 -2
  42. data/spec/gurke/reporters/default_reporter_spec.rb +201 -0
  43. data/spec/gurke/run_list_spec.rb +2 -0
  44. data/spec/gurke/scenario_spec.rb +5 -3
  45. data/spec/gurke/step_definition_spec.rb +3 -1
  46. data/spec/spec_helper.rb +10 -1
  47. metadata +8 -6
@@ -1,18 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'gherkin'
2
4
 
3
5
  module Gurke
4
6
  class Builder
5
- attr_reader :features, :keywords
7
+ attr_reader :features
8
+ attr_writer :keywords
6
9
 
7
10
  def initialize
8
- # @options = options
9
- # @files = files.map do |file|
10
- # split = file.split(':')
11
- # [split[0], split[1..-1].map{|l| Integer(l) }]
12
- # end
13
11
  @language = 'en'
14
12
  @parser = Gherkin::Parser::Parser.new(
15
- self, true, 'root', false, @language)
13
+ self, true, 'root', false, @language
14
+ )
16
15
  end
17
16
 
18
17
  def keywords
@@ -45,7 +44,7 @@ module Gurke
45
44
  end
46
45
 
47
46
  def feature(raw)
48
- tags = raw.tags.map{|t| Tag.new @file, t.line, t }
47
+ tags = raw.tags.map {|t| Tag.new @file, t.line, t }
49
48
 
50
49
  @feature = Feature.new(@file, raw.line, tags, raw)
51
50
  @scenario = nil
@@ -62,14 +61,14 @@ module Gurke
62
61
  end
63
62
 
64
63
  def scenario(raw)
65
- tags = raw.tags.map{|t| Tag.new @file, t.line, t }
64
+ tags = raw.tags.map {|t| Tag.new @file, t.line, t }
66
65
  tags += features.last.tags
67
66
 
68
67
  @scenario = Scenario.new @feature, @file, raw.line, tags, raw
69
68
  @context = @scenario
70
69
  @type = nil
71
70
 
72
- @feature.scenarios << @scenario #unless filtered?(@scenario)
71
+ @feature.scenarios << @scenario
73
72
  end
74
73
 
75
74
  def step(raw)
@@ -79,7 +78,7 @@ module Gurke
79
78
  end
80
79
 
81
80
  def eof(*)
82
- @features.reject!{|f| f.scenarios.empty? }
81
+ @features.reject! {|f| f.scenarios.empty? }
83
82
  @feature = nil
84
83
  @scenario = nil
85
84
  @context = nil
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'capybara'
2
4
  require 'gurke'
3
5
  require 'capybara/dsl'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'trollop'
2
4
 
3
5
  module Gurke
@@ -14,8 +16,8 @@ module Gurke
14
16
  rescue Trollop::HelpNeeded
15
17
  print_help && exit
16
18
  rescue Trollop::CommandlineError => e
17
- $stderr.puts "Error: #{e}"
18
- $stderr.puts "Run with `-h' for more information on available arguments."
19
+ warn "Error: #{e}"
20
+ warn "Run with `-h' for more information on available arguments."
19
21
  exit 255
20
22
  end
21
23
 
@@ -24,70 +26,76 @@ module Gurke
24
26
  require File.expand_path(Gurke.root.join('gurke.rb'))
25
27
  end
26
28
 
27
- options[:require].each do |r|
28
- Dir[r].each{|f| require File.expand_path(f) }
29
- end if options[:require].any?
29
+ if options[:require].any?
30
+ options[:require].each do |r|
31
+ Dir[r].each {|f| require File.expand_path(f) }
32
+ end
33
+ end
30
34
 
31
35
  files = expand_files files, options
32
36
 
33
37
  runner = if options[:drb_server]
34
- Runner::DRbServer
35
- elsif options[:drb]
36
- Runner::DRbClient
37
- else
38
- Runner::LocalRunner
39
- end.new Gurke.config, options
38
+ Runner::DRbServer
39
+ elsif options[:drb]
40
+ Runner::DRbClient
41
+ else
42
+ Runner::LocalRunner
43
+ end.new(Gurke.config, options)
40
44
 
41
45
  Kernel.exit runner.run files
42
46
  end
43
47
 
44
48
  def print_version
45
- $stdout.puts <<-EOF.gsub(/^ {8}/, '')
49
+ $stdout.puts <<~VSTR
46
50
  gurke v#{Gurke::VERSION}
47
- EOF
51
+ VSTR
48
52
  end
49
53
 
50
54
  def print_help
51
55
  parser.educate($stdout)
52
56
  end
53
57
 
54
- # rubocop:disable MethodLength
55
58
  def parser
56
59
  @parser ||= Trollop::Parser.new do
57
60
  opt :help, 'Print this help.'
58
61
  opt :version, 'Show program version information.'
59
62
  opt :backtrace, 'Show full error backtraces.'
60
63
  opt :formatter, 'Select a special formatter as reporter', \
61
- default: 'default'
64
+ default: 'compact'
62
65
  opt :pattern, 'File pattern matching feature files to be run.',
63
- default: 'features/**/*.feature'
64
- opt :require, 'Files matching this pattern will be required after'\
66
+ default: 'features/**/*.feature'
67
+ opt :require, 'Files matching this pattern will be required after' \
65
68
  'loading environment but before running features.',
66
- default: ['features/steps/**/*.rb',
67
- 'features/support/steps/**/*.rb'],
68
- multi: true
69
- opt :tags, 'Only run features and scenarios matching given tag '\
69
+ default: ['features/steps/**/*.rb',
70
+ 'features/support/steps/**/*.rb'],
71
+ multi: true
72
+ opt :tags, 'Only run features and scenarios matching given tag ' \
70
73
  'filtering expression. TODO: Description.',
71
- default: ['~wip'],
72
- multi: true
74
+ default: ['~wip'],
75
+ multi: true
73
76
  opt :drb_server, 'Run gurke DRb server. (experimental)', short: :none
74
- opt :drb, 'Run features on already started DRb server. (experimental)', short: :none
77
+ opt :drb, 'Run features on already started DRb server. (experimental)',
78
+ short: :none
75
79
  end
76
80
  end
77
81
 
78
82
  private
83
+
79
84
  def expand_files(files, options)
80
85
  files = Dir[options[:pattern].to_s] if files.empty? && options[:pattern]
81
- files.inject([]) do |memo, input|
86
+ files.each_with_object([]) do |input, memo|
82
87
  if File.directory? input
83
88
  Dir[input + '/**/*'].each do |file_in_dir|
84
- next if options[:pattern] && !File.fnmatch?(options[:pattern], file_in_dir)
89
+ if options[:pattern] &&
90
+ !File.fnmatch?(options[:pattern], file_in_dir)
91
+ next
92
+ end
93
+
85
94
  memo << File.expand_path(file_in_dir)
86
95
  end
87
96
  else
88
97
  memo << File.expand_path(input)
89
98
  end
90
- memo
91
99
  end
92
100
  end
93
101
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
2
4
 
3
5
  module Gurke
@@ -74,7 +76,7 @@ module Gurke
74
76
  system: HookSet.new
75
77
  }
76
78
 
77
- hooks.merge! each: hooks[:scenario]
79
+ hooks[:each] = hooks[:scenario]
78
80
  hooks
79
81
  end
80
82
  end
@@ -126,15 +128,15 @@ module Gurke
126
128
 
127
129
  def run(context, world, &block)
128
130
  ctx = Context.new context, block
129
- @before.each{|hook| hook.run world, ctx }
131
+ @before.each {|hook| hook.run world, ctx }
130
132
  @around.reduce Context.new(context, block) do |c, e|
131
- Context.new(context, ->{ e.run world, c })
133
+ Context.new(context, -> { e.run world, c })
132
134
  end.call
133
135
  ensure
134
136
  @after.each do |hook|
135
137
  begin
136
138
  hook.run world, ctx
137
- rescue => e
139
+ rescue StandardError => e
138
140
  warn "Rescued error in after hook: #{e}\n#{e.backtrace.join("\n")}"
139
141
  end
140
142
  end
@@ -144,7 +146,8 @@ module Gurke
144
146
  extend Forwardable
145
147
 
146
148
  def initialize(context, block)
147
- @context, @block = context, block
149
+ @context = context
150
+ @block = block
148
151
  end
149
152
 
150
153
  def tags
@@ -171,7 +174,7 @@ module Gurke
171
174
  end
172
175
 
173
176
  def match?(context)
174
- !opts.any?{|k, v| context.metadata[k] != v }
177
+ opts.none? {|k, v| context.metadata[k] != v }
175
178
  end
176
179
 
177
180
  def run(world, *args)
@@ -1,14 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Gurke
2
4
  module DSL
3
5
  def step(pattern, method_name = nil, opts = {}, &block)
4
6
  if method_name.is_a?(Hash) && opts.empty?
5
- method_name, opts = nil, method_name
7
+ opts = method_name
8
+ method_name = nil
6
9
  end
7
10
 
8
11
  if method_name && block_given?
9
- raise ArgumentError.new <<-EOF.strip
12
+ raise ArgumentError.new <<~ERR.strip
10
13
  You can either specify a method name or given a block, not both.
11
- EOF
14
+ ERR
12
15
  end
13
16
 
14
17
  _define_step(pattern, method_name, opts, &block)
@@ -22,9 +25,9 @@ module Gurke
22
25
  end
23
26
 
24
27
  if block_given?
25
- define_method("#{step.method_name}", &block)
28
+ define_method(step.method_name.to_s, &block)
26
29
  elsif method_name
27
- alias_method "#{step.method_name}", method_name
30
+ alias_method step.method_name.to_s, method_name
28
31
  end
29
32
  end
30
33
 
@@ -40,5 +43,6 @@ module Gurke
40
43
  def Then(pattern, method_name = nil, opts = {}, &block)
41
44
  step pattern, method_name, opts.merge(type: :then), &block
42
45
  end
46
+ # rubocop:enable MethodName
43
47
  end
44
48
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Gurke
2
4
  class Feature
3
5
  #
@@ -41,14 +43,6 @@ module Gurke
41
43
  @raw = raw
42
44
  end
43
45
 
44
- def name
45
- raw.name
46
- end
47
-
48
- def description
49
- raw.description
50
- end
51
-
52
46
  # Return name of this feature.
53
47
  #
54
48
  # @return [String] Feature name.
@@ -57,6 +51,10 @@ module Gurke
57
51
  raw.name
58
52
  end
59
53
 
54
+ def description
55
+ raw.description
56
+ end
57
+
60
58
  def failed?
61
59
  scenarios.any?(&:failed?)
62
60
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Gurke
2
4
  #
3
5
  # A {FeatureList} is a list of {Feature} objects.
@@ -19,7 +21,7 @@ module Gurke
19
21
 
20
22
  reporter.invoke :after_features, self
21
23
 
22
- !any?{|s| s.failed? }
24
+ !any?(&:failed?)
23
25
  end
24
26
 
25
27
  # @api private
@@ -28,7 +30,7 @@ module Gurke
28
30
  filter = Filter.new options, files
29
31
 
30
32
  each do |feature|
31
- file, lines = files.select{|f, _| f == feature.file }.first
33
+ file, _lines = files.select {|f, _| f == feature.file }.first
32
34
  next unless file
33
35
 
34
36
  f = Feature.new(feature)
@@ -51,6 +53,8 @@ module Gurke
51
53
  each do |feature|
52
54
  feature.run runner, reporter
53
55
  end
56
+ rescue Interrupt # rubocop:disable HandleExceptions
57
+ # nothing
54
58
  ensure
55
59
  reporter.invoke :end_features, self
56
60
  end
@@ -65,7 +69,7 @@ module Gurke
65
69
 
66
70
  def tag_filters
67
71
  @tag_filters ||= options[:tags].map do |list|
68
- list.strip.split(/[,+\s]\s*/).map{|t| TagFilter.new(t) }
72
+ list.strip.split(/[,+\s]\s*/).map {|t| TagFilter.new(t) }
69
73
  end
70
74
  end
71
75
 
@@ -75,16 +79,16 @@ module Gurke
75
79
 
76
80
  def filtered_by_tags?(scenario)
77
81
  !tag_filters.reduce(false) do |memo, set|
78
- memo || set.all?{|rule| rule.match? scenario }
82
+ memo || set.all? {|rule| rule.match? scenario }
79
83
  end
80
84
  end
81
85
 
82
86
  def filtered_by_line?(scenario)
83
- _, lines = files.select{|f, _| f == scenario.file }.first
87
+ _, lines = files.select {|f, _| f == scenario.file }.first
84
88
 
85
89
  return false if lines.empty?
86
90
 
87
- !lines.any?{|l| scenario.line <= l && scenario.steps.last.line >= l }
91
+ lines.none? {|l| scenario.line <= l && scenario.steps.last.line >= l }
88
92
  end
89
93
 
90
94
  TagFilter = Struct.new(:tag) do
@@ -97,7 +101,7 @@ module Gurke
97
101
  end
98
102
 
99
103
  def match?(taggable)
100
- negated? != taggable.tags.any?{|t| t.name == name }
104
+ negated? != taggable.tags.any? {|t| t.name == name }
101
105
  end
102
106
  end
103
107
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Gurke
2
4
  #
3
5
  # A {Reporter} provides callbacks that will be executed whenever
@@ -5,31 +7,32 @@ module Gurke
5
7
  #
6
8
  # @api public
7
9
  #
10
+ # rubocop:disable MissingCopEnableDirective
11
+ # rubocop:disable UnusedMethodArgument
12
+ #
8
13
  class Reporter
9
- # rubocop:disable UnusedMethodArgument
10
-
11
14
  # List of all callback methods as symbols.
12
15
  #
13
- CALLBACKS = [
14
- :before_features,
15
- :before_feature,
16
- :before_scenario,
17
- :before_step,
18
- :start_features,
19
- :start_feature,
20
- :start_scenario,
21
- :start_background,
22
- :start_step,
23
- :end_features,
24
- :end_feature,
25
- :end_scenario,
26
- :end_background,
27
- :end_step,
28
- :after_features,
29
- :after_feature,
30
- :after_scenario,
31
- :after_step
32
- ]
16
+ CALLBACKS = %i[
17
+ before_features
18
+ before_feature
19
+ before_scenario
20
+ before_step
21
+ start_features
22
+ start_feature
23
+ start_scenario
24
+ start_background
25
+ start_step
26
+ end_features
27
+ end_feature
28
+ end_scenario
29
+ end_background
30
+ end_step
31
+ after_features
32
+ after_feature
33
+ after_scenario
34
+ after_step
35
+ ].freeze
33
36
 
34
37
  # Called before the execution of any feature and before any
35
38
  # before-features hook is invoked.
@@ -252,30 +255,34 @@ module Gurke
252
255
  # @visibility private
253
256
  def invoke(mth, *args)
254
257
  send mth, *args
255
- rescue => e
258
+ rescue StandardError => e
256
259
  warn "Rescued in reporter: #{e}\n" + e.backtrace.join("\n")
257
260
  end
258
261
 
259
-
260
262
  # @api private
261
263
  #
264
+
262
265
  protected
263
- def format_exception(ex)
266
+
267
+ def format_exception(ex, backtrace: true)
264
268
  s = [ex.class.to_s + ': ' + ex.message.strip]
265
- if ex.backtrace.nil?
266
- s << ' <no backtrace available>'
267
- elsif ex.backtrace.empty?
268
- s << ' <backtrace empty>'
269
- else
270
- ex.backtrace.each do |bt|
271
- s << ' ' + bt.strip
269
+
270
+ if backtrace
271
+ if ex.backtrace.nil?
272
+ s << ' <no backtrace available>'
273
+ elsif ex.backtrace.empty?
274
+ s << ' <backtrace empty>'
275
+ else
276
+ ex.backtrace.each do |bt|
277
+ s << ' ' + bt.strip
278
+ end
272
279
  end
273
280
  end
274
281
 
275
282
  if ex.respond_to?(:cause) && ex.cause &&
276
- ex.cause.respond_to?(:message) && ex.cause.respond_to?(:backtrace)
283
+ ex.cause.respond_to?(:message) && ex.cause.respond_to?(:backtrace)
277
284
 
278
- cause = format_exception(ex.cause)
285
+ cause = format_exception(ex.cause, backtrace: backtrace)
279
286
  s << 'caused by: ' + cause.shift
280
287
  s += cause
281
288
  end