lopata 0.1.25 → 0.1.26

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 52985977beeccefe3ae15ad9686dad98119c0f201007ce353d27e182bfb11eaa
4
- data.tar.gz: 5c21409a5efb926421e8d829ab2051dad6f4a4e914ee2e9500c01c28e8b083c7
3
+ metadata.gz: c48d8c35ec3fbda8e6e707ec75a4d48c98a7e0733a4156869e932a00cadb6a10
4
+ data.tar.gz: 70ef965bafbf052815cd06f9807f1ac209dd06668e7fe17dabee4684e7066885
5
5
  SHA512:
6
- metadata.gz: 34503bf27d89b6fca63f77fab8d0f4940f102c01de34058f73d2255258e06e1b5a26b989302b9d842502b8f79b236c80538729b15878655f35d87f0370836947
7
- data.tar.gz: af7b41981f88d5c8fd03e3099d7ff7612a1d24642e7ca27f01bc914a309ed32847fcf4c1a4ae12dbfd42b167040e36c19f2a012f00295761eb4ce125fdebe565
6
+ metadata.gz: 9547b0ce6a37588e0fac9a3f0c899502e755e17caf753eac1bdcaba8dbf2119b2d5ddcda3897b8450cecc03b9796a1b2234bd3f09ebe4e442501cc7f79f62410
7
+ data.tar.gz: f1c6e073a0dce8cfdd22182214fddb5ff88acef2771df288bf8109333533506a941db6e9d3111553e7ea5dea2dba05b21d096924dc424dee6dfea1f5d7cd147d
@@ -1,5 +1,4 @@
1
1
  require_relative 'backtrace_formatter'
2
- require_relative 'group_tree'
3
2
  require 'forwardable'
4
3
 
5
4
  module Lopata
@@ -34,25 +33,27 @@ module Lopata
34
33
  statuses[scenario.status] ||= 0
35
34
  statuses[scenario.status] += 1
36
35
 
37
- if scenario.failed?
38
- GroupTree.steps_hierarhy(scenario.steps).walk_through do |step|
39
- if step.is_a?(Lopata::StepExecution)
40
- next unless step.loggable?
41
- puts colored(" #{status_marker(step.status)} #{step.title}", step.status)
42
- puts indent(4, backtrace_formatter.error_message(step.exception, include_backtrace: true)) if step.failed?
43
- else # GroupTree
44
- group = step
45
- if %i{ passed skipped }.include?(group.status)
46
- puts colored(" #{status_marker(group.status)} #{group.title}", group.status)
47
- false
48
- else
49
- true
50
- end
36
+ log_steps(scenario.steps) if scenario.failed?
37
+
38
+ flush
39
+ end
40
+
41
+ # @private
42
+ def log_steps(steps)
43
+ steps.each do |step|
44
+ next unless step.loggable?
45
+ if step.group?
46
+ status = step.status
47
+ if %i{ passed skipped ignored }.include?(status)
48
+ puts colored(" #{status_marker(step.status)} #{step.title}", status)
49
+ else
50
+ log_steps(step.steps)
51
51
  end
52
+ else
53
+ puts colored(" #{status_marker(step.status)} #{step.title}", step.status)
54
+ puts indent(4, backtrace_formatter.error_message(step.exception, include_backtrace: true)) if step.failed?
52
55
  end
53
56
  end
54
-
55
- flush
56
57
  end
57
58
 
58
59
  private
@@ -61,7 +62,7 @@ module Lopata
61
62
  case status
62
63
  when :failed then red(text)
63
64
  when :passed then green(text)
64
- when :skipped then cyan(text)
65
+ when :skipped, :ignored then cyan(text)
65
66
  when :pending then yellow(text)
66
67
  else text
67
68
  end
@@ -1,7 +1,6 @@
1
1
  require 'httparty'
2
2
  require 'json'
3
3
  require_relative 'backtrace_formatter'
4
- require_relative 'group_tree'
5
4
 
6
5
  module Lopata
7
6
  module Observers
@@ -49,24 +48,7 @@ module Lopata
49
48
 
50
49
  def add_attempt(scenario, finished)
51
50
  status = scenario.failed? ? Lopata::FAILED : Lopata::PASSED
