gurke 1.0.1 → 2.0.0.dev.1.b17

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,5 @@
1
+ require 'rspec/expectations'
2
+
3
+ Gurke.configure do |c|
4
+ c.include RSpec::Matchers
5
+ end
@@ -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