moto 0.0.4 → 0.0.5
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/bin/moto +2 -2
- data/lib/app_generator.rb +9 -0
- data/lib/assert.rb +9 -3
- data/lib/cli.rb +24 -22
- data/lib/clients/website.rb +47 -47
- data/lib/exceptions/moto.rb +7 -0
- data/lib/exceptions/test_forced_failure.rb +7 -0
- data/lib/exceptions/test_forced_passed.rb +7 -0
- data/lib/exceptions/test_skipped.rb +7 -0
- data/lib/listeners/console.rb +1 -0
- data/lib/listeners/console_dots.rb +63 -0
- data/lib/listeners/junit_xml.rb +37 -0
- data/lib/parser.rb +61 -0
- data/lib/result.rb +26 -5
- data/lib/runner.rb +19 -2
- data/lib/test.rb +34 -2
- data/lib/test_generator.rb +15 -3
- data/lib/thread_context.rb +52 -31
- metadata +41 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1c00f9d85d66a288aab3de0089ccb7cd20568dcf
|
4
|
+
data.tar.gz: 913cef9df55570bdcbb9c7e4570ce7a36ab6a6aa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 65932f837806445c3f132b5b01228328f061d63a3247685f3ac5000fed0a6396135a2a92673bc5aa177a8e74fcbf1c5fd00463bb2102cb71c6c52fe7d3d7a7cb
|
7
|
+
data.tar.gz: d3bb6d1cf7b48bde43e8c8729aeae70666ec1a53e0208399ec7662ce6b564547bb6c11a863fb1d069a18dfe17d3aac797d636fa5499e6370b0e8b4906170158a
|
data/bin/moto
CHANGED
data/lib/assert.rb
CHANGED
@@ -2,13 +2,19 @@ module Moto
|
|
2
2
|
module Assert
|
3
3
|
|
4
4
|
def assert_equal(a, b)
|
5
|
-
assert(a==b, "Arguments should be equal: #{a} != #{b}")
|
5
|
+
assert(a==b, "Arguments should be equal: #{a} != #{b}.")
|
6
|
+
end
|
7
|
+
|
8
|
+
def assert_true(b)
|
9
|
+
assert(b, "Logical condition not met, expecting true, given false.")
|
6
10
|
end
|
7
11
|
|
8
12
|
def assert(condition, message)
|
9
13
|
unless condition
|
10
|
-
|
11
|
-
|
14
|
+
line_number = caller.select{ |l| l.match(/\(eval\):\d*:in `run'/) }.first[/\d+/].to_i - 1 # -1 because of added method header in generated class
|
15
|
+
full_message = "ASSERTION FAILED in line #{line_number}: #{message}"
|
16
|
+
@context.runner.result.add_failure(self, full_message)
|
17
|
+
logger.error(full_message)
|
12
18
|
end
|
13
19
|
end
|
14
20
|
|
data/lib/cli.rb
CHANGED
@@ -10,12 +10,18 @@ require 'pp'
|
|
10
10
|
require 'yaml'
|
11
11
|
require 'active_support/inflector'
|
12
12
|
require 'active_support/core_ext/object/blank'
|
13
|
+
require 'fileutils'
|
13
14
|
|
14
15
|
require 'bundler/setup'
|
15
16
|
Bundler.require
|
16
17
|
|
17
|
-
|
18
|
-
|
18
|
+
module MotoApp
|
19
|
+
DIR = Dir.pwd
|
20
|
+
end
|
21
|
+
|
22
|
+
module Moto
|
23
|
+
DIR = File.dirname(File.dirname(__FILE__))
|
24
|
+
end
|
19
25
|
|
20
26
|
require_relative './empty_listener'
|
21
27
|
require_relative './test_logging'
|
@@ -29,33 +35,29 @@ require_relative './page'
|
|
29
35
|
require_relative './clients/base'
|
30
36
|
require_relative './listeners/base'
|
31
37
|
require_relative './listeners/console'
|
38
|
+
require_relative './listeners/console_dots'
|
39
|
+
require_relative './listeners/junit_xml'
|
32
40
|
require_relative './test_generator'
|
41
|
+
require_relative './exceptions/moto'
|
42
|
+
require_relative './exceptions/test_skipped'
|
43
|
+
require_relative './exceptions/test_forced_failure'
|
44
|
+
require_relative './exceptions/test_forced_passed'
|
33
45
|
|
34
46
|
module Moto
|
35
47
|
|
36
48
|
class Cli
|
37
49
|
|
38
50
|
def self.run(argv)
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
# listeners = []
|
51
|
-
listeners = [Moto::Listeners::Console]
|
52
|
-
environments = [:qa, :qa2]
|
53
|
-
# handle possible syntax error here
|
54
|
-
config = eval(File.read("#{APP_DIR}/config/moto.rb"))
|
55
|
-
# overwrite config with values from ARGV (if any given)
|
56
|
-
# e.g. config[:capybara][:default_driver] = :selenium
|
57
|
-
|
58
|
-
runner = Moto::Runner.new(tests, listeners, environments, config)
|
51
|
+
tests = []
|
52
|
+
argv[ :tests ].each do |test_name|
|
53
|
+
test_class_name = test_name
|
54
|
+
|
55
|
+
tg = TestGenerator.new(MotoApp::DIR)
|
56
|
+
t = tg.generate(test_class_name)
|
57
|
+
tests << t
|
58
|
+
end
|
59
|
+
|
60
|
+
runner = Moto::Runner.new(tests, argv[ :reporters ], argv[ :environments ], argv[ :config ])
|
59
61
|
runner.run
|
60
62
|
end
|
61
63
|
|
data/lib/clients/website.rb
CHANGED
@@ -1,47 +1,47 @@
|
|
1
|
-
require 'capybara'
|
2
|
-
|
3
|
-
module Moto
|
4
|
-
module Clients
|
5
|
-
|
6
|
-
class Website < Moto::Clients::Base
|
7
|
-
|
8
|
-
attr_reader :session
|
9
|
-
|
10
|
-
ignore_logging(:page)
|
11
|
-
ignore_logging(:context)
|
12
|
-
ignore_logging(:session)
|
13
|
-
|
14
|
-
def start_run
|
15
|
-
# TODO: make session driver configurable
|
16
|
-
@session = Capybara::Session.new(context.runner.
|
17
|
-
@pages = {}
|
18
|
-
end
|
19
|
-
|
20
|
-
def end_run
|
21
|
-
@session.driver.browser.close # TODO: check that it really works
|
22
|
-
end
|
23
|
-
|
24
|
-
def start_test(test)
|
25
|
-
# @context.current_test.logger.info("Hi mom, I'm opening some pages!")
|
26
|
-
@session.reset_session!
|
27
|
-
end
|
28
|
-
|
29
|
-
def end_test(test)
|
30
|
-
@session.reset_session!
|
31
|
-
end
|
32
|
-
|
33
|
-
def page(p)
|
34
|
-
page_class_name = "#{self.class.name}Pages::#{p}"
|
35
|
-
page_class_name.gsub!('Moto::', 'MotoApp::')
|
36
|
-
if @pages[page_class_name].nil?
|
37
|
-
a = page_class_name.underscore.split('/')
|
38
|
-
page_path = a[1..20].join('/')
|
39
|
-
require "#{
|
40
|
-
@pages[page_class_name] = page_class_name.constantize.new(self)
|
41
|
-
end
|
42
|
-
@pages[page_class_name]
|
43
|
-
end
|
44
|
-
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
1
|
+
require 'capybara'
|
2
|
+
|
3
|
+
module Moto
|
4
|
+
module Clients
|
5
|
+
|
6
|
+
class Website < Moto::Clients::Base
|
7
|
+
|
8
|
+
attr_reader :session
|
9
|
+
|
10
|
+
ignore_logging(:page)
|
11
|
+
ignore_logging(:context)
|
12
|
+
ignore_logging(:session)
|
13
|
+
|
14
|
+
def start_run
|
15
|
+
# TODO: make session driver configurable
|
16
|
+
@session = Capybara::Session.new(context.runner.my_config[:capybara][:default_driver])
|
17
|
+
@pages = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def end_run
|
21
|
+
@session.driver.browser.close # TODO: check that it really works
|
22
|
+
end
|
23
|
+
|
24
|
+
def start_test(test)
|
25
|
+
# @context.current_test.logger.info("Hi mom, I'm opening some pages!")
|
26
|
+
@session.reset_session!
|
27
|
+
end
|
28
|
+
|
29
|
+
def end_test(test)
|
30
|
+
@session.reset_session!
|
31
|
+
end
|
32
|
+
|
33
|
+
def page(p)
|
34
|
+
page_class_name = "#{self.class.name}Pages::#{p}"
|
35
|
+
page_class_name.gsub!('Moto::', 'MotoApp::')
|
36
|
+
if @pages[page_class_name].nil?
|
37
|
+
a = page_class_name.underscore.split('/')
|
38
|
+
page_path = a[1..20].join('/')
|
39
|
+
require "#{MotoApp::DIR}/lib/#{page_path}"
|
40
|
+
@pages[page_class_name] = page_class_name.constantize.new(self)
|
41
|
+
end
|
42
|
+
@pages[page_class_name]
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/listeners/console.rb
CHANGED
@@ -13,6 +13,7 @@ module Moto
|
|
13
13
|
puts " Passed: #{@runner.result.summary[:cnt_passed]}"
|
14
14
|
puts " Failure: #{@runner.result.summary[:cnt_failure]}"
|
15
15
|
puts " Error: #{@runner.result.summary[:cnt_error]}"
|
16
|
+
puts " Skipped: #{@runner.result.summary[:cnt_skipped]}"
|
16
17
|
end
|
17
18
|
|
18
19
|
def start_test(test)
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Moto
|
2
|
+
module Listeners
|
3
|
+
class ConsoleDots < Base
|
4
|
+
|
5
|
+
def start_run
|
6
|
+
end
|
7
|
+
|
8
|
+
def end_run
|
9
|
+
puts ""
|
10
|
+
puts ""
|
11
|
+
puts "FINISHED: #{@runner.result.summary[:result]}, duration: #{Time.at(@runner.result.summary[:duration]).utc.strftime("%H:%M:%S")}"
|
12
|
+
puts "Tests executed: #{@runner.result.summary[:cnt_all]}"
|
13
|
+
puts " Passed: #{@runner.result.summary[:cnt_passed]}"
|
14
|
+
puts " Failure: #{@runner.result.summary[:cnt_failure]}"
|
15
|
+
puts " Error: #{@runner.result.summary[:cnt_error]}"
|
16
|
+
puts " Skipped: #{@runner.result.summary[:cnt_skipped]}"
|
17
|
+
|
18
|
+
if @runner.result.summary[:cnt_failure] > 0
|
19
|
+
puts ""
|
20
|
+
puts "FAILURES: "
|
21
|
+
@runner.result.summary[:tests_failure].each do |test_name, data|
|
22
|
+
puts test_name
|
23
|
+
puts "\t#{data[:failures].join("\n\t")}"
|
24
|
+
puts ""
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
if @runner.result.summary[:cnt_error] > 0
|
29
|
+
puts ""
|
30
|
+
puts "ERRORS: "
|
31
|
+
@runner.result.summary[:tests_error].each do |test_name, data|
|
32
|
+
puts test_name
|
33
|
+
puts "\t#{data[:error]}"
|
34
|
+
puts ""
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
if @runner.result.summary[:cnt_skipped] > 0
|
39
|
+
puts ""
|
40
|
+
puts "SKIPPED: "
|
41
|
+
@runner.result.summary[:tests_skipped].each do |test_name, data|
|
42
|
+
puts test_name
|
43
|
+
puts "\t#{data[:error]}"
|
44
|
+
puts ""
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def start_test(test)
|
50
|
+
end
|
51
|
+
|
52
|
+
def end_test(test)
|
53
|
+
print case @runner.result[test.name][:result]
|
54
|
+
when :passed then "."
|
55
|
+
when :failure then "F"
|
56
|
+
when :error then "E"
|
57
|
+
when :skipped then "S"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module Moto
|
4
|
+
module Listeners
|
5
|
+
class JunitXml < Base
|
6
|
+
|
7
|
+
def end_run
|
8
|
+
path = @runner.my_config[:output_file]
|
9
|
+
|
10
|
+
builder = Nokogiri::XML::Builder.new { |xml|
|
11
|
+
xml.testsuite(
|
12
|
+
errors: @runner.result.summary[:cnt_error],
|
13
|
+
failures: @runner.result.summary[:cnt_failure],
|
14
|
+
name: "Moto run",
|
15
|
+
tests: @runner.result.summary[:cnt_all],
|
16
|
+
time: @runner.result.summary[:duration],
|
17
|
+
timestamp: Time.at(@runner.result.summary[:started_at])) do
|
18
|
+
@runner.result.all.each do |test_name, data|
|
19
|
+
xml.testcase(name: test_name, time: data[:duration], classname: data[:class].name, moto_result: data[:result]) do
|
20
|
+
if !data[:error].nil?
|
21
|
+
xml.error(message: data[:error].message)
|
22
|
+
elsif data[:failures].count > 0
|
23
|
+
data[:failures].each do |f|
|
24
|
+
xml.failure(message: f)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
}
|
31
|
+
|
32
|
+
File.open(path, 'w') {|f| f.write(builder.to_xml) }
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/parser.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
require_relative '../lib/cli'
|
5
|
+
require_relative '../lib/app_generator'
|
6
|
+
|
7
|
+
module Moto
|
8
|
+
|
9
|
+
class Parser
|
10
|
+
|
11
|
+
def self.run(argv)
|
12
|
+
|
13
|
+
# TODO Generate app / Change the way parsing options goes so it doesnt generate them if they`re not needed
|
14
|
+
case argv[0]
|
15
|
+
when 'run' then Moto::Cli.run run_parse(argv)
|
16
|
+
when 'help' then show_help
|
17
|
+
when 'generate' then Moto::AppGenerator.run generate_parse(argv)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.run_parse(argv)
|
22
|
+
# puts Moto::DIR
|
23
|
+
# Default options
|
24
|
+
options = {}
|
25
|
+
options[:reporters] = [Moto::Listeners::ConsoleDots, Moto::Listeners::JunitXml]
|
26
|
+
options[:config] = eval(File.read("#{MotoApp::DIR}/config/moto.rb"))
|
27
|
+
options[:environments] = []
|
28
|
+
|
29
|
+
# Parse arguments
|
30
|
+
# TODO eval ?
|
31
|
+
# TODO const
|
32
|
+
# TODO reporters should be consts - not strings
|
33
|
+
OptionParser.new do |opts|
|
34
|
+
opts.on('-t', "--tests Tests", Array) { |v| options[:tests ] = v }
|
35
|
+
opts.on('-r', '--reporters Reporters', Array) { |v| options[:reporters] = v }
|
36
|
+
opts.on('-e', '--environments Environment', Array) { |v| options[:environments] = v }
|
37
|
+
opts.on('-c', '--const Const') { |v| options[:const] = v }
|
38
|
+
opts.on('-cfg', '--config Config') { |v| options[:config] = options[:config].merge( eval( v ) ) }
|
39
|
+
end.parse!
|
40
|
+
return options
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.generate_parse(argv)
|
44
|
+
options = {}
|
45
|
+
options[:dir]
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.show_help
|
49
|
+
puts """
|
50
|
+
Moto CLI Help:
|
51
|
+
moto run:
|
52
|
+
-t, --tests = Tests to be executed. For e.x Tests\Failure\Failure.rb should be passed as Tests::Failure
|
53
|
+
-r, --reporter = Reporters to be used. Defaults are Moto::Listeners::ConsoleDots, Moto::Listeners::JunitXml
|
54
|
+
-e, --environment etc etc
|
55
|
+
moto generate:
|
56
|
+
moto generate app_name -d, --dir = directory. Default is current dir.
|
57
|
+
"""
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
data/lib/result.rb
CHANGED
@@ -6,6 +6,7 @@ module Moto
|
|
6
6
|
PASSED = :passed # 0
|
7
7
|
FAILURE = :failure # 1
|
8
8
|
ERROR = :error # 2
|
9
|
+
SKIPPED = :skipped # 3
|
9
10
|
|
10
11
|
attr_reader :summary
|
11
12
|
|
@@ -13,6 +14,10 @@ module Moto
|
|
13
14
|
@results[key]
|
14
15
|
end
|
15
16
|
|
17
|
+
def all
|
18
|
+
@results
|
19
|
+
end
|
20
|
+
|
16
21
|
def initialize(runner)
|
17
22
|
@runner = runner
|
18
23
|
@results = {}
|
@@ -32,9 +37,14 @@ module Moto
|
|
32
37
|
@summary[:result] = FAILURE unless @results.values.select{ |v| v[:failures].count > 0 }.empty?
|
33
38
|
@summary[:result] = ERROR unless @results.values.select{ |v| !v[:error].nil? }.empty?
|
34
39
|
@summary[:cnt_all] = @results.count
|
35
|
-
@summary[:
|
36
|
-
@summary[:
|
37
|
-
@summary[:
|
40
|
+
@summary[:tests_passed] = @results.select{ |k,v| v[:result] == PASSED }
|
41
|
+
@summary[:tests_failure] = @results.select{ |k,v| v[:result] == FAILURE }
|
42
|
+
@summary[:tests_error] = @results.select{ |k,v| v[:result] == ERROR }
|
43
|
+
@summary[:tests_skipped] = @results.select{ |k,v| v[:result] == SKIPPED }
|
44
|
+
@summary[:cnt_passed] = @summary[:tests_passed].count
|
45
|
+
@summary[:cnt_failure] = @summary[:tests_failure].count
|
46
|
+
@summary[:cnt_error] = @summary[:tests_error].count
|
47
|
+
@summary[:cnt_skipped] = @summary[:tests_skipped].count
|
38
48
|
end
|
39
49
|
|
40
50
|
def start_test(test)
|
@@ -43,10 +53,21 @@ module Moto
|
|
43
53
|
|
44
54
|
def end_test(test)
|
45
55
|
# calculate result basing on errors/failures
|
56
|
+
@results[test.name][:finished_at] = Time.now.to_f
|
46
57
|
test.result = PASSED
|
47
58
|
test.result = FAILURE unless @results[test.name][:failures].empty?
|
48
|
-
|
49
|
-
|
59
|
+
unless @results[test.name][:error].nil?
|
60
|
+
if @results[test.name][:error].is_a? Moto::Exceptions::TestSkipped
|
61
|
+
test.result = SKIPPED
|
62
|
+
elsif @results[test.name][:error].is_a? Moto::Exceptions::TestForcedPassed
|
63
|
+
test.result = PASSED
|
64
|
+
elsif @results[test.name][:error].is_a? Moto::Exceptions::TestForcedFailure
|
65
|
+
add_failure(test, @results[test.name][:error].message)
|
66
|
+
test.result = FAILURE
|
67
|
+
else
|
68
|
+
test.result = ERROR
|
69
|
+
end
|
70
|
+
end
|
50
71
|
@results[test.name][:duration] = @results[test.name][:finished_at] - @results[test.name][:started_at]
|
51
72
|
@results[test.name][:result] = test.result
|
52
73
|
end
|
data/lib/runner.rb
CHANGED
@@ -15,12 +15,13 @@ module Moto
|
|
15
15
|
|
16
16
|
# TODO: initialize logger from config (yml or just ruby code)
|
17
17
|
# @logger = Logger.new(STDOUT)
|
18
|
-
@logger = Logger.new(File.open("#{
|
18
|
+
@logger = Logger.new(File.open("#{MotoApp::DIR}/moto.log", File::WRONLY | File::APPEND | File::CREAT))
|
19
19
|
# @logger.level = Logger::WARN
|
20
20
|
|
21
21
|
@result = Result.new(self)
|
22
22
|
|
23
23
|
# TODO: validate envs, maybe no-env should be supported as well?
|
24
|
+
environments << :__default if environments.empty?
|
24
25
|
@environments = environments
|
25
26
|
|
26
27
|
@listeners = []
|
@@ -30,9 +31,25 @@ module Moto
|
|
30
31
|
@listeners.unshift(@result)
|
31
32
|
end
|
32
33
|
|
34
|
+
def my_config
|
35
|
+
caller_path = caller.first.to_s.split(/:\d/)[0]
|
36
|
+
keys = []
|
37
|
+
if caller_path.include? MotoApp::DIR
|
38
|
+
caller_path.sub!( "#{MotoApp::DIR}/lib/", '' )
|
39
|
+
keys << 'moto_app'
|
40
|
+
elsif caller_path.include? Moto::DIR
|
41
|
+
caller_path.sub!( "#{Moto::DIR}/lib/", '' )
|
42
|
+
keys << 'moto'
|
43
|
+
end
|
44
|
+
caller_path.sub!('.rb', '')
|
45
|
+
keys << caller_path.split('/')
|
46
|
+
keys.flatten!
|
47
|
+
eval "@config#{keys.map{|k| "[:#{k}]" }.join('')}"
|
48
|
+
end
|
49
|
+
|
33
50
|
def run
|
34
51
|
@listeners.each { |l| l.start_run }
|
35
|
-
test_slices = @tests.each_slice((@tests.size.to_f
|
52
|
+
test_slices = @tests.each_slice((@tests.size.to_f/my_config[:thread_count]).ceil).to_a
|
36
53
|
(0...test_slices.count).each do |i|
|
37
54
|
@threads << Thread.new do
|
38
55
|
tc = ThreadContext.new(self, test_slices[i])
|
data/lib/test.rb
CHANGED
@@ -38,8 +38,13 @@ module Moto
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def set_name
|
41
|
-
|
42
|
-
|
41
|
+
if @env == :__default
|
42
|
+
return @name = "#{self.class.to_s}" if @params.empty?
|
43
|
+
return @name = "#{self.class.to_s}/#{@params['__name']}" if @params.key?('__name')
|
44
|
+
else
|
45
|
+
return @name = "#{self.class.to_s}/#{@env}" if @params.empty?
|
46
|
+
return @name = "#{self.class.to_s}/#{@env}/#{@params['__name']}" if @params.key?('__name')
|
47
|
+
end
|
43
48
|
@name = self.class.to_s
|
44
49
|
end
|
45
50
|
|
@@ -73,6 +78,33 @@ module Moto
|
|
73
78
|
def client(name)
|
74
79
|
@context.client(name)
|
75
80
|
end
|
81
|
+
|
82
|
+
def skip(msg = nil)
|
83
|
+
if msg.nil?
|
84
|
+
msg = "Test skipped with no reason given."
|
85
|
+
else
|
86
|
+
msg = "Skip reason: #{msg}"
|
87
|
+
end
|
88
|
+
raise Exceptions::TestSkipped.new msg
|
89
|
+
end
|
90
|
+
|
91
|
+
def fail(msg = nil)
|
92
|
+
if msg.nil?
|
93
|
+
msg = "Test forcibly failed with no reason given."
|
94
|
+
else
|
95
|
+
msg = "Forced failure, reason: #{msg}"
|
96
|
+
end
|
97
|
+
raise Exceptions::TestForcedFailure.new msg
|
98
|
+
end
|
99
|
+
|
100
|
+
def pass(msg = nil)
|
101
|
+
if msg.nil?
|
102
|
+
msg = "Test forcibly passed with no reason given."
|
103
|
+
else
|
104
|
+
msg = "Forced passed, reason: #{msg}"
|
105
|
+
end
|
106
|
+
raise Exceptions::TestForcedPassed.new msg
|
107
|
+
end
|
76
108
|
|
77
109
|
end
|
78
110
|
end
|
data/lib/test_generator.rb
CHANGED
@@ -17,7 +17,7 @@ module Moto
|
|
17
17
|
test_path = (a[1..20]+[a[-1]]).join('/')
|
18
18
|
|
19
19
|
# TODO: check if this path and constant exists
|
20
|
-
require "#{
|
20
|
+
require "#{MotoApp::DIR}/#{test_path}"
|
21
21
|
test_const = class_name.safe_constantize
|
22
22
|
test_const.new
|
23
23
|
end
|
@@ -39,17 +39,29 @@ module Moto
|
|
39
39
|
full_class_name = 'MotoApp::Tests::'+class_name
|
40
40
|
a = full_class_name.underscore.split('/')
|
41
41
|
test_path = (a[1..20]+[a[-1]]).join('/')
|
42
|
-
test_path = "#{
|
42
|
+
test_path = "#{MotoApp::DIR}/#{test_path}.rb"
|
43
43
|
|
44
44
|
method_body = File.read(test_path) + "\n"
|
45
45
|
|
46
|
+
base = Moto::Test
|
47
|
+
base_class_string = method_body.match( /^#\s*BASE_CLASS:\s(\S+)/ )
|
48
|
+
unless base_class_string.nil?
|
49
|
+
base_class_string = base_class_string[1].strip
|
50
|
+
|
51
|
+
a = base_class_string.underscore.split('/')
|
52
|
+
base_test_path = a[1..20].join('/')
|
53
|
+
|
54
|
+
require "#{MotoApp::DIR}/lib/#{base_test_path}"
|
55
|
+
base = base_class_string.constantize
|
56
|
+
end
|
57
|
+
|
46
58
|
# MotoApp::Tests::Login::Short
|
47
59
|
consts = full_class_name.split('::')
|
48
60
|
class_name = consts.pop
|
49
61
|
|
50
62
|
consts.shift 2 # remove Moto::Test as already defined
|
51
63
|
m = create_module_tree(MotoApp::Tests, consts)
|
52
|
-
cls = Class.new(
|
64
|
+
cls = Class.new(base)
|
53
65
|
m.const_set(class_name.to_sym, cls)
|
54
66
|
|
55
67
|
test_object = cls.new
|
data/lib/thread_context.rb
CHANGED
@@ -15,7 +15,7 @@ module Moto
|
|
15
15
|
t.context = self
|
16
16
|
end
|
17
17
|
# TODO: add all *.yml files from that dir
|
18
|
-
@config = YAML.load_file("#{
|
18
|
+
@config = YAML.load_file("#{MotoApp::DIR}/config/const.yml")
|
19
19
|
end
|
20
20
|
|
21
21
|
def client(name)
|
@@ -24,13 +24,13 @@ module Moto
|
|
24
24
|
name_app = 'MotoApp::Clients::' + name
|
25
25
|
name_moto = 'Moto::Clients::' + name
|
26
26
|
|
27
|
-
c = try_client(name_app,
|
27
|
+
c = try_client(name_app, "#{MotoApp::DIR}/lib")
|
28
28
|
unless c.nil?
|
29
29
|
@clients[name] = c
|
30
30
|
return c
|
31
31
|
end
|
32
32
|
|
33
|
-
c = try_client(name_moto, "#{
|
33
|
+
c = try_client(name_moto, "#{Moto::DIR}/lib")
|
34
34
|
unless c.nil?
|
35
35
|
@clients[name] = c
|
36
36
|
return c
|
@@ -55,43 +55,64 @@ module Moto
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def const(key)
|
58
|
-
|
59
|
-
@
|
58
|
+
key = key.to_s
|
59
|
+
key = "#{@current_test.env.to_s}.#{key}" if @current_test.env != :__default
|
60
|
+
code = if key.include? '.'
|
61
|
+
"@config#{key.split('.').map{|a| "['#{a}']" }.join('')}"
|
62
|
+
else
|
63
|
+
"@config['#{key}']"
|
64
|
+
end
|
65
|
+
begin
|
66
|
+
v = eval code
|
67
|
+
raise if v.nil?
|
68
|
+
rescue
|
69
|
+
raise "There is no const defined for key: #{key}. Environment: #{ (@current_test.env == :__default) ? '<none>' : @current_test.env }"
|
70
|
+
end
|
71
|
+
v
|
60
72
|
end
|
61
73
|
|
62
74
|
def run
|
63
75
|
@tests.each do |test|
|
76
|
+
# remove log files from previous execution
|
77
|
+
FileUtils.rm_rf Dir.glob("#{test.dir}/*.log")
|
78
|
+
max_attempts = @runner.my_config[:max_attempts] || 1
|
64
79
|
@runner.environments.each do |env|
|
65
80
|
params_path = "#{test.dir}/#{test.filename}.yml"
|
66
81
|
params_all = [{}]
|
67
|
-
params_all = YAML.load_file(params_path)
|
68
|
-
#
|
82
|
+
params_all = YAML.load_file(params_path) if File.exists?(params_path)
|
83
|
+
# or convert keys to symbols?
|
84
|
+
# params_all = YAML.load_file(params_path).map{|h| Hash[ h.map{|k,v| [ k.to_sym, v ] } ] } if File.exists?(params_path)
|
69
85
|
params_all.each do |params|
|
70
86
|
# TODO: add filtering out params that are specific to certain envs
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
87
|
+
(1..max_attempts).each do |attempt|
|
88
|
+
test.init(env, params)
|
89
|
+
# TODO: log path might be specified (to some extent) by the configuration
|
90
|
+
@log_path = "#{test.dir}/#{test.name.gsub(/\s+/, '_').gsub('::', '_').gsub('/', '_')}.log"
|
91
|
+
@logger = Logger.new(File.open(@log_path, File::WRONLY | File::TRUNC | File::CREAT))
|
92
|
+
# TODO: make logger level configurable
|
93
|
+
@logger.level = @runner.my_config[:log_level]
|
94
|
+
@current_test = test
|
95
|
+
@runner.listeners.each { |l| l.start_test(test) }
|
96
|
+
@clients.each_value { |c| c.start_test(test) }
|
97
|
+
test.before
|
98
|
+
@logger.info "Start: #{test.name} attempt #{attempt}/#{max_attempts}"
|
99
|
+
begin
|
100
|
+
test.run
|
101
|
+
rescue Exceptions::TestForcedPassed, Exceptions::TestForcedFailure, Exceptions::TestSkipped => e
|
102
|
+
logger.info(e.message)
|
103
|
+
@runner.result.add_error(test, e)
|
104
|
+
rescue Exception => e
|
105
|
+
@logger.error("#{e.class.name}: #{e.message}")
|
106
|
+
@logger.error(e.backtrace.join("\n"))
|
107
|
+
@runner.result.add_error(test, e)
|
108
|
+
end
|
109
|
+
test.after
|
110
|
+
@clients.each_value { |c| c.end_test(test) }
|
111
|
+
@runner.listeners.each { |l| l.end_test(test) }
|
112
|
+
@logger.info("Result: #{test.result}")
|
113
|
+
@logger.close
|
114
|
+
break unless [Result::FAILURE, Result::ERROR].include? test.result
|
115
|
+
end # RETRY
|
95
116
|
end
|
96
117
|
end
|
97
118
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: moto
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bartek Wilczek
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-09-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -25,6 +25,34 @@ dependencies:
|
|
25
25
|
- - ~>
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '3.2'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: fileutils
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: nokogiri
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
28
56
|
description: This is a development version of a rails philosophy inspired framework
|
29
57
|
for web applications functional testing. It supports (or will support) threading,
|
30
58
|
scenario parametrization, different test environments and much more. Stay tuned
|
@@ -37,10 +65,12 @@ executables:
|
|
37
65
|
extensions: []
|
38
66
|
extra_rdoc_files: []
|
39
67
|
files:
|
68
|
+
- lib/app_generator.rb
|
40
69
|
- lib/assert.rb
|
41
70
|
- lib/cli.rb
|
42
71
|
- lib/empty_listener.rb
|
43
72
|
- lib/page.rb
|
73
|
+
- lib/parser.rb
|
44
74
|
- lib/result.rb
|
45
75
|
- lib/runner.rb
|
46
76
|
- lib/runner_logging.rb
|
@@ -50,8 +80,14 @@ files:
|
|
50
80
|
- lib/thread_context.rb
|
51
81
|
- lib/clients/base.rb
|
52
82
|
- lib/clients/website.rb
|
83
|
+
- lib/exceptions/moto.rb
|
84
|
+
- lib/exceptions/test_forced_failure.rb
|
85
|
+
- lib/exceptions/test_forced_passed.rb
|
86
|
+
- lib/exceptions/test_skipped.rb
|
53
87
|
- lib/listeners/base.rb
|
54
88
|
- lib/listeners/console.rb
|
89
|
+
- lib/listeners/console_dots.rb
|
90
|
+
- lib/listeners/junit_xml.rb
|
55
91
|
- bin/moto
|
56
92
|
homepage: https://github.com/bwilczek/moto
|
57
93
|
licenses:
|
@@ -63,9 +99,9 @@ require_paths:
|
|
63
99
|
- lib
|
64
100
|
required_ruby_version: !ruby/object:Gem::Requirement
|
65
101
|
requirements:
|
66
|
-
- -
|
102
|
+
- - ~>
|
67
103
|
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
104
|
+
version: '2.0'
|
69
105
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
106
|
requirements:
|
71
107
|
- - '>='
|
@@ -73,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
73
109
|
version: '0'
|
74
110
|
requirements: []
|
75
111
|
rubyforge_project:
|
76
|
-
rubygems_version: 2.0.
|
112
|
+
rubygems_version: 2.0.14
|
77
113
|
signing_key:
|
78
114
|
specification_version: 4
|
79
115
|
summary: Moto - yet another web testing framework
|