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.
@@ -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.start(@options)
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 :xpath, selector
78
+ find(:xpath, selector)
73
79
  end
74
80
 
75
81
  def find_css(selector)
76
- find :css, selector
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.click_coordinates(x, y)
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
- browser.switch_to_frame(locator)
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
- browser.reset
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
- browser.render(path, options)
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
- browser.render_base64(format, options)
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 paper_size=(size = {})
147
- browser.set_paper_size(size)
179
+ def zoom_factor=(value)
180
+ @zoom_factor = value.to_f
148
181
  end
149
182
 
150
- def zoom_factor=(zoom_factor)
151
- browser.set_zoom_factor(zoom_factor)
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.proxy_authorize(user, password)
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=(headers)
239
+ browser.headers.set(headers)
207
240
  end
208
241
 
209
242
  def add_headers(headers)
210
- browser.add_headers(headers)
243
+ browser.headers.add(headers)
211
244
  end
212
245
 
213
246
  def add_header(name, value, permanent: true)
214
- browser.add_header({ name => value }, permanent: permanent)
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.remove_cookie(options.merge(name: name))
268
+ browser.cookies.remove(**options.merge(name: name))
240
269
  end
241
270
 
242
271
  def clear_cookies
243
- browser.clear_cookies
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, Capybara::Cuprite::MouseEventFailed]
326
+ [Capybara::Cuprite::ObsoleteNode,
327
+ Capybara::Cuprite::MouseEventFailed,
328
+ Ferrum::NoExecutionContextError]
288
329
  end
289
330
 
290
331
  def go_back
291
- browser.go_back
332
+ browser.back
292
333
  end
293
334
 
294
335
  def go_forward
295
- browser.go_forward
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.native : 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 NodeError < ClientError
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
- this.keyupdowned(node, "keydown", keyCode);
147
- this.setValue(node, node.value + char);
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
- this.changed(node);
155
- this.input(node);
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");