cuprite 0.6.0 → 0.7.0

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