lucid 0.3.3 → 0.4.0
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 +4 -4
- data/HISTORY.md +13 -9
- data/README.md +2 -6
- data/lib/lucid.rb +22 -3
- data/lib/lucid/{term/ansicolor.rb → ansicolor.rb} +34 -41
- data/lib/lucid/ast.rb +2 -2
- data/lib/lucid/ast/outline_table.rb +2 -2
- data/lib/lucid/ast/{specs.rb → spec.rb} +10 -1
- data/lib/lucid/ast/step.rb +3 -3
- data/lib/lucid/ast/step_invocation.rb +16 -16
- data/lib/lucid/ast/table.rb +1 -1
- data/lib/lucid/ast/{tdl_walker.rb → walker.rb} +15 -16
- data/lib/lucid/cli/app.rb +23 -21
- data/lib/lucid/cli/{configuration.rb → context.rb} +66 -38
- data/lib/lucid/cli/options.rb +59 -40
- data/lib/lucid/{configuration.rb → context.rb} +2 -3
- data/lib/lucid/{runtime.rb → context_loader.rb} +58 -61
- data/lib/lucid/{runtime/facade.rb → facade.rb} +5 -6
- data/lib/lucid/formatter/ansicolor.rb +15 -14
- data/lib/lucid/formatter/debug.rb +1 -1
- data/lib/lucid/formatter/usage.rb +2 -2
- data/lib/lucid/interface.rb +121 -0
- data/lib/lucid/{runtime/interface_io.rb → interface_io.rb} +2 -2
- data/lib/lucid/interface_rb/rb_language.rb +6 -23
- data/lib/lucid/interface_rb/rb_step_definition.rb +1 -2
- data/lib/lucid/{core_ext/instance_exec.rb → lang_extend.rb} +112 -69
- data/lib/lucid/{runtime/orchestrator.rb → orchestrator.rb} +36 -24
- data/lib/lucid/platform.rb +1 -1
- data/lib/lucid/{runtime/results.rb → results.rb} +10 -10
- data/lib/lucid/{tdl_builder.rb → spec_builder.rb} +26 -22
- data/lib/lucid/spec_file.rb +33 -13
- data/lib/lucid/{runtime/specs_loader.rb → spec_loader.rb} +13 -22
- data/lib/lucid/{step_definition_light.rb → step_definition_usage.rb} +2 -4
- data/lib/lucid/step_definitions.rb +4 -4
- data/spec/lucid/app_spec.rb +6 -18
- data/spec/lucid/ast/background_spec.rb +4 -4
- data/spec/lucid/ast/feature_spec.rb +7 -7
- data/spec/lucid/ast/scenario_outline_spec.rb +9 -9
- data/spec/lucid/ast/specs_spec.rb +8 -8
- data/spec/lucid/ast/step_spec.rb +5 -5
- data/spec/lucid/ast/tdl_walker_spec.rb +5 -5
- data/spec/lucid/{configuration_spec.rb → context_spec.rb} +78 -78
- data/spec/lucid/facade_spec.rb +7 -7
- data/spec/lucid/orchestrator_spec.rb +7 -7
- data/spec/lucid/pending_spec.rb +3 -3
- data/spec/lucid/progress_spec.rb +3 -3
- data/spec/lucid/rb_step_definition_spec.rb +4 -4
- data/spec/lucid/results_spec.rb +2 -2
- data/spec/lucid/runtime_spec.rb +7 -7
- metadata +20 -32
- data/bin/lucid-gen +0 -4
- data/lib/lucid/core_ext/proc.rb +0 -36
- data/lib/lucid/core_ext/string.rb +0 -9
- data/lib/lucid/generator.rb +0 -21
- data/lib/lucid/generators/project.rb +0 -64
- data/lib/lucid/generators/project/Gemfile.tt +0 -6
- data/lib/lucid/generators/project/browser-fluent.rb +0 -37
- data/lib/lucid/generators/project/driver-fluent.rb +0 -1
- data/lib/lucid/generators/project/errors.rb +0 -26
- data/lib/lucid/generators/project/events-fluent.rb +0 -33
- data/lib/lucid/interface_methods.rb +0 -125
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'timeout'
|
2
2
|
|
3
3
|
module Lucid
|
4
|
-
class
|
4
|
+
class ContextLoader
|
5
5
|
|
6
6
|
module InterfaceIO
|
7
7
|
attr_writer :visitor
|
@@ -33,7 +33,7 @@ module Lucid
|
|
33
33
|
@visitor.embed(src, mime_type, label)
|
34
34
|
end
|
35
35
|
|
36
|
-
|
36
|
+
private
|
37
37
|
|
38
38
|
def mri_gets(timeout_seconds)
|
39
39
|
begin
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'lucid/
|
1
|
+
require 'lucid/lang_extend'
|
2
2
|
require 'lucid/interface_rb/rb_lucid'
|
3
3
|
require 'lucid/interface_rb/rb_world'
|
4
4
|
require 'lucid/interface_rb/rb_step_definition'
|
@@ -9,42 +9,32 @@ require 'lucid/interface_rb/matcher'
|
|
9
9
|
begin
|
10
10
|
require 'rspec/expectations'
|
11
11
|
rescue LoadError
|
12
|
-
|
13
|
-
require 'spec/expectations'
|
14
|
-
require 'spec/runner/differs/default'
|
15
|
-
require 'ostruct'
|
16
|
-
rescue LoadError
|
17
|
-
require 'test/unit/assertions'
|
18
|
-
end
|
12
|
+
require 'test/unit/assertions'
|
19
13
|
end
|
20
14
|
|
21
15
|
module Lucid
|
22
16
|
module InterfaceRb
|
23
17
|
class NilDomain < StandardError
|
24
18
|
def initialize
|
25
|
-
super(
|
19
|
+
super('Domain procs should never return nil.')
|
26
20
|
end
|
27
21
|
end
|
28
22
|
|
29
|
-
# Raised if there are 2 or more Domain blocks.
|
30
23
|
class MultipleDomain < StandardError
|
31
24
|
def initialize(first_proc, second_proc)
|
32
25
|
message = "You can only pass a proc to #Domain once, but it's happening\n"
|
33
26
|
message << "in two places:\n\n"
|
34
27
|
message << first_proc.backtrace_line('Domain') << "\n"
|
35
28
|
message << second_proc.backtrace_line('Domain') << "\n\n"
|
36
|
-
message << "Use Ruby modules instead to extend your worlds
|
29
|
+
message << "Use Ruby modules instead to extend your worlds.\n"
|
37
30
|
super(message)
|
38
31
|
end
|
39
32
|
end
|
40
33
|
|
41
|
-
# This module is the Ruby implementation of the TDL API.
|
42
34
|
class RbLanguage
|
43
35
|
include Interface::InterfaceMethods
|
44
36
|
attr_reader :current_domain, :step_definitions
|
45
37
|
|
46
|
-
# Get the expressions of various I18n translations of TDL keywords.
|
47
|
-
# In this case the TDL is based on Gherkin.
|
48
38
|
Gherkin::I18n.code_keywords.each do |adverb|
|
49
39
|
RbLucid.alias_adverb(adverb)
|
50
40
|
end
|
@@ -61,14 +51,7 @@ module Lucid
|
|
61
51
|
begin
|
62
52
|
::RSpec::Matchers
|
63
53
|
rescue NameError
|
64
|
-
|
65
|
-
begin
|
66
|
-
options = OpenStruct.new(:diff_format => :unified, :context_lines => 3)
|
67
|
-
Spec::Expectations.differ = Spec::Expectations::Differs::Default.new(options)
|
68
|
-
::Spec::Matchers
|
69
|
-
rescue NameError
|
70
|
-
::Test::Unit::Assertions
|
71
|
-
end
|
54
|
+
::Test::Unit::Assertions
|
72
55
|
end
|
73
56
|
end
|
74
57
|
|
@@ -162,7 +145,7 @@ module Lucid
|
|
162
145
|
raise NilDomain.new
|
163
146
|
rescue NilDomain => e
|
164
147
|
e.backtrace.clear
|
165
|
-
e.backtrace.push(proc.backtrace_line(
|
148
|
+
e.backtrace.push(proc.backtrace_line('Domain'))
|
166
149
|
raise e
|
167
150
|
end
|
168
151
|
else
|
@@ -1,69 +1,112 @@
|
|
1
|
-
require 'lucid/platform'
|
2
|
-
|
3
|
-
module Lucid
|
4
|
-
# Raised if the number of a StepDefinition's Regexp match groups
|
5
|
-
# is different from the number of Proc arguments.
|
6
|
-
class ArityMismatchError < StandardError
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
class
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
1
|
+
require 'lucid/platform'
|
2
|
+
|
3
|
+
module Lucid
|
4
|
+
# Raised if the number of a StepDefinition's Regexp match groups
|
5
|
+
# is different from the number of Proc arguments.
|
6
|
+
class ArityMismatchError < StandardError
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class String
|
11
|
+
def indent(n)
|
12
|
+
if n >= 0
|
13
|
+
gsub(/^/, ' ' * n)
|
14
|
+
else
|
15
|
+
gsub(/^ {0,#{-n}}/, '')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Proc
|
21
|
+
PROC_PATTERN = /[\d\w]+@(.+):(\d+).*>/
|
22
|
+
PWD = Dir.pwd
|
23
|
+
|
24
|
+
def to_comment_line
|
25
|
+
"# #{file_colon_line}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def backtrace_line(name)
|
29
|
+
"#{file_colon_line}:in `#{name}'"
|
30
|
+
end
|
31
|
+
|
32
|
+
if Proc.new{}.to_s =~ PROC_PATTERN
|
33
|
+
def file_colon_line
|
34
|
+
path, line = *to_s.match(PROC_PATTERN)[1..2]
|
35
|
+
path = File.expand_path(path)
|
36
|
+
pwd = File.expand_path(PWD)
|
37
|
+
pwd.force_encoding(path.encoding)
|
38
|
+
if path.index(pwd)
|
39
|
+
path = path[pwd.length+1..-1]
|
40
|
+
elsif path =~ /.*\/gems\/(.*\.rb)$/
|
41
|
+
path = $1
|
42
|
+
end
|
43
|
+
"#{path}:#{line}"
|
44
|
+
end
|
45
|
+
else
|
46
|
+
STDERR.puts '*** This implementation of Ruby does not report file and line information for procs. ***'
|
47
|
+
|
48
|
+
def file_colon_line
|
49
|
+
'UNKNOWN:-1'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Object
|
55
|
+
def lucid_instance_exec(check_arity, pseudo_method, *args, &block)
|
56
|
+
lucid_run_with_backtrace_filtering(pseudo_method) do
|
57
|
+
if check_arity && !lucid_compatible_arity?(args, block)
|
58
|
+
instance_exec do
|
59
|
+
ari = block.arity
|
60
|
+
ari = ari < 0 ? (ari.abs-1).to_s + '+' : ari
|
61
|
+
s1 = ari == 1 ? '' : 's'
|
62
|
+
s2 = args.length == 1 ? '' : 's'
|
63
|
+
raise Lucid::ArityMismatchError.new(
|
64
|
+
"Your block takes #{ari} argument#{s1}, but the expression matched #{args.length} argument#{s2}."
|
65
|
+
)
|
66
|
+
end
|
67
|
+
else
|
68
|
+
instance_exec(*args, &block)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def lucid_compatible_arity?(args, block)
|
76
|
+
return true if block.arity == args.length
|
77
|
+
if block.arity < 0
|
78
|
+
return true if args.length >= (block.arity.abs - 1)
|
79
|
+
end
|
80
|
+
false
|
81
|
+
end
|
82
|
+
|
83
|
+
def lucid_run_with_backtrace_filtering(pseudo_method)
|
84
|
+
begin
|
85
|
+
yield
|
86
|
+
rescue Exception => e
|
87
|
+
instance_exec_invocation_line = "#{__FILE__}:#{__LINE__ - 2}:in `lucid_run_with_backtrace_filtering'"
|
88
|
+
replace_instance_exec_invocation_line!((e.backtrace || []), instance_exec_invocation_line, pseudo_method)
|
89
|
+
raise e
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
INSTANCE_EXEC_OFFSET = (Lucid::RUBY_2_0 || Lucid::RUBY_1_9 || Lucid::JRUBY) ? -3 : -4
|
94
|
+
|
95
|
+
def replace_instance_exec_invocation_line!(backtrace, instance_exec_invocation_line, pseudo_method)
|
96
|
+
return if Lucid.use_full_backtrace
|
97
|
+
|
98
|
+
instance_exec_pos = backtrace.index(instance_exec_invocation_line)
|
99
|
+
if instance_exec_pos
|
100
|
+
replacement_line = instance_exec_pos + INSTANCE_EXEC_OFFSET
|
101
|
+
backtrace[replacement_line].gsub!(/`.*'/, "`#{pseudo_method}'") if pseudo_method
|
102
|
+
|
103
|
+
depth = backtrace.count { |line| line == instance_exec_invocation_line }
|
104
|
+
end_pos = depth > 1 ? instance_exec_pos : -1
|
105
|
+
|
106
|
+
backtrace[replacement_line+1..end_pos] = nil
|
107
|
+
backtrace.compact!
|
108
|
+
else
|
109
|
+
# Not sure what should happen here.
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -1,11 +1,9 @@
|
|
1
1
|
require 'lucid/factory'
|
2
2
|
require 'lucid/ast/multiline_argument'
|
3
|
-
require 'lucid/
|
3
|
+
require 'lucid/facade'
|
4
4
|
|
5
5
|
module Lucid
|
6
|
-
|
7
|
-
class Runtime
|
8
|
-
|
6
|
+
class ContextLoader
|
9
7
|
class Orchestrator
|
10
8
|
|
11
9
|
require 'forwardable'
|
@@ -20,7 +18,7 @@ module Lucid
|
|
20
18
|
end
|
21
19
|
|
22
20
|
def step(step)
|
23
|
-
@orchestrator.invoke(step.name, AST::MultilineArgument.from(step.doc_string || step.rows))
|
21
|
+
@orchestrator.invoke(step.name, Lucid::AST::MultilineArgument.from(step.doc_string || step.rows))
|
24
22
|
end
|
25
23
|
|
26
24
|
def eof
|
@@ -29,16 +27,16 @@ module Lucid
|
|
29
27
|
|
30
28
|
include Factory
|
31
29
|
|
32
|
-
def initialize(user_interface,
|
33
|
-
@
|
34
|
-
@runtime_facade =
|
30
|
+
def initialize(user_interface, context={})
|
31
|
+
@context = Context.parse(context)
|
32
|
+
@runtime_facade = ContextLoader::Facade.new(self, user_interface)
|
35
33
|
@unsupported_languages = []
|
36
34
|
@supported_languages = []
|
37
35
|
@language_map = {}
|
38
36
|
end
|
39
37
|
|
40
|
-
def configure(
|
41
|
-
@
|
38
|
+
def configure(new_context)
|
39
|
+
@context = Context.parse(new_context)
|
42
40
|
end
|
43
41
|
|
44
42
|
# Invokes a series of steps +steps_text+. Example:
|
@@ -66,19 +64,24 @@ module Lucid
|
|
66
64
|
# The orchestrator will register the the code language and load up an
|
67
65
|
# implementation of that language. There is a provision to make sure
|
68
66
|
# that the language is not already registered.
|
69
|
-
|
70
|
-
|
71
|
-
|
67
|
+
#
|
68
|
+
# @param type [String] the type of file, passed in as an extension value
|
69
|
+
# @return [Object] language class
|
70
|
+
def load_code_language(type)
|
71
|
+
return @language_map[type] if @language_map[type]
|
72
|
+
lucid_language = create_object_of("Lucid::Interface#{type.capitalize}::#{type.capitalize}Language")
|
72
73
|
language = lucid_language.new(@runtime_facade)
|
73
74
|
@supported_languages << language
|
74
|
-
@language_map[
|
75
|
+
@language_map[type] = language
|
75
76
|
language
|
76
77
|
end
|
77
78
|
|
78
79
|
# The orchestrator will load only the loadable execution context files.
|
79
80
|
# This is how the orchestrator will, quite literally, orchestrate the
|
80
81
|
# execution of specs with the code logic that supports those specs.
|
81
|
-
#
|
82
|
+
#
|
83
|
+
# @param files [Array] all files gathering for the execution context
|
84
|
+
# @see Lucid::ContextLoader.load_execution_context
|
82
85
|
def load_files(files)
|
83
86
|
log.info("Orchestrator Load Files:\n")
|
84
87
|
files.each do |file|
|
@@ -98,10 +101,10 @@ module Lucid
|
|
98
101
|
end.flatten
|
99
102
|
end
|
100
103
|
|
101
|
-
def matcher_text(step_keyword, step_name, multiline_arg_class)
|
104
|
+
def matcher_text(step_keyword, step_name, multiline_arg_class)
|
102
105
|
load_code_language('rb') if unknown_programming_language?
|
103
106
|
@supported_languages.map do |programming_language|
|
104
|
-
programming_language.matcher_text(step_keyword, step_name, multiline_arg_class, @
|
107
|
+
programming_language.matcher_text(step_keyword, step_name, multiline_arg_class, @context.matcher_type)
|
105
108
|
end.join("\n")
|
106
109
|
end
|
107
110
|
|
@@ -131,7 +134,7 @@ module Lucid
|
|
131
134
|
end.flatten
|
132
135
|
end
|
133
136
|
|
134
|
-
def step_match(step_name, name_to_report=nil)
|
137
|
+
def step_match(step_name, name_to_report=nil)
|
135
138
|
@match_cache ||= {}
|
136
139
|
|
137
140
|
match = @match_cache[[step_name, name_to_report]]
|
@@ -151,7 +154,7 @@ module Lucid
|
|
151
154
|
end
|
152
155
|
|
153
156
|
def guess_step_matches?
|
154
|
-
@
|
157
|
+
@context.guess?
|
155
158
|
end
|
156
159
|
|
157
160
|
def matches(step_name, name_to_report)
|
@@ -160,7 +163,7 @@ module Lucid
|
|
160
163
|
end.flatten
|
161
164
|
end
|
162
165
|
|
163
|
-
def best_matches(step_name, step_matches)
|
166
|
+
def best_matches(step_name, step_matches)
|
164
167
|
no_groups = step_matches.select {|step_match| step_match.args.length == 0}
|
165
168
|
max_arg_length = step_matches.map {|step_match| step_match.args.length }.max
|
166
169
|
top_groups = step_matches.select {|step_match| step_match.args.length == max_arg_length }
|
@@ -178,12 +181,16 @@ module Lucid
|
|
178
181
|
|
179
182
|
# For each execution context file, the orchestrator will determine the
|
180
183
|
# code language associated with the file.
|
184
|
+
#
|
185
|
+
# @param file [String] relative path/file reference from the spec repo
|
181
186
|
def load_file(file)
|
182
|
-
|
187
|
+
language = get_language_for(file)
|
188
|
+
|
189
|
+
if language.nil?
|
190
|
+
log.info(" * #{file} [NOT SUPPORTED]\n")
|
191
|
+
else
|
183
192
|
log.info(" * #{file}\n")
|
184
193
|
language.load_code_file(file)
|
185
|
-
else
|
186
|
-
log.info(" * #{file} [NOT SUPPORTED]\n")
|
187
194
|
end
|
188
195
|
end
|
189
196
|
|
@@ -197,8 +204,13 @@ module Lucid
|
|
197
204
|
# of a supported language. If an object is returned it will be an
|
198
205
|
# object of this sort:
|
199
206
|
# Lucid::InterfaceRb::RbLanguage
|
207
|
+
#
|
208
|
+
# @param file [String] relative path/file reference from the spec repo
|
209
|
+
# @return [nil or class] full class reference if found, nil otherwise
|
200
210
|
def get_language_for(file)
|
201
|
-
|
211
|
+
extension = File.extname(file)[1..-1]
|
212
|
+
|
213
|
+
if extension
|
202
214
|
return nil if @unsupported_languages.index(extension)
|
203
215
|
begin
|
204
216
|
load_code_language(extension)
|
data/lib/lucid/platform.rb
CHANGED
@@ -2,7 +2,7 @@ require 'rbconfig'
|
|
2
2
|
|
3
3
|
module Lucid
|
4
4
|
unless defined?(Lucid::VERSION)
|
5
|
-
VERSION = '0.
|
5
|
+
VERSION = '0.4.0'
|
6
6
|
BINARY = File.expand_path(File.dirname(__FILE__) + '/../../bin/lucid')
|
7
7
|
LIBDIR = File.expand_path(File.dirname(__FILE__) + '/../../lib')
|
8
8
|
JRUBY = defined?(JRUBY_VERSION)
|
@@ -1,18 +1,18 @@
|
|
1
1
|
module Lucid
|
2
|
-
class
|
2
|
+
class ContextLoader
|
3
3
|
|
4
4
|
class Results
|
5
|
-
def initialize(
|
6
|
-
@
|
5
|
+
def initialize(context)
|
6
|
+
@context = context
|
7
7
|
@inserted_steps = {}
|
8
8
|
@inserted_scenarios = {}
|
9
9
|
end
|
10
10
|
|
11
|
-
def configure(
|
12
|
-
@
|
11
|
+
def configure(new_context)
|
12
|
+
@context = Context.parse(new_context)
|
13
13
|
end
|
14
14
|
|
15
|
-
def step_visited(step)
|
15
|
+
def step_visited(step)
|
16
16
|
step_id = step.object_id
|
17
17
|
|
18
18
|
unless @inserted_steps.has_key?(step_id)
|
@@ -21,7 +21,7 @@ module Lucid
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
def scenario_visited(scenario)
|
24
|
+
def scenario_visited(scenario)
|
25
25
|
scenario_id = scenario.object_id
|
26
26
|
|
27
27
|
unless @inserted_scenarios.has_key?(scenario_id)
|
@@ -39,7 +39,7 @@ module Lucid
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
def scenarios(status = nil)
|
42
|
+
def scenarios(status = nil)
|
43
43
|
@scenarios ||= []
|
44
44
|
if(status)
|
45
45
|
@scenarios.select{|scenario| scenario.status == status}
|
@@ -49,11 +49,11 @@ module Lucid
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def failure?
|
52
|
-
if @
|
52
|
+
if @context.wip?
|
53
53
|
scenarios(:passed).any?
|
54
54
|
else
|
55
55
|
scenarios(:failed).any? || steps(:failed).any? ||
|
56
|
-
(@
|
56
|
+
(@context.strict? && (steps(:undefined).any? || steps(:pending).any?))
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|