spectre-core 1.11.0 → 1.12.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/exe/spectre +516 -524
- data/lib/spectre/assertion.rb +33 -14
- data/lib/spectre/bag.rb +21 -19
- data/lib/spectre/curl.rb +397 -391
- data/lib/spectre/diagnostic.rb +39 -29
- data/lib/spectre/environment.rb +30 -26
- data/lib/spectre/helpers.rb +133 -134
- data/lib/spectre/http/basic_auth.rb +5 -2
- data/lib/spectre/http/keystone.rb +76 -73
- data/lib/spectre/http.rb +82 -76
- data/lib/spectre/logger/console.rb +143 -142
- data/lib/spectre/logger/file.rb +1 -1
- data/lib/spectre/logger.rb +3 -1
- data/lib/spectre/mixin.rb +58 -34
- data/lib/spectre/reporter/console.rb +102 -104
- data/lib/spectre/reporter/junit.rb +100 -100
- data/lib/spectre/resources.rb +49 -46
- data/lib/spectre.rb +440 -438
- metadata +3 -3
data/lib/spectre.rb
CHANGED
@@ -1,438 +1,440 @@
|
|
1
|
-
module Spectre
|
2
|
-
module Version
|
3
|
-
MAJOR = 1
|
4
|
-
MINOR =
|
5
|
-
TINY = 0
|
6
|
-
end
|
7
|
-
|
8
|
-
VERSION = [Version::MAJOR, Version::MINOR, Version::TINY].compact * '.'
|
9
|
-
|
10
|
-
|
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
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
@
|
77
|
-
@
|
78
|
-
@
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
@
|
94
|
-
@
|
95
|
-
@
|
96
|
-
@
|
97
|
-
@
|
98
|
-
@
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
@
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
def
|
122
|
-
|
123
|
-
end
|
124
|
-
|
125
|
-
def
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
end
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
run_info.
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
def
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
end
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
end
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
def
|
400
|
-
|
401
|
-
end
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
1
|
+
module Spectre
|
2
|
+
module Version
|
3
|
+
MAJOR = 1
|
4
|
+
MINOR = 12
|
5
|
+
TINY = 0
|
6
|
+
end
|
7
|
+
|
8
|
+
VERSION = [Version::MAJOR, Version::MINOR, Version::TINY].compact * '.'
|
9
|
+
|
10
|
+
|
11
|
+
class ::Hash
|
12
|
+
def deep_merge!(second)
|
13
|
+
merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge!(v2, &merger) : v2 }
|
14
|
+
self.merge!(second, &merger)
|
15
|
+
end
|
16
|
+
|
17
|
+
def deep_clone
|
18
|
+
Marshal.load(Marshal.dump(self))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class ::Object
|
23
|
+
def to_h
|
24
|
+
self.instance_variables.each_with_object({}) do |var, hash|
|
25
|
+
hash[var.to_s.delete("@")] = self.instance_variable_get(var)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
###########################################
|
32
|
+
# Custom Exceptions
|
33
|
+
###########################################
|
34
|
+
|
35
|
+
|
36
|
+
class ExpectationFailure < Exception
|
37
|
+
attr_reader :expectation
|
38
|
+
|
39
|
+
def initialize message, expectation
|
40
|
+
super message
|
41
|
+
@expectation = expectation
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
###########################################
|
47
|
+
# Internal Classes
|
48
|
+
###########################################
|
49
|
+
|
50
|
+
|
51
|
+
# https://www.dan-manges.com/blog/ruby-dsls-instance-eval-with-delegation
|
52
|
+
class DslClass
|
53
|
+
def _evaluate &block
|
54
|
+
@__bound_self__ = eval('self', block.binding)
|
55
|
+
instance_eval(&block)
|
56
|
+
end
|
57
|
+
|
58
|
+
def _execute *args, &block
|
59
|
+
@__bound_self__ = eval('self', block.binding)
|
60
|
+
instance_exec(*args, &block)
|
61
|
+
end
|
62
|
+
|
63
|
+
def method_missing method, *args, **kwargs, &block
|
64
|
+
if @__bound_self__.respond_to? method
|
65
|
+
@__bound_self__.send(method, *args, **kwargs, &block)
|
66
|
+
else
|
67
|
+
Delegator.redirect(method, *args, **kwargs, &block)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class Subject
|
73
|
+
attr_reader :name, :desc, :specs
|
74
|
+
|
75
|
+
def initialize desc
|
76
|
+
@desc = desc
|
77
|
+
@specs = []
|
78
|
+
@name = desc.downcase.gsub(/[^a-z0-9]+/, '_')
|
79
|
+
end
|
80
|
+
|
81
|
+
def add_spec desc, tags, data, block, context, file
|
82
|
+
name = @name + '-' + (@specs.length+1).to_s
|
83
|
+
@specs << Spec.new(name, self, desc, tags, data, block, context, file)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class Spec
|
88
|
+
attr_reader :name, :subject, :context, :desc, :tags, :data, :block, :file
|
89
|
+
|
90
|
+
def initialize name, subject, desc, tags, data, block, context, file
|
91
|
+
@name = name
|
92
|
+
@context = context
|
93
|
+
@data = data
|
94
|
+
@subject = subject
|
95
|
+
@desc = desc
|
96
|
+
@tags = tags
|
97
|
+
@block = block
|
98
|
+
@file = file
|
99
|
+
end
|
100
|
+
|
101
|
+
def full_desc
|
102
|
+
@subject.desc + ' ' + desc
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class RunInfo
|
107
|
+
attr_accessor :spec, :data, :started, :finished, :error, :failure, :skipped, :log, :properties
|
108
|
+
|
109
|
+
def initialize spec, data=nil
|
110
|
+
@spec = spec
|
111
|
+
@data = data
|
112
|
+
@started = nil
|
113
|
+
@finished = nil
|
114
|
+
@error = nil
|
115
|
+
@failure = nil
|
116
|
+
@skipped = false
|
117
|
+
@log = []
|
118
|
+
@properties = {}
|
119
|
+
end
|
120
|
+
|
121
|
+
def duration
|
122
|
+
@finished - @started
|
123
|
+
end
|
124
|
+
|
125
|
+
def skipped?
|
126
|
+
@skipped
|
127
|
+
end
|
128
|
+
|
129
|
+
def failed?
|
130
|
+
@error != nil
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class Runner
|
135
|
+
@@current
|
136
|
+
|
137
|
+
def self.current
|
138
|
+
@@current
|
139
|
+
end
|
140
|
+
|
141
|
+
def run specs
|
142
|
+
runs = []
|
143
|
+
|
144
|
+
specs.group_by { |x| x.subject }.each do |subject, spec_group|
|
145
|
+
Logger.log_subject subject do
|
146
|
+
spec_group.group_by { |x| x.context }.each do |context, specs|
|
147
|
+
Logger.log_context(context) do
|
148
|
+
runs.concat run_context(context, specs)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
runs
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
158
|
+
|
159
|
+
def run_context context, specs
|
160
|
+
runs = []
|
161
|
+
|
162
|
+
if context.__setup_blocks.count > 0
|
163
|
+
setup_run = run_blocks('setup', context, context.__setup_blocks)
|
164
|
+
runs << setup_run
|
165
|
+
return runs if setup_run.error or setup_run.failure
|
166
|
+
end
|
167
|
+
|
168
|
+
begin
|
169
|
+
specs.each do |spec|
|
170
|
+
raise "Multi data definition (`with' parameter) of '#{spec.subject.desc} #{spec.desc}' has to be an `Array'" unless !spec.data.nil? and spec.data.is_a? Array
|
171
|
+
|
172
|
+
if spec.data.any?
|
173
|
+
spec.data
|
174
|
+
.map { |x| x.is_a?(Hash) ? OpenStruct.new(x) : x }
|
175
|
+
.each do |data|
|
176
|
+
Logger.log_spec(spec, data) do
|
177
|
+
runs << run_spec(spec, data)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
else
|
181
|
+
Logger.log_spec(spec) do
|
182
|
+
runs << run_spec(spec)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
ensure
|
187
|
+
if context.__teardown_blocks.count > 0
|
188
|
+
runs << run_blocks('teardown', context, context.__teardown_blocks)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
runs
|
193
|
+
end
|
194
|
+
|
195
|
+
def run_blocks name, context, blocks
|
196
|
+
ctx = SpecContext.new context.__subject, name
|
197
|
+
spec = Spec.new name, context.__subject, name, [], nil, nil, ctx, nil
|
198
|
+
|
199
|
+
run_info = RunInfo.new spec
|
200
|
+
|
201
|
+
@@current = run_info
|
202
|
+
|
203
|
+
run_info.started = Time.now
|
204
|
+
|
205
|
+
Logger.log_context ctx do
|
206
|
+
begin
|
207
|
+
blocks.each do |block|
|
208
|
+
block.call
|
209
|
+
end
|
210
|
+
|
211
|
+
run_info.finished = Time.now
|
212
|
+
rescue ExpectationFailure => e
|
213
|
+
run_info.failure = e
|
214
|
+
rescue Exception => e
|
215
|
+
run_info.error = e
|
216
|
+
Logger.log_error spec, e
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
run_info.finished = Time.now
|
221
|
+
|
222
|
+
@@current = nil
|
223
|
+
|
224
|
+
run_info
|
225
|
+
end
|
226
|
+
|
227
|
+
def run_spec spec, data=nil
|
228
|
+
run_info = RunInfo.new(spec, data)
|
229
|
+
|
230
|
+
@@current = run_info
|
231
|
+
|
232
|
+
run_info.started = Time.now
|
233
|
+
|
234
|
+
begin
|
235
|
+
if spec.context.__before_blocks.count > 0
|
236
|
+
before_ctx = SpecContext.new(spec.subject, 'before')
|
237
|
+
|
238
|
+
Logger.log_context before_ctx do
|
239
|
+
spec.context.__before_blocks.each do |block|
|
240
|
+
block.call(data)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
spec.block.call(data)
|
246
|
+
rescue ExpectationFailure => e
|
247
|
+
run_info.failure = e
|
248
|
+
rescue Interrupt
|
249
|
+
run_info.skipped = true
|
250
|
+
Logger.log_skipped spec
|
251
|
+
rescue Exception => e
|
252
|
+
run_info.error = e
|
253
|
+
Logger.log_error spec, e
|
254
|
+
ensure
|
255
|
+
if spec.context.__after_blocks.count > 0
|
256
|
+
after_ctx = SpecContext.new(spec.subject, 'after')
|
257
|
+
|
258
|
+
Logger.log_context after_ctx do
|
259
|
+
begin
|
260
|
+
spec.context.__after_blocks.each do |block|
|
261
|
+
block.call
|
262
|
+
end
|
263
|
+
|
264
|
+
run_info.finished = Time.now
|
265
|
+
rescue ExpectationFailure => e
|
266
|
+
run_info.failure = e
|
267
|
+
rescue Exception => e
|
268
|
+
run_info.error = e
|
269
|
+
Logger.log_error spec, e
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
run_info.finished = Time.now
|
276
|
+
|
277
|
+
@@current = nil
|
278
|
+
|
279
|
+
run_info
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
|
284
|
+
###########################################
|
285
|
+
# DSL Classes
|
286
|
+
###########################################
|
287
|
+
|
288
|
+
|
289
|
+
class SpecContext < DslClass
|
290
|
+
attr_reader :__subject, :__desc, :__before_blocks, :__after_blocks, :__setup_blocks, :__teardown_blocks
|
291
|
+
|
292
|
+
def initialize subject, desc=nil
|
293
|
+
@__subject = subject
|
294
|
+
@__desc = desc
|
295
|
+
|
296
|
+
@__before_blocks = []
|
297
|
+
@__after_blocks = []
|
298
|
+
@__setup_blocks = []
|
299
|
+
@__teardown_blocks = []
|
300
|
+
end
|
301
|
+
|
302
|
+
def it desc, tags: [], with: [], &block
|
303
|
+
# Get the file, where the spec is defined.
|
304
|
+
# Nasty, but it works
|
305
|
+
# Maybe there is another way, but this works for now
|
306
|
+
spec_file = nil
|
307
|
+
begin
|
308
|
+
raise
|
309
|
+
rescue => e
|
310
|
+
spec_file = e.backtrace
|
311
|
+
.select { |file| !file.include? 'lib/spectre' }
|
312
|
+
.first
|
313
|
+
.match(/(.*\.rb):\d+/)
|
314
|
+
.captures
|
315
|
+
.first
|
316
|
+
end
|
317
|
+
|
318
|
+
@__subject.add_spec(desc, tags, with, block, self, spec_file)
|
319
|
+
end
|
320
|
+
|
321
|
+
def before &block
|
322
|
+
@__before_blocks << block
|
323
|
+
end
|
324
|
+
|
325
|
+
def after &block
|
326
|
+
@__after_blocks << block
|
327
|
+
end
|
328
|
+
|
329
|
+
def setup &block
|
330
|
+
@__setup_blocks << block
|
331
|
+
end
|
332
|
+
|
333
|
+
def teardown &block
|
334
|
+
@__teardown_blocks << block
|
335
|
+
end
|
336
|
+
|
337
|
+
def context desc=nil, &block
|
338
|
+
ctx = SpecContext.new(@__subject, desc)
|
339
|
+
ctx._evaluate &block
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
|
344
|
+
###########################################
|
345
|
+
# Core Modules
|
346
|
+
###########################################
|
347
|
+
|
348
|
+
|
349
|
+
module Delegator
|
350
|
+
@@mappings = {}
|
351
|
+
|
352
|
+
def self.delegate(*methods, target)
|
353
|
+
methods.each do |method_name|
|
354
|
+
define_method(method_name) do |*args, &block|
|
355
|
+
return super(*args, &block) if respond_to? method_name
|
356
|
+
|
357
|
+
target.send(method_name, *args, &block)
|
358
|
+
end
|
359
|
+
|
360
|
+
@@mappings[method_name] = target
|
361
|
+
|
362
|
+
private method_name
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
def self.redirect method_name, *args, **kwargs, &block
|
367
|
+
target = @@mappings[method_name] || Kernel
|
368
|
+
raise "no variable or method '#{method_name}' found" unless target.respond_to? method_name
|
369
|
+
|
370
|
+
target.send(method_name, *args, **kwargs, &block)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
|
375
|
+
class << self
|
376
|
+
@@subjects = []
|
377
|
+
@@modules = []
|
378
|
+
|
379
|
+
attr_reader :file_log, :logger
|
380
|
+
|
381
|
+
|
382
|
+
def specs spec_filter=[], tags=[]
|
383
|
+
@@subjects
|
384
|
+
.map { |x| x.specs }
|
385
|
+
.flatten
|
386
|
+
.select do |spec|
|
387
|
+
(spec_filter.empty? or spec_filter.any? { |x| spec.name.match('^' + x.gsub('*', '.*') + '$') }) and (tags.empty? or tags.any? { |x| tag?(spec.tags, x) })
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
def tag? tags, tag_exp
|
392
|
+
tags = tags.map { |x| x.to_s }
|
393
|
+
all_tags = tag_exp.split '+'
|
394
|
+
included_tags = all_tags.select { |x| !x.start_with? '!' }
|
395
|
+
excluded_tags = all_tags.select { |x| x.start_with? '!' }.map { |x| x[1..-1] }
|
396
|
+
included_tags & tags == included_tags and excluded_tags & tags == []
|
397
|
+
end
|
398
|
+
|
399
|
+
def delegate *method_names, to: nil
|
400
|
+
Spectre::Delegator.delegate *method_names, to
|
401
|
+
end
|
402
|
+
|
403
|
+
def register &block
|
404
|
+
@@modules << block
|
405
|
+
end
|
406
|
+
|
407
|
+
def configure config
|
408
|
+
@@modules.each do |block|
|
409
|
+
block.call(config)
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
|
414
|
+
###########################################
|
415
|
+
# Global Functions
|
416
|
+
###########################################
|
417
|
+
|
418
|
+
|
419
|
+
def describe desc, &block
|
420
|
+
subject = @@subjects.find { |x| x.desc == desc }
|
421
|
+
|
422
|
+
unless subject
|
423
|
+
subject = Subject.new(desc)
|
424
|
+
@@subjects << subject
|
425
|
+
end
|
426
|
+
|
427
|
+
ctx = SpecContext.new(subject)
|
428
|
+
ctx._evaluate &block
|
429
|
+
end
|
430
|
+
|
431
|
+
def property key, val
|
432
|
+
Spectre::Runner.current.properties[key] = val
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
delegate :describe, :property, to: Spectre
|
437
|
+
end
|
438
|
+
|
439
|
+
|
440
|
+
extend Spectre::Delegator
|