poltergeist 1.4.1 → 1.5.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 +4 -4
- data/LICENSE +1 -1
- data/README.md +39 -16
- data/lib/capybara/poltergeist/browser.rb +44 -1
- data/lib/capybara/poltergeist/client.rb +47 -20
- data/lib/capybara/poltergeist/client/agent.coffee +17 -7
- data/lib/capybara/poltergeist/client/browser.coffee +32 -0
- data/lib/capybara/poltergeist/client/compiled/agent.js +20 -8
- data/lib/capybara/poltergeist/client/compiled/browser.js +44 -0
- data/lib/capybara/poltergeist/client/compiled/main.js +3 -2
- data/lib/capybara/poltergeist/client/compiled/node.js +1 -1
- data/lib/capybara/poltergeist/client/compiled/web_page.js +25 -3
- data/lib/capybara/poltergeist/client/main.coffee +2 -2
- data/lib/capybara/poltergeist/client/node.coffee +1 -1
- data/lib/capybara/poltergeist/client/web_page.coffee +19 -2
- data/lib/capybara/poltergeist/driver.rb +27 -0
- data/lib/capybara/poltergeist/errors.rb +8 -4
- data/lib/capybara/poltergeist/node.rb +9 -4
- data/lib/capybara/poltergeist/version.rb +1 -1
- data/lib/capybara/poltergeist/web_socket_server.rb +6 -9
- metadata +26 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a65b5bdad64852e243d1097f272634b945bf2c09
|
4
|
+
data.tar.gz: 3f79fd0d9e6ea7c32667f278941cd6f394078092
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 416b123a2ab427a59e681dbc1a9c34cc904649f80f5da67dce666a3c27074f42153840471ceadb0d25ecb0cf57429762b6e37244cd0cab4489edc024f86094cb
|
7
|
+
data.tar.gz: 02a445954d2ee06aeb89db30ebcfcfa1da0201f0211c4762caab67d17d0ac852a5ccf1a75d1ec109783fc34c79fe9d3e261c24efaa6140d4086d0dfb1711d2d8
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -9,7 +9,7 @@ provided by [PhantomJS](http://www.phantomjs.org/).
|
|
9
9
|
**If you're viewing this at https://github.com/jonleighton/poltergeist,
|
10
10
|
you're reading the documentation for the master branch.
|
11
11
|
[View documentation for the latest release
|
12
|
-
(1.
|
12
|
+
(1.5.0).](https://github.com/jonleighton/poltergeist/tree/v1.5.0)**
|
13
13
|
|
14
14
|
## Getting help ##
|
15
15
|
|
@@ -43,26 +43,25 @@ dependencies* (you don't need Qt, or a running X server, etc.)
|
|
43
43
|
|
44
44
|
* *Homebrew*: `brew install phantomjs`
|
45
45
|
* *MacPorts*: `sudo port install phantomjs`
|
46
|
-
* *Manual install*: [Download this](http://
|
46
|
+
* *Manual install*: [Download this](http://phantomjs.googlecode.com/files/phantomjs-1.9.2-macosx.zip)
|
47
47
|
|
48
48
|
### Linux ###
|
49
49
|
|
50
|
-
* Download the [32
|
51
|
-
bit](
|
52
|
-
or [64
|
53
|
-
bit](http://code.google.com/p/phantomjs/downloads/detail?name=phantomjs-1.8.1-linux-x86_64.tar.bz2&can=2&q=)
|
50
|
+
* Download the [32 bit](https://phantomjs.googlecode.com/files/phantomjs-1.9.2-linux-i686.tar.bz2)
|
51
|
+
or [64 bit](https://phantomjs.googlecode.com/files/phantomjs-1.9.2-linux-x86_64.tar.bz2)
|
54
52
|
binary.
|
55
53
|
* Extract the tarball and copy `bin/phantomjs` into your `PATH`
|
56
54
|
|
57
55
|
### Windows ###
|
58
|
-
* Download the [precompiled binary](http://phantomjs.
|
56
|
+
* Download the [precompiled binary](http://phantomjs.googlecode.com/files/phantomjs-1.9.2-windows.zip)
|
57
|
+
for Windows
|
59
58
|
|
60
59
|
### Manual compilation ###
|
61
60
|
|
62
61
|
Do this as a last resort if the binaries don't work for you. It will
|
63
62
|
take quite a long time as it has to build WebKit.
|
64
63
|
|
65
|
-
* Download [the source tarball](http://
|
64
|
+
* Download [the source tarball](http://phantomjs.googlecode.com/files/phantomjs-1.9.2-source.zip)
|
66
65
|
* Extract and cd in
|
67
66
|
* `./build.sh`
|
68
67
|
|
@@ -83,10 +82,7 @@ was 1.0.2, so you should use that if you still need Ruby 1.8 support.
|
|
83
82
|
There are no special steps to take. You don't need Xvfb or any running X
|
84
83
|
server at all.
|
85
84
|
|
86
|
-
[Travis CI](https://travis-ci.org/) has PhantomJS pre-installed
|
87
|
-
might not be the latest version. If you need to install the latest
|
88
|
-
version, [check out the .travis.yml that Poltergeist
|
89
|
-
uses](https://github.com/jonleighton/poltergeist/blob/master/.travis.yml).
|
85
|
+
[Travis CI](https://travis-ci.org/) has PhantomJS pre-installed.
|
90
86
|
|
91
87
|
Depending on your tests, one thing that you may need is some fonts. If
|
92
88
|
you're getting errors on a CI that don't occur during development then
|
@@ -105,8 +101,10 @@ and the following optional features:
|
|
105
101
|
* `page.status_code`
|
106
102
|
* `page.response_headers`
|
107
103
|
* `page.save_screenshot`
|
108
|
-
* `page.render_base64`
|
109
|
-
* `page.scroll_to`
|
104
|
+
* `page.driver.render_base64(format, options)`
|
105
|
+
* `page.driver.scroll_to(left, top)`
|
106
|
+
* `page.driver.basic_authorize(user, password)`
|
107
|
+
* `element.native.send_keys(*keys)`
|
110
108
|
* cookie handling
|
111
109
|
* drag-and-drop
|
112
110
|
|
@@ -117,9 +115,12 @@ There are some additional features:
|
|
117
115
|
You can grab screenshots of the page at any point by calling
|
118
116
|
`save_screenshot('/path/to/file.png')` (this works the same way as the PhantomJS
|
119
117
|
render feature, so you can specify other extensions like `.pdf`, `.gif`, etc.)
|
118
|
+
Just in case you render pdf it's might be worth to set `driver.paper_size=` with
|
119
|
+
settings provided by PhantomJS in [here](https://github.com/ariya/phantomjs/wiki/API-Reference-WebPage#wiki-webpage-paperSize)
|
120
120
|
|
121
|
-
By default, only the viewport will be rendered (the part of the page that is in
|
122
|
-
the entire page, use `save_screenshot('/path/to/file.png',
|
121
|
+
By default, only the viewport will be rendered (the part of the page that is in
|
122
|
+
view). To render the entire page, use `save_screenshot('/path/to/file.png',
|
123
|
+
:full => true)`.
|
123
124
|
|
124
125
|
You also have an ability to render selected element. Pass option `selector` with
|
125
126
|
any valid element selector to make a screenshot bounded by that element
|
@@ -198,6 +199,9 @@ You can inspect the network traffic (i.e. what resources have been
|
|
198
199
|
loaded) on the current page by calling `page.driver.network_traffic`.
|
199
200
|
This returns an array of request objects. A request object has a
|
200
201
|
`response_parts` method containing data about the response chunks.
|
202
|
+
Please note that network traffic is not cleared when you visit new page.
|
203
|
+
You can manually clear the network traffic by calling `page.driver.clear_network_traffic`
|
204
|
+
or `page.driver.reset`
|
201
205
|
|
202
206
|
### Manipulating cookies ###
|
203
207
|
|
@@ -238,6 +242,25 @@ page.within_window fb_popup do
|
|
238
242
|
end
|
239
243
|
```
|
240
244
|
|
245
|
+
### Sending keys ###
|
246
|
+
|
247
|
+
There's an ability to send arbitrary keys to the element:
|
248
|
+
|
249
|
+
``` ruby
|
250
|
+
element = find('input#id')
|
251
|
+
element.native.send_key('String')
|
252
|
+
```
|
253
|
+
|
254
|
+
or even more complicated:
|
255
|
+
|
256
|
+
``` ruby
|
257
|
+
element.native.send_keys('H', 'elo', :Left, 'l') # => 'Hello'
|
258
|
+
element.native.send_key(:Enter) # triggers Enter key
|
259
|
+
```
|
260
|
+
Since it's implemented natively in PhantomJS this will exactly imitate user
|
261
|
+
behavior.
|
262
|
+
See more about [sendEvent](http://phantomjs.org/api/webpage/method/send-event.html) and
|
263
|
+
[PhantomJS keys](https://github.com/ariya/phantomjs/commit/cab2635e66d74b7e665c44400b8b20a8f225153a)
|
241
264
|
|
242
265
|
## Customization ##
|
243
266
|
|
@@ -66,6 +66,10 @@ module Capybara::Poltergeist
|
|
66
66
|
command 'visible_text', page_id, id
|
67
67
|
end
|
68
68
|
|
69
|
+
def delete_text(page_id, id)
|
70
|
+
command 'delete_text', page_id, id
|
71
|
+
end
|
72
|
+
|
69
73
|
def attribute(page_id, id, name)
|
70
74
|
command 'attribute', page_id, id, name.to_s
|
71
75
|
end
|
@@ -108,7 +112,7 @@ module Capybara::Poltergeist
|
|
108
112
|
|
109
113
|
def within_frame(handle, &block)
|
110
114
|
if handle.is_a?(Capybara::Node::Base)
|
111
|
-
command 'push_frame', handle[
|
115
|
+
command 'push_frame', handle[:name] || handle[:id]
|
112
116
|
else
|
113
117
|
command 'push_frame', handle
|
114
118
|
end
|
@@ -171,10 +175,18 @@ module Capybara::Poltergeist
|
|
171
175
|
command 'render_base64', format.to_s, !!options[:full], options[:selector]
|
172
176
|
end
|
173
177
|
|
178
|
+
def set_paper_size(size)
|
179
|
+
command 'set_paper_size', size
|
180
|
+
end
|
181
|
+
|
174
182
|
def resize(width, height)
|
175
183
|
command 'resize', width, height
|
176
184
|
end
|
177
185
|
|
186
|
+
def send_keys(page_id, id, keys)
|
187
|
+
command 'send_keys', page_id, id, normalize_keys(keys)
|
188
|
+
end
|
189
|
+
|
178
190
|
def network_traffic
|
179
191
|
command('network_traffic').values.map do |event|
|
180
192
|
NetworkTraffic::Request.new(
|
@@ -184,6 +196,10 @@ module Capybara::Poltergeist
|
|
184
196
|
end
|
185
197
|
end
|
186
198
|
|
199
|
+
def clear_network_traffic
|
200
|
+
command('clear_network_traffic')
|
201
|
+
end
|
202
|
+
|
187
203
|
def equals(page_id, id, other_id)
|
188
204
|
command('equals', page_id, id, other_id)
|
189
205
|
end
|
@@ -228,6 +244,10 @@ module Capybara::Poltergeist
|
|
228
244
|
command 'cookies_enabled', !!flag
|
229
245
|
end
|
230
246
|
|
247
|
+
def set_http_auth(user, password)
|
248
|
+
command 'set_http_auth', user, password
|
249
|
+
end
|
250
|
+
|
231
251
|
def js_errors=(val)
|
232
252
|
command 'set_js_errors', !!val
|
233
253
|
end
|
@@ -261,6 +281,14 @@ module Capybara::Poltergeist
|
|
261
281
|
raise
|
262
282
|
end
|
263
283
|
|
284
|
+
def go_back
|
285
|
+
command 'go_back'
|
286
|
+
end
|
287
|
+
|
288
|
+
def go_forward
|
289
|
+
command 'go_forward'
|
290
|
+
end
|
291
|
+
|
264
292
|
private
|
265
293
|
|
266
294
|
def log(message)
|
@@ -273,5 +301,20 @@ module Capybara::Poltergeist
|
|
273
301
|
options.delete(:selector)
|
274
302
|
end
|
275
303
|
end
|
304
|
+
|
305
|
+
def normalize_keys(keys)
|
306
|
+
keys.map do |key|
|
307
|
+
case key
|
308
|
+
when Array
|
309
|
+
# String itself with modifiers like :alt, :shift, etc
|
310
|
+
raise Error, 'PhantomJS behaviour for key modifiers is currently ' \
|
311
|
+
'broken, we will add this in later versions'
|
312
|
+
when Symbol
|
313
|
+
{ key: key } # Return a known sequence for PhantomJS
|
314
|
+
when String
|
315
|
+
key # Plain string, nothing to do
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
276
319
|
end
|
277
320
|
end
|
@@ -57,32 +57,16 @@ module Capybara::Poltergeist
|
|
57
57
|
|
58
58
|
redirect_stdout do
|
59
59
|
@pid = Process.spawn(*command.map(&:to_s), process_options)
|
60
|
-
ObjectSpace.define_finalizer(self, self.class.process_killer(@pid)
|
60
|
+
ObjectSpace.define_finalizer(self, self.class.process_killer(@pid))
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
64
|
def stop
|
65
65
|
if pid
|
66
|
-
|
67
|
-
if Capybara::Poltergeist.windows?
|
68
|
-
Process.kill('KILL', pid)
|
69
|
-
else
|
70
|
-
Process.kill('TERM', pid)
|
71
|
-
begin
|
72
|
-
Timeout.timeout(KILL_TIMEOUT) { Process.wait(pid) }
|
73
|
-
rescue Timeout::Error
|
74
|
-
Process.kill('KILL', pid)
|
75
|
-
Process.wait(pid)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
rescue Errno::ESRCH, Errno::ECHILD
|
79
|
-
# Zed's dead, baby
|
80
|
-
end
|
66
|
+
kill_phantomjs
|
81
67
|
@out_thread.kill
|
82
|
-
|
83
|
-
@read_io.close
|
68
|
+
close_io
|
84
69
|
ObjectSpace.undefine_finalizer(self)
|
85
|
-
@pid = nil
|
86
70
|
end
|
87
71
|
end
|
88
72
|
|
@@ -103,7 +87,10 @@ module Capybara::Poltergeist
|
|
103
87
|
private
|
104
88
|
|
105
89
|
# This abomination is because JRuby doesn't support the :out option of
|
106
|
-
# Process.spawn
|
90
|
+
# Process.spawn. To be honest it works pretty bad with pipes too, because
|
91
|
+
# we ought close writing end in parent process immediately but JRuby will
|
92
|
+
# lose all the output from child. Process.popen can be used here and seems
|
93
|
+
# it works with JRuby but I've experienced strange mistakes on Rubinius.
|
107
94
|
def redirect_stdout
|
108
95
|
prev = STDOUT.dup
|
109
96
|
prev.autoclose = false
|
@@ -114,5 +101,45 @@ module Capybara::Poltergeist
|
|
114
101
|
STDOUT.reopen(prev)
|
115
102
|
$stdout = STDOUT
|
116
103
|
end
|
104
|
+
|
105
|
+
def kill_phantomjs
|
106
|
+
begin
|
107
|
+
if Capybara::Poltergeist.windows?
|
108
|
+
Process.kill('KILL', pid)
|
109
|
+
else
|
110
|
+
Process.kill('TERM', pid)
|
111
|
+
begin
|
112
|
+
Timeout.timeout(KILL_TIMEOUT) { Process.wait(pid) }
|
113
|
+
rescue Timeout::Error
|
114
|
+
Process.kill('KILL', pid)
|
115
|
+
Process.wait(pid)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
rescue Errno::ESRCH, Errno::ECHILD
|
119
|
+
# Zed's dead, baby
|
120
|
+
end
|
121
|
+
@pid = nil
|
122
|
+
end
|
123
|
+
|
124
|
+
# We grab all the output from PhantomJS like console.log in another thread
|
125
|
+
# and when PhantomJS crashes we try to restart it. In order to do it we stop
|
126
|
+
# server and client and on JRuby see this error `IOError: Stream closed`.
|
127
|
+
# It happens because JRuby tries to close pipe and it is blocked on `eof?`
|
128
|
+
# or `readpartial` call. The error is raised in the related thread and it's
|
129
|
+
# not actually main thread but the thread that listens to the output. That's
|
130
|
+
# why if you put some debug code after `rescue IOError` it won't be shown.
|
131
|
+
# In fact the main thread will continue working after the error even if we
|
132
|
+
# don't use `rescue`. The first attempt to fix it was a try not to block on
|
133
|
+
# IO, but looks like similar issue appers after JRuby upgrade. Perhaps the
|
134
|
+
# only way to fix it is catching the exception what this method overall does.
|
135
|
+
def close_io
|
136
|
+
[@write_io, @read_io].each do |io|
|
137
|
+
begin
|
138
|
+
io.close unless io.closed?
|
139
|
+
rescue IOError
|
140
|
+
raise unless RUBY_ENGINE == 'jruby'
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
117
144
|
end
|
118
145
|
end
|
@@ -37,7 +37,8 @@ class PoltergeistAgent
|
|
37
37
|
|
38
38
|
this.register(el) for el in results
|
39
39
|
catch error
|
40
|
-
|
40
|
+
# DOMException.INVALID_EXPRESSION_ERR is undefined, using pure code
|
41
|
+
if error.code == DOMException.SYNTAX_ERR || error.code == 51
|
41
42
|
throw new PoltergeistAgent.InvalidSelector
|
42
43
|
else
|
43
44
|
throw error
|
@@ -140,6 +141,12 @@ class PoltergeistAgent.Node
|
|
140
141
|
else
|
141
142
|
@element.innerText
|
142
143
|
|
144
|
+
deleteText: ->
|
145
|
+
range = document.createRange()
|
146
|
+
range.selectNodeContents(@element)
|
147
|
+
window.getSelection().addRange(range)
|
148
|
+
window.getSelection().deleteFromDocument()
|
149
|
+
|
143
150
|
getAttribute: (name) ->
|
144
151
|
if name == 'checked' || name == 'selected'
|
145
152
|
@element[name]
|
@@ -164,13 +171,16 @@ class PoltergeistAgent.Node
|
|
164
171
|
@element.value = ''
|
165
172
|
this.trigger('focus')
|
166
173
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
174
|
+
if @element.type == 'number'
|
175
|
+
@element.value = value
|
176
|
+
else
|
177
|
+
for char in value
|
178
|
+
keyCode = this.characterToKeyCode(char)
|
179
|
+
this.keyupdowned('keydown', keyCode)
|
180
|
+
@element.value += char
|
171
181
|
|
172
|
-
|
173
|
-
|
182
|
+
this.keypressed(false, false, false, false, char.charCodeAt(0), char.charCodeAt(0))
|
183
|
+
this.keyupdowned('keyup', keyCode)
|
174
184
|
|
175
185
|
this.changed()
|
176
186
|
this.input()
|
@@ -121,6 +121,9 @@ class Poltergeist.Browser
|
|
121
121
|
visible_text: (page_id, id) ->
|
122
122
|
this.sendResponse this.node(page_id, id).visibleText()
|
123
123
|
|
124
|
+
delete_text: (page_id, id) ->
|
125
|
+
this.sendResponse this.node(page_id, id).deleteText()
|
126
|
+
|
124
127
|
attribute: (page_id, id, name) ->
|
125
128
|
this.sendResponse this.node(page_id, id).getAttribute(name)
|
126
129
|
|
@@ -250,6 +253,15 @@ class Poltergeist.Browser
|
|
250
253
|
@page.setScrollPosition(left: left, top: top)
|
251
254
|
this.sendResponse(true)
|
252
255
|
|
256
|
+
send_keys: (page_id, id, keys) ->
|
257
|
+
# Programmatically generated focus doesn't work for `sendKeys`.
|
258
|
+
# That's why we need something more realistic like user behavior.
|
259
|
+
this.node(page_id, id).mouseEvent('click')
|
260
|
+
for sequence in keys
|
261
|
+
key = if sequence.key? then @page.native.event.key[sequence.key] else sequence
|
262
|
+
@page.sendEvent('keypress', key)
|
263
|
+
this.sendResponse(true)
|
264
|
+
|
253
265
|
render_base64: (format, full, selector = null)->
|
254
266
|
this.set_clip_rect(full, selector)
|
255
267
|
encoded_image = @page.renderBase64(format)
|
@@ -277,6 +289,10 @@ class Poltergeist.Browser
|
|
277
289
|
@page.setClipRect(rect)
|
278
290
|
dimensions
|
279
291
|
|
292
|
+
set_paper_size: (size) ->
|
293
|
+
@page.setPaperSize(size)
|
294
|
+
this.sendResponse(true)
|
295
|
+
|
280
296
|
resize: (width, height) ->
|
281
297
|
@page.setViewportSize(width: width, height: height)
|
282
298
|
this.sendResponse(true)
|
@@ -284,6 +300,10 @@ class Poltergeist.Browser
|
|
284
300
|
network_traffic: ->
|
285
301
|
this.sendResponse(@page.networkTraffic())
|
286
302
|
|
303
|
+
clear_network_traffic: ->
|
304
|
+
@page.clearNetworkTraffic()
|
305
|
+
this.sendResponse(true)
|
306
|
+
|
287
307
|
get_headers: ->
|
288
308
|
this.sendResponse(@page.getCustomHeaders())
|
289
309
|
|
@@ -323,6 +343,10 @@ class Poltergeist.Browser
|
|
323
343
|
phantom.cookiesEnabled = flag
|
324
344
|
this.sendResponse(true)
|
325
345
|
|
346
|
+
set_http_auth: (user, password) ->
|
347
|
+
@page.setHttpAuth(user, password)
|
348
|
+
this.sendResponse(true)
|
349
|
+
|
326
350
|
set_js_errors: (value) ->
|
327
351
|
@js_errors = value
|
328
352
|
this.sendResponse(true)
|
@@ -340,3 +364,11 @@ class Poltergeist.Browser
|
|
340
364
|
# This command is purely for testing error handling
|
341
365
|
browser_error: ->
|
342
366
|
throw new Error('zomg')
|
367
|
+
|
368
|
+
go_back: ->
|
369
|
+
this.page.goBack() if this.page.canGoBack
|
370
|
+
this.sendResponse(true)
|
371
|
+
|
372
|
+
go_forward: ->
|
373
|
+
this.page.goForward() if this.page.canGoForward
|
374
|
+
this.sendResponse(true)
|
@@ -74,7 +74,7 @@ PoltergeistAgent = (function() {
|
|
74
74
|
return _results;
|
75
75
|
} catch (_error) {
|
76
76
|
error = _error;
|
77
|
-
if (error.code === DOMException.SYNTAX_ERR) {
|
77
|
+
if (error.code === DOMException.SYNTAX_ERR || error.code === 51) {
|
78
78
|
throw new PoltergeistAgent.InvalidSelector;
|
79
79
|
} else {
|
80
80
|
throw error;
|
@@ -233,6 +233,14 @@ PoltergeistAgent.Node = (function() {
|
|
233
233
|
}
|
234
234
|
};
|
235
235
|
|
236
|
+
Node.prototype.deleteText = function() {
|
237
|
+
var range;
|
238
|
+
range = document.createRange();
|
239
|
+
range.selectNodeContents(this.element);
|
240
|
+
window.getSelection().addRange(range);
|
241
|
+
return window.getSelection().deleteFromDocument();
|
242
|
+
};
|
243
|
+
|
236
244
|
Node.prototype.getAttribute = function(name) {
|
237
245
|
if (name === 'checked' || name === 'selected') {
|
238
246
|
return this.element[name];
|
@@ -272,13 +280,17 @@ PoltergeistAgent.Node = (function() {
|
|
272
280
|
}
|
273
281
|
this.element.value = '';
|
274
282
|
this.trigger('focus');
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
283
|
+
if (this.element.type === 'number') {
|
284
|
+
this.element.value = value;
|
285
|
+
} else {
|
286
|
+
for (_i = 0, _len = value.length; _i < _len; _i++) {
|
287
|
+
char = value[_i];
|
288
|
+
keyCode = this.characterToKeyCode(char);
|
289
|
+
this.keyupdowned('keydown', keyCode);
|
290
|
+
this.element.value += char;
|
291
|
+
this.keypressed(false, false, false, false, char.charCodeAt(0), char.charCodeAt(0));
|
292
|
+
this.keyupdowned('keyup', keyCode);
|
293
|
+
}
|
282
294
|
}
|
283
295
|
this.changed();
|
284
296
|
this.input();
|
@@ -153,6 +153,10 @@ Poltergeist.Browser = (function() {
|
|
153
153
|
return this.sendResponse(this.node(page_id, id).visibleText());
|
154
154
|
};
|
155
155
|
|
156
|
+
Browser.prototype.delete_text = function(page_id, id) {
|
157
|
+
return this.sendResponse(this.node(page_id, id).deleteText());
|
158
|
+
};
|
159
|
+
|
156
160
|
Browser.prototype.attribute = function(page_id, id, name) {
|
157
161
|
return this.sendResponse(this.node(page_id, id).getAttribute(name));
|
158
162
|
};
|
@@ -324,6 +328,17 @@ Poltergeist.Browser = (function() {
|
|
324
328
|
return this.sendResponse(true);
|
325
329
|
};
|
326
330
|
|
331
|
+
Browser.prototype.send_keys = function(page_id, id, keys) {
|
332
|
+
var key, sequence, _i, _len;
|
333
|
+
this.node(page_id, id).mouseEvent('click');
|
334
|
+
for (_i = 0, _len = keys.length; _i < _len; _i++) {
|
335
|
+
sequence = keys[_i];
|
336
|
+
key = sequence.key != null ? this.page["native"].event.key[sequence.key] : sequence;
|
337
|
+
this.page.sendEvent('keypress', key);
|
338
|
+
}
|
339
|
+
return this.sendResponse(true);
|
340
|
+
};
|
341
|
+
|
327
342
|
Browser.prototype.render_base64 = function(format, full, selector) {
|
328
343
|
var encoded_image;
|
329
344
|
if (selector == null) {
|
@@ -371,6 +386,11 @@ Poltergeist.Browser = (function() {
|
|
371
386
|
return dimensions;
|
372
387
|
};
|
373
388
|
|
389
|
+
Browser.prototype.set_paper_size = function(size) {
|
390
|
+
this.page.setPaperSize(size);
|
391
|
+
return this.sendResponse(true);
|
392
|
+
};
|
393
|
+
|
374
394
|
Browser.prototype.resize = function(width, height) {
|
375
395
|
this.page.setViewportSize({
|
376
396
|
width: width,
|
@@ -383,6 +403,11 @@ Poltergeist.Browser = (function() {
|
|
383
403
|
return this.sendResponse(this.page.networkTraffic());
|
384
404
|
};
|
385
405
|
|
406
|
+
Browser.prototype.clear_network_traffic = function() {
|
407
|
+
this.page.clearNetworkTraffic();
|
408
|
+
return this.sendResponse(true);
|
409
|
+
};
|
410
|
+
|
386
411
|
Browser.prototype.get_headers = function() {
|
387
412
|
return this.sendResponse(this.page.getCustomHeaders());
|
388
413
|
};
|
@@ -435,6 +460,11 @@ Poltergeist.Browser = (function() {
|
|
435
460
|
return this.sendResponse(true);
|
436
461
|
};
|
437
462
|
|
463
|
+
Browser.prototype.set_http_auth = function(user, password) {
|
464
|
+
this.page.setHttpAuth(user, password);
|
465
|
+
return this.sendResponse(true);
|
466
|
+
};
|
467
|
+
|
438
468
|
Browser.prototype.set_js_errors = function(value) {
|
439
469
|
this.js_errors = value;
|
440
470
|
return this.sendResponse(true);
|
@@ -455,6 +485,20 @@ Poltergeist.Browser = (function() {
|
|
455
485
|
throw new Error('zomg');
|
456
486
|
};
|
457
487
|
|
488
|
+
Browser.prototype.go_back = function() {
|
489
|
+
if (this.page.canGoBack) {
|
490
|
+
this.page.goBack();
|
491
|
+
}
|
492
|
+
return this.sendResponse(true);
|
493
|
+
};
|
494
|
+
|
495
|
+
Browser.prototype.go_forward = function() {
|
496
|
+
if (this.page.canGoForward) {
|
497
|
+
this.page.goForward();
|
498
|
+
}
|
499
|
+
return this.sendResponse(true);
|
500
|
+
};
|
501
|
+
|
458
502
|
return Browser;
|
459
503
|
|
460
504
|
})();
|
@@ -89,14 +89,15 @@ Poltergeist.ObsoleteNode = (function(_super) {
|
|
89
89
|
Poltergeist.InvalidSelector = (function(_super) {
|
90
90
|
__extends(InvalidSelector, _super);
|
91
91
|
|
92
|
-
function InvalidSelector(selector) {
|
92
|
+
function InvalidSelector(method, selector) {
|
93
|
+
this.method = method;
|
93
94
|
this.selector = selector;
|
94
95
|
}
|
95
96
|
|
96
97
|
InvalidSelector.prototype.name = "Poltergeist.InvalidSelector";
|
97
98
|
|
98
99
|
InvalidSelector.prototype.args = function() {
|
99
|
-
return [this.selector];
|
100
|
+
return [this.method, this.selector];
|
100
101
|
};
|
101
102
|
|
102
103
|
return InvalidSelector;
|
@@ -4,7 +4,7 @@ Poltergeist.Node = (function() {
|
|
4
4
|
var name, _fn, _i, _len, _ref,
|
5
5
|
_this = this;
|
6
6
|
|
7
|
-
Node.DELEGATES = ['allText', 'visibleText', 'getAttribute', 'value', 'set', 'setAttribute', 'isObsolete', 'removeAttribute', 'isMultiple', 'select', 'tagName', 'find', 'isVisible', 'position', 'trigger', 'parentId', 'mouseEventTest', 'scrollIntoView', 'isDOMEqual', 'isDisabled'];
|
7
|
+
Node.DELEGATES = ['allText', 'visibleText', 'getAttribute', 'value', 'set', 'setAttribute', 'isObsolete', 'removeAttribute', 'isMultiple', 'select', 'tagName', 'find', 'isVisible', 'position', 'trigger', 'parentId', 'mouseEventTest', 'scrollIntoView', 'isDOMEqual', 'isDisabled', 'deleteText'];
|
8
8
|
|
9
9
|
function Node(page, id) {
|
10
10
|
this.page = page;
|
@@ -6,7 +6,7 @@ Poltergeist.WebPage = (function() {
|
|
6
6
|
|
7
7
|
WebPage.CALLBACKS = ['onAlert', 'onConsoleMessage', 'onLoadFinished', 'onInitialized', 'onLoadStarted', 'onResourceRequested', 'onResourceReceived', 'onError', 'onNavigationRequested', 'onUrlChanged', 'onPageCreated'];
|
8
8
|
|
9
|
-
WebPage.DELEGATES = ['open', 'sendEvent', 'uploadFile', 'release', 'render', 'renderBase64'];
|
9
|
+
WebPage.DELEGATES = ['open', 'sendEvent', 'uploadFile', 'release', 'render', 'renderBase64', 'goBack', 'goForward'];
|
10
10
|
|
11
11
|
WebPage.COMMANDS = ['currentUrl', 'find', 'nodeCall', 'documentSize', 'beforeUpload', 'afterUpload'];
|
12
12
|
|
@@ -145,10 +145,19 @@ Poltergeist.WebPage = (function() {
|
|
145
145
|
}
|
146
146
|
};
|
147
147
|
|
148
|
+
WebPage.prototype.setHttpAuth = function(user, password) {
|
149
|
+
this["native"].settings.userName = user;
|
150
|
+
return this["native"].settings.password = password;
|
151
|
+
};
|
152
|
+
|
148
153
|
WebPage.prototype.networkTraffic = function() {
|
149
154
|
return this._networkTraffic;
|
150
155
|
};
|
151
156
|
|
157
|
+
WebPage.prototype.clearNetworkTraffic = function() {
|
158
|
+
return this._networkTraffic = {};
|
159
|
+
};
|
160
|
+
|
152
161
|
WebPage.prototype.content = function() {
|
153
162
|
return this["native"].frameContent;
|
154
163
|
};
|
@@ -198,6 +207,10 @@ Poltergeist.WebPage = (function() {
|
|
198
207
|
return this["native"].viewportSize = size;
|
199
208
|
};
|
200
209
|
|
210
|
+
WebPage.prototype.setPaperSize = function(size) {
|
211
|
+
return this["native"].paperSize = size;
|
212
|
+
};
|
213
|
+
|
201
214
|
WebPage.prototype.scrollPosition = function() {
|
202
215
|
return this["native"].scrollPosition;
|
203
216
|
};
|
@@ -357,7 +370,7 @@ Poltergeist.WebPage = (function() {
|
|
357
370
|
};
|
358
371
|
|
359
372
|
WebPage.prototype.runCommand = function(name, args) {
|
360
|
-
var result;
|
373
|
+
var method, result, selector;
|
361
374
|
result = this.evaluate(function(name, args) {
|
362
375
|
return __poltergeist.externalCall(name, args);
|
363
376
|
}, name, args);
|
@@ -367,7 +380,8 @@ Poltergeist.WebPage = (function() {
|
|
367
380
|
throw new Poltergeist.ObsoleteNode;
|
368
381
|
break;
|
369
382
|
case 'PoltergeistAgent.InvalidSelector':
|
370
|
-
|
383
|
+
method = args[0], selector = args[1];
|
384
|
+
throw new Poltergeist.InvalidSelector(method, selector);
|
371
385
|
break;
|
372
386
|
default:
|
373
387
|
throw new Poltergeist.BrowserError(result.error.message, result.error.stack);
|
@@ -377,6 +391,14 @@ Poltergeist.WebPage = (function() {
|
|
377
391
|
}
|
378
392
|
};
|
379
393
|
|
394
|
+
WebPage.prototype.canGoBack = function() {
|
395
|
+
return this["native"].canGoBack;
|
396
|
+
};
|
397
|
+
|
398
|
+
WebPage.prototype.canGoForward = function() {
|
399
|
+
return this["native"].canGoForward;
|
400
|
+
};
|
401
|
+
|
380
402
|
return WebPage;
|
381
403
|
|
382
404
|
}).call(this);
|
@@ -51,9 +51,9 @@ class Poltergeist.ObsoleteNode extends Poltergeist.Error
|
|
51
51
|
toString: -> this.name
|
52
52
|
|
53
53
|
class Poltergeist.InvalidSelector extends Poltergeist.Error
|
54
|
-
constructor: (@selector) ->
|
54
|
+
constructor: (@method, @selector) ->
|
55
55
|
name: "Poltergeist.InvalidSelector"
|
56
|
-
args: -> [@selector]
|
56
|
+
args: -> [@method, @selector]
|
57
57
|
|
58
58
|
class Poltergeist.FrameNotFound extends Poltergeist.Error
|
59
59
|
constructor: (@frameName) ->
|
@@ -4,7 +4,7 @@ class Poltergeist.Node
|
|
4
4
|
@DELEGATES = ['allText', 'visibleText', 'getAttribute', 'value', 'set', 'setAttribute', 'isObsolete',
|
5
5
|
'removeAttribute', 'isMultiple', 'select', 'tagName', 'find',
|
6
6
|
'isVisible', 'position', 'trigger', 'parentId', 'mouseEventTest',
|
7
|
-
'scrollIntoView', 'isDOMEqual', 'isDisabled']
|
7
|
+
'scrollIntoView', 'isDOMEqual', 'isDisabled', 'deleteText']
|
8
8
|
|
9
9
|
constructor: (@page, @id) ->
|
10
10
|
|
@@ -3,7 +3,7 @@ class Poltergeist.WebPage
|
|
3
3
|
'onLoadStarted', 'onResourceRequested', 'onResourceReceived',
|
4
4
|
'onError', 'onNavigationRequested', 'onUrlChanged', 'onPageCreated']
|
5
5
|
|
6
|
-
@DELEGATES = ['open', 'sendEvent', 'uploadFile', 'release', 'render', 'renderBase64']
|
6
|
+
@DELEGATES = ['open', 'sendEvent', 'uploadFile', 'release', 'render', 'renderBase64', 'goBack', 'goForward']
|
7
7
|
|
8
8
|
@COMMANDS = ['currentUrl', 'find', 'nodeCall', 'documentSize', 'beforeUpload', 'afterUpload']
|
9
9
|
|
@@ -93,9 +93,16 @@ class Poltergeist.WebPage
|
|
93
93
|
@_statusCode = response.status
|
94
94
|
@_responseHeaders = response.headers
|
95
95
|
|
96
|
+
setHttpAuth: (user, password) ->
|
97
|
+
@native.settings.userName = user
|
98
|
+
@native.settings.password = password
|
99
|
+
|
96
100
|
networkTraffic: ->
|
97
101
|
@_networkTraffic
|
98
102
|
|
103
|
+
clearNetworkTraffic: ->
|
104
|
+
@_networkTraffic = {}
|
105
|
+
|
99
106
|
content: ->
|
100
107
|
@native.frameContent
|
101
108
|
|
@@ -132,6 +139,9 @@ class Poltergeist.WebPage
|
|
132
139
|
setViewportSize: (size) ->
|
133
140
|
@native.viewportSize = size
|
134
141
|
|
142
|
+
setPaperSize: (size) ->
|
143
|
+
@native.paperSize = size
|
144
|
+
|
135
145
|
scrollPosition: ->
|
136
146
|
@native.scrollPosition
|
137
147
|
|
@@ -262,8 +272,15 @@ class Poltergeist.WebPage
|
|
262
272
|
when 'PoltergeistAgent.ObsoleteNode'
|
263
273
|
throw new Poltergeist.ObsoleteNode
|
264
274
|
when 'PoltergeistAgent.InvalidSelector'
|
265
|
-
|
275
|
+
[method, selector] = args
|
276
|
+
throw new Poltergeist.InvalidSelector(method, selector)
|
266
277
|
else
|
267
278
|
throw new Poltergeist.BrowserError(result.error.message, result.error.stack)
|
268
279
|
else
|
269
280
|
result.value
|
281
|
+
|
282
|
+
canGoBack: ->
|
283
|
+
@native.canGoBack
|
284
|
+
|
285
|
+
canGoForward: ->
|
286
|
+
@native.canGoForward
|
@@ -161,6 +161,10 @@ module Capybara::Poltergeist
|
|
161
161
|
browser.render_base64(format, options)
|
162
162
|
end
|
163
163
|
|
164
|
+
def paper_size=(size = {})
|
165
|
+
browser.set_paper_size(size)
|
166
|
+
end
|
167
|
+
|
164
168
|
def resize(width, height)
|
165
169
|
browser.resize(width, height)
|
166
170
|
end
|
@@ -174,6 +178,10 @@ module Capybara::Poltergeist
|
|
174
178
|
browser.network_traffic
|
175
179
|
end
|
176
180
|
|
181
|
+
def clear_network_traffic
|
182
|
+
browser.clear_network_traffic
|
183
|
+
end
|
184
|
+
|
177
185
|
def headers
|
178
186
|
browser.get_headers
|
179
187
|
end
|
@@ -221,6 +229,17 @@ module Capybara::Poltergeist
|
|
221
229
|
browser.cookies_enabled = flag
|
222
230
|
end
|
223
231
|
|
232
|
+
# * PhantomJS with set settings doesn't send `Authorize` on POST request
|
233
|
+
# * With manually set header PhantomJS makes next request with
|
234
|
+
# `Authorization: Basic Og==` header when settings are empty and the
|
235
|
+
# response was `401 Unauthorized` (which means Base64.encode64(':')).
|
236
|
+
# Combining both methods to reach proper behavior.
|
237
|
+
def basic_authorize(user, password)
|
238
|
+
browser.set_http_auth(user, password)
|
239
|
+
credentials = ["#{user}:#{password}"].pack('m*').strip
|
240
|
+
add_header('Authorization', "Basic #{credentials}")
|
241
|
+
end
|
242
|
+
|
224
243
|
def debug
|
225
244
|
if @options[:inspector]
|
226
245
|
inspector.open
|
@@ -243,5 +262,13 @@ module Capybara::Poltergeist
|
|
243
262
|
def invalid_element_errors
|
244
263
|
[Capybara::Poltergeist::ObsoleteNode, Capybara::Poltergeist::MouseEventFailed]
|
245
264
|
end
|
265
|
+
|
266
|
+
def go_back
|
267
|
+
browser.go_back
|
268
|
+
end
|
269
|
+
|
270
|
+
def go_forward
|
271
|
+
browser.go_forward
|
272
|
+
end
|
246
273
|
end
|
247
274
|
end
|
@@ -65,13 +65,17 @@ module Capybara
|
|
65
65
|
end
|
66
66
|
|
67
67
|
class InvalidSelector < ClientError
|
68
|
+
def method
|
69
|
+
response['args'][0]
|
70
|
+
end
|
71
|
+
|
68
72
|
def selector
|
69
|
-
response['args']
|
73
|
+
response['args'][1]
|
70
74
|
end
|
71
75
|
|
72
76
|
def message
|
73
77
|
"The browser raised a syntax error while trying to evaluate " \
|
74
|
-
"
|
78
|
+
"#{method} selector #{selector.inspect}"
|
75
79
|
end
|
76
80
|
end
|
77
81
|
|
@@ -142,7 +146,7 @@ module Capybara
|
|
142
146
|
class PhantomJSTooOld < Error
|
143
147
|
def self.===(other)
|
144
148
|
if Cliver::Dependency::VersionMismatch === other
|
145
|
-
warn "{name} exception has been deprecated in favor of using the " +
|
149
|
+
warn "#{name} exception has been deprecated in favor of using the " +
|
146
150
|
"cliver gem for command-line dependency detection. Please " +
|
147
151
|
"handle Cliver::Dependency::VersionMismatch instead."
|
148
152
|
true
|
@@ -155,7 +159,7 @@ module Capybara
|
|
155
159
|
class PhantomJSFailed < Error
|
156
160
|
def self.===(other)
|
157
161
|
if Cliver::Dependency::NotMet === other
|
158
|
-
warn "{name} exception has been deprecated in favor of using the " +
|
162
|
+
warn "#{name} exception has been deprecated in favor of using the " +
|
159
163
|
"cliver gem for command-line dependency detection. Please " +
|
160
164
|
"handle Cliver::Dependency::NotMet instead."
|
161
165
|
true
|
@@ -1,8 +1,5 @@
|
|
1
1
|
module Capybara::Poltergeist
|
2
2
|
class Node < Capybara::Driver::Node
|
3
|
-
NBSP = "\xC2\xA0"
|
4
|
-
NBSP.force_encoding("UTF-8") if NBSP.respond_to?(:force_encoding)
|
5
|
-
|
6
3
|
attr_reader :page_id, :id
|
7
4
|
|
8
5
|
def initialize(driver, page_id, id)
|
@@ -72,6 +69,9 @@ module Capybara::Poltergeist
|
|
72
69
|
end
|
73
70
|
elsif tag_name == 'textarea'
|
74
71
|
command :set, value.to_s
|
72
|
+
elsif self[:contenteditable] == 'true'
|
73
|
+
command :delete_text
|
74
|
+
send_keys(value.to_s)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
@@ -128,10 +128,15 @@ module Capybara::Poltergeist
|
|
128
128
|
command :equals, other.id
|
129
129
|
end
|
130
130
|
|
131
|
+
def send_keys(*keys)
|
132
|
+
command :send_keys, keys
|
133
|
+
end
|
134
|
+
alias_method :send_key, :send_keys
|
135
|
+
|
131
136
|
private
|
132
137
|
|
133
138
|
def filter_text(text)
|
134
|
-
text.to_s
|
139
|
+
Capybara::Helpers.normalize_whitespace(text.to_s)
|
135
140
|
end
|
136
141
|
end
|
137
142
|
end
|
@@ -26,15 +26,13 @@ module Capybara::Poltergeist
|
|
26
26
|
@server = start_server(port)
|
27
27
|
end
|
28
28
|
|
29
|
-
def port
|
30
|
-
server.addr[1]
|
31
|
-
end
|
32
|
-
|
33
29
|
def start_server(port)
|
34
30
|
time = Time.now
|
35
31
|
|
36
32
|
begin
|
37
|
-
TCPServer.open(HOST, port || 0)
|
33
|
+
TCPServer.open(HOST, port || 0).tap do |server|
|
34
|
+
@port = server.addr[1]
|
35
|
+
end
|
38
36
|
rescue Errno::EADDRINUSE
|
39
37
|
if (Time.now - time) < BIND_TIMEOUT
|
40
38
|
sleep(0.01)
|
@@ -89,11 +87,10 @@ module Capybara::Poltergeist
|
|
89
87
|
raise TimeoutError.new(message)
|
90
88
|
end
|
91
89
|
|
90
|
+
# Closing sockets separately as `close_read`, `close_write`
|
91
|
+
# causes IO mistakes on JRuby, using just `close` fixes that.
|
92
92
|
def close
|
93
|
-
[server, socket].compact.each
|
94
|
-
s.close_read
|
95
|
-
s.close_write
|
96
|
-
end
|
93
|
+
[server, socket].compact.each(&:close)
|
97
94
|
end
|
98
95
|
end
|
99
96
|
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.
|
4
|
+
version: 1.5.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: 2013-09
|
11
|
+
date: 2013-12-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: capybara
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2.1
|
19
|
+
version: '2.1'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 2.1
|
26
|
+
version: '2.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: websocket-driver
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ~>
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.
|
61
|
+
version: 0.3.1
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ~>
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.
|
68
|
+
version: 0.3.1
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rspec
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,6 +122,20 @@ dependencies:
|
|
122
122
|
- - ~>
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '1.0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: pdf-reader
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ~>
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 1.3.3
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ~>
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 1.3.3
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
140
|
name: coffee-script
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,7 +178,8 @@ dependencies:
|
|
164
178
|
- - ~>
|
165
179
|
- !ruby/object:Gem::Version
|
166
180
|
version: '0.1'
|
167
|
-
description:
|
181
|
+
description: Poltergeist is a driver for Capybara that allows you to run your tests
|
182
|
+
on a headless WebKit browser, provided by PhantomJS.
|
168
183
|
email:
|
169
184
|
- j@jonathanleighton.com
|
170
185
|
executables: []
|
@@ -202,7 +217,8 @@ files:
|
|
202
217
|
- LICENSE
|
203
218
|
- README.md
|
204
219
|
homepage: http://github.com/jonleighton/poltergeist
|
205
|
-
licenses:
|
220
|
+
licenses:
|
221
|
+
- MIT
|
206
222
|
metadata: {}
|
207
223
|
post_install_message:
|
208
224
|
rdoc_options: []
|
@@ -212,7 +228,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
212
228
|
requirements:
|
213
229
|
- - '>='
|
214
230
|
- !ruby/object:Gem::Version
|
215
|
-
version: 1.9.
|
231
|
+
version: 1.9.3
|
216
232
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
217
233
|
requirements:
|
218
234
|
- - '>='
|
@@ -220,7 +236,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
220
236
|
version: '0'
|
221
237
|
requirements: []
|
222
238
|
rubyforge_project:
|
223
|
-
rubygems_version: 2.
|
239
|
+
rubygems_version: 2.1.11
|
224
240
|
signing_key:
|
225
241
|
specification_version: 4
|
226
242
|
summary: PhantomJS driver for Capybara
|