lopata 0.1.13 → 0.1.14
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 +25 -25
- data/exe/lopata +11 -11
- data/lib/lopata.rb +74 -74
- data/lib/lopata/active_record.rb +135 -135
- data/lib/lopata/condition.rb +30 -30
- data/lib/lopata/configuration.rb +125 -125
- data/lib/lopata/environment.rb +35 -35
- data/lib/lopata/factory_bot.rb +72 -72
- data/lib/lopata/generators/app.rb +42 -42
- data/lib/lopata/generators/templates/Gemfile +7 -7
- data/lib/lopata/generators/templates/Lopatafile +20 -20
- data/lib/lopata/generators/templates/config/environments/qa.yml +7 -7
- data/lib/lopata/generators/templates/config/initializers/capybara.rb +1 -1
- data/lib/lopata/id.rb +22 -22
- data/lib/lopata/loader.rb +31 -31
- data/lib/lopata/observers.rb +4 -4
- data/lib/lopata/observers/backtrace_formatter.rb +103 -103
- data/lib/lopata/observers/base_observer.rb +33 -33
- data/lib/lopata/observers/console_output_observer.rb +100 -100
- data/lib/lopata/observers/web_logger.rb +130 -130
- data/lib/lopata/role.rb +109 -109
- data/lib/lopata/runner.rb +67 -67
- data/lib/lopata/scenario.rb +136 -136
- data/lib/lopata/scenario_builder.rb +497 -497
- data/lib/lopata/shared_step.rb +38 -38
- data/lib/lopata/step.rb +191 -191
- data/lib/lopata/version.rb +6 -6
- data/lib/lopata/world.rb +24 -24
- metadata +4 -4
@@ -1,34 +1,34 @@
|
|
1
|
-
module Lopata
|
2
|
-
module Observers
|
3
|
-
# Lopata allows observe scenarios execution.
|
4
|
-
# All the observers are subclasses of Lopata::Observers::BaseObserver.
|
5
|
-
#
|
6
|
-
# @see Lopata::Observers::ConsoleOutputObserver for implementation example
|
7
|
-
class BaseObserver
|
8
|
-
# Called before scenarios execution.
|
9
|
-
# All the scenarios are prepared at the moment, so it may be used to get number of scenarios
|
10
|
-
# via world.scenarios.count
|
11
|
-
#
|
12
|
-
# @param world [Lopata::World]
|
13
|
-
def started(world)
|
14
|
-
end
|
15
|
-
|
16
|
-
# Called after all scenarios execution.
|
17
|
-
# All the scenarios are finished at the moment, so it may be used for output statistics.
|
18
|
-
#
|
19
|
-
# @param world [Lopata::World]
|
20
|
-
def finished(world)
|
21
|
-
end
|
22
|
-
|
23
|
-
# Called before single scenario execution.
|
24
|
-
# @param scenario [Lopata::Scenario::Execution]
|
25
|
-
def scenario_started(scenario)
|
26
|
-
end
|
27
|
-
|
28
|
-
# Called after single scenario execution.
|
29
|
-
# @param scenario [Lopata::Scenario::Execution]
|
30
|
-
def scenario_finished(scenario)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
1
|
+
module Lopata
|
2
|
+
module Observers
|
3
|
+
# Lopata allows observe scenarios execution.
|
4
|
+
# All the observers are subclasses of Lopata::Observers::BaseObserver.
|
5
|
+
#
|
6
|
+
# @see Lopata::Observers::ConsoleOutputObserver for implementation example
|
7
|
+
class BaseObserver
|
8
|
+
# Called before scenarios execution.
|
9
|
+
# All the scenarios are prepared at the moment, so it may be used to get number of scenarios
|
10
|
+
# via world.scenarios.count
|
11
|
+
#
|
12
|
+
# @param world [Lopata::World]
|
13
|
+
def started(world)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Called after all scenarios execution.
|
17
|
+
# All the scenarios are finished at the moment, so it may be used for output statistics.
|
18
|
+
#
|
19
|
+
# @param world [Lopata::World]
|
20
|
+
def finished(world)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Called before single scenario execution.
|
24
|
+
# @param scenario [Lopata::Scenario::Execution]
|
25
|
+
def scenario_started(scenario)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Called after single scenario execution.
|
29
|
+
# @param scenario [Lopata::Scenario::Execution]
|
30
|
+
def scenario_finished(scenario)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
34
|
end
|
@@ -1,100 +1,100 @@
|
|
1
|
-
require_relative 'backtrace_formatter'
|
2
|
-
require 'forwardable'
|
3
|
-
|
4
|
-
module Lopata
|
5
|
-
module Observers
|
6
|
-
# @private
|
7
|
-
class ConsoleOutputObserver < BaseObserver
|
8
|
-
extend Forwardable
|
9
|
-
# @private
|
10
|
-
attr_reader :output
|
11
|
-
# @private
|
12
|
-
def_delegators :output, :puts, :flush
|
13
|
-
|
14
|
-
def initialize
|
15
|
-
@output = $stdout
|
16
|
-
end
|
17
|
-
|
18
|
-
# @see Lopata::Observers::BaseObserver#finished
|
19
|
-
def finished(world)
|
20
|
-
total = statuses.values.inject(0, &:+)
|
21
|
-
counts = statuses.map do |status, count|
|
22
|
-
colored("%d %s", status) % [count, status]
|
23
|
-
end
|
24
|
-
details = counts.empty? ? "" : "(%s)" % counts.join(', ')
|
25
|
-
puts "#{total} scenario%s %s" % [total == 1 ? '' : 's', details]
|
26
|
-
end
|
27
|
-
|
28
|
-
# @see Lopata::Observers::BaseObserver#scenario_finished
|
29
|
-
def scenario_finished(scenario)
|
30
|
-
message = "#{scenario.title} #{bold(scenario.status.to_s.upcase)}"
|
31
|
-
puts colored(message, scenario.status)
|
32
|
-
|
33
|
-
statuses[scenario.status] ||= 0
|
34
|
-
statuses[scenario.status] += 1
|
35
|
-
|
36
|
-
if scenario.failed?
|
37
|
-
scenario.steps.each do |step|
|
38
|
-
puts colored(" #{status_marker(step.status)} #{step.title}", step.status)
|
39
|
-
puts indent(4, backtrace_formatter.error_message(step.exception, include_backtrace: true)) if step.failed?
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
flush
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
def colored(text, status)
|
49
|
-
case status
|
50
|
-
when :failed then red(text)
|
51
|
-
when :passed then green(text)
|
52
|
-
when :skipped then cyan(text)
|
53
|
-
when :pending then yellow(text)
|
54
|
-
else text
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
{
|
59
|
-
red: 31,
|
60
|
-
green: 32,
|
61
|
-
cyan: 36,
|
62
|
-
yellow: 33,
|
63
|
-
bold: 1,
|
64
|
-
}.each do |color, code|
|
65
|
-
define_method(color) do |text|
|
66
|
-
wrap(text, code)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def wrap(text, code)
|
71
|
-
"\e[#{code}m#{text}\e[0m"
|
72
|
-
end
|
73
|
-
|
74
|
-
def backtrace_formatter
|
75
|
-
@backtrace_formatter ||= Lopata::Observers::BacktraceFormatter.new
|
76
|
-
end
|
77
|
-
|
78
|
-
def status_marker(status)
|
79
|
-
case status
|
80
|
-
when :failed then "[!]"
|
81
|
-
when :skipped then "[-]"
|
82
|
-
when :pending then "[?]"
|
83
|
-
else "[+]"
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
# Adds indent to text
|
88
|
-
# @param cols [Number] number of spaces to be added
|
89
|
-
# @param text [String] text to add indent
|
90
|
-
# @return [String] text with indent
|
91
|
-
def indent(cols, text)
|
92
|
-
text.split("\n").map { |line| " " * cols + line }.join("\n")
|
93
|
-
end
|
94
|
-
|
95
|
-
def statuses
|
96
|
-
@statuses ||= {}
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
1
|
+
require_relative 'backtrace_formatter'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module Lopata
|
5
|
+
module Observers
|
6
|
+
# @private
|
7
|
+
class ConsoleOutputObserver < BaseObserver
|
8
|
+
extend Forwardable
|
9
|
+
# @private
|
10
|
+
attr_reader :output
|
11
|
+
# @private
|
12
|
+
def_delegators :output, :puts, :flush
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@output = $stdout
|
16
|
+
end
|
17
|
+
|
18
|
+
# @see Lopata::Observers::BaseObserver#finished
|
19
|
+
def finished(world)
|
20
|
+
total = statuses.values.inject(0, &:+)
|
21
|
+
counts = statuses.map do |status, count|
|
22
|
+
colored("%d %s", status) % [count, status]
|
23
|
+
end
|
24
|
+
details = counts.empty? ? "" : "(%s)" % counts.join(', ')
|
25
|
+
puts "#{total} scenario%s %s" % [total == 1 ? '' : 's', details]
|
26
|
+
end
|
27
|
+
|
28
|
+
# @see Lopata::Observers::BaseObserver#scenario_finished
|
29
|
+
def scenario_finished(scenario)
|
30
|
+
message = "#{scenario.title} #{bold(scenario.status.to_s.upcase)}"
|
31
|
+
puts colored(message, scenario.status)
|
32
|
+
|
33
|
+
statuses[scenario.status] ||= 0
|
34
|
+
statuses[scenario.status] += 1
|
35
|
+
|
36
|
+
if scenario.failed?
|
37
|
+
scenario.steps.each do |step|
|
38
|
+
puts colored(" #{status_marker(step.status)} #{step.title}", step.status)
|
39
|
+
puts indent(4, backtrace_formatter.error_message(step.exception, include_backtrace: true)) if step.failed?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
flush
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def colored(text, status)
|
49
|
+
case status
|
50
|
+
when :failed then red(text)
|
51
|
+
when :passed then green(text)
|
52
|
+
when :skipped then cyan(text)
|
53
|
+
when :pending then yellow(text)
|
54
|
+
else text
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
{
|
59
|
+
red: 31,
|
60
|
+
green: 32,
|
61
|
+
cyan: 36,
|
62
|
+
yellow: 33,
|
63
|
+
bold: 1,
|
64
|
+
}.each do |color, code|
|
65
|
+
define_method(color) do |text|
|
66
|
+
wrap(text, code)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def wrap(text, code)
|
71
|
+
"\e[#{code}m#{text}\e[0m"
|
72
|
+
end
|
73
|
+
|
74
|
+
def backtrace_formatter
|
75
|
+
@backtrace_formatter ||= Lopata::Observers::BacktraceFormatter.new
|
76
|
+
end
|
77
|
+
|
78
|
+
def status_marker(status)
|
79
|
+
case status
|
80
|
+
when :failed then "[!]"
|
81
|
+
when :skipped then "[-]"
|
82
|
+
when :pending then "[?]"
|
83
|
+
else "[+]"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Adds indent to text
|
88
|
+
# @param cols [Number] number of spaces to be added
|
89
|
+
# @param text [String] text to add indent
|
90
|
+
# @return [String] text with indent
|
91
|
+
def indent(cols, text)
|
92
|
+
text.split("\n").map { |line| " " * cols + line }.join("\n")
|
93
|
+
end
|
94
|
+
|
95
|
+
def statuses
|
96
|
+
@statuses ||= {}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -1,131 +1,131 @@
|
|
1
|
-
require 'httparty'
|
2
|
-
require 'json'
|
3
|
-
require_relative 'backtrace_formatter'
|
4
|
-
|
5
|
-
module Lopata
|
6
|
-
module Observers
|
7
|
-
# @private
|
8
|
-
class WebLogger < BaseObserver
|
9
|
-
def started(world)
|
10
|
-
@client = Lopata::Client.new
|
11
|
-
@client.start(world.scenarios.count)
|
12
|
-
@finished = 0
|
13
|
-
end
|
14
|
-
|
15
|
-
def scenario_finished(scenario)
|
16
|
-
@finished += 1
|
17
|
-
@client.add_attempt(scenario, @finished)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# @private
|
23
|
-
PASSED = 0
|
24
|
-
# @private
|
25
|
-
FAILED = 1
|
26
|
-
# @private
|
27
|
-
PENDING = 2
|
28
|
-
# @private
|
29
|
-
SKIPPED = 5
|
30
|
-
|
31
|
-
# @private
|
32
|
-
class Client
|
33
|
-
include HTTParty
|
34
|
-
|
35
|
-
attr_reader :url, :project_code, :build_number
|
36
|
-
|
37
|
-
def initialize
|
38
|
-
params = Lopata.configuration.web_logging_params
|
39
|
-
raise "Web logging is not initailzed" unless params
|
40
|
-
@url = HTTParty.normalize_base_uri(params[:url])
|
41
|
-
@project_code = params[:project_code]
|
42
|
-
@build_number = params[:build_number]
|
43
|
-
end
|
44
|
-
|
45
|
-
def start(count)
|
46
|
-
@launch_id = JSON.parse(post("/projects/#{project_code}/builds/#{build_number}/launches.json", body: {total: count}).body)['id']
|
47
|
-
end
|
48
|
-
|
49
|
-
def add_attempt(scenario, finished)
|
50
|
-
status = scenario.failed? ? Lopata::FAILED : Lopata::PASSED
|
51
|
-
steps = scenario.steps.map { |s| step_hash(s) }
|
52
|
-
request = { status: status, steps: steps, launch: { id: @launch_id, finished: finished } }
|
53
|
-
test = test_id(scenario)
|
54
|
-
post("/tests/#{test}/attempts.json", body: request)
|
55
|
-
end
|
56
|
-
|
57
|
-
def step_hash(step)
|
58
|
-
hash = { status: step.status, title: step.title }
|
59
|
-
if step.failed?
|
60
|
-
hash[:message] = error_message_for(step)
|
61
|
-
hash[:backtrace] = backtrace_for(step)
|
62
|
-
end
|
63
|
-
hash
|
64
|
-
end
|
65
|
-
|
66
|
-
def test_id(scenario)
|
67
|
-
request = {
|
68
|
-
test: {
|
69
|
-
project_code: project_code,
|
70
|
-
title: scenario.title,
|
71
|
-
scenario: scenario.title,
|
72
|
-
build_number: build_number
|
73
|
-
}
|
74
|
-
}
|
75
|
-
response = post("/tests.json", body: request)
|
76
|
-
JSON.parse(response.body)["id"]
|
77
|
-
end
|
78
|
-
|
79
|
-
def to_rerun
|
80
|
-
get_json("/projects/#{project_code}/builds/#{build_number}/suspects.json")
|
81
|
-
end
|
82
|
-
|
83
|
-
def to_full_rescan
|
84
|
-
to_rerun + get_json("/projects/#{project_code}/builds/#{build_number}/failures.json")
|
85
|
-
end
|
86
|
-
|
87
|
-
private
|
88
|
-
|
89
|
-
def get_json(path)
|
90
|
-
JSON.parse(self.class.get(path, base_uri: url).body)
|
91
|
-
end
|
92
|
-
|
93
|
-
def post(*args)
|
94
|
-
self.class.post(*with_base_uri(args))
|
95
|
-
end
|
96
|
-
|
97
|
-
def patch(*args)
|
98
|
-
self.class.patch(*with_base_uri(args))
|
99
|
-
end
|
100
|
-
|
101
|
-
def with_base_uri(args = [])
|
102
|
-
if args.last.is_a? Hash
|
103
|
-
args.last[:base_uri] = url
|
104
|
-
else
|
105
|
-
args << { base_uri: url }
|
106
|
-
end
|
107
|
-
args
|
108
|
-
end
|
109
|
-
|
110
|
-
def error_message_for(step)
|
111
|
-
if step.exception
|
112
|
-
backtrace_formatter.error_message(step.exception)
|
113
|
-
else
|
114
|
-
'Empty error message'
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def backtrace_for(step)
|
119
|
-
msg = ''
|
120
|
-
if step.exception
|
121
|
-
msg = backtrace_formatter.format(step.exception.backtrace).join("\n")
|
122
|
-
msg << "\n"
|
123
|
-
end
|
124
|
-
msg
|
125
|
-
end
|
126
|
-
|
127
|
-
def backtrace_formatter
|
128
|
-
@backtrace_formatter ||= Lopata::Observers::BacktraceFormatter.new
|
129
|
-
end
|
130
|
-
end
|
1
|
+
require 'httparty'
|
2
|
+
require 'json'
|
3
|
+
require_relative 'backtrace_formatter'
|
4
|
+
|
5
|
+
module Lopata
|
6
|
+
module Observers
|
7
|
+
# @private
|
8
|
+
class WebLogger < BaseObserver
|
9
|
+
def started(world)
|
10
|
+
@client = Lopata::Client.new
|
11
|
+
@client.start(world.scenarios.count)
|
12
|
+
@finished = 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def scenario_finished(scenario)
|
16
|
+
@finished += 1
|
17
|
+
@client.add_attempt(scenario, @finished)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# @private
|
23
|
+
PASSED = 0
|
24
|
+
# @private
|
25
|
+
FAILED = 1
|
26
|
+
# @private
|
27
|
+
PENDING = 2
|
28
|
+
# @private
|
29
|
+
SKIPPED = 5
|
30
|
+
|
31
|
+
# @private
|
32
|
+
class Client
|
33
|
+
include HTTParty
|
34
|
+
|
35
|
+
attr_reader :url, :project_code, :build_number
|
36
|
+
|
37
|
+
def initialize
|
38
|
+
params = Lopata.configuration.web_logging_params
|
39
|
+
raise "Web logging is not initailzed" unless params
|
40
|
+
@url = HTTParty.normalize_base_uri(params[:url])
|
41
|
+
@project_code = params[:project_code]
|
42
|
+
@build_number = params[:build_number]
|
43
|
+
end
|
44
|
+
|
45
|
+
def start(count)
|
46
|
+
@launch_id = JSON.parse(post("/projects/#{project_code}/builds/#{build_number}/launches.json", body: {total: count}).body)['id']
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_attempt(scenario, finished)
|
50
|
+
status = scenario.failed? ? Lopata::FAILED : Lopata::PASSED
|
51
|
+
steps = scenario.steps.map { |s| step_hash(s) }
|
52
|
+
request = { status: status, steps: steps, launch: { id: @launch_id, finished: finished } }
|
53
|
+
test = test_id(scenario)
|
54
|
+
post("/tests/#{test}/attempts.json", body: request)
|
55
|
+
end
|
56
|
+
|
57
|
+
def step_hash(step)
|
58
|
+
hash = { status: step.status, title: step.title }
|
59
|
+
if step.failed?
|
60
|
+
hash[:message] = error_message_for(step)
|
61
|
+
hash[:backtrace] = backtrace_for(step)
|
62
|
+
end
|
63
|
+
hash
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_id(scenario)
|
67
|
+
request = {
|
68
|
+
test: {
|
69
|
+
project_code: project_code,
|
70
|
+
title: scenario.title,
|
71
|
+
scenario: scenario.title,
|
72
|
+
build_number: build_number
|
73
|
+
}
|
74
|
+
}
|
75
|
+
response = post("/tests.json", body: request)
|
76
|
+
JSON.parse(response.body)["id"]
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_rerun
|
80
|
+
get_json("/projects/#{project_code}/builds/#{build_number}/suspects.json")
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_full_rescan
|
84
|
+
to_rerun + get_json("/projects/#{project_code}/builds/#{build_number}/failures.json")
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def get_json(path)
|
90
|
+
JSON.parse(self.class.get(path, base_uri: url).body)
|
91
|
+
end
|
92
|
+
|
93
|
+
def post(*args)
|
94
|
+
self.class.post(*with_base_uri(args))
|
95
|
+
end
|
96
|
+
|
97
|
+
def patch(*args)
|
98
|
+
self.class.patch(*with_base_uri(args))
|
99
|
+
end
|
100
|
+
|
101
|
+
def with_base_uri(args = [])
|
102
|
+
if args.last.is_a? Hash
|
103
|
+
args.last[:base_uri] = url
|
104
|
+
else
|
105
|
+
args << { base_uri: url }
|
106
|
+
end
|
107
|
+
args
|
108
|
+
end
|
109
|
+
|
110
|
+
def error_message_for(step)
|
111
|
+
if step.exception
|
112
|
+
backtrace_formatter.error_message(step.exception)
|
113
|
+
else
|
114
|
+
'Empty error message'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def backtrace_for(step)
|
119
|
+
msg = ''
|
120
|
+
if step.exception
|
121
|
+
msg = backtrace_formatter.format(step.exception.backtrace).join("\n")
|
122
|
+
msg << "\n"
|
123
|
+
end
|
124
|
+
msg
|
125
|
+
end
|
126
|
+
|
127
|
+
def backtrace_formatter
|
128
|
+
@backtrace_formatter ||= Lopata::Observers::BacktraceFormatter.new
|
129
|
+
end
|
130
|
+
end
|
131
131
|
end
|