capybara-lockstep 2.2.2 → 2.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 +4 -4
- data/.github/workflows/test.yml +15 -2
- data/.gitignore +2 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +20 -0
- data/Gemfile +9 -1
- data/Gemfile.lock +24 -6
- data/README.md +42 -7
- data/Rakefile +1 -0
- data/capybara-lockstep.gemspec +0 -1
- data/lib/capybara-lockstep/capybara_ext.rb +5 -1
- data/lib/capybara-lockstep/client/cuprite.rb +39 -0
- data/lib/capybara-lockstep/client/selenium.rb +41 -0
- data/lib/capybara-lockstep/client.rb +5 -32
- data/lib/capybara-lockstep/configuration.rb +2 -2
- data/lib/capybara-lockstep/errors.rb +1 -0
- data/lib/capybara-lockstep/helper.rb +2 -2
- data/lib/capybara-lockstep/lockstep.rb +21 -2
- data/lib/capybara-lockstep/page_access.rb +15 -2
- data/lib/capybara-lockstep/version.rb +1 -1
- data/lib/capybara-lockstep.rb +20 -1
- data/media/logo.dark.shapes.svg +169 -0
- data/media/logo.dark.text.svg +107 -0
- data/media/logo.light.shapes.svg +168 -0
- data/media/logo.light.text.svg +106 -0
- data/media/makandra-with-bottom-margin.dark.svg +180 -0
- data/media/makandra-with-bottom-margin.light.svg +180 -0
- data/tasks/spec_tasks.rb +22 -0
- metadata +12 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a4d067d73940b41b147af2e4acfba7ec42258be32c9a953178a6566b2ea02d47
|
4
|
+
data.tar.gz: b5acd048164fa863786b6b3f60695877d7052f87a14207f9d4e851c5334cca13
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3181f8725182d43722551b220759f705742b0d2e88bea4b244fae812a3ab2390465adb62b07c8a3e59f9c3959a635ca286825d4c6dc5eae4db35101e0635221
|
7
|
+
data.tar.gz: 64dd439ed268526b97543ccab8f64396a3ea83a6cd4eb39ba9e55b04155c69d1029afe5761bba27c7fe04bc8f62e59ff157527ce02f1b492aae432f70e2e071a
|
data/.github/workflows/test.yml
CHANGED
@@ -12,7 +12,7 @@ on:
|
|
12
12
|
- main
|
13
13
|
jobs:
|
14
14
|
test:
|
15
|
-
runs-on: ubuntu-
|
15
|
+
runs-on: ubuntu-24.04
|
16
16
|
timeout-minutes: 3
|
17
17
|
strategy:
|
18
18
|
fail-fast: false
|
@@ -20,10 +20,22 @@ jobs:
|
|
20
20
|
include:
|
21
21
|
- ruby: 2.7.2
|
22
22
|
gemfile: Gemfile
|
23
|
+
capybara_driver: selenium
|
24
|
+
- ruby: 2.7.2
|
25
|
+
gemfile: Gemfile
|
26
|
+
capybara_driver: cuprite
|
23
27
|
- ruby: 3.2.0
|
24
28
|
gemfile: Gemfile
|
29
|
+
capybara_driver: selenium
|
30
|
+
- ruby: 3.4.1
|
31
|
+
gemfile: Gemfile
|
32
|
+
capybara_driver: selenium
|
33
|
+
- ruby: 3.4.1
|
34
|
+
gemfile: Gemfile
|
35
|
+
capybara_driver: cuprite
|
25
36
|
env:
|
26
37
|
BUNDLE_GEMFILE: "${{ matrix.gemfile }}"
|
38
|
+
CAPYBARA_DRIVER: "${{ matrix.capybara_driver }}"
|
27
39
|
steps:
|
28
40
|
- uses: actions/checkout@v3
|
29
41
|
- name: Install Chrome
|
@@ -32,13 +44,14 @@ jobs:
|
|
32
44
|
run: chrome --version
|
33
45
|
- name: Install ChromeDriver
|
34
46
|
uses: nanasess/setup-chromedriver@master
|
47
|
+
if: "${{ matrix.capybara_driver == 'selenium' }}"
|
35
48
|
- name: Install ruby
|
36
49
|
uses: ruby/setup-ruby@v1
|
37
50
|
with:
|
38
51
|
ruby-version: "${{ matrix.ruby }}"
|
39
52
|
- name: Bundle
|
40
53
|
run: |
|
41
|
-
gem install bundler:2.
|
54
|
+
gem install bundler:2.3.1
|
42
55
|
bundle install --no-deployment
|
43
56
|
- name: Run tests
|
44
57
|
uses: nick-invision/retry@v2
|
data/.gitignore
CHANGED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.
|
1
|
+
3.4.1
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,26 @@ 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
|
+
# Unreleased changes
|
6
|
+
|
7
|
+
## Breaking changes
|
8
|
+
|
9
|
+
-
|
10
|
+
|
11
|
+
## Compatible changes
|
12
|
+
|
13
|
+
-
|
14
|
+
|
15
|
+
# 2.3.0
|
16
|
+
|
17
|
+
- Added support for cuprite as the capybara driver
|
18
|
+
|
19
|
+
# 2.2.3
|
20
|
+
|
21
|
+
- Requiring the gem no longer force-loads ActionView (#22)
|
22
|
+
- Calling `visit(nil)` visits the root route instead of crashing (#21)
|
23
|
+
- Tested against Ruby 3.4 (in addition to 2.7 and 3.2)
|
24
|
+
|
5
25
|
|
6
26
|
# 2.2.2
|
7
27
|
|
data/Gemfile
CHANGED
@@ -9,7 +9,7 @@ gem 'activesupport', '~> 6.0'
|
|
9
9
|
gem "rake", "~> 13.0"
|
10
10
|
|
11
11
|
gem "rspec", "~> 3.0"
|
12
|
-
gem "rspec-wait"
|
12
|
+
gem "rspec-wait", '~> 0.0.10' # we test with Ruby 2.x, and 1.0.0 requires 3.x
|
13
13
|
gem 'sinatra'
|
14
14
|
gem 'thin' # ruby 3 does not include a webserver
|
15
15
|
gem 'puma'
|
@@ -17,3 +17,11 @@ gem 'byebug'
|
|
17
17
|
gem 'gemika', '>= 0.8.1'
|
18
18
|
gem 'capybara', '>= 3'
|
19
19
|
gem 'selenium-webdriver', '>= 4'
|
20
|
+
gem 'cuprite'
|
21
|
+
|
22
|
+
# The following gems were previously "default gems" (always available) and are now
|
23
|
+
# "bundled gems" (need to be explicitly required). Not all gems in our Gemfile.lock (dev only) do that yet.
|
24
|
+
# To avoid splitting the Gemfile.lock by Ruby version (gemika test setup), we instead require those
|
25
|
+
# indirect dependencies ourselves.
|
26
|
+
gem 'base64' # needed by selenium-webdriver (and potentially others)
|
27
|
+
gem 'bigdecimal' # needed by activesupport (and potentially others)
|
data/Gemfile.lock
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
capybara-lockstep (2.
|
4
|
+
capybara-lockstep (2.3.0)
|
5
5
|
activesupport (>= 4.2)
|
6
6
|
capybara (>= 3.0)
|
7
7
|
ruby2_keywords
|
8
|
-
selenium-webdriver (>= 4.0)
|
9
8
|
|
10
9
|
GEM
|
11
10
|
remote: https://rubygems.org/
|
@@ -18,6 +17,8 @@ GEM
|
|
18
17
|
zeitwerk (~> 2.3)
|
19
18
|
addressable (2.8.5)
|
20
19
|
public_suffix (>= 2.0.2, < 6.0)
|
20
|
+
base64 (0.2.0)
|
21
|
+
bigdecimal (3.1.8)
|
21
22
|
byebug (11.1.3)
|
22
23
|
capybara (3.39.2)
|
23
24
|
addressable
|
@@ -30,9 +31,18 @@ GEM
|
|
30
31
|
xpath (~> 3.2)
|
31
32
|
childprocess (4.1.0)
|
32
33
|
concurrent-ruby (1.1.10)
|
34
|
+
cuprite (0.17)
|
35
|
+
capybara (~> 3.0)
|
36
|
+
ferrum (~> 0.17.0)
|
33
37
|
daemons (1.4.1)
|
34
38
|
diff-lcs (1.5.1)
|
35
39
|
eventmachine (1.2.7)
|
40
|
+
ferrum (0.17.1)
|
41
|
+
addressable (~> 2.5)
|
42
|
+
base64 (~> 0.2)
|
43
|
+
concurrent-ruby (~> 1.1)
|
44
|
+
webrick (~> 1.7)
|
45
|
+
websocket-driver (~> 0.7)
|
36
46
|
gemika (0.8.1)
|
37
47
|
i18n (1.12.0)
|
38
48
|
concurrent-ruby (~> 1.0)
|
@@ -71,8 +81,8 @@ GEM
|
|
71
81
|
diff-lcs (>= 1.2.0, < 2.0)
|
72
82
|
rspec-support (~> 3.13.0)
|
73
83
|
rspec-support (3.13.1)
|
74
|
-
rspec-wait (
|
75
|
-
rspec (>= 3.
|
84
|
+
rspec-wait (0.0.10)
|
85
|
+
rspec (>= 3.0)
|
76
86
|
ruby2_keywords (0.0.5)
|
77
87
|
rubyzip (2.3.2)
|
78
88
|
selenium-webdriver (4.1.0)
|
@@ -91,6 +101,11 @@ GEM
|
|
91
101
|
tilt (2.3.0)
|
92
102
|
tzinfo (2.0.5)
|
93
103
|
concurrent-ruby (~> 1.0)
|
104
|
+
webrick (1.9.1)
|
105
|
+
websocket-driver (0.8.0)
|
106
|
+
base64
|
107
|
+
websocket-extensions (>= 0.1.0)
|
108
|
+
websocket-extensions (0.1.5)
|
94
109
|
xpath (3.2.0)
|
95
110
|
nokogiri (~> 1.8)
|
96
111
|
zeitwerk (2.6.0)
|
@@ -100,17 +115,20 @@ PLATFORMS
|
|
100
115
|
|
101
116
|
DEPENDENCIES
|
102
117
|
activesupport (~> 6.0)
|
118
|
+
base64
|
119
|
+
bigdecimal
|
103
120
|
byebug
|
104
121
|
capybara (>= 3)
|
105
122
|
capybara-lockstep!
|
123
|
+
cuprite
|
106
124
|
gemika (>= 0.8.1)
|
107
125
|
puma
|
108
126
|
rake (~> 13.0)
|
109
127
|
rspec (~> 3.0)
|
110
|
-
rspec-wait
|
128
|
+
rspec-wait (~> 0.0.10)
|
111
129
|
selenium-webdriver (>= 4)
|
112
130
|
sinatra
|
113
131
|
thin
|
114
132
|
|
115
133
|
BUNDLED WITH
|
116
|
-
2.
|
134
|
+
2.3.1
|
data/README.md
CHANGED
@@ -1,4 +1,24 @@
|
|
1
|
-
|
1
|
+
<p>
|
2
|
+
<a href="https://makandra.de/">
|
3
|
+
<picture>
|
4
|
+
<source media="(prefers-color-scheme: light)" srcset="media/makandra-with-bottom-margin.light.svg">
|
5
|
+
<source media="(prefers-color-scheme: dark)" srcset="media/makandra-with-bottom-margin.dark.svg">
|
6
|
+
<img align="right" width="25%" alt="makandra" src="media/makandra-with-bottom-margin.light.svg">
|
7
|
+
</picture>
|
8
|
+
</a>
|
9
|
+
|
10
|
+
<picture>
|
11
|
+
<source media="(prefers-color-scheme: light)" srcset="media/logo.light.shapes.svg">
|
12
|
+
<source media="(prefers-color-scheme: dark)" srcset="media/logo.dark.shapes.svg">
|
13
|
+
<img width="410" alt="capybara-lockstep" role="heading" aria-level="1" src="media/logo.light.shapes.svg">
|
14
|
+
</picture>
|
15
|
+
</p>
|
16
|
+
|
17
|
+
<p>
|
18
|
+
<a href="https://github.com/makandra/capybara-lockstep/actions">
|
19
|
+
<img alt="Tests" src="https://github.com/makandra/capybara-lockstep/actions/workflows/test.yml/badge.svg">
|
20
|
+
</a>
|
21
|
+
</p>
|
2
22
|
|
3
23
|
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
24
|
|
@@ -9,6 +29,8 @@ If you don't care you may **skip to [installation instructions](#installation)**
|
|
9
29
|
Why are tests flaky?
|
10
30
|
--------------------
|
11
31
|
|
32
|
+
> Watch as a video: [▶️ Fixing Flaky E2E Tests](https://www.youtube.com/watch?v=LaCwiFDm2Vs)
|
33
|
+
|
12
34
|
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
35
|
|
14
36
|
Here is a typical example for a test that will fail with unlucky timing:
|
@@ -98,9 +120,9 @@ Installation
|
|
98
120
|
Check if your application satisfies all requirements for capybara-lockstep:
|
99
121
|
|
100
122
|
- Capybara 2.0 or higher.
|
101
|
-
- Your Capybara driver must use [selenium-webdriver](https://rubygems.org/gems/selenium-webdriver/) 3.0 or
|
123
|
+
- Your Capybara driver must use [selenium-webdriver](https://rubygems.org/gems/selenium-webdriver/) (>3.0) or [cuprite](https://github.com/rubycdp/cuprite). capybara-lockstep deactivates itself for any other driver.
|
102
124
|
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).
|
103
|
-
- This gem was only tested with a
|
125
|
+
- This gem was only tested with a Chrome browser. [Chrome in headless mode](https://makandracards.com/makandra/492109-running-capybara-tests-in-headless-chrome) is recommended, but not required.
|
104
126
|
- This gem was only tested with Rails, but there's no Rails dependency.
|
105
127
|
|
106
128
|
|
@@ -438,10 +460,20 @@ Pull requests are welcome on GitHub at <https://github.com/makandra/capybara-loc
|
|
438
460
|
|
439
461
|
After checking out the repo, run `bin/setup` to install dependencies.
|
440
462
|
|
441
|
-
|
463
|
+
For running tests see [Running tests locally](#running-tests-locally) below
|
442
464
|
|
443
465
|
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
444
466
|
|
467
|
+
### Running tests locally
|
468
|
+
|
469
|
+
capybara-lockstep supports both the `selenium-webdriver` and `cuprite` as drivers for capybara.
|
470
|
+
|
471
|
+
To run all the tests for all supported drivers, run `rake spec:all`.
|
472
|
+
|
473
|
+
To run all the tests for a specific driver, run `rake spec:selenium` or `rake spec:cuprite`.
|
474
|
+
|
475
|
+
The driver can also be specified by setting `CAPYBARA_DRIVER`, so if you want to run a single test for cuprite you can run `CAPYBARA_DRIVER=cuprite bundle exec rspec "spec/features/spec_spec.rb[1]"`.
|
476
|
+
|
445
477
|
### Manually testing a change
|
446
478
|
|
447
479
|
To test an unrelased change with a test suite, we recommend to temporarily link the local repository from your test suites's `Gemfile`:
|
@@ -454,9 +486,12 @@ As an alternative you may also install this gem onto your local machine by runni
|
|
454
486
|
|
455
487
|
### Releasing a new version
|
456
488
|
|
457
|
-
- Update the version number in `version.rb
|
458
|
-
|
459
|
-
|
489
|
+
- Update the version number in `version.rb`. Use Semantic Versioning.
|
490
|
+
- Run `bundle install` so our `Gemfile.lock` (for development) gets the new version.
|
491
|
+
- Update `CHANGELOG.md`.
|
492
|
+
- Commit and push your changes.
|
493
|
+
- 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).
|
494
|
+
- If RubyGems publishing seems to freeze, try entering your OTP code.
|
460
495
|
|
461
496
|
|
462
497
|
## License
|
data/Rakefile
CHANGED
data/capybara-lockstep.gemspec
CHANGED
@@ -29,7 +29,6 @@ Gem::Specification.new do |spec|
|
|
29
29
|
|
30
30
|
# Uncomment to register a new dependency of your gem
|
31
31
|
spec.add_dependency "capybara", ">= 3.0"
|
32
|
-
spec.add_dependency "selenium-webdriver", ">= 4.0"
|
33
32
|
spec.add_dependency "activesupport", ">= 4.2"
|
34
33
|
spec.add_dependency "ruby2_keywords"
|
35
34
|
|
@@ -83,7 +83,10 @@ module Capybara
|
|
83
83
|
module Lockstep
|
84
84
|
module VisitWithWaiting
|
85
85
|
def visit(*args, &block)
|
86
|
-
|
86
|
+
# For some reason, in Capybara proper, visit(nil) navigates to the root route.
|
87
|
+
# We mimic this behavior for (1) parity and (2) to not crash when we inspect the URL below.
|
88
|
+
url = args[0].presence || '/'
|
89
|
+
|
87
90
|
# Some of our apps have a Cucumber step that changes drivers mid-scenario.
|
88
91
|
# It works by creating a new Capybara session and re-visits the URL from the
|
89
92
|
# previous session. If this happens before a URL is ever loaded,
|
@@ -173,6 +176,7 @@ driver_specific_node_classes = [
|
|
173
176
|
(Capybara::Selenium::SafariNode if defined?(Capybara::Selenium::SafariNode)),
|
174
177
|
(Capybara::Selenium::EdgeNode if defined?(Capybara::Selenium::EdgeNode)),
|
175
178
|
(Capybara::Selenium::IENode if defined?(Capybara::Selenium::IENode)),
|
179
|
+
(Capybara::Cuprite::Node if defined?(Capybara::Cuprite::Node)),
|
176
180
|
].compact.freeze
|
177
181
|
|
178
182
|
# For other browsers (like the :remote browser) we instead get a generic node class.
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Capybara
|
2
|
+
module Lockstep
|
3
|
+
class Client::Cuprite < Client
|
4
|
+
|
5
|
+
def with_synchronization_error_handling
|
6
|
+
yield
|
7
|
+
|
8
|
+
rescue ::Ferrum::ScriptTimeoutError
|
9
|
+
timeout_message = "Could not synchronize client within #{timeout} seconds"
|
10
|
+
log timeout_message
|
11
|
+
if timeout_with == :error
|
12
|
+
raise Timeout, timeout_message
|
13
|
+
else
|
14
|
+
# Don't raise an error, this may happen if the server is slow to respond.
|
15
|
+
# We will retry on the next Capybara synchronize call.
|
16
|
+
end
|
17
|
+
|
18
|
+
rescue ::Ferrum::JavaScriptError => e
|
19
|
+
if e.message.include?('unload')
|
20
|
+
log ERROR_NAVIGATED_AWAY
|
21
|
+
else
|
22
|
+
unhandled_synchronize_error(e)
|
23
|
+
end
|
24
|
+
|
25
|
+
rescue ::Ferrum::BrowserError => e
|
26
|
+
if e.message.include?('Session with given id not found.')
|
27
|
+
log ERROR_WINDOW_CLOSED
|
28
|
+
# Don't raise an error, this will happen in an innocent test where a click closes a window.
|
29
|
+
# We will retry on the next Capybara synchronize call.
|
30
|
+
else
|
31
|
+
unhandled_synchronize_error(e)
|
32
|
+
end
|
33
|
+
rescue StandardError => e
|
34
|
+
unhandled_synchronize_error(e)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Capybara
|
2
|
+
module Lockstep
|
3
|
+
class Client::Selenium < Client
|
4
|
+
|
5
|
+
def with_synchronization_error_handling
|
6
|
+
yield
|
7
|
+
rescue ::Selenium::WebDriver::Error::ScriptTimeoutError
|
8
|
+
timeout_message = "Could not synchronize client within #{timeout} seconds"
|
9
|
+
log timeout_message
|
10
|
+
if timeout_with == :error
|
11
|
+
raise Timeout, timeout_message
|
12
|
+
else
|
13
|
+
# Don't raise an error, this may happen if the server is slow to respond.
|
14
|
+
# We will retry on the next Capybara synchronize call.
|
15
|
+
end
|
16
|
+
rescue ::Selenium::WebDriver::Error::UnexpectedAlertOpenError
|
17
|
+
log ERROR_ALERT_OPEN
|
18
|
+
# Don't raise an error, this will happen in an innocent test where a click opens an alert.
|
19
|
+
# We will retry on the next Capybara synchronize call.
|
20
|
+
rescue ::Selenium::WebDriver::Error::NoSuchWindowError
|
21
|
+
log ERROR_WINDOW_CLOSED
|
22
|
+
# Don't raise an error, this will happen in an innocent test where a click closes a window.
|
23
|
+
# We will retry on the next Capybara synchronize call.
|
24
|
+
rescue ::Selenium::WebDriver::Error::JavascriptError => e
|
25
|
+
# When the URL changes while a script is running, my current selenium-webdriver
|
26
|
+
# raises a Selenium::WebDriver::Error::JavascriptError with the message:
|
27
|
+
# "javascript error: document unloaded while waiting for result".
|
28
|
+
# We will retry on the next Capybara synchronize call, by then we should see
|
29
|
+
# the new page.
|
30
|
+
if e.message.include?('unload')
|
31
|
+
log ERROR_NAVIGATED_AWAY
|
32
|
+
else
|
33
|
+
unhandled_synchronize_error(e)
|
34
|
+
end
|
35
|
+
rescue StandardError => e
|
36
|
+
unhandled_synchronize_error(e)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -48,7 +48,7 @@ module Capybara
|
|
48
48
|
|
49
49
|
start_time = Util.current_seconds
|
50
50
|
|
51
|
-
|
51
|
+
with_synchronization_error_handling do
|
52
52
|
Util.with_max_wait_time(timeout) do
|
53
53
|
message_from_js = evaluate_async_script(<<~JS)
|
54
54
|
let done = arguments[0]
|
@@ -84,41 +84,15 @@ module Capybara
|
|
84
84
|
self.synchronized = true
|
85
85
|
end
|
86
86
|
end
|
87
|
-
rescue ::Selenium::WebDriver::Error::ScriptTimeoutError
|
88
|
-
timeout_message = "Could not synchronize client within #{timeout} seconds"
|
89
|
-
log timeout_message
|
90
|
-
if timeout_with == :error
|
91
|
-
raise Timeout, timeout_message
|
92
|
-
else
|
93
|
-
# Don't raise an error, this may happen if the server is slow to respond.
|
94
|
-
# We will retry on the next Capybara synchronize call.
|
95
|
-
end
|
96
|
-
rescue ::Selenium::WebDriver::Error::UnexpectedAlertOpenError
|
97
|
-
log ERROR_ALERT_OPEN
|
98
|
-
# Don't raise an error, this will happen in an innocent test where a click opens an alert.
|
99
|
-
# We will retry on the next Capybara synchronize call.
|
100
|
-
rescue ::Selenium::WebDriver::Error::NoSuchWindowError
|
101
|
-
log ERROR_WINDOW_CLOSED
|
102
|
-
# Don't raise an error, this will happen in an innocent test where a click closes a window.
|
103
|
-
# We will retry on the next Capybara synchronize call.
|
104
|
-
rescue ::Selenium::WebDriver::Error::JavascriptError => e
|
105
|
-
# When the URL changes while a script is running, my current selenium-webdriver
|
106
|
-
# raises a Selenium::WebDriver::Error::JavascriptError with the message:
|
107
|
-
# "javascript error: document unloaded while waiting for result".
|
108
|
-
# We will retry on the next Capybara synchronize call, by then we should see
|
109
|
-
# the new page.
|
110
|
-
if e.message.include?('unload')
|
111
|
-
log ERROR_NAVIGATED_AWAY
|
112
|
-
else
|
113
|
-
unhandled_synchronize_error(e)
|
114
|
-
end
|
115
|
-
rescue StandardError => e
|
116
|
-
unhandled_synchronize_error(e)
|
117
87
|
end
|
118
88
|
end
|
119
89
|
|
120
90
|
private
|
121
91
|
|
92
|
+
def with_synchronization_error_handling
|
93
|
+
raise NoMethodError, "Implement in a driver specific subclass"
|
94
|
+
end
|
95
|
+
|
122
96
|
def unhandled_synchronize_error(e)
|
123
97
|
Lockstep.log "#{e.class.name} while synchronizing: #{e.message}"
|
124
98
|
raise e
|
@@ -135,4 +109,3 @@ module Capybara
|
|
135
109
|
end
|
136
110
|
end
|
137
111
|
end
|
138
|
-
|
@@ -42,7 +42,7 @@ module Capybara
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def mode
|
45
|
-
if
|
45
|
+
if supported_driver?
|
46
46
|
@mode.nil? ? :auto : @mode
|
47
47
|
else
|
48
48
|
:off
|
@@ -106,7 +106,7 @@ module Capybara
|
|
106
106
|
end
|
107
107
|
|
108
108
|
def send_config_to_browser(js)
|
109
|
-
return unless
|
109
|
+
return unless supported_driver?
|
110
110
|
|
111
111
|
begin
|
112
112
|
Util.with_max_wait_time(2) do
|
@@ -9,6 +9,8 @@ module Capybara
|
|
9
9
|
alias synchronizing? synchronizing
|
10
10
|
|
11
11
|
def unsynchronize
|
12
|
+
return if mode == :off
|
13
|
+
|
12
14
|
client.synchronized = false
|
13
15
|
end
|
14
16
|
|
@@ -28,7 +30,7 @@ module Capybara
|
|
28
30
|
|
29
31
|
# The { lazy } option is a performance optimization that will prevent capybara-lockstep
|
30
32
|
# from synchronizing multiple times in expressions like `page.find('.foo').find('.bar')`.
|
31
|
-
# The { lazy } option has nothing
|
33
|
+
# The { lazy } option has nothing to do with :auto mode.
|
32
34
|
#
|
33
35
|
# With { lazy: true } we only synchronize when the Ruby-side thinks we're out of sync.
|
34
36
|
# This saves us an expensive execute_script() roundtrip that goes to the browser and back.
|
@@ -69,7 +71,24 @@ module Capybara
|
|
69
71
|
end
|
70
72
|
|
71
73
|
def client
|
72
|
-
@client
|
74
|
+
if @client.nil? || !@client.is_a?(client_class)
|
75
|
+
# (Re-)Initialize client if missing or the current driver changes
|
76
|
+
@client = client_class.new
|
77
|
+
end
|
78
|
+
|
79
|
+
@client
|
80
|
+
end
|
81
|
+
|
82
|
+
def client_class
|
83
|
+
if selenium_driver?
|
84
|
+
Client::Selenium
|
85
|
+
elsif cuprite_driver?
|
86
|
+
Client::Cuprite
|
87
|
+
else
|
88
|
+
# This should never raise, as capybara lockstep should disable itself for any unsupported driver.
|
89
|
+
# When it still does, there is probably a bug within capybara lockstep.
|
90
|
+
raise DriverNotSupportedError, "The driver #{driver.class.name} is not supported by capybara-lockstep."
|
91
|
+
end
|
73
92
|
end
|
74
93
|
|
75
94
|
end
|
@@ -7,8 +7,8 @@ module Capybara
|
|
7
7
|
|
8
8
|
delegate :evaluate_script, :evaluate_async_script, :execute_script, :driver, to: :page
|
9
9
|
|
10
|
-
def
|
11
|
-
|
10
|
+
def supported_driver?
|
11
|
+
selenium_driver? || cuprite_driver?
|
12
12
|
end
|
13
13
|
|
14
14
|
def alert_present?
|
@@ -17,12 +17,25 @@ module Capybara
|
|
17
17
|
#
|
18
18
|
# Apparently, while an alert/confirm is open, Chrome will block any requests
|
19
19
|
# to its `getLog` API. This causes Selenium to time out with a `Net::ReadTimeout` error
|
20
|
+
return false unless selenium_driver? # This issue is selenium-specific.
|
21
|
+
|
20
22
|
page.driver.browser.switch_to.alert
|
21
23
|
true
|
22
24
|
rescue Capybara::NotSupportedByDriverError, ::Selenium::WebDriver::Error::NoSuchAlertError, ::Selenium::WebDriver::Error::NoSuchWindowError
|
23
25
|
false
|
24
26
|
end
|
25
27
|
|
28
|
+
private
|
29
|
+
|
30
|
+
def selenium_driver?
|
31
|
+
defined?(Capybara::Selenium::Driver) && driver.is_a?(Capybara::Selenium::Driver)
|
32
|
+
end
|
33
|
+
|
34
|
+
def cuprite_driver?
|
35
|
+
defined?(Capybara::Cuprite::Driver) && driver.is_a?(Capybara::Cuprite::Driver)
|
36
|
+
end
|
37
|
+
|
38
|
+
|
26
39
|
end
|
27
40
|
end
|
28
41
|
end
|
data/lib/capybara-lockstep.rb
CHANGED
@@ -1,7 +1,24 @@
|
|
1
1
|
require 'capybara'
|
2
|
-
|
2
|
+
|
3
|
+
selenium_loaded = begin
|
4
|
+
require 'selenium-webdriver'
|
5
|
+
true
|
6
|
+
rescue LoadError
|
7
|
+
false
|
8
|
+
end
|
9
|
+
|
10
|
+
cuprite_loaded = begin
|
11
|
+
require 'capybara/cuprite'
|
12
|
+
true
|
13
|
+
rescue LoadError
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
raise LoadError, "capybara-lockstep requires either selenium-webdriver or cuprite" unless selenium_loaded || cuprite_loaded
|
18
|
+
|
3
19
|
require 'active_support/core_ext/object/blank'
|
4
20
|
require 'active_support/core_ext/module/delegation'
|
21
|
+
require 'active_support/lazy_load_hooks'
|
5
22
|
|
6
23
|
module Capybara
|
7
24
|
module Lockstep
|
@@ -19,4 +36,6 @@ require_relative 'capybara-lockstep/capybara_ext'
|
|
19
36
|
require_relative 'capybara-lockstep/helper'
|
20
37
|
require_relative 'capybara-lockstep/server'
|
21
38
|
require_relative 'capybara-lockstep/client'
|
39
|
+
require_relative 'capybara-lockstep/client/selenium'
|
40
|
+
require_relative 'capybara-lockstep/client/cuprite'
|
22
41
|
require_relative 'capybara-lockstep/middleware'
|