lopata 0.1.1 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,46 @@
1
+ module Lopata
2
+ # @see Lopata::Configuration#role_descriptions
3
+ # @see Lopata::Configuration#default_role
4
+ module Role
5
+ # To be included in Lopata::Scenario
6
+ module Methods
7
+ def current_role
8
+ metadata[:as]
9
+ end
10
+ end
11
+
12
+ # To be included in Lopata::ScenarioBuilder
13
+ module DSL
14
+ def as(*args, &block)
15
+ @roles = args.flatten
16
+ @roles << Lopata::ScenarioBuilder::CalculatedValue.new(&block) if block_given?
17
+ @role_options = nil
18
+ end
19
+
20
+ def role_options
21
+ @role_options ||= build_role_options
22
+ end
23
+
24
+ def without_user
25
+ @without_user = true
26
+ end
27
+
28
+ def build_role_options
29
+ return [] unless roles
30
+ [Lopata::ScenarioBuilder::Diagonal.new(:as, roles.map { |r| [Lopata.configuration.role_descriptions[r], r] })]
31
+ end
32
+
33
+ def roles
34
+ return false if @without_user
35
+ @roles ||= [Lopata.configuration.default_role].compact
36
+ end
37
+
38
+ def diagonals
39
+ super + role_options
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ Lopata::Scenario.include Lopata::Role::Methods
46
+ Lopata::ScenarioBuilder.prepend Lopata::Role::DSL
@@ -1,6 +1,5 @@
1
1
  require 'thor'
2
2
  require_relative 'generators/app'
3
- require_relative 'config'
4
3
  require_relative 'world'
5
4
  require_relative 'loader'
6
5
  require_relative '../lopata'
@@ -11,18 +10,14 @@ module Lopata
11
10
  class Runner < Thor
12
11
  desc 'test', 'Run tests'
13
12
  option :env, default: :qa, aliases: 'e'
14
- option :"no-log", type: :boolean, aliases: 'n'
15
- option :focus, type: :boolean, aliases: 'f'
16
13
  option :rerun, type: :boolean, aliases: 'r'
17
- option :users, type: :array, aliases: 'u'
18
- option :build, aliases: 'b'
19
14
  option :keep, type: :boolean, aliases: 'k'
20
15
  option :text, aliases: 't'
21
16
  def test(*args)
22
17
  configure_from_options
23
18
  Lopata::Loader.load_shared_steps
24
19
  Lopata::Loader.load_scenarios(*args)
25
- world = Lopata::Config.world
20
+ world = Lopata.world
26
21
  world.start
27
22
  world.scenarios.each { |s| s.run }
28
23
  world.finish
@@ -38,17 +33,23 @@ module Lopata
38
33
 
39
34
  no_commands do
40
35
  def configure_from_options
41
- Lopata::Config.ops = {
42
- focus: options[:focus],
43
- rerun: options[:rerun],
44
- users: options[:users],
45
- build: options[:build],
46
- env: options[:env],
47
- keep: options[:keep],
48
- text: options[:text]
49
- }
50
- Lopata::Config.init(options[:env])
51
- Lopata::Config.initialize_test
36
+ Lopata.configure do |c|
37
+ c.env = options[:env].to_sym
38
+ c.keep = options[:keep]
39
+ c.load_environment
40
+ c.run_before_start_hooks
41
+ end
42
+ add_text_filter(options[:text]) if options[:text]
43
+ add_rerun_filter if options[:rerun]
44
+ end
45
+
46
+ def add_text_filter(text)
47
+ Lopata.configuration.filters << -> (scenario) { scenario.title.include?(text) }
48
+ end
49
+
50
+ def add_rerun_filter
51
+ to_rerun = Lopata::Client.new.to_rerun
52
+ Lopata.configuration.filters << -> (scenario) { to_rerun.include?(scenario.title) }
52
53
  end
53
54
  end
54
55
  end
@@ -3,55 +3,115 @@ require 'rspec/expectations'
3
3
  class Lopata::Scenario
4
4
  include RSpec::Matchers
5
5
 
6
- attr_reader :title, :metadata, :steps, :status
6
+ attr_reader :execution
7
7
 
8
- def initialize(title, options_title, metadata = {})
9
- @title = [title, options_title].compact.reject(&:empty?).join(' ')
10
- @metadata = metadata
11
- @steps = []
12
- @status = :not_runned
8
+ def initialize(execution)
9
+ @execution = execution
13
10
  end
14
11
 
