capybara-lockstep 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 514789431b5b017176575acd43c1a5b6558040f31cf7cb4ac119cf10e756dbee
4
- data.tar.gz: 2945ac3b32b7f83a7bc2bd14848f67950c12e5063033d8dbe2063788dfe8c024
3
+ metadata.gz: fa4a510d3f4745ef60812fdf3ab03f6c3e8ede0c6ea4046cb87364d6664e2a7d
4
+ data.tar.gz: 78be38bdb5aa4fcb9147394ac3adb11f3112c18539ba53a096c214931be753cb
5
5
  SHA512:
6
- metadata.gz: b2dac0508c3caaa13fd28fe285c9c3b7d2b796dfba6c2bf788578ffee90135b6504a2e9b731d8bf4496806723d26c28bf5ccb536e5de21c2b5661b354a9e2de1
7
- data.tar.gz: 4e80b685b588dfded10cb0783cbe62aceb00517199ef5527ce76fb76a234c96eb913ec0e7d08df2e22a7dd5a2b2d475bed0d4318add6ddee9fd27403afc2e0fe
6
+ metadata.gz: 356df47acf01a2642769be49f54c48736ea030c803a92b3c059c587e5e82bb927fa0b4e5dcf7953060162a46491cf0122c6a2d0debd1f607cbd1b09cc8608ff2
7
+ data.tar.gz: c5d1189ee309d32197f19765dad9014cc59f13a8d9f6a17287537045cbf02664b480ce9d087e933b3fad1a2c63b4460b5dc5e61e20d3e0b98d76cde9d90eaff3
@@ -3,13 +3,13 @@ name: Tests
3
3
  on:
4
4
  push:
5
5
  branches:
6
- - master
6
+ - main
7
7
  pull_request:
8
8
  branches:
9
- - master
9
+ - main
10
10
  workflow_dispatch:
11
11
  branches:
12
- - master
12
+ - main
13
13
  jobs:
14
14
  test:
15
15
  runs-on: ubuntu-20.04
data/CHANGELOG.md CHANGED
@@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file.
3
3
  This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
4
4
 
5
5
 
6
+ # 2.2.0
7
+
8
+ - We now wait for `<video>` and `<audio>` elements to load their metadata. This addresses a race condition where a media element is inserted into the DOM, but another user action deletes or renames the source before the browser could load the initial metadata frames.
9
+ - We now wait for `<script type="module">`.
10
+ - We no longer wait for `<img loading="lazy">` or `<iframe loading="lazy">`. This prevents a deadlock where we would wait forever for an element that defers loading until it is scrolled into the viewport.
11
+
12
+
6
13
  # 2.1.0
7
14
 
8
15
  - We now synchronize for an additional [JavaScript task](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/) after `history.pushState()`, `history.replaceState()`, `history.forward()`, `history.back()` and `history.go()`.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- capybara-lockstep (2.1.0)
4
+ capybara-lockstep (2.2.0)
5
5
  activesupport (>= 4.2)
6
6
  capybara (>= 3.0)
7
7
  ruby2_keywords
data/README.md CHANGED
@@ -71,7 +71,8 @@ When capybara-lockstep synchronizes it will:
71
71
  - wait for client-side JavaScript to render or hydrate DOM elements.
72
72
  - wait for any pending AJAX requests to finish and their callbacks to be called.
