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