15
- def run
16
- @status = :running
17
- world.notify_observers(:scenario_started, self)
18
- teardown_steps = []
19
- @steps.reject(&:teardown?).each { |step| step.run(self) }
20
- @steps.select(&:teardown?).each { |step| step.run(self) }
21
- @status = @steps.all?(&:passed?) ? :passed : :failed
22
- world.notify_observers(:scenario_finished, self)
12
+ # Marks current step as pending
13
+ def pending(message = nil)
14
+ execution.current_step.pending!(message)
23
15
  end
24
16
 
25
- def match_metadata?(metadata_key)
26
- case metadata_key
27
- when Hash
28
- metadata_key.keys.all? { |k| metadata[k] == metadata_key[k] }
29
- when Array
30
- metadata_key.map { |key| metadata[key] }.none?(&:nil?)
17
+ def metadata
18
+ execution.metadata
19
+ end
20
+
21
+ private
22
+
23
+ def method_missing(method, *args, &block)
24
+ if execution.let_methods.include?(method)
25
+ instance_exec(*args, &execution.let_methods[method])
26
+ elsif metadata.keys.include?(method)
27
+ metadata[method]
31
28
  else
32
- metadata[metadata_key]
29
+ super
33
30
  end
34
31
  end
35
32
 
36
- def world
37
- @world ||= Lopata::Config.world
33
+ def respond_to_missing?(method, *)
34
+ execution.let_methods.include?(method) or metadata.keys.include?(method) or super
38
35
  end
39
36
 
40
- def failed?
41
- status == :failed
42
- end
37
+ class Execution
38
+ attr_reader :scenario, :status, :steps, :title, :current_step
43
39
 
44
- private
40
+ def initialize(title, options_title, metadata = {})
41
+ @title = [title, options_title].compact.reject(&:empty?).join(' ')
42
+ @metadata = metadata
43
+ @let_methods = {}
44
+ @status = :not_runned
45
+ @steps = []
46
+ @scenario = Lopata::Scenario.new(self)
47
+ end
48
+
49
+ def run
50
+ @status = :running
51
+ sort_steps
52
+ world.notify_observers(:scenario_started, self)
53
+ steps.each(&method(:run_step))
54
+ @status = steps.any?(&:failed?) ? :failed : :passed
55
+ world.notify_observers(:scenario_finished, self)
56
+ cleanup
57
+ end
58
+
59
+ def run_step(step)
60
+ return if step.skipped?
61
+ @current_step = step
62
+ step.run(scenario)
63
+ skip_rest if step.failed? && step.skip_rest_on_failure?
64
+ @current_step = nil
65
+ end
66
+
67
+ def world
68
+ Lopata.world
69
+ end
45
70
 
46
- def method_missing(method, *args, &block)
47
- if metadata.keys.include?(method)
48
- metadata[method]
71
+ def failed?
72
+ status == :failed
73
+ end
74
+
75
+ def sort_steps
76
+ @steps = steps.reject(&:teardown_group?) + steps.select(&:teardown_group?)
77
+ end
78
+
79
+ def skip_rest
80
+ steps.select { |s| s.status == :not_runned && !s.teardown? }.each(&:skip!)
81
+ end
82
+
83
+ def metadata
84
+ if current_step
85
+ @metadata.merge(current_step.metadata)
49
86
  else
50
- super
87
+ @metadata
51
88
  end
52
89
  end
53
90
 
54
- def respond_to_missing?(method, *)
55
- metadata.keys.include?(method) or super
91
+ def let_methods
92
+ if current_step
93
+ @let_methods.merge(current_step.let_methods)
94
+ else
95
+ @let_methods
96
+ end
97
+ end
98
+
99
+ def let(method_name, &block)
100
+ # define_singleton_method method_name, &block
101
+ base =
102
+ if current_step && !current_step.groups.empty?
103
+ current_step.groups.last.let_methods
104
+ else
105
+ @let_methods
106
+ end
107
+ base[method_name] = block
56
108
  end
57
- end
109
+
110
+ def cleanup
111
+ @title = nil
112
+ @metadata = nil
113
+ @steps = nil
114
+ @scenario = nil
115
+ end
116
+ end
117
+ end
@@ -1,5 +1,6 @@
1
1
  class Lopata::ScenarioBuilder
2
- attr_reader :title, :common_metadata
2
+ attr_reader :title, :common_metadata, :options, :diagonals
3
+ attr_accessor :shared_step, :group
3
4
 
4
5
  def self.define(title, metadata = {}, &block)
5
6
  builder = new(title, metadata)
@@ -9,47 +10,35 @@ class Lopata::ScenarioBuilder
9
10
 
10
11
  def initialize(title, metadata = {})
11
12
  @title, @common_metadata = title, metadata
13
+ @diagonals = []
14
+ @options = []
12
15
  end
13
16
 
14
17
  def build
