moto 0.0.61 → 0.7.0

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: 79fd340e02c8fc99118145bfff406152d69616b4
4
- data.tar.gz: 0409af8e817650b861ff323af7fd2d4689adffa5
3
+ metadata.gz: 5c6fc26ad79a37b98a47def96713f98aeb645d7a
4
+ data.tar.gz: 319b20fd048182b400a165c5f947eca5da265850
5
5
  SHA512:
6
- metadata.gz: aff3af12f4ce522fd01d733556a3a9b04c6ef155edbbbcadd7556ae00a8d0c4d38da9946907df1094f2b6e806fd79695a13877c2626ff08876faceb7ee5b82bc
7
- data.tar.gz: fa8bdff3222419dda771bee36e38615c50008d87ba500a8905ae010ac133b5f1591846b21ff9d6c2d037c15a838c0b3215b2b4cf2e1d087a87905048763c1a6d
6
+ metadata.gz: b7094905897ffa05fd13dcc0e170d3f9a9faf54b76a27aaff5776f353d664c035f786bab3fe9b8585ce594584a3c03c88f1d852e35e991787eb5eece832134dc
7
+ data.tar.gz: 8fb71b7424b346314b29cd76f232207db2fc3f19214adfbbbd3e2f9239896e8610b568ca3e192759e2ed87bb649b2971059a7f9098db31cb2f82cc9cdb885b92
data/lib/cli.rb CHANGED
@@ -13,7 +13,6 @@ module Moto
13
13
  end
14
14
 
15
15
  require_relative './empty_listener'
16
- require_relative './forward_context_methods'
17
16
  require_relative './test_logging'
18
17
  require_relative './runner_logging'
19
18
  require_relative './runner/test_runner'
@@ -3,23 +3,12 @@ module Moto
3
3
 
4
4
  class Base
5
5
  include Moto::EmptyListener
6
- include Moto::ForwardContextMethods
7
6
 
8
7
  # include Moto::RunnerLogging
9
8
  include Moto::TestLogging
10
9
 
11
- attr_reader :context
12
-
13
10
  ignore_logging(:handle_test_exception)
14
11
 
15
- def initialize(context)
16
- @context = context
17
- end
18
-
19
- def init
20
- # abstract
21
- end
22
-
23
12
  def handle_test_exception(test, exception)
24
13
  # abstract
25
14
  end
@@ -31,6 +20,11 @@ module Moto
31
20
  Moto::Lib::Config.environment_const(key)
32
21
  end
33
22
 
23
+ # Access client defined in Moto or MotoApp via it's name
24
+ def client(name)
25
+ Thread.current['clients_manager'].client(name)
26
+ end
27
+
34
28
  end
35
29
  end
36
30
  end
@@ -0,0 +1,51 @@
1
+ module Moto
2
+ module Lib
3
+ module Clients
4
+ class ClientsManager
5
+
6
+ attr_reader :clients
7
+
8
+ def initialize
9
+ @clients = {}
10
+ end
11
+
12
+ def client(name)
13
+ return @clients[name] if @clients.key? name
14
+
15
+ name_app = 'MotoApp::Lib::Clients::' + name
16
+ name_moto = 'Moto::Clients::' + name
17
+
18
+ client = try_client(name_app, "#{MotoApp::DIR}/")
19
+ if client
20
+ @clients[name] = client
21
+ return client
22
+ end
23
+
24
+ client = try_client(name_moto, "#{Moto::DIR}/lib/")
25
+ if client
26
+ @clients[name] = client
27
+ return client
28
+ end
29
+
30
+ raise "Could not find client class for name: #{name}"
31
+ end
32
+
33
+ def try_client(name, dir)
34
+ client_path = name.underscore.split('/')[1..-1].join('/')
35
+
36
+ if File.file?(dir + client_path + '.rb')
37
+ require dir + client_path
38
+ client_const = name.constantize
39
+ instance = client_const.new
40
+ instance.start_run
41
+ instance
42
+ else
43
+ nil
44
+ end
45
+ end
46
+ private :try_client
47
+
48
+ end
49
+ end
50
+ end
51
+ end
@@ -6,15 +6,25 @@ module Moto
6
6
 
