lopata 0.1.13 → 0.1.17
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/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/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/observers.rb +4 -4
- data/lib/lopata/role.rb +109 -109
- data/lib/lopata/runner.rb +80 -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
- data/lib/lopata.rb +123 -74
- metadata +4 -4
@@ -1,103 +1,103 @@
|
|
1
|
-
module Lopata
|
2
|
-
module Observers
|
3
|
-
# @private
|
4
|
-
# Based on RSpec::Core::BacktraceFormatter
|
5
|
-
#
|
6
|
-
# Provides ability to format backtrace and find source code by file and line number.
|
7
|
-
class BacktraceFormatter
|
8
|
-
# @private
|
9
|
-
attr_accessor :exclusion_patterns, :inclusion_patterns
|
10
|
-
|
11
|
-
def initialize
|
12
|
-
patterns = %w[ /lib\d*/ruby/ bin/ exe/lopata /lib/bundler/ /exe/bundle /\.rvm/ ]
|
13
|
-
patterns.map! { |s| Regexp.new(s.gsub("/", File::SEPARATOR)) }
|
14
|
-
|
15
|
-
@exclusion_patterns = [Regexp.union(*patterns)]
|
16
|
-
@inclusion_patterns = []
|
17
|
-
|
18
|
-
inclusion_patterns << Regexp.new(Dir.getwd)
|
19
|
-
end
|
20
|
-
|
21
|
-
# Filter backtrace.
|
22
|
-
#
|
23
|
-
# @param backtrace [Array<String>] exception backtrace
|
24
|
-
# @return [Array<String>] backtrace lines except ruby libraries, gems and executable files.
|
25
|
-
def format(backtrace)
|
26
|
-
return [] unless backtrace
|
27
|
-
return backtrace if backtrace.empty?
|
28
|
-
|
29
|
-
backtrace.map { |l| backtrace_line(l) }.compact.
|
30
|
-
tap do |filtered|
|
31
|
-
if filtered.empty?
|
32
|
-
filtered.concat backtrace
|
33
|
-
filtered << ""
|
34
|
-
filtered << " Showing full backtrace because every line was filtered out."
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
|
40
|
-
# Extracts error message from excetion
|
41
|
-
#
|
42
|
-
# @param exception [Exception]
|
43
|
-
# @param include_backtrace [Boolean] whether to add formatted backtrace to output
|
44
|
-
# @return [String] error message from excetion, incuding source code line.
|
45
|
-
def error_message(exception, include_backtrace: false)
|
46
|
-
backtrace = format(exception.backtrace)
|
47
|
-
source_line = extract_source_line(backtrace.first)
|
48
|
-
msg = ''
|
49
|
-
msg << "\n#{source_line}\n" if source_line
|
50
|
-
msg << "#{exception.class.name}: " unless exception.class.name =~ /RSpec/
|
51
|
-
msg << exception.message if exception.message
|
52
|
-
msg << "\n#{backtrace.join("\n")}\n" if include_backtrace
|
53
|
-
msg
|
54
|
-
end
|
55
|
-
|
56
|
-
def extract_source_line(backtrace_line)
|
57
|
-
file_and_line_number = backtrace_line.match(/(.+?):(\d+)(|:\d+)/)
|
58
|
-
return nil unless file_and_line_number
|
59
|
-
file_path, line_number = file_and_line_number[1..2]
|
60
|
-
return nil unless File.exist?(file_path)
|
61
|
-
lines = File.read(file_path).split("\n")
|
62
|
-
lines[line_number.to_i - 1]
|
63
|
-
end
|
64
|
-
|
65
|
-
def backtrace_line(line)
|
66
|
-
relative_path(line) unless exclude?(line)
|
67
|
-
end
|
68
|
-
|
69
|
-
def exclude?(line)
|
70
|
-
matches?(exclusion_patterns, line) && !matches?(inclusion_patterns, line)
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
def matches?(patterns, line)
|
76
|
-
patterns.any? { |p| line =~ p }
|
77
|
-
end
|
78
|
-
|
79
|
-
# Matches strings either at the beginning of the input or prefixed with a
|
80
|
-
# whitespace, containing the current path, either postfixed with the
|
81
|
-
# separator, or at the end of the string. Match groups are the character
|
82
|
-
# before and the character after the string if any.
|
83
|
-
#
|
84
|
-
# http://rubular.com/r/fT0gmX6VJX
|
85
|
-
# http://rubular.com/r/duOrD4i3wb
|
86
|
-
# http://rubular.com/r/sbAMHFrOx1
|
87
|
-
def relative_path_regex
|
88
|
-
@relative_path_regex ||= /(\A|\s)#{File.expand_path('.')}(#{File::SEPARATOR}|\s|\Z)/
|
89
|
-
end
|
90
|
-
|
91
|
-
# @param line [String] current code line
|
92
|
-
# @return [String] relative path to line
|
93
|
-
def relative_path(line)
|
94
|
-
line = line.sub(relative_path_regex, "\\1.\\2".freeze)
|
95
|
-
line = line.sub(/\A([^:]+:\d+)$/, '\\1'.freeze)
|
96
|
-
return nil if line == '-e:1'.freeze
|
97
|
-
line
|
98
|
-
rescue SecurityError
|
99
|
-
nil
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
1
|
+
module Lopata
|
2
|
+
module Observers
|
3
|
+
# @private
|
4
|
+
# Based on RSpec::Core::BacktraceFormatter
|
5
|
+
#
|
6
|
+
# Provides ability to format backtrace and find source code by file and line number.
|
7
|
+
class BacktraceFormatter
|
8
|
+
# @private
|
9
|
+
attr_accessor :exclusion_patterns, :inclusion_patterns
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
patterns = %w[ /lib\d*/ruby/ bin/ exe/lopata /lib/bundler/ /exe/bundle /\.rvm/ /rvm/ ]
|
13
|
+
patterns.map! { |s| Regexp.new(s.gsub("/", File::SEPARATOR)) }
|
14
|
+
|
15
|
+
@exclusion_patterns = [Regexp.union(*patterns)]
|
16
|
+
@inclusion_patterns = []
|
17
|
+
|
18
|
+
inclusion_patterns << Regexp.new(Dir.getwd)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Filter backtrace.
|
22
|
+
#
|
23
|
+
# @param backtrace [Array<String>] exception backtrace
|
24
|
+
# @return [Array<String>] backtrace lines except ruby libraries, gems and executable files.
|
25
|
+
def format(backtrace)
|
26
|
+
return [] unless backtrace
|
27
|
+
return backtrace if backtrace.empty?
|
28
|
+
|
29
|
+
backtrace.map { |l| backtrace_line(l) }.compact.
|
30
|
+
tap do |filtered|
|
31
|
+
if filtered.empty?
|
32
|
+
filtered.concat backtrace
|
33
|
+
filtered << ""
|
34
|
+
filtered << " Showing full backtrace because every line was filtered out."
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
# Extracts error message from excetion
|
41
|
+
#
|
42
|
+
# @param exception [Exception]
|
43
|
+
# @param include_backtrace [Boolean] whether to add formatted backtrace to output
|
44
|
+
# @return [String] error message from excetion, incuding source code line.
|
45
|
+
def error_message(exception, include_backtrace: false)
|
46
|
+
backtrace = format(exception.backtrace)
|
47
|
+
source_line = extract_source_line(backtrace.first)
|
48
|
+
msg = ''
|
49
|
+
msg << "\n#{source_line}\n" if source_line
|
50
|
+
msg << "#{exception.class.name}: " unless exception.class.name =~ /RSpec/
|
51
|
+
msg << exception.message if exception.message
|
52
|
+
msg << "\n#{backtrace.join("\n")}\n" if include_backtrace
|
53
|
+
msg
|
54
|
+
end
|
55
|
+
|
56
|
+
def extract_source_line(backtrace_line)
|
57
|
+
file_and_line_number = backtrace_line.match(/(.+?):(\d+)(|:\d+)/)
|
58
|
+
return nil unless file_and_line_number
|
59
|
+
file_path, line_number = file_and_line_number[1..2]
|
60
|
+
return nil unless File.exist?(file_path)
|
61
|
+
lines = File.read(file_path).split("\n")
|
62
|
+
lines[line_number.to_i - 1]
|
63
|
+
end
|
64
|
+
|
65
|
+
def backtrace_line(line)
|
66
|
+
relative_path(line) unless exclude?(line)
|
67
|
+
end
|
68
|
+
|
69
|
+
def exclude?(line)
|
70
|
+
matches?(exclusion_patterns, line) && !matches?(inclusion_patterns, line)
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def matches?(patterns, line)
|
76
|
+
patterns.any? { |p| line =~ p }
|
77
|
+
end
|
78
|
+
|
79
|
+
# Matches strings either at the beginning of the input or prefixed with a
|
80
|
+
# whitespace, containing the current path, either postfixed with the
|
81
|
+
# separator, or at the end of the string. Match groups are the character
|
82
|
+
# before and the character after the string if any.
|
83
|
+
#
|
84
|
+
# http://rubular.com/r/fT0gmX6VJX
|
85
|
+
# http://rubular.com/r/duOrD4i3wb
|
86
|
+
# http://rubular.com/r/sbAMHFrOx1
|
87
|
+
def relative_path_regex
|
88
|
+
@relative_path_regex ||= /(\A|\s)#{File.expand_path('.')}(#{File::SEPARATOR}|\s|\Z)/
|
89
|
+
end
|
90
|
+
|
91
|
+
# @param line [String] current code line
|
92
|
+
# @return [String] relative path to line
|
93
|
+
def relative_path(line)
|
94
|
+
line = line.sub(relative_path_regex, "\\1.\\2".freeze)
|
95
|
+
line = line.sub(/\A([^:]+:\d+)$/, '\\1'.freeze)
|
96
|
+
return nil if line == '-e:1'.freeze
|
97
|
+
line
|
98
|
+
rescue SecurityError
|
99
|
+
nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -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
|