18
+ filters = Lopata.configuration.filters
15
19
  option_combinations.each do |option_set|
16
20
  metadata = common_metadata.merge(option_set.metadata)
17
- scenario = Lopata::Scenario.new(title, option_set.title, metadata)
21
+ scenario = Lopata::Scenario::Execution.new(title, option_set.title, metadata)
18
22
 
19
- steps.each do |step|
20
- next if step.condition && !step.condition.match?(scenario)
21
- step.pre_steps(scenario).each { |s| scenario.steps << s }
22
- scenario.steps << step
23
+ unless filters.empty?
24
+ next unless filters.all? { |f| f[scenario] }
23
25
  end
24
26
 
25
- if Lopata::Config.after_scenario
26
- scenario.steps << Lopata::Step.new(:after_scenario, &Lopata::Config.after_scenario)
27
+ steps_with_hooks.each do |step|
28
+ next if step.condition && !step.condition.match?(scenario)
29
+ step.execution_steps(scenario).each { |s| scenario.steps << s }
27
30
  end
28
31
 
29
32
  world.scenarios << scenario
30
33
  end
31
34
  end
32
35
 
33
- def as(*args, &block)
34
- @roles = args.flatten
35
- @roles << CalculatedValue.new(&block) if block_given?
36
- @role_options = nil
37
- end
38
-
39
- def role_options
40
- @role_options ||= build_role_options
41
- end
42
-
43
36
  def metadata(hash)
44
37
  raise 'metadata expected to be a Hash' unless hash.is_a?(Hash)
45
38
  @common_metadata ||= {}
46
39
  @common_metadata.merge! hash
47
40
  end
48
41
 
49
- def without_user
50
- @without_user = true
51
- end
52
-
53
42
  def skip_when(&block)
54
43
  @skip_when = block
55
44
  end
@@ -58,82 +47,72 @@ class Lopata::ScenarioBuilder
58
47
  @skip_when && @skip_when.call(option_set)
59
48
  end
60
49
 
61
- %i{ setup action it teardown }.each do |name|
50
+ %i{ setup action it teardown verify context }.each do |name|
62
51
  name_if = "%s_if" % name
63
52
  name_unless = "%s_unless" % name
64
- define_method name, ->(*args, &block) { add_step(name, *args, &block) }
65
- define_method name_if, ->(condition, *args, &block) { add_step(name, *args, condition: Lopata::Condition.new(condition), &block) }
66
- define_method name_unless, ->(condition, *args, &block) { add_step(name, *args, condition: Lopata::Condition.new(condition, positive: false), &block) }
53
+ define_method name, ->(*args, **metadata, &block) { add_step(name, *args, metadata: metadata, &block) }
54
+ define_method name_if, ->(condition, *args, **metadata, &block) {
55
+ add_step(name, *args, metadata: metadata, condition: Lopata::Condition.new(condition), &block)
56
+ }
57
+ define_method name_unless, ->(condition, *args, **metadata, &block) {
58
+ add_step(name, *args, condition: Lopata::Condition.new(condition, positive: false), metadata: metadata, &block)
59
+ }
67
60
  end
68
61
 
69
- def add_step(method_name, *args, condition: nil, &block)
62
+ def add_step(method_name, *args, condition: nil, metadata: {}, &block)
70
63
  step_class =
71
- if method_name =~ /^(setup|action|teardown)/
72
- Lopata::ActionStep
73
- else
74
- Lopata::Step
64
+ case method_name
65
+ when /^(setup|action|teardown|verify)/ then Lopata::ActionStep
66
+ when /^(context)/ then Lopata::GroupStep
67
+ else Lopata::Step
75
68
  end
76
- steps << step_class.new(method_name, *args, condition: condition, &block)
69
+ step = step_class.new(method_name, *args, condition: condition, shared_step: shared_step, &block)
70
+ step.metadata = metadata
71
+ steps << step
77
72
  end
78
73
 
79
74
  def steps
80
75
  @steps ||= []
81
76
  end
82
77
 
83
- def cleanup(*args, &block)
84
- add_step_as_is(:cleanup, *args, &block)
85
- end
86
-
87
- def add_step_as_is(method_name, *args, &block)
88
- steps << Lopata::Step.new(method_name, *args) do
89
- # do not convert args - symbols mean name of instance variable
90
- # run_step method_name, *args, &block
91
- instance_exec(&block) if block
78
+ def steps_with_hooks
79
+ s = []
80
+ unless Lopata.configuration.before_scenario_steps.empty?
81
+ s << Lopata::ActionStep.new(:setup, *Lopata.configuration.before_scenario_steps)
92
82
  end
93
- end
94
83
 
