capybara-lockstep 0.2.3 → 0.3.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
  SHA256:
3
- metadata.gz: aa8ccde381bb125f66222689d93c93008ec2137cfabc664a6ce83ff0701cd7a8
4
- data.tar.gz: 7485b88104f0c4b43387f252e600ad7dc23a917eac28914249b8f279d5ab9df3
3
+ metadata.gz: 6e993a9ec82edc1bbea78694f16ae5b3e171255db9eb7e14064922b153d1838d
4
+ data.tar.gz: 1086e7149ca202b105fd0472c839451845079e05c777a0dba839483fe05c473b
5
5
  SHA512:
6
- metadata.gz: 19f52ab1ca0e9f30fe8be0e791b66bcefe9992d958a104a30c602987e310363587f075f7af19bebf8248e76836a008601c700092bc3b1d53fff3fda5b764dcdc
7
- data.tar.gz: e6380494b6940092842f87e54c898e6a2da9311aa2fd1951a602fb193fb44c7ee6f96907704d80382bd1cc2ccc31a7e0b433e27da8906f889aa6f7178fcd2dff
6
+ metadata.gz: e54e30ae12effa97dcdbd73d0f07b465b90270efc3ddd3ad83fcde7a4d305164ac0380f7ab3128a37870e78862483659eb01ff6bcdf8fade32cd52c3024d5c31
7
+ data.tar.gz: c067bf3f9e0be7d6a84668ee7412ea7d93d450f109e8018940e72392277bcc0f5d0ef92717a1692dee8e0443509bc9317a7b403620b6eb62bdb78539ac036098
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- capybara-lockstep (0.2.3)
4
+ capybara-lockstep (0.3.0)
5
5
  activesupport (>= 3.2)
6
6
  capybara (>= 2.0)
7
7
  selenium-webdriver (>= 3)
data/README.md CHANGED
@@ -220,7 +220,6 @@ ensure
220
220
  end
221
221
  ```
222
222
 
223
-
224
223
  ## Timeout
225
224
 
226
225
  By default capybara-lockstep will wait up to 10 seconds for the page initialize and for JavaScript and AJAX request to finish.
@@ -231,8 +230,28 @@ You can configure a different timeout:
231
230
  Capybara::Lockstep.timeout = 5 # seconds
232
231
  ```
233
232
 
233
+ ## Ruby API
234
+
235
+ capybara-lockstep will automatically patch Capybara to wait for the browser after every command. **This should be enough for most test suites**.
236
+
237
+ For additional edge cases you may interact with capybara-lockstep from your Ruby code.
238
+
239
+
240
+ ### Waiting until the browser is idle
241
+
242
+ This will block until the document was loaded, the DOM has been hydrated and all AJAX requests have concluded:
243
+
244
+ ```ruby
245
+ Capybara::Lockstep.synchronize
246
+ ```
234
247
 
248
+ An example use case is a Cucumber step that explicitely waits for JavaScript to finish, in the rare occasion where capybara-lockstep hasn't picked up an event or request:
235
249
 
250
+ ```gherkin
251
+ When 'I wait for the page to load' do
252
+ Capybara::Lockstep.synchronize
253
+ end
254
+ ```
236
255
 
237
256
  ## JavaScript API
238
257
 
@@ -270,55 +289,29 @@ CapybaraLockstep.isIdle() // => true
270
289
 
271
290
  ### Waiting until the browser is idle
272
291
 
273
- ```js
274
- CapybaraLockstep.awaitIdle(callback)
275
- ```
276
-
277
- ## Ruby API
278
-
279
- capybara-lockstep will automatically patch Capybara to wait for the browser after every command. **This should be enough for most test suites**.
280
-
281
- For additional edge cases you may interact with capybara-lockstep from your Ruby code.
282
-
283
-
284
- ### Waiting until the browser is idle
285
-
286
- This will block until the document was loaded and the DOM has been hydrated:
287
-
288
- ```ruby
289
- Capybara::Lockstep.await_initialized
290
- ```
291
-
292
- This will block while the browser is busy with JavaScript and AJAX requests:
293
-
294
- ```ruby
295
- Capybara::Lockstep.await_idle
296
- ```
297
-
298
- ### Checking if the browser is busy
299
-
300
- You can query capybara-lockstep whether it considers the browser to be busy or idle:
292
+ This will run the given callback once the browser is considered to be idle:
301
293
 
