lopata 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
|