lib/capybara/poltergeist/client/agent.coffee in poltergeist-1.7.0 vs lib/capybara/poltergeist/client/agent.coffee in poltergeist-1.8.0
- old
+ new
@@ -1,8 +1,12 @@
# This is injected into each page that is loaded
class PoltergeistAgent
+ # Since this code executes in the sites browser space - copy needed JSON functions
+ # in case user code messes with JSON (early mootools for instance)
+ @.JSON ||= { parse: JSON.parse, stringify: JSON.stringify }
+
constructor: ->
@elements = []
@nodes = {}
externalCall: (name, args) ->
@@ -11,11 +15,11 @@
catch error
{ error: { message: error.toString(), stack: error.stack } }
@stringify: (object) ->
try
- JSON.stringify object, (key, value) ->
+ PoltergeistAgent.JSON.stringify object, (key, value) ->
if Array.isArray(this[key])
return this[key]
else
return value
catch error
@@ -118,10 +122,12 @@
# In the case of an OPTION tag, the change event should come
# from the parent SELECT
if @element.nodeName == 'OPTION'
element = @element.parentNode
+ element = element.parentNode if element.nodeName == 'OPTGROUP'
+ element
else
element = @element
element.dispatchEvent(event)
@@ -170,13 +176,16 @@
range.selectNodeContents(@element)
window.getSelection().removeAllRanges()
window.getSelection().addRange(range)
window.getSelection().deleteFromDocument()
+ getProperty: (name) ->
+ @element[name]
+
getAttributes: ->
attrs = {}
- for attr, i in @element.attributes
+ for attr in @element.attributes
attrs[attr.name] = attr.value.replace("\n","\\n");
attrs
getAttribute: (name) ->
if name == 'checked' || name == 'selected'
@@ -184,10 +193,14 @@
else
@element.getAttribute(name)
scrollIntoView: ->
@element.scrollIntoViewIfNeeded()
+ #Sometimes scrollIntoViewIfNeeded doesn't seem to work, not really sure why.
+ #Just calling scrollIntoView doesnt work either, however calling scrollIntoView
+ #after scrollIntoViewIfNeeded when element is not in the viewport does appear to work
+ @element.scrollIntoView() unless this.isInViewport()
value: ->
if @element.tagName == 'SELECT' && @element.multiple
option.value for option in @element.children when option.selected
else
@@ -197,12 +210,12 @@
return if @element.readOnly
if (@element.maxLength >= 0)
value = value.substr(0, @element.maxLength)
- @element.value = ''
this.trigger('focus')
+ @element.value = ''
if @element.type == 'number'
@element.value = value
else
for char in value
@@ -242,19 +255,29 @@
tagName: ->
@element.tagName
isVisible: (element) ->
- element = @element unless element
+ element ||= @element
- if window.getComputedStyle(element).display == 'none'
- false
- else if element.parentElement
- this.isVisible element.parentElement
- else
- true
+ while (element)
+ style = window.getComputedStyle(element)
+ return false if style.display == 'none' or
+ style.visibility == 'hidden' or
+ parseFloat(style.opacity) == 0
+ element = element.parentElement
+ return true
+
+ isInViewport: ->
+ rect = @element.getBoundingClientRect();
+
+ rect.top >= 0 &&
+ rect.left >= 0 &&
+ rect.bottom <= window.innerHeight &&
+ rect.right <= window.innerWidth
+
isDisabled: ->
@element.disabled || @element.tagName == 'OPTION' && @element.parentNode.disabled
path: ->
elements = @parentIds().reverse().map((id) => @agent.get(id))
@@ -338,15 +361,17 @@
return { status: 'success' }
else
el = el.parentNode
{ status: 'failure', selector: origEl && this.getSelector(origEl) }
-
getSelector: (el) ->
selector = if el.tagName != 'HTML' then this.getSelector(el.parentNode) + ' ' else ''
selector += el.tagName.toLowerCase()
selector += "##{el.id}" if el.id
- for className in el.classList
+
+ #PhantomJS < 2.0 doesn't support classList for SVG elements - so get classes manually
+ classes = el.classList || (el.getAttribute('class')?.trim()?.split(/\s+/)) || []
+ for className in classes when className != ''
selector += ".#{className}"
selector
characterToKeyCode: (character) ->
code = character.toUpperCase().charCodeAt(0)