302
- ```ruby
303
- Capybara::Lockstep.idle? # => true
304
- Capybara::Lockstep.busy? # => false
294
+ ```js
295
+ CapybaraLockstep.synchronize(callback)
305
296
  ```
306
297
 
307
-
308
298
  ## Development
309
299
 
310
300
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
311
301
 
312
302
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
313
303
 
304
+
314
305
  ## Contributing
315
306
 
316
307
  Pull requests are welcome on GitHub at <https://github.com/makandra/capybara-lockstep>.
317
308
 
309
+
318
310
  ## License
319
311
 
320
312
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
321
313
 
314
+
322
315
  ## Credits
323
316
 
324
317
  Henning Koch ([@triskweline](https://twitter.com/triskweline)) from [makandra](https://makandra.com).
@@ -10,8 +10,8 @@ end
10
10
 
11
11
  require_relative 'capybara-lockstep/version'
12
12
  require_relative 'capybara-lockstep/errors'
13
- require_relative 'capybara-lockstep/patiently'
14
13
  require_relative 'capybara-lockstep/configuration'
14
+ require_relative 'capybara-lockstep/logging'
15
15
  require_relative 'capybara-lockstep/lockstep'
16
16
  require_relative 'capybara-lockstep/capybara_ext'
17
17
  require_relative 'capybara-lockstep/helper'
@@ -2,18 +2,23 @@ module Capybara
2
2
  module Lockstep
3
3
  module VisitWithWaiting
4
4
  def visit(*args, &block)
5
- visiting_remote_url = !args[0].start_with?('data:')
5
+ url = args[0]
6
+ # Some of our apps have a Cucumber step that changes drivers mid-scenario.
7
+ # It works by creating a new Capybara session and re-visits the URL from the
8
+ # previous session. If this happens before a URL is ever loaded,
9
+ # it re-visits the URL "data:", which will never "finish" initializing.
10
+ # Also when opening a new tab via Capybara, the initial URL is about:blank.
11
+ visiting_remote_url = !(url.start_with?('data:') || url.start_with?('about:'))
6
12
 
7
- Capybara::Lockstep.catch_up
13
+ if visiting_remote_url
14
+ # We're about to leave this screen, killing all in-flight requests.
15
+ Capybara::Lockstep.synchronize
16
+ end
8
17
 
9
18
  super(*args, &block).tap do
10
- # There is a step that changes drivers mid-scenario.
11
- # It works by creating a new Capybara session and re-visits the
12
- # URL from the previous session. If this happens before a URL is ever
13
- # loaded, it re-visits the URL "data:", which will never "finish"
14
- # initializing.
15
19
  if visiting_remote_url
16
- Capybara::Lockstep.await_initialized
20
+ # puts "After visit: unsynchronizing"
21
+ Capybara::Lockstep.synchronized = false
17
22
  end
18
23
  end
19
24
  end
@@ -28,13 +33,12 @@ end
28
33
 
29
34
  module Capybara
30
35
  module Lockstep
31
- module AwaitIdle
32
- def await_idle(meth)
36
+ module UnsychronizeAfter
37
+ def unsychronize_after(meth)
33
38
  mod = Module.new do
34
39
  define_method meth do |*args, &block|
35
- Capybara::Lockstep.catch_up
36
40
  super(*args, &block).tap do
37
- Capybara::Lockstep.await_idle
41
+ Capybara::Lockstep.synchronized = false
38
42
  end
39
43
  end
40
44
  end
@@ -62,21 +66,21 @@ end
62
66
 
63
67
  node_classes.each do |node_class|
64
68
  node_class.class_eval do
65
- extend Capybara::Lockstep::AwaitIdle
69
+ extend Capybara::Lockstep::UnsychronizeAfter
66
70
 
67
- await_idle :set
68
- await_idle :select_option
69
- await_idle :unselect_option
70
- await_idle :click
71
- await_idle :right_click
72
- await_idle :double_click
73
- await_idle :send_keys
74
- await_idle :hover
75
- await_idle :drag_to
76
- await_idle :drop
77
- await_idle :scroll_by
78
- await_idle :scroll_to
79
- await_idle :trigger
71
+ unsychronize_after :set
72
+ unsychronize_after :select_option
73
+ unsychronize_after :unselect_option
74
+ unsychronize_after :click
75
+ unsychronize_after :right_click
76
+ unsychronize_after :double_click
77
+ unsychronize_after :send_keys
78
+ unsychronize_after :hover
79
+ unsychronize_after :drag_to
80
+ unsychronize_after :drop
81
+ unsychronize_after :scroll_by
82
+ unsychronize_after :scroll_to
83
+ unsychronize_after :trigger
80
84
  end
81
85
  end
82
86
 
@@ -84,7 +88,9 @@ module Capybara
84
88
  module Lockstep
85
89
  module SynchronizeWithCatchUp
86
90
  def synchronize(*args, &block)
87
- Capybara::Lockstep.catch_up
91
+ # This method is called very frequently by capybara.
92
+ # We use the { lazy } option to only synchronize when we're out of sync.
93
+ Capybara::Lockstep.synchronize(lazy: true)
88
94
 
89
95
  super(*args, &block)
90
96
  end
@@ -30,6 +30,10 @@ module Capybara
30
30
  @enabled = enabled
31
31
  end
32
32
 
33
+ def disabled?
34
+ !enabled?
35
+ end
36
+
33
37
  private
34
38
 
35
39
  def javascript_driver?
@@ -37,7 +37,7 @@ window.CapybaraLockstep = (function() {
37
37
 
38
38
  if (isIdle()) {
39
39
  idleCallbacks.forEach(function(callback) {
40
- callback('JavaScript has finished')
40
+ callback('Finished waiting for JavaScript')
41
41
  })
42
42
  idleCallbacks = []
43
43
  }
@@ -116,7 +116,7 @@ window.CapybaraLockstep = (function() {
116
116
  // Dynamic imports or analytics snippets may insert a <script src>
117
117
  // tag that loads and executes additional JavaScript. We want to be isBusy()
118
118
  // until such scripts have loaded or errored.
119
- var observer = new MutationObserver(onMutated)
119
+ var observer = new MutationObserver(onAnyElementChanged)
120
120
  observer.observe(document, { subtree: true, childList: true })
121
121
  }
122
122
 
@@ -139,6 +139,28 @@ window.CapybaraLockstep = (function() {
139
139
  })
140
140
  }
141
141
 
142
+ var INITIALIZING_ATTRIBUTE = 'data-initializing'
143
+
144
+ function trackHydration() {
145
+ // Until we have a body on which we can observe [data-initializing]
146
+ // we consider ourselves busy.
147
+ startWork()
148
+ whenReady(function() {
149
+ stopWork()
150
+ if (document.body.hasAttribute(INITIALIZING_ATTRIBUTE)) {
151
+ startWork()
152
+ var observer = new MutationObserver(onInitializingAttributeChanged)
153
+ observer.observe(document.body, { attributes: true, attributeFilter: [INITIALIZING_ATTRIBUTE] })
154
+ }
155
+ })
156
+ }
157
+
158
+ function onInitializingAttributeChanged() {
159
+ if (!document.body.hasAttribute(INITIALIZING_ATTRIBUTE)) {
160
+ stopWork()
161
+ }
162
+ }
163
+
142
164
  function isRemoteScript(node) {
143
165
  if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'SCRIPT') {
144
166
  var src = node.getAttribute('src')
@@ -155,7 +177,7 @@ window.CapybaraLockstep = (function() {
155
177
  script.addEventListener('error', stopWork)
156
178
  }
157
179
 
158
- function onMutated(changes) {
180
+ function onAnyElementChanged(changes) {
159
181
  changes.forEach(function(change) {
160
182
  change.addedNodes.forEach(function(addedNode) {
161
183
  if (isRemoteScript(addedNode)) {
@@ -168,7 +190,7 @@ window.CapybaraLockstep = (function() {
168
190
  function whenReady(callback) {
169
191
  // Values are "loading", "interactive" and "completed".
170
192
  // https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState
171
- if (document.readyState != 'loading') {
193
+ if (document.readyState !== 'loading') {
172
194
  callback()
173
195
  } else {
174
196
  document.addEventListener('DOMContentLoaded', callback)
@@ -182,9 +204,10 @@ window.CapybaraLockstep = (function() {
182
204
  trackHistory()
183
205
  trackDynamicScripts()
184
206
  trackJQuery()
207
+ trackHydration()
185
208
  }
186
209
 
187
- function awaitIdle(callback) {
210
+ function synchronize(callback) {
188
211
  if (isIdle()) {
189
212
  callback()
190
213
  } else {
@@ -196,7 +219,7 @@ window.CapybaraLockstep = (function() {
196
219
  track: track,
197
220
  startWork: startWork,
198
221
  stopWork: stopWork,
199
- awaitIdle: awaitIdle,
222
+ synchronize: synchronize,
200
223
  isIdle: isIdle,
201
224
  isBusy: isBusy
202
225
  }
@@ -1,133 +1,79 @@
1
1
  module Capybara
2
2
  module Lockstep
3
3
  class << self
4
- include Patiently
5
4
  include Configuration
5
+ include Logging
6
6
 
7
- def await_idle
8
- @delay_await_idle = false
9
- return unless enabled?
10
-
11
- with_max_wait_time(timeout) do
12
- message_from_js = evaluate_async_script(<<~JS)
13
- let done = arguments[0]
14
- if (window.CapybaraLockstep) {
15
- CapybaraLockstep.awaitIdle(done)
16
- } else {
17
- done('Cannot synchronize: Capybara::Lockstep was not included in page')
18
- }
19
- JS
20
- log(message_from_js)
21
- end
22
- rescue ::Selenium::WebDriver::Error::UnexpectedAlertOpenError
23
- log 'Cannot synchronize: Alert is open'
24
- @delay_await_idle = true
25
- end
7
+ attr_accessor :synchronized
26
8
 
27
- def await_initialized
28
- @delay_await_initialized = false
29
- @delay_await_idle = false # since we're also waiting for idle
30
- return unless enabled?
9
+ def synchronized?
10
+ value = page.instance_variable_get(:@lockstep_synchronized)
11
+ # We consider a new Capybara session to be synchronized.
12
+ # This will be set to false after our first visit().
13
+ value.nil? ? true : value
14
+ end
31
15
 
32
- # We're retrying the initialize check every few ms.
33
- # Don't clutter the log with dozens of identical messages.
34
- last_logged_reason = nil
16
+ def synchronized=(value)
17
+ page.instance_variable_set(:@lockstep_synchronized, value)
18
+ end
35
19
 
36
- patiently(timeout) do
37
- if (reason = initialize_reason)
38
- if reason != last_logged_reason
39
- log(reason)
40
- last_logged_reason = reason
41
- end
20
+ ERROR_SNIPPET_MISSING = 'Cannot synchronize: Capybara::Lockstep JavaScript snippet is missing on page'
21
+ ERROR_PAGE_MISSING = 'Cannot synchronize before initial Capybara visit'
42
22
 
43
- # Raise an exception that will be retried by `patiently`
44
- raise Busy, reason
45
- end
23
+ def synchronize(lazy: false)
24
+ if (lazy && synchronized?) || @synchronizing || disabled?
25
+ return
46
26
  end
47
- rescue ::Selenium::WebDriver::Error::UnexpectedAlertOpenError
48
- log 'Cannot synchronize: Alert is open'
49
- @delay_await_initialized = true
50
- end
51
27
 
52
- def catch_up
53
- return if @catching_up
28
+ @synchronizing = true
29
+
30
+ log 'Synchronizing'
54
31
 
55
32
  begin
56
- @catching_up = true
57
- if @delay_await_initialized
58
- log 'Retrying synchronization'
59
- await_initialized
60
- # elsif browser_made_full_page_load?
61
- # log 'Browser loaded new page'
62
- # await_initialized
63
- elsif @delay_await_idle
64
- log 'Retrying synchronization'
65
- await_idle
33
+ with_max_wait_time(timeout) do
34
+ message_from_js = evaluate_async_script(<<~JS)
35
+ let done = arguments[0]
36
+ let synchronize = () => {
37
+ if (window.CapybaraLockstep) {
38
+ CapybaraLockstep.synchronize(done)
39
+ } else {
40
+ done(#{ERROR_SNIPPET_MISSING.to_json})
41
+ }
42
+ }
43
+ let protocol = location.protocol
44
+ if (protocol === 'data:' || protocol == 'about:') {
45
+ done(#{ERROR_PAGE_MISSING.to_json})
46
+ } else if (document.readyState === 'complete') {
47
+ synchronize()
48
+ } else {
49
+ window.addEventListener('load', synchronize)
50
+ }
51
+ JS
52
+
53
+ case message_from_js
54
+ when ERROR_PAGE_MISSING
55
+ log(message_from_js)
56
+ self.synchronized = false
57
+ when ERROR_SNIPPET_MISSING
58
+ log(message_from_js)
59
+ self.synchronized = false
60
+ else
61
+ log message_from_js
62
+ log "Synchronized sucessfully"
63
+ self.synchronized = true
64
+ end
66
65
  end
66
+ rescue StandardError => e
67
+ log "#{e.class.name} while synchronizing: #{e.message}"
68
+ @synchronized = false
69
+ raise e
67
70
  ensure
68
- @catching_up = false
71
+ @synchronizing = false
69
72
  end
70
73
  end
71
74
 
72
- def idle?
73
- unless enabled?
74
- return true
75
- end
76
-
77
- result = execute_script(<<~JS)
78
- if (window.CapybaraLockstep) {
79
- return CapybaraLockstep.isIdle()
80
- } else {
81
- return 'Cannot check busy state: Capybara::Lockstep was not included in page'
82
- }
83
- JS
84
-
85
- if result.is_a?(String)
86
- log(result)
87
- # When the snippet is missing we assume that the browser is idle.
88
- # Otherwise we would wait forever.
89
- true
90
- else
91
- result
92
- end
93
- end
94
-
95
- def busy?
96
- !idle?
97
- end
98
-
99
75
  private
100
76
 
101
- def browser_made_full_page_load?
102
- # Page change without visit()
103
- page.has_css?('body[data-hydrating]')
104
- end
105
-
106
- def initialize_reason
107
- execute_script(<<~JS)
108
- if (location.href.indexOf('data:') == 0) {
109
- return 'Requesting initial page'
110
- }
111
-
112
- if (document.readyState !== "complete") {
113
- return 'Document is loading'
114
- }
115
-
116
- // The application layouts render a <body data-initializing>.
117
- // The [data-initializing] attribute is removed by an Angular directive or Unpoly compiler (frontend).
118
- // to signal that all elements have been activated.
119
- if (document.querySelector('body[data-initializing]')) {
120
- return 'DOM is being hydrated'
121
- }
122
-
123
- if (window.CapybaraLockstep && CapybaraLockstep.isBusy()) {
124
- return 'JavaScript or AJAX requests are running'
125
- }
126
-
127
- return false
128
- JS
129
- end
130
-
131
77
  def page
132
78
  Capybara.current_session
133
79
  end
@@ -144,17 +90,10 @@ module Capybara
144
90
  end
145
91
  end
146
92
 
147
- def log(message)
148
- if debug? && message.present?
149
- message = "[Capybara::Lockstep] #{message}"
150
- if @debug.respond_to?(:debug)
151
- # If someone set Capybara::Lockstep to a logger, use that
152
- @debug.debug(message)
153
- else
154
- # Otherwise print to STDOUT
155
- puts message
156
- end
157
- end
93
+ def ignoring_alerts(&block)
94
+ block.call
95
+ rescue ::Selenium::WebDriver::Error::UnexpectedAlertOpenError
96
+ # no-op
158
97
  end
159
98
 
160
99
  end
@@ -0,0 +1,18 @@
1
+ module Capybara
2
+ module Lockstep
3
+ module Logging
4
+ def log(message)
5
+ if debug? && message.present?
6
+ message = "[Capybara::Lockstep] #{message}"
7
+ if @debug.respond_to?(:debug)
8
+ # If someone set Capybara::Lockstep to a logger, use that
9
+ @debug.debug(message)
10
+ else
11
+ # Otherwise print to STDOUT
12
+ puts message
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,5 @@
1
1
  module Capybara
2
2
  module Lockstep
3
- VERSION = "0.2.3"
3
+ VERSION = "0.3.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.2.3
4
+ version: 0.3.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-03 00:00:00.000000000 Z
11
+ date: 2021-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: capybara
@@ -77,7 +77,7 @@ files:
77
77
  - lib/capybara-lockstep/helper.js
78
78
  - lib/capybara-lockstep/helper.rb
79
79
  - lib/capybara-lockstep/lockstep.rb
80
- - lib/capybara-lockstep/patiently.rb
80
+ - lib/capybara-lockstep/logging.rb
81
81
  - lib/capybara-lockstep/version.rb
82
82
  homepage: https://github.com/makandra/capybara-lockstep
83
83
  licenses:
@@ -1,58 +0,0 @@
1
- module Capybara
2
- module Lockstep
3
- # Ported from https://github.com/makandra/spreewald/blob/master/lib/spreewald_support/tolerance_for_selenium_sync_issues.rb
4
- module Patiently
5
-
6
- RETRY_ERRORS = %w[
7
- Capybara::Lockstep::Busy
8
- Capybara::ElementNotFound
9
- Spec::Expectations::ExpectationNotMetError
10
- RSpec::Expectations::ExpectationNotMetError
11
- Minitest::Assertion
12
- Capybara::Poltergeist::ClickFailed
13
- Capybara::ExpectationNotMet
14
- Selenium::WebDriver::Error::StaleElementReferenceError
15
- Selenium::WebDriver::Error::NoAlertPresentError
16
- Selenium::WebDriver::Error::ElementNotVisibleError
17
- Selenium::WebDriver::Error::NoSuchFrameError
18
- Selenium::WebDriver::Error::NoAlertPresentError
19
- Selenium::WebDriver::Error::JavascriptError
20
- Selenium::WebDriver::Error::UnknownError
21
- Selenium::WebDriver::Error::NoSuchAlertError
22
- ]
23
-
24
- # evaluate_script latency is ~ 0.025s
25
- WAIT_PERIOD = 0.03
26
-
27
- def patiently(timeout = Capybara.default_max_wait_time, &block)
28
- started = monotonic_time
29
- tries = 0
30
- begin
31
- tries += 1
32
- block.call
33
- rescue Exception => e
34
- raise e unless retryable_error?(e)
35
- raise e if (monotonic_time - started > timeout && tries >= 2)
36
- sleep(WAIT_PERIOD)
37
- if monotonic_time == started
38
- raise Capybara::FrozenInTime, "time appears to be frozen, Capybara does not work with libraries which freeze time, consider using time travelling instead"
39
- end
40
- retry
41
- end
42
- end
43
-
44
- private
45
-
46
- def monotonic_time
47
- # We use the system clock (i.e. seconds since boot) to calculate the time,
48
- # because Time.now may be frozen
49
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
50
- end
51
-
52
- def retryable_error?(e)
53
- RETRY_ERRORS.include?(e.class.name)
54
- end
55
-
56
- end
57
- end
58
- end