siesta-lite
Version:
Stress-free JavaScript unit testing and functional testing tool, works in NodeJS and browsers
1,076 lines (890 loc) • 90.6 kB
HTML
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>The source code</title>
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
<style type="text/css">
.highlight { display: block; background-color: #ddd; }
</style>
<script type="text/javascript">
function highlight() {
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
}
</script>
</head>
<body onload="prettyPrint(); highlight();">
<pre class="prettyprint lang-js">/*
Siesta 5.6.1
Copyright(c) 2009-2022 Bryntum AB
https://bryntum.com/contact
https://bryntum.com/products/siesta/license
*/
<span id='Siesta-Test-Element'>/**
</span>@class Siesta.Test.Element
This is a mixin, with helper methods for testing functionality relating to DOM elements. This mixin is consumed by {@link Siesta.Test}
*/
Role('Siesta.Test.Element', {
does : [
Siesta.Util.Role.CanCalculatePageScroll,
Siesta.Util.Role.Dom
],
requires : [
'typeOf',
'chain',
'normalizeElement'
],
has : {
allowMonkeyToClickOnAnchors : false,
allowedCharacters : function () {
return {
// does not include TAB by purpose, because our "TAB" simulation is not perfect
// Also exclude BACKSPACE since it navigates the page
special : 'ENTER/ESCAPE/PAGE-UP/PAGE-DOWN/END/HOME/UP/RIGHT/DOWN/LEFT/INSERT/DELETE',
// does not inlcude * because Ext fails on typing it
punctuation : '.,/()[]{}\\"\'`~!?@#$%^&_=+-',
normal : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
}
}
},
methods : {
<span id='Siesta-Test-Element-method-findCenter'> /**
</span> * Utility method which returns the center of a passed element. The coordinates are by default relative to the
* containing document of the element (so for example if the element is inside of the nested iframe, coordinates
* will be "local" to that iframe element). To get coordinates relative to the test iframe ("global" coordinates),
* pass `local` as `false`.
*
* @param {Siesta.Test.ActionTarget} el The element to find the center of.
* @param {Boolean} [local] Pass `true` means coordinates are relative to the containing document. This is the default value.
* Pass `false` to make sure the coordinates are global to the test window.
*
* @return {Array} The array first element of which is the `x` coordinate and 2nd - `y`
*/
findCenter : function (target, local) {
return this.getTargetCoordinate(target, local);
},
normalizeOffset : function (offset, $el) {
var parts;
if (this.typeOf(offset) === 'Function') offset = offset.call(this)
offset = offset && offset.slice() || [ '50%', '50%' ];
var rect = this.getBoundingClientRect($el[ 0 ])
// The rounding rules are still magical
// one is for sure, that if we get a precise whole number w/o fractional part
// we subtract 1 from it, since pixel counting starts from 0
// when we have fractional part, in good browsers it seems its enough to throw it away with `floor`
// in IE we additionally need to subtract 1
// following the conservative path and subtracting 1 in all browsers
var width = Math.floor(rect.width)
// if (bowser.msie || bowser.edge || width === rect.width) width--
width--
var height = Math.floor(rect.height)
// if (bowser.msie || bowser.edge || height === rect.height) height--
height--
if (typeof (offset[ 0 ]) === 'string') {
parts = offset[ 0 ].split('%');
offset[ 0 ] = parseInt(offset[ 0 ].match(/\d+/)[ 0 ], 10) * width / 100;
if (parts[ 1 ]) {
offset[ 0 ] += parseInt(parts[ 1 ]);
}
}
offset[ 0 ] = Math.round(offset[ 0 ])
if (typeof (offset[ 1 ]) === 'string') {
parts = offset[ 1 ].split('%');
offset[ 1 ] = parseInt(offset[ 1 ].match(/\d+/)[ 0 ], 10) * height / 100;
if (parts[ 1 ]) {
offset[ 1 ] += parseInt(parts[ 1 ]);
}
}
offset[ 1 ] = Math.round(offset[ 1 ])
return offset
},
// return viewport coordinates
// can be simplified with just `getBoundingClientRect` ?
getTargetCoordinate : function (target, local, offset) {
var el = this.normalizeElement(target),
$el = this.$(el),
elOffset = $el.offset(),
elDoc = el.ownerDocument,
elWin = elDoc.defaultView || elDoc.parentWindow,
xy = [ this.pageXtoViewportX(elOffset.left, elWin), this.pageYtoViewportY(elOffset.top, elWin) ]
offset = this.normalizeOffset(offset, $el)
xy[ 0 ] += offset[ 0 ];
xy[ 1 ] += offset[ 1 ];
if (local === false) {
// Potentially we're interacting with an element inside a nested frame, which means
// the coordinates are local to that frame
if (elWin && elWin !== this.global) {
var offsetsToTop = this.$(elWin.frameElement).offset()
xy[ 0 ] += this.pageXtoViewportX(offsetsToTop.left)
xy[ 1 ] += this.pageYtoViewportY(offsetsToTop.top)
}
}
return xy;
},
<span id='Siesta-Test-Element-method-isElementVisible'> /**
</span> * Returns true if the element is visible, checking jQuery :visible selector + style visibility value.
*
* @param {Siesta.Test.ActionTarget} el The element
* @return {Boolean}
*/
isElementVisible : function(el) {
el = this.normalizeElement(el);
// Workaround for OPTION elements which don't behave like normal DOM elements. jQuery always consider them invisible.
// Decide based on visibility of the parent SELECT node
if (el && el.nodeName.toLowerCase() === 'option') {
el = this.$(el).closest('select')[0]
}
if (el) {
try {
// Jquery :visible doesn't handle SVG/VML, so manual check
// accessing to `this.global.SVGElement` throws exceptions for popups in IE 9
if (window.SVGElement && el instanceof this.global.SVGElement)
return el.style.display !== 'none' && el.style.visibility !== 'hidden'
} catch (e) {
}
// Jquery :visible doesn't take visibility into account
return this.$(el).is(':visible') && (this.$(el).css('visibility') !== 'hidden')
}
return false
},
<span id='Siesta-Test-Element-method-assertTextPresent'> /**
</span> * Passes if the `innerText` property of the &lt;body&gt; element contains the text passed
*
* @param {String} text The text to match
* @param {String} [description] The description for the assertion
*/
assertTextPresent : function(text, description) {
this.like(this.global.document.body.innerText, text, description);
},
<span id='Siesta-Test-Element-method-contentLike'> /**
</span> * Passes if the innerHTML of the passed element contains the text passed
*
* @param {Siesta.Test.ActionTarget} el The element to query
* @param {String} text The text to match
* @param {String} [description] The description for the assertion
*/
contentLike : function(el, text, description) {
el = this.normalizeElement(el);
this.like(el.innerHTML, text, description);
},
<span id='Siesta-Test-Element-method-contentNotLike'> /**
</span> * Passes if the innerHTML of the passed element does not contain the text passed
*
* @param {Siesta.Test.ActionTarget} el The element to query
* @param {String} text The text to match
* @param {String} [description] The description for the assertion
*/
contentNotLike : function(el, text, description) {
el = this.normalizeElement(el);
this.unlike(el.innerHTML, text, description);
},
<span id='Siesta-Test-Element-method-waitForContentLike'> /**
</span> * Waits until the innerHTML of the passed element contains the text passed
*
* @param {Siesta.Test.ActionTarget} el The element to query
* @param {String} text The text to match
* @param {Function} callback The callback to call after the CSS selector has been found
* @param {Object} scope The scope for the callback
* @param {Int} timeout The maximum amount of time to wait for the condition to be fulfilled. Defaults to the {@link Siesta.Test.ExtJS#waitForTimeout} value.
*/
waitForContentLike : function(el, text, callback, scope, timeout) {
var R = Siesta.Resource('Siesta.Test.Element');
el = this.normalizeElement(el);
return this.waitFor({
method : function() { return el.innerHTML.match(text); },
callback : callback,
scope : scope,
timeout : timeout,
assertionName : 'waitForContentLike',
description : ' ' + R.get('elementContent') + ' "' + text + '" ' + R.get('toAppear')
});
},
<span id='Siesta-Test-Element-method-waitForContentNotLike'> /**
</span> * Waits until the innerHTML of the passed element does not contain the text passed
*
* @param {Siesta.Test.ActionTarget} el The element to query
* @param {String} text The text to match
* @param {Function} callback The callback to call after the CSS selector has been found
* @param {Object} scope The scope for the callback
* @param {Int} timeout The maximum amount of time to wait for the condition to be fulfilled. Defaults to the {@link Siesta.Test.ExtJS#waitForTimeout} value.
*/
waitForContentNotLike : function(el, text, callback, scope, timeout) {
var R = Siesta.Resource('Siesta.Test.Element');
el = this.normalizeElement(el);
return this.waitFor({
method : function() { return !el.innerHTML.match(text); },
callback : callback,
scope : scope,
timeout : timeout,
assertionName : 'waitForContentNotLike',
description : ' ' + R.get('elementContent') + ' "' + text + '" ' + R.get('toDisappear')
});
},
getRandomTypeString : function (length) {
var allowedCharacters = this.allowedCharacters
var special = allowedCharacters.special.split('/')
var punctuation = allowedCharacters.punctuation
var normal = allowedCharacters.normal
var total = special.length + punctuation.length + normal.length
var str = ''
for (var i = 0; i < length; i++) {
var index = this.randomBetween(0, total - 1)
if (index < normal.length)
str += normal.substr(index, 1)
else {
index -= normal.length
if (index < punctuation.length)
str += punctuation.substr(index, 1)
else {
index -= punctuation.length
str += '[' + special[ index ] + ']'
}
}
}
return str
},
<span id='Siesta-Test-Element-method-monkeyTest'> /**
</span> * Performs clicks, double clicks, right clicks and drags at random coordinates within the passed target.
* Also performs random key presses. You can specify an array of DOM selectors to avoid (see `skipTargets` below).
*
* While doing all these random actions it tracks the number of exceptions thrown and reports a failure
* if there was any. Otherwise it reports a passed assertion.
*
* Use this assertion to "stress-test" your component, making sure it will work correctly in various unexpected
* interaction scenarious.
*
* Note that as a special case, when this method is provided with the document's &lt;body&gt; element as a target,
* it will test the whole browser viewport.
*
* This method supports two call signatures. One is with multiple positional arguments and another with single options object.
*
* @param {Siesta.Test.ActionTarget | Object} options The element to upon which to unleash the "monkey", or a config object with the options
* @param {Siesta.Test.ActionTarget} options.target The element to upon which to unleash the "monkey"
* @param {Array[String]} options.skipTargets An array of DOM selectors, for the elements inside the `target`, which will be avoided by monkeys.
* @param {Number} options.nbrInteractions The number of random interactions to perform. Default value is 30
* @param {String} options.description The optional description for the assertion.
* @param {Function} options.callback The callback to call after all actions are completed
* @param {Object} options.scope The scope for the callback
* @param {Number} nbrInteractions The number of random interactions to perform. Default value is 30
* @param {String} [description] The description for the assertion
* @param {Function} callback The callback to call after all actions are completed
* @param {Object} scope The scope for the callback
*/
monkeyTest : function (el, nbrInteractions, description, callback, scope, stepCallback) {
var skipTargets
var alwaysLogActions = false
if (arguments.length === 1 && el.target) {
nbrInteractions = el.nbrInteractions
description = el.description
callback = el.callback
scope = el.scope
stepCallback = el.stepCallback
skipTargets = el.skipTargets
alwaysLogActions = Boolean(el.alwaysLogActions)
el = el.target
} else {
if (typeof nbrInteractions === 'function') {
callback = nbrInteractions;
scope = description;
description = '';
} else if (typeof description === 'function') {
callback = description;
description = '';
}
}
skipTargets = skipTargets || []
// ignore actions on anchors by default, to prevent page navigation
skipTargets.push('a')
nbrInteractions = typeof nbrInteractions === 'number' ? nbrInteractions : 30;
el = this.normalizeElement(el, false, true);
this.suppressPassedWaitForAssertion = true;
var global = this.global
var isBody = el == global.document.body
var me = this,
root = this.getRootTest(),
offset = me.$(el).offset(),
right = offset.left + me.$(isBody ? global : el).width(),
bottom = offset.top + me.$(isBody ? global : el).height();
var actionLog = []
var R = Siesta.Resource('Siesta.Test.Element');
var queue = new Siesta.Util.Queue({
deferer : me.originalSetTimeout,
deferClearer : me.originalClearTimeout,
interval : me.actionDelay,
observeTest : this,
processor : function (data) {
if (root.nbrExceptions || root.failed) {
assertionChecker()
// do not continue if the test has detected an exception thrown
queue.abort()
} else {
var async = me.beginAsync(null, function (test) {
test.fail(
description || R.get('monkeyNoExceptions'),
R.get('monkeyActionLog') + ":" + JSON.stringify(actionLog)
)
return true
});
var next = data.next
data.next = function () {
// callback to call after each monkey action
stepCallback && stepCallback(data)
me.endAsync(async)
next()
}
data.action(data)
}
}
});
var dummy = []
dummy.length = nbrInteractions
var ignoreAction = function (data, i) {
var target = me.normalizeElement(data.dragFrom || data.xy)
// Prevent also drag target as it could be a link tag
var dragToTarget = data.dragTo && me.normalizeElement(data.dragTo)
for (var i = 0; i < skipTargets.length; i++) {
var toSkip = me.query(skipTargets[ i ], el)
for (var j = 0; j < toSkip.length; j++) {
var skip = toSkip[ j ]
if (
(target && (target == skip || me.contains(skip, target)))
|| dragToTarget && (dragToTarget == skip || me.contains(skip, dragToTarget))
) {
data.next()
return true
}
}
}
return false
}
Joose.A.each(dummy, function (value, i) {
// Inject { waitForSelector : 'body' } before every monkey action to make sure we always have a body
queue.addAsyncStep({
action : function (data) {
me.waitForSelector('body', data.next);
}
});
var xy = [ me.randomBetween(offset.left, right), me.randomBetween(offset.top, bottom) ];
switch (me.randomBetween(0, 4)) {
case 0:
queue.addAsyncStep({
action : function (data) {
if (!ignoreAction(data)) {
actionLog.push({ 'click' : xy })
me.click(data.xy, data.next)
}
},
xy : xy
});
break;
case 1:
queue.addAsyncStep({
action : function (data) {
if (!ignoreAction(data)) {
actionLog.push({ 'doubleclick' : xy })
me.doubleClick(data.xy, data.next)
}
},
xy : xy
});
break;
case 2:
queue.addAsyncStep({
action : function (data) {
if (!ignoreAction(data)) {
if (me.simulator.type != 'native' && "oncontextmenu" in window) {
actionLog.push({ 'rightclick' : xy })
me.rightClick(data.xy, data.next)
} else {
actionLog.push({ 'click' : xy })
me.click(data.xy, data.next)
}
}
},
xy : xy
});
break;
case 3:
var dragTo = [ me.randomBetween(offset.left, right), me.randomBetween(offset.top, bottom) ]
queue.addAsyncStep({
action : function (data) {
if (!ignoreAction(data)) {
actionLog.push({
action : 'drag',
target : xy,
to : dragTo
})
me.dragTo(data.dragFrom, data.dragTo, data.next)
}
},
dragFrom : xy,
dragTo : dragTo
});
break;
case 4:
var text = me.getRandomTypeString(15)
// First click somewhere then type
queue.addAsyncStep({
action : function (data) {
if (!ignoreAction(data)) {
actionLog.push({ 'click' : xy })
me.click(data.xy, function () {
me.waitForSelector('body', function () {
actionLog.push({ 'type' : text.replace("'", "\\'") })
me.type(null, text, data.next)
});
})
}
},
xy : xy
});
break;
}
})
var checkerActivated = false
var assertionChecker = function () {
checkerActivated = true
if (root.nbrExceptions || root.failed) {
me.fail(description || R.get('monkeyNoExceptions'), R.get('monkeyActionLog') + ":" + JSON.stringify(actionLog))
} else
me.pass(description || R.get('monkeyNoExceptions'), alwaysLogActions ? R.get('monkeyActionLog') + ":" + JSON.stringify(actionLog) : undefined)
}
this.on('beforetestfinalizeearly', assertionChecker)
queue.run(function () {
if (!checkerActivated) {
me.un('beforetestfinalizeearly', assertionChecker)
assertionChecker()
}
me.suppressPassedWaitForAssertion = false;
me.processCallbackFromTest(callback, [actionLog], scope || me)
});
},
<span id='Siesta-Test-Element-method-hasCls'> /**
</span> * Passes if the element has the supplied CSS classname
*
* @param {Siesta.Test.ActionTarget} el The element to query
* @param {String} cls The class name to check for
* @param {String} [description] The description for the assertion
*/
hasCls : function (el, cls, description) {
var R = Siesta.Resource('Siesta.Test.Element');
el = this.normalizeElement(el);
if (el.classList.contains(cls)) {
this.pass(description, {
descTpl : R.get('elementHasClass') + ' {cls}',
cls : cls
});
} else {
this.fail(description, {
assertionName : 'hasCls',
got : el.className,
gotDesc : R.get('elementClasses'),
need : cls,
needDesc : R.get('needClass')
})
}
},
<span id='Siesta-Test-Element-method-hasNotCls'> /**
</span> * Passes if the element does not have the supplied CSS classname
*
* @param {Siesta.Test.ActionTarget} el The element to query
* @param {String} cls The class name to check for
* @param {String} [description] The description for the assertion
*/
hasNotCls : function (el, cls, description) {
var R = Siesta.Resource('Siesta.Test.Element');
el = this.normalizeElement(el);
if (!el.classList.contains(cls)) {
this.pass(description, {
descTpl : R.get('elementHasNoClass') + ' {cls}',
cls : cls
});
} else {
this.fail(description, {
assertionName : 'hasNotCls',
got : el.className,
gotDesc : R.get('elementClasses'),
annotation : R.get('elementHasClass') + ' [' + cls + ']'
})
}
},
<span id='Siesta-Test-Element-method-hasStyle'> /**
</span> * Passes if the element has the supplied style value
*
* @param {Siesta.Test.ActionTarget} el The element to query
* @param {String} property The style property to check for
* @param {String} value The style value to check for
* @param {String} [description] The description for the assertion
*/
hasStyle : function (el, property, value, description) {
var R = Siesta.Resource('Siesta.Test.Element');
el = this.normalizeElement(el);
if (this.$(el).css(property) === value) {
this.pass(description, {
descTpl : R.get('hasStyleDescTpl'),
value : value,
property : property
});
} else {
this.fail(description, {
assertionName : 'hasStyle',
got : this.$(el).css(property),
gotDesc : R.get('elementStyles'),
need : value,
needDesc : R.get('needStyle')
});
}
},
<span id='Siesta-Test-Element-method-hasNotStyle'> /**
</span> * Passes if the element does not have the supplied style value
*
* @param {Siesta.Test.ActionTarget} el The element to query
* @param {String} property The style property to check for
* @param {String} value The style value to check for
* @param {String} [description] The description for the assertion
*/
hasNotStyle : function (el, property, value, description) {
var R = Siesta.Resource('Siesta.Test.Element');
el = this.normalizeElement(el);
if (this.$(el).css(property) !== value) {
this.pass(description, {
descTpl : R.get('hasNotStyleDescTpl'),
value : value,
property : property
});
} else {
this.fail(description, {
assertionName : 'hasNotStyle',
got : el.style.toString(),
gotDesc : R.get('elementStyles'),
annotation : R.get('hasTheStyle') + ' [' + property + ']'
});
}
},
<span id='Siesta-Test-Element-method-waitForSelectorAt'> /**
</span> * Waits for a certain CSS selector to be found at the passed XY coordinate, and calls the callback when found.
* The callback will receive the element from the passed XY coordinates.
*
* @param {Array} xy The x and y coordinates to query
* @param {String} selector The CSS selector to check for
* @param {Function} callback The callback to call after the CSS selector has been found
* @param {Object} scope The scope for the callback
* @param {Int} timeout The maximum amount of time to wait for the condition to be fulfilled. Defaults to the {@link Siesta.Test#waitForTimeout} value.
*/
waitForSelectorAt : function(xy, selector, callback, scope, timeout) {
var R = Siesta.Resource('Siesta.Test.Element');
if (!selector) throw R.get('noCssSelector');
var me = this
return this.waitFor({
method : function() {
var el = me.elementFromPoint(xy[0], xy[1], true);
if (el && me.$(el).is(selector)) return el;
},
callback : callback,
scope : scope,
timeout : timeout,
assertionName : 'waitForSelectorAt',
description : ' ' + R.get('selector') + ' "' + selector + '" ' + R.get('toAppearAt') + ': [' + xy.toString() + ']'
});
},
<span id='Siesta-Test-Element-method-waitForSelectorAtCursor'> /**
</span> * Waits for a certain CSS selector to be found at current cursor position, and calls the callback when found.
* The callback will receive the element found.
*
* @param {String} selector The CSS selector to check for
* @param {Function} callback The callback to call after the CSS selector has been found
* @param {Object} scope The scope for the callback
* @param {Int} timeout The maximum amount of time to wait for the condition to be fulfilled. Defaults to the {@link Siesta.Test#waitForTimeout} value.
*/
waitForSelectorAtCursor : function(selector, callback, scope, timeout) {
return this.waitForSelectorAt(this.simulator.currentPosition, selector, callback, scope, timeout);
},
<span id='Siesta-Test-Element-method-waitForSelector'> /**
</span> * Waits for a certain CSS selector to be found in the DOM, and then calls the callback supplied.
* The callback will receive the results of jQuery selector.
*
* @param {String} selector The CSS selector to check for
* @param {Siesta.Test.ActionTarget} root (optional) The root element in which to detect the selector.
* @param {Function} callback The callback to call after the CSS selector has been found
* @param {Object} scope The scope for the callback
* @param {Int} timeout The maximum amount of time to wait for the condition to be fulfilled. Defaults to the {@link Siesta.Test#waitForTimeout} value.
*/
waitForSelector : function(selector, root, callback, scope, timeout) {
var R = Siesta.Resource('Siesta.Test.Element');
var me = this;
if (!selector) throw R.get('noCssSelector');
if (this.typeOf(root) == 'Function') {
timeout = scope;
scope = callback;
callback = root;
root = null;
}
if (root) root = this.normalizeElement(root);
return this.waitFor({
method : function() {
var result = me.query(selector, root);
if (result.length > 0) return result;
},
callback : callback,
scope : scope,
timeout : timeout,
assertionName : 'waitForSelector',
description : ' ' + R.get('selector') + ' "' + selector + '" ' + R.get('toAppear')
});
},
<span id='Siesta-Test-Element-method-waitForSelectors'> /**
</span> * Waits till all the CSS selectors from the provided array to be found in the DOM, and then calls the callback supplied.
*
* @param {Array[String]} selectors The array of CSS selectors to check for
* @param {Siesta.Test.ActionTarget} root (optional) The root element in which to detect the selector.
* @param {Function} callback The callback to call after the CSS selector has been found
* @param {Object} scope The scope for the callback
* @param {Int} timeout The maximum amount of time to wait for the condition to be fulfilled. Defaults to the {@link Siesta.Test#waitForTimeout} value.
*/
waitForSelectors : function(selectors, root, callback, scope, timeout) {
var R = Siesta.Resource('Siesta.Test.Element');
if (selectors.length < 1) throw R.get('waitForSelectorsBadInput');
if (this.typeOf(root) == 'Function') {
timeout = scope;
scope = callback;
callback = root;
root = null;
}
if (root) root = this.normalizeElement(root);
var me = this
return this.waitFor({
method : function () {
var allPresent = true
Joose.A.each(selectors, function (selector) {
if (me.query(selector, root).length === 0) {
allPresent = false
// stop iteration
return false
}
})
return allPresent
},
callback : callback,
scope : scope,
timeout : timeout,
assertionName : 'waitForSelectors',
description : ' ' + R.get('selectors') + ' "' + selectors + '" ' + R.get('toAppear')
});
},
<span id='Siesta-Test-Element-method-waitForSelectorNotFound'> /**
</span> * Waits for a certain CSS selector to not be found in the DOM, and then calls the callback supplied.
*
* @param {String} selector The CSS selector to check for
* @param {Siesta.Test.ActionTarget} root (optional) The root element in which to detect the selector.
* @param {Function} callback The callback to call after the CSS selector has been found
* @param {Object} scope The scope for the callback
* @param {Int} timeout The maximum amount of time to wait for the condition to be fulfilled. Defaults to the {@link Siesta.Test#waitForTimeout} value.
*/
waitForSelectorNotFound : function(selector, root, callback, scope, timeout) {
var R = Siesta.Resource('Siesta.Test.Element');
var me = this;
if (!selector) throw 'A CSS selector must be supplied';
if (this.typeOf(root) == 'Function') {
timeout = scope;
scope = callback;
callback = root;
root = null;
}
if (root) root = this.normalizeElement(root);
return this.waitFor({
method : function() { return me.query(selector, root).length === 0; },
callback : callback,
scope : scope,
timeout : timeout,
assertionName : 'waitForSelectorNotFound',
description : ' ' + R.get('selector') + ' "' + selector + '" ' + R.get('toDisappear')
});
},
<span id='Siesta-Test-Element-method-waitForElementVisible'> /**
</span> * Waits until the passed element becomes "visible" in the DOM and calls the provided callback.
* Please note, that "visible" means element will just have a DOM node, and still may be hidden by another visible element.
*
* The callback will receive the passed element as the 1st argument.
*
* See also {@link #waitForElementTop} method.
*
* @param {Siesta.Test.ActionTarget} el The element to look for.
* @param {Function} callback The callback to call after the CSS selector has been found
* @param {Object} scope The scope for the callback
* @param {Int} timeout The maximum amount of time to wait for the condition to be fulfilled. Defaults to the {@link Siesta.Test.ExtJS#waitForTimeout} value.
*/
waitForElementVisible : function(el, callback, scope, timeout) {
var R = Siesta.Resource('Siesta.Test.Element');
return this.waitFor({
method : function() {
var normalized = this.normalizeElement(el, true);
if (normalized && this.isElementVisible(normalized)) return normalized;
},
callback : callback,
scope : scope,
timeout : timeout,
assertionName : 'waitForElementVisible',
description : ' ' + R.get('element') + ' "' + el.toString() + '" ' + R.get('toAppear')
});
},
<span id='Siesta-Test-Element-method-waitForElementNotVisible'> /**
</span> * Waits until the passed element is becomes not "visible" in the DOM and call the provided callback.
* Please note, that "visible" means element will just have a DOM node, and still may be hidden by another visible element.
*
* The callback will receive the passed element as the 1st argument.
*
* See also {@link #waitForElementNotTop} method.
*
* @param {Siesta.Test.ActionTarget} el The element to look for.
* @param {Function} callback The callback to call after the CSS selector has been found
* @param {Object} scope The scope for the callback
* @param {Int} timeout The maximum amount of time to wait for the condition to be fulfilled. Defaults to the {@link Siesta.Test.ExtJS#waitForTimeout} value.
*/
waitForElementNotVisible : function(el, callback, scope, timeout) {
el = this.normalizeElement(el);
var R = Siesta.Resource('Siesta.Test.Element');
var me = this;
return this.waitFor({
method : function() { return !me.isElementVisible(el) && el; },
callback : callback,
scope : scope,
timeout : timeout,
assertionName : 'waitForElementNotVisible',
description : ' ' + R.get('element') + ' "' + el.toString() + '" ' + R.get('toDisappear')
});
},
<span id='Siesta-Test-Element-method-waitForElementTop'> /**
</span> * Waits until the passed element is the 'top' element in the DOM and call the provided callback.
*
* The callback will receive the passed element as the 1st argument.
*
* @param {Siesta.Test.ActionTarget} el The element to look for.
* @param {Function} callback The callback to call
* @param {Object} scope The scope for the callback
* @param {Int} timeout The maximum amount of time to wait for the condition to be fulfilled. Defaults to the {@link Siesta.Test.ExtJS#waitForTimeout} value.
*/
waitForElementTop : function(el, callback, scope, timeout) {
var R = Siesta.Resource('Siesta.Test.Element');
return this.waitFor({
method : function() {
var normalized = this.normalizeElement(el, true);
if (normalized && this.elementIsTop(normalized, true)) {
return normalized;
}
},
callback : callback,
scope : scope,
timeout : timeout,
assertionName : 'waitForElementTop',
description : ' ' + R.get('element') + ' "' + el.toString() + '" ' + R.get('toBeTopEl')
});
},
<span id='Siesta-Test-Element-method-waitForElementNotTop'> /**
</span> * Waits until the passed element is not the 'top' element in the DOM and calls the provided callback with the element found.
*
* The callback will receive the actual top element.
*
* @param {Siesta.Test.ActionTarget} el The element to look for.
* @param {Function} callback The callback to call
* @param {Object} scope The scope for the callback
* @param {Int} timeout The maximum amount of time to wait for the condition to be fulfilled. Defaults to the {@link Siesta.Test.ExtJS#waitForTimeout} value.
*/
waitForElementNotTop : function(el, callback, scope, timeout) {
el = this.normalizeElement(el);
var R = Siesta.Resource('Siesta.Test.Element');
var me = this
return this.waitFor({
method : function() {
if (!me.elementIsTop(el, true)) {
var center = me.findCenter(el);
return me.elementFromPoint(center[0], center[1], true);
}
},
callback : callback,
scope : scope,
timeout : timeout,
assertionName : 'waitForElementNotTop',
description : ' ' + R.get('element') + ' "' + el.toString() + '" ' + R.get('toNotBeTopEl')
});
},
<span id='Siesta-Test-Element-method-elementIsVisible'> /**
</span> * Passes if the element is in the DOM and visible.
* @param {Siesta.Test.ActionTarget} el The element
* @param {String} [description] The description for the assertion
*/
elementIsVisible : function(el, description) {
el = this.normalizeElement(el, true, false, false, { ignoreNonVisible : false });
this.ok(el && this.isElementVisible(el), description);
},
<span id='Siesta-Test-Element-method-elementIsNotVisible'> /**
</span> * Passes if the element is not visible.
* @param {Siesta.Test.ActionTarget} el The element
* @param {String} [description] The description for the assertion
*/
elementIsNotVisible : function(el, description) {
el = this.normalizeElement(el, false, false, false, { ignoreNonVisible : false });
this.notOk(this.isElementVisible(el), description);
},
<span id='Siesta-Test-Element-method-elementIsTop'> /**
</span> * Utility method which checks if the passed method is the 'top' element at its position. By default, "top" element means,
* that center point of the element is not covered with any other elements. You can also check any other point reachability
* using the "offset" argument.
*
* @param {Siesta.Test.ActionTarget} el The element to look for.
* @param {Boolean} allowChildren true to also include child nodes. False to strictly check for the passed element.
* @param {Array} offset An array of 2 elements, defining "x" and "y" offset from the left-top corner of the element
*
* @return {Boolean} true if the element is the top element.
*/
elementIsTop : function (el, allowChildren, offset) {
el = this.normalizeElement(el);
if (this.nodeIsOrphan(el)) {
return false;
}
// Workaround for OPTION elements which don't behave like normal DOM elements. jQuery always consider them invisible.
// Decide based on visibility of the parent SELECT node
if (el && el.nodeName.toLowerCase() === 'option') {
el = this.$(el).closest('select')[0];
}
var fromPointAPI = this.getQueryableContainer(el)
var localPoint = this.getTargetCoordinate(el, true, offset)
var foundEl = fromPointAPI.elementFromPoint(localPoint[ 0 ], localPoint[ 1 ]);
return foundEl && (foundEl === el || (allowChildren && this.$(foundEl).closest(el).length > 0));
},
// Helper method to find out if an offset is targeting a point outside its target
// Assumes the el passed is visible
isOffsetInsideElementBox : function (el, offset) {
if (!offset) return true;
var $el = this.$(this.normalizeElement(el));
var w = $el.outerWidth();
var h = $el.outerHeight();
offset = this.normalizeOffset(offset, $el);
return offset[0] >= 0 && offset[0] < w &&
offset[1] >= 0 && offset[1] < h;
},
<span id='Siesta-Test-Element-method-elementIsAt'> /**
</span> * Passes if the element is found at the supplied xy coordinates.
*
* @param {Siesta.Test.ActionTarget} el The element to query
* @param {Array} xy The xy coordinate to query.
* @param {Boolean} allowChildren true to also include child nodes. False to strictly check for the passed element.
* @param {String} [description] The description for the assertion
*/
elementIsAt : function(el, xy, allowChildren, description) {
el = this.normalizeElement(el);
var foundEl = this.elementFromPoint(xy[0], xy[1], true);
var R = Siesta.Resource('Siesta.Test.Element');
if (!foundEl) {
this.fail(description, {
assertionName : 'elementIsAt',
got : { x: xy[0], y : xy[1] },
gotDesc : R.get('Position'),
annotation : R.get('noElementAtPosition')
});
} else if (allowChildren) {
if (foundEl === el || this.$(foundEl).closest(el).length > 0) {
this.pass(description, {
descTpl : R.get('elementIsAtDescTpl'),
x : xy[ 0 ],
y : xy[ 1 ]