capybara-lockstep 2.1.0 → 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +3 -3
- data/CHANGELOG.md +11 -0
- data/Gemfile.lock +15 -15
- data/README.md +28 -11
- data/capybara-lockstep.gemspec +1 -1
- data/lib/capybara-lockstep/capybara_ext.rb +29 -22
- data/lib/capybara-lockstep/helper.js +39 -30
- data/lib/capybara-lockstep/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8bad1a61632d37c5e705b886be4cb840a62923f73887c1443060fb00c6e69aa2
|
4
|
+
data.tar.gz: 9243b559659a33a00e6f803304284aa9cd47e5c2f110a11b47eb2e4e62441537
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b285d685609f913c92b4b8d5556d34d7d77e2fb6d80e5fac4cda189c81a2b81445fc65a5680726c2d21b16d0bcdcb1fcb35e3a6169b2febca3244eaa555b985
|
7
|
+
data.tar.gz: d598437ffb3eaf066d6cbcb747ac6c5445bed0a2c2cbc5113f42e0d947d4b71fc7bb4752ede4193cca989acafd5d126757c67d3454fbfc226014d7b42056b55f
|
data/.github/workflows/test.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,17 @@ All notable changes to this project will be documented in this file.
|
|
2
2
|
|
3
3
|
This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
4
4
|
|
5
|
+
# 2.2.1
|
6
|
+
|
7
|
+
- Fixed a bug that disabled most functionality for drivers with `browser: :remote`.
|
8
|
+
|
9
|
+
|
10
|
+
# 2.2.0
|
11
|
+
|
12
|
+
- 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.
|
13
|
+
- We now wait for `<script type="module">`.
|
14
|
+
- 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.
|
15
|
+
|
5
16
|
|
6
17
|
# 2.1.0
|
7
18
|
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
capybara-lockstep (2.1
|
4
|
+
capybara-lockstep (2.2.1)
|
5
5
|
activesupport (>= 4.2)
|
6
6
|
capybara (>= 3.0)
|
7
7
|
ruby2_keywords
|
@@ -31,7 +31,7 @@ GEM
|
|
31
31
|
childprocess (4.1.0)
|
32
32
|
concurrent-ruby (1.1.10)
|
33
33
|
daemons (1.4.1)
|
34
|
-
diff-lcs (1.
|
34
|
+
diff-lcs (1.5.1)
|
35
35
|
eventmachine (1.2.7)
|
36
36
|
gemika (0.8.1)
|
37
37
|
i18n (1.12.0)
|
@@ -58,21 +58,21 @@ GEM
|
|
58
58
|
rake (13.1.0)
|
59
59
|
regexp_parser (2.8.2)
|
60
60
|
rexml (3.2.5)
|
61
|
-
rspec (3.
|
62
|
-
rspec-core (~> 3.
|
63
|
-
rspec-expectations (~> 3.
|
64
|
-
rspec-mocks (~> 3.
|
65
|
-
rspec-core (3.
|
66
|
-
rspec-support (~> 3.
|
67
|
-
rspec-expectations (3.
|
61
|
+
rspec (3.13.0)
|
62
|
+
rspec-core (~> 3.13.0)
|
63
|
+
rspec-expectations (~> 3.13.0)
|
64
|
+
rspec-mocks (~> 3.13.0)
|
65
|
+
rspec-core (3.13.0)
|
66
|
+
rspec-support (~> 3.13.0)
|
67
|
+
rspec-expectations (3.13.1)
|
68
68
|
diff-lcs (>= 1.2.0, < 2.0)
|
69
|
-
rspec-support (~> 3.
|
70
|
-
rspec-mocks (3.
|
69
|
+
rspec-support (~> 3.13.0)
|
70
|
+
rspec-mocks (3.13.1)
|
71
71
|
diff-lcs (>= 1.2.0, < 2.0)
|
72
|
-
rspec-support (~> 3.
|
73
|
-
rspec-support (3.
|
74
|
-
rspec-wait (0.0.
|
75
|
-
rspec (>= 3
|
72
|
+
rspec-support (~> 3.13.0)
|
73
|
+
rspec-support (3.13.1)
|
74
|
+
rspec-wait (0.0.10)
|
75
|
+
rspec (>= 3.0)
|
76
76
|
ruby2_keywords (0.0.5)
|
77
77
|
rubyzip (2.3.2)
|
78
78
|
selenium-webdriver (4.1.0)
|
data/README.md
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
|
3
3
|
This Ruby gem synchronizes [Capybara](https://github.com/teamcapybara/capybara) commands with client-side JavaScript and AJAX requests. This greatly improves the stability of an end-to-end ("E2E") test suite, even if that suite has timing issues.
|
4
4
|
|
5
|
-
The next section
|
5
|
+
The next section explains why your test suite is flaky and how capybara-lockstep can help.\
|
6
6
|
If you don't care you may **skip to [installation instructions](#installation)**.
|
7
7
|
|
8
8
|
|
9
9
|
Why are tests flaky?
|
10
10
|
--------------------
|
11
11
|
|
12
|
-
A naively written E2E test will have [race conditions](https://makandracards.com/makandra/47336-fixing-flaky-integration-tests) between the test script and the controlled browser. How often these timing issues will
|
12
|
+
A naively written E2E test will have [race conditions](https://makandracards.com/makandra/47336-fixing-flaky-integration-tests) between the test script and the controlled browser. How often these timing issues will cause your tests to fail depends on luck and your machine's performance. You may not see these issues for years until a colleague runs your suite on their new laptop.
|
13
13
|
|
14
14
|
Here is a typical example for a test that will fail with unlucky timing:
|
15
15
|
|
@@ -26,10 +26,10 @@ end
|
|
26
26
|
|
27
27
|
This test has four timing issues that may cause it to fail:
|
28
28
|
|
29
|
-
1. We click on the *New tweet* button, but the
|
30
|
-
2. We start filling in the form, but it
|
29
|
+
1. We click on the *New tweet* button, but the JS event handler to open the tweet form hasn't been registered yet.
|
30
|
+
2. We start filling in the form, but it hasn't been loaded yet.
|
31
31
|
3. After sending the tweet we immediately navigate away, killing the form submission request that is still in flight. Hence the tweet will never appear in the next step.
|
32
|
-
4. We look for the new tweet, but the timeline
|
32
|
+
4. We look for the new tweet, but the timeline hasn't been loaded yet.
|
33
33
|
|
34
34
|
[Capybara will retry](https://github.com/teamcapybara/capybara#asynchronous-javascript-ajax-and-friends) individual commands or expectations when they fail.\
|
35
35
|
However, only issues **2** and **4** can be healed by retrying.
|
@@ -71,9 +71,10 @@ 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
|
-
-
|
74
|
+
- wait 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
|
+
- wait 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
|
-
In summary Capybara can no longer observe or interact with the page while HTTP requests are in flight.
|
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.
|
78
79
|
|
79
80
|
|
@@ -83,7 +84,6 @@ Async work not synchronized by capybara-lockstep includes:
|
|
83
84
|
|
84
85
|
- Animations
|
85
86
|
- Websocket connections
|
86
|
-
- Media elements (`<video>`, `<audio>`)
|
87
87
|
- Service workers
|
88
88
|
- Work scheduled via `setTimeout()` or `setInterval()`.
|
89
89
|
|
@@ -99,13 +99,14 @@ Check if your application satisfies all requirements for capybara-lockstep:
|
|
99
99
|
|
100
100
|
- Capybara 2.0 or higher.
|
101
101
|
- Your Capybara driver must use [selenium-webdriver](https://rubygems.org/gems/selenium-webdriver/) 3.0 or higher. capybara-lockstep deactivates itself for any other driver.
|
102
|
+
There is a [fork](https://github.com/Skalar/capybara-lockstep/tree/playwright-driver) with support for [capybara-playwright-driver](https://github.com/YusukeIwaki/capybara-playwright-driver).
|
102
103
|
- This gem was only tested with a Selenium-controlled Chrome browser. [Chrome in headless mode](https://makandracards.com/makandra/492109-running-capybara-tests-in-headless-chrome) is recommended, but not required.
|
103
104
|
- This gem was only tested with Rails, but there's no Rails dependency.
|
104
105
|
|
105
106
|
|
106
107
|
### Installing the Ruby gem
|
107
108
|
|
108
|
-
Assuming that you're using Rails
|
109
|
+
Assuming that you're using Rails, add this to your application's `Gemfile`:
|
109
110
|
|
110
111
|
```ruby
|
111
112
|
group :test do
|
@@ -141,9 +142,9 @@ Ideally the snippet should be included in the `<head>` before any other `<script
|
|
141
142
|
|
142
143
|
### Including the middleware (optional)
|
143
144
|
|
144
|
-
This gem provides
|
145
|
+
This gem provides Rack middleware to block Capybara while your Rails (or Rack) backend is busy.
|
145
146
|
|
146
|
-
Using the middleware is optional, as the [JavaScript snippet](#including-the-javascript-snippet-required) already for
|
147
|
+
Using the middleware is optional, as the [JavaScript snippet](#including-the-javascript-snippet-required) already waits for asynchronous work on the client. However, using the middleware covers some additional edge cases. For example, the middleware detects requests that were aborted on the frontend, but are still being processed by the backend.
|
147
148
|
|
148
149
|
To include the middleware in a Rails application, add the following line to `config/environments/test.rb`:
|
149
150
|
|
@@ -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
|
|
data/capybara-lockstep.gemspec
CHANGED
@@ -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/
|
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.
|
@@ -3,36 +3,46 @@ require 'ruby2_keywords'
|
|
3
3
|
module Capybara
|
4
4
|
module Lockstep
|
5
5
|
module SynchronizeMacros
|
6
|
+
def self.extended(by)
|
7
|
+
by.instance_eval do
|
8
|
+
prepend(@synchronize_before_module = Module.new)
|
9
|
+
prepend(@synchronize_after_module = Module.new)
|
10
|
+
prepend(@unsynchronize_after_module = Module.new)
|
11
|
+
end
|
12
|
+
end
|
6
13
|
|
7
14
|
def synchronize_before(meth, lazy:)
|
8
|
-
|
15
|
+
@synchronize_before_module.module_eval do
|
9
16
|
define_method meth do |*args, &block|
|
10
|
-
|
17
|
+
@synchronize_before_count ||= 0
|
18
|
+
@synchronize_before_count += 1
|
19
|
+
Lockstep.auto_synchronize(lazy: lazy, log: "Synchronizing before ##{meth}") if @synchronize_before_count == 1
|
11
20
|
super(*args, &block)
|
21
|
+
ensure
|
22
|
+
@synchronize_before_count -= 1
|
12
23
|
end
|
13
24
|
|
14
25
|
ruby2_keywords meth
|
15
26
|
end
|
16
|
-
|
17
|
-
prepend(mod)
|
18
27
|
end
|
19
28
|
|
20
29
|
def synchronize_after(meth)
|
21
|
-
|
30
|
+
@synchronize_after_module.module_eval do
|
22
31
|
define_method meth do |*args, &block|
|
32
|
+
@synchronize_after_count ||= 0
|
33
|
+
@synchronize_after_count += 1
|
23
34
|
super(*args, &block)
|
24
35
|
ensure
|
25
|
-
Lockstep.auto_synchronize
|
36
|
+
Lockstep.auto_synchronize(log: "Synchronizing after ##{meth}") if @synchronize_after_count == 1
|
37
|
+
@synchronize_after_count -= 1
|
26
38
|
end
|
27
39
|
|
28
40
|
ruby2_keywords meth
|
29
41
|
end
|
30
|
-
|
31
|
-
prepend(mod)
|
32
42
|
end
|
33
43
|
|
34
44
|
def unsynchronize_after(meth)
|
35
|
-
|
45
|
+
@unsynchronize_after_module.module_eval do
|
36
46
|
define_method meth do |*args, &block|
|
37
47
|
super(*args, &block)
|
38
48
|
ensure
|
@@ -41,10 +51,7 @@ module Capybara
|
|
41
51
|
|
42
52
|
ruby2_keywords meth
|
43
53
|
end
|
44
|
-
|
45
|
-
prepend(mod)
|
46
54
|
end
|
47
|
-
|
48
55
|
end
|
49
56
|
end
|
50
57
|
end
|
@@ -158,23 +165,23 @@ Capybara::Session.class_eval do
|
|
158
165
|
synchronize_around_script_method :evaluate_async_script
|
159
166
|
end
|
160
167
|
|
161
|
-
# Capybara 3
|
162
|
-
#
|
163
|
-
|
168
|
+
# In Capybara 3 there are the specialized classes for nodes for most browers.
|
169
|
+
# We need to patch relevant methods on all of these.
|
170
|
+
driver_specific_node_classes = [
|
164
171
|
(Capybara::Selenium::ChromeNode if defined?(Capybara::Selenium::ChromeNode)),
|
165
172
|
(Capybara::Selenium::FirefoxNode if defined?(Capybara::Selenium::FirefoxNode)),
|
166
173
|
(Capybara::Selenium::SafariNode if defined?(Capybara::Selenium::SafariNode)),
|
167
174
|
(Capybara::Selenium::EdgeNode if defined?(Capybara::Selenium::EdgeNode)),
|
168
175
|
(Capybara::Selenium::IENode if defined?(Capybara::Selenium::IENode)),
|
169
|
-
].compact
|
176
|
+
].compact.freeze
|
170
177
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
178
|
+
# For other browsers (like the :remote browser) we instead get a generic node class.
|
179
|
+
# This is also the case for Capybara 2.
|
180
|
+
generic_node_classes = [
|
181
|
+
Capybara::Selenium::Node,
|
182
|
+
].freeze
|
176
183
|
|
177
|
-
|
184
|
+
[*driver_specific_node_classes, *generic_node_classes].each do |node_class|
|
178
185
|
node_class.class_eval do
|
179
186
|
extend Capybara::Lockstep::SynchronizeMacros
|
180
187
|
|
@@ -177,39 +177,47 @@ window.CapybaraLockstep = (function() {
|
|
177
177
|
}
|
178
178
|
|
179
179
|
function isRemoteScript(element) {
|
180
|
-
|
181
|
-
|
182
|
-
let type = element.getAttribute('type')
|
180
|
+
return element.matches('script[src]') && !hasDataSource(element)
|
181
|
+
}
|
183
182
|
|
184
|
-
|
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
|
189
|
-
|
190
|
-
|
191
|
-
|
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
|
-
|
194
|
-
|
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
|
-
|
197
|
-
|
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
|
204
|
-
|
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
|
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(
|
224
|
-
element.removeEventListener(
|
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,
|
251
|
+
setTimeout(checkCondition, 150)
|
244
252
|
}
|
245
253
|
|
246
|
-
element.addEventListener(
|
247
|
-
element.addEventListener(
|
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,
|
260
|
-
trackRemoteElement(addedNode,
|
267
|
+
trackRemoteElement(addedNode, isTrackableImage, 'Image')
|
268
|
+
trackRemoteElement(addedNode, isTrackableIFrame, 'Inline frame')
|
269
|
+
trackMediaElement(addedNode, isTrackableMediaElement, 'Media element')
|
261
270
|
}
|
262
271
|
})
|
263
272
|
})
|
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
|
4
|
+
version: 2.2.1
|
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-
|
11
|
+
date: 2024-06-19 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/
|
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: []
|
@@ -124,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
124
|
- !ruby/object:Gem::Version
|
125
125
|
version: '0'
|
126
126
|
requirements: []
|
127
|
-
rubygems_version: 3.4.
|
127
|
+
rubygems_version: 3.4.1
|
128
128
|
signing_key:
|
129
129
|
specification_version: 4
|
130
130
|
summary: Synchronize Capybara commands with client-side JavaScript and AJAX requests
|