cuprite 0.7.1 → 0.12
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/LICENSE +1 -1
- data/README.md +105 -106
- data/lib/capybara/cuprite.rb +0 -6
- data/lib/capybara/cuprite/browser.rb +58 -3
- data/lib/capybara/cuprite/driver.rb +45 -12
- data/lib/capybara/cuprite/javascripts/index.js +1 -1
- data/lib/capybara/cuprite/node.rb +13 -8
- data/lib/capybara/cuprite/page.rb +46 -12
- data/lib/capybara/cuprite/version.rb +1 -1
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bac1e4393d6796d34198e45428ea8d443b67136e4805e77f7c7d3f35451cefbb
|
4
|
+
data.tar.gz: 27edf7157ac47b0a6bbc579e7908aaa0313d91b7f3702417453f76f1230e882d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8ed43b593e4a071e449a80ac92925abafff1d9afa2a3934783963a19411deddcc18b1906467d5d355a93ed891ec4aae4b21509031190020d154202ba21904e8
|
7
|
+
data.tar.gz: ed39a7d445203f93b402fc313caa883449d7e743a6690196ba7f6bed9ee1d8c506f06d4b8ae620c8bc9d1c21c5770458b88564e9e937f7b5583e01a931fece39
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,26 +1,25 @@
|
|
1
|
-
# Cuprite - Headless Chrome driver for Capybara
|
1
|
+
# Cuprite - Headless Chrome driver for Capybara
|
2
2
|
|
3
|
-
|
3
|
+
Cuprite is a pure Ruby driver (read as _no_ Selenium/WebDriver/ChromeDriver
|
4
|
+
dependency) for [Capybara](https://github.com/teamcapybara/capybara). It allows
|
5
|
+
you to run Capybara tests on a headless Chrome or Chromium. Under the hood it
|
6
|
+
uses [Ferrum](https://github.com/rubycdp/ferrum#index) which is high-level API
|
7
|
+
to the browser by CDP protocol. The design of the driver is as close to
|
8
|
+
[Poltergeist](https://github.com/teampoltergeist/poltergeist) as possible though
|
9
|
+
it's not a goal.
|
4
10
|
|
5
|
-
Cuprite
|
6
|
-
|
7
|
-
you to run your Capybara tests on a headless [Chrome](https://www.google.com/chrome/)
|
8
|
-
or [Chromium](https://www.chromium.org/) by [CDP protocol](https://chromedevtools.github.io/devtools-protocol/).
|
9
|
-
Under the hood it uses [Ferrum](https://github.com/route/ferrum) which is
|
10
|
-
high-level API to the browser again by CDP protocol.
|
11
|
+
[Cuprite](https://evrone.com/cuprite) designed & supported by [Evrone](https://evrone.com/)
|
12
|
+
What else we build [with Ruby](https://evrone.com/ruby)
|
11
13
|
|
12
|
-
The emphasis was made on raw CDP protocol because Headless Chrome allows you to
|
13
|
-
do so many things that are barely supported by WebDriver because it should have
|
14
|
-
consistent design with other browsers. The design of the driver will be as
|
15
|
-
close to [Poltergeist](https://github.com/teampoltergeist/poltergeist) as
|
16
|
-
possible though it's not a goal.
|
17
14
|
|
18
|
-
## Install
|
15
|
+
## Install
|
19
16
|
|
20
|
-
Add
|
17
|
+
Add this to your `Gemfile` and run `bundle install`.
|
21
18
|
|
22
19
|
``` ruby
|
23
|
-
|
20
|
+
group :test do
|
21
|
+
gem "cuprite"
|
22
|
+
end
|
24
23
|
```
|
25
24
|
|
26
25
|
In your test setup add:
|
@@ -33,21 +32,31 @@ Capybara.register_driver(:cuprite) do |app|
|
|
33
32
|
end
|
34
33
|
```
|
35
34
|
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
if you use `Docker` don't forget to pass `no-sandbox` option:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
Capybara::Cuprite::Driver.new(app, browser_options: { 'no-sandbox': nil })
|
39
|
+
```
|
40
|
+
|
41
|
+
Since Cuprite uses [Ferrum](https://github.com/rubycdp/ferrum#examples) there
|
42
|
+
are many useful methods you can call even using this driver:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
browser = page.driver.browser
|
46
|
+
browser.mouse.move(x: 123, y: 456).down.up
|
47
|
+
```
|
48
|
+
|
49
|
+
If you already have tests on Poltergeist then it should simply work, for
|
50
|
+
Selenium you better check your code for `manage` calls because it works
|
51
|
+
differently in Cuprite, see the documentation below.
|
39
52
|
|
40
|
-
## Install Chrome ##
|
41
53
|
|
42
|
-
|
43
|
-
way because it either will be outdated or unofficial, both are bad. Download it
|
44
|
-
from official [source](https://www.chromium.org/getting-involved/download-chromium).
|
45
|
-
Chrome binary should be in the `PATH` or `BROWSER_PATH` or you can pass it as an
|
46
|
-
option
|
54
|
+
## Customization
|
47
55
|
|
48
|
-
|
56
|
+
See the full list of options for
|
57
|
+
[Ferrum](https://github.com/rubycdp/ferrum#customization).
|
49
58
|
|
50
|
-
You can
|
59
|
+
You can pass options with the following code in your test setup:
|
51
60
|
|
52
61
|
``` ruby
|
53
62
|
Capybara.register_driver(:cuprite) do |app|
|
@@ -55,47 +64,52 @@ Capybara.register_driver(:cuprite) do |app|
|
|
55
64
|
end
|
56
65
|
```
|
57
66
|
|
58
|
-
|
67
|
+
`Cuprite`-specific options are:
|
68
|
+
|
69
|
+
* options `Hash`
|
70
|
+
* `:url_blacklist` (Array) - array of strings to match against requested URLs
|
71
|
+
* `:url_whitelist` (Array) - array of strings to match against requested URLs
|
72
|
+
|
73
|
+
|
74
|
+
## Debugging
|
59
75
|
|
60
|
-
|
76
|
+
If you pass `inspector` option, remote debugging will be enabled if you run
|
77
|
+
tests with `INSPECTOR=true`. Then you can put `page.driver.debug` or
|
78
|
+
`page.driver.debug(binding)` in your test to pause it. This will launch the
|
79
|
+
browser where you can inspect the content.
|
61
80
|
|
62
81
|
```ruby
|
63
|
-
Capybara
|
82
|
+
Capybara.register_driver :cuprite do |app|
|
83
|
+
Capybara::Cuprite::Driver.new(app, inspector: ENV['INSPECTOR'])
|
84
|
+
end
|
64
85
|
```
|
65
86
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
* `:extensions` (Array) - An array of JS files to be preloaded into the browser
|
83
|
-
* `:port` (Integer) - Remote debugging port for headless Chrome
|
84
|
-
* `:host` (String) - Remote debugging address for headless Chrome
|
85
|
-
* `:url` (String) - URL for a running instance of Chrome. If this is set, a
|
86
|
-
browser process will not be spawned.
|
87
|
-
* `:url_blacklist` (Array) - array of strings to match against requested URLs
|
88
|
-
* `:url_whitelist` (Array) - array of strings to match against requested URLs
|
89
|
-
* `:process_timeout` (Integer) - How long to wait for the Chrome process to
|
90
|
-
respond on startup
|
87
|
+
then somewhere in the test:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
it "does something useful" do
|
91
|
+
visit root_path
|
92
|
+
|
93
|
+
fill_in "field", with: "value"
|
94
|
+
page.driver.debug(binding)
|
95
|
+
|
96
|
+
expect(page).to have_content("value")
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
100
|
+
In the middle of the execution Chrome will open a new tab where you can inspect
|
101
|
+
the content and also if you passed `binding` an `irb` or `pry` console will be
|
102
|
+
opened where you can further experiment with the test.
|
91
103
|
|
92
|
-
|
104
|
+
|
105
|
+
## Clicking/Scrolling
|
93
106
|
|
94
107
|
* `page.driver.click(x, y)` Click a very specific area of the screen.
|
95
|
-
* `page.driver.scroll_to(left, top)` Scroll to given position.
|
96
|
-
* `element.send_keys(*keys)` Send keys to given node.
|
108
|
+
* `page.driver.scroll_to(left, top)` Scroll to a given position.
|
109
|
+
* `element.send_keys(*keys)` Send keys to a given node.
|
110
|
+
|
97
111
|
|
98
|
-
|
112
|
+
## Request headers
|
99
113
|
|
100
114
|
Manipulate HTTP request headers like a boss:
|
101
115
|
|
@@ -111,11 +125,11 @@ Notice that `headers=` will overwrite already set headers. You should use
|
|
111
125
|
subsequent HTTP requests (including requests for assets, AJAX, etc). They will
|
112
126
|
be automatically cleared at the end of the test.
|
113
127
|
|
114
|
-
### Network traffic ###
|
115
128
|
|
116
|
-
|
117
|
-
loaded) on the current page. This returns an array of request objects.
|
129
|
+
## Network traffic
|
118
130
|
|
131
|
+
* `page.driver.network_traffic` Inspect network traffic (loaded resources) on
|
132
|
+
the current page. This returns an array of request objects.
|
119
133
|
|
120
134
|
```ruby
|
121
135
|
page.driver.network_traffic # => [Request, ...]
|
@@ -123,11 +137,30 @@ request = page.driver.network_traffic.first
|
|
123
137
|
request.response
|
124
138
|
```
|
125
139
|
|
140
|
+
* `page.driver.wait_for_network_idle` Natively waits for network idle and if
|
141
|
+
there are no active connections returns or raises `TimeoutError` error. Accepts
|
142
|
+
the same options as
|
143
|
+
[`wait_for_idle`](https://github.com/rubycdp/ferrum#wait_for_idleoptions)
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
page.driver.wait_for_network_idle
|
147
|
+
page.driver.refresh
|
148
|
+
```
|
149
|
+
|
126
150
|
Please note that network traffic is not cleared when you visit new page. You can
|
127
151
|
manually clear the network traffic by calling `page.driver.clear_network_traffic`
|
128
152
|
or `page.driver.reset`
|
129
153
|
|
130
|
-
|
154
|
+
* `page.driver.wait_for_reload` unlike `wait_for_network_idle` will wait until
|
155
|
+
the whole page is reloaded or raise a timeout error. It's useful when you know
|
156
|
+
that for example after clicking autocomplete suggestion you expect page to be
|
157
|
+
reloaded, you have a few choices - put sleep or wait for network idle, but both
|
158
|
+
are bad. Sleep makes you wait longer or less than needed, network idle can
|
159
|
+
return earlier even before the whole page is started to reload. Here's the
|
160
|
+
rescue.
|
161
|
+
|
162
|
+
|
163
|
+
## Manipulating cookies
|
131
164
|
|
132
165
|
The following methods are used to inspect and manipulate cookies:
|
133
166
|
|
@@ -142,18 +175,22 @@ The following methods are used to inspect and manipulate cookies:
|
|
142
175
|
* `page.driver.remove_cookie(name)` - remove a cookie
|
143
176
|
* `page.driver.clear_cookies` - clear all cookies
|
144
177
|
|
145
|
-
|
178
|
+
|
179
|
+
## Screenshot
|
146
180
|
|
147
181
|
Besides capybara screenshot method you can get image as Base64:
|
148
182
|
|
149
183
|
* `page.driver.render_base64(format, options)`
|
150
184
|
|
151
|
-
|
185
|
+
|
186
|
+
## Authorization
|
152
187
|
|
153
188
|
* `page.driver.basic_authorize(user, password)`
|
154
189
|
* `page.driver.set_proxy(ip, port, type, user, password)`
|
155
190
|
|
156
|
-
|
191
|
+
|
192
|
+
## URL Blacklisting & Whitelisting
|
193
|
+
|
157
194
|
Cuprite supports URL blacklisting, which allows you to prevent scripts from
|
158
195
|
running on designated domains:
|
159
196
|
|
@@ -161,8 +198,8 @@ running on designated domains:
|
|
161
198
|
page.driver.browser.url_blacklist = ["http://www.example.com"]
|
162
199
|
```
|
163
200
|
|
164
|
-
and also URL whitelisting, which allows scripts to only run
|
165
|
-
|
201
|
+
and also URL whitelisting, which allows scripts to only run on designated
|
202
|
+
domains:
|
166
203
|
|
167
204
|
```ruby
|
168
205
|
page.driver.browser.url_whitelist = ["http://www.example.com"]
|
@@ -171,41 +208,3 @@ page.driver.browser.url_whitelist = ["http://www.example.com"]
|
|
171
208
|
If you are experiencing slower run times, consider creating a URL whitelist of
|
172
209
|
domains that are essential or a blacklist of domains that are not essential,
|
173
210
|
such as ad networks or analytics, to your testing environment.
|
174
|
-
|
175
|
-
### Remote debugging ###
|
176
|
-
|
177
|
-
If you use the `inspector: true` option, remote debugging will be enabled. When
|
178
|
-
this option is enabled, you can insert `page.driver.debug` into your tests to
|
179
|
-
pause the test and launch a browser which gives you the Chrome inspector to view
|
180
|
-
all your open pages and inspect them.
|
181
|
-
|
182
|
-
You could set the inspector option via an environment variable:
|
183
|
-
|
184
|
-
```ruby
|
185
|
-
Capybara.register_driver :cuprite do |app|
|
186
|
-
Capybara::Cuprite::Driver.new(app, inspector: ENV['INSPECTOR'])
|
187
|
-
end
|
188
|
-
```
|
189
|
-
|
190
|
-
## License ##
|
191
|
-
|
192
|
-
Copyright 2018-2019 Machinio
|
193
|
-
|
194
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
195
|
-
a copy of this software and associated documentation files (the
|
196
|
-
"Software"), to deal in the Software without restriction, including
|
197
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
198
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
199
|
-
permit persons to whom the Software is furnished to do so, subject to
|
200
|
-
the following conditions:
|
201
|
-
|
202
|
-
The above copyright notice and this permission notice shall be
|
203
|
-
included in all copies or substantial portions of the Software.
|
204
|
-
|
205
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
206
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
207
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
208
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
209
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
210
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
211
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/capybara/cuprite.rb
CHANGED
@@ -2,18 +2,12 @@
|
|
2
2
|
|
3
3
|
require "ferrum"
|
4
4
|
require "capybara"
|
5
|
-
|
6
|
-
module Capybara::Cuprite
|
7
|
-
end
|
8
|
-
|
9
5
|
require "capybara/cuprite/driver"
|
10
6
|
require "capybara/cuprite/browser"
|
11
7
|
require "capybara/cuprite/page"
|
12
8
|
require "capybara/cuprite/node"
|
13
9
|
require "capybara/cuprite/errors"
|
14
10
|
|
15
|
-
Ferrum::Page.prepend(Capybara::Cuprite::Page)
|
16
|
-
|
17
11
|
Capybara.register_driver(:cuprite) do |app|
|
18
12
|
Capybara::Cuprite::Driver.new(app)
|
19
13
|
end
|
@@ -6,7 +6,6 @@ module Capybara::Cuprite
|
|
6
6
|
class Browser < Ferrum::Browser
|
7
7
|
extend Forwardable
|
8
8
|
|
9
|
-
delegate %i[find_or_create_page] => :targets
|
10
9
|
delegate %i[send_keys select set hover trigger before_click switch_to_frame
|
11
10
|
find_modal accept_confirm dismiss_confirm accept_prompt
|
12
11
|
dismiss_prompt reset_modals] => :page
|
@@ -19,6 +18,22 @@ module Capybara::Cuprite
|
|
19
18
|
self.url_whitelist = options[:url_whitelist]
|
20
19
|
|
21
20
|
super
|
21
|
+
@page = false
|
22
|
+
end
|
23
|
+
|
24
|
+
def page
|
25
|
+
raise Ferrum::NoSuchPageError if @page.nil?
|
26
|
+
@page ||= attach_page
|
27
|
+
end
|
28
|
+
|
29
|
+
def reset
|
30
|
+
super
|
31
|
+
@page = attach_page
|
32
|
+
end
|
33
|
+
|
34
|
+
def quit
|
35
|
+
super
|
36
|
+
@page = false
|
22
37
|
end
|
23
38
|
|
24
39
|
def url_whitelist=(patterns)
|
@@ -53,16 +68,46 @@ module Capybara::Cuprite
|
|
53
68
|
find_all(method, selector, { "objectId" => object_id })
|
54
69
|
end
|
55
70
|
|
71
|
+
def window_handle
|
72
|
+
page.target_id
|
73
|
+
end
|
74
|
+
|
75
|
+
def window_handles
|
76
|
+
targets.keys
|
77
|
+
end
|
78
|
+
|
56
79
|
def within_window(locator = nil, &block)
|
80
|
+
original = window_handle
|
81
|
+
|
57
82
|
if Capybara::VERSION.to_f < 3.0
|
58
83
|
target_id = window_handles.find do |target_id|
|
59
|
-
page =
|
84
|
+
page = attach_page(target_id)
|
60
85
|
locator == page.frame_name
|
61
86
|
end
|
62
87
|
locator = target_id if target_id
|
63
88
|
end
|
64
89
|
|
65
|
-
|
90
|
+
if window_handles.include?(locator)
|
91
|
+
switch_to_window(locator)
|
92
|
+
yield
|
93
|
+
else
|
94
|
+
raise Ferrum::NoSuchPageError
|
95
|
+
end
|
96
|
+
ensure
|
97
|
+
switch_to_window(original)
|
98
|
+
end
|
99
|
+
|
100
|
+
def switch_to_window(target_id)
|
101
|
+
target = targets[target_id]
|
102
|
+
raise Ferrum::NoSuchPageError unless target
|
103
|
+
@page = attach_page(target.id)
|
104
|
+
end
|
105
|
+
|
106
|
+
def close_window(target_id)
|
107
|
+
target = targets[target_id]
|
108
|
+
raise Ferrum::NoSuchPageError unless target
|
109
|
+
@page = nil if @page.target_id == target.id
|
110
|
+
target.page.close
|
66
111
|
end
|
67
112
|
|
68
113
|
def browser_error
|
@@ -155,5 +200,15 @@ module Capybara::Cuprite
|
|
155
200
|
end
|
156
201
|
end
|
157
202
|
end
|
203
|
+
|
204
|
+
def attach_page(target_id = nil)
|
205
|
+
target = targets[target_id] if target_id
|
206
|
+
target ||= default_context.default_target
|
207
|
+
return target.page if target.attached?
|
208
|
+
|
209
|
+
target.maybe_sleep_if_new_window
|
210
|
+
target.page = Page.new(target.id, self)
|
211
|
+
target.page
|
212
|
+
end
|
158
213
|
end
|
159
214
|
end
|
@@ -26,6 +26,8 @@ module Capybara::Cuprite
|
|
26
26
|
@screen_size ||= DEFAULT_MAXIMIZE_SCREEN_SIZE
|
27
27
|
|
28
28
|
@options[:save_path] = Capybara.save_path.to_s if Capybara.save_path
|
29
|
+
|
30
|
+
ENV["FERRUM_DEBUG"] = "true" if ENV["CUPRITE_DEBUG"]
|
29
31
|
end
|
30
32
|
|
31
33
|
def needs_server?
|
@@ -50,7 +52,7 @@ module Capybara::Cuprite
|
|
50
52
|
end
|
51
53
|
|
52
54
|
def frame_url
|
53
|
-
|
55
|
+
evaluate_script("window.location.href")
|
54
56
|
end
|
55
57
|
|
56
58
|
def html
|
@@ -71,7 +73,7 @@ module Capybara::Cuprite
|
|
71
73
|
end
|
72
74
|
|
73
75
|
def frame_title
|
74
|
-
|
76
|
+
evaluate_script("document.title")
|
75
77
|
end
|
76
78
|
|
77
79
|
def find_xpath(selector)
|
@@ -129,7 +131,10 @@ module Capybara::Cuprite
|
|
129
131
|
end
|
130
132
|
|
131
133
|
def open_new_window
|
132
|
-
browser.
|
134
|
+
target = browser.default_context.create_target
|
135
|
+
target.maybe_sleep_if_new_window
|
136
|
+
target.page = Page.new(target.id, browser)
|
137
|
+
target.page
|
133
138
|
end
|
134
139
|
|
135
140
|
def switch_to_window(handle)
|
@@ -141,7 +146,7 @@ module Capybara::Cuprite
|
|
141
146
|
end
|
142
147
|
|
143
148
|
def no_such_window_error
|
144
|
-
Ferrum::
|
149
|
+
Ferrum::NoSuchPageError
|
145
150
|
end
|
146
151
|
|
147
152
|
def reset!
|
@@ -238,7 +243,9 @@ module Capybara::Cuprite
|
|
238
243
|
server = type ? "#{type}=#{ip}:#{port}" : "#{ip}:#{port}"
|
239
244
|
@options[:browser_options].merge!("proxy-server" => server)
|
240
245
|
@options[:browser_options].merge!("proxy-bypass-list" => bypass) if bypass
|
241
|
-
browser.network.authorize(type: :proxy, user: user, password: password)
|
246
|
+
browser.network.authorize(type: :proxy, user: user, password: password) do |request|
|
247
|
+
request.continue
|
248
|
+
end
|
242
249
|
end
|
243
250
|
|
244
251
|
def headers
|
@@ -282,22 +289,41 @@ module Capybara::Cuprite
|
|
282
289
|
browser.cookies.clear
|
283
290
|
end
|
284
291
|
|
292
|
+
def wait_for_network_idle(**options)
|
293
|
+
browser.network.wait_for_idle(**options)
|
294
|
+
end
|
295
|
+
|
285
296
|
def clear_memory_cache
|
286
297
|
browser.network.clear(:cache)
|
287
298
|
end
|
288
299
|
|
289
300
|
def basic_authorize(user, password)
|
290
|
-
browser.network.authorize(user: user, password: password)
|
301
|
+
browser.network.authorize(user: user, password: password) do |request|
|
302
|
+
request.continue
|
303
|
+
end
|
291
304
|
end
|
292
305
|
alias_method :authorize, :basic_authorize
|
293
306
|
|
294
|
-
def
|
307
|
+
def debug_url
|
308
|
+
"http://#{browser.process.host}:#{browser.process.port}"
|
309
|
+
end
|
310
|
+
|
311
|
+
def debug(binding = nil)
|
295
312
|
if @options[:inspector]
|
296
|
-
Process.spawn(browser.process.path,
|
297
|
-
|
313
|
+
Process.spawn(browser.process.path, debug_url)
|
314
|
+
|
315
|
+
if binding&.respond_to?(:pry)
|
316
|
+
Pry.start(binding)
|
317
|
+
elsif binding&.respond_to?(:irb)
|
318
|
+
binding.irb
|
319
|
+
else
|
320
|
+
pause
|
321
|
+
end
|
298
322
|
else
|
299
323
|
raise Error, "To use the remote debugging, you have to launch " \
|
300
|
-
"the driver with `inspector:
|
324
|
+
"the driver with `inspector: ENV['INSPECTOR']` " \
|
325
|
+
"configuration option and run your test suite passing " \
|
326
|
+
"env variable"
|
301
327
|
end
|
302
328
|
end
|
303
329
|
|
@@ -308,7 +334,7 @@ module Capybara::Cuprite
|
|
308
334
|
# In jRuby - STDIN returns immediately from select
|
309
335
|
# see https://github.com/jruby/jruby/issues/1783
|
310
336
|
read, write = IO.pipe
|
311
|
-
Thread.new { IO.copy_stream(STDIN, write); write.close }
|
337
|
+
thread = Thread.new { IO.copy_stream(STDIN, write); write.close }
|
312
338
|
|
313
339
|
STDERR.puts "Cuprite execution paused. Press enter (or run 'kill -CONT #{Process.pid}') to continue."
|
314
340
|
|
@@ -324,6 +350,8 @@ module Capybara::Cuprite
|
|
324
350
|
end
|
325
351
|
end
|
326
352
|
ensure
|
353
|
+
thread.kill
|
354
|
+
read.close
|
327
355
|
trap("SIGCONT", old_trap) # Restore the previous signal handler, if there was one.
|
328
356
|
STDERR.puts "Continuing"
|
329
357
|
end
|
@@ -335,7 +363,8 @@ module Capybara::Cuprite
|
|
335
363
|
def invalid_element_errors
|
336
364
|
[Capybara::Cuprite::ObsoleteNode,
|
337
365
|
Capybara::Cuprite::MouseEventFailed,
|
338
|
-
Ferrum::NoExecutionContextError
|
366
|
+
Ferrum::NoExecutionContextError,
|
367
|
+
Ferrum::NodeNotFoundError]
|
339
368
|
end
|
340
369
|
|
341
370
|
def go_back
|
@@ -350,6 +379,10 @@ module Capybara::Cuprite
|
|
350
379
|
browser.refresh
|
351
380
|
end
|
352
381
|
|
382
|
+
def wait_for_reload(*args)
|
383
|
+
browser.wait_for_reload(*args)
|
384
|
+
end
|
385
|
+
|
353
386
|
def accept_modal(type, options = {})
|
354
387
|
case type
|
355
388
|
when :alert, :confirm
|
@@ -128,7 +128,7 @@ class Cuprite {
|
|
128
128
|
this.trigger(node, "focus");
|
129
129
|
this.setValue(node, "");
|
130
130
|
|
131
|
-
if (node.type == "number" || node.type == "date") {
|
131
|
+
if (node.type == "number" || node.type == "date" || node.type == "range") {
|
132
132
|
this.setValue(node, value);
|
133
133
|
this.input(node);
|
134
134
|
} else if (node.type == "time") {
|
@@ -104,6 +104,8 @@ module Capybara::Cuprite
|
|
104
104
|
when "file"
|
105
105
|
files = value.respond_to?(:to_ary) ? value.to_ary.map(&:to_s) : value.to_s
|
106
106
|
command(:select_file, files)
|
107
|
+
when "color"
|
108
|
+
node.evaluate("this.setAttribute('value', '#{value}')")
|
107
109
|
else
|
108
110
|
command(:set, value.to_s)
|
109
111
|
end
|
@@ -144,16 +146,16 @@ module Capybara::Cuprite
|
|
144
146
|
command(:disabled?)
|
145
147
|
end
|
146
148
|
|
147
|
-
def click(keys = [],
|
148
|
-
prepare_and_click(:left, __method__, keys,
|
149
|
+
def click(keys = [], **options)
|
150
|
+
prepare_and_click(:left, __method__, keys, options)
|
149
151
|
end
|
150
152
|
|
151
|
-
def right_click(keys = [],
|
152
|
-
prepare_and_click(:right, __method__, keys,
|
153
|
+
def right_click(keys = [], **options)
|
154
|
+
prepare_and_click(:right, __method__, keys, options)
|
153
155
|
end
|
154
156
|
|
155
|
-
def double_click(keys = [],
|
156
|
-
prepare_and_click(:double, __method__, keys,
|
157
|
+
def double_click(keys = [], **options)
|
158
|
+
prepare_and_click(:double, __method__, keys, options)
|
157
159
|
end
|
158
160
|
|
159
161
|
def hover
|
@@ -225,9 +227,12 @@ module Capybara::Cuprite
|
|
225
227
|
|
226
228
|
private
|
227
229
|
|
228
|
-
def prepare_and_click(mode, name, keys,
|
230
|
+
def prepare_and_click(mode, name, keys, options)
|
231
|
+
delay = options[:delay].to_i
|
232
|
+
x, y = options.values_at(:x, :y)
|
233
|
+
offset = { x: x, y: y, position: options[:offset] || :top }
|
229
234
|
command(:before_click, name, keys, offset)
|
230
|
-
node.click(mode: mode, keys: keys, offset: offset)
|
235
|
+
node.click(mode: mode, keys: keys, offset: offset, delay: delay)
|
231
236
|
end
|
232
237
|
|
233
238
|
def filter_text(text)
|
@@ -1,13 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "forwardable"
|
4
|
+
|
3
5
|
module Capybara::Cuprite
|
4
|
-
|
6
|
+
class Page < Ferrum::Page
|
5
7
|
MODAL_WAIT = ENV.fetch("CUPRITE_MODAL_WAIT", 0.05).to_f
|
8
|
+
TRIGGER_CLICK_WAIT = ENV.fetch("CUPRITE_TRIGGER_CLICK_WAIT", 0.1).to_f
|
9
|
+
|
10
|
+
extend Forwardable
|
11
|
+
delegate %i[at_css at_xpath css xpath
|
12
|
+
current_url current_title body execution_id
|
13
|
+
evaluate evaluate_on evaluate_async execute] => :active_frame
|
6
14
|
|
7
15
|
def initialize(*args)
|
8
|
-
|
16
|
+
@frame_stack = []
|
9
17
|
@accept_modal = []
|
10
18
|
@modal_messages = []
|
19
|
+
super
|
11
20
|
end
|
12
21
|
|
13
22
|
def set(node, value)
|
@@ -21,7 +30,7 @@ module Capybara::Cuprite
|
|
21
30
|
|
22
31
|
def trigger(node, event)
|
23
32
|
options = {}
|
24
|
-
options.merge!(wait:
|
33
|
+
options.merge!(wait: TRIGGER_CLICK_WAIT) if event.to_s == "click" && TRIGGER_CLICK_WAIT > 0
|
25
34
|
evaluate_on(node: node, expression: %(_cuprite.trigger(this, "#{event}")), **options)
|
26
35
|
end
|
27
36
|
|
@@ -59,7 +68,7 @@ module Capybara::Cuprite
|
|
59
68
|
|
60
69
|
def find_modal(options)
|
61
70
|
start = Ferrum.monotonic_time
|
62
|
-
timeout = options.fetch(:wait)
|
71
|
+
timeout = options.fetch(:wait, browser.timeout)
|
63
72
|
expect_text = options[:text]
|
64
73
|
expect_regexp = expect_text.is_a?(Regexp) ? expect_text : Regexp.escape(expect_text.to_s)
|
65
74
|
not_found_msg = "Unable to find modal dialog"
|
@@ -85,7 +94,12 @@ module Capybara::Cuprite
|
|
85
94
|
|
86
95
|
def before_click(node, name, keys = [], offset = {})
|
87
96
|
evaluate_on(node: node, expression: "_cuprite.scrollIntoViewport(this)")
|
88
|
-
|
97
|
+
|
98
|
+
# If offset is given it may go outside of the element and likely error
|
99
|
+
# will be raised that we detected another element at this position.
|
100
|
+
return true if offset[:x] || offset[:y]
|
101
|
+
|
102
|
+
x, y = find_position(node, **offset)
|
89
103
|
evaluate_on(node: node, expression: "_cuprite.mouseEventTest(this, '#{name}', #{x}, #{y})")
|
90
104
|
true
|
91
105
|
rescue Ferrum::JavaScriptError => e
|
@@ -104,6 +118,14 @@ module Capybara::Cuprite
|
|
104
118
|
end
|
105
119
|
end
|
106
120
|
|
121
|
+
def frame_name
|
122
|
+
evaluate("window.name")
|
123
|
+
end
|
124
|
+
|
125
|
+
def title
|
126
|
+
active_frame.current_title
|
127
|
+
end
|
128
|
+
|
107
129
|
private
|
108
130
|
|
109
131
|
def prepare_page
|
@@ -115,15 +137,15 @@ module Capybara::Cuprite
|
|
115
137
|
on(:request) do |request, index, total|
|
116
138
|
if @browser.url_blacklist && !@browser.url_blacklist.empty?
|
117
139
|
if @browser.url_blacklist.any? { |r| request.match?(r) }
|
118
|
-
request.abort and
|
140
|
+
request.abort and next
|
119
141
|
else
|
120
|
-
request.continue and
|
142
|
+
request.continue and next
|
121
143
|
end
|
122
144
|
elsif @browser.url_whitelist && !@browser.url_whitelist.empty?
|
123
145
|
if @browser.url_whitelist.any? { |r| request.match?(r) }
|
124
|
-
request.continue and
|
146
|
+
request.continue and next
|
125
147
|
else
|
126
|
-
request.abort and
|
148
|
+
request.abort and next
|
127
149
|
end
|
128
150
|
elsif index + 1 < total
|
129
151
|
# There are other callbacks that may handle this request
|
@@ -144,7 +166,11 @@ module Capybara::Cuprite
|
|
144
166
|
options.merge!(promptText: response) if response
|
145
167
|
command("Page.handleJavaScriptDialog", **options)
|
146
168
|
else
|
147
|
-
|
169
|
+
with_text = params["message"] ? "with text `#{params["message"]}` " : ""
|
170
|
+
warn "Modal window #{with_text}has been opened, but you didn't wrap "\
|
171
|
+
"your code into (`accept_prompt` | `dismiss_prompt` | "\
|
172
|
+
"`accept_confirm` | `dismiss_confirm` | `accept_alert`), "\
|
173
|
+
"accepting by default"
|
148
174
|
options = { accept: true }
|
149
175
|
response = params["defaultPrompt"]
|
150
176
|
options.merge!(promptText: response) if response
|
@@ -153,8 +179,8 @@ module Capybara::Cuprite
|
|
153
179
|
end
|
154
180
|
end
|
155
181
|
|
156
|
-
def find_position(node,
|
157
|
-
x, y = node.find_position(
|
182
|
+
def find_position(node, **options)
|
183
|
+
x, y = node.find_position(**options)
|
158
184
|
rescue Ferrum::BrowserError => e
|
159
185
|
if e.message == "Could not compute content quads."
|
160
186
|
raise MouseEventFailed.new("MouseEventFailed: click, none, 0, 0")
|
@@ -162,5 +188,13 @@ module Capybara::Cuprite
|
|
162
188
|
raise
|
163
189
|
end
|
164
190
|
end
|
191
|
+
|
192
|
+
def active_frame
|
193
|
+
if @frame_stack.empty?
|
194
|
+
main_frame
|
195
|
+
else
|
196
|
+
@frames[@frame_stack.last]
|
197
|
+
end
|
198
|
+
end
|
165
199
|
end
|
166
200
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cuprite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.12'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dmitry Vorotilin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: capybara
|
@@ -34,16 +34,16 @@ dependencies:
|
|
34
34
|
name: ferrum
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
|
-
- - "
|
37
|
+
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: 0.
|
39
|
+
version: 0.10.0
|
40
40
|
type: :runtime
|
41
41
|
prerelease: false
|
42
42
|
version_requirements: !ruby/object:Gem::Requirement
|
43
43
|
requirements:
|
44
|
-
- - "
|
44
|
+
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: 0.
|
46
|
+
version: 0.10.0
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: image_size
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -189,7 +189,7 @@ files:
|
|
189
189
|
- lib/capybara/cuprite/node.rb
|
190
190
|
- lib/capybara/cuprite/page.rb
|
191
191
|
- lib/capybara/cuprite/version.rb
|
192
|
-
homepage: https://github.com/
|
192
|
+
homepage: https://github.com/rubycdp/cuprite
|
193
193
|
licenses:
|
194
194
|
- MIT
|
195
195
|
metadata: {}
|
@@ -208,7 +208,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
208
208
|
- !ruby/object:Gem::Version
|
209
209
|
version: '0'
|
210
210
|
requirements: []
|
211
|
-
rubygems_version: 3.
|
211
|
+
rubygems_version: 3.1.2
|
212
212
|
signing_key:
|
213
213
|
specification_version: 4
|
214
214
|
summary: Headless Chrome driver for Capybara
|