73
73
  - wait for dynamically inserted `<script>`s to load (e.g. from [dynamic imports](https://webpack.js.org/guides/code-splitting/#dynamic-imports) or Analytics snippets).
74
- - waits for dynamically inserted `<img>` or `<iframe>` elements to load.
74
+ - waits for dynamically inserted `<img>` or `<iframe>` elements to load (ignoring [lazy-loaded](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#lazy) elements).
75
+ - waits for dynamically inserted `<audio>` and `<video>` elements to load their [metadata](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/loadedmetadata_event)
75
76
 
76
77
  In summary Capybara can no longer observe or interact with the page while HTTP requests are in flight.
77
78
  This covers most async work that causes flaky tests.
@@ -158,7 +159,23 @@ use Capybara::Lockstep::Middleware
158
159
  # Other middleware here
159
160
  ```
160
161
 
162
+ ### Configuring Selenium WebDriver (recommended)
161
163
 
164
+ By default, webdrivers will automatically dismiss any user prompts (like alerts) when trying to perform an action.
165
+ While capybara-lockstep carefully detects alerts before synchronizing, and will skip interaction with the browser to avoid accidentally dismissing alerts, it can not synchronize around some rare race conditions.
166
+
167
+ [We recommend](https://makandracards.com/makandra/617366-how-to-configure-selenium-webdriver-to-not-automatically-close-alerts-or-other-browser-dialogs) you configure your webdriver to not automatically dismiss user prompts by setting the "unhandled prompt behavior" capability to [`ignore`](https://w3c.github.io/webdriver/#dfn-known-prompt-handling-approaches-table). Using "ignore", errors are raised like with the default behavior, but user prompts are kept open.
168
+
169
+ For example, the Chrome driver can be configured like this:
170
+ ```ruby
171
+ Capybara.register_driver(:selenium) do |app|
172
+ options = Selenium::WebDriver::Chrome::Options.new(
173
+ unhandled_prompt_behavior: 'ignore',
174
+ # ...
175
+ )
176
+ Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
177
+ end
178
+ ```
162
179
 
163
180
  ### Verify successful integration
164
181
 
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
15
15
  spec.metadata["source_code_uri"] = spec.homepage
16
16
 
17
17
  spec.metadata["bug_tracker_uri"] = "https://github.com/makandra/capybara-lockstep/issues"
18
- spec.metadata["changelog_uri"] = "https://github.com/makandra/capybara-lockstep/blob/master/CHANGELOG.md"
18
+ spec.metadata["changelog_uri"] = "https://github.com/makandra/capybara-lockstep/blob/main/CHANGELOG.md"
19
19
  spec.metadata["rubygems_mfa_required"] = 'true'
20
20
 
21
21
  # Specify which files should be added to the gem when it is released.
@@ -177,39 +177,47 @@ window.CapybaraLockstep = (function() {
177
177
  }
178
178
 
179
179
  function isRemoteScript(element) {
180
- if (element.tagName === 'SCRIPT') {
181
- let src = element.getAttribute('src')
182
- let type = element.getAttribute('type')
180
+ return element.matches('script[src]') && !hasDataSource(element)
181
+ }
183
182
 
184
- return src && (!type || /javascript/i.test(type))
185
- }
183
+ function isTrackableImage(element) {
184
+ return element.matches('img') &&
185
+ !element.complete &&
186
+ !hasDataSource(element) &&
187
+ element.getAttribute('loading') !== 'lazy'
186
188
  }
187
189
 
188
- function isRemoteImage(element) {
189
- if (element.tagName === 'IMG' && !element.complete) {
190
- let src = element.getAttribute('src')
191
- let srcSet = element.getAttribute('srcset')
190
+ function isTrackableIFrame(element) {
191
+ return element.matches('iframe') &&
192
+ !hasDataSource(element) &&
193
+ element.getAttribute('loading') !== 'lazy'
194
+ }
195
+
196
+ function hasDataSource(element) {
197
+ // <img> can have <img src> and <img srcset>
198
+ // <video> can have <video src> or <video><source src>
199
+ // <audio> can have <audio src> or <audio><source src>
200
+ return element.matches('[src*="data:"], [srcset*="data:"]') ||
201
+ !!element.querySelector('source [src*="data:"], source [srcset*="data:"]')
202
+ }
192
203
 
193
- let localSrcPattern = /^data:/
194
- let localSrcSetPattern = /(^|\s)data:/
204
+ function isTrackableMediaElement(element) {
205
+ return element.matches('audio, video') &&
206
+ element.readyState === 0 && // no metadata known
207
+ !hasDataSource(element) &&
208
+ element.getAttribute('preload') !== 'none'
209
+ }
195
210
 
196
- let hasLocalSrc = src && localSrcPattern.test(src)
197
- let hasLocalSrcSet = srcSet && localSrcSetPattern.test(srcSet)
211
+ function trackRemoteElement(element, condition, workTag) {
212
+ trackLoadingElement(element, condition, workTag, 'load', 'error')
198
213
 
199
- return (src && !hasLocalSrc) || (srcSet && !hasLocalSrcSet)
200
- }
201
214
  }
202
215
 
203
- function isRemoteInlineFrame(element) {
204
- if (element.tagName === 'IFRAME') {
205
- let src = element.getAttribute('src')
206
- let localSrcPattern = /^data:/
207
- let hasLocalSrc = src && localSrcPattern.test(src)
208
- return (src && !hasLocalSrc)
209
- }
216
+ function trackMediaElement(element, condition, workTag) {
217
+ trackLoadingElement(element, condition, workTag, 'loadedmetadata', 'error')
210
218
  }
211
219
 
212
- function trackRemoteElement(element, condition, workTag) {
220
+ function trackLoadingElement(element, condition, workTag, loadEvent, errorEvent) {
213
221
  if (!condition(element)) {
214
222
  return
215
223
  }
@@ -220,8 +228,8 @@ window.CapybaraLockstep = (function() {
220
228
 
221
229
  let doStop = function() {
222
230
  stopped = true
223
- element.removeEventListener('load', doStop)
224
- element.removeEventListener('error', doStop)
231
+ element.removeEventListener(loadEvent, doStop)
232
+ element.removeEventListener(errorEvent, doStop)
225
233
  stopWork(workTag)
226
234
  }
227
235
 
@@ -240,11 +248,11 @@ window.CapybaraLockstep = (function() {
240
248
  }
241
249
 
242
250
  let scheduleCheckCondition = function() {
243
- setTimeout(checkCondition, 200)
251
+ setTimeout(checkCondition, 150)
244
252
  }
245
253
 
246
- element.addEventListener('load', doStop)
247
- element.addEventListener('error', doStop)
254
+ element.addEventListener(loadEvent, doStop)
255
+ element.addEventListener(errorEvent, doStop)
248
256
 
249
257
  // We periodically check whether we still think the element will
250
258
  // produce a `load` or `error` event.
@@ -256,8 +264,9 @@ window.CapybaraLockstep = (function() {
256
264
  change.addedNodes.forEach(function(addedNode) {
257
265
  if (addedNode.nodeType === Node.ELEMENT_NODE) {
258
266
  trackRemoteElement(addedNode, isRemoteScript, 'Script')
259
- trackRemoteElement(addedNode, isRemoteImage, 'Image')
260
- trackRemoteElement(addedNode, isRemoteInlineFrame, 'Inline frame')
267
+ trackRemoteElement(addedNode, isTrackableImage, 'Image')
268
+ trackRemoteElement(addedNode, isTrackableIFrame, 'Inline frame')
269
+ trackMediaElement(addedNode, isTrackableMediaElement, 'Media element')
261
270
  }
262
271
  })
263
272
  })
@@ -1,5 +1,5 @@
1
1
  module Capybara
2
2
  module Lockstep
3
- VERSION = "2.1.0"
3
+ VERSION = "2.2.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: 2.1.0
4
+ version: 2.2.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: 2024-01-10 00:00:00.000000000 Z
11
+ date: 2024-02-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: capybara
@@ -107,7 +107,7 @@ metadata:
107
107
  homepage_uri: https://github.com/makandra/capybara-lockstep
108
108
  source_code_uri: https://github.com/makandra/capybara-lockstep
109
109
  bug_tracker_uri: https://github.com/makandra/capybara-lockstep/issues
110
- changelog_uri: https://github.com/makandra/capybara-lockstep/blob/master/CHANGELOG.md
110
+ changelog_uri: https://github.com/makandra/capybara-lockstep/blob/main/CHANGELOG.md
111
111
  rubygems_mfa_required: 'true'
112
112
  post_install_message:
113
113
  rdoc_options: []