lopata 0.0.15 → 0.0.16
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 +5 -5
- data/lib/lopata.rb +11 -0
- data/lib/lopata/config.rb +1 -1
- data/lib/lopata/generators/templates/config/initializers/capybara.rb +1 -1
- data/lib/lopata/rspec/version.rb +1 -1
- data/lib/lopata/scenario.rb +393 -0
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6cd685207d7486c0cda7d76e5bbb0276b5af0e0ff73446e4d6866b49e5a8dbc5
|
4
|
+
data.tar.gz: 54350543eddacf904531c311dd301858ae4cb42830edef735daed97417956b42
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34ac0e832ac235f88371a44773a9f2df2f87b8099d9857300761dcf4ba6fc81da2386988684253d80e2d4d423457cd6b60e59cf152895b44468276ae1c667284
|
7
|
+
data.tar.gz: e287063637735bf244f351396457f07857426fc568d269e6be6fd1de07f94df18c5bd29cca3aa53c8ffd1c81007f9c0b64c5cd4d96616d441f8f143611e300b7
|
data/lib/lopata.rb
CHANGED
@@ -1,2 +1,13 @@
|
|
1
1
|
require 'lopata/id'
|
2
2
|
require 'lopata/config'
|
3
|
+
require 'lopata/scenario'
|
4
|
+
|
5
|
+
module Lopata
|
6
|
+
def self.define(*args, &block)
|
7
|
+
Lopata::Scenario.define(*args, &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.xdefine(*args, &block)
|
11
|
+
Lopata::Scenario.xdefine(*args, &block)
|
12
|
+
end
|
13
|
+
end
|
data/lib/lopata/config.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
Capybara.default_driver = :selenium if defined? Capybara
|
1
|
+
Capybara.default_driver = :selenium if defined? Capybara
|
data/lib/lopata/rspec/version.rb
CHANGED
@@ -0,0 +1,393 @@
|
|
1
|
+
class Lopata::Scenario
|
2
|
+
def self.define(title = nil, metadata = nil, &block)
|
3
|
+
scenario = new
|
4
|
+
scenario.title(title) if title
|
5
|
+
scenario.metadata(metadata) if metadata
|
6
|
+
scenario.instance_exec &block
|
7
|
+
scenario.build_rspec
|
8
|
+
end
|
9
|
+
|
10
|
+
# Do noting. Exclude defined scenario from suite.
|
11
|
+
def self.xdefine(*attrs)
|
12
|
+
end
|
13
|
+
|
14
|
+
def build_rspec
|
15
|
+
option_combinations.each do |option_set|
|
16
|
+
args = prepare_args(option_set)
|
17
|
+
raise "scenario required a name in first argument" unless args.first.is_a? String
|
18
|
+
steps = @steps
|
19
|
+
spec = RSpec.describe(*args)
|
20
|
+
spec.send :extend, RSpecInjections
|
21
|
+
spec.nested_with_as(*args) do
|
22
|
+
steps.each do |block|
|
23
|
+
instance_exec &block
|
24
|
+
end
|
25
|
+
if Lopata::Config.after_scenario
|
26
|
+
instance_exec &Lopata::Config.after_scenario
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def as(*args, &block)
|
33
|
+
@roles = args.flatten
|
34
|
+
@roles << CalculatedValue.new(&block) if block_given?
|
35
|
+
@role_options = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def role_options
|
39
|
+
@role_options ||= build_role_options
|
40
|
+
end
|
41
|
+
|
42
|
+
def metadata(hash)
|
43
|
+
raise 'metadata expected to be a Hash' unless hash.is_a?(Hash)
|
44
|
+
@common_metadata ||= {}
|
45
|
+
@common_metadata.merge! hash
|
46
|
+
end
|
47
|
+
|
48
|
+
def without_user
|
49
|
+
@without_user = true
|
50
|
+
end
|
51
|
+
|
52
|
+
def skip_when(&block)
|
53
|
+
@skip_when = block
|
54
|
+
end
|
55
|
+
|
56
|
+
def skip?(option_set)
|
57
|
+
@skip_when && @skip_when.call(option_set)
|
58
|
+
end
|
59
|
+
|
60
|
+
%i{setup action it context teardown include_context include_examples}.each do |name|
|
61
|
+
name_if = "%s_if" % name
|
62
|
+
name_unless = "%s_unless" % name
|
63
|
+
define_method name, ->(*args, &block) { add_step(name, *args, &block) }
|
64
|
+
define_method name_if, ->(condition, *args, &block) { add_if_step(name, condition, *args, &block) }
|
65
|
+
define_method name_unless, ->(condition, *args, &block) { add_unless_step(name, condition, *args, &block) }
|
66
|
+
end
|
67
|
+
|
68
|
+
def cleanup(*args, &block)
|
69
|
+
add_step_as_is(:cleanup, *args, &block)
|
70
|
+
end
|
71
|
+
|
72
|
+
def add_step(method_name, *args, &block)
|
73
|
+
@steps ||= []
|
74
|
+
@steps << Proc.new do
|
75
|
+
# will be called in context of rspec group
|
76
|
+
flat_args = args.flatten
|
77
|
+
flat_args = Lopata::Scenario.separate_args(flat_args) if method_name =~ /^(setup|action)/
|
78
|
+
converted_args = Lopata::Scenario.convert_args(metadata, *flat_args)
|
79
|
+
send method_name, *converted_args, &block
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def add_if_step(method_name, condition, *args, &block)
|
84
|
+
@steps ||= []
|
85
|
+
@steps << Proc.new do
|
86
|
+
# will be called in context of rspec group
|
87
|
+
if match_metadata?(condition)
|
88
|
+
flat_args = args.flatten
|
89
|
+
flat_args = Lopata::Scenario.separate_args(flat_args) if method_name =~ /^(setup|action)/
|
90
|
+
converted_args = Lopata::Scenario.convert_args(metadata, *flat_args)
|
91
|
+
send method_name, *converted_args, &block
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def add_unless_step(method_name, condition, *args, &block)
|
97
|
+
@steps ||= []
|
98
|
+
@steps << Proc.new do
|
99
|
+
# will be called in context of rspec group
|
100
|
+
unless match_metadata?(condition)
|
101
|
+
flat_args = args.flatten
|
102
|
+
flat_args = Lopata::Scenario.separate_args(flat_args) if method_name =~ /^(setup|action)/
|
103
|
+
converted_args = Lopata::Scenario.convert_args(metadata, *flat_args)
|
104
|
+
send method_name, *converted_args, &block
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def add_step_as_is(method_name, *args, &block)
|
110
|
+
@steps ||= []
|
111
|
+
@steps << Proc.new do
|
112
|
+
# do not convert args - symbols mean name of instance variable
|
113
|
+
send method_name, *args, &block
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def let_metadata(*keys)
|
118
|
+
@steps ||= []
|
119
|
+
@steps << Proc.new do
|
120
|
+
m = metadata
|
121
|
+
keys.each do |key|
|
122
|
+
define_method key do
|
123
|
+
m[key]
|
124
|
+
end
|
125
|
+
|
126
|
+
define_singleton_method key do
|
127
|
+
m[key]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def let_method(method_name, &block)
|
134
|
+
@steps ||= []
|
135
|
+
@steps << Proc.new do
|
136
|
+
define_method method_name, &block
|
137
|
+
define_singleton_method method_name, &block
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def steps(&block)
|
142
|
+
@steps ||= []
|
143
|
+
@steps << block
|
144
|
+
end
|
145
|
+
|
146
|
+
def steps_if(metadata_key, &block)
|
147
|
+
@steps ||= []
|
148
|
+
@steps << Proc.new do
|
149
|
+
if match_metadata?(metadata_key)
|
150
|
+
instance_exec &block
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
alias steps_for steps_if
|
155
|
+
|
156
|
+
def steps_unless(metadata_key, &block)
|
157
|
+
@steps ||= []
|
158
|
+
@steps << Proc.new do
|
159
|
+
unless match_metadata?(metadata_key)
|
160
|
+
instance_exec &block
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
alias steps_for_not steps_unless
|
165
|
+
|
166
|
+
def self.convert_args(metadata, *args)
|
167
|
+
args.map do |arg|
|
168
|
+
case arg
|
169
|
+
# trait symbols as link to metadata.
|
170
|
+
when Symbol then metadata[arg]
|
171
|
+
else
|
172
|
+
arg
|
173
|
+
end
|
174
|
+
end.flatten
|
175
|
+
end
|
176
|
+
|
177
|
+
def self.separate_args(args)
|
178
|
+
args.map { |a| a.is_a?(String) && a =~ /,/ ? a.split(',').map(&:strip) : a }.flatten
|
179
|
+
end
|
180
|
+
|
181
|
+
def build_role_options
|
182
|
+
return [] unless roles
|
183
|
+
[Diagonal.new(:as, roles.map { |r| [nil, r] })]
|
184
|
+
end
|
185
|
+
|
186
|
+
def roles
|
187
|
+
return false if @without_user
|
188
|
+
@roles ||= [Lopata::Config.default_role].compact
|
189
|
+
end
|
190
|
+
|
191
|
+
def title(value)
|
192
|
+
@title = value
|
193
|
+
end
|
194
|
+
|
195
|
+
def option(metadata_key, variants)
|
196
|
+
options << Option.new(metadata_key, variants)
|
197
|
+
end
|
198
|
+
|
199
|
+
def diagonal(metadata_key, variants)
|
200
|
+
diagonals << Diagonal.new(metadata_key, variants)
|
201
|
+
end
|
202
|
+
|
203
|
+
def options
|
204
|
+
@options ||= []
|
205
|
+
end
|
206
|
+
|
207
|
+
def diagonals
|
208
|
+
@diagonals ||= []
|
209
|
+
end
|
210
|
+
|
211
|
+
def option_combinations
|
212
|
+
combinations = combine([OptionSet.new], options + diagonals + role_options)
|
213
|
+
while !(diagonals + role_options).all?(&:complete?)
|
214
|
+
combinations << OptionSet.new(*(options + diagonals + role_options).map(&:next_variant))
|
215
|
+
end
|
216
|
+
combinations.reject { |option_set| skip?(option_set) }
|
217
|
+
end
|
218
|
+
|
219
|
+
def combine(source_combinations, rest_options)
|
220
|
+
# raise 'source_combinations cannot be empty' if source_combinations.blank?
|
221
|
+
return source_combinations if rest_options.blank?
|
222
|
+
combinations = []
|
223
|
+
current_option = rest_options.shift
|
224
|
+
source_combinations.each do |source_variants|
|
225
|
+
current_option.level_variants.each do |v|
|
226
|
+
combinations << (source_variants + OptionSet.new(v))
|
227
|
+
end
|
228
|
+
end
|
229
|
+
combine(combinations, rest_options)
|
230
|
+
end
|
231
|
+
|
232
|
+
def prepare_args(option_set, *args)
|
233
|
+
options_title, metadata = option_set.title, option_set.metadata
|
234
|
+
if args[0].is_a? String
|
235
|
+
args[0] = [@title, options_title, args[0]].reject(&:blank?).join(' ')
|
236
|
+
else
|
237
|
+
args.unshift([@title, options_title].reject(&:blank?).join(' '))
|
238
|
+
end
|
239
|
+
|
240
|
+
metadata.merge!(@common_metadata) if @common_metadata
|
241
|
+
|
242
|
+
if args.last.is_a? Hash
|
243
|
+
args.last.merge!(metadata)
|
244
|
+
else
|
245
|
+
args << metadata
|
246
|
+
end
|
247
|
+
args
|
248
|
+
end
|
249
|
+
|
250
|
+
# Набор вариантов, собранный для одного теста
|
251
|
+
class OptionSet
|
252
|
+
attr_reader :variants
|
253
|
+
def initialize(*variants)
|
254
|
+
@variants = {}
|
255
|
+
variants.each { |v| self << v }
|
256
|
+
end
|
257
|
+
|
258
|
+
def +(other_set)
|
259
|
+
self.class.new(*@variants.values).tap do |sum|
|
260
|
+
other_set.each { |v| sum << v }
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def <<(variant)
|
265
|
+
@variants[variant.key] = variant
|
266
|
+
end
|
267
|
+
|
268
|
+
def [](key)
|
269
|
+
@variants[key]
|
270
|
+
end
|
271
|
+
|
272
|
+
def each(&block)
|
273
|
+
@variants.values.each(&block)
|
274
|
+
end
|
275
|
+
|
276
|
+
def title
|
277
|
+
@variants.values.map(&:title).compact.join(' ')
|
278
|
+
end
|
279
|
+
|
280
|
+
def metadata
|
281
|
+
@variants.values.inject({}) do |metadata, variant|
|
282
|
+
metadata.merge(variant.metadata(self))
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
class Variant
|
288
|
+
attr_reader :key, :title, :value
|
289
|
+
|
290
|
+
def initialize(key, title, value)
|
291
|
+
@key, @title, @value = key, title, check_lambda_arity(value)
|
292
|
+
end
|
293
|
+
|
294
|
+
def metadata(option_set)
|
295
|
+
data = { key => value }
|
296
|
+
if value.is_a? Hash
|
297
|
+
value.each do |k, v|
|
298
|
+
sub_key = "%s_%s" % [key, k]
|
299
|
+
data[sub_key.to_sym] = v
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
data.each do |key, v|
|
304
|
+
data[key] = v.calculate(option_set) if v.is_a? CalculatedValue
|
305
|
+
end
|
306
|
+
data
|
307
|
+
end
|
308
|
+
|
309
|
+
def self.join(variants)
|
310
|
+
title, metadata = nil, {}
|
311
|
+
variants.each do |v|
|
312
|
+
title = [title, v.title].compact.join(' ')
|
313
|
+
metadata.merge!(v.metadata)
|
314
|
+
end
|
315
|
+
[title, metadata]
|
316
|
+
end
|
317
|
+
|
318
|
+
private
|
319
|
+
|
320
|
+
# Лямдда будет передаваться как блок в instance_eval, которому плохеет, если пришло что-то с нулевой
|
321
|
+
# arity. Поэтому для лямбд с нулевой arity делаем arity == 1
|
322
|
+
def check_lambda_arity(v)
|
323
|
+
if v.is_a?(Proc) && v.arity == 0
|
324
|
+
->(_) { instance_exec(&v) }
|
325
|
+
else
|
326
|
+
v
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
class CalculatedValue
|
332
|
+
def initialize(&block)
|
333
|
+
@proc = block
|
334
|
+
end
|
335
|
+
|
336
|
+
def calculate(option_set)
|
337
|
+
@proc.call(option_set)
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
class Option
|
342
|
+
attr_reader :variants
|
343
|
+
def initialize(key, variants)
|
344
|
+
@variants =
|
345
|
+
if variants.is_a? Hash
|
346
|
+
variants.map { |title, value| Variant.new(key, title, value) }
|
347
|
+
else
|
348
|
+
# Array of arrays of two elements
|
349
|
+
variants.map { |v| Variant.new(key, *v) }
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
# Variants to apply at one level
|
354
|
+
def level_variants
|
355
|
+
variants
|
356
|
+
end
|
357
|
+
|
358
|
+
def next_variant
|
359
|
+
@current ||= 0
|
360
|
+
selected_variant = variants[@current]
|
361
|
+
@current += 1
|
362
|
+
if @current >= variants.length
|
363
|
+
@current = 0
|
364
|
+
@complete = true # all variants have been selected
|
365
|
+
end
|
366
|
+
selected_variant
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
class Diagonal < Option
|
371
|
+
def level_variants
|
372
|
+
[next_variant]
|
373
|
+
end
|
374
|
+
|
375
|
+
def complete?
|
376
|
+
@complete
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
# RSpec helpers for spec builing
|
381
|
+
module RSpecInjections
|
382
|
+
def match_metadata?(metadata_key)
|
383
|
+
case metadata_key
|
384
|
+
when Hash
|
385
|
+
metadata_key.keys.all? { |k| metadata[k] == metadata_key[k] }
|
386
|
+
when Array
|
387
|
+
metadata_key.map { |key| metadata[key] }.none?(&:nil?)
|
388
|
+
else
|
389
|
+
metadata[metadata_key]
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
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.0.
|
4
|
+
version: 0.0.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexey Volochnev
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-05-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|
@@ -63,6 +63,7 @@ files:
|
|
63
63
|
- lib/lopata/rspec/role.rb
|
64
64
|
- lib/lopata/rspec/version.rb
|
65
65
|
- lib/lopata/runner.rb
|
66
|
+
- lib/lopata/scenario.rb
|
66
67
|
homepage: https://github.com/avolochnev/lopata
|
67
68
|
licenses:
|
68
69
|
- MIT
|
@@ -82,9 +83,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
82
83
|
- !ruby/object:Gem::Version
|
83
84
|
version: '0'
|
84
85
|
requirements: []
|
85
|
-
|
86
|
-
rubygems_version: 2.5.2
|
86
|
+
rubygems_version: 3.0.3
|
87
87
|
signing_key:
|
88
88
|
specification_version: 4
|
89
|
-
summary: lopata-0.0.
|
89
|
+
summary: lopata-0.0.16
|
90
90
|
test_files: []
|