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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +4 -0
- data/bin/gurke +2 -0
- data/features/gurke.feature +2 -5
- data/features/gurke.rb +2 -1
- data/features/gurke/backtrace_filtering.feature +3 -4
- data/features/gurke/context_in_hooks.feature +1 -2
- data/features/gurke/filter_by_tags.feature +5 -6
- data/features/gurke/include_by_tags.feature +1 -2
- data/features/gurke/other_reporter.feature +3 -4
- data/features/gurke/pending_steps.feature +1 -4
- data/features/gurke/run_specific_directories.feature +4 -5
- data/features/gurke/run_specific_scenarios.feature +5 -6
- data/features/gurke/step_specific_definitions.feature +1 -4
- data/features/support/steps/cli_steps.rb +17 -8
- data/features/support/steps/file_steps.rb +2 -9
- data/gurke.gemspec +10 -9
- data/lib/gurke.rb +3 -0
- data/lib/gurke/background.rb +2 -0
- data/lib/gurke/builder.rb +10 -11
- data/lib/gurke/capybara.rb +2 -0
- data/lib/gurke/cli.rb +35 -27
- data/lib/gurke/configuration.rb +9 -6
- data/lib/gurke/dsl.rb +9 -5
- data/lib/gurke/feature.rb +6 -8
- data/lib/gurke/feature_list.rb +11 -7
- data/lib/gurke/reporter.rb +41 -34
- data/lib/gurke/reporters/compact_reporter.rb +137 -0
- data/lib/gurke/reporters/default_reporter.rb +57 -16
- data/lib/gurke/reporters/null_reporter.rb +4 -2
- data/lib/gurke/reporters/team_city_reporter.rb +11 -13
- data/lib/gurke/rspec.rb +2 -0
- data/lib/gurke/run_list.rb +2 -0
- data/lib/gurke/runner.rb +12 -6
- data/lib/gurke/scenario.rb +41 -2
- data/lib/gurke/step.rb +24 -14
- data/lib/gurke/step_definition.rb +3 -1
- data/lib/gurke/steps.rb +3 -1
- data/lib/gurke/tag.rb +5 -1
- data/lib/gurke/version.rb +5 -3
- data/spec/gurke/feature_list_spec.rb +4 -2
- data/spec/gurke/reporters/default_reporter_spec.rb +201 -0
- data/spec/gurke/run_list_spec.rb +2 -0
- data/spec/gurke/scenario_spec.rb +5 -3
- data/spec/gurke/step_definition_spec.rb +3 -1
- data/spec/spec_helper.rb +10 -1
- metadata +8 -6
data/lib/gurke/builder.rb
CHANGED
@@ -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
|
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
|
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
|
data/lib/gurke/capybara.rb
CHANGED
data/lib/gurke/cli.rb
CHANGED
@@ -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
|
-
|
18
|
-
|
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].
|
28
|
-
|
29
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
49
|
+
$stdout.puts <<~VSTR
|
46
50
|
gurke v#{Gurke::VERSION}
|
47
|
-
|
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
|
-
|
64
|
+
default: 'compact'
|
62
65
|
opt :pattern, 'File pattern matching feature files to be run.',
|
63
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
72
|
-
|
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)',
|
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.
|
86
|
+
files.each_with_object([]) do |input, memo|
|
82
87
|
if File.directory? input
|
83
88
|
Dir[input + '/**/*'].each do |file_in_dir|
|
84
|
-
|
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
|
data/lib/gurke/configuration.rb
CHANGED
@@ -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
|
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
|
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
|
-
|
177
|
+
opts.none? {|k, v| context.metadata[k] != v }
|
175
178
|
end
|
176
179
|
|
177
180
|
def run(world, *args)
|
data/lib/gurke/dsl.rb
CHANGED
@@ -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
|
-
|
7
|
+
opts = method_name
|
8
|
+
method_name = nil
|
6
9
|
end
|
7
10
|
|
8
11
|
if method_name && block_given?
|
9
|
-
raise ArgumentError.new
|
12
|
+
raise ArgumentError.new <<~ERR.strip
|
10
13
|
You can either specify a method name or given a block, not both.
|
11
|
-
|
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(
|
28
|
+
define_method(step.method_name.to_s, &block)
|
26
29
|
elsif method_name
|
27
|
-
alias_method
|
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
|
data/lib/gurke/feature.rb
CHANGED
@@ -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
|
data/lib/gurke/feature_list.rb
CHANGED
@@ -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?
|
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,
|
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
|
-
|
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
|
data/lib/gurke/reporter.rb
CHANGED
@@ -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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
-
|
266
|
+
|
267
|
+
def format_exception(ex, backtrace: true)
|
264
268
|
s = [ex.class.to_s + ': ' + ex.message.strip]
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
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
|
-
|
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
|