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