poltergeist 1.10.0 → 1.11.0

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