lib/capybara/poltergeist/client/browser.coffee in poltergeist-0.4.0 vs lib/capybara/poltergeist/client/browser.coffee in poltergeist-0.5.0

- old
+ new

@@ -1,61 +1,83 @@ class Poltergeist.Browser constructor: (@owner) -> - @state = 'default' + @state = 'default' + @page_id = 0 + this.resetPage() resetPage: -> @page.release() if @page? - @page = new Poltergeist.WebPage @page.onLoadStarted = => @state = 'loading' if @state == 'clicked' @page.onLoadFinished = (status) => if @state == 'loading' - @owner.sendResponse(status) + this.sendResponse(status) @state = 'default' + @page.onInitialized = => + @page_id += 1 + + sendResponse: (response) -> + errors = @page.errors() + + if errors.length > 0 + @page.clearErrors() + @owner.sendError(new Poltergeist.JavascriptError(errors)) + else + @owner.sendResponse(response) + + node: (page_id, id) -> + if page_id == @page_id + @page.get(id) + else + throw new Poltergeist.ObsoleteNode + visit: (url) -> @state = 'loading' @page.open(url) current_url: -> - @owner.sendResponse @page.currentUrl() + this.sendResponse @page.currentUrl() body: -> - @owner.sendResponse @page.content() + this.sendResponse @page.content() source: -> - @owner.sendResponse @page.source() + this.sendResponse @page.source() - find: (selector, id) -> - @owner.sendResponse @page.find(selector, id) + find: (selector) -> + this.sendResponse(page_id: @page_id, ids: @page.find(selector)) - text: (id) -> - @owner.sendResponse @page.get(id).text() + find_within: (page_id, id, selector) -> + this.sendResponse this.node(page_id, id).find(selector) - attribute: (id, name) -> - @owner.sendResponse @page.get(id).getAttribute(name) + text: (page_id, id) -> + this.sendResponse this.node(page_id, id).text() - value: (id) -> - @owner.sendResponse @page.get(id).value() + attribute: (page_id, id, name) -> + this.sendResponse this.node(page_id, id).getAttribute(name) - set: (id, value) -> - @page.get(id).set(value) - @owner.sendResponse(true) + value: (page_id, id) -> + this.sendResponse this.node(page_id, id).value() + set: (page_id, id, value) -> + this.node(page_id, id).set(value) + this.sendResponse(true) + # PhantomJS only allows us to reference the element by CSS selector, not XPath, # so we have to add an attribute to the element to identify it, then remove it # afterwards. # # PhantomJS does not support multiple-file inputs, so we have to blatently cheat # by temporarily changing it to a single-file input. This obviously could break # things in various ways, which is not ideal, but it works in the simplest case. - select_file: (id, value) -> - element = @page.get(id) + select_file: (page_id, id, value) -> + element = this.node(page_id, id) multiple = element.isMultiple() element.removeAttribute('multiple') if multiple element.setAttribute('_poltergeist_selected', '') @@ -63,65 +85,65 @@ @page.uploadFile('[_poltergeist_selected]', value) element.removeAttribute('_poltergeist_selected') element.setAttribute('multiple', 'multiple') if multiple - @owner.sendResponse(true) + this.sendResponse(true) - select: (id, value) -> - @owner.sendResponse @page.get(id).select(value) + select: (page_id, id, value) -> + this.sendResponse this.node(page_id, id).select(value) - tag_name: (id) -> - @owner.sendResponse @page.get(id).tagName() + tag_name: (page_id, id) -> + this.sendResponse this.node(page_id, id).tagName() - visible: (id) -> - @owner.sendResponse @page.get(id).isVisible() + visible: (page_id, id) -> + this.sendResponse this.node(page_id, id).isVisible() evaluate: (script) -> - @owner.sendResponse JSON.parse(@page.evaluate("function() { return JSON.stringify(#{script}) }")) + this.sendResponse JSON.parse(@page.evaluate("function() { return JSON.stringify(#{script}) }")) execute: (script) -> @page.execute("function() { #{script} }") - @owner.sendResponse(true) + this.sendResponse(true) push_frame: (id) -> @page.pushFrame(id) - @owner.sendResponse(true) + this.sendResponse(true) pop_frame: -> @page.popFrame() - @owner.sendResponse(true) + this.sendResponse(true) - click: (id) -> + click: (page_id, id) -> # If the click event triggers onLoadStarted, we will transition to the 'loading' # state and wait for onLoadFinished before sending a response. @state = 'clicked' - @page.get(id).click() + this.node(page_id, id).click() # Use a timeout in order to let the stack clear, so that the @page.onLoadStarted # callback can (possibly) fire, before we decide whether to send a response. setTimeout( => if @state == 'clicked' @state = 'default' - @owner.sendResponse(true) + this.sendResponse(true) , 10 ) - drag: (id, other_id) -> - @page.get(id).dragTo(@page.get(other_id)) - @owner.sendResponse(true) + drag: (page_id, id, other_id) -> + this.node(page_id, id).dragTo(@page.get(other_id)) + this.sendResponse(true) - trigger: (id, event) -> - @page.get(id).trigger(event) - @owner.sendResponse(event) + trigger: (page_id, id, event) -> + this.node(page_id, id).trigger(event) + this.sendResponse(event) reset: -> this.resetPage() - @owner.sendResponse(true) + this.sendResponse(true) render: (path, full) -> dimensions = @page.validatedDimensions() document = dimensions.document viewport = dimensions.viewport @@ -133,14 +155,14 @@ @page.setScrollPosition(left: dimensions.left, top: dimensions.top) else @page.setClipRect(left: 0, top: 0, width: viewport.width, height: viewport.height) @page.render(path) - @owner.sendResponse(true) + this.sendResponse(true) resize: (width, height) -> @page.setViewportSize(width: width, height: height) - @owner.sendResponse(true) + this.sendResponse(true) exit: -> phantom.exit() noop: ->