lopata 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/lopata.rb +1 -1
- data/lib/lopata/condition.rb +30 -0
- data/lib/lopata/config.rb +6 -4
- data/lib/lopata/loader.rb +26 -0
- data/lib/lopata/observers/web_logger.rb +129 -0
- data/lib/lopata/runner.rb +5 -7
- data/lib/lopata/scenario.rb +3 -22
- data/lib/lopata/scenario_builder.rb +26 -56
- data/lib/lopata/shared_step.rb +5 -5
- data/lib/lopata/step.rb +47 -3
- data/lib/lopata/version.rb +1 -1
- data/lib/lopata/world.rb +6 -26
- metadata +6 -5
- data/lib/lopata/download_dir.rb +0 -70
- data/lib/lopata/rspec/formatter.rb +0 -143
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9233d9fba2a55749f02037d5ca106c0cb9249debda448aac58ccc2cc724a378e
|
4
|
+
data.tar.gz: 65ea743e92f5084b6f04b52595296cb80a3d4540f4cb8ba8bc2c774cbc448aaf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a706fde4a5acc83b27400ce50c19137b7fd9f296bfb2ac4baeec55e50e5fe48b4bf6e90110611f4f69cd2dc9571a15d1de34e54e8bec094f0c2c2d5cc5ca1a87
|
7
|
+
data.tar.gz: 2d0764e801260489815f30b8572ffdff7bdf6572fc64b55d0e53c091f8f125925374ae9cf706b1fe0bc956a6d56ef23b6dd1bd767b7ce13de550b4878dc86f4f
|
data/README.md
CHANGED
@@ -14,7 +14,7 @@ Create new lopata project:
|
|
14
14
|
|
15
15
|
Setup environment: edit <project-name>/config/environments/qa.yml for setup project for testing.
|
16
16
|
|
17
|
-
Write tests: puts
|
17
|
+
Write tests: puts tests in <project-name>/scenarios folder. Define shared steps in <project-name>/shared_steps folder.
|
18
18
|
|
19
19
|
Run tests:
|
20
20
|
|
data/lib/lopata.rb
CHANGED
@@ -10,8 +10,8 @@ module Lopata
|
|
10
10
|
Lopata::ScenarioBuilder.define(*args, &block)
|
11
11
|
end
|
12
12
|
|
13
|
+
# Skip scenario definition. Option to temporary ignore scenario
|
13
14
|
def self.xdefine(*args, &block)
|
14
|
-
Lopata::ScenarioBuilder.xdefine(*args, &block)
|
15
15
|
end
|
16
16
|
|
17
17
|
def self.shared_step(name, &block)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Lopata
|
2
|
+
class Condition
|
3
|
+
attr_reader :condition, :positive
|
4
|
+
def initialize(condition, positive: true)
|
5
|
+
@condition, @positive = condition, positive
|
6
|
+
end
|
7
|
+
|
8
|
+
EMPTY = new({})
|
9
|
+
|
10
|
+
alias positive? positive
|
11
|
+
|
12
|
+
def match?(scenario)
|
13
|
+
matched = match_metadata?(scenario)
|
14
|
+
positive? ? matched : !matched
|
15
|
+
end
|
16
|
+
|
17
|
+
def match_metadata?(scenario)
|
18
|
+
metadata = scenario.metadata
|
19
|
+
case condition
|
20
|
+
when Hash
|
21
|
+
condition.keys.all? { |k| metadata[k] == condition[k] }
|
22
|
+
when Array
|
23
|
+
condition.map { |key| metadata[key] }.none?(&:nil?)
|
24
|
+
else
|
25
|
+
metadata[condition]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
data/lib/lopata/config.rb
CHANGED
@@ -49,10 +49,8 @@ module Lopata
|
|
49
49
|
|
50
50
|
def init_lopata_logging(build)
|
51
51
|
self.build_number = build
|
52
|
-
|
53
|
-
|
54
|
-
c.add_formatter Lopata::RSpec::Formatter
|
55
|
-
end
|
52
|
+
require 'lopata/observers/web_logger'
|
53
|
+
add_observer Lopata::Observers::WebLogger.new
|
56
54
|
end
|
57
55
|
|
58
56
|
def init_rerun
|
@@ -95,5 +93,9 @@ module Lopata
|
|
95
93
|
def world
|
96
94
|
@world ||= Lopata::World.new
|
97
95
|
end
|
96
|
+
|
97
|
+
def add_observer(observer)
|
98
|
+
world.observers << observer
|
99
|
+
end
|
98
100
|
end
|
99
101
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Lopata::Loader
|
2
|
+
extend self
|
3
|
+
|
4
|
+
# Loads scenarios for running in current session
|
5
|
+
#
|
6
|
+
# @param args [Array<String>] files to be load.
|
7
|
+
# All files from default location to be loaded if empty.
|
8
|
+
def load_scenarios(*args)
|
9
|
+
if args.empty?
|
10
|
+
load_all_scenarios
|
11
|
+
else
|
12
|
+
args.each do |file|
|
13
|
+
load File.expand_path(file)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Loads all scenarios from predefined paths
|
19
|
+
def load_all_scenarios
|
20
|
+
Dir["scenarios/**/*.rb"].each { |f| load File.expand_path(f) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def load_shared_steps
|
24
|
+
Dir["shared_steps/**/*rb"].each { |f| load File.expand_path(f) }
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Lopata
|
5
|
+
module Observers
|
6
|
+
class WebLogger < BaseObserver
|
7
|
+
def started(world)
|
8
|
+
raise "Build number is not initailzed in Lopata::Config" unless Lopata::Config.build_number
|
9
|
+
@client = Lopata::Client.new(Lopata::Config.build_number)
|
10
|
+
@client.start(world.scenarios.count)
|
11
|
+
end
|
12
|
+
|
13
|
+
def scenario_finished(scenario)
|
14
|
+
if scenario.failed?
|
15
|
+
backtrace = backtrace_for(scenario)
|
16
|
+
@client.add_attempt(scenario, Lopata::FAILED, error_message_for(scenario), backtrace)
|
17
|
+
else
|
18
|
+
@client.add_attempt(scenario, Lopata::PASSED)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# def example_pending(notification)
|
23
|
+
# example = notification.example
|
24
|
+
# @client.add_attempt(example, Lopata::PENDING, example.execution_result.pending_message)
|
25
|
+
# end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def error_message_for(scenario)
|
30
|
+
exception = scenario.steps.map(&:exception).compact.last
|
31
|
+
msg = ''
|
32
|
+
if exception
|
33
|
+
msg << "#{exception.class.name}: " unless exception.class.name =~ /RSpec/
|
34
|
+
msg << "#{exception.message.to_s}" if exception.message
|
35
|
+
end
|
36
|
+
(msg.length == 0) ? 'Empty message' : msg
|
37
|
+
end
|
38
|
+
|
39
|
+
def backtrace_for(scenario)
|
40
|
+
exception = scenario.steps.map(&:exception).compact.last
|
41
|
+
msg = ''
|
42
|
+
if exception
|
43
|
+
msg = exception.backtrace.join("\n")
|
44
|
+
msg << "\n"
|
45
|
+
end
|
46
|
+
msg
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
PASSED = 0
|
52
|
+
FAILED = 1
|
53
|
+
PENDING = 2
|
54
|
+
|
55
|
+
class Client
|
56
|
+
include HTTParty
|
57
|
+
base_uri Lopata::Config.lopata_host
|
58
|
+
|
59
|
+
attr_accessor :build_number
|
60
|
+
|
61
|
+
def initialize(build_number)
|
62
|
+
@build_number = build_number
|
63
|
+
end
|
64
|
+
|
65
|
+
def start(count)
|
66
|
+
@launch_id = JSON.parse(post("/projects/#{project_code}/builds/#{build_number}/launches.json", body: {total: count}).body)['id']
|
67
|
+
end
|
68
|
+
|
69
|
+
def add_attempt(scenario, status, msg = nil, backtrace = nil)
|
70
|
+
test = test_id(scenario)
|
71
|
+
request = { status: status}
|
72
|
+
request[:message] = msg if msg
|
73
|
+
request[:backtrace] = backtrace if backtrace
|
74
|
+
post("/tests/#{test}/attempts.json", body: request)
|
75
|
+
inc_finished
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_id(scenario)
|
79
|
+
request = {
|
80
|
+
test: {
|
81
|
+
project_code: project_code,
|
82
|
+
title: scenario.title,
|
83
|
+
scenario: scenario.title,
|
84
|
+
build_number: build_number
|
85
|
+
}
|
86
|
+
}
|
87
|
+
response = post("/tests.json", body: request)
|
88
|
+
JSON.parse(response.body)["id"]
|
89
|
+
end
|
90
|
+
|
91
|
+
def to_rerun
|
92
|
+
get_json("/projects/#{project_code}/builds/#{build_number}/suspects.json")
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_full_rescan
|
96
|
+
to_rerun + get_json("/projects/#{project_code}/builds/#{build_number}/failures.json")
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def get_json(url)
|
102
|
+
JSON.parse(self.class.get(url).body)
|
103
|
+
end
|
104
|
+
|
105
|
+
def post(*args)
|
106
|
+
self.class.post(*args)
|
107
|
+
end
|
108
|
+
|
109
|
+
def patch(*args)
|
110
|
+
self.class.patch(*args)
|
111
|
+
end
|
112
|
+
|
113
|
+
def inc_finished
|
114
|
+
@finished ||= 0
|
115
|
+
@finished += 1
|
116
|
+
response = patch("/launches/#{@launch_id}",
|
117
|
+
body: { finished: @finished }.to_json,
|
118
|
+
headers: { 'Content-Type' => 'application/json', 'Accept' => 'application/json' })
|
119
|
+
if response.code == 404
|
120
|
+
puts 'Launch has been cancelled. Exit.'
|
121
|
+
exit!
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def project_code
|
126
|
+
Lopata::Config.lopata_code
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/lib/lopata/runner.rb
CHANGED
@@ -2,8 +2,10 @@ require 'thor'
|
|
2
2
|
require_relative 'generators/app'
|
3
3
|
require_relative 'config'
|
4
4
|
require_relative 'world'
|
5
|
+
require_relative 'loader'
|
5
6
|
require_relative '../lopata'
|
6
7
|
require_relative 'observers'
|
8
|
+
require_relative 'condition'
|
7
9
|
|
8
10
|
module Lopata
|
9
11
|
class Runner < Thor
|
@@ -18,12 +20,10 @@ module Lopata
|
|
18
20
|
option :text, aliases: 't'
|
19
21
|
def test(*args)
|
20
22
|
configure_from_options
|
21
|
-
|
22
|
-
|
23
|
+
Lopata::Loader.load_shared_steps
|
24
|
+
Lopata::Loader.load_scenarios(*args)
|
23
25
|
world = Lopata::Config.world
|
24
|
-
world.
|
25
|
-
world.load_shared_steps
|
26
|
-
world.load_scenarios(*args)
|
26
|
+
world.start
|
27
27
|
world.scenarios.each { |s| s.run }
|
28
28
|
world.finish
|
29
29
|
end
|
@@ -49,8 +49,6 @@ module Lopata
|
|
49
49
|
}
|
50
50
|
Lopata::Config.init(options[:env])
|
51
51
|
Lopata::Config.initialize_test
|
52
|
-
# ENV['HOME'] = File.absolute_path('.') # disable warning on rspec loading on windows
|
53
|
-
# Lopata::Config.init_rspec
|
54
52
|
end
|
55
53
|
end
|
56
54
|
end
|
data/lib/lopata/scenario.rb
CHANGED
@@ -5,9 +5,9 @@ class Lopata::Scenario
|
|
5
5
|
|
6
6
|
attr_reader :title, :metadata, :steps, :status
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
@title =
|
10
|
-
@metadata =
|
8
|
+
def initialize(title, options_title, metadata = {})
|
9
|
+
@title = [title, options_title].compact.reject(&:empty?).join(' ')
|
10
|
+
@metadata = metadata
|
11
11
|
@steps = []
|
12
12
|
@status = :not_runned
|
13
13
|
end
|
@@ -33,29 +33,10 @@ class Lopata::Scenario
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
def run_step(method_name, *args, &block)
|
37
|
-
instance_exec(&block)
|
38
|
-
end
|
39
|
-
|
40
36
|
def world
|
41
37
|
@world ||= Lopata::Config.world
|
42
38
|
end
|
43
39
|
|
44
|
-
def convert_args(*args)
|
45
|
-
args.map do |arg|
|
46
|
-
case arg
|
47
|
-
# trait symbols as link to metadata.
|
48
|
-
when Symbol then metadata[arg]
|
49
|
-
else
|
50
|
-
arg
|
51
|
-
end
|
52
|
-
end.flatten
|
53
|
-
end
|
54
|
-
|
55
|
-
def separate_args(args)
|
56
|
-
args.map { |a| a.is_a?(String) && a =~ /,/ ? a.split(',').map(&:strip) : a }.flatten
|
57
|
-
end
|
58
|
-
|
59
40
|
def failed?
|
60
41
|
status == :failed
|
61
42
|
end
|
@@ -1,28 +1,30 @@
|
|
1
1
|
class Lopata::ScenarioBuilder
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
builder
|
2
|
+
attr_reader :title, :common_metadata
|
3
|
+
|
4
|
+
def self.define(title, metadata = {}, &block)
|
5
|
+
builder = new(title, metadata)
|
6
6
|
builder.instance_exec &block
|
7
7
|
builder.build
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
def initialize(title, metadata = {})
|
11
|
+
@title, @common_metadata = title, metadata
|
12
12
|
end
|
13
13
|
|
14
14
|
def build
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
raise "scenario required a name in first argument" unless args.first.is_a? String
|
19
|
-
scenario = Lopata::Scenario.new(*args)
|
15
|
+
option_combinations.each do |option_set|
|
16
|
+
metadata = common_metadata.merge(option_set.metadata)
|
17
|
+
scenario = Lopata::Scenario.new(title, option_set.title, metadata)
|
20
18
|
|
21
19
|
steps.each do |step|
|
20
|
+
next if step.condition && !step.condition.match?(scenario)
|
21
|
+
step.pre_steps(scenario).each { |s| scenario.steps << s }
|
22
22
|
scenario.steps << step
|
23
23
|
end
|
24
24
|
|
25
|
-
|
25
|
+
if Lopata::Config.after_scenario
|
26
|
+
scenario.steps << Lopata::Step.new(:after_scenario, &Lopata::Config.after_scenario)
|
27
|
+
end
|
26
28
|
|
27
29
|
world.scenarios << scenario
|
28
30
|
end
|
@@ -56,36 +58,22 @@ class Lopata::ScenarioBuilder
|
|
56
58
|
@skip_when && @skip_when.call(option_set)
|
57
59
|
end
|
58
60
|
|
59
|
-
%i{setup action it teardown}.each do |name|
|
61
|
+
%i{ setup action it teardown }.each do |name|
|
60
62
|
name_if = "%s_if" % name
|
61
63
|
name_unless = "%s_unless" % name
|
62
64
|
define_method name, ->(*args, &block) { add_step(name, *args, &block) }
|
63
|
-
define_method name_if, ->(condition, *args, &block) { add_step(name, *args,
|
64
|
-
define_method name_unless, ->(condition, *args, &block) { add_step(name, *args,
|
65
|
+
define_method name_if, ->(condition, *args, &block) { add_step(name, *args, condition: Lopata::Condition.new(condition), &block) }
|
66
|
+
define_method name_unless, ->(condition, *args, &block) { add_step(name, *args, condition: Lopata::Condition.new(condition, positive: false), &block) }
|
65
67
|
end
|
66
68
|
|
67
|
-
def add_step(method_name, *args,
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
69
|
+
def add_step(method_name, *args, condition: nil, &block)
|
70
|
+
step_class =
|
71
|
+
if method_name =~ /^(setup|action|teardown)/
|
72
|
+
Lopata::ActionStep
|
73
|
+
else
|
74
|
+
Lopata::Step
|
85
75
|
end
|
86
|
-
|
87
|
-
instance_exec(&block) if block
|
88
|
-
end
|
76
|
+
steps << step_class.new(method_name, *args, condition: condition, &block)
|
89
77
|
end
|
90
78
|
|
91
79
|
def steps
|
@@ -120,10 +108,6 @@ class Lopata::ScenarioBuilder
|
|
120
108
|
@roles ||= [Lopata::Config.default_role].compact
|
121
109
|
end
|
122
110
|
|
123
|
-
def title(value)
|
124
|
-
@title = value
|
125
|
-
end
|
126
|
-
|
127
111
|
def option(metadata_key, variants)
|
128
112
|
options << Option.new(metadata_key, variants)
|
129
113
|
end
|
@@ -161,22 +145,8 @@ class Lopata::ScenarioBuilder
|
|
161
145
|
combine(combinations, rest_options)
|
162
146
|
end
|
163
147
|
|
164
|
-
def
|
165
|
-
|
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
|
148
|
+
def world
|
149
|
+
@world ||= Lopata::Config.world
|
180
150
|
end
|
181
151
|
|
182
152
|
# Набор вариантов, собранный для одного теста
|
data/lib/lopata/shared_step.rb
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
module Lopata
|
2
2
|
class SharedStep
|
3
|
-
attr_reader :block
|
3
|
+
attr_reader :name, :block
|
4
4
|
|
5
5
|
class SharedStepNotFound < StandardError; end
|
6
6
|
|
7
7
|
def self.register(name, &block)
|
8
8
|
raise ArgumentError, "Comma is not allowed in shared step name: '%s'" % name if name =~ /,/
|
9
9
|
@shared_steps ||= {}
|
10
|
-
@shared_steps[name] = new(&block)
|
10
|
+
@shared_steps[name] = new(name, &block)
|
11
11
|
end
|
12
12
|
|
13
13
|
def self.find(name)
|
14
14
|
@shared_steps[name] or raise StandardError, "Shared step '%s' not found" % name
|
15
15
|
end
|
16
16
|
|
17
|
-
def initialize(&block)
|
18
|
-
@block = block
|
17
|
+
def initialize(name, &block)
|
18
|
+
@name, @block = name, block
|
19
19
|
end
|
20
20
|
|
21
21
|
def steps
|
@@ -23,7 +23,7 @@ module Lopata
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def build_steps
|
26
|
-
builder = Lopata::ScenarioBuilder.new
|
26
|
+
builder = Lopata::ScenarioBuilder.new(name)
|
27
27
|
builder.instance_exec(&block)
|
28
28
|
builder.steps
|
29
29
|
end
|
data/lib/lopata/step.rb
CHANGED
@@ -1,20 +1,21 @@
|
|
1
1
|
module Lopata
|
2
2
|
class Step
|
3
|
-
attr_reader :block, :status, :exception
|
3
|
+
attr_reader :block, :status, :exception, :args, :condition
|
4
4
|
|
5
|
-
def initialize(method_name, *args, &block)
|
5
|
+
def initialize(method_name, *args, condition: nil, &block)
|
6
6
|
@method_name = method_name
|
7
7
|
@args = args
|
8
8
|
@status = :not_started
|
9
9
|
@block = block
|
10
10
|
@exception = nil
|
11
|
+
@condition = condition || Lopata::Condition::EMPTY
|
11
12
|
end
|
12
13
|
|
13
14
|
def run(scenario)
|
14
15
|
@status = :running
|
15
16
|
world.notify_observers(:step_started, self)
|
16
17
|
begin
|
17
|
-
scenario
|
18
|
+
run_step(scenario)
|
18
19
|
@status = :passed
|
19
20
|
rescue Exception => e
|
20
21
|
@status = :failed
|
@@ -23,6 +24,10 @@ module Lopata
|
|
23
24
|
world.notify_observers(:step_finished, self)
|
24
25
|
end
|
25
26
|
|
27
|
+
def run_step(scenario)
|
28
|
+
scenario.instance_exec(&block) if block
|
29
|
+
end
|
30
|
+
|
26
31
|
def world
|
27
32
|
@world ||= Lopata::Config.world
|
28
33
|
end
|
@@ -38,5 +43,44 @@ module Lopata
|
|
38
43
|
def teardown?
|
39
44
|
%i{ teardown cleanup }.include?(@method_name)
|
40
45
|
end
|
46
|
+
|
47
|
+
def pre_steps(scenario)
|
48
|
+
[]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Used for action, setup, teardown
|
53
|
+
class ActionStep < Step
|
54
|
+
def pre_steps(scenario)
|
55
|
+
steps = []
|
56
|
+
convert_args(scenario).each do |step|
|
57
|
+
if step.is_a?(String)
|
58
|
+
Lopata::SharedStep.find(step).steps.each do |shared_step|
|
59
|
+
steps += shared_step.pre_steps(scenario)
|
60
|
+
steps << shared_step
|
61
|
+
end
|
62
|
+
elsif step.is_a?(Proc)
|
63
|
+
steps << Lopata::Step.new(method_name, &step)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
steps
|
67
|
+
end
|
68
|
+
|
69
|
+
def separate_args(args)
|
70
|
+
args.map { |a| a.is_a?(String) && a =~ /,/ ? a.split(',').map(&:strip) : a }.flatten
|
71
|
+
end
|
72
|
+
|
73
|
+
def convert_args(scenario)
|
74
|
+
flat_args = separate_args(args.flatten)
|
75
|
+
flat_args.map do |arg|
|
76
|
+
case arg
|
77
|
+
# trait symbols as link to metadata.
|
78
|
+
when Symbol then scenario.metadata[arg]
|
79
|
+
else
|
80
|
+
arg
|
81
|
+
end
|
82
|
+
end.flatten
|
83
|
+
end
|
84
|
+
|
41
85
|
end
|
42
86
|
end
|
data/lib/lopata/version.rb
CHANGED
data/lib/lopata/world.rb
CHANGED
@@ -1,32 +1,12 @@
|
|
1
1
|
class Lopata::World
|
2
|
-
attr_reader :scenarios
|
2
|
+
attr_reader :scenarios
|
3
3
|
|
4
4
|
def initialize
|
5
5
|
@scenarios = []
|
6
|
-
@observers = []
|
7
6
|
end
|
8
7
|
|
9
|
-
|
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) }
|
8
|
+
def start
|
9
|
+
notify_observers(:started, self)
|
30
10
|
end
|
31
11
|
|
32
12
|
# Called at the end of test running.
|
@@ -37,13 +17,13 @@ class Lopata::World
|
|
37
17
|
end
|
38
18
|
|
39
19
|
def notify_observers(event, context)
|
40
|
-
|
20
|
+
observers.each do |observer|
|
41
21
|
observer.send event, context
|
42
22
|
end
|
43
23
|
end
|
44
24
|
|
45
25
|
# Define observers based on configuration
|
46
|
-
def
|
47
|
-
@observers
|
26
|
+
def observers
|
27
|
+
@observers ||= [Lopata::Observers::ConsoleOutputObserver.new]
|
48
28
|
end
|
49
29
|
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.1.
|
4
|
+
version: 0.1.1
|
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-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|
@@ -90,8 +90,8 @@ files:
|
|
90
90
|
- README.md
|
91
91
|
- exe/lopata
|
92
92
|
- lib/lopata.rb
|
93
|
+
- lib/lopata/condition.rb
|
93
94
|
- lib/lopata/config.rb
|
94
|
-
- lib/lopata/download_dir.rb
|
95
95
|
- lib/lopata/generators/app.rb
|
96
96
|
- lib/lopata/generators/templates/.rspec
|
97
97
|
- lib/lopata/generators/templates/Gemfile
|
@@ -100,12 +100,13 @@ files:
|
|
100
100
|
- lib/lopata/generators/templates/config/initializers/capybara.rb
|
101
101
|
- lib/lopata/generators/templates/spec/spec_helper.rb
|
102
102
|
- lib/lopata/id.rb
|
103
|
+
- lib/lopata/loader.rb
|
103
104
|
- lib/lopata/observers.rb
|
104
105
|
- lib/lopata/observers/base_observer.rb
|
105
106
|
- lib/lopata/observers/console_output_observer.rb
|
107
|
+
- lib/lopata/observers/web_logger.rb
|
106
108
|
- lib/lopata/rspec/ar_dsl.rb
|
107
109
|
- lib/lopata/rspec/dsl.rb
|
108
|
-
- lib/lopata/rspec/formatter.rb
|
109
110
|
- lib/lopata/rspec/role.rb
|
110
111
|
- lib/lopata/runner.rb
|
111
112
|
- lib/lopata/scenario.rb
|
@@ -136,5 +137,5 @@ requirements: []
|
|
136
137
|
rubygems_version: 3.0.3
|
137
138
|
signing_key:
|
138
139
|
specification_version: 4
|
139
|
-
summary: lopata-0.1.
|
140
|
+
summary: lopata-0.1.1
|
140
141
|
test_files: []
|
data/lib/lopata/download_dir.rb
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
module Lopata
|
2
|
-
module DownloadDir
|
3
|
-
RELATIVE_PATH = File.join('.', 'tmp', 'target')
|
4
|
-
|
5
|
-
extend self
|
6
|
-
|
7
|
-
def path
|
8
|
-
@path ||= File.absolute_path(RELATIVE_PATH)
|
9
|
-
end
|
10
|
-
|
11
|
-
def empty!
|
12
|
-
FileUtils.rm Dir.glob(File.join(path, '*'))
|
13
|
-
end
|
14
|
-
|
15
|
-
def ensure_exist
|
16
|
-
FileUtils::mkdir_p path unless Dir.exist?(path)
|
17
|
-
end
|
18
|
-
|
19
|
-
def has_file?(file_name)
|
20
|
-
require 'timeout'
|
21
|
-
target_file = filepath(file_name)
|
22
|
-
Timeout.timeout(10) do
|
23
|
-
sleep 0.1 until File.exist?(target_file)
|
24
|
-
true
|
25
|
-
end
|
26
|
-
rescue Timeout::Error
|
27
|
-
false
|
28
|
-
end
|
29
|
-
|
30
|
-
def filepath(name)
|
31
|
-
File.join(path, name)
|
32
|
-
end
|
33
|
-
|
34
|
-
def init_capybara
|
35
|
-
target_path = path
|
36
|
-
target_path = target_path.gsub('/', '\\') if Gem.win_platform?
|
37
|
-
|
38
|
-
profile = Selenium::WebDriver::Firefox::Profile.new
|
39
|
-
profile['browser.download.folderList'] = 2
|
40
|
-
profile['browser.download.manager.showWhenStarting'] = false
|
41
|
-
ensure_exist
|
42
|
-
profile['browser.download.dir'] = target_path
|
43
|
-
profile['browser.download.downloadDir'] = target_path
|
44
|
-
profile['browser.download.defaultFolder'] = target_path
|
45
|
-
profile['browser.helperApps.alwaysAsk.force'] = false
|
46
|
-
profile['browser.download.useDownloadDir'] = true
|
47
|
-
profile['browser.helperApps.neverAsk.saveToDisk'] =
|
48
|
-
%w{
|
49
|
-
application/octet-stream
|
50
|
-
application/msword
|
51
|
-
application/pdf
|
52
|
-
application/x-pdf
|
53
|
-
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
54
|
-
application/vnd.ms-excel
|
55
|
-
application/xml
|
56
|
-
application/json
|
57
|
-
}.join(', ')
|
58
|
-
profile['pdfjs.disabled'] = true
|
59
|
-
profile['plugin.scan.Acrobat'] = "99.0"
|
60
|
-
profile['plugin.scan.plid.all'] = false
|
61
|
-
|
62
|
-
Capybara.register_driver :selenium_with_download do |app|
|
63
|
-
Capybara::Selenium::Driver.new(app, browser: :firefox, profile: profile)
|
64
|
-
end
|
65
|
-
|
66
|
-
Capybara.default_driver = :selenium_with_download
|
67
|
-
# Capybara.default_max_wait_time = 10
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
@@ -1,143 +0,0 @@
|
|
1
|
-
require 'rspec/core/formatters/base_formatter'
|
2
|
-
require 'httparty'
|
3
|
-
require 'json'
|
4
|
-
require 'lopata/config'
|
5
|
-
|
6
|
-
module Lopata
|
7
|
-
module RSpec
|
8
|
-
class Formatter < ::RSpec::Core::Formatters::BaseFormatter
|
9
|
-
::RSpec::Core::Formatters.register self, :start, :example_passed, :example_pending, :example_failed
|
10
|
-
|
11
|
-
def start(notification)
|
12
|
-
raise "Build number is not initailzed in Lopata::Config" unless Lopata::Config.build_number
|
13
|
-
@client = Lopata::Client.new(Lopata::Config.build_number)
|
14
|
-
@client.start(notification.count)
|
15
|
-
end
|
16
|
-
|
17
|
-
def example_passed(notification)
|
18
|
-
@client.add_attempt(notification.example, Lopata::PASSED)
|
19
|
-
end
|
20
|
-
|
21
|
-
def example_failed(notification)
|
22
|
-
example = notification.example
|
23
|
-
backtrace = backtrace_for(notification)
|
24
|
-
@client.add_attempt(example, Lopata::FAILED, error_message_for(example), backtrace)
|
25
|
-
end
|
26
|
-
|
27
|
-
def example_pending(notification)
|
28
|
-
example = notification.example
|
29
|
-
@client.add_attempt(example, Lopata::PENDING, example.execution_result.pending_message)
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def error_message_for(example)
|
35
|
-
exception = example.execution_result.exception
|
36
|
-
msg = ''
|
37
|
-
msg << "#{exception.class.name}: " unless exception.class.name =~ /RSpec/
|
38
|
-
msg << "#{exception.message.to_s}" if exception.message
|
39
|
-
msg.blank? ? 'Empty message' : msg
|
40
|
-
end
|
41
|
-
|
42
|
-
def backtrace_for(notification)
|
43
|
-
example = notification.example
|
44
|
-
exception = example.execution_result.exception
|
45
|
-
msg = notification.formatted_backtrace.map(&:strip).join("\n")
|
46
|
-
msg << "\n"
|
47
|
-
if shared_group = find_shared_group(example)
|
48
|
-
msg << "# Shared Example Group: \"#{shared_group.metadata[:shared_group_name]}\" called from "
|
49
|
-
msg << "#{backtrace_line(shared_group.metadata[:example_group][:location])}\n"
|
50
|
-
end
|
51
|
-
msg
|
52
|
-
end
|
53
|
-
|
54
|
-
def find_shared_group(example)
|
55
|
-
group_and_parent_groups(example).find {|group| group.metadata[:shared_group_name]}
|
56
|
-
end
|
57
|
-
|
58
|
-
def group_and_parent_groups(example)
|
59
|
-
example.example_group.parent_groups + [example.example_group]
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
PASSED = 0
|
65
|
-
FAILED = 1
|
66
|
-
PENDING = 2
|
67
|
-
|
68
|
-
class Client
|
69
|
-
include HTTParty
|
70
|
-
base_uri Lopata::Config.lopata_host
|
71
|
-
|
72
|
-
attr_accessor :build_number
|
73
|
-
|
74
|
-
def initialize(build_number)
|
75
|
-
@build_number = build_number
|
76
|
-
end
|
77
|
-
|
78
|
-
def start(count)
|
79
|
-
@launch_id = JSON.parse(post("/projects/#{project_code}/builds/#{build_number}/launches.json", body: {total: count}).body)['id']
|
80
|
-
end
|
81
|
-
|
82
|
-
def add_attempt(example, status, msg = nil, backtrace = nil)
|
83
|
-
test = test_id(example)
|
84
|
-
request = { status: status}
|
85
|
-
request[:message] = msg if msg
|
86
|
-
request[:backtrace] = backtrace if backtrace
|
87
|
-
post("/tests/#{test}/attempts.json", body: request)
|
88
|
-
inc_finished
|
89
|
-
end
|
90
|
-
|
91
|
-
def test_id(example)
|
92
|
-
request = {
|
93
|
-
test: {
|
94
|
-
project_code: project_code,
|
95
|
-
title: example.full_description,
|
96
|
-
scenario: example.metadata[:example_group][:full_description],
|
97
|
-
build_number: build_number
|
98
|
-
}
|
99
|
-
}
|
100
|
-
response = post("/tests.json", body: request)
|
101
|
-
JSON.parse(response.body)["id"]
|
102
|
-
end
|
103
|
-
|
104
|
-
def to_rerun
|
105
|
-
get_json("/projects/#{project_code}/builds/#{build_number}/suspects.json")
|
106
|
-
end
|
107
|
-
|
108
|
-
def to_full_rescan
|
109
|
-
to_rerun + get_json("/projects/#{project_code}/builds/#{build_number}/failures.json")
|
110
|
-
end
|
111
|
-
|
112
|
-
private
|
113
|
-
|
114
|
-
def get_json(url)
|
115
|
-
JSON.parse(self.class.get(url).body)
|
116
|
-
end
|
117
|
-
|
118
|
-
def post(*args)
|
119
|
-
self.class.post(*args)
|
120
|
-
end
|
121
|
-
|
122
|
-
def patch(*args)
|
123
|
-
self.class.patch(*args)
|
124
|
-
end
|
125
|
-
|
126
|
-
def inc_finished
|
127
|
-
@finished ||= 0
|
128
|
-
@finished += 1
|
129
|
-
response = patch("/launches/#{@launch_id}",
|
130
|
-
body: { finished: @finished }.to_json,
|
131
|
-
headers: { 'Content-Type' => 'application/json', 'Accept' => 'application/json' })
|
132
|
-
if response.code == 404
|
133
|
-
puts 'Launch has been cancelled. Exit.'
|
134
|
-
exit!
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
def project_code
|
139
|
-
Lopata::Config.lopata_code
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|