7
7
  class Website < Moto::Clients::Base
8
8
 
9
- attr_reader :session
10
-
11
- ignore_logging(:page)
12
- ignore_logging(:context)
13
9
  ignore_logging(:session)
14
10
 
15
- def init
11
+ def initialize
16
12
  register_grid_driver
17
13
  register_chrome_driver
14
+
15
+ if config[:default_selector]
16
+ Capybara.default_selector = config[:default_selector]
17
+ end
18
+ end
19
+
20
+ # Returns Capybara session, if none was available for this thread it will create one.
21
+ def session
22
+ if Thread.current['capybara_session'].nil?
23
+ Thread.current['capybara_session'] = Capybara::Session.new(config[:default_driver])
24
+ Thread.current['capybara_session'].driver.browser.manage.window.maximize
25
+ end
26
+
27
+ Thread.current['capybara_session']
18
28
  end
19
29
 
20
30
  # @return [Hash] Config section for Capybara driver.
@@ -24,13 +34,7 @@ module Moto
24
34
  private :config
25
35
 
26
36
  def start_run
27
- # TODO: make session driver configurable
28
- if config[:default_selector]
29
- Capybara.default_selector = config[:default_selector]
30
- end
31
37
 
32
- Thread.current['capybara_session'] = Capybara::Session.new(config[:default_driver])
33
- @pages = {}
34
38
  end
35
39
 
36
40
  def end_run
@@ -44,18 +48,6 @@ module Moto
44
48
  def end_test(test)
45
49
  Thread.current['capybara_session'].reset_session!
46
50
  end
47
- #TODO fix moto to use Lib module
48
- def page(p)
49
- page_class_name = "#{self.class.name}::Pages::#{p}"
50
- page_class_name.gsub!('Moto::', 'MotoApp::Lib::')
51
- if @pages[page_class_name].nil?
52
- a = page_class_name.underscore.split('/')
53
- page_path = a[1..-1].join('/')
54
- require "#{MotoApp::DIR}/#{page_path}"
55
- @pages[page_class_name] = page_class_name.constantize.new(self)
56
- end
57
- @pages[page_class_name]
58
- end
59
51
 
60
52
  def register_grid_driver
61
53
  grid_config = config[:grid]
@@ -1,23 +1,14 @@
1
1
  module Moto
2
-
3
2
  class Page
4
3
 
5
4
  include Moto::TestLogging
6
- include Moto::ForwardContextMethods
7
5
 
8
6
  ignore_logging :const
9
7
  ignore_logging :session
10
8
 
11
- attr_reader :website
12
- attr_reader :context
13
-
14
- def initialize(website)
15
- @website = website
16
- @context = @website.context
17
- end
18
-
19
- def page(p)
20
- @context.client(@website.class.name.split('::').pop).page(p)
9
+ # Returns Capybara's session by means of on-the-fly client&session creation.
10
+ def session
11
+ Thread.current['clients_manager'].client('Website').session
21
12
  end
22
13
 
23
14
  def raise_unless_loaded
@@ -91,25 +91,18 @@ module Moto
91
91
  # Generates test instances, based on fully defined class file
92
92
  # @return [Moto::Test::Base]
93
93
  def generate_for_full_class_code(test_path_absolute)
94
-
95
- begin
96
- require test_path_absolute
97
- rescue Exception
98
- # will catch an error with non existent base class in test and stop it from breaking whole batch of tests
99
- # no need to handle it here, next begin/rescue clause in this function will finish the job
100
- end
101
-
102
94
  # Checking if it's possible to create test based on provided path. In case something is wrong with
103
95
  # modules structure in class itself Moto::Test::Base will be instantized with raise injected into its run()
104
96
  # so we can have proper reporting and summary even if the test doesn't execute.
105
97
  begin
98
+ require test_path_absolute
106
99
  class_name = test_path_absolute.gsub("#{MotoApp::DIR}/", 'moto_app/').camelize.chomp('.rb').constantize
107
100
  test_object = class_name.new
