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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9148f21a0e6aa5b6a157f4430be6e4669c8ebb5f
4
- data.tar.gz: 493eceb7d79389aace5dfb095167ec1639024505
3
+ metadata.gz: 1c00f9d85d66a288aab3de0089ccb7cd20568dcf
4
+ data.tar.gz: 913cef9df55570bdcbb9c7e4570ce7a36ab6a6aa
5
5
  SHA512:
6
- metadata.gz: 59ed4b2f7ce51f3c1480673de389ff26ca961502474253cca32de6189f0d2fe7404dc937331ede52bfb0c185296ef4fa5bf5c2255502b7d9a58f0283b97aa39c
7
- data.tar.gz: 802ff8c1cef394da74787ea43433ce46f213ab67b48a5abefce6c3a5c48dad7acf846e0292363ba7390bae72c40f2c7f4ae4dd4d5045ac1033eb17973f3fffc2
6
+ metadata.gz: 65932f837806445c3f132b5b01228328f061d63a3247685f3ac5000fed0a6396135a2a92673bc5aa177a8e74fcbf1c5fd00463bb2102cb71c6c52fe7d3d7a7cb
7
+ data.tar.gz: d3bb6d1cf7b48bde43e8c8729aeae70666ec1a53e0208399ec7662ce6b564547bb6c11a863fb1d069a18dfe17d3aac797d636fa5499e6370b0e8b4906170158a
data/bin/moto CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require_relative '../lib/cli'
3
+ require_relative '../lib/parser'
4
4
 
5
- Moto::Cli.run ARGV
5
+ Moto::Parser.run ARGV
@@ -0,0 +1,9 @@
1
+ module Moto
2
+ class AppGenerator
3
+
4
+ def self.run(argv)
5
+
6
+ end
7
+
8
+ end
9
+ end
@@ -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
- @context.runner.result.add_failure(self, message)
11
- logger.error("ASSERTION FAILED: #{message}")
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
- APP_DIR = Dir.pwd
18
- MOTO_DIR = File.dirname(File.dirname(__FILE__))
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
- test_class_name = argv[0]
40
-
41
- tg = TestGenerator.new(APP_DIR)
42
- t = tg.generate(test_class_name)
43
-
44
- tests = [t]
45
-
46
- # parsing ARGV and creating config will come here
47
- # instantiation of tests for ARGV params also happens here
48
- # instantiate listeners/reporters
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
 
@@ -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.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 "#{APP_DIR}/#{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
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
@@ -0,0 +1,7 @@
1
+ module Moto
2
+ module Exceptions
3
+ class MotoException < RuntimeError
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Moto
2
+ module Exceptions
3
+ class TestForcedFailure < MotoException
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Moto
2
+ module Exceptions
3
+ class TestForcedPassed < MotoException
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Moto
2
+ module Exceptions
3
+ class TestSkipped < MotoException
4
+
5
+ end
6
+ end
7
+ end
@@ -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
@@ -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
@@ -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[:cnt_passed] = @results.values.select{ |v| v[:result] == PASSED }.count
36
- @summary[:cnt_failure] = @results.values.select{ |v| v[:result] == FAILURE }.count
37
- @summary[:cnt_error] = @results.values.select{ |v| v[:result] == ERROR }.count
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
- test.result = ERROR unless @results[test.name][:error].nil?
49
- @results[test.name][:finished_at] = Time.now.to_f
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
@@ -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("#{APP_DIR}/moto.log", File::WRONLY | File::APPEND | File::CREAT))
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/@config[:thread_count]).ceil).to_a
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])
@@ -38,8 +38,13 @@ module Moto
38
38
  end
39
39
 
40
40
  def set_name
41
- return @name = "#{self.class.to_s}/#{@env}" if @params.empty?
42
- return @name = "#{self.class.to_s}/#{@env}/#{@params[:__name]}" if @params.key?(:__name)
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
@@ -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 "#{APP_DIR}/#{test_path}"
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 = "#{APP_DIR}/#{test_path}.rb"
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(Moto::Test)
64
+ cls = Class.new(base)
53
65
  m.const_set(class_name.to_sym, cls)
54
66
 
55
67
  test_object = cls.new
@@ -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("#{APP_DIR}/config/const.yml")
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, APP_DIR)
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, "#{MOTO_DIR}/lib")
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
- # TODO: add support to consts with no env
59
- @config[@current_test.env.to_s][key.to_s]
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).map{|h| Hash[ h.map{|k,v| [ k.to_sym, v ] } ] } if File.exists?(params_path)
68
- # params_all = YAML.load_file(params_path) if File.exists?(params_path)
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
- test.init(env, params)
72
- # TODO: log path might be specified (to some extent) by the configuration
73
- @log_path = "#{test.dir}/#{test.name.gsub(/\s+/, '_').gsub('::', '_').gsub('/', '_')}.log"
74
- # TODO: remove log files from previous execution
75
- @logger = Logger.new(File.open(@log_path, File::WRONLY | File::TRUNC | File::CREAT))
76
- # TODO: make logger level configurable
77
- @logger.level = @runner.config[:log_level]
78
- @current_test = test
79
- @runner.listeners.each { |l| l.start_test(test) }
80
- @clients.each_value { |c| c.start_test(test) }
81
- test.before
82
- @logger.info "Start: #{test.name}"
83
- begin
84
- test.run
85
- rescue Exception => e
86
- @logger.error("#{e.class.name}: #{e.message}")
87
- @logger.error(e.backtrace.join("\n"))
88
- @runner.result.add_error(test, e)
89
- end
90
- test.after
91
- @clients.each_value { |c| c.end_test(test) }
92
- @runner.listeners.each { |l| l.end_test(test) }
93
- @logger.info("Result: #{test.result}")
94
- @logger.close
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
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-08-17 00:00:00.000000000 Z
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.15
112
+ rubygems_version: 2.0.14
77
113
  signing_key:
78
114
  specification_version: 4
79
115
  summary: Moto - yet another web testing framework