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