108
- rescue NameError
101
+ rescue NameError => e
109
102
  class_name = Moto::Test::Base
110
103
  test_object = class_name.new
111
104
 
112
- error_message = "ERROR: Invalid module structure: #{test_path_absolute.gsub("#{MotoApp::DIR}/", 'moto_app/').camelize.chomp('.rb')}"
105
+ error_message = "ERROR: Invalid test: #{test_path_absolute.gsub("#{MotoApp::DIR}/", 'moto_app/').camelize.chomp('.rb')}.\nMESSAGE: #{e}"
113
106
  inject_error_to_test(test_object, error_message)
114
107
  end
115
108
 
@@ -5,15 +5,11 @@ module Moto
5
5
  module Runner
6
6
  class TestRunner
7
7
 
8
- attr_reader :logger
9
8
  attr_reader :test_reporter
10
9
 
11
10
  def initialize(test_paths_absolute, test_reporter)
12
11
  @test_paths_absolute = test_paths_absolute
13
12
  @test_reporter = test_reporter
14
-
15
- # TODO: initialize logger from config (yml or just ruby code)
16
- @logger = Logger.new(File.open("#{MotoApp::DIR}/moto.log", File::WRONLY | File::APPEND | File::CREAT))
17
13
  end
18
14
 
19
15
  def run
@@ -30,10 +26,15 @@ module Moto
30
26
  # Create as many threads as we're allowed by the config file.
31
27
  # test_provider.get_test - will invoke Queue.pop thus putting the thread to sleep
32
28
  # once there is no more work.
29
+
30
+ Thread.abort_on_exception = true
31
+
33
32
  (1..threads_max).each do |index|
34
33
  Thread.new do
35
34
  Thread.current[:id] = index
36
35
  loop do
36
+ Thread.current['clients_manager'] = Moto::Lib::Clients::ClientsManager.new
37
+
37
38
  tc = ThreadContext.new(test_provider.get_test, @test_reporter)
38
39
  tc.run
39
40
  end
@@ -1,103 +1,58 @@
1
1
  require 'erb'
2
2
  require 'fileutils'
3
3
  require_relative '../../lib/config'
4
+ require_relative '../../lib/clients/clients_manager'
4
5
 
5
6
  module Moto
6
7
  module Runner
7
8
  class ThreadContext
8
9
 
9
- # all resources specific for single thread will be initialized here. E.g. browser session
10
- attr_reader :logger
11
10
  attr_reader :test
12
11
 
13
12
  def initialize(test, test_reporter)
14
13
  @test = test
15
- @clients = {}
16
- @test.context = self
17
14
  @test_reporter = test_reporter
18
15
 
19
- # TODO: temporary fix
20
- Thread.current['context'] = self
21
- end
22
-
23
- def client(name)
24
- return @clients[name] if @clients.key? name
25
-
26
- name_app = 'MotoApp::Lib::Clients::' + name
27
- name_moto = 'Moto::Clients::' + name
28
-
29
- c = try_client(name_app, "#{MotoApp::DIR}/")
30
- unless c.nil?
31
- @clients[name] = c
32
- return c
16
+ log_directory = File.dirname(@test.log_path)
17
+ if !File.directory?(log_directory)
18
+ FileUtils.mkdir_p(log_directory)
33
19
  end
34
20
 
35
- c = try_client(name_moto, "#{Moto::DIR}/lib")
36
- unless c.nil?
37
- @clients[name] = c
38
- return c
39
- end
40
- raise "Could not find client class for name #{name}"
21
+ Thread.current['logger'] = Logger.new(File.open(@test.log_path, File::WRONLY | File::TRUNC | File::CREAT))
22
+ Thread.current['logger'].level = config[:test_log_level] || Logger::DEBUG
41
23
  end
42
24
 
43
- def try_client(name, dir)
44
- begin
45
- a = name.underscore.split('/')
46
- client_path = a[1..-1].join('/')
47
- require "#{dir}/#{client_path}"
48
- client_const = name.constantize
49
- instance = client_const.new(self)
50
- instance.init
51
- instance.start_run
52
- instance.start_test(@test)
53
- return instance
54
- rescue Exception => e
55
- # puts e
56
- # puts e.backtrace
57
- return nil
58
- end
59
- end
60
-
61
- #
62
25
  def run
