cuprite 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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");
|