lopata 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/lib/lopata.rb +2 -1
- data/lib/lopata/active_record.rb +37 -0
- data/lib/lopata/factory_bot.rb +51 -15
- data/lib/lopata/generators/app.rb +2 -0
- data/lib/lopata/id.rb +1 -0
- data/lib/lopata/loader.rb +2 -0
- data/lib/lopata/observers.rb +1 -0
- data/lib/lopata/observers/backtrace_formatter.rb +1 -1
- data/lib/lopata/observers/base_observer.rb +2 -2
- data/lib/lopata/observers/web_logger.rb +4 -0
- data/lib/lopata/role.rb +45 -0
- data/lib/lopata/runner.rb +8 -2
- data/lib/lopata/scenario.rb +19 -0
- data/lib/lopata/scenario_builder.rb +241 -16
- data/lib/lopata/shared_step.rb +2 -0
- data/lib/lopata/step.rb +5 -1
- data/lib/lopata/version.rb +2 -1
- data/lib/lopata/world.rb +5 -11
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f4b4814d88cd3b9da3a34159e3c1c977d05cbb0504d3dc0b2ab8034044dfbf0
|
4
|
+
data.tar.gz: 378e5a3c346c64cb4430577dcfab31cfefd54a2c8e8ee30046609cf10e1bdba4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff0498d9c9b665d52f5a8d1e65cdc6dc9078f0df1cd4f7fd831393af04ee564a5cf9a9f55305bd4423ff92062cdb7fd4a728bb2be657f196f91447faddffa941
|
7
|
+
data.tar.gz: 063a3914c5e5dd088ad56e37ea527c540e8dc82f0d56e7dca4564e61699541000e4d801a80898b2d89eb6719cfd4d1a887d3884a52335f0e6339ffc9a91569fb
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--no-private
|
data/lib/lopata.rb
CHANGED
@@ -6,6 +6,7 @@ require 'lopata/scenario'
|
|
6
6
|
require 'lopata/step'
|
7
7
|
require 'lopata/shared_step'
|
8
8
|
|
9
|
+
# Namespace for all Lopata code.
|
9
10
|
module Lopata
|
10
11
|
# Define the scenario.
|
11
12
|
# @see Lopata::ScenarioBuilder.define
|
@@ -34,7 +35,7 @@ module Lopata
|
|
34
35
|
# end
|
35
36
|
# end
|
36
37
|
# @param name [String] shared step unique name
|
37
|
-
# @param block shared step action sequence definition
|
38
|
+
# @param block [Block] shared step action sequence definition
|
38
39
|
def self.shared_step(name, &block)
|
39
40
|
Lopata::SharedStep.register(name, &block)
|
40
41
|
end
|
data/lib/lopata/active_record.rb
CHANGED
@@ -71,6 +71,39 @@ module Lopata
|
|
71
71
|
def reload(*objects)
|
72
72
|
objects.flatten.compact.each(&:reload)
|
73
73
|
end
|
74
|
+
|
75
|
+
# Marks object to be destroyed at the end of scenario
|
76
|
+
#
|
77
|
+
# @param object [ActiveRecord::Base] the object to be destoryed at the end of scenario
|
78
|
+
# @return the given object, so chains can be build
|
79
|
+
def cleanup_later(object)
|
80
|
+
return nil unless object
|
81
|
+
@created_objects ||= []
|
82
|
+
@created_objects << object
|
83
|
+
object
|
84
|
+
end
|
85
|
+
|
86
|
+
# Find ActiveRecord object of given class by params.
|
87
|
+
# Marks the returned object to be destroyed at the end of scenario.
|
88
|
+
#
|
89
|
+
# @example
|
90
|
+
# action do
|
91
|
+
# # UI actions creating the user
|
92
|
+
# @user = find_created(User, username: 'testuser')
|
93
|
+
# end
|
94
|
+
# it 'created' do
|
95
|
+
# expect(@user).to_not be_nil
|
96
|
+
# end
|
97
|
+
# # No cleanup needed
|
98
|
+
# # cleanup :user
|
99
|
+
#
|
100
|
+
# @param cls [Class] active record model class
|
101
|
+
# @param params [Hash] options for record finding
|
102
|
+
# @return [ActiveRecord::Base, nil] the object or nil if not found
|
103
|
+
# @see #cleanup_later called on the hood
|
104
|
+
def find_created(cls, params)
|
105
|
+
cleanup_later cls.where(params).take
|
106
|
+
end
|
74
107
|
end
|
75
108
|
|
76
109
|
# To be included in Lopata::ScenarioBuilder. The methods may be used in build time.
|
@@ -92,6 +125,10 @@ module Lopata
|
|
92
125
|
end
|
93
126
|
end
|
94
127
|
|
128
|
+
Lopata.configure do |c|
|
129
|
+
c.after_scenario { cleanup @created_objects }
|
130
|
+
end
|
131
|
+
|
95
132
|
params = Lopata.environment['db']
|
96
133
|
ActiveRecord::Base.establish_connection(params) if params
|
97
134
|
|
data/lib/lopata/factory_bot.rb
CHANGED
@@ -1,23 +1,63 @@
|
|
1
1
|
require_relative 'active_record'
|
2
2
|
|
3
3
|
module Lopata
|
4
|
+
# Helpers for FactoryBot usage in tests.
|
5
|
+
#
|
6
|
+
# Make helpers available in scenarios by
|
7
|
+
#
|
8
|
+
# require 'lopata/factory_bot'
|
9
|
+
#
|
10
|
+
# Automatically adds ActiveRecord helpers.
|
11
|
+
# @see Lopata::ActiveRecord
|
12
|
+
#
|
13
|
+
# Allows to create ActiveRecord object by FactoryBot definitions.
|
14
|
+
# All the objects created by FactoryBot helpers will be destroyed automatically
|
15
|
+
# at the end of scenario.
|
16
|
+
# @see Lopata::ActiveRecord::Methods#cleanup
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
#
|
20
|
+
# # Configure db connection at config/environments/qa.yml like rails:
|
21
|
+
# # db:
|
22
|
+
# # adapter: postgresql
|
23
|
+
# # host: your.database.host
|
24
|
+
# # username: username
|
25
|
+
# # password: password
|
26
|
+
# # database: database
|
27
|
+
# require 'active_record'
|
28
|
+
# require 'factory_bot'
|
29
|
+
# require 'lopata/facotory_bot'
|
30
|
+
#
|
31
|
+
# class User < ActiveRecord::Base; end
|
32
|
+
#
|
33
|
+
# FactoryBot.define do
|
34
|
+
# factory :user do
|
35
|
+
# username { 'testuser' }
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# Lopata.define 'User creation' do
|
40
|
+
# setup do
|
41
|
+
# @user = create(:user)
|
42
|
+
# end
|
43
|
+
# # No cleanup needed - @user will be destroyed automatically
|
44
|
+
# # cleanup :user
|
45
|
+
#
|
46
|
+
# it 'works' do
|
47
|
+
# expect(@user).to_not be_nil
|
48
|
+
# end
|
49
|
+
# end
|
50
|
+
#
|
4
51
|
module FactoryBot
|
5
52
|
# To be included in Lopata::Scenario
|
6
53
|
module Methods
|
54
|
+
# Wrapper for FactoryBot#create
|
55
|
+
# Calls the FactoryBot#create with given paramters and returns it result.
|
56
|
+
# Additionally store the created object for destroying at the end of scenario.
|
57
|
+
# @see Lopata::ActiveRecord::Methods#cleanup
|
7
58
|
def create(*params)
|
8
59
|
cleanup_later ::FactoryBot.create(*params)
|
9
60
|
end
|
10
|
-
|
11
|
-
def find_created(cls, params)
|
12
|
-
cleanup_later cls.where(params).take
|
13
|
-
end
|
14
|
-
|
15
|
-
def cleanup_later(object)
|
16
|
-
return nil unless object
|
17
|
-
@created_objects ||= []
|
18
|
-
@created_objects << object
|
19
|
-
object
|
20
|
-
end
|
21
61
|
end
|
22
62
|
|
23
63
|
# To be included in Lopata::ScenarioBuilder
|
@@ -29,8 +69,4 @@ end
|
|
29
69
|
Lopata::Scenario.include Lopata::FactoryBot::Methods
|
30
70
|
Lopata::ScenarioBuilder.include Lopata::FactoryBot::DSL
|
31
71
|
|
32
|
-
Lopata.configure do |c|
|
33
|
-
c.after_scenario { cleanup @created_objects }
|
34
|
-
end
|
35
|
-
|
36
72
|
::FactoryBot.find_definitions
|
data/lib/lopata/id.rb
CHANGED
data/lib/lopata/loader.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# @private
|
1
2
|
module Lopata::Loader
|
2
3
|
extend self
|
3
4
|
|
@@ -20,6 +21,7 @@ module Lopata::Loader
|
|
20
21
|
Dir["scenarios/**/*.rb"].each { |f| load File.expand_path(f) }
|
21
22
|
end
|
22
23
|
|
24
|
+
# Loads all shared steps from predefined paths
|
23
25
|
def load_shared_steps
|
24
26
|
Dir["shared_steps/**/*rb"].each { |f| load File.expand_path(f) }
|
25
27
|
end
|
data/lib/lopata/observers.rb
CHANGED
@@ -39,7 +39,7 @@ module Lopata
|
|
39
39
|
|
40
40
|
# Extracts error message from excetion
|
41
41
|
#
|
42
|
-
# @param
|
42
|
+
# @param exception [Exception]
|
43
43
|
# @param include_backtrace [Boolean] whether to add formatted backtrace to output
|
44
44
|
# @return [String] error message from excetion, incuding source code line.
|
45
45
|
def error_message(exception, include_backtrace: false)
|
@@ -21,12 +21,12 @@ module Lopata
|
|
21
21
|
end
|
22
22
|
|
23
23
|
# Called before single scenario execution.
|
24
|
-
# @param
|
24
|
+
# @param scenario [Lopata::Scenario::Execution]
|
25
25
|
def scenario_started(scenario)
|
26
26
|
end
|
27
27
|
|
28
28
|
# Called after single scenario execution.
|
29
|
-
# @param
|
29
|
+
# @param scenario [Lopata::Scenario::Execution]
|
30
30
|
def scenario_finished(scenario)
|
31
31
|
end
|
32
32
|
end
|
data/lib/lopata/role.rb
CHANGED
@@ -1,9 +1,33 @@
|
|
1
1
|
module Lopata
|
2
|
+
# Adds ability to run scenarios by given user roles
|
3
|
+
#
|
4
|
+
# @example Usage
|
5
|
+
# require 'lopata/role'
|
6
|
+
# Lopata.configure do |c|
|
7
|
+
# c.role_descriptions = {
|
8
|
+
# user: 'User',
|
9
|
+
# admin: 'Admin'
|
10
|
+
# }
|
11
|
+
# c.default_role = :user
|
12
|
+
# c.before_scenaro 'setup user'
|
13
|
+
# end
|
14
|
+
# Lopata.shared_step 'setup user' do
|
15
|
+
# setup { @user = User.create(role: current_role) if current_role }
|
16
|
+
# cleanup :user
|
17
|
+
# end
|
18
|
+
# Lopata.define 'login' do
|
19
|
+
# # will generate 2 scenarios, one for :user and one for :admin
|
20
|
+
# as :user, :admin
|
21
|
+
# action 'login'
|
22
|
+
# # verify the user is logged in
|
23
|
+
# end
|
24
|
+
#
|
2
25
|
# @see Lopata::Configuration#role_descriptions
|
3
26
|
# @see Lopata::Configuration#default_role
|
4
27
|
module Role
|
5
28
|
# To be included in Lopata::Scenario
|
6
29
|
module Methods
|
30
|
+
# @return [Symbol, nil] user role for current scenario or nil, if scenario is running without user.
|
7
31
|
def current_role
|
8
32
|
metadata[:as]
|
9
33
|
end
|
@@ -11,30 +35,50 @@ module Lopata
|
|
11
35
|
|
12
36
|
# To be included in Lopata::ScenarioBuilder
|
13
37
|
module DSL
|
38
|
+
# Enumerate the roles the scenario to be runned under.
|
39
|
+
# If not invoked the default role only will be used.
|
40
|
+
#
|
41
|
+
# The scenario should be set to use the role via before_scenario step using #current_role param.
|
42
|
+
#
|
43
|
+
# @param args [Array<Symbol>] list of roles the scenario to be runned with.
|
44
|
+
# @param block [Block] the block to calculate role from scenario metadata.
|
14
45
|
def as(*args, &block)
|
15
46
|
@roles = args.flatten
|
16
47
|
@roles << Lopata::ScenarioBuilder::CalculatedValue.new(&block) if block_given?
|
17
48
|
@role_options = nil
|
18
49
|
end
|
19
50
|
|
51
|
+
# @private
|
20
52
|
def role_options
|
21
53
|
@role_options ||= build_role_options
|
22
54
|
end
|
23
55
|
|
56
|
+
# Marks scenario to be runned without user.
|
57
|
+
#
|
58
|
+
# @example
|
59
|
+
# Lopata.define 'scenario withou user' do
|
60
|
+
# without_user
|
61
|
+
# it 'does not define the user' do
|
62
|
+
# expect(current_role).to be_nil
|
63
|
+
# end
|
64
|
+
# end
|
24
65
|
def without_user
|
25
66
|
@without_user = true
|
26
67
|
end
|
27
68
|
|
69
|
+
# @private
|
28
70
|
def build_role_options
|
29
71
|
return [] unless roles
|
30
72
|
[Lopata::ScenarioBuilder::Diagonal.new(:as, roles.map { |r| [Lopata.configuration.role_descriptions[r], r] })]
|
31
73
|
end
|
32
74
|
|
75
|
+
# @private
|
33
76
|
def roles
|
34
77
|
return false if @without_user
|
35
78
|
@roles ||= [Lopata.configuration.default_role].compact
|
36
79
|
end
|
37
80
|
|
81
|
+
# @private
|
38
82
|
def diagonals
|
39
83
|
super + role_options
|
40
84
|
end
|
@@ -43,4 +87,5 @@ module Lopata
|
|
43
87
|
end
|
44
88
|
|
45
89
|
Lopata::Scenario.include Lopata::Role::Methods
|
90
|
+
# Prepend the module to overload #diagonals method
|
46
91
|
Lopata::ScenarioBuilder.prepend Lopata::Role::DSL
|
data/lib/lopata/runner.rb
CHANGED
@@ -7,6 +7,7 @@ require_relative 'observers'
|
|
7
7
|
require_relative 'condition'
|
8
8
|
|
9
9
|
module Lopata
|
10
|
+
# @private
|
10
11
|
class Runner < Thor
|
11
12
|
desc 'test', 'Run tests'
|
12
13
|
option :env, default: :qa, aliases: 'e'
|
@@ -14,13 +15,14 @@ module Lopata
|
|
14
15
|
option :keep, type: :boolean, aliases: 'k'
|
15
16
|
option :text, aliases: 't'
|
16
17
|
def test(*args)
|
18
|
+
trap_interrupt
|
17
19
|
configure_from_options
|
18
20
|
Lopata::Loader.load_shared_steps
|
19
21
|
Lopata::Loader.load_scenarios(*args)
|
20
22
|
world = Lopata.world
|
21
|
-
world.
|
23
|
+
world.notify_observers(:started, world)
|
22
24
|
world.scenarios.each { |s| s.run }
|
23
|
-
world.
|
25
|
+
world.notify_observers(:finished, world)
|
24
26
|
end
|
25
27
|
|
26
28
|
default_task :test
|
@@ -51,6 +53,10 @@ module Lopata
|
|
51
53
|
to_rerun = Lopata::Client.new.to_rerun
|
52
54
|
Lopata.configuration.filters << -> (scenario) { to_rerun.include?(scenario.title) }
|
53
55
|
end
|
56
|
+
|
57
|
+
def trap_interrupt
|
58
|
+
trap('INT') { exit!(1) }
|
59
|
+
end
|
54
60
|
end
|
55
61
|
end
|
56
62
|
end
|
data/lib/lopata/scenario.rb
CHANGED
@@ -1,25 +1,41 @@
|
|
1
1
|
require 'rspec/expectations'
|
2
2
|
|
3
|
+
# Scenario runtime class.
|
4
|
+
#
|
5
|
+
# All the scenarios are running in context of separate Lopata::Scenario object.
|
6
|
+
#
|
3
7
|
class Lopata::Scenario
|
4
8
|
include RSpec::Matchers
|
5
9
|
|
10
|
+
# @private
|
6
11
|
attr_reader :execution
|
7
12
|
|
13
|
+
# @private
|
8
14
|
def initialize(execution)
|
9
15
|
@execution = execution
|
10
16
|
end
|
11
17
|
|
12
18
|
# Marks current step as pending
|
19
|
+
# @example
|
20
|
+
# it 'pending step' do
|
21
|
+
# pending
|
22
|
+
# expect(1).to eq 2
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# Pending steps wont be failed
|
13
26
|
def pending(message = nil)
|
14
27
|
execution.current_step.pending!(message)
|
15
28
|
end
|
16
29
|
|
30
|
+
# @return [Hash] metadata available for current step
|
31
|
+
# @note The metadata keys also availalbe as methods (via method_missing)
|
17
32
|
def metadata
|
18
33
|
execution.metadata
|
19
34
|
end
|
20
35
|
|
21
36
|
private
|
22
37
|
|
38
|
+
# @private
|
23
39
|
def method_missing(method, *args, &block)
|
24
40
|
if execution.let_methods.include?(method)
|
25
41
|
instance_exec(*args, &execution.let_methods[method])
|
@@ -30,10 +46,13 @@ class Lopata::Scenario
|
|
30
46
|
end
|
31
47
|
end
|
32
48
|
|
49
|
+
# @private
|
33
50
|
def respond_to_missing?(method, *)
|
34
51
|
execution.let_methods.include?(method) or metadata.keys.include?(method) or super
|
35
52
|
end
|
36
53
|
|
54
|
+
# @private
|
55
|
+
# Scenario execution and live-cycle information
|
37
56
|
class Execution
|
38
57
|
attr_reader :scenario, :status, :steps, :title, :current_step
|
39
58
|
|
@@ -1,19 +1,39 @@
|
|
1
|
+
# Context for scenario creation.
|
1
2
|
class Lopata::ScenarioBuilder
|
3
|
+
# @private
|
2
4
|
attr_reader :title, :common_metadata, :options, :diagonals
|
5
|
+
# @private
|
3
6
|
attr_accessor :shared_step, :group
|
4
7
|
|
8
|
+
# Defines one or more scenarios.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# Lopata.define 'scenario' do
|
12
|
+
# setup 'test user'
|
13
|
+
# action 'login'
|
14
|
+
# verify 'home page displayed'
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# Given block will be calculated in context of the ScenarioBuilder
|
18
|
+
#
|
19
|
+
# @param title [String] scenario unique title
|
20
|
+
# @param metadata [Hash] metadata to be used within the scenario
|
21
|
+
# @param block [Block] the scenario definition
|
22
|
+
# @see Lopata.define
|
5
23
|
def self.define(title, metadata = {}, &block)
|
6
24
|
builder = new(title, metadata)
|
7
25
|
builder.instance_exec &block
|
8
26
|
builder.build
|
9
27
|
end
|
10
28
|
|
29
|
+
# @private
|
11
30
|
def initialize(title, metadata = {})
|
12
31
|
@title, @common_metadata = title, metadata
|
13
32
|
@diagonals = []
|
14
33
|
@options = []
|
15
34
|
end
|
16
35
|
|
36
|
+
# @private
|
17
37
|
def build
|
18
38
|
filters = Lopata.configuration.filters
|
19
39
|
option_combinations.each do |option_set|
|
@@ -33,21 +53,104 @@ class Lopata::ScenarioBuilder
|
|
33
53
|
end
|
34
54
|
end
|
35
55
|
|
56
|
+
# @!group Defining variants
|
57
|
+
|
58
|
+
# Define option for the scenario.
|
59
|
+
#
|
60
|
+
# The scenario will be generated for all the options.
|
61
|
+
# If more then one option given, the scenarios for all options combinations will be generated.
|
62
|
+
#
|
63
|
+
# @param metadata_key [Symbol] the key to access option data via metadata.
|
64
|
+
# @param variants [Hash{String => Object}] variants for the option
|
65
|
+
# Keys are titles of the variant, values are metadata values.
|
66
|
+
#
|
67
|
+
# @example
|
68
|
+
# Lopata.define 'scenario' do
|
69
|
+
# option :one, 'one' => 1, 'two' => 2
|
70
|
+
# option :two, 'two' => 2, 'three' => 3
|
71
|
+
# # will generate 4 scenarios:
|
72
|
+
# # - 'scenario one two'
|
73
|
+
# # - 'scenario one three'
|
74
|
+
# # - 'scenario two two'
|
75
|
+
# # - 'scenario two three'
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# @see #diagonal
|
79
|
+
def option(metadata_key, variants)
|
80
|
+
@options << Option.new(metadata_key, variants)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Define diagonal for the scenario.
|
84
|
+
#
|
85
|
+
# The scenario will be generated for all the variants of the diagonal.
|
86
|
+
# Each variant of diagonal will be selected for at least one scenario.
|
87
|
+
# It may be included in more then one scenario when other diagonal or option has more variants.
|
88
|
+
#
|
89
|
+
# @param metadata_key [Symbol] the key to access diagonal data via metadata.
|
90
|
+
# @param variants [Hash{String => Object}] variants for the diagonal.
|
91
|
+
# Keys are titles of the variant, values are metadata values.
|
92
|
+
#
|
93
|
+
# @example
|
94
|
+
# Lopata.define 'scenario' do
|
95
|
+
# option :one, 'one' => 1, 'two' => 2
|
96
|
+
# diagonal :two, 'two' => 2, 'three' => 3
|
97
|
+
# diagonal :three, 'three' => 3, 'four' => 4, 'five' => 5
|
98
|
+
# # will generate 3 scenarios:
|
99
|
+
# # - 'scenario one two three'
|
100
|
+
# # - 'scenario two three four'
|
101
|
+
# # - 'scenario one two five'
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# @see #option
|
105
|
+
def diagonal(metadata_key, variants)
|
106
|
+
@diagonals << Diagonal.new(metadata_key, variants)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Define additional metadata for the scenario
|
110
|
+
#
|
111
|
+
# @example
|
112
|
+
# Lopata.define 'scenario' do
|
113
|
+
# metadata key: 'value'
|
114
|
+
# it 'metadata available' do
|
115
|
+
# expect(metadata[:key]).to eq 'value'
|
116
|
+
# end
|
117
|
+
# end
|
36
118
|
def metadata(hash)
|
37
119
|
raise 'metadata expected to be a Hash' unless hash.is_a?(Hash)
|
38
120
|
@common_metadata ||= {}
|
39
121
|
@common_metadata.merge! hash
|
40
122
|
end
|
41
123
|
|
124
|
+
# Skip scenario for given variants combination
|
125
|
+
#
|
126
|
+
# @example
|
127
|
+
# Lopata.define 'multiple options' do
|
128
|
+
# option :one, 'one' => 1, 'two' => 2
|
129
|
+
# option :two, 'two' => 2, 'three' => 3
|
130
|
+
# skip_when { |opt| opt.metadata[:one] == opt.metadata[:two] }
|
131
|
+
# it 'not equal' do
|
132
|
+
# expect(one).to_not eq two
|
133
|
+
# end
|
134
|
+
# end
|
135
|
+
#
|
42
136
|
def skip_when(&block)
|
43
137
|
@skip_when = block
|
44
138
|
end
|
45
139
|
|
140
|
+
# @private
|
46
141
|
def skip?(option_set)
|
47
142
|
@skip_when && @skip_when.call(option_set)
|
48
143
|
end
|
49
144
|
|
50
|
-
|
145
|
+
# @!endgroup
|
146
|
+
|
147
|
+
# @!group Defining Steps
|
148
|
+
|
149
|
+
# @private
|
150
|
+
# @macro [attach] define_step_method
|
151
|
+
# @!scope instance
|
152
|
+
# @method $1
|
153
|
+
def self.define_step_method(name)
|
51
154
|
name_if = "%s_if" % name
|
52
155
|
name_unless = "%s_unless" % name
|
53
156
|
define_method name, ->(*args, **metadata, &block) { add_step(name, *args, metadata: metadata, &block) }
|
@@ -59,6 +162,124 @@ class Lopata::ScenarioBuilder
|
|
59
162
|
}
|
60
163
|
end
|
61
164
|
|
165
|
+
# Define setup step.
|
166
|
+
# @example
|
167
|
+
# setup do
|
168
|
+
# end
|
169
|
+
#
|
170
|
+
# # setup from named shared step
|
171
|
+
# setup 'create user'
|
172
|
+
#
|
173
|
+
# # setup with both shared step and code block
|
174
|
+
# setup 'create user' do
|
175
|
+
# @user.update(admin: true)
|
176
|
+
# end
|
177
|
+
#
|
178
|
+
# Setup step used for set test data.
|
179
|
+
# @overload setup(*steps, &block)
|
180
|
+
# @param steps [Array<String, Symbol, Proc>] the steps to be runned as a part of setup.
|
181
|
+
# String - name of shared step to be called.
|
182
|
+
# Symbol - metadata key, referenced to shared step name.
|
183
|
+
# Proc - in-place step implementation.
|
184
|
+
# @param block [Block] The implementation of the step.
|
185
|
+
define_step_method :setup
|
186
|
+
|
187
|
+
# Define action step.
|
188
|
+
# @example
|
189
|
+
# action do
|
190
|
+
# end
|
191
|
+
#
|
192
|
+
# # action from named shared step
|
193
|
+
# action 'login'
|
194
|
+
#
|
195
|
+
# # setup with both shared step and code block
|
196
|
+
# action 'login', 'go dashboard' do
|
197
|
+
# @user.update(admin: true)
|
198
|
+
# end
|
199
|
+
#
|
200
|
+
# Action step is used for emulate user or external system action
|
201
|
+
#
|
202
|
+
# @overload action(*steps, &block)
|
203
|
+
# @param steps [Array<String, Symbol, Proc>] the steps to be runned as a part of action.
|
204
|
+
# String - name of shared step to be called.
|
205
|
+
# Symbol - metadata key, referenced to shared step name.
|
206
|
+
# Proc - in-place step implementation.
|
207
|
+
# @param block [Block] The implementation of the step.
|
208
|
+
define_step_method :action
|
209
|
+
|
210
|
+
# Define teardown step.
|
211
|
+
# @example
|
212
|
+
# setup { @user = User.create! }
|
213
|
+
# teardown { @user.destroy }
|
214
|
+
# Teardown step will be called at the end of scenario running.
|
215
|
+
# But it suggested to be decared right after setup or action step which require teardown.
|
216
|
+
#
|
217
|
+
# @overload teardown(*steps, &block)
|
218
|
+
# @param steps [Array<String, Symbol, Proc>] the steps to be runned as a part of teardown.
|
219
|
+
# String - name of shared step to be called.
|
220
|
+
# Symbol - metadata key, referenced to shared step name.
|
221
|
+
# Proc - in-place step implementation.
|
222
|
+
# @param block [Block] The implementation of the step.
|
223
|
+
define_step_method :teardown
|
224
|
+
|
225
|
+
# Define verify steps.
|
226
|
+
# @example
|
227
|
+
# verify 'home page displayed' # call shared step.
|
228
|
+
# Usually for validation shared steps inclusion
|
229
|
+
#
|
230
|
+
# @overload verify(*steps, &block)
|
231
|
+
# @param steps [Array<String, Symbol, Proc>] the steps to be runned as a part of verification.
|
232
|
+
# String - name of shared step to be called.
|
233
|
+
# Symbol - metadata key, referenced to shared step name.
|
234
|
+
# Proc - in-place step implementation.
|
235
|
+
# @param block [Block] The implementation of the step.
|
236
|
+
define_step_method :verify
|
237
|
+
|
238
|
+
# Define group of steps.
|
239
|
+
# The metadata for the group may be overriden
|
240
|
+
# @example
|
241
|
+
# context 'the task', task: :created do
|
242
|
+
# verify 'task setup'
|
243
|
+
# it 'created' do
|
244
|
+
# expect(metadata[:task]).to eq :created
|
245
|
+
# end
|
246
|
+
# end
|
247
|
+
# Teardown steps within group will be called at the end of the group, not scenario
|
248
|
+
# @overload context(title, **metadata, &block)
|
249
|
+
# @param title [String] context title
|
250
|
+
# @param metadata [Hash] the step additional metadata
|
251
|
+
# @param block [Block] The implementation of the step.
|
252
|
+
define_step_method :context
|
253
|
+
|
254
|
+
# Define single validation step.
|
255
|
+
# @example
|
256
|
+
# it 'works' do
|
257
|
+
# expect(1).to eq 1
|
258
|
+
# end
|
259
|
+
# @overload it(title, &block)
|
260
|
+
# @param title [String] validation title
|
261
|
+
# @param block [Block] The implementation of the step.
|
262
|
+
define_step_method :it
|
263
|
+
|
264
|
+
# Define runtime method for the scenario.
|
265
|
+
#
|
266
|
+
# @note
|
267
|
+
# The method to be called via #method_missing, so it wont override already defined methods.
|
268
|
+
#
|
269
|
+
# @example
|
270
|
+
# let(:square) { |num| num * num }
|
271
|
+
# it 'calculated' do
|
272
|
+
# expect(square(4)).to eq 16
|
273
|
+
# end
|
274
|
+
def let(method_name, &block)
|
275
|
+
steps << Lopata::Step.new(:let) do
|
276
|
+
execution.let(method_name, &block)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
# @!endgroup
|
281
|
+
|
282
|
+
# @private
|
62
283
|
def add_step(method_name, *args, condition: nil, metadata: {}, &block)
|
63
284
|
step_class =
|
64
285
|
case method_name
|
@@ -71,10 +292,12 @@ class Lopata::ScenarioBuilder
|
|
71
292
|
steps << step
|
72
293
|
end
|
73
294
|
|
295
|
+
# @private
|
74
296
|
def steps
|
75
297
|
@steps ||= []
|
76
298
|
end
|
77
299
|
|
300
|
+
# @private
|
78
301
|
def steps_with_hooks
|
79
302
|
s = []
|
80
303
|
unless Lopata.configuration.before_scenario_steps.empty?
|
@@ -90,20 +313,7 @@ class Lopata::ScenarioBuilder
|
|
90
313
|
s
|
91
314
|
end
|
92
315
|
|
93
|
-
|
94
|
-
steps << Lopata::Step.new(:let) do
|
95
|
-
execution.let(method_name, &block)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def option(metadata_key, variants)
|
100
|
-
@options << Option.new(metadata_key, variants)
|
101
|
-
end
|
102
|
-
|
103
|
-
def diagonal(metadata_key, variants)
|
104
|
-
@diagonals << Diagonal.new(metadata_key, variants)
|
105
|
-
end
|
106
|
-
|
316
|
+
# @private
|
107
317
|
def option_combinations
|
108
318
|
combinations = combine([OptionSet.new], options + diagonals)
|
109
319
|
while !diagonals.all?(&:complete?)
|
@@ -112,6 +322,7 @@ class Lopata::ScenarioBuilder
|
|
112
322
|
combinations.reject { |option_set| skip?(option_set) }
|
113
323
|
end
|
114
324
|
|
325
|
+
# @private
|
115
326
|
def combine(source_combinations, rest_options)
|
116
327
|
return source_combinations if rest_options.empty?
|
117
328
|
combinations = []
|
@@ -124,40 +335,50 @@ class Lopata::ScenarioBuilder
|
|
124
335
|
combine(combinations, rest_options)
|
125
336
|
end
|
126
337
|
|
338
|
+
# @private
|
127
339
|
def world
|
128
340
|
Lopata.world
|
129
341
|
end
|
130
342
|
|
131
|
-
#
|
343
|
+
# Set of options for scenario
|
132
344
|
class OptionSet
|
345
|
+
# @private
|
133
346
|
attr_reader :variants
|
347
|
+
|
348
|
+
# @private
|
134
349
|
def initialize(*variants)
|
135
350
|
@variants = {}
|
136
351
|
variants.compact.each { |v| self << v }
|
137
352
|
end
|
138
353
|
|
354
|
+
# @private
|
139
355
|
def +(other_set)
|
140
356
|
self.class.new(*@variants.values).tap do |sum|
|
141
357
|
other_set.each { |v| sum << v }
|
142
358
|
end
|
143
359
|
end
|
144
360
|
|
361
|
+
# @private
|
145
362
|
def <<(variant)
|
146
363
|
@variants[variant.key] = variant
|
147
364
|
end
|
148
365
|
|
366
|
+
# @private
|
149
367
|
def [](key)
|
150
368
|
@variants[key]
|
151
369
|
end
|
152
370
|
|
371
|
+
# @private
|
153
372
|
def each(&block)
|
154
373
|
@variants.values.each(&block)
|
155
374
|
end
|
156
375
|
|
376
|
+
# @private
|
157
377
|
def title
|
158
378
|
@variants.values.map(&:title).compact.join(' ')
|
159
379
|
end
|
160
380
|
|
381
|
+
# @return [Hash{Symbol => Object}] metadata for this option set
|
161
382
|
def metadata
|
162
383
|
@variants.values.inject({}) do |metadata, variant|
|
163
384
|
metadata.merge(variant.metadata(self))
|
@@ -165,6 +386,7 @@ class Lopata::ScenarioBuilder
|
|
165
386
|
end
|
166
387
|
end
|
167
388
|
|
389
|
+
# @private
|
168
390
|
class Variant
|
169
391
|
attr_reader :key, :title, :value, :option
|
170
392
|
|
@@ -213,6 +435,7 @@ class Lopata::ScenarioBuilder
|
|
213
435
|
end
|
214
436
|
end
|
215
437
|
|
438
|
+
# @private
|
216
439
|
class CalculatedValue
|
217
440
|
def initialize(&block)
|
218
441
|
@proc = block
|
@@ -223,6 +446,7 @@ class Lopata::ScenarioBuilder
|
|
223
446
|
end
|
224
447
|
end
|
225
448
|
|
449
|
+
# @private
|
226
450
|
class Option
|
227
451
|
attr_reader :variants, :key
|
228
452
|
def initialize(key, variants)
|
@@ -258,6 +482,7 @@ class Lopata::ScenarioBuilder
|
|
258
482
|
end
|
259
483
|
end
|
260
484
|
|
485
|
+
# @private
|
261
486
|
class Diagonal < Option
|
262
487
|
def level_variants
|
263
488
|
[next_variant]
|
data/lib/lopata/shared_step.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
module Lopata
|
2
|
+
# @private
|
2
3
|
class SharedStep
|
3
4
|
attr_reader :name, :block
|
4
5
|
|
5
6
|
class NotFound < StandardError; end
|
6
7
|
|
8
|
+
|
7
9
|
def self.register(name, &block)
|
8
10
|
raise ArgumentError, "Comma is not allowed in shared step name: '%s'" % name if name =~ /,/
|
9
11
|
registry[name] = new(name, &block)
|
data/lib/lopata/step.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module Lopata
|
2
|
+
# @private
|
2
3
|
class Step
|
3
4
|
attr_reader :block, :args, :condition, :method_name, :shared_step
|
4
5
|
# metadata overrien by the step.
|
@@ -26,7 +27,8 @@ module Lopata
|
|
26
27
|
end
|
27
28
|
end
|
28
29
|
|
29
|
-
#
|
30
|
+
# @private
|
31
|
+
# Used for action, setup, teardown, verify
|
30
32
|
class ActionStep < Step
|
31
33
|
def execution_steps(scenario, groups: [])
|
32
34
|
steps = []
|
@@ -66,6 +68,7 @@ module Lopata
|
|
66
68
|
end
|
67
69
|
end
|
68
70
|
|
71
|
+
# @private
|
69
72
|
# Used for context
|
70
73
|
class GroupStep < Step
|
71
74
|
|
@@ -94,6 +97,7 @@ module Lopata
|
|
94
97
|
end
|
95
98
|
end
|
96
99
|
|
100
|
+
#@private
|
97
101
|
class StepExecution
|
98
102
|
attr_reader :step, :status, :exception, :block, :pending_message, :groups
|
99
103
|
extend Forwardable
|
data/lib/lopata/version.rb
CHANGED
data/lib/lopata/world.rb
CHANGED
@@ -1,21 +1,15 @@
|
|
1
|
+
# Container for gobal non-configuration data
|
1
2
|
class Lopata::World
|
3
|
+
# Scenarios are selected for current run
|
4
|
+
# @return [Array<Lopata::Scenario::Execution>]
|
2
5
|
attr_reader :scenarios
|
3
6
|
|
7
|
+
# @private
|
4
8
|
def initialize
|
5
9
|
@scenarios = []
|
6
10
|
end
|
7
11
|
|
8
|
-
|
9
|
-
notify_observers(:started, self)
|
10
|
-
end
|
11
|
-
|
12
|
-
# Called at the end of test running.
|
13
|
-
#
|
14
|
-
# Notifies observers about testing finish
|
15
|
-
def finish
|
16
|
-
notify_observers(:finished, self)
|
17
|
-
end
|
18
|
-
|
12
|
+
# @private
|
19
13
|
def notify_observers(event, context)
|
20
14
|
observers.each do |observer|
|
21
15
|
observer.send event, context
|
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.1.
|
4
|
+
version: 0.1.7
|
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-06-
|
11
|
+
date: 2020-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|
@@ -87,6 +87,7 @@ executables:
|
|
87
87
|
extensions: []
|
88
88
|
extra_rdoc_files: []
|
89
89
|
files:
|
90
|
+
- ".yardopts"
|
90
91
|
- README.md
|
91
92
|
- exe/lopata
|
92
93
|
- lib/lopata.rb
|
@@ -137,5 +138,5 @@ requirements: []
|
|
137
138
|
rubygems_version: 3.0.3
|
138
139
|
signing_key:
|
139
140
|
specification_version: 4
|
140
|
-
summary: lopata-0.1.
|
141
|
+
summary: lopata-0.1.7
|
141
142
|
test_files: []
|