63
26
  max_attempts = config[:test_attempt_max] || 1
64
27
  sleep_time = config[:test_attempt_sleep] || 0
65
28
 
66
- log_directory = File.dirname(@test.log_path)
67
- if !File.directory?(log_directory)
68
- FileUtils.mkdir_p(log_directory)
69
- end
70
-
71
- @logger = Logger.new(File.open(@test.log_path, File::WRONLY | File::TRUNC | File::CREAT))
72
- @logger.level = config[:test_log_level] || Logger::DEBUG
73
-
74
29
  # Reporting: start_test
75
30
  @test_reporter.report_start_test(@test.status)
76
31
 
77
32
  (1..max_attempts).each do |attempt|
78
33
 
79
- @clients.each_value { |c| c.start_test(@test) }
34
+ Thread.current['clients_manager'].clients.each_value { |c| c.start_test(@test) }
80
35
  @test.before
81
- @logger.info "Start: #{@test.name} attempt #{attempt}/#{max_attempts}"
36
+ Thread.current['logger'].info("Start: #{@test.name} attempt #{attempt}/#{max_attempts}")
82
37
 
83
38
  begin
84
39
  @test.run_test
85
40
  rescue Exceptions::TestForcedPassed, Exceptions::TestForcedFailure, Exceptions::TestSkipped => e
86
- @logger.info(e.message)
41
+ Thread.current['logger'].info(e.message)
87
42
  rescue Exception => e
88
- @logger.error("#{e.class.name}: #{e.message}")
89
- @logger.error(e.backtrace.join("\n"))
90
- @clients.each_value { |c| c.handle_test_exception(@test, e) }
43
+ Thread.current['logger'].error("#{e.class.name}: #{e.message}")
44
+ Thread.current['logger'].error(e.backtrace.join("\n"))
45
+ Thread.current['clients_manager'].clients.each_value { |c| c.handle_test_exception(@test, e) }
91
46
  end
92
47
 
93
48
  @test.after
94
- @clients.each_value { |c| c.end_test(@test) }
49
+ Thread.current['clients_manager'].clients.each_value { |c| c.end_test(@test) }
95
50
 
96
- @logger.info("Result: #{@test.status.results.last.code}")
51
+ Thread.current['logger'].info("Result: #{@test.status.results.last.code}")
97
52
 
98
- # test should have another attempt only in case of an error
99
- # pass, skip and fail statuses end attempts
100
- if @test.status.results.last.code != Moto::Test::Result::ERROR
53
+ # test should have another attempt in case of an error / failure / none at all
54
+ if (@test.status.results.last.code == Moto::Test::Result::ERROR && !Moto::Lib::Config.moto[:test_reattempt_on_error]) ||
55
+ (@test.status.results.last.code == Moto::Test::Result::FAILURE && !Moto::Lib::Config.moto[:test_reattempt_on_fail] )
101
56
  break
102
57
  end
103
58
 
@@ -109,12 +64,12 @@ module Moto
109
64
  end # Make another attempt
110
65
 
111
66
  # Close and flush stream to file
112
- @logger.close
67
+ Thread.current['logger'].close
113
68
 
114
69
  # Reporting: end_test
115
70
  @test_reporter.report_end_test(@test.status)
116
71
 
117
- @clients.each_value { |c| c.end_run }
72
+ Thread.current['clients_manager'].clients.each_value { |c| c.end_run }
118
73
 
119
74
  end
120
75
 
@@ -1,7 +1,6 @@
1
1
  module Moto
2
2
  module RunnerLogging
3
3
 
4
-
5
4
  # TODO: merge it somehow with TestLogging. Parametrize logger object?
6
5
  def self.included(cls)
7
6
  def cls.method_added(name)
@@ -14,9 +13,9 @@ module Moto
14
13
  original_method = "original_#{name}"
15
14
  alias_method original_method, name
16
15
  define_method(name) do |*args|
