poltergeist 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Poltergeist - A PhantomJS driver for Capybara #
2
2
 
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
 
5
5
  [![Build Status](https://secure.travis-ci.org/jonleighton/poltergeist.png)](http://travis-ci.org/jonleighton/poltergeist)
6
6
 
@@ -12,8 +12,32 @@ provided by [PhantomJS](http://www.phantomjs.org/).
12
12
 
13
13
  Add `poltergeist` to your Gemfile, and add in your test setup add:
14
14
 
15
- require 'capybara/poltergeist'
16
- Capybara.javascript_driver = :poltergeist
15
+ ``` ruby
16
+ require 'capybara/poltergeist'
17
+ Capybara.javascript_driver = :poltergeist
18
+ ```
19
+
20
+ ## Important note about Rack versions < 1.3.0 ##
21
+
22
+ Prior to version 1.3.0, the Rack handlers for Mongrel and Thin wrap your
23
+ app in the `Rack::Chunked` middleware so that it uses
24
+ `Transfer-Encoding: chunked`
25
+ ([commit](https://github.com/rack/rack/commit/50cdd0bf000a9ffb3eb3760fda2ff3e1ad18f3a7)).
26
+ This has been observed to cause problems,
27
+ probably due to race conditions in Qt's HTTP handling code, so you are
28
+ recommended to avoid this by specifying your own server setup for
29
+ Capybara:
30
+
31
+ ``` ruby
32
+ Capybara.server do |app, port|
33
+ require 'rack/handler/thin'
34
+ Thin::Logging.silent = true
35
+ Thin::Server.new('0.0.0.0', port, app).start
36
+ end
37
+ ```
38
+
39
+ If you're using Rails 3.0, this affects you. If you're using Rails 3.1+,
40
+ this doesn't affect you.
17
41
 
18
42
  ## Installing PhantomJS ##
19
43
 
@@ -32,9 +56,7 @@ the relationship between `bin/phantomjs` and `lib/`. This is because the
32
56
  `bin/phantomjs` binary looks in `../lib/` for its library files. So the
33
57
  best thing to do is to link (rather than copy) it into your `PATH`:
34
58
 
35
- ```
36
- ln -s /path/to/phantomjs/bin/phantomjs /usr/local/bin/phantomjs
37
- ```
59
+ ln -s /path/to/phantomjs/bin/phantomjs /usr/local/bin/phantomjs
38
60
 
39
61
  ### Compiling PhantomJS ###
40
62
 
@@ -53,7 +75,8 @@ you should copy (or link) the `bin/phantomjs` binary into your `PATH`.
53
75
 
54
76
  ## Running on a CI ##
55
77
 
56
- Currently PhantomJS is not 'truly headless', so to run it on a continuous integration
78
+ Currently PhantomJS is not yet 'truly headless' (but that's planned for the future),
79
+ so to run it on a continuous integration
57
80
  server you will need to install [Xvfb](http://en.wikipedia.org/wiki/Xvfb).
58
81
 
59
82
  ### On any generic server ###
@@ -98,16 +121,18 @@ size to 1024x768 by default, but you can set this yourself with `page.driver.res
98
121
  You can customize the way that Capybara sets up Poltegeist via the following code in your
99
122
  test setup:
100
123
 
101
- Capybara.register_driver :poltergeist do |app|
102
- Capybara::Poltergeist::Driver.new(app, options)
103
- end
124
+ ``` ruby
125
+ Capybara.register_driver :poltergeist do |app|
126
+ Capybara::Poltergeist::Driver.new(app, options)
127
+ end
128
+ ```
104
129
 
105
130
  `options` is a hash of options. The following options are supported:
106
131
 
107
- * `:phantomjs` (String) - A custom path to the phantomjs executable
108
- * `:debug` (Boolean) - When true, debug output is logged to `STDERR`
109
- * `:logger` (Object responding to `puts`) - When present, debug output is written to this object
110
- * `:timeout` (Numeric) - The number of seconds we'll wait for a response
132
+ * `:phantomjs` (String) - A custom path to the phantomjs executable
133
+ * `:debug` (Boolean) - When true, debug output is logged to `STDERR`
134
+ * `:logger` (Object responding to `puts`) - When present, debug output is written to this object
135
+ * `:timeout` (Numeric) - The number of seconds we'll wait for a response
111
136
  when communicating with PhantomJS. `nil` means wait forever. Default
112
137
  is 30.
113
138
 
@@ -117,22 +142,19 @@ Please file bug reports on Github and include example code to reproduce the prob
117
142
  possible. (Tests are even better.) Please also provide the output with
118
143
  `:debug` turned on, and screenshots if you think it's relevant.
119
144
 
120
- ## Why not use [capybara-webkit](https://github.com/thoughtbot/capybara-webkit)? ##
145
+ ## Differences from [capybara-webkit](https://github.com/thoughtbot/capybara-webkit) ##
121
146
 
122
- If capybara-webkit works for you, then by all means carry on using it.
147
+ Poltergeist is similar to capybara-webkit, but here are the key
148
+ differences:
123
149
 
124
- However, I have had some trouble with it, and Poltergeist basically started
125
- as an experiment to see whether a PhantomJS driver was possible. (It turned out it
126
- was, but only thanks to some new features since the 1.3 release.)
150
+ * It's more hackable. Poltergeist is written in Ruby + CoffeeScript.
151
+ We only have to worry about C++ when dealing with issues in
152
+ PhantomJS itself. In contrast, the majority of capybara-webkit is
153
+ written in C++.
127
154
 
128
- In the long term, I think having a PhantomJS driver makes sense, because that allows
129
- PhantomJS to concentrate on being an awesome headless browser, while the capybara driver
130
- (Poltergeist) is able to be the minimal amount of glue code necessary to drive the
131
- browser.
132
-
133
- I also find it more pleasant to hack in CoffeeScript than C++,
134
- particularly as my C++ experience only goes as far as trying to make
135
- PhantomJS/Qt/WebKit work with Poltergeist :)
155
+ * We're able to tap into the PhantomJS community. When PhantomJS
156
+ improves, Poltergeist improves. User's don't have to install Qt
157
+ because self-contained PhantomJS binary packages are available.
136
158
 
137
159
  ## Hacking ##
138
160
 
@@ -142,11 +164,6 @@ anyone who does a few good pull requests.
142
164
  To get setup, run `bundle install`. You can run the full test suite with
143
165
  `rspec spec/` or `rake`.
144
166
 
145
- I previously set up the repository on [Travis CI](http://travis-ci.org/)
146
- but unfortunately given they need a custom-built Qt+PhantomJS in order
147
- to pass, it can't be used for now. When static Linux PhantomJS builds
148
- are working this can be revisited.
149
-
150
167
  While PhantomJS is capable of compiling and running CoffeeScript code
151
168
  directly, I prefer to compile the code myself and distribute that (it
152
169
  makes debugging easier). Running `rake autocompile` will watch the
@@ -155,7 +172,21 @@ makes debugging easier). Running `rake autocompile` will watch the
155
172
 
156
173
  ## Changes ##
157
174
 
158
- ### 0.3 ###
175
+ ### 0.4.0 ###
176
+
177
+ * Element click position is now calculated using the native
178
+ `getBoundingClientRect()` method, which will be faster and less
179
+ buggy.
180
+
181
+ * Handle `window.confirm()`. Always returns true, which is the same
182
+ as capybara-webkit. [Issue #10]
183
+
184
+ * Handle `window.prompt()`. Returns the default value, if present, or
185
+ null.
186
+
187
+ * Fix bug with page Javascript page loading causing problems. [Issue #19]
188
+
189
+ ### 0.3.0 ###
159
190
 
160
191
  * There was a bad bug to do with clicking elements in a page where the
161
192
  page is smaller than the window. The incorrect position would be
@@ -175,11 +206,11 @@ makes debugging easier). Running `rake autocompile` will watch the
175
206
  * Added the `:timeout` option to configure the timeout when talking to
176
207
  PhantomJS.
177
208
 
178
- ### 0.2 ###
209
+ ### 0.2.0 ###
179
210
 
180
211
  * First version considered 'ready', hopefully fewer problems.
181
212
 
182
- ### 0.1 ###
213
+ ### 0.1.0 ###
183
214
 
184
215
  * First version, various problems.
185
216
 
@@ -9,7 +9,7 @@ module Capybara::Poltergeist
9
9
  def initialize(options = {})
10
10
  @options = options
11
11
  @server = Server.new(options.fetch(:timeout, DEFAULT_TIMEOUT))
12
- @client = Client.new(server.port, options[:phantomjs])
12
+ @client = Client.start(server.port, options[:phantomjs])
13
13
  end
14
14
 
15
15
  def timeout
@@ -2,29 +2,47 @@ require 'sfl'
2
2
 
3
3
  module Capybara::Poltergeist
4
4
  class Client
5
- PHANTOM_SCRIPT = File.expand_path('../client/compiled/main.js', __FILE__)
5
+ PHANTOMJS_SCRIPT = File.expand_path('../client/compiled/main.js', __FILE__)
6
+ PHANTOMJS_VERSION = "1.4.1"
6
7
 
7
- attr_reader :thread, :pid, :err, :port, :path
8
+ attr_reader :pid, :port, :path
9
+
10
+ def self.start(*args)
11
+ client = new(*args)
12
+ client.start
13
+ client
14
+ end
8
15
 
9
16
  def initialize(port, path = nil)
10
17
  @port = port
11
18
  @path = path || 'phantomjs'
12
-
13
- start
14
19
  at_exit { stop }
15
20
  end
16
21
 
17
22
  def start
18
- @pid = Kernel.spawn("#{path} #{PHANTOM_SCRIPT} #{port}")
23
+ check_phantomjs_version
24
+ @pid = Kernel.spawn("#{path} #{PHANTOMJS_SCRIPT} #{port}")
19
25
  end
20
26
 
21
27
  def stop
22
- Process.kill('TERM', pid)
28
+ Process.kill('TERM', pid) if pid
23
29
  end
24
30
 
25
31
  def restart
26
32
  stop
27
33
  start
28
34
  end
35
+
36
+ private
37
+
38
+ def check_phantomjs_version
39
+ return if @phantomjs_version_checked
40
+
41
+ version = `#{path} --version`.chomp
42
+ if version < PHANTOMJS_VERSION
43
+ raise PhantomJSTooOld.new(version)
44
+ end
45
+ @phantomjs_version_checked = true
46
+ end
29
47
  end
30
48
  end
@@ -151,18 +151,8 @@ class PoltergeistAgent.Node
151
151
  true
152
152
 
153
153
  position: ->
154
- pos = (element) ->
155
- x = element.offsetLeft
156
- y = element.offsetTop
157
-
158
- if element.offsetParent
159
- parentPos = pos(element.offsetParent)
160
-
161
- x += parentPos.x
162
- y += parentPos.y
163
-
164
- { x: x, y: y }
165
- pos @element
154
+ rect = @element.getBoundingClientRect()
155
+ { top: rect.top, left: rect.left }
166
156
 
167
157
  trigger: (name) ->
168
158
  if Node.EVENTS.MOUSE.indexOf(name) != -1
@@ -185,3 +175,6 @@ document.addEventListener(
185
175
  'DOMContentLoaded',
186
176
  -> console.log('__DOMContentLoaded')
187
177
  )
178
+
179
+ window.confirm = (message) -> true
180
+ window.prompt = (message, _default) -> _default or null
@@ -1,19 +1,23 @@
1
1
  class Poltergeist.Browser
2
2
  constructor: (@owner) ->
3
- @awaiting_response = false
3
+ @state = 'default'
4
4
  this.resetPage()
5
5
 
6
6
  resetPage: ->
7
7
  @page.release() if @page?
8
8
 
9
9
  @page = new Poltergeist.WebPage
10
+
11
+ @page.onLoadStarted = =>
12
+ @state = 'loading' if @state == 'clicked'
13
+
10
14
  @page.onLoadFinished = (status) =>
11
- if @awaiting_response
15
+ if @state == 'loading'
12
16
  @owner.sendResponse(status)
13
- @awaiting_response = false
17
+ @state = 'default'
14
18
 
15
19
  visit: (url) ->
16
- @awaiting_response = true
20
+ @state = 'loading'
17
21
  @page.open(url)
18
22
 
19
23
  current_url: ->
@@ -73,7 +77,7 @@ class Poltergeist.Browser
73
77
  @owner.sendResponse @page.get(id).isVisible()
74
78
 
75
79
  evaluate: (script) ->
76
- @owner.sendResponse @page.evaluate("function() { return #{script} }")
80
+ @owner.sendResponse JSON.parse(@page.evaluate("function() { return JSON.stringify(#{script}) }"))
77
81
 
78
82
  execute: (script) ->
79
83
  @page.execute("function() { #{script} }")
@@ -88,13 +92,9 @@ class Poltergeist.Browser
88
92
  @owner.sendResponse(true)
89
93
 
90
94
  click: (id) ->
91
- load_detected = false
92
-
93
- # Detect if the click event triggers a page load. If it does, don't send
94
- # a response here, because the response will be sent once the page has loaded.
95
- @page.onLoadStarted = =>
96
- @awaiting_response = true
97
- load_detected = true
95
+ # If the click event triggers onLoadStarted, we will transition to the 'loading'
96
+ # state and wait for onLoadFinished before sending a response.
97
+ @state = 'clicked'
98
98
 
99
99
  @page.get(id).click()
100
100
 
@@ -102,8 +102,9 @@ class Poltergeist.Browser
102
102
  # callback can (possibly) fire, before we decide whether to send a response.
103
103
  setTimeout(
104
104
  =>
105
- @page.onLoadStarted = null
106
- @owner.sendResponse(true) unless load_detected
105
+ if @state == 'clicked'
106
+ @state = 'default'
107
+ @owner.sendResponse(true)
107
108
  ,
108
109
  10
109
110
  )
@@ -177,22 +177,12 @@ PoltergeistAgent.Node = (function() {
177
177
  }
178
178
  };
179
179
  Node.prototype.position = function() {
180
- var pos;
181
- pos = function(element) {
182
- var parentPos, x, y;
183
- x = element.offsetLeft;
184
- y = element.offsetTop;
185
- if (element.offsetParent) {
186
- parentPos = pos(element.offsetParent);
187
- x += parentPos.x;
188
- y += parentPos.y;
189
- }
190
- return {
191
- x: x,
192
- y: y
193
- };
180
+ var rect;
181
+ rect = this.element.getBoundingClientRect();
182
+ return {
183
+ top: rect.top,
184
+ left: rect.left
194
185
  };
195
- return pos(this.element);
196
186
  };
197
187
  Node.prototype.trigger = function(name) {
198
188
  var event;
@@ -212,4 +202,10 @@ PoltergeistAgent.Node = (function() {
212
202
  window.__poltergeist = new PoltergeistAgent;
213
203
  document.addEventListener('DOMContentLoaded', function() {
214
204
  return console.log('__DOMContentLoaded');
215
- });
205
+ });
206
+ window.confirm = function(message) {
207
+ return true;
208
+ };
209
+ window.prompt = function(message, _default) {
210
+ return _default || null;
211
+ };
@@ -2,7 +2,7 @@ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments)
2
2
  Poltergeist.Browser = (function() {
3
3
  function Browser(owner) {
4
4
  this.owner = owner;
5
- this.awaiting_response = false;
5
+ this.state = 'default';
6
6
  this.resetPage();
7
7
  }
8
8
  Browser.prototype.resetPage = function() {
@@ -10,15 +10,20 @@ Poltergeist.Browser = (function() {
10
10
  this.page.release();
11
11
  }
12
12
  this.page = new Poltergeist.WebPage;
13
+ this.page.onLoadStarted = __bind(function() {
14
+ if (this.state === 'clicked') {
15
+ return this.state = 'loading';
16
+ }
17
+ }, this);
13
18
  return this.page.onLoadFinished = __bind(function(status) {
14
- if (this.awaiting_response) {
19
+ if (this.state === 'loading') {
15
20
  this.owner.sendResponse(status);
16
- return this.awaiting_response = false;
21
+ return this.state = 'default';
17
22
  }
18
23
  }, this);
19
24
  };
20
25
  Browser.prototype.visit = function(url) {
21
- this.awaiting_response = true;
26
+ this.state = 'loading';
22
27
  return this.page.open(url);
23
28
  };
24
29
  Browser.prototype.current_url = function() {
@@ -71,7 +76,7 @@ Poltergeist.Browser = (function() {
71
76
  return this.owner.sendResponse(this.page.get(id).isVisible());
72
77
  };
73
78
  Browser.prototype.evaluate = function(script) {
74
- return this.owner.sendResponse(this.page.evaluate("function() { return " + script + " }"));
79
+ return this.owner.sendResponse(JSON.parse(this.page.evaluate("function() { return JSON.stringify(" + script + ") }")));
75
80
  };
76
81
  Browser.prototype.execute = function(script) {
77
82
  this.page.execute("function() { " + script + " }");
@@ -86,16 +91,11 @@ Poltergeist.Browser = (function() {
86
91
  return this.owner.sendResponse(true);
87
92
  };
88
93
  Browser.prototype.click = function(id) {
89
- var load_detected;
90
- load_detected = false;
91
- this.page.onLoadStarted = __bind(function() {
92
- this.awaiting_response = true;
93
- return load_detected = true;
94
- }, this);
94
+ this.state = 'clicked';
95
95
  this.page.get(id).click();
96
96
  return setTimeout(__bind(function() {
97
- this.page.onLoadStarted = null;
98
- if (!load_detected) {
97
+ if (this.state === 'clicked') {
98
+ this.state = 'default';
99
99
  return this.owner.sendResponse(true);
100
100
  }
101
101
  }, this), 10);
@@ -1,8 +1,4 @@
1
1
  var Poltergeist;
2
- if (("" + phantom.version.major + "." + phantom.version.minor + "." + phantom.version.patch) < "1.4.1") {
3
- console.log("Poltergeist requires a PhantomJS version of at least 1.4.1");
4
- phantom.exit(1);
5
- }
6
2
  Poltergeist = (function() {
7
3
  function Poltergeist(port) {
8
4
  this.browser = new Poltergeist.Browser(this);
@@ -29,7 +29,7 @@ Poltergeist.Node = (function() {
29
29
  _fn(name);
30
30
  }
31
31
  Node.prototype.scrollIntoView = function() {
32
- var dimensions, document, pos, scroll, viewport, _ref2, _ref3;
32
+ var adjust, dimensions, document, pos, scroll, viewport;
33
33
  dimensions = this.page.validatedDimensions();
34
34
  document = dimensions.document;
35
35
  viewport = dimensions.viewport;
@@ -38,38 +38,33 @@ Poltergeist.Node = (function() {
38
38
  left: dimensions.left,
39
39
  top: dimensions.top
40
40
  };
41
- if (!((dimensions.left <= (_ref2 = pos.x) && _ref2 < dimensions.right))) {
42
- scroll.left = Math.min(pos.x, document.width - viewport.width);
43
- }
44
- if (!((dimensions.top <= (_ref3 = pos.y) && _ref3 < dimensions.bottom))) {
45
- scroll.top = Math.min(pos.y, document.height - viewport.height);
46
- }
41
+ adjust = function(coord, measurement) {
42
+ if (pos[coord] < 0) {
43
+ return scroll[coord] = Math.max(0, scroll[coord] + pos[coord] - (viewport[measurement] / 2));
44
+ } else if (pos[coord] >= viewport[measurement]) {
45
+ return scroll[coord] = Math.min(document[measurement] - viewport[measurement], scroll[coord] + pos[coord] - viewport[measurement] + (viewport[measurement] / 2));
46
+ }
47
+ };
48
+ adjust('left', 'width');
49
+ adjust('top', 'height');
47
50
  if (scroll.left !== dimensions.left || scroll.top !== dimensions.top) {
48
51
  this.page.setScrollPosition(scroll);
52
+ pos = this.position();
49
53
  }
50
- return {
51
- position: this.relativePosition(pos, scroll),
52
- scroll: scroll
53
- };
54
- };
55
- Node.prototype.relativePosition = function(position, scroll) {
56
- return {
57
- x: position.x - scroll.left,
58
- y: position.y - scroll.top
59
- };
54
+ return pos;
60
55
  };
61
56
  Node.prototype.click = function() {
62
57
  var position;
63
- position = this.scrollIntoView().position;
64
- return this.page.sendEvent('click', position.x, position.y);
58
+ position = this.scrollIntoView();
59
+ return this.page.sendEvent('click', position.left, position.top);
65
60
  };
66
61
  Node.prototype.dragTo = function(other) {
67
- var otherPosition, position, scroll, _ref2;
68
- _ref2 = this.scrollIntoView(), position = _ref2.position, scroll = _ref2.scroll;
69
- otherPosition = this.relativePosition(other.position(), scroll);
70
- this.page.sendEvent('mousedown', position.x, position.y);
71
- this.page.sendEvent('mousemove', otherPosition.x, otherPosition.y);
72
- return this.page.sendEvent('mouseup', otherPosition.x, otherPosition.y);
62
+ var otherPosition, position;
63
+ position = this.scrollIntoView();
64
+ otherPosition = other.position();
65
+ this.page.sendEvent('mousedown', position.left, position.top);
66
+ this.page.sendEvent('mousemove', otherPosition.left, otherPosition.top);
67
+ return this.page.sendEvent('mouseup', otherPosition.left, otherPosition.top);
73
68
  };
74
69
  return Node;
75
70
  }).call(this);
@@ -107,10 +107,9 @@ Poltergeist.WebPage = (function() {
107
107
  };
108
108
  };
109
109
  WebPage.prototype.validatedDimensions = function() {
110
- var changed, dimensions, document, orig_left, orig_top;
110
+ var dimensions, document, orig_left, orig_top;
111
111
  dimensions = this.dimensions();
112
112
  document = dimensions.document;
113
- changed = false;
114
113
  orig_left = dimensions.left;
115
114
  orig_top = dimensions.top;
116
115
  if (dimensions.right > document.width) {
@@ -1,7 +1,3 @@
1
- if "#{phantom.version.major}.#{phantom.version.minor}.#{phantom.version.patch}" < "1.4.1"
2
- console.log "Poltergeist requires a PhantomJS version of at least 1.4.1"
3
- phantom.exit(1)
4
-
5
1
  class Poltergeist
6
2
  constructor: (port) ->
7
3
  @browser = new Poltergeist.Browser(this)
@@ -28,30 +28,36 @@ class Poltergeist.Node
28
28
 
29
29
  scroll = { left: dimensions.left, top: dimensions.top }
30
30
 
31
- unless dimensions.left <= pos.x < dimensions.right
32
- scroll.left = Math.min(pos.x, document.width - viewport.width)
33
-
34
- unless dimensions.top <= pos.y < dimensions.bottom
35
- scroll.top = Math.min(pos.y, document.height - viewport.height)
31
+ adjust = (coord, measurement) ->
32
+ if pos[coord] < 0
33
+ scroll[coord] = Math.max(
34
+ 0,
35
+ scroll[coord] + pos[coord] - (viewport[measurement] / 2)
36
+ )
37
+
38
+ else if pos[coord] >= viewport[measurement]
39
+ scroll[coord] = Math.min(
40
+ document[measurement] - viewport[measurement],
41
+ scroll[coord] + pos[coord] - viewport[measurement] + (viewport[measurement] / 2)
42
+ )
43
+
44
+ adjust('left', 'width')
45
+ adjust('top', 'height')
36
46
 
37
47
  if scroll.left != dimensions.left || scroll.top != dimensions.top
38
48
  @page.setScrollPosition(scroll)
49
+ pos = this.position()
39
50
 
40
- position: this.relativePosition(pos, scroll),
41
- scroll: scroll
42
-
43
- relativePosition: (position, scroll) ->
44
- x: position.x - scroll.left
45
- y: position.y - scroll.top
51
+ pos
46
52
 
47
53
  click: ->
48
- position = this.scrollIntoView().position
49
- @page.sendEvent('click', position.x, position.y)
54
+ position = this.scrollIntoView()
55
+ @page.sendEvent('click', position.left, position.top)
50
56
 
51
57
  dragTo: (other) ->
52
- { position, scroll } = this.scrollIntoView()
53
- otherPosition = this.relativePosition(other.position(), scroll)
58
+ position = this.scrollIntoView()
59
+ otherPosition = other.position()
54
60
 
55
- @page.sendEvent('mousedown', position.x, position.y)
56
- @page.sendEvent('mousemove', otherPosition.x, otherPosition.y)
57
- @page.sendEvent('mouseup', otherPosition.x, otherPosition.y)
61
+ @page.sendEvent('mousedown', position.left, position.top)
62
+ @page.sendEvent('mousemove', otherPosition.left, otherPosition.top)
63
+ @page.sendEvent('mouseup', otherPosition.left, otherPosition.top)
@@ -83,7 +83,6 @@ class Poltergeist.WebPage
83
83
  validatedDimensions: ->
84
84
  dimensions = this.dimensions()
85
85
  document = dimensions.document
86
- changed = false
87
86
 
88
87
  orig_left = dimensions.left
89
88
  orig_top = dimensions.top
@@ -42,5 +42,17 @@ module Capybara
42
42
  "The PhantomJS client died while processing #{@message}"
43
43
  end
44
44
  end
45
+
46
+ class PhantomJSTooOld < Error
47
+ attr_reader :version
48
+
49
+ def initialize(version)
50
+ @version = version
51
+ end
52
+
53
+ def message
54
+ "PhantomJS version #{version} is too old. You must use at least version #{Client::PHANTOMJS_VERSION}"
55
+ end
56
+ end
45
57
  end
46
58
  end
@@ -1,5 +1,5 @@
1
1
  module Capybara
2
2
  module Poltergeist
3
- VERSION = "0.3.0"
3
+ VERSION = "0.4.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: poltergeist
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-13 00:00:00.000000000 Z
12
+ date: 2012-02-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: capybara
16
- requirement: &17990880 !ruby/object:Gem::Requirement
16
+ requirement: &16526020 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '1.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *17990880
24
+ version_requirements: *16526020
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: json
27
- requirement: &17990000 !ruby/object:Gem::Requirement
27
+ requirement: &16525520 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '1.6'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *17990000
35
+ version_requirements: *16525520
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: sfl
38
- requirement: &17988720 !ruby/object:Gem::Requirement
38
+ requirement: &16525060 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '2.0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *17988720
46
+ version_requirements: *16525060
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: http_parser.rb
49
- requirement: &17987320 !ruby/object:Gem::Requirement
49
+ requirement: &16524600 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 0.5.3
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *17987320
57
+ version_requirements: *16524600
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: faye-websocket
60
- requirement: &17986640 !ruby/object:Gem::Requirement
60
+ requirement: &16524140 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,21 +65,21 @@ dependencies:
65
65
  version: 0.2.0
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *17986640
68
+ version_requirements: *16524140
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
- requirement: &17985780 !ruby/object:Gem::Requirement
71
+ requirement: &16523680 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ~>
75
75
  - !ruby/object:Gem::Version
76
- version: 2.7.0
76
+ version: 2.8.0
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *17985780
79
+ version_requirements: *16523680
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: sinatra
82
- requirement: &17984960 !ruby/object:Gem::Requirement
82
+ requirement: &16523220 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ~>
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '1.0'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *17984960
90
+ version_requirements: *16523220
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: rake
93
- requirement: &17967380 !ruby/object:Gem::Requirement
93
+ requirement: &16522760 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ~>
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: 0.9.2
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *17967380
101
+ version_requirements: *16522760
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: image_size
104
- requirement: &17966680 !ruby/object:Gem::Requirement
104
+ requirement: &16522300 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ~>
@@ -109,7 +109,7 @@ dependencies:
109
109
  version: '1.0'
110
110
  type: :development
111
111
  prerelease: false
112
- version_requirements: *17966680
112
+ version_requirements: *16522300
113
113
  description: PhantomJS driver for Capybara
114
114
  email:
115
115
  - j@jonathanleighton.com
@@ -160,7 +160,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
160
160
  version: '0'
161
161
  requirements: []
162
162
  rubyforge_project:
163
- rubygems_version: 1.8.10
163
+ rubygems_version: 1.8.15
164
164
  signing_key:
165
165
  specification_version: 3
166
166
  summary: PhantomJS driver for Capybara