cuprite 0.15 → 0.15.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -7
- data/lib/capybara/cuprite/browser.rb +44 -24
- data/lib/capybara/cuprite/driver.rb +21 -3
- data/lib/capybara/cuprite/javascripts/index.js +21 -7
- data/lib/capybara/cuprite/node.rb +2 -2
- data/lib/capybara/cuprite/options.rb +14 -0
- data/lib/capybara/cuprite/page.rb +20 -7
- data/lib/capybara/cuprite/version.rb +1 -1
- data/lib/capybara/cuprite.rb +3 -0
- metadata +6 -5
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/README.md
CHANGED
@@ -4,9 +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.
|
7
|
+
to the browser by CDP protocol.
|
10
8
|
|
11
9
|
|
12
10
|
## Install
|
@@ -43,8 +41,7 @@ browser = page.driver.browser
|
|
43
41
|
browser.mouse.move(x: 123, y: 456).down.up
|
44
42
|
```
|
45
43
|
|
46
|
-
|
47
|
-
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
|
48
45
|
differently in Cuprite, see the documentation below.
|
49
46
|
|
50
47
|
|
@@ -64,8 +61,8 @@ end
|
|
64
61
|
`Cuprite`-specific options are:
|
65
62
|
|
66
63
|
* options `Hash`
|
67
|
-
* `:url_blacklist` (Array) - array of
|
68
|
-
* `: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
|
69
66
|
|
70
67
|
|
71
68
|
## Debugging
|
@@ -11,50 +11,63 @@ module Capybara
|
|
11
11
|
find_modal accept_confirm dismiss_confirm accept_prompt
|
12
12
|
dismiss_prompt reset_modals] => :page
|
13
13
|
|
14
|
-
attr_reader :url_blacklist, :url_whitelist
|
15
|
-
alias url_blocklist url_blacklist
|
16
|
-
alias url_allowlist url_whitelist
|
17
|
-
|
18
14
|
def initialize(options = nil)
|
19
|
-
options ||= {}
|
20
|
-
@client = nil
|
21
|
-
self.url_blacklist = options[:url_blacklist]
|
22
|
-
self.url_whitelist = options[:url_whitelist]
|
23
|
-
|
24
15
|
super
|
25
|
-
|
16
|
+
|
17
|
+
@options.url_blacklist = prepare_wildcards(options&.dig(:url_blacklist))
|
18
|
+
@options.url_whitelist = prepare_wildcards(options&.dig(:url_whitelist))
|
19
|
+
|
20
|
+
@page = nil
|
26
21
|
end
|
27
22
|
|
28
|
-
def
|
23
|
+
def command(...)
|
29
24
|
super
|
30
|
-
|
25
|
+
rescue Ferrum::DeadBrowserError
|
26
|
+
restart
|
27
|
+
raise
|
31
28
|
end
|
32
29
|
|
33
30
|
def page
|
34
|
-
raise Ferrum::NoSuchPageError if @page
|
31
|
+
raise Ferrum::NoSuchPageError if @page&.closed?
|
35
32
|
|
36
33
|
@page ||= attach_page
|
37
34
|
end
|
38
35
|
|
39
36
|
def reset
|
40
37
|
super
|
41
|
-
@
|
38
|
+
@options.reset_window_size
|
39
|
+
@page = nil
|
42
40
|
end
|
43
41
|
|
44
42
|
def quit
|
45
43
|
super
|
46
|
-
@page =
|
44
|
+
@page = nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def resize(**options)
|
48
|
+
@options.window_size = [options[:width], options[:height]]
|
49
|
+
super
|
47
50
|
end
|
48
51
|
|
52
|
+
def url_whitelist
|
53
|
+
@options.url_whitelist
|
54
|
+
end
|
55
|
+
alias url_allowlist url_whitelist
|
56
|
+
|
49
57
|
def url_whitelist=(patterns)
|
50
|
-
@url_whitelist = prepare_wildcards(patterns)
|
51
|
-
page.network.whitelist = @url_whitelist if @client && @url_whitelist.any?
|
58
|
+
@options.url_whitelist = prepare_wildcards(patterns)
|
59
|
+
page.network.whitelist = @options.url_whitelist if @client && @options.url_whitelist.any?
|
52
60
|
end
|
53
61
|
alias url_allowlist= url_whitelist=
|
54
62
|
|
63
|
+
def url_blacklist
|
64
|
+
@options.url_blacklist
|
65
|
+
end
|
66
|
+
alias url_blocklist url_blacklist
|
67
|
+
|
55
68
|
def url_blacklist=(patterns)
|
56
|
-
@url_blacklist = prepare_wildcards(patterns)
|
57
|
-
page.network.blacklist = @url_blacklist if @client && @url_blacklist.any?
|
69
|
+
@options.url_blacklist = prepare_wildcards(patterns)
|
70
|
+
page.network.blacklist = @options.url_blacklist if @client && @options.url_blacklist.any?
|
58
71
|
end
|
59
72
|
alias url_blocklist= url_blacklist=
|
60
73
|
|
@@ -109,8 +122,13 @@ module Capybara
|
|
109
122
|
target = targets[target_id]
|
110
123
|
raise Ferrum::NoSuchPageError unless target
|
111
124
|
|
112
|
-
@page =
|
125
|
+
@page = ClosedPage.new if @page.target_id == target.id
|
113
126
|
target.page.close
|
127
|
+
targets.delete(target_id) # page.close is async, delete target asap
|
128
|
+
end
|
129
|
+
|
130
|
+
def active_element
|
131
|
+
evaluate("document.activeElement")
|
114
132
|
end
|
115
133
|
|
116
134
|
def browser_error
|
@@ -121,23 +139,25 @@ module Capybara
|
|
121
139
|
raise NotImplementedError
|
122
140
|
end
|
123
141
|
|
124
|
-
def drag(node, other, steps)
|
142
|
+
def drag(node, other, steps, delay = nil)
|
125
143
|
x1, y1 = node.find_position
|
126
144
|
x2, y2 = other.find_position
|
127
145
|
|
128
146
|
mouse.move(x: x1, y: y1)
|
129
147
|
mouse.down
|
148
|
+
sleep delay if delay
|
130
149
|
mouse.move(x: x2, y: y2, steps: steps)
|
131
150
|
mouse.up
|
132
151
|
end
|
133
152
|
|
134
|
-
def drag_by(node, x, y, steps)
|
153
|
+
def drag_by(node, x, y, steps, delay = nil)
|
135
154
|
x1, y1 = node.find_position
|
136
155
|
x2 = x1 + x
|
137
156
|
y2 = y1 + y
|
138
157
|
|
139
158
|
mouse.move(x: x1, y: y1)
|
140
159
|
mouse.down
|
160
|
+
sleep delay if delay
|
141
161
|
mouse.move(x: x2, y: y2, steps: steps)
|
142
162
|
mouse.up
|
143
163
|
end
|
@@ -224,10 +244,10 @@ module Capybara
|
|
224
244
|
def attach_page(target_id = nil)
|
225
245
|
target = targets[target_id] if target_id
|
226
246
|
target ||= default_context.default_target
|
227
|
-
return target.page if target.
|
247
|
+
return target.page if target.connected?
|
228
248
|
|
229
249
|
target.maybe_sleep_if_new_window
|
230
|
-
target.page = Page.new(target.
|
250
|
+
target.page = Page.new(target.client, context_id: target.context_id, target_id: target.id)
|
231
251
|
target.page
|
232
252
|
end
|
233
253
|
end
|
@@ -15,6 +15,7 @@ module Capybara
|
|
15
15
|
delegate %i[restart quit status_code timeout timeout= current_url title body
|
16
16
|
window_handles close_window switch_to_window within_window window_handle
|
17
17
|
back forward refresh wait_for_reload viewport_size device_pixel_ratio] => :browser
|
18
|
+
delegate %i[send_keys] => :active_element
|
18
19
|
alias html body
|
19
20
|
alias current_window_handle window_handle
|
20
21
|
alias go_back back
|
@@ -34,7 +35,11 @@ module Capybara
|
|
34
35
|
@screen_size ||= DEFAULT_MAXIMIZE_SCREEN_SIZE
|
35
36
|
@options[:save_path] ||= File.expand_path(Capybara.save_path) if Capybara.save_path
|
36
37
|
|
37
|
-
|
38
|
+
# It's set for debug() to make devtools tab open correctly.
|
39
|
+
@options[:browser_options] ||= {}
|
40
|
+
unless @options[:browser_options][:"remote-allow-origins"]
|
41
|
+
@options[:browser_options].merge!("remote-allow-origins": "*")
|
42
|
+
end
|
38
43
|
|
39
44
|
super()
|
40
45
|
end
|
@@ -64,6 +69,10 @@ module Capybara
|
|
64
69
|
evaluate_script("document.title")
|
65
70
|
end
|
66
71
|
|
72
|
+
def active_element
|
73
|
+
Node.new(self, browser.active_element)
|
74
|
+
end
|
75
|
+
|
67
76
|
def find_xpath(selector)
|
68
77
|
find(:xpath, selector)
|
69
78
|
end
|
@@ -109,7 +118,7 @@ module Capybara
|
|
109
118
|
def open_new_window
|
110
119
|
target = browser.default_context.create_target
|
111
120
|
target.maybe_sleep_if_new_window
|
112
|
-
target.page = Page.new(target.
|
121
|
+
target.page = Page.new(target.client, context_id: target.context_id, target_id: target.id)
|
113
122
|
target.page
|
114
123
|
end
|
115
124
|
|
@@ -265,7 +274,12 @@ module Capybara
|
|
265
274
|
alias authorize basic_authorize
|
266
275
|
|
267
276
|
def debug_url
|
268
|
-
|
277
|
+
response = JSON.parse(Net::HTTP.get(URI(build_remote_debug_url(path: "/json"))))
|
278
|
+
|
279
|
+
devtools_frontend_path = response[0]&.[]("devtoolsFrontendUrl")
|
280
|
+
raise "Could not generate debug url for remote debugging session" unless devtools_frontend_path
|
281
|
+
|
282
|
+
build_remote_debug_url(path: devtools_frontend_path)
|
269
283
|
end
|
270
284
|
|
271
285
|
def debug(binding = nil)
|
@@ -363,6 +377,10 @@ module Capybara
|
|
363
377
|
|
364
378
|
private
|
365
379
|
|
380
|
+
def build_remote_debug_url(path:)
|
381
|
+
"http://#{browser.process.host}:#{browser.process.port}#{path}"
|
382
|
+
end
|
383
|
+
|
366
384
|
def default_domain
|
367
385
|
if @started
|
368
386
|
URI.parse(browser.current_url).host
|
@@ -84,7 +84,6 @@ class Cuprite {
|
|
84
84
|
return true;
|
85
85
|
}
|
86
86
|
|
87
|
-
|
88
87
|
isDisabled(node) {
|
89
88
|
let xpath = "parent::optgroup[@disabled] | \
|
90
89
|
ancestor::select[@disabled] | \
|
@@ -344,10 +343,24 @@ class Cuprite {
|
|
344
343
|
|
345
344
|
_isInViewport(node) {
|
346
345
|
let rect = node.getBoundingClientRect();
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
346
|
+
|
347
|
+
let inViewport = rect.top >= 0 &&
|
348
|
+
rect.left >= 0 &&
|
349
|
+
rect.bottom <= window.innerHeight &&
|
350
|
+
rect.right <= window.innerWidth;
|
351
|
+
|
352
|
+
if (inViewport) {
|
353
|
+
// check if obscured by another element
|
354
|
+
let x = rect.width/2;
|
355
|
+
let y = rect.height/2 ;
|
356
|
+
|
357
|
+
let px = rect.left + x,
|
358
|
+
py = rect.top + y,
|
359
|
+
e = document.elementFromPoint(px, py);
|
360
|
+
return node == e;
|
361
|
+
}
|
362
|
+
|
363
|
+
return false;
|
351
364
|
}
|
352
365
|
|
353
366
|
select(node, value) {
|
@@ -356,12 +369,13 @@ class Cuprite {
|
|
356
369
|
} else if (value == false && !node.parentNode.multiple) {
|
357
370
|
return false;
|
358
371
|
} else {
|
359
|
-
|
372
|
+
let parentNode = node.parentNode;
|
373
|
+
this.trigger(parentNode, "focus");
|
360
374
|
|
361
375
|
node.selected = value;
|
362
376
|
this.changed(node);
|
363
377
|
|
364
|
-
this.trigger(
|
378
|
+
this.trigger(parentNode, "blur");
|
365
379
|
return true;
|
366
380
|
}
|
367
381
|
}
|
@@ -162,13 +162,13 @@ module Capybara
|
|
162
162
|
def drag_to(other, **options)
|
163
163
|
options[:steps] ||= 1
|
164
164
|
|
165
|
-
command(:drag, other.node, options[:steps])
|
165
|
+
command(:drag, other.node, options[:steps], options[:delay])
|
166
166
|
end
|
167
167
|
|
168
168
|
def drag_by(x, y, **options)
|
169
169
|
options[:steps] ||= 1
|
170
170
|
|
171
|
-
command(:drag_by, x, y, options[:steps])
|
171
|
+
command(:drag_by, x, y, options[:steps], options[:delay])
|
172
172
|
end
|
173
173
|
|
174
174
|
def trigger(event)
|
@@ -4,6 +4,12 @@ require "forwardable"
|
|
4
4
|
|
5
5
|
module Capybara
|
6
6
|
module Cuprite
|
7
|
+
class ClosedPage
|
8
|
+
def closed?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
7
13
|
class Page < Ferrum::Page
|
8
14
|
MODAL_WAIT = ENV.fetch("CUPRITE_MODAL_WAIT", 0.05).to_f
|
9
15
|
TRIGGER_CLICK_WAIT = ENV.fetch("CUPRITE_TRIGGER_CLICK_WAIT", 0.1).to_f
|
@@ -13,7 +19,7 @@ module Capybara
|
|
13
19
|
current_url current_title body execution_id execution_id!
|
14
20
|
evaluate evaluate_on evaluate_async execute] => :active_frame
|
15
21
|
|
16
|
-
def initialize(
|
22
|
+
def initialize(...)
|
17
23
|
@frame_stack = []
|
18
24
|
@accept_modal = []
|
19
25
|
@modal_messages = []
|
@@ -70,17 +76,17 @@ module Capybara
|
|
70
76
|
|
71
77
|
def find_modal(options)
|
72
78
|
start = Ferrum::Utils::ElapsedTime.monotonic_time
|
73
|
-
timeout = options.fetch(:wait, browser.timeout)
|
74
79
|
expect_text = options[:text]
|
75
80
|
expect_regexp = expect_text.is_a?(Regexp) ? expect_text : Regexp.escape(expect_text.to_s)
|
76
81
|
not_found_msg = "Unable to find modal dialog"
|
77
82
|
not_found_msg += " with #{expect_text}" if expect_text
|
83
|
+
wait = options.fetch(:wait, timeout)
|
78
84
|
|
79
85
|
begin
|
80
86
|
modal_text = @modal_messages.shift
|
81
87
|
raise Capybara::ModalNotFound if modal_text.nil? || (expect_text && !modal_text.match(expect_regexp))
|
82
88
|
rescue Capybara::ModalNotFound => e
|
83
|
-
raise e, not_found_msg if Ferrum::Utils::ElapsedTime.timeout?(start,
|
89
|
+
raise e, not_found_msg if Ferrum::Utils::ElapsedTime.timeout?(start, wait)
|
84
90
|
|
85
91
|
sleep(MODAL_WAIT)
|
86
92
|
retry
|
@@ -129,15 +135,22 @@ module Capybara
|
|
129
135
|
active_frame.current_title
|
130
136
|
end
|
131
137
|
|
138
|
+
def closed?
|
139
|
+
false
|
140
|
+
end
|
141
|
+
|
132
142
|
private
|
133
143
|
|
134
144
|
def prepare_page
|
135
145
|
super
|
136
146
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
147
|
+
width, height = @options.window_size
|
148
|
+
resize(width: width, height: height)
|
149
|
+
|
150
|
+
if @options.url_blacklist.any?
|
151
|
+
network.blacklist = @options.url_blacklist
|
152
|
+
elsif @options.url_whitelist.any?
|
153
|
+
network.whitelist = @options.url_whitelist
|
141
154
|
end
|
142
155
|
|
143
156
|
on("Page.javascriptDialogOpening") do |params|
|
data/lib/capybara/cuprite.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
ENV["FERRUM_DEBUG"] = "true" if ENV["CUPRITE_DEBUG"]
|
4
|
+
|
3
5
|
require "ferrum"
|
4
6
|
require "capybara"
|
5
7
|
require "capybara/cuprite/driver"
|
6
8
|
require "capybara/cuprite/browser"
|
7
9
|
require "capybara/cuprite/page"
|
10
|
+
require "capybara/cuprite/options"
|
8
11
|
require "capybara/cuprite/node"
|
9
12
|
require "capybara/cuprite/errors"
|
10
13
|
|
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:
|
4
|
+
version: 0.15.1
|
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: 2024-06-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: capybara
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 0.15.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.
|
40
|
+
version: 0.15.0
|
41
41
|
description: Cuprite is a driver for Capybara that allows you to run your tests on
|
42
42
|
a headless Chrome browser
|
43
43
|
email:
|
@@ -55,6 +55,7 @@ files:
|
|
55
55
|
- lib/capybara/cuprite/errors.rb
|
56
56
|
- lib/capybara/cuprite/javascripts/index.js
|
57
57
|
- lib/capybara/cuprite/node.rb
|
58
|
+
- lib/capybara/cuprite/options.rb
|
58
59
|
- lib/capybara/cuprite/page.rb
|
59
60
|
- lib/capybara/cuprite/version.rb
|
60
61
|
homepage: https://github.com/rubycdp/cuprite
|
@@ -81,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
82
|
- !ruby/object:Gem::Version
|
82
83
|
version: '0'
|
83
84
|
requirements: []
|
84
|
-
rubygems_version: 3.
|
85
|
+
rubygems_version: 3.5.11
|
85
86
|
signing_key:
|
86
87
|
specification_version: 4
|
87
88
|
summary: Headless Chrome driver for Capybara
|