17
- @context.runner.logger.debug("#{self.class.name}::#{__callee__} ENTER >>> #{args}") unless excluded_methods.include? name
16
+ Thread.current['logger'].debug("#{self.class.name}::#{__callee__} ENTER >>> #{args}") unless excluded_methods.include? name
18
17
  result = send original_method, *args
19
- @context.runner.logger.debug("#{self.class.name}::#{__callee__} LEAVE <<< #{result} ") unless excluded_methods.include? name
18
+ Thread.current['logger'].debug("#{self.class.name}::#{__callee__} LEAVE <<< #{result} ") unless excluded_methods.include? name
20
19
  result
21
20
  end
22
21
  @added = false
@@ -4,10 +4,7 @@ module Moto
4
4
  module Test
5
5
  class Base
6
6
 
7
- include Moto::ForwardContextMethods
8
-
9
7
  attr_reader :name
10
- attr_writer :context
11
8
  attr_reader :env
12
9
  attr_reader :params
13
10
  attr_accessor :static_path
@@ -156,7 +153,7 @@ module Moto
156
153
  end
157
154
 
158
155
  status.log_failure("ASSERTION FAILED in line #{line_number}: #{message}")
159
- logger.error(message)
156
+ Thread.current['logger'].error(message)
160
157
  end
161
158
  end
162
159
 
@@ -167,6 +164,14 @@ module Moto
167
164
  Moto::Lib::Config.environment_const(key)
168
165
  end
169
166
 
167
+ def client(name)
168
+ Thread.current['clients_manager'].client(name)
169
+ end
170
+
171
+ def session
172
+ client('Website').session
173
+ end
174
+
170
175
  end
171
176
  end
172
177
  end
@@ -36,10 +36,10 @@ module Moto
36
36
  end
37
37
  end
38
38
  end
39
- @context.test.logger.debug("ENTER >>> #{self.class.name}::#{__callee__}(#{args})") unless skip_logging
39
+ Thread.current['logger'].debug("ENTER >>> #{self.class.name}::#{__callee__}(#{args})") unless skip_logging
40
40
  result = send original_method, *args
41
41
  # Below is the hack to properly escape binary data (if any manages to make it to logs)
42
- @context.test.logger.debug("LEAVE <<< #{self.class.name}::#{__callee__} => #{[result].to_s[1..-2]}") unless skip_logging
42
+ Thread.current['logger'].debug("LEAVE <<< #{self.class.name}::#{__callee__} => #{[result].to_s[1..-2]}") unless skip_logging
43
43
  result
44
44
  end
45
45
  @added = false
@@ -1,3 +1,3 @@
1
1
  module Moto
2
- VERSION = '0.0.61'
2
+ VERSION = '0.7.0'
3
3
  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.61
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bartek Wilczek
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2016-05-18 00:00:00.000000000 Z
14
+ date: 2016-05-26 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: activesupport
@@ -87,6 +87,7 @@ files:
87
87
  - lib/app_generator.rb
88
88
  - lib/cli.rb
89
89
  - lib/clients/base.rb
90
+ - lib/clients/clients_manager.rb
90
91
  - lib/clients/website.rb
91
92
  - lib/config.rb
92
93
  - lib/empty_listener.rb
@@ -94,7 +95,6 @@ files:
94
95
  - lib/exceptions/test_forced_failure.rb
95
96
  - lib/exceptions/test_forced_passed.rb
96
97
  - lib/exceptions/test_skipped.rb
97
- - lib/forward_context_methods.rb
98
98
  - lib/initializer.rb
99
99
  - lib/page.rb
100
100
  - lib/parser.rb
@@ -1,19 +0,0 @@
1
- module Moto
2
- module ForwardContextMethods
3
- # Access client object instance for given class name.
4
- def client(name)
5
- @context.client(name)
6
- end
7
-
8
- # Write message to test execution log file. See Ruby Logger class for details.
9
- def logger
10
- @context.logger
11
- end
12
-
13
- # Access currently executed test
14
- def test
15
- @context.test
16
- end
17
-
18
- end
19
- end