52
- steps = []
53
- GroupTree.steps_hierarhy(scenario.steps).walk_through do |step|
54
- if step.is_a?(Lopata::StepExecution)
55
- next unless step.loggable?
56
- steps << step_hash(step)
57
- else # GroupTree
58
-
59
- group = step
60
- if %i{ passed skipped }.include?(group.status)
61
- steps << group_hash(group)
62
- false
63
- else
64
- true
65
- end
66
- end
67
- end
68
-
69
- steps = scenario.steps.select(&:loggable?).map { |s| step_hash(s) }
51
+ steps = build_hashes(scenario.steps)
70
52
  request = { status: status, steps: steps, launch: { id: @launch_id, finished: finished } }
71
53
  test = test_id(scenario)
72
54
  post("/tests/#{test}/attempts.json", body: request)
@@ -76,6 +58,23 @@ module Lopata
76
58
  puts e.backtrace
77
59
  end
78
60
 
61
+ def build_hashes(steps)
62
+ hashes = []
63
+ steps.each do |step|
64
+ next unless step.loggable?
65
+ if step.group?
66
+ if %i{ passed skipped ignored }.include?(step.status)
67
+ hashes << group_hash(step)
68
+ else
69
+ hashes += build_hashes(step.steps)
70
+ end
71
+ else
72
+ hashes << step_hash(step)
73
+ end
74
+ end
75
+ hashes
76
+ end
77
+
79
78
  def step_hash(step)
80
79
  hash = { status: step.status, title: step.title }
81
80
  if step.failed?
@@ -45,8 +45,9 @@ class Lopata::Scenario
45
45
 
46
46
  # @private
47
47
  def method_missing(method, *args, &block)
48
- if execution.let_methods.include?(method)
49
- execution.let_methods[method].call_in_scenario(self, *args)
48
+
49
+ if (let_method = execution.find_let_method(method))
50
+ let_method.call_in_scenario(self, *args)
50
51
  elsif metadata.keys.include?(method)
51
52
  metadata[method]
52
53
  else
@@ -56,21 +57,18 @@ class Lopata::Scenario
56
57
 
57
58
  # @private
58
59
  def respond_to_missing?(method, *)
59
- execution.let_methods.include?(method) or metadata.keys.include?(method) or super
60
+ execution.find_let_method(method) or metadata.keys.include?(method) or super
60
61
  end
61
62
 
62
63
  # @private
63
64
  # Scenario execution and live-cycle information
64
65
  class Execution
65
- attr_reader :scenario, :status, :steps, :title, :current_step
66
-
67
- def initialize(title, options_title, metadata = {})
68
- @title = [title, options_title].compact.reject(&:empty?).join(' ')
69
- @metadata = metadata
70
- @let_methods = {}
71
- @status = :not_runned
72
- @steps = []
66
+ attr_reader :scenario, :current_step, :top
67
+
68
+ def initialize(title, metadata = {})
73
69
  @scenario = Lopata::Scenario.new(self)
70
+ @top = Lopata::GroupExecution.new(Lopata::TopStep.new(title, metadata: metadata), nil, steps: [])
71
+ @current_step = @top
74
72
  end
75
73
 
76
74
  # Provide a human-readable representation of this class
@@ -79,32 +77,42 @@ class Lopata::Scenario
79
77
  end
80
78
  alias to_s inspect
81
79
 
80
+ def steps
81
+ top.steps
82
+ end
83
+
82
84
  def run
83
- @status = :running
84
- sort_steps
85
85
  world.notify_observers(:scenario_started, self)
86
- steps.each(&method(:run_step))
87
- @status = steps.any?(&:failed?) ? :failed : :passed
86
+ run_step(top)
88
87
  world.notify_observers(:scenario_finished, self)
89
88
  cleanup
90
89
  end
91
90
 
92
91
  def run_step(step)
93
- return if step.skipped? || step.ignored?
94
- groups = step.groups
95
- if groups.length > 0 && groups != @current_groups
96
- @current_groups = groups
97
- condition = groups.last.condition
98
- if condition&.dynamic? && !condition.match_dynamic?(scenario)
99
- step.ignored!
100
- ignore_groups(groups)
101
- return
92
+ @current_step = step
93
+ return :skipped if step.skipped?
94
+ return :ignored if step.ignored?
95
+ if step.condition&.dynamic && !step.condition.match_dynamic?(scenario)
96
+ step.ignored!
97
+ return :ignored
98
+ end
99
+ if step.group?
100
+ skip_rest = false
101
+ step.steps.each do
102
+ if _1.teardown?
103
+ run_step(_1)
104
+ elsif skip_rest
105
+ _1.skip!
106
+ else
107
+ run_step(_1)
108
+ skip_rest = true if _1.failed? && _1.skip_rest_on_failure?
109
+ end
102
110
  end
