lopata 0.0.16 → 0.1.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/lopata +8 -1
- data/lib/lopata.rb +9 -2
- data/lib/lopata/config.rb +7 -1
- data/lib/lopata/generators/app.rb +2 -1
- data/lib/lopata/generators/templates/.rspec +1 -0
- data/lib/lopata/generators/templates/Gemfile +4 -4
- data/lib/lopata/generators/templates/Lopatafile +1 -0
- data/lib/lopata/generators/templates/spec/spec_helper.rb +2 -0
- data/lib/lopata/observers.rb +4 -0
- data/lib/lopata/observers/base_observer.rb +23 -0
- data/lib/lopata/observers/console_output_observer.rb +62 -0
- data/lib/lopata/runner.rb +36 -21
- data/lib/lopata/scenario.rb +42 -359
- data/lib/lopata/scenario_builder.rb +311 -0
- data/lib/lopata/shared_step.rb +31 -0
- data/lib/lopata/step.rb +42 -0
- data/lib/lopata/version.rb +5 -0
- data/lib/lopata/world.rb +49 -0
- metadata +58 -8
- data/lib/lopata/rspec/version.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ad9bc72f5e0429f18b12bc247e3fbf4346278b5c6b487a02c665e3c020bd5f7
|
4
|
+
data.tar.gz: bde38469c56e1ef03b1480fa09d9d879867660983fcd9abe807ee8be553aaaed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba6f140682c330ca68eb2a8d3f27aed552bd376c1a1c773ebe1914b4738da39adfe500a4254e06c2ea3865bd5fedc7246e5301eca6e130683bc4119e7db30071
|
7
|
+
data.tar.gz: 0d6b7e1f4ae029539a1c65fd0e9ca4546610bbe6e8da8b37250ec71a106cc3cfe0445b4a6b299de9cbd5a2b1a5e4949b7bad6f178af32e702252755553b69474
|
data/exe/lopata
CHANGED
@@ -1,4 +1,11 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'bundler/setup'
|
3
3
|
require_relative '../lib/lopata/runner'
|
4
|
-
|
4
|
+
|
5
|
+
# use default command with arguments if given command is unknown.
|
6
|
+
argv = ARGV.dup
|
7
|
+
unless Lopata::Runner.all_commands.keys.include? argv
|
8
|
+
argv.unshift 'test'
|
9
|
+
end
|
10
|
+
|
11
|
+
Lopata::Runner.start argv
|
data/lib/lopata.rb
CHANGED
@@ -1,13 +1,20 @@
|
|
1
1
|
require 'lopata/id'
|
2
2
|
require 'lopata/config'
|
3
|
+
require 'lopata/scenario_builder'
|
3
4
|
require 'lopata/scenario'
|
5
|
+
require 'lopata/step'
|
6
|
+
require 'lopata/shared_step'
|
4
7
|
|
5
8
|
module Lopata
|
6
9
|
def self.define(*args, &block)
|
7
|
-
Lopata::
|
10
|
+
Lopata::ScenarioBuilder.define(*args, &block)
|
8
11
|
end
|
9
12
|
|
10
13
|
def self.xdefine(*args, &block)
|
11
|
-
Lopata::
|
14
|
+
Lopata::ScenarioBuilder.xdefine(*args, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.shared_step(name, &block)
|
18
|
+
Lopata::SharedStep.register(name, &block)
|
12
19
|
end
|
13
20
|
end
|
data/lib/lopata/config.rb
CHANGED
@@ -7,7 +7,9 @@ module Lopata
|
|
7
7
|
|
8
8
|
def init(env)
|
9
9
|
require 'yaml'
|
10
|
-
@config =
|
10
|
+
@config = {}
|
11
|
+
config_filename = "./config/environments/#{env}.yml"
|
12
|
+
@config = YAML::load(File.open(config_filename)) if File.exists?(config_filename)
|
11
13
|
init_db
|
12
14
|
@role_descriptions ||= {}
|
13
15
|
# init_includes
|
@@ -89,5 +91,9 @@ module Lopata
|
|
89
91
|
def initialize_test
|
90
92
|
@before_start.call if @before_start
|
91
93
|
end
|
94
|
+
|
95
|
+
def world
|
96
|
+
@world ||= Lopata::World.new
|
97
|
+
end
|
92
98
|
end
|
93
99
|
end
|
@@ -14,6 +14,7 @@ module Lopata
|
|
14
14
|
template '.rspec', "#{name}/.rspec"
|
15
15
|
template 'config/environments/qa.yml', "#{name}/config/environments/qa.yml"
|
16
16
|
template 'config/initializers/capybara.rb', "#{name}/config/initializers/capybara.rb"
|
17
|
+
template 'spec/spec_helper.rb', "#{name}/spec/spec_helper.rb"
|
17
18
|
end
|
18
19
|
|
19
20
|
def init_dirs
|
@@ -21,7 +22,7 @@ module Lopata
|
|
21
22
|
empty_directory "#{name}/app/#{dir}"
|
22
23
|
end
|
23
24
|
|
24
|
-
%w{
|
25
|
+
%w{scenarios shared_steps config/initializers}.each do |dir|
|
25
26
|
empty_directory "#{name}/#{dir}"
|
26
27
|
end
|
27
28
|
end
|
@@ -2,8 +2,8 @@
|
|
2
2
|
gem 'selenium-webdriver'
|
3
3
|
gem 'rspec', :require => 'spec'
|
4
4
|
gem 'rake'
|
5
|
-
gem 'activerecord', '~>
|
6
|
-
gem 'capybara', '~>
|
5
|
+
gem 'activerecord', '~> 6.0.2'
|
6
|
+
gem 'capybara', '~> 3.11.1'
|
7
7
|
gem 'thor'
|
8
|
-
<% require "lopata/
|
9
|
-
gem 'lopata', '<%= Lopata::
|
8
|
+
<% require "lopata/version" %>
|
9
|
+
gem 'lopata', '<%= Lopata::Version::STRING %>'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Lopata
|
2
|
+
module Observers
|
3
|
+
class BaseObserver
|
4
|
+
def started(world)
|
5
|
+
end
|
6
|
+
|
7
|
+
def finished(world)
|
8
|
+
end
|
9
|
+
|
10
|
+
def scenario_started(scenario)
|
11
|
+
end
|
12
|
+
|
13
|
+
def scenario_finished(scenario)
|
14
|
+
end
|
15
|
+
|
16
|
+
def step_started(step)
|
17
|
+
end
|
18
|
+
|
19
|
+
def step_finished(step)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Lopata
|
2
|
+
module Observers
|
3
|
+
class ConsoleOutputObserver < BaseObserver
|
4
|
+
def finished(world)
|
5
|
+
total = world.scenarios.length
|
6
|
+
statuses = world.scenarios.map(&:status)
|
7
|
+
counts = statuses.uniq.map do |status|
|
8
|
+
colored("%d %s", status) % [statuses.count { |s| s == status }, status]
|
9
|
+
end
|
10
|
+
details = counts.empty? ? "" : "(%s)" % counts.join(', ')
|
11
|
+
puts "#{total} scenario%s %s" % [total == 1 ? '' : 's', details]
|
12
|
+
end
|
13
|
+
|
14
|
+
def step_finished(step)
|
15
|
+
@failed_steps << step if step.failed?
|
16
|
+
end
|
17
|
+
|
18
|
+
def scenario_started(scenario)
|
19
|
+
@failed_steps = []
|
20
|
+
end
|
21
|
+
|
22
|
+
def scenario_finished(scenario)
|
23
|
+
message = "#{scenario.title} #{bold(scenario.status.to_s.upcase)}"
|
24
|
+
puts colored(message, scenario.status)
|
25
|
+
|
26
|
+
@failed_steps.each do |step|
|
27
|
+
if step.exception
|
28
|
+
puts step.exception.message
|
29
|
+
puts step.exception.backtrace.join("\n")
|
30
|
+
puts
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def colored(text, status)
|
38
|
+
case status
|
39
|
+
when :failed then red(text)
|
40
|
+
when :passed then green(text)
|
41
|
+
else text
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def red(text)
|
46
|
+
wrap(text, 31)
|
47
|
+
end
|
48
|
+
|
49
|
+
def green(text)
|
50
|
+
wrap(text, 32)
|
51
|
+
end
|
52
|
+
|
53
|
+
def bold(text)
|
54
|
+
wrap(text, 1)
|
55
|
+
end
|
56
|
+
|
57
|
+
def wrap(text, code)
|
58
|
+
"\e[#{code}m#{text}\e[0m"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/lopata/runner.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'thor'
|
2
2
|
require_relative 'generators/app'
|
3
3
|
require_relative 'config'
|
4
|
+
require_relative 'world'
|
5
|
+
require_relative '../lopata'
|
6
|
+
require_relative 'observers'
|
4
7
|
|
5
8
|
module Lopata
|
6
9
|
class Runner < Thor
|
@@ -13,35 +16,47 @@ module Lopata
|
|
13
16
|
option :build, aliases: 'b'
|
14
17
|
option :keep, type: :boolean, aliases: 'k'
|
15
18
|
option :text, aliases: 't'
|
16
|
-
def test
|
17
|
-
|
18
|
-
|
19
|
-
Dir["./spec/support/**/*.rb"].sort.each { |f| require f}
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
env: options[:env],
|
27
|
-
keep: options[:keep],
|
28
|
-
text: options[:text]
|
29
|
-
}
|
30
|
-
Lopata::Config.init(options[:env])
|
31
|
-
Lopata::Config.initialize_test
|
32
|
-
Lopata::Config.init_rspec
|
33
|
-
|
34
|
-
::RSpec::Core::Runner.run ['spec']
|
19
|
+
def test(*args)
|
20
|
+
configure_from_options
|
21
|
+
|
22
|
+
# Dir["./spec/support/**/*.rb"].sort.each { |f| require f}
|
23
|
+
world = Lopata::Config.world
|
24
|
+
world.setup_observers
|
25
|
+
world.load_shared_steps
|
26
|
+
world.load_scenarios(*args)
|
27
|
+
world.scenarios.each { |s| s.run }
|
28
|
+
world.finish
|
35
29
|
end
|
36
30
|
|
37
31
|
default_task :test
|
38
32
|
|
39
33
|
register Generators::App, :new, 'lopata new project-name', 'Init new lopata projects'
|
34
|
+
|
35
|
+
def self.exit_on_failure?
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
39
|
+
no_commands do
|
40
|
+
def configure_from_options
|
41
|
+
Lopata::Config.ops = {
|
42
|
+
focus: options[:focus],
|
43
|
+
rerun: options[:rerun],
|
44
|
+
users: options[:users],
|
45
|
+
build: options[:build],
|
46
|
+
env: options[:env],
|
47
|
+
keep: options[:keep],
|
48
|
+
text: options[:text]
|
49
|
+
}
|
50
|
+
Lopata::Config.init(options[:env])
|
51
|
+
Lopata::Config.initialize_test
|
52
|
+
# ENV['HOME'] = File.absolute_path('.') # disable warning on rspec loading on windows
|
53
|
+
# Lopata::Config.init_rspec
|
54
|
+
end
|
55
|
+
end
|
40
56
|
end
|
41
57
|
end
|
42
58
|
|
43
59
|
unless ARGV.first == 'new'
|
44
|
-
|
45
|
-
eval File.binread('./Lopatafile')
|
60
|
+
eval File.binread('./Lopatafile') if File.exists?('./Lopatafile')
|
46
61
|
end
|
47
62
|
|
data/lib/lopata/scenario.rb
CHANGED
@@ -1,169 +1,47 @@
|
|
1
|
-
|
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
|
1
|
+
require 'rspec/expectations'
|
51
2
|
|
52
|
-
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
def skip?(option_set)
|
57
|
-
@skip_when && @skip_when.call(option_set)
|
58
|
-
end
|
3
|
+
class Lopata::Scenario
|
4
|
+
include RSpec::Matchers
|
59
5
|
|
60
|
-
|
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
|
6
|
+
attr_reader :title, :metadata, :steps, :status
|
67
7
|
|
68
|
-
def
|
69
|
-
|
8
|
+
def initialize(*args)
|
9
|
+
@title = args.first
|
10
|
+
@metadata = args.last.is_a?(Hash) ? args.last : {}
|
11
|
+
@steps = []
|
12
|
+
@status = :not_runned
|
70
13
|
end
|
71
14
|
|
72
|
-
def
|
73
|
-
@
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
end
|
15
|
+
def run
|
16
|
+
@status = :running
|
17
|
+
world.notify_observers(:scenario_started, self)
|
18
|
+
teardown_steps = []
|
19
|
+
@steps.reject(&:teardown?).each { |step| step.run(self) }
|
20
|
+
@steps.select(&:teardown?).each { |step| step.run(self) }
|
21
|
+
@status = @steps.all?(&:passed?) ? :passed : :failed
|
22
|
+
world.notify_observers(:scenario_finished, self)
|
81
23
|
end
|
82
24
|
|
83
|
-
def
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
25
|
+
def match_metadata?(metadata_key)
|
26
|
+
case metadata_key
|
27
|
+
when Hash
|
28
|
+
metadata_key.keys.all? { |k| metadata[k] == metadata_key[k] }
|
29
|
+
when Array
|
30
|
+
metadata_key.map { |key| metadata[key] }.none?(&:nil?)
|
31
|
+
else
|
32
|
+
metadata[metadata_key]
|
130
33
|
end
|
131
34
|
end
|
132
35
|
|
133
|
-
def
|
134
|
-
|
135
|
-
@steps << Proc.new do
|
136
|
-
define_method method_name, &block
|
137
|
-
define_singleton_method method_name, &block
|
138
|
-
end
|
36
|
+
def run_step(method_name, *args, &block)
|
37
|
+
instance_exec(&block)
|
139
38
|
end
|
140
39
|
|
141
|
-
def
|
142
|
-
@
|
143
|
-
@steps << block
|
40
|
+
def world
|
41
|
+
@world ||= Lopata::Config.world
|
144
42
|
end
|
145
43
|
|
146
|
-
def
|
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)
|
44
|
+
def convert_args(*args)
|
167
45
|
args.map do |arg|
|
168
46
|
case arg
|
169
47
|
# trait symbols as link to metadata.
|
@@ -174,220 +52,25 @@ class Lopata::Scenario
|
|
174
52
|
end.flatten
|
175
53
|
end
|
176
54
|
|
177
|
-
def
|
55
|
+
def separate_args(args)
|
178
56
|
args.map { |a| a.is_a?(String) && a =~ /,/ ? a.split(',').map(&:strip) : a }.flatten
|
179
57
|
end
|
180
58
|
|
181
|
-
def
|
182
|
-
|
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
|
59
|
+
def failed?
|
60
|
+
status == :failed
|
248
61
|
end
|
249
62
|
|
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
|
63
|
+
private
|
263
64
|
|
264
|
-
def
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
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
|
65
|
+
def method_missing(method, *args, &block)
|
66
|
+
if metadata.keys.include?(method)
|
67
|
+
metadata[method]
|
68
|
+
else
|
69
|
+
super
|
365
70
|
end
|
366
|
-
selected_variant
|
367
71
|
end
|
368
|
-
end
|
369
72
|
|
370
|
-
|
371
|
-
|
372
|
-
[next_variant]
|
73
|
+
def respond_to_missing?(method, *)
|
74
|
+
metadata.keys.include?(method) or super
|
373
75
|
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
|
76
|
+
end
|
@@ -0,0 +1,311 @@
|
|
1
|
+
class Lopata::ScenarioBuilder
|
2
|
+
def self.define(title, metadata = nil, &block)
|
3
|
+
builder = new
|
4
|
+
builder.title(title)
|
5
|
+
builder.metadata(metadata) if metadata
|
6
|
+
builder.instance_exec &block
|
7
|
+
builder.build
|
8
|
+
end
|
9
|
+
|
10
|
+
# Do noting. Exclude defined scenario from suite.
|
11
|
+
def self.xdefine(*attrs)
|
12
|
+
end
|
13
|
+
|
14
|
+
def build
|
15
|
+
world = Lopata::Config.world
|
16
|
+
option_combinations.map do |option_set|
|
17
|
+
args = prepare_args(option_set)
|
18
|
+
raise "scenario required a name in first argument" unless args.first.is_a? String
|
19
|
+
scenario = Lopata::Scenario.new(*args)
|
20
|
+
|
21
|
+
steps.each do |step|
|
22
|
+
scenario.steps << step
|
23
|
+
end
|
24
|
+
|
25
|
+
scenario.steps << Lopata::Config.after_scenario if Lopata::Config.after_scenario
|
26
|
+
|
27
|
+
world.scenarios << scenario
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def as(*args, &block)
|
32
|
+
@roles = args.flatten
|
33
|
+
@roles << CalculatedValue.new(&block) if block_given?
|
34
|
+
@role_options = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def role_options
|
38
|
+
@role_options ||= build_role_options
|
39
|
+
end
|
40
|
+
|
41
|
+
def metadata(hash)
|
42
|
+
raise 'metadata expected to be a Hash' unless hash.is_a?(Hash)
|
43
|
+
@common_metadata ||= {}
|
44
|
+
@common_metadata.merge! hash
|
45
|
+
end
|
46
|
+
|
47
|
+
def without_user
|
48
|
+
@without_user = true
|
49
|
+
end
|
50
|
+
|
51
|
+
def skip_when(&block)
|
52
|
+
@skip_when = block
|
53
|
+
end
|
54
|
+
|
55
|
+
def skip?(option_set)
|
56
|
+
@skip_when && @skip_when.call(option_set)
|
57
|
+
end
|
58
|
+
|
59
|
+
%i{setup action it teardown}.each do |name|
|
60
|
+
name_if = "%s_if" % name
|
61
|
+
name_unless = "%s_unless" % name
|
62
|
+
define_method name, ->(*args, &block) { add_step(name, *args, &block) }
|
63
|
+
define_method name_if, ->(condition, *args, &block) { add_step(name, *args, if_cond: condition, &block) }
|
64
|
+
define_method name_unless, ->(condition, *args, &block) { add_step(name, *args, unless_cond: condition, &block) }
|
65
|
+
end
|
66
|
+
|
67
|
+
def add_step(method_name, *args, if_cond: nil, unless_cond: nil, &block)
|
68
|
+
steps << Lopata::Step.new(method_name, *args) do
|
69
|
+
# will be called in context of scenario
|
70
|
+
next if if_cond && !match_metadata?(if_cond)
|
71
|
+
next if unless_cond && match_metadata?(unless_cond)
|
72
|
+
|
73
|
+
flat_args = args.flatten
|
74
|
+
flat_args = separate_args(flat_args) if method_name =~ /^(setup|action)/
|
75
|
+
converted_args = convert_args(*flat_args)
|
76
|
+
converted_args.shift if method_name =~ /^it$/
|
77
|
+
converted_args.each do |step|
|
78
|
+
if step.is_a?(String)
|
79
|
+
Lopata::SharedStep.find(step).steps.each do |shared_step|
|
80
|
+
instance_exec(&shared_step.block)
|
81
|
+
end
|
82
|
+
elsif step.is_a?(Proc)
|
83
|
+
instance_exec(&step)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
# run_step method_name, *converted_args, &block
|
87
|
+
instance_exec(&block) if block
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def steps
|
92
|
+
@steps ||= []
|
93
|
+
end
|
94
|
+
|
95
|
+
def cleanup(*args, &block)
|
96
|
+
add_step_as_is(:cleanup, *args, &block)
|
97
|
+
end
|
98
|
+
|
99
|
+
def add_step_as_is(method_name, *args, &block)
|
100
|
+
steps << Lopata::Step.new(method_name, *args) do
|
101
|
+
# do not convert args - symbols mean name of instance variable
|
102
|
+
# run_step method_name, *args, &block
|
103
|
+
instance_exec(&block) if block
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def let(method_name, &block)
|
108
|
+
steps << Lopata::Step.new(nil) do
|
109
|
+
define_singleton_method method_name, &block
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def build_role_options
|
114
|
+
return [] unless roles
|
115
|
+
[Diagonal.new(:as, roles.map { |r| [nil, r] })]
|
116
|
+
end
|
117
|
+
|
118
|
+
def roles
|
119
|
+
return false if @without_user
|
120
|
+
@roles ||= [Lopata::Config.default_role].compact
|
121
|
+
end
|
122
|
+
|
123
|
+
def title(value)
|
124
|
+
@title = value
|
125
|
+
end
|
126
|
+
|
127
|
+
def option(metadata_key, variants)
|
128
|
+
options << Option.new(metadata_key, variants)
|
129
|
+
end
|
130
|
+
|
131
|
+
def diagonal(metadata_key, variants)
|
132
|
+
diagonals << Diagonal.new(metadata_key, variants)
|
133
|
+
end
|
134
|
+
|
135
|
+
def options
|
136
|
+
@options ||= []
|
137
|
+
end
|
138
|
+
|
139
|
+
def diagonals
|
140
|
+
@diagonals ||= []
|
141
|
+
end
|
142
|
+
|
143
|
+
def option_combinations
|
144
|
+
combinations = combine([OptionSet.new], options + diagonals + role_options)
|
145
|
+
while !(diagonals + role_options).all?(&:complete?)
|
146
|
+
combinations << OptionSet.new(*(options + diagonals + role_options).map(&:next_variant))
|
147
|
+
end
|
148
|
+
combinations.reject { |option_set| skip?(option_set) }
|
149
|
+
end
|
150
|
+
|
151
|
+
def combine(source_combinations, rest_options)
|
152
|
+
# raise 'source_combinations cannot be empty' if source_combinations.blank?
|
153
|
+
return source_combinations if rest_options.empty?
|
154
|
+
combinations = []
|
155
|
+
current_option = rest_options.shift
|
156
|
+
source_combinations.each do |source_variants|
|
157
|
+
current_option.level_variants.each do |v|
|
158
|
+
combinations << (source_variants + OptionSet.new(v))
|
159
|
+
end
|
160
|
+
end
|
161
|
+
combine(combinations, rest_options)
|
162
|
+
end
|
163
|
+
|
164
|
+
def prepare_args(option_set, *args)
|
165
|
+
options_title, metadata = option_set.title, option_set.metadata
|
166
|
+
if args[0].is_a? String
|
167
|
+
args[0] = [@title, options_title, args[0]].compact.reject(&:empty?).join(' ')
|
168
|
+
else
|
169
|
+
args.unshift([@title, options_title].compact.reject(&:empty?).join(' '))
|
170
|
+
end
|
171
|
+
|
172
|
+
metadata.merge!(@common_metadata) if @common_metadata
|
173
|
+
|
174
|
+
if args.last.is_a? Hash
|
175
|
+
args.last.merge!(metadata)
|
176
|
+
else
|
177
|
+
args << metadata
|
178
|
+
end
|
179
|
+
args
|
180
|
+
end
|
181
|
+
|
182
|
+
# Набор вариантов, собранный для одного теста
|
183
|
+
class OptionSet
|
184
|
+
attr_reader :variants
|
185
|
+
def initialize(*variants)
|
186
|
+
@variants = {}
|
187
|
+
variants.compact.each { |v| self << v }
|
188
|
+
end
|
189
|
+
|
190
|
+
def +(other_set)
|
191
|
+
self.class.new(*@variants.values).tap do |sum|
|
192
|
+
other_set.each { |v| sum << v }
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def <<(variant)
|
197
|
+
@variants[variant.key] = variant
|
198
|
+
end
|
199
|
+
|
200
|
+
def [](key)
|
201
|
+
@variants[key]
|
202
|
+
end
|
203
|
+
|
204
|
+
def each(&block)
|
205
|
+
@variants.values.each(&block)
|
206
|
+
end
|
207
|
+
|
208
|
+
def title
|
209
|
+
@variants.values.map(&:title).compact.join(' ')
|
210
|
+
end
|
211
|
+
|
212
|
+
def metadata
|
213
|
+
@variants.values.inject({}) do |metadata, variant|
|
214
|
+
metadata.merge(variant.metadata(self))
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
class Variant
|
220
|
+
attr_reader :key, :title, :value
|
221
|
+
|
222
|
+
def initialize(key, title, value)
|
223
|
+
@key, @title, @value = key, title, check_lambda_arity(value)
|
224
|
+
end
|
225
|
+
|
226
|
+
def metadata(option_set)
|
227
|
+
data = { key => value }
|
228
|
+
if value.is_a? Hash
|
229
|
+
value.each do |k, v|
|
230
|
+
sub_key = "%s_%s" % [key, k]
|
231
|
+
data[sub_key.to_sym] = v
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
data.each do |key, v|
|
236
|
+
data[key] = v.calculate(option_set) if v.is_a? CalculatedValue
|
237
|
+
end
|
238
|
+
data
|
239
|
+
end
|
240
|
+
|
241
|
+
def self.join(variants)
|
242
|
+
title, metadata = nil, {}
|
243
|
+
variants.each do |v|
|
244
|
+
title = [title, v.title].compact.join(' ')
|
245
|
+
metadata.merge!(v.metadata)
|
246
|
+
end
|
247
|
+
[title, metadata]
|
248
|
+
end
|
249
|
+
|
250
|
+
private
|
251
|
+
|
252
|
+
# Лямдда будет передаваться как блок в instance_eval, которому плохеет, если пришло что-то с нулевой
|
253
|
+
# arity. Поэтому для лямбд с нулевой arity делаем arity == 1
|
254
|
+
def check_lambda_arity(v)
|
255
|
+
if v.is_a?(Proc) && v.arity == 0
|
256
|
+
->(_) { instance_exec(&v) }
|
257
|
+
else
|
258
|
+
v
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
class CalculatedValue
|
264
|
+
def initialize(&block)
|
265
|
+
@proc = block
|
266
|
+
end
|
267
|
+
|
268
|
+
def calculate(option_set)
|
269
|
+
@proc.call(option_set)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
class Option
|
274
|
+
attr_reader :variants
|
275
|
+
def initialize(key, variants)
|
276
|
+
@variants =
|
277
|
+
if variants.is_a? Hash
|
278
|
+
variants.map { |title, value| Variant.new(key, title, value) }
|
279
|
+
else
|
280
|
+
# Array of arrays of two elements
|
281
|
+
variants.map { |v| Variant.new(key, *v) }
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# Variants to apply at one level
|
286
|
+
def level_variants
|
287
|
+
variants
|
288
|
+
end
|
289
|
+
|
290
|
+
def next_variant
|
291
|
+
@current ||= 0
|
292
|
+
selected_variant = variants[@current]
|
293
|
+
@current += 1
|
294
|
+
if @current >= variants.length
|
295
|
+
@current = 0
|
296
|
+
@complete = true # all variants have been selected
|
297
|
+
end
|
298
|
+
selected_variant
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
class Diagonal < Option
|
303
|
+
def level_variants
|
304
|
+
[next_variant]
|
305
|
+
end
|
306
|
+
|
307
|
+
def complete?
|
308
|
+
@complete
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Lopata
|
2
|
+
class SharedStep
|
3
|
+
attr_reader :block
|
4
|
+
|
5
|
+
class SharedStepNotFound < StandardError; end
|
6
|
+
|
7
|
+
def self.register(name, &block)
|
8
|
+
raise ArgumentError, "Comma is not allowed in shared step name: '%s'" % name if name =~ /,/
|
9
|
+
@shared_steps ||= {}
|
10
|
+
@shared_steps[name] = new(&block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.find(name)
|
14
|
+
@shared_steps[name] or raise StandardError, "Shared step '%s' not found" % name
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(&block)
|
18
|
+
@block = block
|
19
|
+
end
|
20
|
+
|
21
|
+
def steps
|
22
|
+
@steps ||= build_steps
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_steps
|
26
|
+
builder = Lopata::ScenarioBuilder.new
|
27
|
+
builder.instance_exec(&block)
|
28
|
+
builder.steps
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/lopata/step.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module Lopata
|
2
|
+
class Step
|
3
|
+
attr_reader :block, :status, :exception
|
4
|
+
|
5
|
+
def initialize(method_name, *args, &block)
|
6
|
+
@method_name = method_name
|
7
|
+
@args = args
|
8
|
+
@status = :not_started
|
9
|
+
@block = block
|
10
|
+
@exception = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def run(scenario)
|
14
|
+
@status = :running
|
15
|
+
world.notify_observers(:step_started, self)
|
16
|
+
begin
|
17
|
+
scenario.instance_exec(&block)
|
18
|
+
@status = :passed
|
19
|
+
rescue Exception => e
|
20
|
+
@status = :failed
|
21
|
+
@exception = e
|
22
|
+
end
|
23
|
+
world.notify_observers(:step_finished, self)
|
24
|
+
end
|
25
|
+
|
26
|
+
def world
|
27
|
+
@world ||= Lopata::Config.world
|
28
|
+
end
|
29
|
+
|
30
|
+
def failed?
|
31
|
+
status == :failed
|
32
|
+
end
|
33
|
+
|
34
|
+
def passed?
|
35
|
+
status == :passed
|
36
|
+
end
|
37
|
+
|
38
|
+
def teardown?
|
39
|
+
%i{ teardown cleanup }.include?(@method_name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/lopata/world.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
class Lopata::World
|
2
|
+
attr_reader :scenarios, :observers
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@scenarios = []
|
6
|
+
@observers = []
|
7
|
+
end
|
8
|
+
|
9
|
+
# Loads scenarios for running in current session
|
10
|
+
#
|
11
|
+
# @param args [Array<String>] files to be load.
|
12
|
+
# All files from default location to be loaded if empty.
|
13
|
+
def load_scenarios(*args)
|
14
|
+
if args.empty?
|
15
|
+
load_all_scenarios
|
16
|
+
else
|
17
|
+
args.each do |file|
|
18
|
+
load File.expand_path(file)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Loads all scenarios from predefined paths
|
24
|
+
def load_all_scenarios
|
25
|
+
Dir["scenarios/**/*.rb"].each { |f| load File.expand_path(f) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def load_shared_steps
|
29
|
+
Dir["shared_steps/**/*rb"].each { |f| load File.expand_path(f) }
|
30
|
+
end
|
31
|
+
|
32
|
+
# Called at the end of test running.
|
33
|
+
#
|
34
|
+
# Notifies observers about testing finish
|
35
|
+
def finish
|
36
|
+
notify_observers(:finished, self)
|
37
|
+
end
|
38
|
+
|
39
|
+
def notify_observers(event, context)
|
40
|
+
@observers.each do |observer|
|
41
|
+
observer.send event, context
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Define observers based on configuration
|
46
|
+
def setup_observers
|
47
|
+
@observers = [Lopata::Observers::ConsoleOutputObserver.new]
|
48
|
+
end
|
49
|
+
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.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexey Volochnev
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-05-
|
11
|
+
date: 2020-05-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|
@@ -30,15 +30,57 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
33
|
+
version: '1.0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec-expectations
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.9'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.9'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: cucumber
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.1'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: aruba
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.0'
|
83
|
+
description: Functional acceptance tesging
|
42
84
|
email: alexey.volochnev@gmail.com
|
43
85
|
executables:
|
44
86
|
- lopata
|
@@ -56,14 +98,22 @@ files:
|
|
56
98
|
- lib/lopata/generators/templates/Lopatafile
|
57
99
|
- lib/lopata/generators/templates/config/environments/qa.yml
|
58
100
|
- lib/lopata/generators/templates/config/initializers/capybara.rb
|
101
|
+
- lib/lopata/generators/templates/spec/spec_helper.rb
|
59
102
|
- lib/lopata/id.rb
|
103
|
+
- lib/lopata/observers.rb
|
104
|
+
- lib/lopata/observers/base_observer.rb
|
105
|
+
- lib/lopata/observers/console_output_observer.rb
|
60
106
|
- lib/lopata/rspec/ar_dsl.rb
|
61
107
|
- lib/lopata/rspec/dsl.rb
|
62
108
|
- lib/lopata/rspec/formatter.rb
|
63
109
|
- lib/lopata/rspec/role.rb
|
64
|
-
- lib/lopata/rspec/version.rb
|
65
110
|
- lib/lopata/runner.rb
|
66
111
|
- lib/lopata/scenario.rb
|
112
|
+
- lib/lopata/scenario_builder.rb
|
113
|
+
- lib/lopata/shared_step.rb
|
114
|
+
- lib/lopata/step.rb
|
115
|
+
- lib/lopata/version.rb
|
116
|
+
- lib/lopata/world.rb
|
67
117
|
homepage: https://github.com/avolochnev/lopata
|
68
118
|
licenses:
|
69
119
|
- MIT
|
@@ -76,7 +126,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
76
126
|
requirements:
|
77
127
|
- - ">="
|
78
128
|
- !ruby/object:Gem::Version
|
79
|
-
version: 2.
|
129
|
+
version: 2.3.0
|
80
130
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
131
|
requirements:
|
82
132
|
- - ">="
|
@@ -86,5 +136,5 @@ requirements: []
|
|
86
136
|
rubygems_version: 3.0.3
|
87
137
|
signing_key:
|
88
138
|
specification_version: 4
|
89
|
-
summary: lopata-0.0
|
139
|
+
summary: lopata-0.1.0
|
90
140
|
test_files: []
|