capybara-lockstep 0.3.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,7 +17,22 @@ module Capybara
17
17
  tag_options[:nonce] = options.fetch(:nonce, true)
18
18
  end
19
19
 
20
- javascript_tag(capybara_lockstep_js, tag_options)
20
+ js = capybara_lockstep_js + capybara_lockstep_config_js(options)
21
+ javascript_tag(js, tag_options)
22
+ end
23
+
24
+ def capybara_lockstep_config_js(options = {})
25
+ js = ''
26
+
27
+ if (debug = options.fetch(:debug, Lockstep.debug?))
28
+ js += "\nCapybaraLockstep.debug = #{debug.to_json}"
29
+ end
30
+
31
+ if (wait_tasks = options.fetch(:wait_tasks, Lockstep.wait_tasks))
32
+ js += "\nCapybaraLockstep.waitTasks = #{wait_tasks.to_json}"
33
+ end
34
+
35
+ js
21
36
  end
22
37
 
23
38
  end
@@ -1,10 +1,16 @@
1
1
  module Capybara
2
2
  module Lockstep
3
+ ERROR_SNIPPET_MISSING = 'Cannot synchronize: capybara-lockstep JavaScript snippet is missing'
4
+ ERROR_PAGE_MISSING = 'Cannot synchronize before initial Capybara visit'
5
+ ERROR_ALERT_OPEN = 'Cannot synchronize while an alert is open'
6
+ ERROR_NAVIGATED_AWAY = "Browser navigated away while synchronizing"
7
+
3
8
  class << self
4
9
  include Configuration
5
10
  include Logging
6
11
 
7
- attr_accessor :synchronized
12
+ attr_accessor :synchronizing
13
+ alias synchronizing? synchronizing
8
14
 
9
15
  def synchronized?
10
16
  value = page.instance_variable_get(:@lockstep_synchronized)
@@ -17,19 +23,24 @@ module Capybara
17
23
  page.instance_variable_set(:@lockstep_synchronized, value)
18
24
  end
19
25
 
20
- ERROR_SNIPPET_MISSING = 'Cannot synchronize: Capybara::Lockstep JavaScript snippet is missing on page'
21
- ERROR_PAGE_MISSING = 'Cannot synchronize before initial Capybara visit'
22
- ERROR_ALERT_OPEN = 'Cannot synchronize while an alert is open'
23
-
24
26
  def synchronize(lazy: false)
25
- if (lazy && synchronized?) || @synchronizing || disabled?
27
+ if (lazy && synchronized?) || synchronizing? || disabled?
26
28
  return
27
29
  end
28
30
 
29
- @synchronizing = true
31
+ synchronize_now
32
+ end
33
+
34
+ private
35
+
36
+ def synchronize_now
37
+ self.synchronizing = true
38
+ self.synchronized = false
30
39
 
31
40
  log 'Synchronizing'
32
41
 
42
+ start_time = current_seconds
43
+
33
44
  begin
34
45
  with_max_wait_time(timeout) do
35
46
  message_from_js = evaluate_async_script(<<~JS)
@@ -54,31 +65,46 @@ module Capybara
54
65
  case message_from_js
55
66
  when ERROR_PAGE_MISSING
56
67
  log(message_from_js)
57
- self.synchronized = false
58
68
  when ERROR_SNIPPET_MISSING
59
69
  log(message_from_js)
60
- self.synchronized = false
61
70
  else
62
71
  log message_from_js
63
- log "Synchronized successfully"
72
+ end_time = current_seconds
73
+ ms_elapsed = ((end_time.to_f - start_time) * 1000).round
74
+ log "Synchronized successfully [#{ms_elapsed} ms]"
64
75
  self.synchronized = true
65
76
  end
66
77
  end
78
+ rescue ::Selenium::WebDriver::Error::ScriptTimeoutError
79
+ log "Could not synchronize within #{timeout} seconds"
80
+ # Don't raise an error, this may happen if the server is slow to respond.
81
+ # We will retry on the next Capybara synchronize call.
67
82
  rescue ::Selenium::WebDriver::Error::UnexpectedAlertOpenError
68
83
  log ERROR_ALERT_OPEN
69
- @synchronized = false
70
84
  # Don't raise an error, this will happen in an innocent test.
71
85
  # We will retry on the next Capybara synchronize call.
86
+ rescue ::Selenium::WebDriver::Error::JavascriptError => e
87
+ # When the URL changes while a script is running, my current selenium-webdriver
88
+ # raises a Selenium::WebDriver::Error::JavascriptError with the message:
89
+ # "javascript error: document unloaded while waiting for result".
90
+ # We will retry on the next Capybara synchronize call, by then we should see
91
+ # the new page.
92
+ if e.message.include?('unload')
93
+ log ERROR_NAVIGATED_AWAY
94
+ else
95
+ unhandled_synchronize_error(e)
96
+ end
72
97
  rescue StandardError => e
73
- log "#{e.class.name} while synchronizing: #{e.message}"
74
- @synchronized = false
75
- raise e
98
+ unhandled_synchronize_error(e)
76
99
  ensure
77
- @synchronizing = false
100
+ self.synchronizing = false
78
101
  end
79
102
  end
80
103
 
81
- private
104
+ def unhandled_synchronize_error(e)
105
+ log "#{e.class.name} while synchronizing: #{e.message}"
106
+ raise e
107
+ end
82
108
 
83
109
  def page
84
110
  Capybara.current_session
@@ -102,6 +128,10 @@ module Capybara
102
128
  # no-op
103
129
  end
104
130
 
131
+ def current_seconds
132
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
133
+ end
134
+
105
135
  end
106
136
 
107
137
  end
@@ -3,8 +3,8 @@ module Capybara
3
3
  module Logging
4
4
  def log(message)
5
5
  if debug? && message.present?
6
- message = "[Capybara::Lockstep] #{message}"
7
- if @debug.respond_to?(:debug)
6
+ message = "[capybara-lockstep] #{message}"
7
+ if is_logger?(@debug)
8
8
  # If someone set Capybara::Lockstep to a logger, use that
9
9
  @debug.debug(message)
10
10
  else
@@ -13,6 +13,12 @@ module Capybara
13
13
  end
14
14
  end
15
15
  end
16
+
17
+ private
18
+
19
+ def is_logger?(object)
20
+ @debug.respond_to?(:debug)
21
+ end
16
22
  end
17
23
  end
18
24
  end
@@ -1,5 +1,5 @@
1
1
  module Capybara
2
2
  module Lockstep
3
- VERSION = "0.3.2"
3
+ VERSION = "0.7.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capybara-lockstep
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Henning Koch
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-03-04 00:00:00.000000000 Z
11
+ date: 2021-05-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: capybara
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: ruby2_keywords
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  description:
56
70
  email:
57
71
  - henning.koch@makandra.de
@@ -59,9 +73,11 @@ executables: []
59
73
  extensions: []
60
74
  extra_rdoc_files: []
61
75
  files:
76
+ - ".github/workflows/test.yml"
62
77
  - ".gitignore"
63
78
  - ".rspec"
64
79
  - ".ruby-version"
80
+ - CHANGELOG.md
65
81
  - Gemfile
66
82
  - Gemfile.lock
67
83
  - LICENSE.txt
@@ -100,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
100
116
  - !ruby/object:Gem::Version
101
117
  version: '0'
102
118
  requirements: []
103
- rubygems_version: 3.2.5
119
+ rubygems_version: 3.0.3
104
120
  signing_key:
105
121
  specification_version: 4
106
122
  summary: Synchronize Capybara commands with client-side JavaScript and AJAX requests