poltergeist 1.10.0 → 1.11.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 475593a2e1c767e61acb2023b065eb7688972efa
4
- data.tar.gz: d35e4e7d9d2245564ea960045a891589c6a1dbed
3
+ metadata.gz: cc14ad1bf5ce9dacde0dfef301ec4fca4e46bd4d
4
+ data.tar.gz: 41bb2dbf585f8c59b32eeb7b10e7289dae0d193e
5
5
  SHA512:
6
- metadata.gz: 14677723d5e05debc486229fbe765a5e4fa6fdd49620daf32bcac5347c8856ea448946babba7904574ab904400ae9ae5db125de9d56683a745f6719a22f4c840
7
- data.tar.gz: fd6f1a43a661fc191cc7e80e63ed02bb601dbf21c17f40145a24da82941859add75100da027e8656450c0aed062606d6e17cc33d9e0da538cacd9d2a71358550
6
+ metadata.gz: 48a9db9338cbca32461001b10787638bd486cbfe4f3ebf4ec5a080a20abbefc7a79b5ced7788e2d935d4f06d0dc86aae37325a3b0df53e3504d8bb1e77e61f05
7
+ data.tar.gz: 2f75709c6262d5fd35075eadaa7a633f9ade052b68dc3a382ec5d6afeb5574d3a8d956d846caf07fc5de9c10a9a9b16d789065f67ba7ed6589adb2efcba4c666
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Poltergeist - A PhantomJS driver for Capybara #
2
2
 
