capybara-lockstep 2.2.3 → 2.3.1
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 +16 -2
- data/.gitignore +2 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +14 -0
- data/Gemfile +6 -2
- data/Gemfile.lock +48 -16
- data/README.md +21 -6
- data/Rakefile +1 -0
- data/capybara-lockstep.gemspec +2 -4
- data/lib/capybara-lockstep/capybara_ext.rb +13 -26
- 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/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 +19 -1
- data/tasks/spec_tasks.rb +22 -0
- metadata +9 -34
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b10f22c42f855e5a7ca55b34c6a7ab33da9712f8f345f4ff6278e93756aca6a6
|
|
4
|
+
data.tar.gz: 670652bccdf353c723d0c3466be51bcccff00f9a189306a142caed2e10714f06
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ef6f4cb2a0c2c105e29a99cbaf4f8c2467526b38741dd47a8eb772a4df440c7a0ef27f8e05958d4bbc07746c4e0d9a63763ebd52372db61efbddb81b1d1e90ba
|
|
7
|
+
data.tar.gz: c309f4a88411eae4ea65645fe3d99a97a8558af368057aff51d06dd146f289c0ff8b1f3b5c48764200eeb9c221c1b5adc1ff3982428ff51b0699ebfe98f79f46
|
data/.github/workflows/test.yml
CHANGED
|
@@ -12,20 +12,33 @@ 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
|
|
19
19
|
matrix:
|
|
20
20
|
include:
|
|
21
|
-
- ruby: 2.
|
|
21
|
+
- ruby: 3.2.0
|
|
22
22
|
gemfile: Gemfile
|
|
23
|
+
capybara_driver: selenium
|
|
23
24
|
- ruby: 3.2.0
|
|
24
25
|
gemfile: Gemfile
|
|
26
|
+
capybara_driver: cuprite
|
|
27
|
+
- ruby: 3.4.1
|
|
28
|
+
gemfile: Gemfile
|
|
29
|
+
capybara_driver: selenium
|
|
25
30
|
- ruby: 3.4.1
|
|
26
31
|
gemfile: Gemfile
|
|
32
|
+
capybara_driver: cuprite
|
|
33
|
+
- ruby: 4.0.1
|
|
34
|
+
gemfile: Gemfile
|
|
35
|
+
capybara_driver: selenium
|
|
36
|
+
- ruby: 4.0.1
|
|
37
|
+
gemfile: Gemfile
|
|
38
|
+
capybara_driver: cuprite
|
|
27
39
|
env:
|
|
28
40
|
BUNDLE_GEMFILE: "${{ matrix.gemfile }}"
|
|
41
|
+
CAPYBARA_DRIVER: "${{ matrix.capybara_driver }}"
|
|
29
42
|
steps:
|
|
30
43
|
- uses: actions/checkout@v3
|
|
31
44
|
- name: Install Chrome
|
|
@@ -34,6 +47,7 @@ jobs:
|
|
|
34
47
|
run: chrome --version
|
|
35
48
|
- name: Install ChromeDriver
|
|
36
49
|
uses: nanasess/setup-chromedriver@master
|
|
50
|
+
if: "${{ matrix.capybara_driver == 'selenium' }}"
|
|
37
51
|
- name: Install ruby
|
|
38
52
|
uses: ruby/setup-ruby@v1
|
|
39
53
|
with:
|
data/.gitignore
CHANGED
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4.0.1
|
data/CHANGELOG.md
CHANGED
|
@@ -13,6 +13,20 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
|
|
|
13
13
|
-
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
## 2.3.1 - 2026-02-17
|
|
17
|
+
|
|
18
|
+
### Compatible changes
|
|
19
|
+
|
|
20
|
+
- Add tests for Ruby 4.0
|
|
21
|
+
- Increase `required_ruby_version` and `activesupport` minimum version in gemspec so people
|
|
22
|
+
on legacy Ruby and Rails versions don't receive future upgrades
|
|
23
|
+
- Drop tests and support for Ruby < 3 and Rails < 7
|
|
24
|
+
- Drop dependency on `ruby2_keywords`
|
|
25
|
+
|
|
26
|
+
# 2.3.0
|
|
27
|
+
|
|
28
|
+
- Added support for cuprite as the capybara driver
|
|
29
|
+
|
|
16
30
|
# 2.2.3
|
|
17
31
|
|
|
18
32
|
- Requiring the gem no longer force-loads ActionView (#22)
|
data/Gemfile
CHANGED
|
@@ -5,7 +5,7 @@ source "https://rubygems.org"
|
|
|
5
5
|
# Specify your gem's dependencies in capybara-lockstep.gemspec
|
|
6
6
|
gemspec
|
|
7
7
|
|
|
8
|
-
gem 'activesupport', '~>
|
|
8
|
+
gem 'activesupport', '~> 8.0'
|
|
9
9
|
gem "rake", "~> 13.0"
|
|
10
10
|
|
|
11
11
|
gem "rspec", "~> 3.0"
|
|
@@ -14,9 +14,10 @@ gem 'sinatra'
|
|
|
14
14
|
gem 'thin' # ruby 3 does not include a webserver
|
|
15
15
|
gem 'puma'
|
|
16
16
|
gem 'byebug'
|
|
17
|
-
gem 'gemika'
|
|
17
|
+
gem 'gemika'
|
|
18
18
|
gem 'capybara', '>= 3'
|
|
19
19
|
gem 'selenium-webdriver', '>= 4'
|
|
20
|
+
gem 'cuprite'
|
|
20
21
|
|
|
21
22
|
# The following gems were previously "default gems" (always available) and are now
|
|
22
23
|
# "bundled gems" (need to be explicitly required). Not all gems in our Gemfile.lock (dev only) do that yet.
|
|
@@ -24,3 +25,6 @@ gem 'selenium-webdriver', '>= 4'
|
|
|
24
25
|
# indirect dependencies ourselves.
|
|
25
26
|
gem 'base64' # needed by selenium-webdriver (and potentially others)
|
|
26
27
|
gem 'bigdecimal' # needed by activesupport (and potentially others)
|
|
28
|
+
gem 'ostruct'
|
|
29
|
+
gem 'logger'
|
|
30
|
+
gem 'cgi'
|
data/Gemfile.lock
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
capybara-lockstep (2.
|
|
5
|
-
activesupport (>=
|
|
4
|
+
capybara-lockstep (2.3.1)
|
|
5
|
+
activesupport (>= 7.0)
|
|
6
6
|
capybara (>= 3.0)
|
|
7
|
-
ruby2_keywords
|
|
8
|
-
selenium-webdriver (>= 4.0)
|
|
9
7
|
|
|
10
8
|
GEM
|
|
11
9
|
remote: https://rubygems.org/
|
|
12
10
|
specs:
|
|
13
|
-
activesupport (
|
|
14
|
-
|
|
11
|
+
activesupport (8.1.2)
|
|
12
|
+
base64
|
|
13
|
+
bigdecimal
|
|
14
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
15
|
+
connection_pool (>= 2.2.5)
|
|
16
|
+
drb
|
|
15
17
|
i18n (>= 1.6, < 2)
|
|
18
|
+
json
|
|
19
|
+
logger (>= 1.4.2)
|
|
16
20
|
minitest (>= 5.1)
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
securerandom (>= 0.3)
|
|
22
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
23
|
+
uri (>= 0.13.1)
|
|
19
24
|
addressable (2.8.5)
|
|
20
25
|
public_suffix (>= 2.0.2, < 6.0)
|
|
21
26
|
base64 (0.2.0)
|
|
@@ -30,24 +35,41 @@ GEM
|
|
|
30
35
|
rack-test (>= 0.6.3)
|
|
31
36
|
regexp_parser (>= 1.5, < 3.0)
|
|
32
37
|
xpath (~> 3.2)
|
|
38
|
+
cgi (0.5.1)
|
|
33
39
|
childprocess (4.1.0)
|
|
34
|
-
concurrent-ruby (1.
|
|
40
|
+
concurrent-ruby (1.3.6)
|
|
41
|
+
connection_pool (3.0.2)
|
|
42
|
+
cuprite (0.17)
|
|
43
|
+
capybara (~> 3.0)
|
|
44
|
+
ferrum (~> 0.17.0)
|
|
35
45
|
daemons (1.4.1)
|
|
36
46
|
diff-lcs (1.5.1)
|
|
47
|
+
drb (2.2.3)
|
|
37
48
|
eventmachine (1.2.7)
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
ferrum (0.17.1)
|
|
50
|
+
addressable (~> 2.5)
|
|
51
|
+
base64 (~> 0.2)
|
|
52
|
+
concurrent-ruby (~> 1.1)
|
|
53
|
+
webrick (~> 1.7)
|
|
54
|
+
websocket-driver (~> 0.7)
|
|
55
|
+
gemika (2.0.0)
|
|
56
|
+
i18n (1.14.8)
|
|
40
57
|
concurrent-ruby (~> 1.0)
|
|
58
|
+
json (2.18.1)
|
|
59
|
+
logger (1.7.0)
|
|
41
60
|
matrix (0.4.2)
|
|
42
61
|
mini_mime (1.1.5)
|
|
43
62
|
mini_portile2 (2.8.5)
|
|
44
|
-
minitest (
|
|
63
|
+
minitest (6.0.1)
|
|
64
|
+
prism (~> 1.5)
|
|
45
65
|
mustermann (3.0.0)
|
|
46
66
|
ruby2_keywords (~> 0.0.1)
|
|
47
67
|
nio4r (2.6.1)
|
|
48
68
|
nokogiri (1.15.5)
|
|
49
69
|
mini_portile2 (~> 2.8.2)
|
|
50
70
|
racc (~> 1.4)
|
|
71
|
+
ostruct (0.6.3)
|
|
72
|
+
prism (1.9.0)
|
|
51
73
|
public_suffix (5.0.4)
|
|
52
74
|
puma (6.4.0)
|
|
53
75
|
nio4r (~> 2.0)
|
|
@@ -77,6 +99,7 @@ GEM
|
|
|
77
99
|
rspec (>= 3.0)
|
|
78
100
|
ruby2_keywords (0.0.5)
|
|
79
101
|
rubyzip (2.3.2)
|
|
102
|
+
securerandom (0.4.1)
|
|
80
103
|
selenium-webdriver (4.1.0)
|
|
81
104
|
childprocess (>= 0.5, < 5.0)
|
|
82
105
|
rexml (~> 3.2, >= 3.2.5)
|
|
@@ -91,23 +114,32 @@ GEM
|
|
|
91
114
|
eventmachine (~> 1.0, >= 1.0.4)
|
|
92
115
|
rack (>= 1, < 3)
|
|
93
116
|
tilt (2.3.0)
|
|
94
|
-
tzinfo (2.0.
|
|
117
|
+
tzinfo (2.0.6)
|
|
95
118
|
concurrent-ruby (~> 1.0)
|
|
119
|
+
uri (1.1.1)
|
|
120
|
+
webrick (1.9.1)
|
|
121
|
+
websocket-driver (0.8.0)
|
|
122
|
+
base64
|
|
123
|
+
websocket-extensions (>= 0.1.0)
|
|
124
|
+
websocket-extensions (0.1.5)
|
|
96
125
|
xpath (3.2.0)
|
|
97
126
|
nokogiri (~> 1.8)
|
|
98
|
-
zeitwerk (2.6.0)
|
|
99
127
|
|
|
100
128
|
PLATFORMS
|
|
101
129
|
ruby
|
|
102
130
|
|
|
103
131
|
DEPENDENCIES
|
|
104
|
-
activesupport (~>
|
|
132
|
+
activesupport (~> 8.0)
|
|
105
133
|
base64
|
|
106
134
|
bigdecimal
|
|
107
135
|
byebug
|
|
108
136
|
capybara (>= 3)
|
|
109
137
|
capybara-lockstep!
|
|
110
|
-
|
|
138
|
+
cgi
|
|
139
|
+
cuprite
|
|
140
|
+
gemika
|
|
141
|
+
logger
|
|
142
|
+
ostruct
|
|
111
143
|
puma
|
|
112
144
|
rake (~> 13.0)
|
|
113
145
|
rspec (~> 3.0)
|
data/README.md
CHANGED
|
@@ -120,9 +120,9 @@ Installation
|
|
|
120
120
|
Check if your application satisfies all requirements for capybara-lockstep:
|
|
121
121
|
|
|
122
122
|
- Capybara 2.0 or higher.
|
|
123
|
-
- 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.
|
|
124
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).
|
|
125
|
-
- 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.
|
|
126
126
|
- This gem was only tested with Rails, but there's no Rails dependency.
|
|
127
127
|
|
|
128
128
|
|
|
@@ -460,10 +460,22 @@ Pull requests are welcome on GitHub at <https://github.com/makandra/capybara-loc
|
|
|
460
460
|
|
|
461
461
|
After checking out the repo, run `bin/setup` to install dependencies.
|
|
462
462
|
|
|
463
|
-
|
|
463
|
+
For running tests see [Running tests locally](#running-tests-locally) below
|
|
464
464
|
|
|
465
465
|
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
466
466
|
|
|
467
|
+
### Running tests locally
|
|
468
|
+
|
|
469
|
+
capybara-lockstep supports both the `selenium-webdriver` and `cuprite` as drivers for capybara.
|
|
470
|
+
|
|
471
|
+
Make sure, your chromedriver version is up to date.
|
|
472
|
+
|
|
473
|
+
To run all the tests for all supported drivers, run `rake spec:all`.
|
|
474
|
+
|
|
475
|
+
To run all the tests for a specific driver, run `rake spec:selenium` or `rake spec:cuprite`.
|
|
476
|
+
|
|
477
|
+
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]"`.
|
|
478
|
+
|
|
467
479
|
### Manually testing a change
|
|
468
480
|
|
|
469
481
|
To test an unrelased change with a test suite, we recommend to temporarily link the local repository from your test suites's `Gemfile`:
|
|
@@ -476,9 +488,12 @@ As an alternative you may also install this gem onto your local machine by runni
|
|
|
476
488
|
|
|
477
489
|
### Releasing a new version
|
|
478
490
|
|
|
479
|
-
- Update the version number in `version.rb
|
|
480
|
-
|
|
481
|
-
|
|
491
|
+
- Update the version number in `version.rb`. Use Semantic Versioning.
|
|
492
|
+
- Run `bundle install` so our `Gemfile.lock` (for development) gets the new version.
|
|
493
|
+
- Update `CHANGELOG.md`.
|
|
494
|
+
- Commit and push your changes.
|
|
495
|
+
- 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).
|
|
496
|
+
- If RubyGems publishing seems to freeze, try entering your OTP code.
|
|
482
497
|
|
|
483
498
|
|
|
484
499
|
## License
|
data/Rakefile
CHANGED
data/capybara-lockstep.gemspec
CHANGED
|
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
|
9
9
|
spec.summary = "Synchronize Capybara commands with client-side JavaScript and AJAX requests"
|
|
10
10
|
spec.homepage = "https://github.com/makandra/capybara-lockstep"
|
|
11
11
|
spec.license = "MIT"
|
|
12
|
-
spec.required_ruby_version = Gem::Requirement.new(">=
|
|
12
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0")
|
|
13
13
|
|
|
14
14
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
15
15
|
spec.metadata["source_code_uri"] = spec.homepage
|
|
@@ -29,9 +29,7 @@ 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 "
|
|
33
|
-
spec.add_dependency "activesupport", ">= 4.2"
|
|
34
|
-
spec.add_dependency "ruby2_keywords"
|
|
32
|
+
spec.add_dependency "activesupport", ">= 7.0"
|
|
35
33
|
|
|
36
34
|
# For more information and examples about making a new gem, checkout our
|
|
37
35
|
# guide at: https://bundler.io/guides/creating_gem.html
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
require 'ruby2_keywords'
|
|
2
|
-
|
|
3
1
|
module Capybara
|
|
4
2
|
module Lockstep
|
|
5
3
|
module SynchronizeMacros
|
|
@@ -13,43 +11,37 @@ module Capybara
|
|
|
13
11
|
|
|
14
12
|
def synchronize_before(meth, lazy:)
|
|
15
13
|
@synchronize_before_module.module_eval do
|
|
16
|
-
define_method meth do |*args, &block|
|
|
14
|
+
define_method meth do |*args, **kwargs, &block|
|
|
17
15
|
@synchronize_before_count ||= 0
|
|
18
16
|
@synchronize_before_count += 1
|
|
19
17
|
Lockstep.auto_synchronize(lazy: lazy, log: "Synchronizing before ##{meth}") if @synchronize_before_count == 1
|
|
20
|
-
super(*args, &block)
|
|
18
|
+
super(*args, **kwargs, &block)
|
|
21
19
|
ensure
|
|
22
20
|
@synchronize_before_count -= 1
|
|
23
21
|
end
|
|
24
|
-
|
|
25
|
-
ruby2_keywords meth
|
|
26
22
|
end
|
|
27
23
|
end
|
|
28
24
|
|
|
29
25
|
def synchronize_after(meth)
|
|
30
26
|
@synchronize_after_module.module_eval do
|
|
31
|
-
define_method meth do |*args, &block|
|
|
27
|
+
define_method meth do |*args, **kwargs, &block|
|
|
32
28
|
@synchronize_after_count ||= 0
|
|
33
29
|
@synchronize_after_count += 1
|
|
34
|
-
super(*args, &block)
|
|
30
|
+
super(*args, **kwargs, &block)
|
|
35
31
|
ensure
|
|
36
32
|
Lockstep.auto_synchronize(log: "Synchronizing after ##{meth}") if @synchronize_after_count == 1
|
|
37
33
|
@synchronize_after_count -= 1
|
|
38
34
|
end
|
|
39
|
-
|
|
40
|
-
ruby2_keywords meth
|
|
41
35
|
end
|
|
42
36
|
end
|
|
43
37
|
|
|
44
38
|
def unsynchronize_after(meth)
|
|
45
39
|
@unsynchronize_after_module.module_eval do
|
|
46
|
-
define_method meth do |*args, &block|
|
|
47
|
-
super(*args, &block)
|
|
40
|
+
define_method meth do |*args, **kwargs, &block|
|
|
41
|
+
super(*args, **kwargs, &block)
|
|
48
42
|
ensure
|
|
49
43
|
Lockstep.unsynchronize
|
|
50
44
|
end
|
|
51
|
-
|
|
52
|
-
ruby2_keywords meth
|
|
53
45
|
end
|
|
54
46
|
end
|
|
55
47
|
end
|
|
@@ -82,7 +74,7 @@ end
|
|
|
82
74
|
module Capybara
|
|
83
75
|
module Lockstep
|
|
84
76
|
module VisitWithWaiting
|
|
85
|
-
def visit(*args, &block)
|
|
77
|
+
def visit(*args, **kwargs, &block)
|
|
86
78
|
# For some reason, in Capybara proper, visit(nil) navigates to the root route.
|
|
87
79
|
# We mimic this behavior for (1) parity and (2) to not crash when we inspect the URL below.
|
|
88
80
|
url = args[0].presence || '/'
|
|
@@ -104,15 +96,13 @@ module Capybara
|
|
|
104
96
|
Lockstep.auto_synchronize(lazy: false, log: "Synchronizing before visiting #{url}")
|
|
105
97
|
end
|
|
106
98
|
|
|
107
|
-
super(*args, &block).tap do
|
|
99
|
+
super(*args, **kwargs, &block).tap do
|
|
108
100
|
if visiting_real_url
|
|
109
101
|
# We haven't yet synchronized the new screen.
|
|
110
102
|
Lockstep.unsynchronize
|
|
111
103
|
end
|
|
112
104
|
end
|
|
113
105
|
end
|
|
114
|
-
|
|
115
|
-
ruby2_keywords :visit
|
|
116
106
|
end
|
|
117
107
|
end
|
|
118
108
|
end
|
|
@@ -127,7 +117,7 @@ module Capybara
|
|
|
127
117
|
|
|
128
118
|
def synchronize_around_script_method(meth)
|
|
129
119
|
mod = Module.new do
|
|
130
|
-
define_method meth do |script, *args, &block|
|
|
120
|
+
define_method meth do |script, *args, **kwargs, &block|
|
|
131
121
|
# Synchronization uses execute_script itself, so don't synchronize when
|
|
132
122
|
# we're already synchronizing.
|
|
133
123
|
if !Lockstep.synchronizing?
|
|
@@ -142,7 +132,7 @@ module Capybara
|
|
|
142
132
|
Lockstep.auto_synchronize(lazy: !script_may_navigate_away, log: "Synchronizing before script: #{script}")
|
|
143
133
|
end
|
|
144
134
|
|
|
145
|
-
super(script, *args, &block)
|
|
135
|
+
super(script, *args, **kwargs, &block)
|
|
146
136
|
ensure
|
|
147
137
|
if !Lockstep.synchronizing?
|
|
148
138
|
# We haven't yet synchronized with whatever changes the JavaScript
|
|
@@ -150,8 +140,6 @@ module Capybara
|
|
|
150
140
|
Lockstep.unsynchronize
|
|
151
141
|
end
|
|
152
142
|
end
|
|
153
|
-
|
|
154
|
-
ruby2_keywords meth
|
|
155
143
|
end
|
|
156
144
|
prepend(mod)
|
|
157
145
|
end
|
|
@@ -176,6 +164,7 @@ driver_specific_node_classes = [
|
|
|
176
164
|
(Capybara::Selenium::SafariNode if defined?(Capybara::Selenium::SafariNode)),
|
|
177
165
|
(Capybara::Selenium::EdgeNode if defined?(Capybara::Selenium::EdgeNode)),
|
|
178
166
|
(Capybara::Selenium::IENode if defined?(Capybara::Selenium::IENode)),
|
|
167
|
+
(Capybara::Cuprite::Node if defined?(Capybara::Cuprite::Node)),
|
|
179
168
|
].compact.freeze
|
|
180
169
|
|
|
181
170
|
# For other browsers (like the :remote browser) we instead get a generic node class.
|
|
@@ -245,16 +234,14 @@ end
|
|
|
245
234
|
module Capybara
|
|
246
235
|
module Lockstep
|
|
247
236
|
module SynchronizeWithCatchUp
|
|
248
|
-
def synchronize(*args, &block)
|
|
237
|
+
def synchronize(*args, **kwargs, &block)
|
|
249
238
|
# This method is called by Capybara before most interactions with
|
|
250
239
|
# the browser. It is a different method than Capybara::Lockstep.synchronize!
|
|
251
240
|
# We use the { lazy } option to only synchronize when we're out of sync.
|
|
252
241
|
Lockstep.auto_synchronize(lazy: true, log: 'Synchronizing before node access')
|
|
253
242
|
|
|
254
|
-
super(*args, &block)
|
|
243
|
+
super(*args, **kwargs, &block)
|
|
255
244
|
end
|
|
256
|
-
|
|
257
|
-
ruby2_keywords :synchronize
|
|
258
245
|
end
|
|
259
246
|
end
|
|
260
247
|
end
|
|
@@ -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,5 +1,21 @@
|
|
|
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'
|
|
5
21
|
require 'active_support/lazy_load_hooks'
|
|
@@ -20,4 +36,6 @@ require_relative 'capybara-lockstep/capybara_ext'
|
|
|
20
36
|
require_relative 'capybara-lockstep/helper'
|
|
21
37
|
require_relative 'capybara-lockstep/server'
|
|
22
38
|
require_relative 'capybara-lockstep/client'
|
|
39
|
+
require_relative 'capybara-lockstep/client/selenium'
|
|
40
|
+
require_relative 'capybara-lockstep/client/cuprite'
|
|
23
41
|
require_relative 'capybara-lockstep/middleware'
|
data/tasks/spec_tasks.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
namespace :spec do
|
|
2
|
+
CAPYBARA_DRIVERS = %w[selenium cuprite]
|
|
3
|
+
|
|
4
|
+
desc "Run all tests for all drivers"
|
|
5
|
+
task :all do
|
|
6
|
+
CAPYBARA_DRIVERS.each do |driver|
|
|
7
|
+
run_specs driver
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
CAPYBARA_DRIVERS.each do |driver|
|
|
12
|
+
task driver do
|
|
13
|
+
run_specs driver
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def run_specs(driver)
|
|
18
|
+
puts "Running specs using #{driver} as the capybara driver:\n"
|
|
19
|
+
sh "CAPYBARA_DRIVER=#{driver} bundle exec rake spec"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: capybara-lockstep
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Henning Koch
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: capybara
|
|
@@ -23,48 +23,20 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '3.0'
|
|
26
|
-
- !ruby/object:Gem::Dependency
|
|
27
|
-
name: selenium-webdriver
|
|
28
|
-
requirement: !ruby/object:Gem::Requirement
|
|
29
|
-
requirements:
|
|
30
|
-
- - ">="
|
|
31
|
-
- !ruby/object:Gem::Version
|
|
32
|
-
version: '4.0'
|
|
33
|
-
type: :runtime
|
|
34
|
-
prerelease: false
|
|
35
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
-
requirements:
|
|
37
|
-
- - ">="
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: '4.0'
|
|
40
26
|
- !ruby/object:Gem::Dependency
|
|
41
27
|
name: activesupport
|
|
42
28
|
requirement: !ruby/object:Gem::Requirement
|
|
43
29
|
requirements:
|
|
44
30
|
- - ">="
|
|
45
31
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '
|
|
47
|
-
type: :runtime
|
|
48
|
-
prerelease: false
|
|
49
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
-
requirements:
|
|
51
|
-
- - ">="
|
|
52
|
-
- !ruby/object:Gem::Version
|
|
53
|
-
version: '4.2'
|
|
54
|
-
- !ruby/object:Gem::Dependency
|
|
55
|
-
name: ruby2_keywords
|
|
56
|
-
requirement: !ruby/object:Gem::Requirement
|
|
57
|
-
requirements:
|
|
58
|
-
- - ">="
|
|
59
|
-
- !ruby/object:Gem::Version
|
|
60
|
-
version: '0'
|
|
32
|
+
version: '7.0'
|
|
61
33
|
type: :runtime
|
|
62
34
|
prerelease: false
|
|
63
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
36
|
requirements:
|
|
65
37
|
- - ">="
|
|
66
38
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '0'
|
|
39
|
+
version: '7.0'
|
|
68
40
|
email:
|
|
69
41
|
- henning.koch@makandra.de
|
|
70
42
|
executables: []
|
|
@@ -87,6 +59,8 @@ files:
|
|
|
87
59
|
- lib/capybara-lockstep.rb
|
|
88
60
|
- lib/capybara-lockstep/capybara_ext.rb
|
|
89
61
|
- lib/capybara-lockstep/client.rb
|
|
62
|
+
- lib/capybara-lockstep/client/cuprite.rb
|
|
63
|
+
- lib/capybara-lockstep/client/selenium.rb
|
|
90
64
|
- lib/capybara-lockstep/configuration.rb
|
|
91
65
|
- lib/capybara-lockstep/errors.rb
|
|
92
66
|
- lib/capybara-lockstep/helper.js
|
|
@@ -104,6 +78,7 @@ files:
|
|
|
104
78
|
- media/logo.light.text.svg
|
|
105
79
|
- media/makandra-with-bottom-margin.dark.svg
|
|
106
80
|
- media/makandra-with-bottom-margin.light.svg
|
|
81
|
+
- tasks/spec_tasks.rb
|
|
107
82
|
homepage: https://github.com/makandra/capybara-lockstep
|
|
108
83
|
licenses:
|
|
109
84
|
- MIT
|
|
@@ -120,14 +95,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
120
95
|
requirements:
|
|
121
96
|
- - ">="
|
|
122
97
|
- !ruby/object:Gem::Version
|
|
123
|
-
version:
|
|
98
|
+
version: 3.0.0
|
|
124
99
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
125
100
|
requirements:
|
|
126
101
|
- - ">="
|
|
127
102
|
- !ruby/object:Gem::Version
|
|
128
103
|
version: '0'
|
|
129
104
|
requirements: []
|
|
130
|
-
rubygems_version:
|
|
105
|
+
rubygems_version: 4.0.3
|
|
131
106
|
specification_version: 4
|
|
132
107
|
summary: Synchronize Capybara commands with client-side JavaScript and AJAX requests
|
|
133
108
|
test_files: []
|