111
+ step.status!
112
+ else
113
+ step.run(scenario)
114
+ step.status
103
115
  end
104
- @current_step = step
105
- step.run(scenario)
106
- skip_rest if step.failed? && step.skip_rest_on_failure?
107
- @current_step = nil
108
116
  end
109
117
 
110
118
  def world
@@ -115,55 +123,47 @@ class Lopata::Scenario
115
123
  status == :failed
116
124
  end
117
125
 
118
- def sort_steps
119
- @steps = steps.reject(&:teardown_group?) + steps.select(&:teardown_group?)
126
+ def metadata
127
+ current_step.metadata
120
128
  end
121
129
 
122
- def skip_rest
123
- steps.select { |s| s.status == :not_runned && !s.teardown? }.each(&:skip!)
130
+ def let_methods
131
+ current_step.let_methods
124
132
  end
125
133
 
126
- def ignore_groups(groups)
127
- steps.select { _1.status == :not_runned && _1.groups.take(groups.length) == groups }.each(&:ignored!)
134
+ def find_let_method(name)
135
+ current_step.find_let_method(name)
128
136
  end
129
137
 
130
- def metadata
131
- if current_step
132
- @metadata.merge(current_step.metadata)
133
- else
134
- @metadata
135
- end
138
+ def title
139
+ top.title
136
140
  end
137
141
 
138
- def let_methods
139
- if current_step
140
- @let_methods.merge(current_step.let_methods)
141
- else
142
- @let_methods
143
- end
142
+ def status
143
+ top.status
144
144
  end
145
145
 
146
146
  def let_base
147
- if current_step && !current_step.groups.empty?
148
- current_step.groups.last.let_methods
147
+ if current_step.group?
148
+ current_step
149
149
  else
150
- @let_methods
150
+ current_step.parent
151
151
  end
152
152
  end
153
153
 
154
154
  def let(method_name, &block)
155
- let_base[method_name] = LetMethod.new(&block)
155
+ let_base.add_let_method(method_name, LetMethod.new(&block))
156
156
  end
157
157
 
158
158
  def let!(method_name, &block)
159
- let_base[method_name] = LetBangMethod.new(&block)
159
+ let_base.add_let_method(method_name, LetBangMethod.new(&block))
160
160
  end
161
161
 
162
162
  def cleanup
163
163
  @title = nil
164
- @metadata = nil
165
- @steps = nil
166
164
  @scenario = nil
165
+ @top = nil
166
+ @current_step = nil
167
167
  end
168
168
  end
169
169
 
@@ -38,16 +38,20 @@ class Lopata::ScenarioBuilder
38
38
  filters = Lopata.configuration.filters
39
39
  option_combinations.each do |option_set|
40
40
  metadata = common_metadata.merge(option_set.metadata)
41
- scenario = Lopata::Scenario::Execution.new(title, option_set.title, metadata)
42
-
41
+ scenario_title = [title, option_set.title].compact.reject(&:empty?).join(' ')
42
+ scenario = Lopata::Scenario::Execution.new(scenario_title, metadata)
43
+
43
44
  unless filters.empty?
44
45
  next unless filters.all? { |f| f[scenario] }
45
46
  end
46
47
 
48
+ exec_steps = []
47
49
  steps_with_hooks.each do |step|
48
50
  next if step.condition && !step.condition.match?(scenario)
49
- step.execution_steps(scenario).each { |s| scenario.steps << s }
51
+ step.execution_steps(scenario, parent: scenario.top).each { |s| exec_steps << s }
50
52
  end
53
+ scenario.steps.push(*exec_steps.reject(&:teardown?))
54
+ scenario.steps.push(*exec_steps.select(&:teardown?))
51
55
 
52
56
  world.scenarios << scenario
53
57
  end
data/lib/lopata/step.rb CHANGED
@@ -7,12 +7,13 @@ module Lopata
7
7
  # metadata overrien by the step.
8
8
  attr_accessor :metadata
9
9
 
10
- def initialize(method_name, *args, condition: nil, shared_step: nil, &block)
10
+ def initialize(method_name, *args, condition: nil, shared_step: nil, metadata: {}, &block)
11
11
  @method_name = method_name
12
12
  @args = args
13
13
  @block = block
