cuprite 0.6.0 → 0.7.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 +103 -67
- data/lib/capybara/cuprite.rb +8 -21
- data/lib/capybara/cuprite/browser.rb +66 -224
- data/lib/capybara/cuprite/driver.rb +87 -44
- data/lib/capybara/cuprite/errors.rb +1 -64
- data/lib/capybara/cuprite/{browser/javascripts → javascripts}/index.js +26 -20
- data/lib/capybara/cuprite/node.rb +37 -31
- data/lib/capybara/cuprite/page.rb +166 -0
- data/lib/capybara/cuprite/version.rb +1 -1
- metadata +8 -42
- data/lib/capybara/cuprite/browser/client.rb +0 -74
- data/lib/capybara/cuprite/browser/dom.rb +0 -50
- data/lib/capybara/cuprite/browser/frame.rb +0 -115
- data/lib/capybara/cuprite/browser/input.json +0 -1341
- data/lib/capybara/cuprite/browser/input.rb +0 -200
- data/lib/capybara/cuprite/browser/net.rb +0 -90
- data/lib/capybara/cuprite/browser/page.rb +0 -378
- data/lib/capybara/cuprite/browser/process.rb +0 -223
- data/lib/capybara/cuprite/browser/runtime.rb +0 -182
- data/lib/capybara/cuprite/browser/targets.rb +0 -129
- data/lib/capybara/cuprite/browser/web_socket.rb +0 -69
- data/lib/capybara/cuprite/network/error.rb +0 -25
- data/lib/capybara/cuprite/network/request.rb +0 -33
- data/lib/capybara/cuprite/network/response.rb +0 -44
@@ -1,21 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "uri"
|
4
|
-
|
5
4
|
require "forwardable"
|
6
5
|
|
7
6
|
module Capybara::Cuprite
|
8
7
|
class Driver < Capybara::Driver::Base
|
8
|
+
DEFAULT_MAXIMIZE_SCREEN_SIZE = [1366, 768].freeze
|
9
|
+
EXTENSION = File.expand_path("javascripts/index.js", __dir__)
|
10
|
+
|
9
11
|
extend Forwardable
|
10
12
|
|
11
13
|
delegate %i(restart quit status_code timeout timeout=) => :browser
|
12
14
|
|
13
|
-
attr_reader :app, :options
|
15
|
+
attr_reader :app, :options, :screen_size
|
14
16
|
|
15
17
|
def initialize(app, options = {})
|
16
18
|
@app = app
|
17
|
-
@options = options
|
19
|
+
@options = options.dup
|
18
20
|
@started = false
|
21
|
+
|
22
|
+
@options[:extensions] ||= []
|
23
|
+
@options[:extensions] << EXTENSION
|
24
|
+
|
25
|
+
@screen_size = @options.delete(:screen_size)
|
26
|
+
@screen_size ||= DEFAULT_MAXIMIZE_SCREEN_SIZE
|
27
|
+
|
28
|
+
@options[:save_path] = Capybara.save_path.to_s if Capybara.save_path
|
19
29
|
end
|
20
30
|
|
21
31
|
def needs_server?
|
@@ -23,7 +33,7 @@ module Capybara::Cuprite
|
|
23
33
|
end
|
24
34
|
|
25
35
|
def browser
|
26
|
-
@browser ||= Browser.
|
36
|
+
@browser ||= Browser.new(@options)
|
27
37
|
end
|
28
38
|
|
29
39
|
def visit(url)
|
@@ -64,20 +74,20 @@ module Capybara::Cuprite
|
|
64
74
|
browser.frame_title
|
65
75
|
end
|
66
76
|
|
67
|
-
def find(method, selector)
|
68
|
-
browser.find(method, selector).map { |target_id, node| Node.new(self, target_id, node) }
|
69
|
-
end
|
70
|
-
|
71
77
|
def find_xpath(selector)
|
72
|
-
find
|
78
|
+
find(:xpath, selector)
|
73
79
|
end
|
74
80
|
|
75
81
|
def find_css(selector)
|
76
|
-
find
|
82
|
+
find(:css, selector)
|
83
|
+
end
|
84
|
+
|
85
|
+
def find(method, selector)
|
86
|
+
browser.find(method, selector).map { |native| Node.new(self, native) }
|
77
87
|
end
|
78
88
|
|
79
89
|
def click(x, y)
|
80
|
-
browser.
|
90
|
+
browser.mouse.click(x: x, y: y)
|
81
91
|
end
|
82
92
|
|
83
93
|
def evaluate_script(script, *args)
|
@@ -96,7 +106,14 @@ module Capybara::Cuprite
|
|
96
106
|
end
|
97
107
|
|
98
108
|
def switch_to_frame(locator)
|
99
|
-
|
109
|
+
handle = case locator
|
110
|
+
when Capybara::Node::Element
|
111
|
+
locator.native.description["frameId"]
|
112
|
+
when :parent, :top
|
113
|
+
locator
|
114
|
+
end
|
115
|
+
|
116
|
+
browser.switch_to_frame(handle)
|
100
117
|
end
|
101
118
|
|
102
119
|
def current_window_handle
|
@@ -124,31 +141,47 @@ module Capybara::Cuprite
|
|
124
141
|
end
|
125
142
|
|
126
143
|
def no_such_window_error
|
127
|
-
NoSuchWindowError
|
144
|
+
Ferrum::NoSuchWindowError
|
128
145
|
end
|
129
146
|
|
130
147
|
def reset!
|
131
|
-
|
148
|
+
@zoom_factor = nil
|
149
|
+
@paper_size = nil
|
132
150
|
browser.url_blacklist = @options[:url_blacklist]
|
133
151
|
browser.url_whitelist = @options[:url_whitelist]
|
152
|
+
browser.reset
|
134
153
|
@started = false
|
135
154
|
end
|
136
155
|
|
137
156
|
def save_screenshot(path, options = {})
|
138
|
-
|
157
|
+
options[:scale] = @zoom_factor if @zoom_factor
|
158
|
+
|
159
|
+
if pdf?(path, options)
|
160
|
+
options[:paperWidth] = @paper_size[:width].to_f if @paper_size
|
161
|
+
options[:paperHeight] = @paper_size[:height].to_f if @paper_size
|
162
|
+
browser.pdf(path: path, **options)
|
163
|
+
else
|
164
|
+
browser.screenshot(path: path, **options)
|
165
|
+
end
|
139
166
|
end
|
140
167
|
alias_method :render, :save_screenshot
|
141
168
|
|
142
169
|
def render_base64(format = :png, options = {})
|
143
|
-
|
170
|
+
if pdf?(nil, options)
|
171
|
+
options[:paperWidth] = @paper_size[:width].to_f if @paper_size
|
172
|
+
options[:paperHeight] = @paper_size[:height].to_f if @paper_size
|
173
|
+
browser.pdf(encoding: :base64, **options)
|
174
|
+
else
|
175
|
+
browser.screenshot(format: format, encoding: :base64, **options)
|
176
|
+
end
|
144
177
|
end
|
145
178
|
|
146
|
-
def
|
147
|
-
|
179
|
+
def zoom_factor=(value)
|
180
|
+
@zoom_factor = value.to_f
|
148
181
|
end
|
149
182
|
|
150
|
-
def
|
151
|
-
|
183
|
+
def paper_size=(value)
|
184
|
+
@paper_size = value
|
152
185
|
end
|
153
186
|
|
154
187
|
def resize(width, height)
|
@@ -179,7 +212,7 @@ module Capybara::Cuprite
|
|
179
212
|
end
|
180
213
|
|
181
214
|
def scroll_to(left, top)
|
182
|
-
browser.scroll_to(left, top)
|
215
|
+
browser.mouse.scroll_to(left, top)
|
183
216
|
end
|
184
217
|
|
185
218
|
def network_traffic(type = nil)
|
@@ -195,23 +228,23 @@ module Capybara::Cuprite
|
|
195
228
|
server = type ? "#{type}=#{ip}:#{port}" : "#{ip}:#{port}"
|
196
229
|
@options[:browser_options].merge!("proxy-server" => server)
|
197
230
|
@options[:browser_options].merge!("proxy-bypass-list" => bypass) if bypass
|
198
|
-
browser.
|
231
|
+
browser.authorize(type: :proxy, user: user, password: password)
|
199
232
|
end
|
200
233
|
|
201
234
|
def headers
|
202
|
-
browser.headers
|
235
|
+
browser.headers.get
|
203
236
|
end
|
204
237
|
|
205
238
|
def headers=(headers)
|
206
|
-
browser.headers
|
239
|
+
browser.headers.set(headers)
|
207
240
|
end
|
208
241
|
|
209
242
|
def add_headers(headers)
|
210
|
-
browser.
|
243
|
+
browser.headers.add(headers)
|
211
244
|
end
|
212
245
|
|
213
246
|
def add_header(name, value, permanent: true)
|
214
|
-
browser.
|
247
|
+
browser.headers.add({ name => value }, permanent: permanent)
|
215
248
|
end
|
216
249
|
|
217
250
|
def response_headers
|
@@ -219,7 +252,7 @@ module Capybara::Cuprite
|
|
219
252
|
end
|
220
253
|
|
221
254
|
def cookies
|
222
|
-
browser.cookies
|
255
|
+
browser.cookies.all
|
223
256
|
end
|
224
257
|
|
225
258
|
def set_cookie(name, value, options = {})
|
@@ -227,20 +260,16 @@ module Capybara::Cuprite
|
|
227
260
|
options[:name] ||= name
|
228
261
|
options[:value] ||= value
|
229
262
|
options[:domain] ||= default_domain
|
230
|
-
|
231
|
-
expires = options.delete(:expires).to_i
|
232
|
-
options[:expires] = expires if expires > 0
|
233
|
-
|
234
|
-
browser.set_cookie(options)
|
263
|
+
browser.cookies.set(**options)
|
235
264
|
end
|
236
265
|
|
237
266
|
def remove_cookie(name, **options)
|
238
267
|
options[:domain] = default_domain if options.empty?
|
239
|
-
browser.
|
268
|
+
browser.cookies.remove(**options.merge(name: name))
|
240
269
|
end
|
241
270
|
|
242
271
|
def clear_cookies
|
243
|
-
browser.
|
272
|
+
browser.cookies.clear
|
244
273
|
end
|
245
274
|
|
246
275
|
def clear_memory_cache
|
@@ -248,10 +277,20 @@ module Capybara::Cuprite
|
|
248
277
|
end
|
249
278
|
|
250
279
|
def basic_authorize(user, password)
|
251
|
-
browser.authorize(user, password)
|
280
|
+
browser.authorize(user: user, password: password)
|
252
281
|
end
|
253
282
|
alias_method :authorize, :basic_authorize
|
254
283
|
|
284
|
+
def debug
|
285
|
+
if @options[:inspector]
|
286
|
+
Process.spawn(browser.process.path, "http://#{browser.process.host}:#{browser.process.port}")
|
287
|
+
pause
|
288
|
+
else
|
289
|
+
raise Error, "To use the remote debugging, you have to launch " \
|
290
|
+
"the driver with `inspector: true` configuration option"
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
255
294
|
def pause
|
256
295
|
# STDIN is not necessarily connected to a keyboard. It might even be closed.
|
257
296
|
# So we need a method other than keypress to continue.
|
@@ -284,15 +323,17 @@ module Capybara::Cuprite
|
|
284
323
|
end
|
285
324
|
|
286
325
|
def invalid_element_errors
|
287
|
-
[Capybara::Cuprite::ObsoleteNode,
|
326
|
+
[Capybara::Cuprite::ObsoleteNode,
|
327
|
+
Capybara::Cuprite::MouseEventFailed,
|
328
|
+
Ferrum::NoExecutionContextError]
|
288
329
|
end
|
289
330
|
|
290
331
|
def go_back
|
291
|
-
browser.
|
332
|
+
browser.back
|
292
333
|
end
|
293
334
|
|
294
335
|
def go_forward
|
295
|
-
browser.
|
336
|
+
browser.forward
|
296
337
|
end
|
297
338
|
|
298
339
|
def refresh
|
@@ -336,11 +377,7 @@ module Capybara::Cuprite
|
|
336
377
|
end
|
337
378
|
|
338
379
|
def native_args(args)
|
339
|
-
args.map { |arg| arg.is_a?(Capybara::Cuprite::Node) ? arg.
|
340
|
-
end
|
341
|
-
|
342
|
-
def screen_size
|
343
|
-
@options[:screen_size] || [1366, 768]
|
380
|
+
args.map { |arg| arg.is_a?(Capybara::Cuprite::Node) ? arg.node : arg }
|
344
381
|
end
|
345
382
|
|
346
383
|
def session_wait_time
|
@@ -368,11 +405,17 @@ module Capybara::Cuprite
|
|
368
405
|
when Array
|
369
406
|
arg.map { |e| unwrap_script_result(e) }
|
370
407
|
when Hash
|
371
|
-
return Capybara::Cuprite::Node.new(self, arg["target_id"], arg["node"]) if arg["target_id"]
|
372
408
|
arg.each { |k, v| arg[k] = unwrap_script_result(v) }
|
409
|
+
when Ferrum::Node
|
410
|
+
Node.new(self, arg)
|
373
411
|
else
|
374
412
|
arg
|
375
413
|
end
|
376
414
|
end
|
415
|
+
|
416
|
+
def pdf?(path, options)
|
417
|
+
(path && File.extname(path).delete(".") == "pdf") ||
|
418
|
+
options[:format].to_s == "pdf"
|
419
|
+
end
|
377
420
|
end
|
378
421
|
end
|
@@ -3,7 +3,6 @@
|
|
3
3
|
module Capybara
|
4
4
|
module Cuprite
|
5
5
|
class Error < StandardError; end
|
6
|
-
class NoSuchWindowError < Error; end
|
7
6
|
|
8
7
|
class ClientError < Error
|
9
8
|
attr_reader :response
|
@@ -13,45 +12,6 @@ module Capybara
|
|
13
12
|
end
|
14
13
|
end
|
15
14
|
|
16
|
-
class BrowserError < ClientError
|
17
|
-
def code
|
18
|
-
response["code"]
|
19
|
-
end
|
20
|
-
|
21
|
-
def data
|
22
|
-
response["data"]
|
23
|
-
end
|
24
|
-
|
25
|
-
def message
|
26
|
-
response["message"]
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
class JavaScriptError < ClientError
|
31
|
-
attr_reader :class_name, :message
|
32
|
-
|
33
|
-
def initialize(response)
|
34
|
-
super
|
35
|
-
@class_name, @message = response.values_at("className", "description")
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
class StatusFailError < ClientError
|
40
|
-
def message
|
41
|
-
"Request to #{response["url"]} failed to reach server, check DNS and/or server status"
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
class FrameNotFound < ClientError
|
46
|
-
def name
|
47
|
-
response["args"].first
|
48
|
-
end
|
49
|
-
|
50
|
-
def message
|
51
|
-
"The frame "#{name}" was not found."
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
15
|
class InvalidSelector < ClientError
|
56
16
|
def initialize(response, method, selector)
|
57
17
|
super(response)
|
@@ -82,16 +42,14 @@ module Capybara
|
|
82
42
|
end
|
83
43
|
end
|
84
44
|
|
85
|
-
class
|
45
|
+
class ObsoleteNode < ClientError
|
86
46
|
attr_reader :node
|
87
47
|
|
88
48
|
def initialize(node, response)
|
89
49
|
@node = node
|
90
50
|
super(response)
|
91
51
|
end
|
92
|
-
end
|
93
52
|
|
94
|
-
class ObsoleteNode < NodeError
|
95
53
|
def message
|
96
54
|
"The element you are trying to interact with is either not part of the DOM, or is " \
|
97
55
|
"not currently visible on the page (perhaps display: none is set). " \
|
@@ -100,26 +58,5 @@ module Capybara
|
|
100
58
|
"new element."
|
101
59
|
end
|
102
60
|
end
|
103
|
-
|
104
|
-
class TimeoutError < Error
|
105
|
-
def message
|
106
|
-
"Timed out waiting for response. It's possible that this happened " \
|
107
|
-
"because something took a very long time (for example a page load " \
|
108
|
-
"was slow). If so, setting the Cuprite :timeout option to a higher " \
|
109
|
-
"value might help."
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
class ScriptTimeoutError < Error
|
114
|
-
def message
|
115
|
-
"Timed out waiting for evaluated script to resturn a value"
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
class DeadBrowser < Error
|
120
|
-
def initialize(message = "Chrome is dead")
|
121
|
-
super
|
122
|
-
end
|
123
|
-
end
|
124
61
|
end
|
125
62
|
end
|
@@ -123,13 +123,17 @@ class Cuprite {
|
|
123
123
|
value = value.substr(0, node.maxLength);
|
124
124
|
}
|
125
125
|
|
126
|
+
let valueBefore = node.value;
|
127
|
+
|
126
128
|
this.trigger(node, "focus");
|
127
129
|
this.setValue(node, "");
|
128
130
|
|
129
131
|
if (node.type == "number" || node.type == "date") {
|
130
132
|
this.setValue(node, value);
|
133
|
+
this.input(node);
|
131
134
|
} else if (node.type == "time") {
|
132
135
|
this.setValue(node, new Date(value).toTimeString().split(" ")[0]);
|
136
|
+
this.input(node);
|
133
137
|
} else if (node.type == "datetime-local") {
|
134
138
|
value = new Date(value);
|
135
139
|
let year = value.getFullYear();
|
@@ -139,20 +143,29 @@ class Cuprite {
|
|
139
143
|
let min = ("0" + value.getMinutes()).slice(-2);
|
140
144
|
let sec = ("0" + value.getSeconds()).slice(-2);
|
141
145
|
this.setValue(node, `${year}-${month}-${date}T${hour}:${min}:${sec}`);
|
146
|
+
this.input(node);
|
142
147
|
} else {
|
143
148
|
for (let i = 0; i < value.length; i++) {
|
144
149
|
let char = value[i];
|
145
150
|
let keyCode = this.characterToKeyCode(char);
|
146
|
-
|
147
|
-
|
151
|
+
// call the following functions in order, if one returns false (preventDefault),
|
152
|
+
// stop the call chain
|
153
|
+
[
|
154
|
+
() => this.keyupdowned(node, "keydown", keyCode),
|
155
|
+
() => this.keypressed(node, false, false, false, false, char.charCodeAt(0), char.charCodeAt(0)),
|
156
|
+
() => {
|
157
|
+
this.setValue(node, node.value + char)
|
158
|
+
this.input(node)
|
159
|
+
}
|
160
|
+
].some(fn => fn())
|
148
161
|
|
149
|
-
this.keypressed(node, false, false, false, false, char.charCodeAt(0), char.charCodeAt(0));
|
150
162
|
this.keyupdowned(node, "keyup", keyCode);
|
151
163
|
}
|
152
164
|
}
|
153
165
|
|
154
|
-
|
155
|
-
|
166
|
+
if (valueBefore !== node.value) {
|
167
|
+
this.changed(node);
|
168
|
+
}
|
156
169
|
this.trigger(node, "blur");
|
157
170
|
}
|
158
171
|
|
@@ -172,14 +185,20 @@ class Cuprite {
|
|
172
185
|
node.dispatchEvent(event);
|
173
186
|
}
|
174
187
|
|
188
|
+
/**
|
189
|
+
* @return {boolean} false when an event handler called preventDefault()
|
190
|
+
*/
|
175
191
|
keyupdowned(node, eventName, keyCode) {
|
176
192
|
let event = document.createEvent("UIEvents");
|
177
193
|
event.initEvent(eventName, true, true);
|
178
194
|
event.keyCode = keyCode;
|
179
195
|
event.charCode = 0;
|
180
|
-
node.dispatchEvent(event);
|
196
|
+
return !node.dispatchEvent(event);
|
181
197
|
}
|
182
198
|
|
199
|
+
/**
|
200
|
+
* @return {boolean} false when an event handler called preventDefault()
|
201
|
+
*/
|
183
202
|
keypressed(node, altKey, ctrlKey, shiftKey, metaKey, keyCode, charCode) {
|
184
203
|
event = document.createEvent("UIEvents");
|
185
204
|
event.initEvent("keypress", true, true);
|
@@ -190,7 +209,7 @@ class Cuprite {
|
|
190
209
|
event.metaKey = metaKey;
|
191
210
|
event.keyCode = keyCode;
|
192
211
|
event.charCode = charCode;
|
193
|
-
node.dispatchEvent(event);
|
212
|
+
return !node.dispatchEvent(event);
|
194
213
|
}
|
195
214
|
|
196
215
|
characterToKeyCode(char) {
|
@@ -450,19 +469,6 @@ class Cuprite {
|
|
450
469
|
return node.contains(selectedNode);
|
451
470
|
}
|
452
471
|
|
453
|
-
isCyclic(object) {
|
454
|
-
if (Array.isArray(object) && object.every(n => n instanceof Node)) {
|
455
|
-
return false;
|
456
|
-
}
|
457
|
-
|
458
|
-
try {
|
459
|
-
this._json.stringify(object);
|
460
|
-
return false;
|
461
|
-
} catch (e) {
|
462
|
-
return true;
|
463
|
-
}
|
464
|
-
}
|
465
|
-
|
466
472
|
// This command is purely for testing error handling
|
467
473
|
browserError() {
|
468
474
|
throw new Error("zomg");
|