poltergeist 0.4.0 → 0.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.
- data/README.md +48 -2
- data/lib/capybara/poltergeist.rb +3 -0
- data/lib/capybara/poltergeist/browser.rb +48 -55
- data/lib/capybara/poltergeist/client.rb +42 -12
- data/lib/capybara/poltergeist/client/agent.coffee +48 -5
- data/lib/capybara/poltergeist/client/browser.coffee +64 -42
- data/lib/capybara/poltergeist/client/compiled/agent.js +71 -8
- data/lib/capybara/poltergeist/client/compiled/browser.js +68 -41
- data/lib/capybara/poltergeist/client/compiled/main.js +34 -5
- data/lib/capybara/poltergeist/client/compiled/node.js +34 -25
- data/lib/capybara/poltergeist/client/compiled/web_page.js +30 -7
- data/lib/capybara/poltergeist/client/main.coffee +26 -3
- data/lib/capybara/poltergeist/client/node.coffee +30 -23
- data/lib/capybara/poltergeist/client/web_page.coffee +31 -7
- data/lib/capybara/poltergeist/driver.rb +54 -19
- data/lib/capybara/poltergeist/errors.rb +63 -6
- data/lib/capybara/poltergeist/inspector.rb +35 -0
- data/lib/capybara/poltergeist/node.rb +15 -5
- data/lib/capybara/poltergeist/server.rb +7 -12
- data/lib/capybara/poltergeist/spawn.rb +17 -0
- data/lib/capybara/poltergeist/util.rb +12 -0
- data/lib/capybara/poltergeist/version.rb +1 -1
- data/lib/capybara/poltergeist/web_socket_server.rb +25 -6
- metadata +31 -25
| @@ -7,8 +7,8 @@ Poltergeist.WebPage = (function() { | |
| 7 7 | 
             
              function WebPage() {
         | 
| 8 8 | 
             
                var callback, _i, _len, _ref;
         | 
| 9 9 | 
             
                this["native"] = require('webpage').create();
         | 
| 10 | 
            -
                this.nodes = {};
         | 
| 11 10 | 
             
                this._source = "";
         | 
| 11 | 
            +
                this._errors = [];
         | 
| 12 12 | 
             
                this.setViewportSize({
         | 
| 13 13 | 
             
                  width: 1024,
         | 
| 14 14 | 
             
                  height: 768
         | 
| @@ -54,7 +54,8 @@ Poltergeist.WebPage = (function() { | |
| 54 54 | 
             
                if (this.evaluate(function() {
         | 
| 55 55 | 
             
                  return typeof __poltergeist;
         | 
| 56 56 | 
             
                }) === "undefined") {
         | 
| 57 | 
            -
                   | 
| 57 | 
            +
                  this["native"].injectJs('agent.js');
         | 
| 58 | 
            +
                  return this.nodes = {};
         | 
| 58 59 | 
             
                }
         | 
| 59 60 | 
             
              };
         | 
| 60 61 | 
             
              WebPage.prototype.onConsoleMessageNative = function(message) {
         | 
| @@ -66,8 +67,12 @@ Poltergeist.WebPage = (function() { | |
| 66 67 | 
             
              WebPage.prototype.onLoadFinishedNative = function() {
         | 
| 67 68 | 
             
                return this._source || (this._source = this["native"].content);
         | 
| 68 69 | 
             
              };
         | 
| 69 | 
            -
              WebPage.prototype.onConsoleMessage = function(message) {
         | 
| 70 | 
            -
                 | 
| 70 | 
            +
              WebPage.prototype.onConsoleMessage = function(message, line, file) {
         | 
| 71 | 
            +
                if (line === 0 && file === "undefined") {
         | 
| 72 | 
            +
                  return this._errors.push(message);
         | 
| 73 | 
            +
                } else {
         | 
| 74 | 
            +
                  return console.log(message);
         | 
| 75 | 
            +
                }
         | 
| 71 76 | 
             
              };
         | 
| 72 77 | 
             
              WebPage.prototype.content = function() {
         | 
| 73 78 | 
             
                return this["native"].content;
         | 
| @@ -75,6 +80,12 @@ Poltergeist.WebPage = (function() { | |
| 75 80 | 
             
              WebPage.prototype.source = function() {
         | 
| 76 81 | 
             
                return this._source;
         | 
| 77 82 | 
             
              };
         | 
| 83 | 
            +
              WebPage.prototype.errors = function() {
         | 
| 84 | 
            +
                return this._errors;
         | 
| 85 | 
            +
              };
         | 
| 86 | 
            +
              WebPage.prototype.clearErrors = function() {
         | 
| 87 | 
            +
                return this._errors = [];
         | 
| 88 | 
            +
              };
         | 
| 78 89 | 
             
              WebPage.prototype.viewportSize = function() {
         | 
| 79 90 | 
             
                return this["native"].viewportSize;
         | 
| 80 91 | 
             
              };
         | 
| @@ -135,7 +146,7 @@ Poltergeist.WebPage = (function() { | |
| 135 146 | 
             
              WebPage.prototype.evaluate = function() {
         | 
| 136 147 | 
             
                var args, fn;
         | 
| 137 148 | 
             
                fn = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
         | 
| 138 | 
            -
                return this["native"].evaluate("function() { return " + (this.stringifyCall(fn, args)) + " }");
         | 
| 149 | 
            +
                return JSON.parse(this["native"].evaluate("function() { return JSON.stringify(" + (this.stringifyCall(fn, args)) + ") }"));
         | 
| 139 150 | 
             
              };
         | 
| 140 151 | 
             
              WebPage.prototype.execute = function() {
         | 
| 141 152 | 
             
                var args, fn;
         | 
| @@ -163,9 +174,21 @@ Poltergeist.WebPage = (function() { | |
| 163 174 | 
             
                };
         | 
| 164 175 | 
             
              };
         | 
| 165 176 | 
             
              WebPage.prototype.runCommand = function(name, arguments) {
         | 
| 166 | 
            -
                 | 
| 167 | 
            -
             | 
| 177 | 
            +
                var result;
         | 
| 178 | 
            +
                result = this.evaluate(function(name, arguments) {
         | 
| 179 | 
            +
                  return __poltergeist.externalCall(name, arguments);
         | 
| 168 180 | 
             
                }, name, arguments);
         | 
| 181 | 
            +
                if (result.error) {
         | 
| 182 | 
            +
                  switch (result.error) {
         | 
| 183 | 
            +
                    case "PoltergeistAgent.ObsoleteNode":
         | 
| 184 | 
            +
                      throw new Poltergeist.ObsoleteNode;
         | 
| 185 | 
            +
                      break;
         | 
| 186 | 
            +
                    default:
         | 
| 187 | 
            +
                      throw result.error;
         | 
| 188 | 
            +
                  }
         | 
| 189 | 
            +
                } else {
         | 
| 190 | 
            +
                  return result.value;
         | 
| 191 | 
            +
                }
         | 
| 169 192 | 
             
              };
         | 
| 170 193 | 
             
              return WebPage;
         | 
| 171 194 | 
             
            }).call(this);
         | 
| @@ -7,13 +7,36 @@ class Poltergeist | |
| 7 7 | 
             
                try
         | 
| 8 8 | 
             
                  @browser[command.name].apply(@browser, command.args)
         | 
| 9 9 | 
             
                catch error
         | 
| 10 | 
            -
                   | 
| 10 | 
            +
                  this.sendError(error)
         | 
| 11 11 |  | 
| 12 12 | 
             
              sendResponse: (response) ->
         | 
| 13 | 
            -
                @connection.send( | 
| 13 | 
            +
                @connection.send(response: response)
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              sendError: (error) ->
         | 
| 16 | 
            +
                @connection.send(
         | 
| 17 | 
            +
                  error:
         | 
| 18 | 
            +
                    name: error.name || 'Generic',
         | 
| 19 | 
            +
                    args: error.args && error.args() || [error.toString()]
         | 
| 20 | 
            +
                )
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            # This is necessary because the remote debugger will wrap the
         | 
| 23 | 
            +
            # script in a function, causing the Poltergeist variable to
         | 
| 24 | 
            +
            # become local.
         | 
| 25 | 
            +
            window.Poltergeist = Poltergeist
         | 
| 14 26 |  | 
| 15 27 | 
             
            class Poltergeist.ObsoleteNode
         | 
| 16 | 
            -
               | 
| 28 | 
            +
              name: "Poltergeist.ObsoleteNode"
         | 
| 29 | 
            +
              args: -> []
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            class Poltergeist.ClickFailed
         | 
| 32 | 
            +
              constructor: (@selector, @position) ->
         | 
| 33 | 
            +
              name: "Poltergeist.ClickFailed"
         | 
| 34 | 
            +
              args: -> [@selector, @position]
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            class Poltergeist.JavascriptError
         | 
| 37 | 
            +
              constructor: (@errors) ->
         | 
| 38 | 
            +
              name: "Poltergeist.JavascriptError"
         | 
| 39 | 
            +
              args: -> [@errors]
         | 
| 17 40 |  | 
| 18 41 | 
             
            phantom.injectJs('web_page.js')
         | 
| 19 42 | 
             
            phantom.injectJs('node.js')
         | 
| @@ -1,26 +1,21 @@ | |
| 1 1 | 
             
            # Proxy object for forwarding method calls to the node object inside the page.
         | 
| 2 2 |  | 
| 3 3 | 
             
            class Poltergeist.Node
         | 
| 4 | 
            -
              @DELEGATES = ['text', 'getAttribute', 'value', 'set', 'setAttribute', | 
| 5 | 
            -
                            ' | 
| 4 | 
            +
              @DELEGATES = ['text', 'getAttribute', 'value', 'set', 'setAttribute',
         | 
| 5 | 
            +
                            'removeAttribute', 'isMultiple', 'select', 'tagName', 'find',
         | 
| 6 | 
            +
                            'isVisible', 'position', 'trigger', 'parentId', 'clickTest']
         | 
| 6 7 |  | 
| 7 8 | 
             
              constructor: (@page, @id) ->
         | 
| 8 9 |  | 
| 9 10 | 
             
              parent: ->
         | 
| 10 11 | 
             
                new Poltergeist.Node(@page, this.parentId())
         | 
| 11 12 |  | 
| 12 | 
            -
              isObsolete: ->
         | 
| 13 | 
            -
                @page.nodeCall(@id, 'isObsolete')
         | 
| 14 | 
            -
             | 
| 15 13 | 
             
              for name in @DELEGATES
         | 
| 16 14 | 
             
                do (name) =>
         | 
| 17 15 | 
             
                  this.prototype[name] = (arguments...) ->
         | 
| 18 | 
            -
                     | 
| 19 | 
            -
                      throw new Poltergeist.ObsoleteNode
         | 
| 20 | 
            -
                    else
         | 
| 21 | 
            -
                      @page.nodeCall(@id, name, arguments)
         | 
| 16 | 
            +
                    @page.nodeCall(@id, name, arguments)
         | 
| 22 17 |  | 
| 23 | 
            -
               | 
| 18 | 
            +
              clickPosition: (scrollIntoView = true) ->
         | 
| 24 19 | 
             
                dimensions = @page.validatedDimensions()
         | 
| 25 20 | 
             
                document   = dimensions.document
         | 
| 26 21 | 
             
                viewport   = dimensions.viewport
         | 
| @@ -41,23 +36,35 @@ class Poltergeist.Node | |
| 41 36 | 
             
                      scroll[coord] + pos[coord] - viewport[measurement] + (viewport[measurement] / 2)
         | 
| 42 37 | 
             
                    )
         | 
| 43 38 |  | 
| 44 | 
            -
                 | 
| 45 | 
            -
             | 
| 39 | 
            +
                if scrollIntoView
         | 
| 40 | 
            +
                  adjust('left', 'width')
         | 
| 41 | 
            +
                  adjust('top',  'height')
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  if scroll.left != dimensions.left || scroll.top != dimensions.top
         | 
| 44 | 
            +
                    @page.setScrollPosition(scroll)
         | 
| 45 | 
            +
                    pos = this.position()
         | 
| 46 46 |  | 
| 47 | 
            -
                 | 
| 48 | 
            -
                   | 
| 49 | 
            -
                  pos = this.position()
         | 
| 47 | 
            +
                middle = (start, end, size) ->
         | 
| 48 | 
            +
                  start + ((Math.min(end, size) - start) / 2)
         | 
| 50 49 |  | 
| 51 | 
            -
                 | 
| 50 | 
            +
                {
         | 
| 51 | 
            +
                  x: middle(pos.left, pos.right,  viewport.width),
         | 
| 52 | 
            +
                  y: middle(pos.top,  pos.bottom, viewport.height)
         | 
| 53 | 
            +
                }
         | 
| 52 54 |  | 
| 53 55 | 
             
              click: ->
         | 
| 54 | 
            -
                 | 
| 55 | 
            -
                 | 
| 56 | 
            +
                pos  = this.clickPosition()
         | 
| 57 | 
            +
                test = this.clickTest(pos.x, pos.y)
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                if test.status == 'success'
         | 
| 60 | 
            +
                  @page.sendEvent('click', pos.x, pos.y)
         | 
| 61 | 
            +
                else
         | 
| 62 | 
            +
                  throw new Poltergeist.ClickFailed(test.selector, pos)
         | 
| 56 63 |  | 
| 57 64 | 
             
              dragTo: (other) ->
         | 
| 58 | 
            -
                position      = this. | 
| 59 | 
            -
                otherPosition = other. | 
| 65 | 
            +
                position      = this.clickPosition()
         | 
| 66 | 
            +
                otherPosition = other.clickPosition(false)
         | 
| 60 67 |  | 
| 61 | 
            -
                @page.sendEvent('mousedown', position. | 
| 62 | 
            -
                @page.sendEvent('mousemove', otherPosition. | 
| 63 | 
            -
                @page.sendEvent('mouseup',   otherPosition. | 
| 68 | 
            +
                @page.sendEvent('mousedown', position.x,      position.y)
         | 
| 69 | 
            +
                @page.sendEvent('mousemove', otherPosition.x, otherPosition.y)
         | 
| 70 | 
            +
                @page.sendEvent('mouseup',   otherPosition.x, otherPosition.y)
         | 
| @@ -1,13 +1,15 @@ | |
| 1 1 | 
             
            class Poltergeist.WebPage
         | 
| 2 2 | 
             
              @CALLBACKS = ['onAlert', 'onConsoleMessage', 'onLoadFinished', 'onInitialized',
         | 
| 3 3 | 
             
                            'onLoadStarted', 'onResourceRequested', 'onResourceReceived']
         | 
| 4 | 
            +
             | 
| 4 5 | 
             
              @DELEGATES = ['open', 'sendEvent', 'uploadFile', 'release', 'render']
         | 
| 6 | 
            +
             | 
| 5 7 | 
             
              @COMMANDS  = ['currentUrl', 'find', 'nodeCall', 'pushFrame', 'popFrame', 'documentSize']
         | 
| 6 8 |  | 
| 7 9 | 
             
              constructor: ->
         | 
| 8 10 | 
             
                @native  = require('webpage').create()
         | 
| 9 | 
            -
                @nodes   = {}
         | 
| 10 11 | 
             
                @_source = ""
         | 
| 12 | 
            +
                @_errors = []
         | 
| 11 13 |  | 
| 12 14 | 
             
                this.setViewportSize(width: 1024, height: 768)
         | 
| 13 15 |  | 
| @@ -29,11 +31,12 @@ class Poltergeist.WebPage | |
| 29 31 | 
             
              onInitializedNative: ->
         | 
| 30 32 | 
             
                @_source = null
         | 
| 31 33 | 
             
                this.injectAgent()
         | 
| 32 | 
            -
                this.setScrollPosition( | 
| 34 | 
            +
                this.setScrollPosition(left: 0, top: 0)
         | 
| 33 35 |  | 
| 34 36 | 
             
              injectAgent: ->
         | 
| 35 37 | 
             
                if this.evaluate(-> typeof __poltergeist) == "undefined"
         | 
| 36 38 | 
             
                  @native.injectJs('agent.js')
         | 
| 39 | 
            +
                  @nodes = {}
         | 
| 37 40 |  | 
| 38 41 | 
             
              onConsoleMessageNative: (message) ->
         | 
| 39 42 | 
             
                if message == '__DOMContentLoaded'
         | 
| @@ -43,8 +46,14 @@ class Poltergeist.WebPage | |
| 43 46 | 
             
              onLoadFinishedNative: ->
         | 
| 44 47 | 
             
                @_source or= @native.content
         | 
| 45 48 |  | 
| 46 | 
            -
              onConsoleMessage: (message) ->
         | 
| 47 | 
            -
                 | 
| 49 | 
            +
              onConsoleMessage: (message, line, file) ->
         | 
| 50 | 
            +
                if line == 0 && file == "undefined"
         | 
| 51 | 
            +
                  # file:line will always be "undefined:0" in current release of
         | 
| 52 | 
            +
                  # PhantomJS ;(
         | 
| 53 | 
            +
                  @_errors.push(message)
         | 
| 54 | 
            +
                else
         | 
| 55 | 
            +
                  # here line == 1 && file == "". don't ask me why!
         | 
| 56 | 
            +
                  console.log(message)
         | 
| 48 57 |  | 
| 49 58 | 
             
              content: ->
         | 
| 50 59 | 
             
                @native.content
         | 
| @@ -52,6 +61,12 @@ class Poltergeist.WebPage | |
| 52 61 | 
             
              source: ->
         | 
| 53 62 | 
             
                @_source
         | 
| 54 63 |  | 
| 64 | 
            +
              errors: ->
         | 
| 65 | 
            +
                @_errors
         | 
| 66 | 
            +
             | 
| 67 | 
            +
              clearErrors: ->
         | 
| 68 | 
            +
                @_errors = []
         | 
| 69 | 
            +
             | 
| 55 70 | 
             
              viewportSize: ->
         | 
| 56 71 | 
             
                @native.viewportSize
         | 
| 57 72 |  | 
| @@ -104,7 +119,7 @@ class Poltergeist.WebPage | |
| 104 119 | 
             
                @nodes[id] or= new Poltergeist.Node(this, id)
         | 
| 105 120 |  | 
| 106 121 | 
             
              evaluate: (fn, args...) ->
         | 
| 107 | 
            -
                @native.evaluate("function() { return #{this.stringifyCall(fn, args)} }")
         | 
| 122 | 
            +
                JSON.parse @native.evaluate("function() { return JSON.stringify(#{this.stringifyCall(fn, args)}) }")
         | 
| 108 123 |  | 
| 109 124 | 
             
              execute: (fn, args...) ->
         | 
| 110 125 | 
             
                @native.evaluate("function() { #{this.stringifyCall(fn, args)} }")
         | 
| @@ -129,7 +144,16 @@ class Poltergeist.WebPage | |
| 129 144 | 
             
                    that[name].apply(that, arguments)
         | 
| 130 145 |  | 
| 131 146 | 
             
              runCommand: (name, arguments) ->
         | 
| 132 | 
            -
                this.evaluate(
         | 
| 133 | 
            -
                  (name, arguments) -> __poltergeist | 
| 147 | 
            +
                result = this.evaluate(
         | 
| 148 | 
            +
                  (name, arguments) -> __poltergeist.externalCall(name, arguments),
         | 
| 134 149 | 
             
                  name, arguments
         | 
| 135 150 | 
             
                )
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                if result.error
         | 
| 153 | 
            +
                  switch result.error
         | 
| 154 | 
            +
                    when "PoltergeistAgent.ObsoleteNode"
         | 
| 155 | 
            +
                      throw new Poltergeist.ObsoleteNode
         | 
| 156 | 
            +
                    else
         | 
| 157 | 
            +
                      throw result.error
         | 
| 158 | 
            +
                else
         | 
| 159 | 
            +
                  result.value
         | 
| @@ -1,34 +1,65 @@ | |
| 1 1 | 
             
            module Capybara::Poltergeist
         | 
| 2 2 | 
             
              class Driver < Capybara::Driver::Base
         | 
| 3 | 
            -
                 | 
| 3 | 
            +
                DEFAULT_TIMEOUT = 30
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                attr_reader :app, :app_server, :server, :client, :browser, :options
         | 
| 4 6 |  | 
| 5 7 | 
             
                def initialize(app, options = {})
         | 
| 6 | 
            -
                  @app | 
| 7 | 
            -
                  @options | 
| 8 | 
            -
                  @ | 
| 9 | 
            -
                  @ | 
| 8 | 
            +
                  @app       = app
         | 
| 9 | 
            +
                  @options   = options
         | 
| 10 | 
            +
                  @browser   = nil
         | 
| 11 | 
            +
                  @inspector = nil
         | 
| 12 | 
            +
                  @server    = nil
         | 
| 13 | 
            +
                  @client    = nil
         | 
| 10 14 |  | 
| 11 | 
            -
                  @ | 
| 15 | 
            +
                  @app_server = Capybara::Server.new(app)
         | 
| 16 | 
            +
                  @app_server.boot if Capybara.run_server
         | 
| 12 17 | 
             
                end
         | 
| 13 18 |  | 
| 14 19 | 
             
                def browser
         | 
| 15 | 
            -
                  @browser ||= Browser.new(
         | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 20 | 
            +
                  @browser ||= Browser.new(server, client, logger)
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def inspector
         | 
| 24 | 
            +
                  @inspector ||= options[:inspector] && Inspector.new(options[:inspector])
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                def server
         | 
| 28 | 
            +
                  @server ||= Server.new(options.fetch(:timeout, DEFAULT_TIMEOUT))
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                def client
         | 
| 32 | 
            +
                  @client ||= Client.start(server.port, inspector, options[:phantomjs])
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                def client_pid
         | 
| 36 | 
            +
                  client.pid
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                def timeout
         | 
| 40 | 
            +
                  server.timeout
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                def timeout=(sec)
         | 
| 44 | 
            +
                  server.timeout = sec
         | 
| 19 45 | 
             
                end
         | 
| 20 46 |  | 
| 21 47 | 
             
                def restart
         | 
| 22 48 | 
             
                  browser.restart
         | 
| 23 49 | 
             
                end
         | 
| 24 50 |  | 
| 51 | 
            +
                def quit
         | 
| 52 | 
            +
                  server.stop
         | 
| 53 | 
            +
                  client.stop
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 25 56 | 
             
                # logger should be an object that responds to puts, or nil
         | 
| 26 57 | 
             
                def logger
         | 
| 27 58 | 
             
                  options[:logger] || (options[:debug] && STDERR)
         | 
| 28 59 | 
             
                end
         | 
| 29 60 |  | 
| 30 | 
            -
                def visit(path | 
| 31 | 
            -
                  browser.visit | 
| 61 | 
            +
                def visit(path)
         | 
| 62 | 
            +
                  browser.visit app_server.url(path)
         | 
| 32 63 | 
             
                end
         | 
| 33 64 |  | 
| 34 65 | 
             
                def current_url
         | 
| @@ -44,7 +75,7 @@ module Capybara::Poltergeist | |
| 44 75 | 
             
                end
         | 
| 45 76 |  | 
| 46 77 | 
             
                def find(selector)
         | 
| 47 | 
            -
                  browser.find(selector).map { | | 
| 78 | 
            +
                  browser.find(selector).map { |page_id, id| Capybara::Poltergeist::Node.new(self, page_id, id) }
         | 
| 48 79 | 
             
                end
         | 
| 49 80 |  | 
| 50 81 | 
             
                def evaluate_script(script)
         | 
| @@ -72,6 +103,16 @@ module Capybara::Poltergeist | |
| 72 103 | 
             
                  browser.resize(width, height)
         | 
| 73 104 | 
             
                end
         | 
| 74 105 |  | 
| 106 | 
            +
                def debug
         | 
| 107 | 
            +
                  inspector.open
         | 
| 108 | 
            +
                  pause
         | 
| 109 | 
            +
                end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                def pause
         | 
| 112 | 
            +
                  STDERR.puts "Poltergeist execution paused. Press enter to continue."
         | 
| 113 | 
            +
                  STDIN.gets
         | 
| 114 | 
            +
                end
         | 
| 115 | 
            +
             | 
| 75 116 | 
             
                def wait?
         | 
| 76 117 | 
             
                  true
         | 
| 77 118 | 
             
                end
         | 
| @@ -79,11 +120,5 @@ module Capybara::Poltergeist | |
| 79 120 | 
             
                def invalid_element_errors
         | 
| 80 121 | 
             
                  [Capybara::Poltergeist::ObsoleteNode]
         | 
| 81 122 | 
             
                end
         | 
| 82 | 
            -
             | 
| 83 | 
            -
                private
         | 
| 84 | 
            -
             | 
| 85 | 
            -
                def url(path)
         | 
| 86 | 
            -
                  server.url(path)
         | 
| 87 | 
            -
                end
         | 
| 88 123 | 
             
              end
         | 
| 89 124 | 
             
            end
         | 
| @@ -3,11 +3,21 @@ module Capybara | |
| 3 3 | 
             
                class Error < StandardError
         | 
| 4 4 | 
             
                end
         | 
| 5 5 |  | 
| 6 | 
            -
                class  | 
| 7 | 
            -
                  attr_reader : | 
| 6 | 
            +
                class ClientError < Error
         | 
| 7 | 
            +
                  attr_reader :response
         | 
| 8 8 |  | 
| 9 | 
            -
                  def initialize( | 
| 10 | 
            -
                    @ | 
| 9 | 
            +
                  def initialize(response)
         | 
| 10 | 
            +
                    @response = response
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                class BrowserError < ClientError
         | 
| 15 | 
            +
                  def name
         | 
| 16 | 
            +
                    response['name']
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def text
         | 
| 20 | 
            +
                    response['args'].first
         | 
| 11 21 | 
             
                  end
         | 
| 12 22 |  | 
| 13 23 | 
             
                  def message
         | 
| @@ -15,11 +25,44 @@ module Capybara | |
| 15 25 | 
             
                  end
         | 
| 16 26 | 
             
                end
         | 
| 17 27 |  | 
| 18 | 
            -
                class  | 
| 28 | 
            +
                class JavascriptError < ClientError
         | 
| 29 | 
            +
                  def javascript_messages
         | 
| 30 | 
            +
                    response['args'].first
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  def message
         | 
| 34 | 
            +
                    "One or more errors were raised in the Javascript code on the page: #{javascript_messages.inspect} " \
         | 
| 35 | 
            +
                      "Unfortunately, it is not currently possible to provide a stack trace, or even the line/file where " \
         | 
| 36 | 
            +
                      "the error occurred. (This is due to lack of support within QtWebKit.) Fixing this is a high " \
         | 
| 37 | 
            +
                      "priority, but we're not there yet."
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                class NodeError < ClientError
         | 
| 19 42 | 
             
                  attr_reader :node
         | 
| 20 43 |  | 
| 21 | 
            -
                  def initialize(node)
         | 
| 44 | 
            +
                  def initialize(node, response)
         | 
| 22 45 | 
             
                    @node = node
         | 
| 46 | 
            +
                    super(response)
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                class ObsoleteNode < NodeError
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                class ClickFailed < NodeError
         | 
| 54 | 
            +
                  def selector
         | 
| 55 | 
            +
                    response['args'][0]
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  def position
         | 
| 59 | 
            +
                    [response['args'][1]['x'], response['args'][1]['y']]
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  def message
         | 
| 63 | 
            +
                    "Click at co-ordinates [#{position.join(', ')}] failed. Poltergeist detected " \
         | 
| 64 | 
            +
                      "another element with CSS selector '#{selector}' at this position. " \
         | 
| 65 | 
            +
                      "It may be overlapping the element you are trying to click."
         | 
| 23 66 | 
             
                  end
         | 
| 24 67 | 
             
                end
         | 
| 25 68 |  | 
| @@ -54,5 +97,19 @@ module Capybara | |
| 54 97 | 
             
                    "PhantomJS version #{version} is too old. You must use at least version #{Client::PHANTOMJS_VERSION}"
         | 
| 55 98 | 
             
                  end
         | 
| 56 99 | 
             
                end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                class PhantomJSFailed < Error
         | 
| 102 | 
            +
                  attr_reader :status
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                  def initialize(status)
         | 
| 105 | 
            +
                    @status = status
         | 
| 106 | 
            +
                  end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                  def message
         | 
| 109 | 
            +
                    "PhantomJS returned non-zero exit status #{status.exitstatus}. Ensure there is an X display available and " \
         | 
| 110 | 
            +
                      "that DISPLAY is set. (See the Poltergeist README for details.) Make sure 'phantomjs --version' " \
         | 
| 111 | 
            +
                      "runs successfully on your system."
         | 
| 112 | 
            +
                  end
         | 
| 113 | 
            +
                end
         | 
| 57 114 | 
             
              end
         | 
| 58 115 | 
             
            end
         |