Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: exviva/poltergeist
base: b7ceeb6ae9
...
head fork: exviva/poltergeist
compare: 8cf26f3f34
Checking mergeability… Don't worry, you can still create the pull request.
  • 9 commits
  • 18 files changed
  • 0 commit comments
  • 2 contributors
Commits on Oct 03, 2013
@route route Update the other links on PhantomJS builds [ci skip] 00f7b2a
@route route #395 Add better error message for wrong xpath
If someone fill wrong selector in `find` method this poor description will be shown:
"There was an error inside the PhantomJS portion of Poltergeist.
This is probably a bug, so please report it.
Error: INVALID_EXPRESSION_ERR: DOM XPath Exception 51"
Now we raise `InvalidSelector` in consistency with selenium.
c48cff0
Commits on Oct 05, 2013
@route route Fix #398 remember server port rather than ask server for its port eac…
…h time
c37bba4
Commits on Oct 08, 2013
@route route Don't forget to call quit on custom driver 0707343
Commits on Oct 09, 2013
@route route Fix #263 add `basic_authorize` method 8afd1cf
Commits on Oct 10, 2013
@route route Fix #263 HTTP Basic Authentication
* Use strip on Authorize header otherwise it forms bad request
* Combine both methods settings and manually set header
fc7cc73
@route route Add CHANGELOG entry 9b68f65
Commits on Oct 11, 2013
@route route Add missed change log and readme entries [ci skip] 24e154a
Commits on Oct 14, 2013
@exviva Cherry-pick mouse events at from michiels 8cf26f3
View
7 CHANGELOG.md
@@ -2,9 +2,14 @@
#### Features ####
* Added ability to set paper_size via a driver setter (Philippe Lehoux)
+* Can support Basic HTTP authentication
#### Bug fixes ####
-* Use `Capybara::Helpers.normalize_whitespace` in filter_text to strip unicode whitespace (Wataru Miyaguni)
+* Use `Capybara::Helpers.normalize_whitespace` in filter_text to strip unicode
+ whitespace (Wataru Miyaguni)
+* Fix missed interpolation on deprecated error classes
+* Raise InvalidSelector when wrong xpath selector was used [Issue #395]
+* Fix `driver.quit` before visiting any url with `IOError` error [Issue #398]
### 1.4.1 ###
View
25 README.md
@@ -43,26 +43,25 @@ dependencies* (you don't need Qt, or a running X server, etc.)
* *Homebrew*: `brew install phantomjs`
* *MacPorts*: `sudo port install phantomjs`
-* *Manual install*: [Download this](http://code.google.com/p/phantomjs/downloads/detail?name=phantomjs-1.8.1-macosx.zip&can=2&q=)
+* *Manual install*: [Download this](http://phantomjs.googlecode.com/files/phantomjs-1.9.2-macosx.zip)
### Linux ###
-* Download the [32
-bit](https://phantomjs.googlecode.com/files/phantomjs-1.9.2-linux-i686.tar.bz2)
-or [64
-bit](https://phantomjs.googlecode.com/files/phantomjs-1.9.2-linux-x86_64.tar.bz2)
+* Download the [32 bit](https://phantomjs.googlecode.com/files/phantomjs-1.9.2-linux-i686.tar.bz2)
+or [64 bit](https://phantomjs.googlecode.com/files/phantomjs-1.9.2-linux-x86_64.tar.bz2)
binary.
* Extract the tarball and copy `bin/phantomjs` into your `PATH`
### Windows ###
-* Download the [precompiled binary](http://phantomjs.org/download.html) for Windows
+* Download the [precompiled binary](http://phantomjs.googlecode.com/files/phantomjs-1.9.2-windows.zip)
+for Windows
### Manual compilation ###
Do this as a last resort if the binaries don't work for you. It will
take quite a long time as it has to build WebKit.
-* Download [the source tarball](http://code.google.com/p/phantomjs/downloads/detail?name=phantomjs-1.8.1-source.zip&can=2&q=)
+* Download [the source tarball](http://phantomjs.googlecode.com/files/phantomjs-1.9.2-source.zip)
* Extract and cd in
* `./build.sh`
@@ -102,8 +101,9 @@ and the following optional features:
* `page.status_code`
* `page.response_headers`
* `page.save_screenshot`
-* `page.render_base64`
-* `page.scroll_to`
+* `page.driver.render_base64(format, options)`
+* `page.driver.scroll_to(left, top)`
+* `page.driver.basic_authorize(user, password)`
* cookie handling
* drag-and-drop
@@ -114,9 +114,12 @@ There are some additional features:
You can grab screenshots of the page at any point by calling
`save_screenshot('/path/to/file.png')` (this works the same way as the PhantomJS
render feature, so you can specify other extensions like `.pdf`, `.gif`, etc.)
+Just in case you render pdf it's might be worth to set `driver.paper_size=` with
+settings provided by PhantomJS in [here](https://github.com/ariya/phantomjs/wiki/API-Reference-WebPage#wiki-webpage-paperSize)
-By default, only the viewport will be rendered (the part of the page that is in view). To render
-the entire page, use `save_screenshot('/path/to/file.png', :full => true)`.
+By default, only the viewport will be rendered (the part of the page that is in
+view). To render the entire page, use `save_screenshot('/path/to/file.png',
+:full => true)`.
You also have an ability to render selected element. Pass option `selector` with
any valid element selector to make a screenshot bounded by that element
View
16 lib/capybara/poltergeist/browser.rb
@@ -145,6 +145,18 @@ def drag(page_id, id, other_id)
command 'drag', page_id, id, other_id
end
+ def mouse_down_at(x, y)
+ command 'mouse_down_at', x, y
+ end
+
+ def mouse_move_at(x, y)
+ command 'mouse_move_at', x, y
+ end
+
+ def mouse_up_at(x, y)
+ command 'mouse_up_at', x, y
+ end
+
def select(page_id, id, value)
command 'select', page_id, id, value
end
@@ -232,6 +244,10 @@ def cookies_enabled=(flag)
command 'cookies_enabled', !!flag
end
+ def set_http_auth(user, password)
+ command 'set_http_auth', user, password
+ end
+
def js_errors=(val)
command 'set_js_errors', !!val
end
View
3  lib/capybara/poltergeist/client/agent.coffee
@@ -37,7 +37,8 @@ class PoltergeistAgent
this.register(el) for el in results
catch error
- if error.code == DOMException.SYNTAX_ERR
+ # DOMException.INVALID_EXPRESSION_ERR is undefined, using pure code
+ if error.code == DOMException.SYNTAX_ERR || error.code == 51
throw new PoltergeistAgent.InvalidSelector
else
throw error
View
17 lib/capybara/poltergeist/client/browser.coffee
@@ -69,6 +69,10 @@ class Poltergeist.Browser
else
@owner.sendResponse(response)
+ sendEvent: (eventType, args...) ->
+ @page.sendEvent(eventType, args...)
+ this.sendResponse(true)
+
add_extension: (extension) ->
@page.injectExtension extension
this.sendResponse 'success'
@@ -202,6 +206,15 @@ class Poltergeist.Browser
@page = prev_page if prev_page
this.sendResponse(true)
+ mouse_down_at: (x, y) ->
+ this.sendEvent("mousedown", x, y)
+
+ mouse_move_at: (x, y) ->
+ this.sendEvent("mousemove", x, y)
+
+ mouse_up_at: (x, y) ->
+ this.sendEvent("mouseup", x, y)
+
mouse_event: (page_id, id, name) ->
# Get the node before changing state, in case there is an exception
node = this.node(page_id, id)
@@ -327,6 +340,10 @@ class Poltergeist.Browser
phantom.cookiesEnabled = flag
this.sendResponse(true)
+ set_http_auth: (user, password) ->
+ @page.setHttpAuth(user, password)
+ this.sendResponse(true)
+
set_js_errors: (value) ->
@js_errors = value
this.sendResponse(true)
View
2  lib/capybara/poltergeist/client/compiled/agent.js
@@ -74,7 +74,7 @@ PoltergeistAgent = (function() {
return _results;
} catch (_error) {
error = _error;
- if (error.code === DOMException.SYNTAX_ERR) {
+ if (error.code === DOMException.SYNTAX_ERR || error.code === 51) {
throw new PoltergeistAgent.InvalidSelector;
} else {
throw error;
View
27 lib/capybara/poltergeist/client/compiled/browser.js
@@ -1,3 +1,5 @@
+var __slice = [].slice;
+
Poltergeist.Browser = (function() {
function Browser(owner, width, height) {
this.owner = owner;
@@ -90,6 +92,14 @@ Poltergeist.Browser = (function() {
}
};
+ Browser.prototype.sendEvent = function() {
+ var args, eventType, _ref;
+
+ eventType = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+ (_ref = this.page).sendEvent.apply(_ref, [eventType].concat(__slice.call(args)));
+ return this.sendResponse(true);
+ };
+
Browser.prototype.add_extension = function(extension) {
this.page.injectExtension(extension);
return this.sendResponse('success');
@@ -261,6 +271,18 @@ Poltergeist.Browser = (function() {
return this.sendResponse(true);
};
+ Browser.prototype.mouse_down_at = function(x, y) {
+ return this.sendEvent("mousedown", x, y);
+ };
+
+ Browser.prototype.mouse_move_at = function(x, y) {
+ return this.sendEvent("mousemove", x, y);
+ };
+
+ Browser.prototype.mouse_up_at = function(x, y) {
+ return this.sendEvent("mouseup", x, y);
+ };
+
Browser.prototype.mouse_event = function(page_id, id, name) {
var node,
_this = this;
@@ -440,6 +462,11 @@ Poltergeist.Browser = (function() {
return this.sendResponse(true);
};
+ Browser.prototype.set_http_auth = function(user, password) {
+ this.page.setHttpAuth(user, password);
+ return this.sendResponse(true);
+ };
+
Browser.prototype.set_js_errors = function(value) {
this.js_errors = value;
return this.sendResponse(true);
View
5 lib/capybara/poltergeist/client/compiled/main.js
@@ -89,14 +89,15 @@ Poltergeist.ObsoleteNode = (function(_super) {
Poltergeist.InvalidSelector = (function(_super) {
__extends(InvalidSelector, _super);
- function InvalidSelector(selector) {
+ function InvalidSelector(method, selector) {
+ this.method = method;
this.selector = selector;
}
InvalidSelector.prototype.name = "Poltergeist.InvalidSelector";
InvalidSelector.prototype.args = function() {
- return [this.selector];
+ return [this.method, this.selector];
};
return InvalidSelector;
View
10 lib/capybara/poltergeist/client/compiled/web_page.js
@@ -145,6 +145,11 @@ Poltergeist.WebPage = (function() {
}
};
+ WebPage.prototype.setHttpAuth = function(user, password) {
+ this["native"].settings.userName = user;
+ return this["native"].settings.password = password;
+ };
+
WebPage.prototype.networkTraffic = function() {
return this._networkTraffic;
};
@@ -361,7 +366,7 @@ Poltergeist.WebPage = (function() {
};
WebPage.prototype.runCommand = function(name, args) {
- var result;
+ var method, result, selector;
result = this.evaluate(function(name, args) {
return __poltergeist.externalCall(name, args);
}, name, args);
@@ -371,7 +376,8 @@ Poltergeist.WebPage = (function() {
throw new Poltergeist.ObsoleteNode;
break;
case 'PoltergeistAgent.InvalidSelector':
- throw new Poltergeist.InvalidSelector(args[1]);
+ method = args[0], selector = args[1];
+ throw new Poltergeist.InvalidSelector(method, selector);
break;
default:
throw new Poltergeist.BrowserError(result.error.message, result.error.stack);
View
4 lib/capybara/poltergeist/client/main.coffee
@@ -51,9 +51,9 @@ class Poltergeist.ObsoleteNode extends Poltergeist.Error
toString: -> this.name
class Poltergeist.InvalidSelector extends Poltergeist.Error
- constructor: (@selector) ->
+ constructor: (@method, @selector) ->
name: "Poltergeist.InvalidSelector"
- args: -> [@selector]
+ args: -> [@method, @selector]
class Poltergeist.FrameNotFound extends Poltergeist.Error
constructor: (@frameName) ->
View
7 lib/capybara/poltergeist/client/web_page.coffee
@@ -93,6 +93,10 @@ class Poltergeist.WebPage
@_statusCode = response.status
@_responseHeaders = response.headers
+ setHttpAuth: (user, password) ->
+ @native.settings.userName = user
+ @native.settings.password = password
+
networkTraffic: ->
@_networkTraffic
@@ -265,7 +269,8 @@ class Poltergeist.WebPage
when 'PoltergeistAgent.ObsoleteNode'
throw new Poltergeist.ObsoleteNode
when 'PoltergeistAgent.InvalidSelector'
- throw new Poltergeist.InvalidSelector(args[1])
+ [method, selector] = args
+ throw new Poltergeist.InvalidSelector(method, selector)
else
throw new Poltergeist.BrowserError(result.error.message, result.error.stack)
else
View
23 lib/capybara/poltergeist/driver.rb
@@ -147,6 +147,18 @@ def window_handles
browser.window_handles
end
+ def mouse_down_at(x, y)
+ browser.mouse_down_at(x, y)
+ end
+
+ def mouse_move_at(x, y)
+ browser.mouse_move_at(x, y)
+ end
+
+ def mouse_up_at(x, y)
+ browser.mouse_up_at(x, y)
+ end
+
def reset!
browser.reset
@started = false
@@ -225,6 +237,17 @@ def cookies_enabled=(flag)
browser.cookies_enabled = flag
end
+ # * PhantomJS with set settings doesn't send `Authorize` on POST request
+ # * With manually set header PhantomJS makes next request with
+ # `Authorization: Basic Og==` header when settings are empty and the
+ # response was `401 Unauthorized` (which means Base64.encode64(':')).
+ # Combining both methods to reach proper behavior.
+ def basic_authorize(user, password)
+ browser.set_http_auth(user, password)
+ credentials = ["#{user}:#{password}"].pack('m*').strip
+ add_header('Authorization', "Basic #{credentials}")
+ end
+
def debug
if @options[:inspector]
inspector.open
View
8 lib/capybara/poltergeist/errors.rb
@@ -65,13 +65,17 @@ def message
end
class InvalidSelector < ClientError
+ def method
+ response['args'][0]
+ end
+
def selector
- response['args'].first
+ response['args'][1]
end
def message
"The browser raised a syntax error while trying to evaluate " \
- "the selector #{selector.inspect}"
+ "#{method} selector #{selector.inspect}"
end
end
View
8 lib/capybara/poltergeist/web_socket_server.rb
@@ -26,15 +26,13 @@ def initialize(port = nil, timeout = nil)
@server = start_server(port)
end
- def port
- server.addr[1]
- end
-
def start_server(port)
time = Time.now
begin
- TCPServer.open(HOST, port || 0)
+ TCPServer.open(HOST, port || 0).tap do |server|
+ @port = server.addr[1]
+ end
rescue Errno::EADDRINUSE
if (Time.now - time) < BIND_TIMEOUT
sleep(0.01)
View
92 spec/integration/driver_spec.rb
@@ -44,14 +44,18 @@ def session_url(path)
end
it 'supports capturing console.log' do
- output = StringIO.new
- Capybara.register_driver :poltergeist_with_logger do |app|
- Capybara::Poltergeist::Driver.new(app, :phantomjs_logger => output)
- end
+ begin
+ output = StringIO.new
+ Capybara.register_driver :poltergeist_with_logger do |app|
+ Capybara::Poltergeist::Driver.new(app, :phantomjs_logger => output)
+ end
- session = Capybara::Session.new(:poltergeist_with_logger, TestApp)
- session.visit('/poltergeist/console_log')
- expect(output.string).to include('Hello world')
+ session = Capybara::Session.new(:poltergeist_with_logger, TestApp)
+ session.visit('/poltergeist/console_log')
+ expect(output.string).to include('Hello world')
+ ensure
+ session.driver.quit
+ end
end
# FIXME: It definitely must be fixed on jruby, because all futher tests fail
@@ -62,6 +66,11 @@ def session_url(path)
expect(@driver.html).to include('Hello world')
end
+ it 'quits silently before visit call' do
+ driver = Capybara::Poltergeist::Driver.new(nil)
+ expect { driver.quit }.not_to raise_error
+ end
+
it 'has a viewport size of 1024x768 by default' do
@session.visit('/')
expect(
@@ -352,15 +361,19 @@ def create_screenshot(file, *args)
end
it 'supports extending the phantomjs world' do
- @extended_driver.visit session_url("/poltergeist/requiring_custom_extension")
- expect(@extended_driver.body).
- to include(%Q%Location: <span id="location">1,-1</span>%)
- expect(
- @extended_driver.evaluate_script("document.getElementById('location').innerHTML")
- ).to eq('1,-1')
- expect(
- @extended_driver.evaluate_script('navigator.geolocation')
- ).to_not eq(nil)
+ begin
+ @extended_driver.visit session_url("/poltergeist/requiring_custom_extension")
+ expect(@extended_driver.body).
+ to include(%Q%Location: <span id="location">1,-1</span>%)
+ expect(
+ @extended_driver.evaluate_script("document.getElementById('location').innerHTML")
+ ).to eq('1,-1')
+ expect(
+ @extended_driver.evaluate_script('navigator.geolocation')
+ ).to_not eq(nil)
+ ensure
+ @extended_driver.quit
+ end
end
end
@@ -607,5 +620,52 @@ def create_screenshot(file, *args)
expect(@driver.browser.window_handles).to eq(["popup"])
expect(@driver.window_handles).to eq(["popup"])
end
+
+ context 'basic http authentication' do
+ it 'denies without credentials' do
+ @session.visit '/poltergeist/basic_auth'
+
+ expect(@session.status_code).to eq(401)
+ expect(@session).not_to have_content('Welcome, authenticated client')
+ end
+
+ it 'allows with given credentials' do
+ @driver.basic_authorize('login', 'pass')
+
+ @session.visit '/poltergeist/basic_auth'
+
+ expect(@session.status_code).to eq(200)
+ expect(@session).to have_content('Welcome, authenticated client')
+ end
+
+ it 'allows even overwriting headers' do
+ @driver.basic_authorize('login', 'pass')
+ @driver.headers = [{ 'Poltergeist' => 'true' }]
+
+ @session.visit '/poltergeist/basic_auth'
+
+ expect(@session.status_code).to eq(200)
+ expect(@session).to have_content('Welcome, authenticated client')
+ end
+
+ it 'denies with wrong credentials' do
+ @driver.basic_authorize('user', 'pass!')
+
+ @session.visit '/poltergeist/basic_auth'
+
+ expect(@session.status_code).to eq(401)
+ expect(@session).not_to have_content('Welcome, authenticated client')
+ end
+
+ it 'allows on POST request' do
+ @driver.basic_authorize('login', 'pass')
+
+ @session.visit '/poltergeist/basic_auth'
+ @session.click_button('Submit')
+
+ expect(@session.status_code).to eq(200)
+ expect(@session).to have_content('Authorized POST request')
+ end
+ end
end
end
View
5 spec/integration/session_spec.rb
@@ -527,6 +527,11 @@
expect { @session.find(:css, "table tr:last") }.to raise_error(Capybara::Poltergeist::InvalidSelector)
end
+ it 'throws an error on wrong xpath' do
+ @session.visit('/poltergeist/with_js')
+ expect { @session.find(:xpath, '#remove_me') }.to raise_error(Capybara::Poltergeist::InvalidSelector)
+ end
+
context 'whitespace stripping tests' do
before do
@session.visit '/poltergeist/filter_text_test'
View
23 spec/support/test_app.rb
@@ -7,6 +7,19 @@ class TestApp
POLTERGEIST_VIEWS = File.dirname(__FILE__) + "/views"
POLTERGEIST_PUBLIC = File.dirname(__FILE__) + "/public"
+ helpers do
+ def requires_credentials(login, password)
+ return if authorized?(login, password)
+ headers['WWW-Authenticate'] = 'Basic realm="Restricted Area"'
+ halt 401, "Not authorized\n"
+ end
+
+ def authorized?(login, password)
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
+ @auth.provided? and @auth.basic? and @auth.credentials and @auth.credentials == [login, password]
+ end
+ end
+
get '/poltergeist/test.js' do
File.read("#{POLTERGEIST_PUBLIC}/test.js")
end
@@ -41,6 +54,16 @@ class TestApp
"slow page"
end
+ get '/poltergeist/basic_auth' do
+ requires_credentials('login', 'pass')
+ render_view :basic_auth
+ end
+
+ post '/poltergeist/post_basic_auth' do
+ requires_credentials('login', 'pass')
+ 'Authorized POST request'
+ end
+
get '/poltergeist/:view' do |view|
render_view view
end
View
5 spec/support/views/basic_auth.erb
@@ -0,0 +1,5 @@
+Welcome, authenticated client
+
+<form name="input" action="/poltergeist/post_basic_auth" method="post">
+ <input type="submit" value="Submit">
+</form>

No commit comments for this range

Something went wrong with that request. Please try again.