cuprite 0.12 → 0.15.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/LICENSE +1 -1
- data/README.md +14 -12
- data/lib/capybara/cuprite/browser.rb +196 -155
- data/lib/capybara/cuprite/cookie.rb +32 -32
- data/lib/capybara/cuprite/driver.rb +335 -363
- data/lib/capybara/cuprite/errors.rb +9 -8
- data/lib/capybara/cuprite/javascripts/index.js +47 -27
- data/lib/capybara/cuprite/node.rb +218 -224
- data/lib/capybara/cuprite/options.rb +14 -0
- data/lib/capybara/cuprite/page.rb +148 -154
- data/lib/capybara/cuprite/version.rb +1 -1
- data/lib/capybara/cuprite.rb +3 -0
- metadata +19 -145
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78b7dde58d02147e2985473da0a7527d338ff086e4a4aaf0fd9302e5faf3ee11
|
4
|
+
data.tar.gz: 777726c6161975950df923a305b0bdb9da54b5531b7a5339ed0d4beaf264beb6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 04e88a47d9d2d2df68ad11dca8224f6d24001340b1743f538e6fdb64e818381037da7987b9db25284336b469c986d9d9e883192743749c4c6cad6c43adc160a1
|
7
|
+
data.tar.gz: a059dae9790af13c8a56999d6d76d5920e863e217b346a721a4051f0cc786e295a35c0e574c00e425b87e2d0c7a8b140c991183ab3246d1678566318745c9ecf
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -4,12 +4,7 @@ Cuprite is a pure Ruby driver (read as _no_ Selenium/WebDriver/ChromeDriver
|
|
4
4
|
dependency) for [Capybara](https://github.com/teamcapybara/capybara). It allows
|
5
5
|
you to run Capybara tests on a headless Chrome or Chromium. Under the hood it
|
6
6
|
uses [Ferrum](https://github.com/rubycdp/ferrum#index) which is high-level API
|
7
|
-
to the browser by CDP protocol.
|
8
|
-
[Poltergeist](https://github.com/teampoltergeist/poltergeist) as possible though
|
9
|
-
it's not a goal.
|
10
|
-
|
11
|
-
[Cuprite](https://evrone.com/cuprite) designed & supported by [Evrone](https://evrone.com/)
|
12
|
-
What else we build [with Ruby](https://evrone.com/ruby)
|
7
|
+
to the browser by CDP protocol.
|
13
8
|
|
14
9
|
|
15
10
|
## Install
|
@@ -46,8 +41,7 @@ browser = page.driver.browser
|
|
46
41
|
browser.mouse.move(x: 123, y: 456).down.up
|
47
42
|
```
|
48
43
|
|
49
|
-
|
50
|
-
Selenium you better check your code for `manage` calls because it works
|
44
|
+
For Selenium you better check your code for `manage` calls because it works
|
51
45
|
differently in Cuprite, see the documentation below.
|
52
46
|
|
53
47
|
|
@@ -67,8 +61,8 @@ end
|
|
67
61
|
`Cuprite`-specific options are:
|
68
62
|
|
69
63
|
* options `Hash`
|
70
|
-
* `:url_blacklist` (Array) - array of
|
71
|
-
* `:url_whitelist` (Array) - array of
|
64
|
+
* `:url_blacklist` (Array) - array of regexes to match against requested URLs
|
65
|
+
* `:url_whitelist` (Array) - array of regexes to match against requested URLs
|
72
66
|
|
73
67
|
|
74
68
|
## Debugging
|
@@ -186,6 +180,9 @@ Besides capybara screenshot method you can get image as Base64:
|
|
186
180
|
## Authorization
|
187
181
|
|
188
182
|
* `page.driver.basic_authorize(user, password)`
|
183
|
+
|
184
|
+
## Proxy
|
185
|
+
|
189
186
|
* `page.driver.set_proxy(ip, port, type, user, password)`
|
190
187
|
|
191
188
|
|
@@ -195,16 +192,21 @@ Cuprite supports URL blacklisting, which allows you to prevent scripts from
|
|
195
192
|
running on designated domains:
|
196
193
|
|
197
194
|
```ruby
|
198
|
-
page.driver.browser.url_blacklist =
|
195
|
+
page.driver.browser.url_blacklist = %r{http://www.example.com}
|
199
196
|
```
|
200
197
|
|
201
198
|
and also URL whitelisting, which allows scripts to only run on designated
|
202
199
|
domains:
|
203
200
|
|
204
201
|
```ruby
|
205
|
-
page.driver.browser.url_whitelist =
|
202
|
+
page.driver.browser.url_whitelist = %r{http://www.example.com}
|
206
203
|
```
|
207
204
|
|
208
205
|
If you are experiencing slower run times, consider creating a URL whitelist of
|
209
206
|
domains that are essential or a blacklist of domains that are not essential,
|
210
207
|
such as ad networks or analytics, to your testing environment.
|
208
|
+
|
209
|
+
## License
|
210
|
+
|
211
|
+
The gem is available as open source under the terms of the
|
212
|
+
[MIT License](https://opensource.org/licenses/MIT).
|
@@ -2,213 +2,254 @@
|
|
2
2
|
|
3
3
|
require "forwardable"
|
4
4
|
|
5
|
-
module Capybara
|
6
|
-
|
7
|
-
|
5
|
+
module Capybara
|
6
|
+
module Cuprite
|
7
|
+
class Browser < Ferrum::Browser
|
8
|
+
extend Forwardable
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
delegate %i[send_keys select set hover trigger before_click switch_to_frame
|
11
|
+
find_modal accept_confirm dismiss_confirm accept_prompt
|
12
|
+
dismiss_prompt reset_modals] => :page
|
12
13
|
|
13
|
-
|
14
|
+
def initialize(options = nil)
|
15
|
+
super
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
self.url_blacklist = options[:url_blacklist]
|
18
|
-
self.url_whitelist = options[:url_whitelist]
|
17
|
+
@options.url_blacklist = prepare_wildcards(options&.dig(:url_blacklist))
|
18
|
+
@options.url_whitelist = prepare_wildcards(options&.dig(:url_whitelist))
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
end
|
20
|
+
@page = nil
|
21
|
+
end
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
def command(...)
|
24
|
+
super
|
25
|
+
rescue Ferrum::DeadBrowserError
|
26
|
+
restart
|
27
|
+
raise
|
28
|
+
end
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
@page = attach_page
|
32
|
-
end
|
30
|
+
def page
|
31
|
+
raise Ferrum::NoSuchPageError if @page&.closed?
|
33
32
|
|
34
|
-
|
35
|
-
|
36
|
-
@page = false
|
37
|
-
end
|
33
|
+
@page ||= attach_page
|
34
|
+
end
|
38
35
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
36
|
+
def reset
|
37
|
+
super
|
38
|
+
@options.reset_window_size
|
39
|
+
@page = nil
|
40
|
+
end
|
43
41
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
42
|
+
def quit
|
43
|
+
super
|
44
|
+
@page = nil
|
45
|
+
end
|
48
46
|
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
def resize(**options)
|
48
|
+
@options.window_size = [options[:width], options[:height]]
|
49
|
+
super
|
50
|
+
end
|
52
51
|
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
def url_whitelist
|
53
|
+
@options.url_whitelist
|
54
|
+
end
|
55
|
+
alias url_allowlist url_whitelist
|
56
56
|
|
57
|
-
|
58
|
-
|
59
|
-
|
57
|
+
def url_whitelist=(patterns)
|
58
|
+
@options.url_whitelist = prepare_wildcards(patterns)
|
59
|
+
page.network.whitelist = @options.url_whitelist if @client && @options.url_whitelist.any?
|
60
|
+
end
|
61
|
+
alias url_allowlist= url_whitelist=
|
60
62
|
|
61
|
-
|
62
|
-
|
63
|
-
|
63
|
+
def url_blacklist
|
64
|
+
@options.url_blacklist
|
65
|
+
end
|
66
|
+
alias url_blocklist url_blacklist
|
64
67
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
68
|
+
def url_blacklist=(patterns)
|
69
|
+
@options.url_blacklist = prepare_wildcards(patterns)
|
70
|
+
page.network.blacklist = @options.url_blacklist if @client && @options.url_blacklist.any?
|
71
|
+
end
|
72
|
+
alias url_blocklist= url_blacklist=
|
70
73
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
+
def visit(*args)
|
75
|
+
goto(*args)
|
76
|
+
end
|
74
77
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
+
def status_code
|
79
|
+
network.status
|
80
|
+
end
|
78
81
|
|
79
|
-
|
80
|
-
|
82
|
+
def find(method, selector)
|
83
|
+
find_all(method, selector)
|
84
|
+
end
|
81
85
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
86
|
+
def property(node, name)
|
87
|
+
node.property(name)
|
88
|
+
end
|
89
|
+
|
90
|
+
def find_within(node, method, selector)
|
91
|
+
resolved = page.command("DOM.resolveNode", nodeId: node.node_id)
|
92
|
+
object_id = resolved.dig("object", "objectId")
|
93
|
+
find_all(method, selector, { "objectId" => object_id })
|
94
|
+
end
|
95
|
+
|
96
|
+
def window_handle
|
97
|
+
page.target_id
|
88
98
|
end
|
89
99
|
|
90
|
-
|
100
|
+
def window_handles
|
101
|
+
targets.keys
|
102
|
+
end
|
103
|
+
|
104
|
+
def within_window(locator = nil)
|
105
|
+
original = window_handle
|
106
|
+
raise Ferrum::NoSuchPageError unless window_handles.include?(locator)
|
107
|
+
|
91
108
|
switch_to_window(locator)
|
92
109
|
yield
|
93
|
-
|
94
|
-
|
110
|
+
ensure
|
111
|
+
switch_to_window(original)
|
95
112
|
end
|
96
|
-
ensure
|
97
|
-
switch_to_window(original)
|
98
|
-
end
|
99
113
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
@page = attach_page(target.id)
|
104
|
-
end
|
114
|
+
def switch_to_window(target_id)
|
115
|
+
target = targets[target_id]
|
116
|
+
raise Ferrum::NoSuchPageError unless target
|
105
117
|
|
106
|
-
|
107
|
-
|
108
|
-
raise Ferrum::NoSuchPageError unless target
|
109
|
-
@page = nil if @page.target_id == target.id
|
110
|
-
target.page.close
|
111
|
-
end
|
118
|
+
@page = attach_page(target.id)
|
119
|
+
end
|
112
120
|
|
113
|
-
|
114
|
-
|
115
|
-
|
121
|
+
def close_window(target_id)
|
122
|
+
target = targets[target_id]
|
123
|
+
raise Ferrum::NoSuchPageError unless target
|
116
124
|
|
117
|
-
|
118
|
-
|
119
|
-
|
125
|
+
@page = ClosedPage.new if @page.target_id == target.id
|
126
|
+
target.page.close
|
127
|
+
targets.delete(target_id) # page.close is async, delete target asap
|
128
|
+
end
|
120
129
|
|
121
|
-
|
122
|
-
|
123
|
-
|
130
|
+
def active_element
|
131
|
+
evaluate("document.activeElement")
|
132
|
+
end
|
124
133
|
|
125
|
-
|
126
|
-
|
127
|
-
|
134
|
+
def browser_error
|
135
|
+
evaluate("_cuprite.browserError()")
|
136
|
+
end
|
128
137
|
|
129
|
-
|
130
|
-
|
131
|
-
|
138
|
+
def source
|
139
|
+
raise NotImplementedError
|
140
|
+
end
|
132
141
|
|
133
|
-
|
134
|
-
|
135
|
-
|
142
|
+
def drag(node, other, steps, delay = nil)
|
143
|
+
x1, y1 = node.find_position
|
144
|
+
x2, y2 = other.find_position
|
136
145
|
|
137
|
-
|
138
|
-
|
139
|
-
|
146
|
+
mouse.move(x: x1, y: y1)
|
147
|
+
mouse.down
|
148
|
+
sleep delay if delay
|
149
|
+
mouse.move(x: x2, y: y2, steps: steps)
|
150
|
+
mouse.up
|
151
|
+
end
|
140
152
|
|
141
|
-
|
142
|
-
|
143
|
-
|
153
|
+
def drag_by(node, x, y, steps, delay = nil)
|
154
|
+
x1, y1 = node.find_position
|
155
|
+
x2 = x1 + x
|
156
|
+
y2 = y1 + y
|
144
157
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
158
|
+
mouse.move(x: x1, y: y1)
|
159
|
+
mouse.down
|
160
|
+
sleep delay if delay
|
161
|
+
mouse.move(x: x2, y: y2, steps: steps)
|
162
|
+
mouse.up
|
163
|
+
end
|
149
164
|
|
150
|
-
|
151
|
-
|
152
|
-
|
165
|
+
def select_file(node, value)
|
166
|
+
node.select_file(value)
|
167
|
+
end
|
153
168
|
|
154
|
-
|
155
|
-
|
156
|
-
|
169
|
+
def parents(node)
|
170
|
+
evaluate_on(node: node, expression: "_cuprite.parents(this)", by_value: false)
|
171
|
+
end
|
157
172
|
|
158
|
-
|
159
|
-
|
160
|
-
|
173
|
+
def visible_text(node)
|
174
|
+
evaluate_on(node: node, expression: "_cuprite.visibleText(this)")
|
175
|
+
end
|
161
176
|
|
162
|
-
|
163
|
-
|
164
|
-
|
177
|
+
def delete_text(node)
|
178
|
+
evaluate_on(node: node, expression: "_cuprite.deleteText(this)")
|
179
|
+
end
|
165
180
|
|
166
|
-
|
167
|
-
|
168
|
-
|
181
|
+
def attributes(node)
|
182
|
+
value = evaluate_on(node: node, expression: "_cuprite.getAttributes(this)")
|
183
|
+
JSON.parse(value)
|
184
|
+
end
|
169
185
|
|
170
|
-
|
171
|
-
|
172
|
-
|
186
|
+
def attribute(node, name)
|
187
|
+
evaluate_on(node: node, expression: %(_cuprite.getAttribute(this, "#{name}")))
|
188
|
+
end
|
189
|
+
|
190
|
+
def value(node)
|
191
|
+
evaluate_on(node: node, expression: "_cuprite.value(this)")
|
192
|
+
end
|
193
|
+
|
194
|
+
def visible?(node)
|
195
|
+
evaluate_on(node: node, expression: "_cuprite.isVisible(this)")
|
196
|
+
end
|
197
|
+
|
198
|
+
def disabled?(node)
|
199
|
+
evaluate_on(node: node, expression: "_cuprite.isDisabled(this)")
|
200
|
+
end
|
201
|
+
|
202
|
+
def path(node)
|
203
|
+
evaluate_on(node: node, expression: "_cuprite.path(this)")
|
204
|
+
end
|
205
|
+
|
206
|
+
def all_text(node)
|
207
|
+
node.text
|
208
|
+
end
|
173
209
|
|
174
|
-
|
210
|
+
private
|
175
211
|
|
176
|
-
|
177
|
-
begin
|
212
|
+
def find_all(method, selector, within = nil)
|
178
213
|
nodes = if within
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
214
|
+
evaluate("_cuprite.find(arguments[0], arguments[1], arguments[2])", method, selector, within)
|
215
|
+
else
|
216
|
+
evaluate("_cuprite.find(arguments[0], arguments[1])", method, selector)
|
217
|
+
end
|
183
218
|
|
184
|
-
nodes.
|
219
|
+
nodes.select(&:node?)
|
185
220
|
rescue Ferrum::JavaScriptError => e
|
186
|
-
if e.class_name == "InvalidSelector"
|
187
|
-
|
188
|
-
end
|
221
|
+
raise InvalidSelector.new(e.response, method, selector) if e.class_name == "InvalidSelector"
|
222
|
+
|
189
223
|
raise
|
190
224
|
end
|
191
|
-
end
|
192
225
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
226
|
+
def prepare_wildcards(patterns)
|
227
|
+
string_passed = false
|
228
|
+
|
229
|
+
Array(patterns).map do |pattern|
|
230
|
+
if pattern.is_a?(Regexp)
|
231
|
+
pattern
|
232
|
+
else
|
233
|
+
string_passed = true
|
234
|
+
pattern = pattern.gsub("*", ".*")
|
235
|
+
Regexp.new(pattern, Regexp::IGNORECASE)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
ensure
|
239
|
+
if string_passed
|
240
|
+
warn "Passing strings to blacklist/whitelist is deprecated, pass regexp at #{caller(4..4).first}"
|
200
241
|
end
|
201
242
|
end
|
202
|
-
end
|
203
243
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
244
|
+
def attach_page(target_id = nil)
|
245
|
+
target = targets[target_id] if target_id
|
246
|
+
target ||= default_context.default_target
|
247
|
+
return target.page if target.connected?
|
208
248
|
|
209
|
-
|
210
|
-
|
211
|
-
|
249
|
+
target.maybe_sleep_if_new_window
|
250
|
+
target.page = Page.new(target.client, context_id: target.context_id, target_id: target.id)
|
251
|
+
target.page
|
252
|
+
end
|
212
253
|
end
|
213
254
|
end
|
214
255
|
end
|
@@ -1,46 +1,46 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Capybara
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
module Capybara
|
4
|
+
module Cuprite
|
5
|
+
class Cookie
|
6
|
+
def initialize(attributes)
|
7
|
+
@attributes = attributes
|
8
|
+
end
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
def name
|
11
|
+
@attributes["name"]
|
12
|
+
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
def value
|
15
|
+
@attributes["value"]
|
16
|
+
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
def domain
|
19
|
+
@attributes["domain"]
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
def path
|
23
|
+
@attributes["path"]
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
def size
|
27
|
+
@attributes["size"]
|
28
|
+
end
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
def secure?
|
31
|
+
@attributes["secure"]
|
32
|
+
end
|
32
33
|
|
33
|
-
|
34
|
-
|
35
|
-
|
34
|
+
def httponly?
|
35
|
+
@attributes["httpOnly"]
|
36
|
+
end
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
def session?
|
39
|
+
@attributes["session"]
|
40
|
+
end
|
40
41
|
|
41
|
-
|
42
|
-
|
43
|
-
Time.at(@attributes["expires"])
|
42
|
+
def expires
|
43
|
+
Time.at(@attributes["expires"]) if (@attributes["expires"]).positive?
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|