3
- [![Build Status](https://secure.travis-ci.org/teampoltergeist/poltergeist.png)](http://travis-ci.org/teampoltergeist/poltergeist)
3
+ [![Build Status](https://secure.travis-ci.org/teampoltergeist/poltergeist.svg)](http://travis-ci.org/teampoltergeist/poltergeist)
4
4
 
5
5
  Poltergeist is a driver for [Capybara](https://github.com/jnicklas/capybara). It allows you to
6
6
  run your Capybara tests on a headless [WebKit](http://webkit.org) browser,
@@ -9,7 +9,7 @@ provided by [PhantomJS](http://phantomjs.org/).
9
9
  **If you're viewing this at https://github.com/teampoltergeist/poltergeist,
10
10
  you're reading the documentation for the master branch.
11
11
  [View documentation for the latest release
12
- (1.9.0).](https://github.com/teampoltergeist/poltergeist/tree/v1.9.0)**
12
+ (1.10.0).](https://github.com/teampoltergeist/poltergeist/tree/v1.10.0)**
13
13
 
14
14
  ## Getting help ##
15
15
 
@@ -109,7 +109,7 @@ and the following optional features:
109
109
  * `page.driver.render_base64(format, options)`
110
110
  * `page.driver.scroll_to(left, top)`
111
111
  * `page.driver.basic_authorize(user, password)`
112
- * `element.native.send_keys(*keys)`
112
+ * `element.send_keys(*keys)`
113
113
  * `page.driver.set_proxy(ip, port, type, user, password)`
114
114
  * window API
115
115
  * cookie handling
@@ -130,7 +130,7 @@ view). To render the entire page, use `save_screenshot('/path/to/file.png',
130
130
  :full => true)`.
131
131
 
132
132
  You also have an ability to render selected element. Pass option `selector` with
133
- any valid element selector to make a screenshot bounded by that element
133
+ any valid CSS element selector to make a screenshot bounded by that element
134
134
  `save_screenshot('/path/to/file.png', :selector => '#id')`.
135
135
 
136
136
  If you need for some reasons base64 encoded screenshot you can simply call
@@ -226,14 +226,14 @@ There's an ability to send arbitrary keys to the element:
226
226
 
227
227
  ``` ruby
228
228
  element = find('input#id')
229
- element.native.send_key('String')
229
+ element.send_keys('String')
230
230
  ```
231
231
 
232
232
  or even more complicated:
233
233
 
234
234
  ``` ruby
235
- element.native.send_keys('H', 'elo', :Left, 'l') # => 'Hello'
236
- element.native.send_key(:Enter) # triggers Enter key
235
+ element.send_keys('H', 'elo', :left, 'l') # => 'Hello'
236
+ element.send_keys(:enter) # triggers Enter key
237
237
  ```
238
238
  Since it's implemented natively in PhantomJS this will exactly imitate user
239
239
  behavior.
@@ -266,6 +266,8 @@ end
266
266
  * `:js_errors` (Boolean) - When false, JavaScript errors do not get re-raised in Ruby.
267
267
  * `:window_size` (Array) - The dimensions of the browser window in which to test, expressed
268
268
  as a 2-element array, e.g. [1024, 768]. Default: [1024, 768]
269
+ * `:screen_size` (Array) - The dimensions the window size will be set to when Window#maximize is called. Expressed
270
+ as a 2-element array, e.g. [1600, 1200]. Default: [1366, 768]
269
271
  * `:phantomjs_options` (Array) - Additional [command line options](http://phantomjs.org/api/command-line.html)
270
272
  to be passed to PhantomJS, e.g. `['--load-images=no', '--ignore-ssl-errors=yes']`
271
273
  * `:extensions` (Array) - An array of JS files to be preloaded into
@@ -140,6 +140,17 @@ module Capybara::Poltergeist
140
140
  command 'pop_frame'
141
141
  end
142
142
 
143
+ def switch_to_frame(handle, &block)
144
+ case handle
145
+ when Capybara::Node::Base
146
+ command 'push_frame', [handle.native.page_id, handle.native.id]
147
+ when :parent
148
+ command 'pop_frame'
149
+ when :top
150
+ command 'pop_frame', true
151
+ end
152
+ end
153
+
143
154
  def window_handle
144
155
  command 'window_handle'
145
156
  end
@@ -236,8 +236,8 @@ class Poltergeist.Browser
236
236
  push_frame: (name, timeout = (new Date().getTime()) + 2000) ->
237
237
  @pushFrame(@current_command, name, timeout)
238
238
 
239
- pop_frame: ->
240
- @current_command.sendResponse(@currentPage.popFrame())
239
+ pop_frame: (pop_all = false)->
240
+ @current_command.sendResponse(@currentPage.popFrame(pop_all))
241
241
 
242
242
  window_handles: ->
243
243
  handles = @pages.filter((p) -> !p.closed).map((p) -> p.handle)
@@ -346,7 +346,10 @@ class Poltergeist.Browser
346
346
  target.mouseEvent('click')
347
347
 
348
348
  for sequence in keys
349
- key = if sequence.key? then @currentPage.keyCode(sequence.key) else sequence
349
+ key = if sequence.key?
350
+ @currentPage.keyCode(sequence.key) || sequence.key
351
+ else
352
+ sequence
350
353
  if sequence.modifier?
351
354
  modifier_keys = @currentPage.keyModifierKeys(sequence.modifier)
352
355
  modifier_code = @currentPage.keyModifierCode(sequence.modifier)
@@ -1,6 +1,6 @@
1
1
  class Poltergeist.Cmd
2
2
  constructor: (@owner, @id, @name, @args)->
3
-
3
+ @_response_sent = false
4
4
  sendResponse: (response) ->
5
5
  errors = @browser.currentPage.errors
6
6
  @browser.currentPage.clearErrors()
@@ -8,10 +8,19 @@ class Poltergeist.Cmd
8
8
  if errors.length > 0 && @browser.js_errors
9
9
  @sendError(new Poltergeist.JavascriptError(errors))
10
10
  else
11
- @owner.sendResponse(@id, response)
11
+ @owner.sendResponse(@id, response) unless @_response_sent
12
+ @_response_sent = true
12
13
 
13
14
  sendError: (errors) ->
14
- @owner.sendError(@id, errors)
15
+ @owner.sendError(@id, errors) unless @_response_sent
16
+ @_response_sent = true
15
17
 
16
18
  run: (@browser) ->
17
- @browser.runCommand(this)
19
+ try
20
+ @browser.runCommand(this)
21
+ catch error
22
+ if error instanceof Poltergeist.Error
23
+ @sendError(error)
24
+ else
25
+ @sendError(new Poltergeist.BrowserError(error.toString(), error.stack))
26
+
@@ -304,8 +304,11 @@ Poltergeist.Browser = (function() {
304
304
  return this.pushFrame(this.current_command, name, timeout);
305
305
  };
306
306
 
307
- Browser.prototype.pop_frame = function() {
308
- return this.current_command.sendResponse(this.currentPage.popFrame());
307
+ Browser.prototype.pop_frame = function(pop_all) {
308
+ if (pop_all == null) {
309
+ pop_all = false;
310
+ }
311
+ return this.current_command.sendResponse(this.currentPage.popFrame(pop_all));
309
312
  };
310
313
 
311
314
  Browser.prototype.window_handles = function() {
@@ -454,7 +457,7 @@ Poltergeist.Browser = (function() {
454
457
  }
455
458
  for (i = 0, len = keys.length; i < len; i++) {
456
459
  sequence = keys[i];
457
- key = sequence.key != null ? this.currentPage.keyCode(sequence.key) : sequence;
460
+ key = sequence.key != null ? this.currentPage.keyCode(sequence.key) || sequence.key : sequence;
458
461
  if (sequence.modifier != null) {
459
462
  modifier_keys = this.currentPage.keyModifierKeys(sequence.modifier);
460
463
  modifier_code = this.currentPage.keyModifierCode(sequence.modifier);
@@ -4,6 +4,7 @@ Poltergeist.Cmd = (function() {
4
4
  this.id = id;
5
5
  this.name = name;
6
6
  this.args = args;
7
+ this._response_sent = false;
7
8
  }
8
9
 
9
10
  Cmd.prototype.sendResponse = function(response) {
@@ -13,17 +14,33 @@ Poltergeist.Cmd = (function() {
13
14
  if (errors.length > 0 && this.browser.js_errors) {
14
15
  return this.sendError(new Poltergeist.JavascriptError(errors));
15
16
  } else {
16
- return this.owner.sendResponse(this.id, response);
17
+ if (!this._response_sent) {
18
+ this.owner.sendResponse(this.id, response);
19
+ }
20
+ return this._response_sent = true;
17
21
  }
18
22
  };
19
23
 
20
24
  Cmd.prototype.sendError = function(errors) {
21
- return this.owner.sendError(this.id, errors);
25
+ if (!this._response_sent) {
26
+ this.owner.sendError(this.id, errors);
27
+ }
28
+ return this._response_sent = true;
22
29
  };
23
30
 
24
31
  Cmd.prototype.run = function(browser) {
32
+ var error, error1;
25
33
  this.browser = browser;
26
- return this.browser.runCommand(this);
34
+ try {
35
+ return this.browser.runCommand(this);
36
+ } catch (error1) {
37
+ error = error1;
38
+ if (error instanceof Poltergeist.Error) {
39
+ return this.sendError(error);
40
+ } else {
41
+ return this.sendError(new Poltergeist.BrowserError(error.toString(), error.stack));
42
+ }
43
+ }
27
44
  };
28
45
 
29
46
  return Cmd;
@@ -4,30 +4,17 @@ var Poltergeist, system,
4
4
 
5
5
  Poltergeist = (function() {
6
6
  function Poltergeist(port, width, height) {
7
- var that;
8
7
  this.browser = new Poltergeist.Browser(width, height);
9
8
  this.connection = new Poltergeist.Connection(this, port);
10
- that = this;
11
- phantom.onError = function(message, stack) {
12
- return that.onError(message, stack);
13
- };
14
- this.running = false;
9
+ phantom.onError = (function(_this) {
10
+ return function(message, stack) {
11
+ return _this.onError(message, stack);
12
+ };
13
+ })(this);
15
14
  }
16
15
 
17
16
  Poltergeist.prototype.runCommand = function(command) {
18
- var error, error1;
19
- this.running = true;
20
- command = new Poltergeist.Cmd(this, command.id, command.name, command.args);
21
- try {
22
- return command.run(this.browser);
23
- } catch (error1) {
24
- error = error1;
25
- if (error instanceof Poltergeist.Error) {
26
- return this.sendError(command.id, error);
27
- } else {
28
- return this.sendError(command.id, new Poltergeist.BrowserError(error.toString(), error.stack));
29
- }
30
- }
17
+ return new Poltergeist.Cmd(this, command.id, command.name, command.args).run(this.browser);
31
18
  };
32
19
 
33
20
  Poltergeist.prototype.sendResponse = function(command_id, response) {
@@ -48,12 +35,8 @@ Poltergeist = (function() {
48
35
  };
49
36
 
50
37
  Poltergeist.prototype.send = function(data) {
51
- if (this.running) {
52
- this.connection.send(data);
53
- this.running = false;
54
- return true;
55
- }
56
- return false;
38
+ this.connection.send(data);
39
+ return true;
57
40
  };
58
41
 
59
42
  return Poltergeist;
@@ -23,7 +23,6 @@ Poltergeist.WebPage = (function() {
23
23
  this.state = 'default';
24
24
  this.urlWhitelist = [];
25
25
  this.urlBlacklist = [];
26
- this.frames = [];
27
26
  this.errors = [];
28
27
  this._networkTraffic = {};
29
28
  this._tempHeaders = {};
@@ -422,36 +421,35 @@ Poltergeist.WebPage = (function() {
422
421
  WebPage.prototype.pushFrame = function(name) {
423
422
  var frame_no;
424
423
  if (this["native"]().switchToFrame(name)) {
425
- this.frames.push(name);
426
424
  return true;
427
- } else {
428
- frame_no = this["native"]().evaluate(function(frame_name) {
429
- var f, frames, idx;
430
- frames = document.querySelectorAll("iframe, frame");
431
- return ((function() {
432
- var k, len2, results;
433
- results = [];
434
- for (idx = k = 0, len2 = frames.length; k < len2; idx = ++k) {
435
- f = frames[idx];
436
- if ((f != null ? f['name'] : void 0) === frame_name || (f != null ? f['id'] : void 0) === frame_name) {
437
- results.push(idx);
438
- }
439
- }
440
- return results;
441
- })())[0];
442
- }, name);
443
- if ((frame_no != null) && this["native"]().switchToFrame(frame_no)) {
444
- this.frames.push(name);
445
- return true;
446
- } else {
447
- return false;
448
- }
449
425
  }
426
+ frame_no = this["native"]().evaluate(function(frame_name) {
427
+ var f, frames, idx;
428
+ frames = document.querySelectorAll("iframe, frame");
429
+ return ((function() {
430
+ var k, len2, results;
431
+ results = [];
432
+ for (idx = k = 0, len2 = frames.length; k < len2; idx = ++k) {
433
+ f = frames[idx];
434
+ if ((f != null ? f['name'] : void 0) === frame_name || (f != null ? f['id'] : void 0) === frame_name) {
435
+ results.push(idx);
436
+ }
437
+ }
438
+ return results;
439
+ })())[0];
440
+ }, name);
441
+ return (frame_no != null) && this["native"]().switchToFrame(frame_no);
450
442
  };
451
443
 
452
- WebPage.prototype.popFrame = function() {
453
- this.frames.pop();
454
- return this["native"]().switchToParentFrame();
444
+ WebPage.prototype.popFrame = function(pop_all) {
445
+ if (pop_all == null) {
446
+ pop_all = false;
447
+ }
448
+ if (pop_all) {
449
+ return this["native"]().switchToMainFrame();
450
+ } else {
451
+ return this["native"]().switchToParentFrame();
452
+ }
455
453
  };
456
454
 
457
455
  WebPage.prototype.dimensions = function() {
@@ -529,17 +527,17 @@ Poltergeist.WebPage = (function() {
529
527
  };
530
528
 
531
529
  WebPage.prototype.bindCallback = function(name) {
532
- var that;
533
- that = this;
534
- this["native"]()[name] = function() {
535
- var result;
536
- if (that[name + 'Native'] != null) {
537
- result = that[name + 'Native'].apply(that, arguments);
538
- }
539
- if (result !== false && (that[name] != null)) {
540
- return that[name].apply(that, arguments);
541
- }
542
- };
530
+ this["native"]()[name] = (function(_this) {
531
+ return function() {
532
+ var result;
533
+ if (_this[name + 'Native'] != null) {
534
+ result = _this[name + 'Native'].apply(_this, arguments);
535
+ }
536
+ if (result !== false && (_this[name] != null)) {
537
+ return _this[name].apply(_this, arguments);
538
+ }
539
+ };
540
+ })(this);
543
541
  return true;
544
542
  };
545
543
 
@@ -3,22 +3,10 @@ class Poltergeist
3
3
  @browser = new Poltergeist.Browser(width, height)
4
4
  @connection = new Poltergeist.Connection(this, port)
5
5
 
6
- # The QtWebKit bridge doesn't seem to like Function.prototype.bind
7
- that = this
8
- phantom.onError = (message, stack) -> that.onError(message, stack)
9
-
10
- @running = false
6
+ phantom.onError = (message, stack) => @onError(message, stack)
11
7
 
12
8
  runCommand: (command) ->
13
- @running = true
14
- command = new Poltergeist.Cmd(this, command.id, command.name, command.args)
15
- try
16
- command.run(@browser)
17
- catch error
18
- if error instanceof Poltergeist.Error
19
- this.sendError(command.id, error)
20
- else
21
- this.sendError(command.id, new Poltergeist.BrowserError(error.toString(), error.stack))
9
+ new Poltergeist.Cmd(this, command.id, command.name, command.args).run(@browser)
22
10
 
23
11
  sendResponse: (command_id, response) ->
24
12
  this.send(command_id: command_id, response: response)
@@ -32,14 +20,8 @@ class Poltergeist
32
20
  )
33
21
 
34
22
  send: (data) ->
35
- # Prevents more than one response being sent for a single
36
- # command. This can happen in some scenarios where an error
37
- # is raised but the script can still continue.
38
- if @running
39
- @connection.send(data)
40
- @running = false
41
- return true
42
- return false
23
+ @connection.send(data)
24
+ return true
43
25
 
44
26
  # This is necessary because the remote debugger will wrap the
45
27
  # script in a function, causing the Poltergeist variable to
@@ -22,7 +22,6 @@ class Poltergeist.WebPage
22
22
  @state = 'default'
23
23
  @urlWhitelist = []
24
24
  @urlBlacklist = []
25
- @frames = []
26
25
  @errors = []
27
26
  @_networkTraffic = {}
28
27
  @_tempHeaders = {}
@@ -289,24 +288,21 @@ class Poltergeist.WebPage
289
288
  this.setCustomHeaders(allHeaders)
290
289
 
291
290
  pushFrame: (name) ->
292
- if this.native().switchToFrame(name)
293
- @frames.push(name)
294
- return true
291
+ return true if this.native().switchToFrame(name)
292
+
293
+ # if switch by name fails - find index and try again
294
+ frame_no = this.native().evaluate(
295
+ (frame_name) ->
296
+ frames = document.querySelectorAll("iframe, frame")
297
+ (idx for f, idx in frames when f?['name'] == frame_name or f?['id'] == frame_name)[0]
298
+ , name)
299
+ frame_no? and this.native().switchToFrame(frame_no)
300
+
301
+ popFrame: (pop_all = false)->
302
+ if pop_all
303
+ this.native().switchToMainFrame()
295
304
  else
296
- frame_no = this.native().evaluate(
297
- (frame_name) ->
298
- frames = document.querySelectorAll("iframe, frame")
299
- (idx for f, idx in frames when f?['name'] == frame_name or f?['id'] == frame_name)[0]
300
- , name)
301
- if frame_no? and this.native().switchToFrame(frame_no)
302
- @frames.push(name)
303
- return true
304
- else
305
- return false
306
-
307
- popFrame: ->
308
- @frames.pop()
309
- this.native().switchToParentFrame()
305
+ this.native().switchToParentFrame()
310
306
 
311
307
  dimensions: ->
312
308
  scroll = this.scrollPosition()
@@ -365,16 +361,10 @@ class Poltergeist.WebPage
365
361
  # escaping the string.
366
362
  "(#{fn.toString()}).apply(this, PoltergeistAgent.JSON.parse(#{JSON.stringify(JSON.stringify(args))}))"
367
363
 
368
- # For some reason phantomjs seems to have trouble with doing 'fat arrow' binding here,
369
- # hence the 'that' closure.
370
364
  bindCallback: (name) ->
371
- that = this
372
- this.native()[name] = ->
373
- if that[name + 'Native']? # For internal callbacks
374
- result = that[name + 'Native'].apply(that, arguments)
375
-
376
- if result != false && that[name]? # For externally set callbacks
377
- that[name].apply(that, arguments)
365
+ @native()[name] = =>
366
+ result = @[name + 'Native'].apply(@, arguments) if @[name + 'Native']? # For internal callbacks
367
+ @[name].apply(@, arguments) if result != false && @[name]? # For externally set callbacks
378
368
  return true
379
369
 
380
370
  # Any error raised here or inside the evaluate will get reported to
@@ -147,6 +147,10 @@ module Capybara::Poltergeist
147
147
  browser.within_frame(name, &block)
148
148
  end
149
149
 
150
+ def switch_to_frame(locator, &block)
151
+ browser.switch_to_frame(locator, &block)
152
+ end
153
+
150
154
  def current_window_handle
151
155
  browser.window_handle
152
156
  end
@@ -210,6 +214,10 @@ module Capybara::Poltergeist
210
214
  end
211
215
  end
212
216
 
217
+ def maximize_window(handle)
218
+ resize_window_to(handle, *screen_size)
219
+ end
220
+
213
221
  def window_size(handle)
214
222
  within_window(handle) do
215
223
  evaluate_script('[window.innerWidth, window.innerHeight]')
@@ -380,17 +388,21 @@ module Capybara::Poltergeist
380
388
 
381
389
  private
382
390
 
391
+ def screen_size
392
+ options[:screen_size] || [1366,768]
393
+ end
394
+
383
395
  def find_modal(options)
384
396
  start_time = Time.now
385
397
  timeout_sec = options[:wait] || begin Capybara.default_max_wait_time rescue Capybara.default_wait_time end
386
398
  expect_text = options[:text]
399
+ expect_regexp = expect_text.is_a?(Regexp) ? expect_text : Regexp.escape(expect_text.to_s)
387
400
  not_found_msg = 'Unable to find modal dialog'
388
401
  not_found_msg += " with #{expect_text}" if expect_text
389
402
 
390
403
  begin
391
404
  modal_text = browser.modal_message
392
- raise Capybara::ModalNotFound if modal_text.nil?
393
- raise Capybara::ModalNotFound if (expect_text && (modal_text != expect_text))
405
+ raise Capybara::ModalNotFound if modal_text.nil? || (expect_text && !modal_text.match(expect_regexp))
394
406
  rescue Capybara::ModalNotFound => e
395
407
  raise e, not_found_msg if (Time.now - start_time) >= timeout_sec
396
408
  sleep(0.05)
@@ -92,7 +92,7 @@ module Capybara::Poltergeist
92
92
  end
93
93
  elsif tag_name == 'textarea'
94
94
  command :set, value.to_s
95
- elsif self[:contenteditable] == 'true'
95
+ elsif self[:isContentEditable]
96
96
  command :delete_text
97
97
  send_keys(value.to_s)
98
98
  end
@@ -1,5 +1,5 @@
1
1
  module Capybara
2
2
  module Poltergeist
3
- VERSION = "1.10.0"
3
+ VERSION = "1.11.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: poltergeist
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.10.0
4
+ version: 1.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Leighton
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-06-27 00:00:00.000000000 Z
11
+ date: 2016-10-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: capybara
@@ -287,7 +287,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
287
287
  version: '0'
288
288
  requirements: []
289
289
  rubyforge_project:
290
- rubygems_version: 2.6.4
290
+ rubygems_version: 2.5.1
291
291
  signing_key:
292
292
  specification_version: 4
293
293
  summary: PhantomJS driver for Capybara