cuprite 0.7.0 → 0.11
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 +107 -109
- data/lib/capybara/cuprite.rb +0 -6
- data/lib/capybara/cuprite/browser.rb +61 -6
- data/lib/capybara/cuprite/driver.rb +53 -15
- data/lib/capybara/cuprite/javascripts/index.js +1 -1
- data/lib/capybara/cuprite/node.rb +11 -8
- data/lib/capybara/cuprite/page.rb +49 -15
- 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: 8ec561634fc66a075a61f4221aef5276b57e447c0810865fdc6562a41842ce42
|
4
|
+
data.tar.gz: af475a5d00fe0f32dc8ddf643bd7bc0cfb80409ae8ec4cb627a976cc9b1b753c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f460096e3ce6e8613be751bc237d7efde7a3c6a8b63ba54283b677d6f7a6d7c25bb1182bb38dc3762a42e7eb3c8f19ca4678f7d82591c79b6849606e021dd501
|
7
|
+
data.tar.gz: 95b4633594b2581bd3e4259b110652668b75362fccca572c43b0020e11e02701f077fed5be558abfa1960955e9697c79961b0408c461020eade8c609acb9e731
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,26 +1,27 @@
|
|
1
|
-
# Cuprite - Headless Chrome driver for Capybara
|
1
|
+
# Cuprite - Headless Chrome driver for Capybara
|
2
2
|
|
3
|
-
[](https://circleci.com/gh/rubycdp/cuprite)
|
4
4
|
|
5
|
-
Cuprite is a pure Ruby driver (read as _no_
|
6
|
-
|
7
|
-
you to run
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
Cuprite is a pure Ruby driver (read as _no_ Selenium/WebDriver/ChromeDriver
|
6
|
+
dependency) for [Capybara](https://github.com/teamcapybara/capybara). It allows
|
7
|
+
you to run Capybara tests on a headless Chrome or Chromium. Under the hood it
|
8
|
+
uses [Ferrum](https://github.com/rubycdp/ferrum#index) which is high-level API
|
9
|
+
to the browser by CDP protocol. The design of the driver is as close to
|
10
|
+
[Poltergeist](https://github.com/teampoltergeist/poltergeist) as possible though
|
11
|
+
it's not a goal.
|
11
12
|
|
12
|
-
|
13
|
-
|
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.
|
13
|
+
[Cuprite](https://evrone.com/cuprite) designed & supported by [Evrone](https://evrone.com/)
|
14
|
+
What else we build [with Ruby](https://evrone.com/ruby)
|
17
15
|
|
18
|
-
## Install ##
|
19
16
|
|
20
|
-
|
17
|
+
## Install
|
18
|
+
|
19
|
+
Add this to your `Gemfile` and run `bundle install`.
|
21
20
|
|
22
21
|
``` ruby
|
23
|
-
|
22
|
+
group :test do
|
23
|
+
gem "cuprite"
|
24
|
+
end
|
24
25
|
```
|
25
26
|
|
26
27
|
In your test setup add:
|
@@ -33,21 +34,31 @@ Capybara.register_driver(:cuprite) do |app|
|
|
33
34
|
end
|
34
35
|
```
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
if you use `Docker` don't forget to pass `no-sandbox` option:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
Capybara::Cuprite::Driver.new(app, browser_options: { 'no-sandbox': nil })
|
41
|
+
```
|
42
|
+
|
43
|
+
Since Cuprite uses [Ferrum](https://github.com/rubycdp/ferrum#examples) there
|
44
|
+
are many useful methods you can call even using this driver:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
browser = page.driver.browser
|
48
|
+
browser.mouse.move(x: 123, y: 456).down.up
|
49
|
+
```
|
50
|
+
|
51
|
+
If you already have tests on Poltergeist then it should simply work, for
|
52
|
+
Selenium you better check your code for `manage` calls because it works
|
53
|
+
differently in Cuprite, see the documentation below.
|
39
54
|
|
40
|
-
## Install Chrome ##
|
41
55
|
|
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
|
56
|
+
## Customization
|
47
57
|
|
48
|
-
|
58
|
+
See the full list of options for
|
59
|
+
[Ferrum](https://github.com/rubycdp/ferrum#customization).
|
49
60
|
|
50
|
-
You can
|
61
|
+
You can pass options with the following code in your test setup:
|
51
62
|
|
52
63
|
``` ruby
|
53
64
|
Capybara.register_driver(:cuprite) do |app|
|
@@ -55,47 +66,52 @@ Capybara.register_driver(:cuprite) do |app|
|
|
55
66
|
end
|
56
67
|
```
|
57
68
|
|
58
|
-
|
69
|
+
`Cuprite`-specific options are:
|
59
70
|
|
60
|
-
|
71
|
+
* options `Hash`
|
72
|
+
* `:url_blacklist` (Array) - array of strings to match against requested URLs
|
73
|
+
* `:url_whitelist` (Array) - array of strings to match against requested URLs
|
74
|
+
|
75
|
+
|
76
|
+
## Debugging
|
77
|
+
|
78
|
+
If you pass `inspector` option, remote debugging will be enabled if you run
|
79
|
+
tests with `INSPECTOR=true`. Then you can put `page.driver.debug` or
|
80
|
+
`page.driver.debug(binding)` in your test to pause it. This will launch the
|
81
|
+
browser where you can inspect the content.
|
61
82
|
|
62
83
|
```ruby
|
63
|
-
Capybara
|
84
|
+
Capybara.register_driver :cuprite do |app|
|
85
|
+
Capybara::Cuprite::Driver.new(app, inspector: ENV['INSPECTOR'])
|
86
|
+
end
|
64
87
|
```
|
65
88
|
|
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
|
89
|
+
then somewhere in the test:
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
it "does something useful" do
|
93
|
+
visit root_path
|
94
|
+
|
95
|
+
fill_in "field", with: "value"
|
96
|
+
page.driver.debug(binding)
|
97
|
+
|
98
|
+
expect(page).to have_content("value")
|
99
|
+
end
|
100
|
+
```
|
101
|
+
|
102
|
+
In the middle of the execution Chrome will open a new tab where you can inspect
|
103
|
+
the content and also if you passed `binding` an `irb` or `pry` console will be
|
104
|
+
opened where you can further experiment with the test.
|
91
105
|
|
92
|
-
|
106
|
+
|
107
|
+
## Clicking/Scrolling
|
93
108
|
|
94
109
|
* `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.
|
110
|
+
* `page.driver.scroll_to(left, top)` Scroll to a given position.
|
111
|
+
* `element.send_keys(*keys)` Send keys to a given node.
|
112
|
+
|
97
113
|
|
98
|
-
|
114
|
+
## Request headers
|
99
115
|
|
100
116
|
Manipulate HTTP request headers like a boss:
|
101
117
|
|
@@ -111,11 +127,11 @@ Notice that `headers=` will overwrite already set headers. You should use
|
|
111
127
|
subsequent HTTP requests (including requests for assets, AJAX, etc). They will
|
112
128
|
be automatically cleared at the end of the test.
|
113
129
|
|
114
|
-
### Network traffic ###
|
115
130
|
|
116
|
-
|
117
|
-
loaded) on the current page. This returns an array of request objects.
|
131
|
+
## Network traffic
|
118
132
|
|
133
|
+
* `page.driver.network_traffic` Inspect network traffic (loaded resources) on
|
134
|
+
the current page. This returns an array of request objects.
|
119
135
|
|
120
136
|
```ruby
|
121
137
|
page.driver.network_traffic # => [Request, ...]
|
@@ -123,11 +139,30 @@ request = page.driver.network_traffic.first
|
|
123
139
|
request.response
|
124
140
|
```
|
125
141
|
|
142
|
+
* `page.driver.wait_for_network_idle` Natively waits for network idle and if
|
143
|
+
there are no active connections returns or raises `TimeoutError` error. Accepts
|
144
|
+
the same options as
|
145
|
+
[`wait_for_idle`](https://github.com/rubycdp/ferrum#wait_for_idleoptions)
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
page.driver.wait_for_network_idle
|
149
|
+
page.driver.refresh
|
150
|
+
```
|
151
|
+
|
126
152
|
Please note that network traffic is not cleared when you visit new page. You can
|
127
153
|
manually clear the network traffic by calling `page.driver.clear_network_traffic`
|
128
154
|
or `page.driver.reset`
|
129
155
|
|
130
|
-
|
156
|
+
* `page.driver.wait_for_reload` unlike `wait_for_network_idle` will wait until
|
157
|
+
the whole page is reloaded or raise a timeout error. It's useful when you know
|
158
|
+
that for example after clicking autocomplete suggestion you expect page to be
|
159
|
+
reloaded, you have a few choices - put sleep or wait for network idle, but both
|
160
|
+
are bad. Sleep makes you wait longer or less than needed, network idle can
|
161
|
+
return earlier even before the whole page is started to reload. Here's the
|
162
|
+
rescue.
|
163
|
+
|
164
|
+
|
165
|
+
## Manipulating cookies
|
131
166
|
|
132
167
|
The following methods are used to inspect and manipulate cookies:
|
133
168
|
|
@@ -142,18 +177,22 @@ The following methods are used to inspect and manipulate cookies:
|
|
142
177
|
* `page.driver.remove_cookie(name)` - remove a cookie
|
143
178
|
* `page.driver.clear_cookies` - clear all cookies
|
144
179
|
|
145
|
-
|
180
|
+
|
181
|
+
## Screenshot
|
146
182
|
|
147
183
|
Besides capybara screenshot method you can get image as Base64:
|
148
184
|
|
149
185
|
* `page.driver.render_base64(format, options)`
|
150
186
|
|
151
|
-
|
187
|
+
|
188
|
+
## Authorization
|
152
189
|
|
153
190
|
* `page.driver.basic_authorize(user, password)`
|
154
191
|
* `page.driver.set_proxy(ip, port, type, user, password)`
|
155
192
|
|
156
|
-
|
193
|
+
|
194
|
+
## URL Blacklisting & Whitelisting
|
195
|
+
|
157
196
|
Cuprite supports URL blacklisting, which allows you to prevent scripts from
|
158
197
|
running on designated domains:
|
159
198
|
|
@@ -161,8 +200,8 @@ running on designated domains:
|
|
161
200
|
page.driver.browser.url_blacklist = ["http://www.example.com"]
|
162
201
|
```
|
163
202
|
|
164
|
-
and also URL whitelisting, which allows scripts to only run
|
165
|
-
|
203
|
+
and also URL whitelisting, which allows scripts to only run on designated
|
204
|
+
domains:
|
166
205
|
|
167
206
|
```ruby
|
168
207
|
page.driver.browser.url_whitelist = ["http://www.example.com"]
|
@@ -171,44 +210,3 @@ page.driver.browser.url_whitelist = ["http://www.example.com"]
|
|
171
210
|
If you are experiencing slower run times, consider creating a URL whitelist of
|
172
211
|
domains that are essential or a blacklist of domains that are not essential,
|
173
212
|
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 can register this debugger driver with a different name and set it
|
183
|
-
as the current javascript driver. By example, in your helper file:
|
184
|
-
|
185
|
-
```ruby
|
186
|
-
Capybara.register_driver :cuprite_debug do |app|
|
187
|
-
Capybara::Cuprite::Driver.new(app, inspector: true)
|
188
|
-
end
|
189
|
-
|
190
|
-
Capybara.javascript_driver = :cuprite_debug
|
191
|
-
```
|
192
|
-
|
193
|
-
## License ##
|
194
|
-
|
195
|
-
Copyright 2018-2019 Machinio
|
196
|
-
|
197
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
198
|
-
a copy of this software and associated documentation files (the
|
199
|
-
"Software"), to deal in the Software without restriction, including
|
200
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
201
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
202
|
-
permit persons to whom the Software is furnished to do so, subject to
|
203
|
-
the following conditions:
|
204
|
-
|
205
|
-
The above copyright notice and this permission notice shall be
|
206
|
-
included in all copies or substantial portions of the Software.
|
207
|
-
|
208
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
209
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
210
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
211
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
212
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
213
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
214
|
-
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,16 +18,32 @@ 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)
|
25
40
|
@url_whitelist = prepare_wildcards(patterns)
|
26
|
-
page.
|
41
|
+
page.network.intercept if @client && !@url_whitelist.empty?
|
27
42
|
end
|
28
43
|
|
29
44
|
def url_blacklist=(patterns)
|
30
45
|
@url_blacklist = prepare_wildcards(patterns)
|
31
|
-
page.
|
46
|
+
page.network.intercept if @client && !@url_blacklist.empty?
|
32
47
|
end
|
33
48
|
|
34
49
|
def visit(*args)
|
@@ -36,7 +51,7 @@ module Capybara::Cuprite
|
|
36
51
|
end
|
37
52
|
|
38
53
|
def status_code
|
39
|
-
status
|
54
|
+
network.status
|
40
55
|
end
|
41
56
|
|
42
57
|
def find(method, selector)
|
@@ -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!
|
@@ -216,11 +221,21 @@ module Capybara::Cuprite
|
|
216
221
|
end
|
217
222
|
|
218
223
|
def network_traffic(type = nil)
|
219
|
-
browser.
|
224
|
+
traffic = browser.network.traffic
|
225
|
+
|
226
|
+
case type.to_s
|
227
|
+
when "all"
|
228
|
+
traffic
|
229
|
+
when "blocked"
|
230
|
+
traffic.select(&:blocked?)
|
231
|
+
else
|
232
|
+
# when request isn't blocked
|
233
|
+
traffic.reject(&:blocked?)
|
234
|
+
end
|
220
235
|
end
|
221
236
|
|
222
237
|
def clear_network_traffic
|
223
|
-
browser.
|
238
|
+
browser.network.clear(:traffic)
|
224
239
|
end
|
225
240
|
|
226
241
|
def set_proxy(ip, port, type = nil, user = nil, password = nil, bypass = nil)
|
@@ -228,7 +243,7 @@ module Capybara::Cuprite
|
|
228
243
|
server = type ? "#{type}=#{ip}:#{port}" : "#{ip}:#{port}"
|
229
244
|
@options[:browser_options].merge!("proxy-server" => server)
|
230
245
|
@options[:browser_options].merge!("proxy-bypass-list" => bypass) if bypass
|
231
|
-
browser.authorize(type: :proxy, user: user, password: password)
|
246
|
+
browser.network.authorize(type: :proxy, user: user, password: password)
|
232
247
|
end
|
233
248
|
|
234
249
|
def headers
|
@@ -248,7 +263,7 @@ module Capybara::Cuprite
|
|
248
263
|
end
|
249
264
|
|
250
265
|
def response_headers
|
251
|
-
browser.
|
266
|
+
browser.network.response&.headers
|
252
267
|
end
|
253
268
|
|
254
269
|
def cookies
|
@@ -272,22 +287,39 @@ module Capybara::Cuprite
|
|
272
287
|
browser.cookies.clear
|
273
288
|
end
|
274
289
|
|
290
|
+
def wait_for_network_idle(**options)
|
291
|
+
browser.network.wait_for_idle(**options)
|
292
|
+
end
|
293
|
+
|
275
294
|
def clear_memory_cache
|
276
|
-
browser.
|
295
|
+
browser.network.clear(:cache)
|
277
296
|
end
|
278
297
|
|
279
298
|
def basic_authorize(user, password)
|
280
|
-
browser.authorize(user: user, password: password)
|
299
|
+
browser.network.authorize(user: user, password: password)
|
281
300
|
end
|
282
301
|
alias_method :authorize, :basic_authorize
|
283
302
|
|
284
|
-
def
|
303
|
+
def debug_url
|
304
|
+
"http://#{browser.process.host}:#{browser.process.port}"
|
305
|
+
end
|
306
|
+
|
307
|
+
def debug(binding = nil)
|
285
308
|
if @options[:inspector]
|
286
|
-
Process.spawn(browser.process.path,
|
287
|
-
|
309
|
+
Process.spawn(browser.process.path, debug_url)
|
310
|
+
|
311
|
+
if binding&.respond_to?(:pry)
|
312
|
+
Pry.start(binding)
|
313
|
+
elsif binding&.respond_to?(:irb)
|
314
|
+
binding.irb
|
315
|
+
else
|
316
|
+
pause
|
317
|
+
end
|
288
318
|
else
|
289
319
|
raise Error, "To use the remote debugging, you have to launch " \
|
290
|
-
"the driver with `inspector:
|
320
|
+
"the driver with `inspector: ENV['INSPECTOR']` " \
|
321
|
+
"configuration option and run your test suite passing " \
|
322
|
+
"env variable"
|
291
323
|
end
|
292
324
|
end
|
293
325
|
|
@@ -298,7 +330,7 @@ module Capybara::Cuprite
|
|
298
330
|
# In jRuby - STDIN returns immediately from select
|
299
331
|
# see https://github.com/jruby/jruby/issues/1783
|
300
332
|
read, write = IO.pipe
|
301
|
-
Thread.new { IO.copy_stream(STDIN, write); write.close }
|
333
|
+
thread = Thread.new { IO.copy_stream(STDIN, write); write.close }
|
302
334
|
|
303
335
|
STDERR.puts "Cuprite execution paused. Press enter (or run 'kill -CONT #{Process.pid}') to continue."
|
304
336
|
|
@@ -314,6 +346,8 @@ module Capybara::Cuprite
|
|
314
346
|
end
|
315
347
|
end
|
316
348
|
ensure
|
349
|
+
thread.kill
|
350
|
+
read.close
|
317
351
|
trap("SIGCONT", old_trap) # Restore the previous signal handler, if there was one.
|
318
352
|
STDERR.puts "Continuing"
|
319
353
|
end
|
@@ -340,6 +374,10 @@ module Capybara::Cuprite
|
|
340
374
|
browser.refresh
|
341
375
|
end
|
342
376
|
|
377
|
+
def wait_for_reload(*args)
|
378
|
+
browser.wait_for_reload(*args)
|
379
|
+
end
|
380
|
+
|
343
381
|
def accept_modal(type, options = {})
|
344
382
|
case type
|
345
383
|
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") {
|
@@ -144,16 +144,16 @@ module Capybara::Cuprite
|
|
144
144
|
command(:disabled?)
|
145
145
|
end
|
146
146
|
|
147
|
-
def click(keys = [],
|
148
|
-
prepare_and_click(:left, __method__, keys,
|
147
|
+
def click(keys = [], **options)
|
148
|
+
prepare_and_click(:left, __method__, keys, options)
|
149
149
|
end
|
150
150
|
|
151
|
-
def right_click(keys = [],
|
152
|
-
prepare_and_click(:right, __method__, keys,
|
151
|
+
def right_click(keys = [], **options)
|
152
|
+
prepare_and_click(:right, __method__, keys, options)
|
153
153
|
end
|
154
154
|
|
155
|
-
def double_click(keys = [],
|
156
|
-
prepare_and_click(:double, __method__, keys,
|
155
|
+
def double_click(keys = [], **options)
|
156
|
+
prepare_and_click(:double, __method__, keys, options)
|
157
157
|
end
|
158
158
|
|
159
159
|
def hover
|
@@ -225,9 +225,12 @@ module Capybara::Cuprite
|
|
225
225
|
|
226
226
|
private
|
227
227
|
|
228
|
-
def prepare_and_click(mode, name, keys,
|
228
|
+
def prepare_and_click(mode, name, keys, options)
|
229
|
+
delay = options[:delay].to_i
|
230
|
+
x, y = options.values_at(:x, :y)
|
231
|
+
offset = { x: x, y: y, position: options[:offset] || :top }
|
229
232
|
command(:before_click, name, keys, offset)
|
230
|
-
node.click(mode: mode, keys: keys, offset: offset)
|
233
|
+
node.click(mode: mode, keys: keys, offset: offset, delay: delay)
|
231
234
|
end
|
232
235
|
|
233
236
|
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,26 +118,34 @@ 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
|
110
132
|
super
|
111
133
|
|
112
|
-
|
134
|
+
network.intercept if !Array(@browser.url_whitelist).empty? ||
|
113
135
|
!Array(@browser.url_blacklist).empty?
|
114
136
|
|
115
|
-
on(:
|
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
|
@@ -134,7 +156,7 @@ module Capybara::Cuprite
|
|
134
156
|
end
|
135
157
|
end
|
136
158
|
|
137
|
-
|
159
|
+
on("Page.javascriptDialogOpening") do |params|
|
138
160
|
accept_modal = @accept_modal.last
|
139
161
|
if accept_modal == true || accept_modal == false
|
140
162
|
@accept_modal.pop
|
@@ -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.11'
|
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: 2020-07-29 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.9.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.9.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
|