lopata 0.1.11 → 0.1.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +25 -25
- data/exe/lopata +11 -11
- data/lib/lopata.rb +100 -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 -27
- 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 -90
- data/lib/lopata/runner.rb +67 -67
- data/lib/lopata/scenario.rb +136 -136
- data/lib/lopata/scenario_builder.rb +497 -495
- 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,43 +1,43 @@
|
|
1
|
-
module Lopata
|
2
|
-
# @private
|
3
|
-
module Generators
|
4
|
-
# @private
|
5
|
-
class App < Thor::Group
|
6
|
-
include Thor::Actions
|
7
|
-
argument :name
|
8
|
-
|
9
|
-
def self.source_root
|
10
|
-
File.join(File.dirname(__FILE__), 'templates')
|
11
|
-
end
|
12
|
-
|
13
|
-
def create_root_files
|
14
|
-
template 'Lopatafile', "#{name}/Lopatafile"
|
15
|
-
template 'Gemfile', "#{name}/Gemfile"
|
16
|
-
template 'config/environments/qa.yml', "#{name}/config/environments/qa.yml"
|
17
|
-
template 'config/initializers/capybara.rb', "#{name}/config/initializers/capybara.rb"
|
18
|
-
end
|
19
|
-
|
20
|
-
def init_dirs
|
21
|
-
%w{models services pages}.each do |dir|
|
22
|
-
empty_directory "#{name}/app/#{dir}"
|
23
|
-
end
|
24
|
-
|
25
|
-
%w{scenarios shared_steps config/initializers}.each do |dir|
|
26
|
-
empty_directory "#{name}/#{dir}"
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def bundle
|
31
|
-
Dir.chdir name do
|
32
|
-
_bundle_command = Gem.bin_path('bundler', 'bundle')
|
33
|
-
|
34
|
-
require 'bundler'
|
35
|
-
Bundler.with_clean_env do
|
36
|
-
output = `"#{Gem.ruby}" "#{_bundle_command}"`
|
37
|
-
print output # unless options[:quiet]
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
1
|
+
module Lopata
|
2
|
+
# @private
|
3
|
+
module Generators
|
4
|
+
# @private
|
5
|
+
class App < Thor::Group
|
6
|
+
include Thor::Actions
|
7
|
+
argument :name
|
8
|
+
|
9
|
+
def self.source_root
|
10
|
+
File.join(File.dirname(__FILE__), 'templates')
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_root_files
|
14
|
+
template 'Lopatafile', "#{name}/Lopatafile"
|
15
|
+
template 'Gemfile', "#{name}/Gemfile"
|
16
|
+
template 'config/environments/qa.yml', "#{name}/config/environments/qa.yml"
|
17
|
+
template 'config/initializers/capybara.rb', "#{name}/config/initializers/capybara.rb"
|
18
|
+
end
|
19
|
+
|
20
|
+
def init_dirs
|
21
|
+
%w{models services pages}.each do |dir|
|
22
|
+
empty_directory "#{name}/app/#{dir}"
|
23
|
+
end
|
24
|
+
|
25
|
+
%w{scenarios shared_steps config/initializers}.each do |dir|
|
26
|
+
empty_directory "#{name}/#{dir}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def bundle
|
31
|
+
Dir.chdir name do
|
32
|
+
_bundle_command = Gem.bin_path('bundler', 'bundle')
|
33
|
+
|
34
|
+
require 'bundler'
|
35
|
+
Bundler.with_clean_env do
|
36
|
+
output = `"#{Gem.ruby}" "#{_bundle_command}"`
|
37
|
+
print output # unless options[:quiet]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
43
|
end
|
@@ -1,7 +1,7 @@
|
|
1
|
-
source 'https://rubygems.org'
|
2
|
-
gem 'selenium-webdriver'
|
3
|
-
gem 'activerecord', '~> 6.0.2'
|
4
|
-
gem 'capybara', '~> 3.11.1'
|
5
|
-
gem 'thor'
|
6
|
-
<% require "lopata/version" %>
|
7
|
-
gem 'lopata', '<%= Lopata::Version::STRING %>'
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
gem 'selenium-webdriver'
|
3
|
+
gem 'activerecord', '~> 6.0.2'
|
4
|
+
gem 'capybara', '~> 3.11.1'
|
5
|
+
gem 'thor'
|
6
|
+
<% require "lopata/version" %>
|
7
|
+
gem 'lopata', '<%= Lopata::Version::STRING %>'
|
@@ -1,20 +1,20 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
Encoding.default_external = Encoding::UTF_8
|
3
|
-
Encoding.default_internal = Encoding::UTF_8
|
4
|
-
|
5
|
-
require 'rubygems'
|
6
|
-
require 'bundler/setup'
|
7
|
-
require 'active_support/all'
|
8
|
-
require 'capybara'
|
9
|
-
require 'capybara/dsl'
|
10
|
-
require 'selenium/webdriver'
|
11
|
-
require 'fileutils'
|
12
|
-
require 'active_support'
|
13
|
-
require 'active_record'
|
14
|
-
require 'lopata'
|
15
|
-
|
16
|
-
relative_load_paths = %w[app/pages app/services app/models]
|
17
|
-
ActiveSupport::Dependencies.autoload_paths += relative_load_paths
|
18
|
-
|
19
|
-
Dir["./config/initializers/*.rb"].each { |f| require f }
|
20
|
-
|
1
|
+
# coding: utf-8
|
2
|
+
Encoding.default_external = Encoding::UTF_8
|
3
|
+
Encoding.default_internal = Encoding::UTF_8
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'bundler/setup'
|
7
|
+
require 'active_support/all'
|
8
|
+
require 'capybara'
|
9
|
+
require 'capybara/dsl'
|
10
|
+
require 'selenium/webdriver'
|
11
|
+
require 'fileutils'
|
12
|
+
require 'active_support'
|
13
|
+
require 'active_record'
|
14
|
+
require 'lopata'
|
15
|
+
|
16
|
+
relative_load_paths = %w[app/pages app/services app/models]
|
17
|
+
ActiveSupport::Dependencies.autoload_paths += relative_load_paths
|
18
|
+
|
19
|
+
Dir["./config/initializers/*.rb"].each { |f| require f }
|
20
|
+
|
@@ -1,7 +1,7 @@
|
|
1
|
-
url: http://yourapp.com
|
2
|
-
# db:
|
3
|
-
# adapter: postgresql
|
4
|
-
# host: localhost
|
5
|
-
# username: username
|
6
|
-
# password: password
|
7
|
-
# database: database
|
1
|
+
url: http://yourapp.com
|
2
|
+
# db:
|
3
|
+
# adapter: postgresql
|
4
|
+
# host: localhost
|
5
|
+
# username: username
|
6
|
+
# password: password
|
7
|
+
# database: database
|
@@ -1 +1 @@
|
|
1
|
-
Capybara.default_driver = :selenium if defined? Capybara
|
1
|
+
Capybara.default_driver = :selenium if defined? Capybara
|
data/lib/lopata/id.rb
CHANGED
@@ -1,22 +1,22 @@
|
|
1
|
-
module Lopata
|
2
|
-
# @private
|
3
|
-
module Id
|
4
|
-
extend self
|
5
|
-
|
6
|
-
def next(prefix = nil)
|
7
|
-
id = "%d_%d" % [timestamp, seq_num]
|
8
|
-
id = "%s_%s" % [prefix, id] if prefix
|
9
|
-
id
|
10
|
-
end
|
11
|
-
|
12
|
-
def timestamp
|
13
|
-
@timestamp ||= Time.now.strftime("%Y%m%d%H%M%S")
|
14
|
-
end
|
15
|
-
|
16
|
-
def seq_num
|
17
|
-
@seq_num ||= 0
|
18
|
-
@seq_num += 1
|
19
|
-
@seq_num
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
1
|
+
module Lopata
|
2
|
+
# @private
|
3
|
+
module Id
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def next(prefix = nil)
|
7
|
+
id = "%d_%d" % [timestamp, seq_num]
|
8
|
+
id = "%s_%s" % [prefix, id] if prefix
|
9
|
+
id
|
10
|
+
end
|
11
|
+
|
12
|
+
def timestamp
|
13
|
+
@timestamp ||= Time.now.strftime("%Y%m%d%H%M%S")
|
14
|
+
end
|
15
|
+
|
16
|
+
def seq_num
|
17
|
+
@seq_num ||= 0
|
18
|
+
@seq_num += 1
|
19
|
+
@seq_num
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/lopata/loader.rb
CHANGED
@@ -1,28 +1,32 @@
|
|
1
|
-
# @private
|
2
|
-
module Lopata::Loader
|
3
|
-
extend self
|
4
|
-
|
5
|
-
# Loads scenarios for running in current session
|
6
|
-
#
|
7
|
-
# @param args [Array<String>] files to be load.
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
1
|
+
# @private
|
2
|
+
module Lopata::Loader
|
3
|
+
extend self
|
4
|
+
|
5
|
+
# Loads scenarios for running in current session
|
6
|
+
#
|
7
|
+
# @param args [Array<String>] files to be load.
|
8
|
+
# Mask (e. g. 'scenarios/**/*.rb') is can be passed as well.
|
9
|
+
# All files from default location to be loaded if empty.
|
10
|
+
def load_scenarios(*args)
|
11
|
+
if args.empty?
|
12
|
+
load_all_scenarios
|
13
|
+
else
|
14
|
+
args.each(&method(:load_by_mask))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Loads all scenarios from predefined paths
|
19
|
+
def load_all_scenarios
|
20
|
+
load_by_mask "scenarios/**/*.rb"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Loads all shared steps from predefined paths
|
24
|
+
def load_shared_steps
|
25
|
+
load_by_mask "shared_steps/**/*rb"
|
26
|
+
end
|
27
|
+
|
28
|
+
# @private
|
29
|
+
def load_by_mask(mask)
|
30
|
+
Dir[mask].each { |f| load File.expand_path(f) }
|
31
|
+
end
|
28
32
|
end
|
data/lib/lopata/observers.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
# @private
|
2
|
-
module Lopata::Observers
|
3
|
-
autoload :BaseObserver, 'lopata/observers/base_observer.rb'
|
4
|
-
autoload :ConsoleOutputObserver, 'lopata/observers/console_output_observer.rb'
|
1
|
+
# @private
|
2
|
+
module Lopata::Observers
|
3
|
+
autoload :BaseObserver, 'lopata/observers/base_observer.rb'
|
4
|
+
autoload :ConsoleOutputObserver, 'lopata/observers/console_output_observer.rb'
|
5
5
|
end
|
@@ -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
|