lopata 0.1.25 → 0.1.26

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