14
14
  @shared_step = shared_step
15
15
  @condition = condition
16
+ @metadata = metadata
16
17
  initialized! if defined? initialized!
17
18
  end
18
19
 
@@ -22,31 +23,31 @@ module Lopata
22
23
  base_title
23
24
  end
24
25
 
25
- def execution_steps(scenario, groups: [])
26
+ def execution_steps(scenario, parent:)
26
27
  return [] if condition && !condition.match?(scenario)
27
28
  return [] unless block
28
- [StepExecution.new(self, groups, condition: condition, &block)]
29
+ [StepExecution.new(self, parent, condition: condition, &block)]
29
30
  end
30
31
  end
31
32
 
32
33
  # @private
33
34
  # Used for action, setup, teardown, verify
34
35
  class ActionStep < Step
35
- def execution_steps(scenario, groups: [])
36
+ def execution_steps(scenario, parent:)
36
37
  steps = []
37
38
  return steps if condition && !condition.match?(scenario)
38
39
  convert_args(scenario).each do |step|
39
40
  if step.is_a?(String)
40
41
  Lopata::SharedStep.find(step).steps.each do |shared_step|
41
42
  next if shared_step.condition && !shared_step.condition.match?(scenario)
42
- steps += shared_step.execution_steps(scenario, groups: groups)
43
+ steps += shared_step.execution_steps(scenario, parent: parent)
43
44
  end
44
45
  elsif step.is_a?(Proc)
45
- steps << StepExecution.new(self, groups, condition: condition, &step)
46
+ steps << StepExecution.new(self, parent, condition: condition, &step)
46
47
  end
47
48
  end
48
- steps << StepExecution.new(self, groups, condition: condition, &block) if block
49
- steps.reject { |s| !s.block }
49
+ steps << StepExecution.new(self, parent, condition: condition, &block) if block
50
+ steps
50
51
  end
51
52
 
52
53
  def separate_args(args)
@@ -74,22 +75,18 @@ module Lopata
74
75
  # Used for context
75
76
  class GroupStep < Step
76
77
 
77
- def execution_steps(scenario, groups: [])
78
+ def execution_steps(scenario, parent: nil)
78
79
  steps = []
79
80
  return steps if condition && !condition.match?(scenario)
81
+ group = GroupExecution.new(self, parent, steps: steps, condition: condition)
80
82
  @steps.each do |step|
81
- steps += step.execution_steps(scenario, groups: groups + [self])
83
+ steps += step.execution_steps(scenario, parent: group)
82
84
  end
83
- steps.reject! { |s| !s.block }
84
- steps.reject { |s| s.teardown_group?(self) } + steps.select { |s| s.teardown_group?(self) }
85
+ group.steps.push(*steps.reject(&:teardown?))
86
+ group.steps.push(*steps.select(&:teardown?))
87
+ [group]
85
88
  end
86
89
 
87
- def let_methods
88
- @let_methods ||= {}
89
- end
90
-
91
- private
92
-
93
90
  # Group step's block is a block in context of builder, not scenario. So hide the @block to not be used in scenario.
94
91
  def initialized!
95
92
  builder = Lopata::ScenarioBuilder.new(title)
@@ -99,70 +96,60 @@ module Lopata
99
96
  end
100
97
  end
101
98
 
102
- #@private
103
- class StepExecution
104
- attr_reader :step, :status, :exception, :block, :pending_message, :groups, :condition
99
+ # @private
100
+ class TopStep < Step
101
+ def initialize(title, metadata: {})
102
+ super(:top, title, metadata: metadata)
103
+ end
104
+ end
105
+
106
+ # @private
107
+ # Abstract execution step. Composition, may be group or step.
108
+ class BaseExecution
109
+ attr_reader :step, :status, :parent, :condition
105
110
  extend Forwardable
106
111
  def_delegators :step, :method_name
107
112
 
108
- class PendingStepFixedError < StandardError; end
109
-
110
- def initialize(step, groups, condition: nil, &block)
113
+ def initialize(step, parent, condition: nil)
111
114
  @step = step
115
+ @parent = parent
112
116
  @status = :not_runned
113
- @exception = nil
114
- @block = block
115
- @groups = groups
116
117
  @condition = condition
117
118
  end
118
119
 
119
- def title
120
- "#{group_title}#{step.title}"
120
+ def group?
121
+ false
121
122
  end
122
123
 
123
- def group_title
124
- groups.map { |g| "#{g.title}: " }.join
124
+ def top?
125
+ !parent
125
126
  end
