cuprite 0.3.0 → 0.4.0
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/README.md +21 -15
- data/lib/capybara/cuprite/browser.rb +58 -31
- data/lib/capybara/cuprite/browser/input.rb +12 -3
- data/lib/capybara/cuprite/browser/javascripts/index.js +10 -1
- data/lib/capybara/cuprite/browser/page.rb +25 -5
- data/lib/capybara/cuprite/browser/process.rb +30 -16
- data/lib/capybara/cuprite/browser/runtime.rb +13 -2
- data/lib/capybara/cuprite/browser/targets.rb +8 -1
- data/lib/capybara/cuprite/driver.rb +13 -4
- data/lib/capybara/cuprite/node.rb +69 -1
- data/lib/capybara/cuprite/version.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d183c86ecad6aaaf681364bda5ee919e5952a2b3a8d55cad40b3a6cec57ff26f
|
4
|
+
data.tar.gz: cccf18bb4e24c66262a0a6302ea7611e3aa1897c9b2442c24cfbc0feedfb46b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '056827d707f4e8388aa2350b53791d060f7c30872d5930311606d750822483a667b5d74093e03217cfa3a342c4fe8cd5d5b766fd3155de9c5ff0c6da6b335a17'
|
7
|
+
data.tar.gz: 389f981e6ae4b3a06cc3babbb70b942b0e2a8844bee690fc508af111bab65b333777f7b330554d2ae0f43abae918e3395ff1ae306b17df015efe94eaebd8c0e8
|
data/README.md
CHANGED
@@ -5,8 +5,7 @@
|
|
5
5
|
Cuprite is a pure Ruby driver (read as _no_ Java/Selenium/WebDriver/ChromeDriver
|
6
6
|
requirement) for [Capybara](https://github.com/teamcapybara/capybara). It allows
|
7
7
|
you to run your Capybara tests on a headless [Chrome](https://www.google.com/chrome/)
|
8
|
-
or [Chromium](https://www.chromium.org/) browser
|
9
|
-
for now because we work with tip-of-tree [protocol](https://chromedevtools.github.io/devtools-protocol/).
|
8
|
+
or [Chromium](https://www.chromium.org/) browser by [CDP protocol](https://chromedevtools.github.io/devtools-protocol/).
|
10
9
|
|
11
10
|
The emphasis was made on raw CDP protocol because Headless Chrome allows you to
|
12
11
|
do so many cool things that are barely supported by WebDriver because it should
|
@@ -21,8 +20,8 @@ Poltergest/PhantomJS:
|
|
21
20
|
|
22
21
|
```
|
23
22
|
cuprite:
|
24
|
-
Finished in 8 minutes
|
25
|
-
|
23
|
+
Finished in 8 minutes 58 seconds (files took 0.89959 seconds to load)
|
24
|
+
1555 examples, 0 failures, 27 pending
|
26
25
|
|
27
26
|
selenium headless chrome:
|
28
27
|
Finished in 9 minutes 13 seconds (files took 0.97749 seconds to load)
|
@@ -39,11 +38,6 @@ Finished in 11 minutes 49 seconds (files took 0.54019 seconds to load)
|
|
39
38
|
gem "cuprite"
|
40
39
|
```
|
41
40
|
|
42
|
-
Though we recommend using github until we release 1.0:
|
43
|
-
``` ruby
|
44
|
-
gem "cuprite", github: "machinio/cuprite"
|
45
|
-
```
|
46
|
-
|
47
41
|
and run `bundle install`.
|
48
42
|
|
49
43
|
In your test setup add:
|
@@ -137,13 +131,25 @@ end
|
|
137
131
|
|
138
132
|
`options` is a hash of options. The following options are supported:
|
139
133
|
|
140
|
-
* `:
|
141
|
-
|
142
|
-
|
143
|
-
|
134
|
+
* `:browser_path` (String) - Path to chrome binary, you can also set ENV
|
135
|
+
variable as `BROWSER_PATH=some/path/chrome bundle exec rspec`.
|
136
|
+
* `:headless` (Boolean) - Set browser as headless or not, `true` by default.
|
137
|
+
* `:logger` (Object responding to `puts`) - When present, debug output is
|
138
|
+
written to this object.
|
139
|
+
* `:timeout` (Numeric) - The number of seconds we'll wait for a response when
|
140
|
+
communicating with browser. Default is 30.
|
141
|
+
* `:js_errors` (Boolean) - When true, JavaScript errors get re-raised in Ruby.
|
142
|
+
* `:window_size` (Array) - The dimensions of the browser window in which to
|
144
143
|
test, expressed as a 2-element array, e.g. [1024, 768]. Default: [1024, 768]
|
145
|
-
|
146
|
-
|
144
|
+
* `:browser_options` (Hash) - Additional command line options,
|
145
|
+
[see them all](https://peter.sh/experiments/chromium-command-line-switches/)
|
146
|
+
e.g. `{ "ignore-certificate-errors" => nil }`
|
147
|
+
* `:extensions` (Array) - An array of JS files to be preloaded into the browser
|
148
|
+
* `:port` (Integer) - Remote debugging port for headless Chrome
|
149
|
+
* `:host` (String) - Remote debugging address for headless Chrome
|
150
|
+
* `:url_blacklist` (Array) - array of strings to match against requested URLs
|
151
|
+
* `:url_whitelist` (Array) - array of strings to match against requested URLs
|
152
|
+
|
147
153
|
|
148
154
|
### URL Blacklisting & Whitelisting ###
|
149
155
|
Cuprite supports URL blacklisting, which allows you to prevent scripts from
|
@@ -34,12 +34,13 @@ module Capybara::Cuprite
|
|
34
34
|
go_back go_forward find_modal accept_confirm dismiss_confirm
|
35
35
|
accept_prompt dismiss_prompt reset_modals) => :page
|
36
36
|
|
37
|
-
attr_reader :process, :logger
|
37
|
+
attr_reader :process, :logger, :js_errors
|
38
38
|
attr_writer :timeout
|
39
39
|
|
40
40
|
def initialize(options = nil)
|
41
41
|
@options = Hash(options)
|
42
42
|
@logger, @timeout = @options.values_at(:logger, :timeout)
|
43
|
+
@js_errors = @options.fetch(:js_errors, false)
|
43
44
|
|
44
45
|
if ENV["CUPRITE_DEBUG"]
|
45
46
|
STDOUT.sync = true
|
@@ -79,16 +80,7 @@ module Capybara::Cuprite
|
|
79
80
|
end
|
80
81
|
|
81
82
|
def visible_text(node)
|
82
|
-
|
83
|
-
evaluate_on(node: node, expr: "_cuprite.visibleText(this)")
|
84
|
-
rescue BrowserError => e
|
85
|
-
# FIXME: ObsoleteNode first arg is node, so it should be in node class
|
86
|
-
if e.message == "No node with given id found"
|
87
|
-
raise ObsoleteNode.new(self, e.response)
|
88
|
-
end
|
89
|
-
|
90
|
-
raise
|
91
|
-
end
|
83
|
+
evaluate_on(node: node, expr: "_cuprite.visibleText(this)")
|
92
84
|
end
|
93
85
|
|
94
86
|
def delete_text(node)
|
@@ -96,32 +88,36 @@ module Capybara::Cuprite
|
|
96
88
|
end
|
97
89
|
|
98
90
|
def select_file(node, value)
|
99
|
-
|
91
|
+
page.command("DOM.setFileInputFiles", nodeId: node["nodeId"], files: Array(value))
|
100
92
|
end
|
101
93
|
|
102
|
-
def render(path,
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
File.open(path.to_s, "wb") { |f| f.write(
|
94
|
+
def render(path, options = {})
|
95
|
+
format = options.delete(:format)
|
96
|
+
options = options.merge(path: path)
|
97
|
+
bin = Base64.decode64(render_base64(format, options))
|
98
|
+
File.open(path.to_s, "wb") { |f| f.write(bin) }
|
107
99
|
end
|
108
100
|
|
109
|
-
def render_base64(format
|
110
|
-
|
111
|
-
|
112
|
-
|
101
|
+
def render_base64(format, options = {})
|
102
|
+
options = render_options(format, options)
|
103
|
+
|
104
|
+
if options[:format].to_s == "pdf"
|
105
|
+
options = {}
|
106
|
+
options[:paperWidth] = @paper_size[:width].to_f if @paper_size
|
107
|
+
options[:paperHeight] = @paper_size[:height].to_f if @paper_size
|
108
|
+
options[:scale] = @zoom_factor if @zoom_factor
|
109
|
+
page.command("Page.printToPDF", **options)
|
110
|
+
else
|
111
|
+
page.command("Page.captureScreenshot", **options)
|
112
|
+
end.fetch("data")
|
113
113
|
end
|
114
114
|
|
115
115
|
def set_zoom_factor(zoom_factor)
|
116
|
-
|
116
|
+
@zoom_factor = zoom_factor.to_f
|
117
117
|
end
|
118
118
|
|
119
119
|
def set_paper_size(size)
|
120
|
-
|
121
|
-
end
|
122
|
-
|
123
|
-
def set_proxy(ip, port, type, user, password)
|
124
|
-
raise NotImplementedError
|
120
|
+
@paper_size = size
|
125
121
|
end
|
126
122
|
|
127
123
|
def headers=(headers)
|
@@ -198,6 +194,7 @@ module Capybara::Cuprite
|
|
198
194
|
|
199
195
|
def reset
|
200
196
|
@headers = {}
|
197
|
+
@zoom_factor = nil
|
201
198
|
targets.reset
|
202
199
|
end
|
203
200
|
|
@@ -216,6 +213,10 @@ module Capybara::Cuprite
|
|
216
213
|
command("Browser.crash")
|
217
214
|
end
|
218
215
|
|
216
|
+
def browser_error
|
217
|
+
page.evaluate("_cuprite.browserError()")
|
218
|
+
end
|
219
|
+
|
219
220
|
def command(*args)
|
220
221
|
id = @client.command(*args)
|
221
222
|
@client.wait(id: id)
|
@@ -236,10 +237,36 @@ module Capybara::Cuprite
|
|
236
237
|
@client = Client.new(self, @process.ws_url)
|
237
238
|
end
|
238
239
|
|
239
|
-
def
|
240
|
-
|
241
|
-
|
242
|
-
|
240
|
+
def render_options(format, opts)
|
241
|
+
options = {}
|
242
|
+
|
243
|
+
format ||= File.extname(opts[:path]).delete(".") || "png"
|
244
|
+
format = "jpeg" if format == "jpg"
|
245
|
+
raise "Not supported format: #{format}. jpeg | png | pdf" if format !~ /jpeg|png|pdf/i
|
246
|
+
options.merge!(format: format)
|
247
|
+
|
248
|
+
options.merge!(quality: opts[:quality] ? opts[:quality] : 75) if format == "jpeg"
|
249
|
+
|
250
|
+
warn "Ignoring :selector in #render since full: true was given at #{caller(1..1).first}" if !!opts[:full] && opts[:selector]
|
251
|
+
|
252
|
+
if !!opts[:full]
|
253
|
+
width, height = page.evaluate("[document.documentElement.offsetWidth, document.documentElement.offsetHeight]")
|
254
|
+
options.merge!(clip: { x: 0, y: 0, width: width, height: height, scale: @zoom_factor || 1.0 }) if width > 0 && height > 0
|
255
|
+
elsif opts[:selector]
|
256
|
+
rect = page.evaluate("document.querySelector('#{opts[:selector]}').getBoundingClientRect()")
|
257
|
+
options.merge!(clip: { x: rect["x"], y: rect["y"], width: rect["width"], height: rect["height"], scale: @zoom_factor || 1.0 })
|
258
|
+
end
|
259
|
+
|
260
|
+
if @zoom_factor
|
261
|
+
if !options[:clip]
|
262
|
+
width, height = page.evaluate("[document.documentElement.clientWidth, document.documentElement.clientHeight]")
|
263
|
+
options[:clip] = { x: 0, y: 0, width: width, height: height }
|
264
|
+
end
|
265
|
+
|
266
|
+
options[:clip].merge!(scale: @zoom_factor)
|
267
|
+
end
|
268
|
+
|
269
|
+
options
|
243
270
|
end
|
244
271
|
|
245
272
|
def find_all(method, selector, within = nil)
|
@@ -75,8 +75,8 @@ module Capybara::Cuprite
|
|
75
75
|
evaluate_on(node: node, expr: %(_cuprite.trigger(this, "#{event}")), **options)
|
76
76
|
end
|
77
77
|
|
78
|
-
def scroll_to(
|
79
|
-
|
78
|
+
def scroll_to(top, left)
|
79
|
+
execute("window.scrollTo(#{top}, #{left})")
|
80
80
|
end
|
81
81
|
|
82
82
|
def send_keys(node, keys)
|
@@ -171,7 +171,16 @@ module Capybara::Cuprite
|
|
171
171
|
end
|
172
172
|
|
173
173
|
def get_content_quads(node)
|
174
|
-
|
174
|
+
begin
|
175
|
+
result = command("DOM.getContentQuads", nodeId: node["nodeId"])
|
176
|
+
rescue BrowserError => e
|
177
|
+
if e.message == "Could not compute content quads."
|
178
|
+
raise MouseEventFailed.new("MouseEventFailed: click, none, 0, 0")
|
179
|
+
else
|
180
|
+
raise
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
175
184
|
raise "Node is either not visible or not an HTMLElement" if result["quads"].size == 0
|
176
185
|
|
177
186
|
# FIXME: Case when a few quads returned
|
@@ -24,7 +24,7 @@ class Cuprite {
|
|
24
24
|
results.push(xpath.snapshotItem(i));
|
25
25
|
}
|
26
26
|
} else {
|
27
|
-
results = within.querySelectorAll(selector);
|
27
|
+
results = Array.from(within.querySelectorAll(selector));
|
28
28
|
}
|
29
29
|
|
30
30
|
return results;
|
@@ -441,6 +441,10 @@ class Cuprite {
|
|
441
441
|
}
|
442
442
|
|
443
443
|
isCyclic(object) {
|
444
|
+
if (Array.isArray(object) && object.every(n => n instanceof Node)) {
|
445
|
+
return false;
|
446
|
+
}
|
447
|
+
|
444
448
|
try {
|
445
449
|
this._json.stringify(object);
|
446
450
|
return false;
|
@@ -448,6 +452,11 @@ class Cuprite {
|
|
448
452
|
return true;
|
449
453
|
}
|
450
454
|
}
|
455
|
+
|
456
|
+
// This command is purely for testing error handling
|
457
|
+
browserError() {
|
458
|
+
throw new Error("zomg");
|
459
|
+
}
|
451
460
|
}
|
452
461
|
|
453
462
|
window._cuprite = new Cuprite;
|
@@ -90,11 +90,17 @@ module Capybara::Cuprite
|
|
90
90
|
@client.close
|
91
91
|
end
|
92
92
|
|
93
|
-
def resize(width, height)
|
93
|
+
def resize(width: nil, height: nil, fullscreen: false)
|
94
94
|
result = @browser.command("Browser.getWindowForTarget", targetId: @target_id)
|
95
95
|
@window_id, @bounds = result.values_at("windowId", "bounds")
|
96
|
-
|
97
|
-
|
96
|
+
|
97
|
+
if fullscreen
|
98
|
+
@browser.command("Browser.setWindowBounds", windowId: @window_id, bounds: { windowState: "fullscreen" })
|
99
|
+
else
|
100
|
+
@browser.command("Browser.setWindowBounds", windowId: @window_id, bounds: { windowState: "normal" })
|
101
|
+
@browser.command("Browser.setWindowBounds", windowId: @window_id, bounds: { width: width, height: height, windowState: "normal" })
|
102
|
+
command("Emulation.setDeviceMetricsOverride", width: width, height: height, deviceScaleFactor: 1, mobile: false)
|
103
|
+
end
|
98
104
|
end
|
99
105
|
|
100
106
|
def refresh
|
@@ -182,7 +188,7 @@ module Capybara::Cuprite
|
|
182
188
|
@wait = 0
|
183
189
|
end
|
184
190
|
|
185
|
-
|
191
|
+
@client.wait(id: id)
|
186
192
|
end
|
187
193
|
|
188
194
|
private
|
@@ -196,16 +202,27 @@ module Capybara::Cuprite
|
|
196
202
|
end
|
197
203
|
end
|
198
204
|
|
205
|
+
if @browser.js_errors
|
206
|
+
@client.subscribe("Runtime.exceptionThrown") do |params|
|
207
|
+
Thread.main.raise JavaScriptError.new(params.dig("exceptionDetails", "exception"))
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
199
211
|
@client.subscribe("Page.javascriptDialogOpening") do |params|
|
200
212
|
accept_modal = @accept_modal.last
|
201
213
|
if accept_modal == true || accept_modal == false
|
202
214
|
@accept_modal.pop
|
203
215
|
@modal_messages << params["message"]
|
204
216
|
options = { accept: accept_modal }
|
205
|
-
default = params["defaultPrompt"]
|
206
217
|
response = @modal_response || params["defaultPrompt"]
|
207
218
|
options.merge!(promptText: response) if response
|
208
219
|
@client.command("Page.handleJavaScriptDialog", **options)
|
220
|
+
else
|
221
|
+
warn "Modal window has been opened, but you didn't wrap your code into (`accept_prompt` | `dismiss_prompt` | `accept_confirm` | `dismiss_confirm` | `accept_alert`), accepting by default"
|
222
|
+
options = { accept: true }
|
223
|
+
response = params["defaultPrompt"]
|
224
|
+
options.merge!(promptText: response) if response
|
225
|
+
@client.command("Page.handleJavaScriptDialog", **options)
|
209
226
|
end
|
210
227
|
end
|
211
228
|
|
@@ -280,6 +297,9 @@ module Capybara::Cuprite
|
|
280
297
|
command("Runtime.enable")
|
281
298
|
command("Log.enable")
|
282
299
|
command("Network.enable")
|
300
|
+
if Capybara.save_path
|
301
|
+
command("Page.setDownloadBehavior", behavior: "allow", downloadPath: Capybara.save_path)
|
302
|
+
end
|
283
303
|
|
284
304
|
@browser.extensions.each do |extension|
|
285
305
|
@client.command("Page.addScriptToEvaluateOnNewDocument", source: extension)
|
@@ -53,20 +53,27 @@ module Capybara::Cuprite
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def initialize(options)
|
56
|
-
@options =
|
56
|
+
@options = {}
|
57
57
|
|
58
|
-
detect_browser_path
|
58
|
+
detect_browser_path(options)
|
59
59
|
|
60
60
|
window_size = options.fetch(:window_size, [1024, 768])
|
61
|
-
@options
|
61
|
+
@options.merge!("window-size" => window_size.join(","))
|
62
62
|
|
63
63
|
port = options.fetch(:port, BROWSER_PORT)
|
64
|
-
@options
|
64
|
+
@options.merge!("remote-debugging-port" => port)
|
65
65
|
|
66
66
|
host = options.fetch(:host, BROWSER_HOST)
|
67
|
-
@options
|
67
|
+
@options.merge!("remote-debugging-address" => host)
|
68
68
|
|
69
69
|
@options = DEFAULT_OPTIONS.merge(@options)
|
70
|
+
|
71
|
+
unless options.fetch(:headless, true)
|
72
|
+
@options.delete("headless")
|
73
|
+
@options.delete("disable-gpu")
|
74
|
+
end
|
75
|
+
|
76
|
+
@options.merge!(options.fetch(:browser_options, {}))
|
70
77
|
end
|
71
78
|
|
72
79
|
def start
|
@@ -99,15 +106,24 @@ module Capybara::Cuprite
|
|
99
106
|
start
|
100
107
|
end
|
101
108
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
109
|
+
private
|
110
|
+
|
111
|
+
def detect_browser_path(options)
|
112
|
+
@path =
|
113
|
+
options[:browser_path] ||
|
114
|
+
BROWSER_PATH || (
|
115
|
+
if RUBY_PLATFORM.include?('darwin')
|
116
|
+
[
|
117
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
118
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
119
|
+
].find { |path| File.exist?(path) }
|
120
|
+
else
|
121
|
+
%w[chromium google-chrome-unstable google-chrome-beta google-chrome chrome].reduce(nil) do |path, exe|
|
122
|
+
path = Cliver.detect(exe)
|
123
|
+
break path if path
|
124
|
+
end
|
125
|
+
end
|
126
|
+
)
|
111
127
|
|
112
128
|
unless @path
|
113
129
|
message = "Could not find an executable `#{exe}`. Try to make it " \
|
@@ -117,8 +133,6 @@ module Capybara::Cuprite
|
|
117
133
|
end
|
118
134
|
end
|
119
135
|
|
120
|
-
private
|
121
|
-
|
122
136
|
def redirect_stdout(write_io)
|
123
137
|
if Capybara::Cuprite.mri?
|
124
138
|
yield
|
@@ -76,8 +76,19 @@ module Capybara::Cuprite
|
|
76
76
|
options = options.merge(executionContextId: execution_context_id)
|
77
77
|
end
|
78
78
|
|
79
|
-
|
80
|
-
|
79
|
+
begin
|
80
|
+
attempts ||= 1
|
81
|
+
response = command("Runtime.callFunctionOn", **options)
|
82
|
+
response.dig("result").tap { |r| handle_error(r) }
|
83
|
+
rescue BrowserError => e
|
84
|
+
case e.message
|
85
|
+
when "No node with given id found", "Could not find node with given id", "Cannot find context with specified id"
|
86
|
+
sleep 0.1
|
87
|
+
attempts += 1
|
88
|
+
options = options.merge(executionContextId: execution_context_id)
|
89
|
+
retry if attempts <= 3
|
90
|
+
end
|
91
|
+
end
|
81
92
|
end
|
82
93
|
|
83
94
|
# FIXME: We should have a central place to handle all type of errors
|
@@ -107,7 +107,14 @@ module Capybara::Cuprite
|
|
107
107
|
end
|
108
108
|
|
109
109
|
def targets
|
110
|
-
|
110
|
+
attempts ||= 1
|
111
|
+
# Targets cannot be empty the must be at least one default target.
|
112
|
+
targets = @browser.command("Target.getTargets")["targetInfos"]
|
113
|
+
raise TypeError if targets.empty?
|
114
|
+
targets
|
115
|
+
rescue TypeError
|
116
|
+
attempts += 1
|
117
|
+
retry if attempts < 3
|
111
118
|
end
|
112
119
|
|
113
120
|
def default?(target)
|
@@ -14,7 +14,7 @@ module Capybara::Cuprite
|
|
14
14
|
|
15
15
|
def initialize(app, options = {})
|
16
16
|
@app = app
|
17
|
-
@options = options
|
17
|
+
@options = options
|
18
18
|
@started = false
|
19
19
|
end
|
20
20
|
|
@@ -152,7 +152,7 @@ module Capybara::Cuprite
|
|
152
152
|
end
|
153
153
|
|
154
154
|
def resize(width, height)
|
155
|
-
browser.resize(width, height)
|
155
|
+
browser.resize(width: width, height: height)
|
156
156
|
end
|
157
157
|
alias_method :resize_window, :resize
|
158
158
|
|
@@ -172,6 +172,12 @@ module Capybara::Cuprite
|
|
172
172
|
end
|
173
173
|
end
|
174
174
|
|
175
|
+
def fullscreen_window(handle)
|
176
|
+
within_window(handle) do
|
177
|
+
browser.resize(fullscreen: true)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
175
181
|
def scroll_to(left, top)
|
176
182
|
browser.scroll_to(left, top)
|
177
183
|
end
|
@@ -184,8 +190,11 @@ module Capybara::Cuprite
|
|
184
190
|
browser.clear_network_traffic
|
185
191
|
end
|
186
192
|
|
187
|
-
|
188
|
-
|
193
|
+
# FIXME: check user/pass proxy auth
|
194
|
+
def set_proxy(ip, port, type = "http", user = nil, password = nil, bypass = nil)
|
195
|
+
@options[:browser] ||= {}
|
196
|
+
@options[:browser].merge!("proxy-server" => "#{type}=#{ip}:#{port}")
|
197
|
+
@options[:browser].merge!("proxy-bypass-list" => bypass) if bypass
|
189
198
|
end
|
190
199
|
|
191
200
|
def headers
|
@@ -17,7 +17,7 @@ module Capybara::Cuprite
|
|
17
17
|
browser.send(name, @node, *args)
|
18
18
|
rescue BrowserError => e
|
19
19
|
case e.message
|
20
|
-
when "
|
20
|
+
when "No node with given id found"
|
21
21
|
raise ObsoleteNode.new(self, e.response)
|
22
22
|
when "Cuprite.MouseEventFailed"
|
23
23
|
raise MouseEventFailed.new(self, e.response)
|
@@ -168,6 +168,29 @@ module Capybara::Cuprite
|
|
168
168
|
command(:trigger, event)
|
169
169
|
end
|
170
170
|
|
171
|
+
def scroll_to(element, location, position = nil)
|
172
|
+
if element.is_a?(Node)
|
173
|
+
scroll_element_to_location(element, location)
|
174
|
+
elsif location.is_a?(Symbol)
|
175
|
+
scroll_to_location(location)
|
176
|
+
else
|
177
|
+
scroll_to_coords(*position)
|
178
|
+
end
|
179
|
+
self
|
180
|
+
end
|
181
|
+
|
182
|
+
def scroll_by(x, y)
|
183
|
+
driver.execute_script <<~JS, self, x, y
|
184
|
+
var el = arguments[0];
|
185
|
+
if (el.scrollBy){
|
186
|
+
el.scrollBy(arguments[1], arguments[2]);
|
187
|
+
} else {
|
188
|
+
el.scrollTop = el.scrollTop + arguments[2];
|
189
|
+
el.scrollLeft = el.scrollLeft + arguments[1];
|
190
|
+
}
|
191
|
+
JS
|
192
|
+
end
|
193
|
+
|
171
194
|
def ==(other)
|
172
195
|
# We compare backendNodeId because once nodeId is sent to frontend backend
|
173
196
|
# never returns same nodeId sending 0. In other words frontend is
|
@@ -212,5 +235,50 @@ module Capybara::Cuprite
|
|
212
235
|
.tr("\u00a0", " ")
|
213
236
|
end
|
214
237
|
end
|
238
|
+
|
239
|
+
def scroll_element_to_location(element, location)
|
240
|
+
scroll_opts = case location
|
241
|
+
when :top
|
242
|
+
'true'
|
243
|
+
when :bottom
|
244
|
+
'false'
|
245
|
+
when :center
|
246
|
+
"{behavior: 'instant', block: 'center'}"
|
247
|
+
else
|
248
|
+
raise ArgumentError, "Invalid scroll_to location: #{location}"
|
249
|
+
end
|
250
|
+
driver.execute_script <<~JS, element
|
251
|
+
arguments[0].scrollIntoView(#{scroll_opts})
|
252
|
+
JS
|
253
|
+
end
|
254
|
+
|
255
|
+
def scroll_to_location(location)
|
256
|
+
scroll_y = case location
|
257
|
+
when :top
|
258
|
+
'0'
|
259
|
+
when :bottom
|
260
|
+
'arguments[0].scrollHeight'
|
261
|
+
when :center
|
262
|
+
'(arguments[0].scrollHeight - arguments[0].clientHeight)/2'
|
263
|
+
end
|
264
|
+
driver.execute_script <<~JS, self
|
265
|
+
if (arguments[0].scrollTo){
|
266
|
+
arguments[0].scrollTo(0, #{scroll_y});
|
267
|
+
} else {
|
268
|
+
arguments[0].scrollTop = #{scroll_y};
|
269
|
+
}
|
270
|
+
JS
|
271
|
+
end
|
272
|
+
|
273
|
+
def scroll_to_coords(x, y)
|
274
|
+
driver.execute_script <<~JS, self, x, y
|
275
|
+
if (arguments[0].scrollTo){
|
276
|
+
arguments[0].scrollTo(arguments[1], arguments[2]);
|
277
|
+
} else {
|
278
|
+
arguments[0].scrollTop = arguments[2];
|
279
|
+
arguments[0].scrollLeft = arguments[1];
|
280
|
+
}
|
281
|
+
JS
|
282
|
+
end
|
215
283
|
end
|
216
284
|
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.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dmitry Vorotilin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-01-
|
11
|
+
date: 2019-01-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: capybara
|
@@ -176,6 +176,20 @@ dependencies:
|
|
176
176
|
- - "~>"
|
177
177
|
- !ruby/object:Gem::Version
|
178
178
|
version: '3.0'
|
179
|
+
- !ruby/object:Gem::Dependency
|
180
|
+
name: chunky_png
|
181
|
+
requirement: !ruby/object:Gem::Requirement
|
182
|
+
requirements:
|
183
|
+
- - "~>"
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
version: '1.3'
|
186
|
+
type: :development
|
187
|
+
prerelease: false
|
188
|
+
version_requirements: !ruby/object:Gem::Requirement
|
189
|
+
requirements:
|
190
|
+
- - "~>"
|
191
|
+
- !ruby/object:Gem::Version
|
192
|
+
version: '1.3'
|
179
193
|
description: Cuprite is a driver for Capybara that allows you to run your tests on
|
180
194
|
a headless Chrome browser
|
181
195
|
email:
|