ghostjs
Version:
Modern web integration test runner
231 lines (213 loc) • 6.31 kB
JavaScript
export default class Element {
constructor (page, selector) {
this.page = page
this.selector = selector
}
async click (x, y) {
return this.mouse('click', x, y)
}
async mouse (method, xPos, yPos) {
return new Promise(resolve => {
this.page.evaluate((selector, mouseType, xPos, yPos) => {
try {
var el = document.querySelector(selector)
var evt = document.createEvent('MouseEvents')
var calculatedX = xPos || 1
var calculatedY = yPos || 1
try {
var pos = el.getBoundingClientRect()
if (!xPos) {
calculatedX = Math.floor((pos.left + pos.right) / 2)
}
if (!yPos) {
calculatedY = Math.floor((pos.top + pos.bottom) / 2)
}
} catch(e) {}
evt.initMouseEvent(mouseType, true, true, window, 1, 1, 1, calculatedX, calculatedY, false, false, false, false, 0, el)
el.dispatchEvent(evt)
return true
} catch (e) {
console.log('Failed dispatching ' + mouseType + 'mouse event on ' + selector + ': ' + e)
return false
}
},
this.selector, method, xPos, yPos,
(err, result) => {
resolve(result)
})
})
}
/**
* Sets a form field to the provided value.
* For non-text inputs like selects and radio options, we try to check the right value based on option name.
*/
async fill (setFill) {
return new Promise(resolve => {
this.page.evaluate((selector, value) => {
var el = document.querySelector(selector)
if (!el) {
return null
}
// Focus on the element
try {
el.focus()
} catch (e) {
console.log('Unable to focus on element ' + el.outerHTML + ': ' + e)
}
var nodeName = el.nodeName.toLowerCase()
switch (nodeName) {
case 'input':
var type = el.getAttribute('type') || 'text'
switch (type.toLowerCase()) {
case 'checkbox':
el.checked = !!value;
break;
case 'file':
throw {
name: 'FileUploadError',
message:'File support coming soon.',
path: value
}
case 'radio':
el.checked = !!value
break;
default:
el.value = value;
break
}
break;
case 'select':
if (el.multiple) {
[].forEach.call(el.options, function(option) {
option.selected = value.indexOf(option.value) !== -1
})
// Search options if we can't find the value.
if (el.value === '') {
[].forEach.call(el.options, function(option) {
option.selected = value.indexOf(option.text) !== -1;
})
}
} else {
el.value = value
// Search options if we can't find the value.
if (el.value !== value) {
[].some.call(el.options, function(option) {
option.selected = value === option.text
return value === option.text
})
}
}
break;
case 'textarea':
el.value = value
break;
default:
console.log('unsupported type', nodeName)
}
// Emulate the change and input events
['change', 'input'].forEach(function(name) {
var event = document.createEvent('HTMLEvents')
event.initEvent(name, true, true)
el.dispatchEvent(event)
})
// Blur the element
try {
el.blur()
} catch (e) {
console.log('Unable to blur element ' + el.outerHTML + ': ' + e)
}
},
this.selector, setFill,
(err, result) => {
resolve(result)
})
})
}
async getAttribute (attribute) {
return new Promise(resolve => {
this.page.evaluate((selector, attribute) => {
return document.querySelector(selector)[attribute]
},
this.selector, attribute,
(err, result) => {
resolve(result)
})
})
}
async html () {
return await this.getAttribute('innerHTML')
}
async text () {
return await this.getAttribute('textContent')
}
async isVisible () {
return new Promise(resolve => {
this.page.evaluate((selector) => {
var el = document.querySelector(selector)
var style
try {
style = window.getComputedStyle(el, null)
} catch (e) {
return false
}
if (!style) {
return false
}
var hidden = style.visibility === 'hidden' || style.display === 'none';
if (hidden) {
return false
}
if (style.display === 'inline' || style.display === 'inline-block') {
return true
}
return el.clientHeight > 0 && el.clientWidth > 0
},
this.selector,
(err, result) => {
resolve(result)
})
})
}
async rect (func) {
return new Promise(resolve => {
this.page.evaluate((selector) => {
var el = document.querySelector(selector)
if (!el) {
return null
}
var rect = el.getBoundingClientRect()
return {
top: rect.top,
right: rect.right,
bottom: rect.bottom,
left: rect.left,
height: rect.height,
width: rect.width
}
},
this.selector,
(err, result) => {
resolve(result)
})
})
}
async script (func, args) {
if (!Array.isArray(args)) {
args = [args]
}
return new Promise(resolve => {
this.page.evaluate((func, selector, args) => {
var el = document.querySelector(selector)
args.unshift(el)
var invoke = new Function(
'return ' + func
)();
return invoke.apply(null, args)
},
func.toString(), this.selector, args,
(err, result) => {
resolve(result)
})
})
}
}