126
127
 
127
- def run(scenario)
128
- @status = :running
129
- begin
130
- unless check_dynamic_condition?(scenario)
131
- @status = :ignored
132
- return
133
- end
134
- run_step(scenario)
135
- if pending?
136
- @status = :failed
137
- raise PendingStepFixedError, 'Expected step to fail since it is pending, but it passed.'
138
- else
139
- @status = :passed
140
- end
141
- rescue Exception => e
142
- @status = :failed unless pending?
143
- @exception = e
144
- end
128
+ def teardown?
129
+ %i{ teardown cleanup }.include?(method_name)
145
130
  end
146
131
 
147
- def run_step(scenario)
148
- return unless block
149
- scenario.instance_exec(&block)
132
+ def parents
133
+ result = []
134
+ prnt = parent
135
+ while prnt
136
+ result << prnt
137
+ prnt = prnt.parent
138
+ end
139
+ result
150
140
  end
151
141
 
152
- def check_dynamic_condition?(scenario)
153
- dynamic_conditions.each do
154
- return false unless _1.match_dynamic?(scenario)
142
+ # Step metadata is a combination of metadata given for step and all contexts (groups) the step included
143
+ def metadata
144
+ result = step.metadata || {}
145
+ if parent
146
+ result = parent.metadata.merge(result)
155
147
  end
156
- true
148
+ result
157
149
  end
158
150
 
159
- def dynamic_conditions
160
- conds = []
161
- conds << condition if condition&.dynamic?
162
- groups.each do
163
- conds << _1.condition if _1.condition&.dynamic?
164
- end
165
- conds
151
+ def find_let_method(name)
152
+ parent&.find_let_method(name)
166
153
  end
167
154
 
168
155
  def failed?
@@ -189,13 +176,12 @@ module Lopata
189
176
  @status = :skipped
190
177
  end
191
178
 
192
- def pending?
193
- status == :pending
194
- end
195
-
196
- def pending!(message = nil)
197
- @status = :pending
198
- @pending_message = message
179
+ def title
180
+ if parent && !parent.top?
181
+ "#{parent.title}: #{step.title}"
182
+ else
183
+ step.title
184
+ end
199
185
  end
200
186
 
201
187
  # Need log this step.
@@ -204,30 +190,96 @@ module Lopata
204
190
  not %i{ let let! }.include?(method_name)
205
191
  end
206
192
 
207
- def teardown?
208
- %i{ teardown cleanup }.include?(method_name)
193
+ def skip_rest_on_failure?
194
+ %i{ setup action }.include?(method_name)
209
195
  end
196
+ end
210
197
 
211
- def teardown_group?(group = nil)
212
- teardown? && self.groups.last == group
198
+ # @private
199
+ class GroupExecution < BaseExecution
200
+ attr_reader :steps
201
+
202
+ def initialize(step, parent, condition: nil, steps:)
203
+ super(step, parent, condition: condition)
204
+ @steps = steps
205
+ @let_methods = {}
213
206
  end
214
207
 
215
- def in_group?(group)
216
- groups.include?(group)
208
+ def group?
209
+ true
217
210
  end
218
211
 
219
- def skip_rest_on_failure?
220
- %i{ setup action }.include?(method_name)
212
+ def status!
213
+ # return @status if @status
214
+ statuses = steps.map(&:status!).uniq
215
+ @status =
216
+ if statuses.length == 1
217
+ statuses.first
218
+ elsif statuses.include?(:failed)
219
+ :failed
220
+ else
221
+ statuses.first || :skipped
222
+ end
223
+ @status = :passed if @status == :pending
224
+ @status
221
225
  end
222
226
 
223
- # Step metadata is a combination of metadata given for step and all contexts (groups) the step included
224
- def metadata
225
- (groups + [step]).compact.inject({}) { |merged, part| merged.merge(part.metadata || {}) }
227
+ def find_let_method(name)
228
+ @let_methods[name] || parent&.find_let_method(name)
229
+ end
230
+
231
+ def add_let_method(name, method)
232
+ @let_methods[name] = method
233
+ end
234
+
235
+ def ignored!
236
+ @status = :ignored
237
+ steps.each(&:ignored!)
238
+ end
239
+ end
240
+
241
+ # @private
242
+ class StepExecution < BaseExecution
243
+ attr_reader :exception, :block, :pending_message
244
+
245
+ class PendingStepFixedError < StandardError; end
246
+
247
+ def initialize(step, parent, condition: nil, &block)
248
+ super(step, parent, condition: condition)
249
+ @exception = nil
250
+ @block = block
251
+ end
252
+
253
+ alias status! status
254
+
255
+ def run(scenario)
256
+ @status = :running
257
+ begin
258
+ run_step(scenario)
259
+ if pending?
260
+ @status = :failed
261
+ raise PendingStepFixedError, 'Expected step to fail since it is pending, but it passed.'
262
+ else
263
+ @status = :passed
264
+ end
265
+ rescue Exception => e
266
+ @status = :failed unless pending?
267
+ @exception = e
268
+ end
226
269
  end
