gurke 2.4.2 → 3.0.0

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.
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