gurke 1.0.1 → 2.0.0.dev.1.b17
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 +13 -5
- data/README.md +85 -38
- data/bin/gurke +5 -0
- data/features/gurke.feature +47 -0
- data/features/gurke.rb +12 -0
- data/features/gurke/backtrace_filtering.feature +36 -0
- data/features/gurke/filter_by_tags.feature +70 -0
- data/features/gurke/step_specific_definitions.feature +41 -0
- data/features/support/steps/cli_steps.rb +50 -0
- data/features/support/steps/file_steps.rb +32 -0
- data/gurke.gemspec +11 -7
- data/lib/gurke.rb +43 -16
- data/lib/gurke/background.rb +33 -0
- data/lib/gurke/builder.rb +107 -0
- data/lib/gurke/capybara.rb +28 -0
- data/lib/gurke/cli.rb +67 -0
- data/lib/gurke/configuration.rb +118 -0
- data/lib/gurke/dsl.rb +45 -0
- data/lib/gurke/feature.rb +55 -0
- data/lib/gurke/reporter.rb +98 -0
- data/lib/gurke/rspec.rb +5 -0
- data/lib/gurke/runner.rb +156 -0
- data/lib/gurke/scenario.rb +84 -0
- data/lib/gurke/step.rb +41 -0
- data/lib/gurke/step_definition.rb +31 -0
- data/lib/gurke/steps.rb +36 -0
- data/lib/gurke/tag.rb +41 -0
- data/lib/gurke/version.rb +14 -0
- metadata +59 -36
- data/.gitignore +0 -19
- data/Gemfile +0 -4
- data/Rakefile +0 -1
- data/lib/gurke/current.rb +0 -39
- data/lib/gurke/formatter.rb +0 -180
- data/lib/gurke/formatters/base.rb +0 -21
- data/lib/gurke/formatters/headless.rb +0 -108
- data/lib/gurke/hooks.rb +0 -42
- data/lib/gurke/patch/cucumber_cli_configuration.rb +0 -16
data/lib/gurke/dsl.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
module Gurke
|
2
|
+
#
|
3
|
+
module DSL
|
4
|
+
def step(pattern, method_name = nil, opts = {}, &block)
|
5
|
+
if method_name.is_a?(Hash) && opts.empty?
|
6
|
+
method_name, opts = nil, method_name
|
7
|
+
end
|
8
|
+
|
9
|
+
if method_name && block_given?
|
10
|
+
raise ArgumentError.new <<-EOF.strip
|
11
|
+
You can either specify a method name or given a block, not both.
|
12
|
+
EOF
|
13
|
+
end
|
14
|
+
|
15
|
+
_define_step(pattern, method_name, opts, &block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def _define_step(pattern, method_name, opts, &block)
|
19
|
+
step = StepDefinition.new(pattern, opts)
|
20
|
+
|
21
|
+
define_method("match: #{step.method_name}") do |name, s = nil|
|
22
|
+
step.match(name, s)
|
23
|
+
end
|
24
|
+
|
25
|
+
if block_given?
|
26
|
+
define_method("#{step.method_name}", &block)
|
27
|
+
elsif method_name
|
28
|
+
alias_method "#{step.method_name}", method_name
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# rubocop:disable MethodName
|
33
|
+
def Given(pattern, method_name = nil, opts = {}, &block)
|
34
|
+
step pattern, method_name, opts.merge(type: :given), &block
|
35
|
+
end
|
36
|
+
|
37
|
+
def When(pattern, method_name = nil, opts = {}, &block)
|
38
|
+
step pattern, method_name, opts.merge(type: :when), &block
|
39
|
+
end
|
40
|
+
|
41
|
+
def Then(pattern, method_name = nil, opts = {}, &block)
|
42
|
+
step pattern, method_name, opts.merge(type: :then), &block
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Gurke
|
2
|
+
#
|
3
|
+
class Feature
|
4
|
+
#
|
5
|
+
# Return path to file containing this feature.
|
6
|
+
#
|
7
|
+
# @return [String] File path.
|
8
|
+
#
|
9
|
+
attr_reader :file
|
10
|
+
|
11
|
+
# Return line number where this feature is defined.
|
12
|
+
#
|
13
|
+
# @return [Fixnum] Line number.
|
14
|
+
#
|
15
|
+
attr_reader :line
|
16
|
+
|
17
|
+
attr_reader :tags
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
attr_reader :raw
|
21
|
+
|
22
|
+
# @api private
|
23
|
+
def initialize(file, line, tags, raw)
|
24
|
+
@file, @line, @tags, @raw = file, line, tags, raw
|
25
|
+
end
|
26
|
+
|
27
|
+
def name
|
28
|
+
raw.name
|
29
|
+
end
|
30
|
+
|
31
|
+
def description
|
32
|
+
raw.description
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return list of scenarios this feature specifies.
|
36
|
+
#
|
37
|
+
# @return [Array<Scenario>] Scenarios.
|
38
|
+
#
|
39
|
+
def scenarios
|
40
|
+
@scenarios ||= []
|
41
|
+
end
|
42
|
+
|
43
|
+
def backgrounds
|
44
|
+
@backgrounds ||= []
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return name of this feature.
|
48
|
+
#
|
49
|
+
# @return [String] Feature name.
|
50
|
+
#
|
51
|
+
def name
|
52
|
+
raw.name
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
|
3
|
+
# Colorize colors:
|
4
|
+
# :black, :red, :green, :yellow, :blue,
|
5
|
+
# :magenta, :cyan, :white, :default, :light_black,
|
6
|
+
# :light_red, :light_green, :light_yellow, :light_blue,
|
7
|
+
# :light_magenta, :light_cyan, :light_white
|
8
|
+
|
9
|
+
module Gurke
|
10
|
+
#
|
11
|
+
class Reporter
|
12
|
+
def start_features(*)
|
13
|
+
end
|
14
|
+
|
15
|
+
def start_feature(feature)
|
16
|
+
io.puts "#{yellow('Feature')}: #{feature.name}"
|
17
|
+
io.puts ' ' + light_black(feature.description.split("\n").join("\n "))
|
18
|
+
io.puts
|
19
|
+
end
|
20
|
+
|
21
|
+
def start_scenario(scenario, feature)
|
22
|
+
io.puts " #{yellow('Scenario')}: #{scenario.name}"
|
23
|
+
io.puts light_black(' Background:') if feature.backgrounds.any?
|
24
|
+
end
|
25
|
+
|
26
|
+
def start_background(*)
|
27
|
+
@background = true
|
28
|
+
end
|
29
|
+
|
30
|
+
def finish_background(*)
|
31
|
+
@background = false
|
32
|
+
end
|
33
|
+
|
34
|
+
def start_step(step, *)
|
35
|
+
io.print ' ' if @background
|
36
|
+
io.print ' '
|
37
|
+
io.print yellow(step.keyword)
|
38
|
+
io.print step.name.gsub(/"(.*?)"/, cyan('\0'))
|
39
|
+
end
|
40
|
+
|
41
|
+
def finish_step(step, *)
|
42
|
+
case step.state
|
43
|
+
when :pending
|
44
|
+
print_braces yellow('pending')
|
45
|
+
when :failed
|
46
|
+
print_braces red('failure')
|
47
|
+
io.puts
|
48
|
+
io.puts red(" #{step.exception.class}:")
|
49
|
+
|
50
|
+
msg = step.exception.message.split("\n").join("\n ")
|
51
|
+
io.puts red(" #{msg}")
|
52
|
+
|
53
|
+
io.puts red(" #{step.exception.backtrace.join("\n ")}")
|
54
|
+
when :success
|
55
|
+
print_braces green('success')
|
56
|
+
else
|
57
|
+
print_braces cyan('skipped')
|
58
|
+
end
|
59
|
+
io.puts
|
60
|
+
io.flush
|
61
|
+
end
|
62
|
+
|
63
|
+
def finish_scenario(*)
|
64
|
+
io.puts
|
65
|
+
end
|
66
|
+
|
67
|
+
def finish_feature(*)
|
68
|
+
io.puts
|
69
|
+
end
|
70
|
+
|
71
|
+
def finish_features(features)
|
72
|
+
scenarios = features.map(&:scenarios).flatten
|
73
|
+
|
74
|
+
io.puts " #{scenarios.size} scenarios: "\
|
75
|
+
"#{scenarios.select(&:failed?).size} failing, "\
|
76
|
+
"#{scenarios.select(&:pending?).size} pending"
|
77
|
+
io.puts
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def print_braces(str)
|
83
|
+
io.print " (#{str})"
|
84
|
+
end
|
85
|
+
|
86
|
+
def io
|
87
|
+
$stdout
|
88
|
+
end
|
89
|
+
|
90
|
+
[:black, :red, :green, :yellow, :blue,
|
91
|
+
:magenta, :cyan, :white, :default, :light_black,
|
92
|
+
:light_red, :light_green, :light_yellow, :light_blue,
|
93
|
+
:light_magenta, :light_cyan, :light_white].each do |color|
|
94
|
+
|
95
|
+
define_method(color){|str| io.tty? ? str.send(color) : str }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/lib/gurke/rspec.rb
ADDED
data/lib/gurke/runner.rb
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
module Gurke
|
2
|
+
#
|
3
|
+
class Runner
|
4
|
+
attr_reader :builder
|
5
|
+
attr_reader :files
|
6
|
+
attr_reader :options
|
7
|
+
|
8
|
+
def initialize(files, options = {})
|
9
|
+
@options = options
|
10
|
+
@files = files
|
11
|
+
@builder = Builder.new options
|
12
|
+
end
|
13
|
+
|
14
|
+
def reporter
|
15
|
+
@reporter ||= Reporter.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
files.each{|f| builder.parse(f) }
|
20
|
+
|
21
|
+
with_hooks(:features, nil, nil) do
|
22
|
+
run_features builder.features
|
23
|
+
end
|
24
|
+
|
25
|
+
!builder.features
|
26
|
+
.map(&:scenarios)
|
27
|
+
.flatten
|
28
|
+
.any?{|s| s.failed? || s.pending? }
|
29
|
+
end
|
30
|
+
|
31
|
+
def run_features(features)
|
32
|
+
reporter.start_features(features)
|
33
|
+
|
34
|
+
features.each do |feature|
|
35
|
+
run_feature(feature)
|
36
|
+
end
|
37
|
+
|
38
|
+
reporter.finish_features(features)
|
39
|
+
end
|
40
|
+
|
41
|
+
def run_feature(feature)
|
42
|
+
reporter.start_feature(feature)
|
43
|
+
|
44
|
+
feature.scenarios.each do |scenario|
|
45
|
+
run_scenario(scenario, feature)
|
46
|
+
end
|
47
|
+
|
48
|
+
reporter.finish_feature(feature)
|
49
|
+
end
|
50
|
+
|
51
|
+
def run_scenario(scenario, feature)
|
52
|
+
reporter.start_scenario(scenario, feature)
|
53
|
+
|
54
|
+
world = world_for(scenario, feature)
|
55
|
+
|
56
|
+
with_hooks(:scenario, scenario, world) do
|
57
|
+
feature.backgrounds.each do |b|
|
58
|
+
run_background(b, scenario, feature, world)
|
59
|
+
end
|
60
|
+
scenario.steps.each{|s| run_step(s, scenario, feature, world) }
|
61
|
+
end
|
62
|
+
|
63
|
+
reporter.finish_scenario(scenario, feature)
|
64
|
+
end
|
65
|
+
|
66
|
+
def run_background(background, scenario, feature, world)
|
67
|
+
reporter.start_background(background)
|
68
|
+
|
69
|
+
background.steps.each{|s| run_step(s, scenario, feature, world) }
|
70
|
+
|
71
|
+
reporter.finish_background(background)
|
72
|
+
end
|
73
|
+
|
74
|
+
def run_step(step, scenario, feature, world)
|
75
|
+
reporter.start_step(step, scenario, feature)
|
76
|
+
|
77
|
+
result = nil
|
78
|
+
with_filtered_backtrace do
|
79
|
+
match = Steps.find_step(step, world, step.type)
|
80
|
+
|
81
|
+
if scenario.pending? || scenario.failed?
|
82
|
+
result = StepResult.new(step, :skipped)
|
83
|
+
return
|
84
|
+
end
|
85
|
+
|
86
|
+
m = world.method(match.method_name)
|
87
|
+
world.send match.method_name, *(match.params + [step])[0...m.arity]
|
88
|
+
end
|
89
|
+
|
90
|
+
result = StepResult.new(step, :success)
|
91
|
+
rescue StepPending => e
|
92
|
+
scenario.pending! e
|
93
|
+
result = StepResult.new(step, :pending, e)
|
94
|
+
rescue => e
|
95
|
+
scenario.failed! e
|
96
|
+
result = StepResult.new(step, :failed, e)
|
97
|
+
ensure
|
98
|
+
reporter.finish_step(result, scenario, feature)
|
99
|
+
end
|
100
|
+
|
101
|
+
def with_hooks(scope, _context, world, &block)
|
102
|
+
Configuration::BEFORE_HOOKS.for(scope).each do |hook|
|
103
|
+
hook.run(world)
|
104
|
+
end
|
105
|
+
Configuration::AROUND_HOOKS.for(scope).reduce(block) do |blk, hook|
|
106
|
+
proc { hook.run(world, blk) }
|
107
|
+
end.call
|
108
|
+
Configuration::AFTER_HOOKS.for(scope).each do |hook|
|
109
|
+
hook.run(world)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def world_for(scenario, _feature)
|
114
|
+
scenario.send(:world)
|
115
|
+
end
|
116
|
+
|
117
|
+
def with_filtered_backtrace
|
118
|
+
yield
|
119
|
+
rescue => e
|
120
|
+
unless options[:backtrace]
|
121
|
+
base = File.expand_path(Gurke.root.dirname)
|
122
|
+
e.backtrace.select!{|l| File.expand_path(l)[0...base.size] == base }
|
123
|
+
end
|
124
|
+
raise
|
125
|
+
end
|
126
|
+
|
127
|
+
#
|
128
|
+
class StepResult
|
129
|
+
attr_reader :step, :exception, :state
|
130
|
+
|
131
|
+
def initialize(step, state, err = nil)
|
132
|
+
@step, @state, @exception = step, state, err
|
133
|
+
end
|
134
|
+
|
135
|
+
Gurke::Step.public_instance_methods(false).each do |m|
|
136
|
+
define_method(m){|*args| step.send m, *args }
|
137
|
+
end
|
138
|
+
|
139
|
+
def failed?
|
140
|
+
@state == :failed
|
141
|
+
end
|
142
|
+
|
143
|
+
def pending?
|
144
|
+
@state == :pending
|
145
|
+
end
|
146
|
+
|
147
|
+
def skipped?
|
148
|
+
@state == :skipped
|
149
|
+
end
|
150
|
+
|
151
|
+
def success?
|
152
|
+
@state == :success
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Gurke
|
2
|
+
#
|
3
|
+
class Scenario
|
4
|
+
#
|
5
|
+
# Return path to file containing this scenario.
|
6
|
+
#
|
7
|
+
# @return [String] File path.
|
8
|
+
#
|
9
|
+
attr_reader :file
|
10
|
+
|
11
|
+
# Return line number where the scenario is defined.
|
12
|
+
#
|
13
|
+
# @return [Fixnum] Line number.
|
14
|
+
#
|
15
|
+
attr_reader :line
|
16
|
+
|
17
|
+
attr_reader :tags
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
attr_reader :raw
|
21
|
+
|
22
|
+
# @api private
|
23
|
+
def initialize(file, line, tags, raw)
|
24
|
+
@file, @line, @tags, @raw = file, line, tags, raw
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return list of this scenario's steps.
|
28
|
+
#
|
29
|
+
# This does not include background steps.
|
30
|
+
#
|
31
|
+
# @return [Array<Step>] Steps.
|
32
|
+
#
|
33
|
+
def steps
|
34
|
+
@steps ||= []
|
35
|
+
end
|
36
|
+
|
37
|
+
# Return name of the scenario.
|
38
|
+
#
|
39
|
+
# @return [String] Scenario name.
|
40
|
+
#
|
41
|
+
def name
|
42
|
+
raw.name
|
43
|
+
end
|
44
|
+
|
45
|
+
def pending?
|
46
|
+
@state == :pending
|
47
|
+
end
|
48
|
+
|
49
|
+
def failed?
|
50
|
+
@state == :failed
|
51
|
+
end
|
52
|
+
|
53
|
+
attr_reader :exception
|
54
|
+
|
55
|
+
# @api private
|
56
|
+
def failed!(error)
|
57
|
+
@exception = error
|
58
|
+
@state = :failed
|
59
|
+
end
|
60
|
+
|
61
|
+
# @api private
|
62
|
+
def pending!(error)
|
63
|
+
return if failed?
|
64
|
+
|
65
|
+
@exception = error
|
66
|
+
@state = :pending
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def world
|
72
|
+
@world ||= begin
|
73
|
+
cls = Class.new
|
74
|
+
cls.send :include, Gurke.world
|
75
|
+
|
76
|
+
Gurke.config.inclusions.each do |incl|
|
77
|
+
cls.send :include, incl.mod
|
78
|
+
end
|
79
|
+
cls.send :include, Gurke::Steps
|
80
|
+
cls.new
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|