227
270
 
228
- # Step methods is a combination of let_methods for all contexts (group) the step included
229
- def let_methods
230
- (groups).compact.inject({}) { |merged, part| merged.merge(part.let_methods || {}) }
271
+ def run_step(scenario)
272
+ return unless block
273
+ scenario.instance_exec(&block)
274
+ end
275
+
276
+ def pending?
277
+ status == :pending
278
+ end
279
+
280
+ def pending!(message = nil)
281
+ @status = :pending
282
+ @pending_message = message
231
283
  end
232
284
  end
233
285
  end
@@ -1,6 +1,6 @@
1
1
  module Lopata
2
2
  # @private
3
3
  module Version
4
- STRING = '0.1.25'
4
+ STRING = '0.1.26'
5
5
  end
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lopata
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.25
4
+ version: 0.1.26
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexey Volochnev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-09-05 00:00:00.000000000 Z
11
+ date: 2023-09-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -107,7 +107,6 @@ files:
107
107
  - lib/lopata/observers/backtrace_formatter.rb
108
108
  - lib/lopata/observers/base_observer.rb
109
109
  - lib/lopata/observers/console_output_observer.rb
110
- - lib/lopata/observers/group_tree.rb
111
110
  - lib/lopata/observers/web_logger.rb
112
111
  - lib/lopata/role.rb
113
112
  - lib/lopata/runner.rb
@@ -139,5 +138,5 @@ requirements: []
139
138
  rubygems_version: 3.2.15
140
139
  signing_key:
141
140
  specification_version: 4
142
- summary: lopata-0.1.25
141
+ summary: lopata-0.1.26
143
142
  test_files: []
@@ -1,64 +0,0 @@
1
- module Lopata
2
- GroupTree = Struct.new(:group, :items, :title) do
3
- def status
4
- # return @status if @status
5
- statuses = items.map(&:status).uniq
6
- @status =
7
- if statuses.length == 1
8
- statuses.first
9
- elsif statuses.include?(:failed)
10
- :failed
11
- elsif
12
- statuses.first
13
- end
14
- @status
15
- end
16
-
17
- # Returns steps hierarhy: Group with nestet setps or groups
18
- def self.steps_hierarhy(steps)
19
- top = GroupTree.new(nil, [], '')
20
- hierarhy = [top]
21
- current_groups = []
22
- steps.each do |step|
23
- if step.groups == current_groups
24
- hierarhy.last.items << step
25
- else
26
- # ensure hierarhy to is in current step tree - remove from hierary all groups not in new tree.
27
- while hierarhy.last.group && !step.groups.include?(hierarhy.last.group)
28
- hierarhy.pop
29
- end
30
- if hierarhy.last.group == step.groups.last
31
- hierarhy.last.items << step
32
- else
33
- group_rest = step.groups.dup
34
- while hierarhy.last.group && group_rest.first != hierarhy.last.group
35
- group_rest.shift
36
- end
37
- group_rest.shift if group_rest.first == hierarhy.last.group
38
- group_rest.each do
39
- title = (hierarhy.map(&:group).compact + [_1]).map(&:title).join(': ')
40
- group = GroupTree.new(_1, [], title)
41
- hierarhy.last.items << group
42
- hierarhy << group
43
- end
44
- hierarhy.last.items << step
45
- end
46
- current_groups = step.groups
47
- end
48
- end
49
- return top
50
- end
51
-
52
- def walk_through(&block)
53
- items.each do |step|
54
- if step.is_a?(Lopata::StepExecution)
55
- yield step
56
- else # GroupTree
57
- group = step
58
- go_dipper = yield group
59
- group.walk_through(&block) if go_dipper
60
- end
61
- end
62
- end
63
- end
64
- end