clintegracon 0.4.1
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 +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +12 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +158 -0
- data/Rakefile +60 -0
- data/clintegracon.gemspec +34 -0
- data/lib/CLIntegracon.rb +9 -0
- data/lib/CLIntegracon/adapter/bacon.rb +208 -0
- data/lib/CLIntegracon/configuration.rb +67 -0
- data/lib/CLIntegracon/diff.rb +81 -0
- data/lib/CLIntegracon/file_tree_spec.rb +213 -0
- data/lib/CLIntegracon/file_tree_spec_context.rb +180 -0
- data/lib/CLIntegracon/formatter.rb +77 -0
- data/lib/CLIntegracon/subject.rb +128 -0
- data/lib/CLIntegracon/version.rb +3 -0
- data/spec/bacon/execution_output.txt +72 -0
- data/spec/bacon/spec_helper.rb +60 -0
- data/spec/fixtures/bin/coffeemaker.rb +58 -0
- data/spec/integration/coffeemaker_help/after/execution_output.txt +23 -0
- data/spec/integration/coffeemaker_help/before/.gitkeep +0 -0
- data/spec/integration/coffeemaker_no_milk/after/BlackEye.brewed-coffee +1 -0
- data/spec/integration/coffeemaker_no_milk/after/CaPheSuaDa.brewed-coffee +1 -0
- data/spec/integration/coffeemaker_no_milk/after/Coffeemakerfile.yml +5 -0
- data/spec/integration/coffeemaker_no_milk/after/RedTux.brewed-coffee +1 -0
- data/spec/integration/coffeemaker_no_milk/after/execution_output.txt +6 -0
- data/spec/integration/coffeemaker_no_milk/before/Coffeemakerfile.yml +5 -0
- data/spec/integration/coffeemaker_sweetner_honey/after/Affogato.brewed-coffee +2 -0
- data/spec/integration/coffeemaker_sweetner_honey/after/BlackEye.brewed-coffee +2 -0
- data/spec/integration/coffeemaker_sweetner_honey/after/Coffeemakerfile.yml +3 -0
- data/spec/integration/coffeemaker_sweetner_honey/after/RedTux.brewed-coffee +2 -0
- data/spec/integration/coffeemaker_sweetner_honey/after/execution_output.txt +4 -0
- data/spec/integration/coffeemaker_sweetner_honey/before/Coffeemakerfile.yml +3 -0
- data/spec/unit/adapter/bacon_spec.rb +187 -0
- data/spec/unit/configuration_spec.rb +72 -0
- metadata +176 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module CLIntegracon
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
# @return [Configuration]
|
8
|
+
# Get the shared configuration, set by {self.configure}.
|
9
|
+
attr_accessor :shared_config
|
10
|
+
|
11
|
+
# Set a new shared configuration
|
12
|
+
#
|
13
|
+
# @param [Block<() -> ()>] block
|
14
|
+
# the block which is evaluated on the new shared configuration
|
15
|
+
#
|
16
|
+
def configure(&block)
|
17
|
+
self.shared_config ||= Configuration.new
|
18
|
+
shared_config.instance_eval &block
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
class Configuration
|
24
|
+
|
25
|
+
# Get the subject to configure it
|
26
|
+
#
|
27
|
+
# @return [Subject]
|
28
|
+
#
|
29
|
+
def subject
|
30
|
+
@subject ||= Subject.new()
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get the context to configure it
|
34
|
+
#
|
35
|
+
# @return [FileTreeSpecContext]
|
36
|
+
#
|
37
|
+
def context
|
38
|
+
@context ||= FileTreeSpecContext.new()
|
39
|
+
end
|
40
|
+
|
41
|
+
# Hook this gem in a test framework by a supported adapter
|
42
|
+
#
|
43
|
+
# @param [Symbol] test_framework
|
44
|
+
# the test framework
|
45
|
+
#
|
46
|
+
def hook_into test_framework
|
47
|
+
adapter = self.class.adapters[test_framework]
|
48
|
+
raise ArgumentError.new "No adapter for test framework #{test_framework}" if adapter.nil?
|
49
|
+
require adapter
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
# Get the file paths of supported adapter implementations by test framework
|
55
|
+
#
|
56
|
+
# @return [Hash<Symbol, String>]
|
57
|
+
# test framework to adapter implementation files
|
58
|
+
#
|
59
|
+
def self.adapters
|
60
|
+
adapter_dir = Pathname('../adapter').expand_path(__FILE__)
|
61
|
+
@adapters ||= Dir.chdir(adapter_dir) do
|
62
|
+
Hash[Dir['*.rb'].map { |path| [path.gsub(/\.rb$/, '').to_sym, adapter_dir + path] }]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'colored'
|
3
|
+
require 'diffy'
|
4
|
+
|
5
|
+
module CLIntegracon
|
6
|
+
class Diff
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
# @return [Pathname]
|
10
|
+
# the expected file
|
11
|
+
attr_reader :expected
|
12
|
+
|
13
|
+
# @return [Pathname]
|
14
|
+
# the produced file
|
15
|
+
attr_reader :produced
|
16
|
+
|
17
|
+
# @return [Pathname]
|
18
|
+
# the relative path to the expected file
|
19
|
+
attr_reader :relative_path
|
20
|
+
|
21
|
+
# @return [Proc<(Pathname)->(to_s)>]
|
22
|
+
# the proc, which transforms the files in a better comparable form
|
23
|
+
attr_accessor :preparator
|
24
|
+
|
25
|
+
# Init a new diff
|
26
|
+
#
|
27
|
+
# @param [Pathname] expected
|
28
|
+
# the expected file
|
29
|
+
#
|
30
|
+
# @param [Pathname] produced
|
31
|
+
# the produced file
|
32
|
+
#
|
33
|
+
# @param [Pathname] relative_path
|
34
|
+
# the relative path to the expected file
|
35
|
+
#
|
36
|
+
# @param [Block<(Pathname)->(to_s)>] preparator
|
37
|
+
# the block, which transforms the files in a better comparable form
|
38
|
+
#
|
39
|
+
def initialize(expected, produced, relative_path=nil, &preparator)
|
40
|
+
@expected = expected
|
41
|
+
@produced = produced
|
42
|
+
@relative_path = relative_path
|
43
|
+
preparator ||= Proc.new { |x| x } #id
|
44
|
+
self.preparator = preparator
|
45
|
+
end
|
46
|
+
|
47
|
+
def prepared_expected
|
48
|
+
@prepared_expected ||= preparator.call(expected)
|
49
|
+
end
|
50
|
+
|
51
|
+
def prepared_produced
|
52
|
+
@prepared_produced ||= preparator.call(produced)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Check if the produced output equals the expected
|
56
|
+
#
|
57
|
+
# @return [Bool]
|
58
|
+
# whether the expected is equal to the produced
|
59
|
+
#
|
60
|
+
def is_equal?
|
61
|
+
@is_equal ||= if prepared_expected.is_a? Pathname
|
62
|
+
FileUtils.compare_file(prepared_expected, prepared_produced)
|
63
|
+
else
|
64
|
+
prepared_expected == prepared_produced
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Enumerate all lines which differ.
|
69
|
+
#
|
70
|
+
# @param [Hash] options
|
71
|
+
# see Diffy#initialize for help.
|
72
|
+
#
|
73
|
+
# @return [Diffy::Diff]
|
74
|
+
#
|
75
|
+
def each(options = {}, &block)
|
76
|
+
options = { :source => 'files', :context => 3 }.merge options
|
77
|
+
Diffy::Diff.new(prepared_expected.to_s, prepared_produced.to_s, options).each &block
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'CLIntegracon/diff'
|
3
|
+
require 'CLIntegracon/formatter'
|
4
|
+
|
5
|
+
module CLIntegracon
|
6
|
+
class FileTreeSpec
|
7
|
+
|
8
|
+
# @return [FileTreeSpecContext]
|
9
|
+
# The context, which configures path and file behaviors
|
10
|
+
attr_reader :context
|
11
|
+
|
12
|
+
# @return [String]
|
13
|
+
# The concrete spec folder
|
14
|
+
attr_reader :spec_folder
|
15
|
+
|
16
|
+
# @return [Pathname]
|
17
|
+
# The concrete spec path
|
18
|
+
def spec_path
|
19
|
+
context.spec_path + spec_folder
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [Pathname]
|
23
|
+
# The concrete before directory for this spec
|
24
|
+
def before_path
|
25
|
+
spec_path + context.before_dir
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Pathname]
|
29
|
+
# The concrete after directory for this spec
|
30
|
+
def after_path
|
31
|
+
spec_path + context.after_dir
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Pathname]
|
35
|
+
# The concrete temp directory for this spec
|
36
|
+
def temp_path
|
37
|
+
context.temp_path + spec_folder
|
38
|
+
end
|
39
|
+
|
40
|
+
# Init a spec with a given context
|
41
|
+
#
|
42
|
+
# @param [FileTreeSpecContext] context
|
43
|
+
# The context, which configures path and file behaviors
|
44
|
+
#
|
45
|
+
# @param [String] spec_folder
|
46
|
+
# The concrete spec folder
|
47
|
+
#
|
48
|
+
def initialize(context, spec_folder)
|
49
|
+
@context = context
|
50
|
+
@spec_folder = spec_folder
|
51
|
+
end
|
52
|
+
|
53
|
+
# Run this spec
|
54
|
+
#
|
55
|
+
# @param [Block<(FileTreeSpec)->()>] block
|
56
|
+
# The block, which will be executed after chdir into the created temporary
|
57
|
+
# directory. In this block you will likely run your modifications to the
|
58
|
+
# file system and use the received FileTreeSpec instance to make asserts
|
59
|
+
# with the test framework of your choice.
|
60
|
+
#
|
61
|
+
def run(&block)
|
62
|
+
prepare!
|
63
|
+
|
64
|
+
copy_files!
|
65
|
+
|
66
|
+
Dir.chdir(temp_path) do
|
67
|
+
block.call self
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Compares the expected and produced directory by using the rules
|
72
|
+
# defined in the context
|
73
|
+
#
|
74
|
+
# @param [Block<(Diff)->()>] diff_block
|
75
|
+
# The block, where you will likely define a test for each file to compare.
|
76
|
+
# It will receive a Diff of each of the expected and produced files.
|
77
|
+
#
|
78
|
+
def compare(&diff_block)
|
79
|
+
transform_paths!
|
80
|
+
|
81
|
+
glob_all(after_path).each do |relative_path|
|
82
|
+
expected = after_path + relative_path
|
83
|
+
|
84
|
+
next unless expected.file?
|
85
|
+
|
86
|
+
block = special_behavior_for_path relative_path
|
87
|
+
next if block == context.class.nop
|
88
|
+
|
89
|
+
diff = diff_files(expected, relative_path)
|
90
|
+
diff.preparator = block unless block.nil?
|
91
|
+
|
92
|
+
diff_block.call diff
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Compares the expected and produced directory by using the rules
|
97
|
+
# defined in the context for unexpected files.
|
98
|
+
#
|
99
|
+
# This is separate because you probably don't want to define an extra
|
100
|
+
# test case for each file, which wasn't expected at all. So you can
|
101
|
+
# keep your test cases consistent.
|
102
|
+
#
|
103
|
+
# @param [Block<(Array)->()>] diff_block
|
104
|
+
# The block, where you will likely define a test that no unexpected files exists.
|
105
|
+
# It will receive an Array.
|
106
|
+
#
|
107
|
+
def check_unexpected_files(&block)
|
108
|
+
expected_files = glob_all after_path
|
109
|
+
produced_files = glob_all
|
110
|
+
unexpected_files = produced_files - expected_files
|
111
|
+
|
112
|
+
# Select only files
|
113
|
+
unexpected_files.reject! { |path| !path.file? }
|
114
|
+
|
115
|
+
# Filter ignored paths
|
116
|
+
unexpected_files.reject! { |path| special_behavior_for_path(path) == context.class.nop }
|
117
|
+
|
118
|
+
block.call unexpected_files
|
119
|
+
end
|
120
|
+
|
121
|
+
# Return a Formatter
|
122
|
+
#
|
123
|
+
# @return [Formatter]
|
124
|
+
#
|
125
|
+
def formatter
|
126
|
+
@formatter ||= Formatter.new(self)
|
127
|
+
end
|
128
|
+
|
129
|
+
protected
|
130
|
+
|
131
|
+
# Prepare the temporary directory
|
132
|
+
#
|
133
|
+
def prepare!
|
134
|
+
context.prepare!
|
135
|
+
|
136
|
+
temp_path.rmtree if temp_path.exist?
|
137
|
+
temp_path.mkdir
|
138
|
+
end
|
139
|
+
|
140
|
+
# Copies the before subdirectory of the given tests folder in the temporary
|
141
|
+
# directory.
|
142
|
+
#
|
143
|
+
def copy_files!
|
144
|
+
source = before_path
|
145
|
+
destination = temp_path
|
146
|
+
FileUtils.cp_r("#{source}/.", destination)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Applies the in the context configured transformations.
|
150
|
+
#
|
151
|
+
def transform_paths!
|
152
|
+
context.transform_paths.each do |path, block|
|
153
|
+
Dir.glob(path) do |produced_path|
|
154
|
+
produced = Pathname(produced_path)
|
155
|
+
block.call(produced)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Searches recursively for all files and take care for including hidden files
|
161
|
+
# if this is configured in the context.
|
162
|
+
#
|
163
|
+
# @param [String] path
|
164
|
+
# The relative or absolute path to search in (optional)
|
165
|
+
#
|
166
|
+
# @return [Array<Pathname>]
|
167
|
+
#
|
168
|
+
def glob_all(path=nil)
|
169
|
+
Dir.chdir path || '.' do
|
170
|
+
Dir.glob("**/*", context.include_hidden_files? ? File::FNM_DOTMATCH : 0).map { |path|
|
171
|
+
Pathname(path)
|
172
|
+
}
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Find the special behavior for a given path
|
177
|
+
#
|
178
|
+
# @return [Block<(Pathname) -> to_s>]
|
179
|
+
# This block takes the Pathname and transforms the file in a better comparable
|
180
|
+
# state. If it returns nil, the file is ignored.
|
181
|
+
#
|
182
|
+
def special_behavior_for_path(path)
|
183
|
+
context.special_paths.each do |key, block|
|
184
|
+
matched = if key.is_a?(Regexp)
|
185
|
+
path.to_s.match(key)
|
186
|
+
else
|
187
|
+
File.fnmatch(key, path)
|
188
|
+
end
|
189
|
+
next unless matched
|
190
|
+
return block
|
191
|
+
end
|
192
|
+
return nil
|
193
|
+
end
|
194
|
+
|
195
|
+
# Compares two files to check if they are identical and produces a clear diff
|
196
|
+
# to highlight the differences.
|
197
|
+
#
|
198
|
+
# @param [Pathname] expected
|
199
|
+
# The file in the after directory
|
200
|
+
#
|
201
|
+
# @param [Pathname] relative_path
|
202
|
+
# The file in the temp directory
|
203
|
+
#
|
204
|
+
# @return [Diff]
|
205
|
+
# An object holding a diff
|
206
|
+
#
|
207
|
+
def diff_files(expected, relative_path)
|
208
|
+
produced = temp_path + relative_path
|
209
|
+
Diff.new(expected, produced, relative_path)
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'CLIntegracon/file_tree_spec'
|
2
|
+
|
3
|
+
module CLIntegracon
|
4
|
+
class FileTreeSpecContext
|
5
|
+
|
6
|
+
#-----------------------------------------------------------------------------#
|
7
|
+
|
8
|
+
# @!group Attributes
|
9
|
+
|
10
|
+
# @return [Pathname]
|
11
|
+
# The relative path to the integration specs
|
12
|
+
attr_accessor :spec_path
|
13
|
+
|
14
|
+
# @return [Pathname]
|
15
|
+
# The relative path from a concrete spec directory to the directory containing the input files,
|
16
|
+
# which will be available at execution
|
17
|
+
attr_accessor :before_dir
|
18
|
+
|
19
|
+
# @return [Pathname]
|
20
|
+
# The relative path from a concrete spec directory to the directory containing the expected files after
|
21
|
+
# the execution
|
22
|
+
attr_accessor :after_dir
|
23
|
+
|
24
|
+
# @return [Pathname]
|
25
|
+
# The relative path to the directory containing the produced files after the
|
26
|
+
# execution. This must not be the same as the before_dir or the after_dir.
|
27
|
+
#
|
28
|
+
# @note **Attention**: This path will been deleted before running to ensure a clean sandbox for testing.
|
29
|
+
#
|
30
|
+
attr_accessor :temp_path
|
31
|
+
|
32
|
+
# @return [Hash<String,Block>]
|
33
|
+
# the special paths of files, which need to be transformed in a better comparable form
|
34
|
+
attr_accessor :transform_paths
|
35
|
+
|
36
|
+
# @return [Hash<String,Block>]
|
37
|
+
# the special paths of files, where an individual file diff handling is needed
|
38
|
+
attr_accessor :special_paths
|
39
|
+
|
40
|
+
# @return [Bool]
|
41
|
+
# whether to include hidden files, when searching directories (true by default)
|
42
|
+
attr_accessor :include_hidden_files
|
43
|
+
alias :include_hidden_files? :include_hidden_files
|
44
|
+
|
45
|
+
|
46
|
+
#-----------------------------------------------------------------------------#
|
47
|
+
|
48
|
+
# @!group Initializer
|
49
|
+
|
50
|
+
# "Designated" initializer
|
51
|
+
#
|
52
|
+
# @param [Hash<Symbol,String>] properties
|
53
|
+
# The configuration parameter (optional):
|
54
|
+
# :spec_path => see self.spec_path
|
55
|
+
# :before_dir => see self.before_dir
|
56
|
+
# :after_dir => see self.after_dir
|
57
|
+
# :temp_path => see self.temp_path
|
58
|
+
#
|
59
|
+
def initialize(properties={})
|
60
|
+
self.spec_path = properties[:spec_path] || '.'
|
61
|
+
self.temp_path = properties[:temp_path] || 'tmp'
|
62
|
+
self.before_dir = properties[:before_dir] || 'before'
|
63
|
+
self.after_dir = properties[:after_dir] || 'after'
|
64
|
+
self.transform_paths = {}
|
65
|
+
self.special_paths = {}
|
66
|
+
self.include_hidden_files = true
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
#-----------------------------------------------------------------------------#
|
71
|
+
|
72
|
+
# @!group Helper
|
73
|
+
|
74
|
+
# This value is used for ignored paths
|
75
|
+
#
|
76
|
+
# @return [Proc]
|
77
|
+
# Does nothing
|
78
|
+
def self.nop
|
79
|
+
@nop ||= Proc.new {}
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
#-----------------------------------------------------------------------------#
|
84
|
+
|
85
|
+
# @!group Setter
|
86
|
+
|
87
|
+
def spec_path=(spec_path)
|
88
|
+
# Spec dir has to exist.
|
89
|
+
@spec_path= Pathname(spec_path).realpath
|
90
|
+
end
|
91
|
+
|
92
|
+
def temp_path=(temp_path)
|
93
|
+
# Temp dir, doesn't have to exist itself, it will been created, but let's ensure
|
94
|
+
# that at least the last but one path component exist.
|
95
|
+
raise "temp_path's parent directory doesn't exist" unless (Pathname(temp_path) + '..').exist?
|
96
|
+
@temp_path = Pathname(temp_path).realpath
|
97
|
+
end
|
98
|
+
|
99
|
+
def before_dir=(before_dir)
|
100
|
+
@before_dir = Pathname(before_dir)
|
101
|
+
end
|
102
|
+
|
103
|
+
def after_dir=(after_dir)
|
104
|
+
@after_dir = Pathname(after_dir)
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
#-----------------------------------------------------------------------------#
|
109
|
+
|
110
|
+
# @!group DSL-like Setter
|
111
|
+
|
112
|
+
# Registers a block for special handling certain files, matched with globs.
|
113
|
+
# Multiple transformers can match a single file.
|
114
|
+
#
|
115
|
+
# @param [String...] file_paths
|
116
|
+
# The file path(s) of the files, which were created/changed and need transformation
|
117
|
+
#
|
118
|
+
# @param [Block<(Pathname) -> ()>] block
|
119
|
+
# The block, which takes each of the matched files, transforms it if needed
|
120
|
+
# in a better comparable form in the temporary path, so that the temporary
|
121
|
+
# will be compared to a given after file, or makes appropriate expects, which
|
122
|
+
# depend on the used test framework
|
123
|
+
#
|
124
|
+
def transform_produced(*file_paths, &block)
|
125
|
+
file_paths.each do |file_path|
|
126
|
+
self.transform_paths[file_path] = block
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Registers a block for special handling certain files, matched with globs.
|
131
|
+
# Registered file paths will be excluded from default comparison by `diff`.
|
132
|
+
# Multiple special handlers can match a single file.
|
133
|
+
#
|
134
|
+
# @param [String|Regexp...] file_paths
|
135
|
+
# The file path(s) of the files, which were created/changed and need special comparison
|
136
|
+
#
|
137
|
+
# @param [Block<(Pathname) -> (String)>] block
|
138
|
+
# The block, which takes each of the matched files, transforms it if needed
|
139
|
+
# in a better comparable form.
|
140
|
+
#
|
141
|
+
def has_special_handling_for(*file_paths, &block)
|
142
|
+
file_paths.each do |file_path|
|
143
|
+
self.special_paths[file_path] = block
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Copies the before subdirectory of the given tests folder in the temporary
|
148
|
+
# directory.
|
149
|
+
#
|
150
|
+
# @param [String|RegExp...] file_path
|
151
|
+
# the file path of the files, which were changed and need special comparison
|
152
|
+
#
|
153
|
+
def ignores(*file_path)
|
154
|
+
has_special_handling_for *file_path, &self.class.nop
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
#-----------------------------------------------------------------------------#
|
159
|
+
|
160
|
+
# @!group Interaction
|
161
|
+
|
162
|
+
# Prepare the temporary directory
|
163
|
+
#
|
164
|
+
def prepare!
|
165
|
+
temp_path.mkpath
|
166
|
+
end
|
167
|
+
|
168
|
+
# Get a specific spec with given folder to run it
|
169
|
+
#
|
170
|
+
# @param [String] folder
|
171
|
+
# The name of the folder of the tests
|
172
|
+
#
|
173
|
+
# @return [FileTreeSpec]
|
174
|
+
#
|
175
|
+
def spec(spec_folder)
|
176
|
+
FileTreeSpec.new(self, spec_folder)
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
end
|