95
- def let(method_name, &block)
96
- steps << Lopata::Step.new(nil) do
97
- define_singleton_method method_name, &block
84
+ s += steps
85
+
86
+ unless Lopata.configuration.after_scenario_steps.empty?
87
+ s << Lopata::ActionStep.new(:teardown, *Lopata.configuration.after_scenario_steps)
98
88
  end
99
- end
100
89
 
101
- def build_role_options
102
- return [] unless roles
103
- [Diagonal.new(:as, roles.map { |r| [nil, r] })]
90
+ s
104
91
  end
105
92
 
106
- def roles
107
- return false if @without_user
108
- @roles ||= [Lopata::Config.default_role].compact
93
+ def let(method_name, &block)
94
+ steps << Lopata::Step.new(:let) do
95
+ execution.let(method_name, &block)
96
+ end
109
97
  end
110
98
 
111
99
  def option(metadata_key, variants)
112
- options << Option.new(metadata_key, variants)
100
+ @options << Option.new(metadata_key, variants)
113
101
  end
114
102
 
115
103
  def diagonal(metadata_key, variants)
116
- diagonals << Diagonal.new(metadata_key, variants)
117
- end
118
-
119
- def options
120
- @options ||= []
121
- end
122
-
123
- def diagonals
124
- @diagonals ||= []
104
+ @diagonals << Diagonal.new(metadata_key, variants)
125
105
  end
126
106
 
127
107
  def option_combinations
128
- combinations = combine([OptionSet.new], options + diagonals + role_options)
129
- while !(diagonals + role_options).all?(&:complete?)
130
- combinations << OptionSet.new(*(options + diagonals + role_options).map(&:next_variant))
108
+ combinations = combine([OptionSet.new], options + diagonals)
109
+ while !diagonals.all?(&:complete?)
110
+ combinations << OptionSet.new(*(options + diagonals).map(&:next_variant))
131
111
  end
132
112
  combinations.reject { |option_set| skip?(option_set) }
133
113
  end
134
114
 
135
115
  def combine(source_combinations, rest_options)
136
- # raise 'source_combinations cannot be empty' if source_combinations.blank?
137
116
  return source_combinations if rest_options.empty?
138
117
  combinations = []
139
118
  current_option = rest_options.shift
@@ -146,7 +125,7 @@ class Lopata::ScenarioBuilder
146
125
  end
147
126
 
148
127
  def world
149
- @world ||= Lopata::Config.world
128
+ Lopata.world
150
129
  end
151
130
 
152
131
  # Набор вариантов, собранный для одного теста
@@ -187,10 +166,10 @@ class Lopata::ScenarioBuilder
187
166
  end
188
167
 
189
168
  class Variant
190
- attr_reader :key, :title, :value
169
+ attr_reader :key, :title, :value, :option
191
170
 
192
- def initialize(key, title, value)
193
- @key, @title, @value = key, title, check_lambda_arity(value)
171
+ def initialize(option, key, title, value)
172
+ @option, @key, @title, @value = option, key, title, check_lambda_arity(value)
194
173
  end
195
174
 
196
175
  def metadata(option_set)
@@ -202,6 +181,10 @@ class Lopata::ScenarioBuilder
202
181
  end
203
182
  end
204
183
 
184
+ option.available_metadata_keys.each do |key|
185
+ data[key] = nil unless data.has_key?(key)
186
+ end
187
+
205
188
  data.each do |key, v|
206
189
  data[key] = v.calculate(option_set) if v.is_a? CalculatedValue
207
190
  end
@@ -241,14 +224,15 @@ class Lopata::ScenarioBuilder
241
224
  end
242
225
 
243
226
  class Option
244
- attr_reader :variants
227
+ attr_reader :variants, :key
245
228
  def initialize(key, variants)
229
+ @key = key
246
230
  @variants =
247
231
  if variants.is_a? Hash
248
- variants.map { |title, value| Variant.new(key, title, value) }
232
+ variants.map { |title, value| Variant.new(self, key, title, value) }
249
233
  else
250
234
  # Array of arrays of two elements
251
- variants.map { |v| Variant.new(key, *v) }
235
+ variants.map { |v| Variant.new(self, key, *v) }
252
236
  end
253
237
  end
254
238
 
@@ -267,6 +251,11 @@ class Lopata::ScenarioBuilder
267
251
  end
268
252
  selected_variant
269
253
  end
254
+
255
+ def available_metadata_keys
256
+ @available_metadata_keys ||= variants
257
+ .map(&:value).select { |v| v.is_a?(Hash) }.flat_map(&:keys).map { |k| "#{key}_#{k}".to_sym }.uniq
258
+ end
270
259
  end
271
260
 
272
261
  class Diagonal < Option