UNPKG

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
<!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(&#39;Siesta.Test.Element&#39;, { does : [ Siesta.Util.Role.CanCalculatePageScroll, Siesta.Util.Role.Dom ], requires : [ &#39;typeOf&#39;, &#39;chain&#39;, &#39;normalizeElement&#39; ], has : { allowMonkeyToClickOnAnchors : false, allowedCharacters : function () { return { // does not include TAB by purpose, because our &quot;TAB&quot; simulation is not perfect // Also exclude BACKSPACE since it navigates the page special : &#39;ENTER/ESCAPE/PAGE-UP/PAGE-DOWN/END/HOME/UP/RIGHT/DOWN/LEFT/INSERT/DELETE&#39;, // does not inlcude * because Ext fails on typing it punctuation : &#39;.,/()[]{}\\&quot;\&#39;`~!?@#$%^&amp;_=+-&#39;, normal : &quot;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789&quot; } } }, 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 &quot;local&quot; to that iframe element). To get coordinates relative to the test iframe (&quot;global&quot; 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) === &#39;Function&#39;) offset = offset.call(this) offset = offset &amp;&amp; offset.slice() || [ &#39;50%&#39;, &#39;50%&#39; ]; 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 ]) === &#39;string&#39;) { parts = offset[ 0 ].split(&#39;%&#39;); 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 ]) === &#39;string&#39;) { parts = offset[ 1 ].split(&#39;%&#39;); 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&#39;re interacting with an element inside a nested frame, which means // the coordinates are local to that frame if (elWin &amp;&amp; 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&#39;t behave like normal DOM elements. jQuery always consider them invisible. // Decide based on visibility of the parent SELECT node if (el &amp;&amp; el.nodeName.toLowerCase() === &#39;option&#39;) { el = this.$(el).closest(&#39;select&#39;)[0] } if (el) { try { // Jquery :visible doesn&#39;t handle SVG/VML, so manual check // accessing to `this.global.SVGElement` throws exceptions for popups in IE 9 if (window.SVGElement &amp;&amp; el instanceof this.global.SVGElement) return el.style.display !== &#39;none&#39; &amp;&amp; el.style.visibility !== &#39;hidden&#39; } catch (e) { } // Jquery :visible doesn&#39;t take visibility into account return this.$(el).is(&#39;:visible&#39;) &amp;&amp; (this.$(el).css(&#39;visibility&#39;) !== &#39;hidden&#39;) } return false }, <span id='Siesta-Test-Element-method-assertTextPresent'> /** </span> * Passes if the `innerText` property of the &amp;lt;body&amp;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(&#39;Siesta.Test.Element&#39;); el = this.normalizeElement(el); return this.waitFor({ method : function() { return el.innerHTML.match(text); }, callback : callback, scope : scope, timeout : timeout, assertionName : &#39;waitForContentLike&#39;, description : &#39; &#39; + R.get(&#39;elementContent&#39;) + &#39; &quot;&#39; + text + &#39;&quot; &#39; + R.get(&#39;toAppear&#39;) }); }, <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(&#39;Siesta.Test.Element&#39;); el = this.normalizeElement(el); return this.waitFor({ method : function() { return !el.innerHTML.match(text); }, callback : callback, scope : scope, timeout : timeout, assertionName : &#39;waitForContentNotLike&#39;, description : &#39; &#39; + R.get(&#39;elementContent&#39;) + &#39; &quot;&#39; + text + &#39;&quot; &#39; + R.get(&#39;toDisappear&#39;) }); }, getRandomTypeString : function (length) { var allowedCharacters = this.allowedCharacters var special = allowedCharacters.special.split(&#39;/&#39;) var punctuation = allowedCharacters.punctuation var normal = allowedCharacters.normal var total = special.length + punctuation.length + normal.length var str = &#39;&#39; for (var i = 0; i &lt; length; i++) { var index = this.randomBetween(0, total - 1) if (index &lt; normal.length) str += normal.substr(index, 1) else { index -= normal.length if (index &lt; punctuation.length) str += punctuation.substr(index, 1) else { index -= punctuation.length str += &#39;[&#39; + special[ index ] + &#39;]&#39; } } } 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 &quot;stress-test&quot; 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&#39;s &amp;lt;body&amp;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 &quot;monkey&quot;, or a config object with the options * @param {Siesta.Test.ActionTarget} options.target The element to upon which to unleash the &quot;monkey&quot; * @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 &amp;&amp; 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 === &#39;function&#39;) { callback = nbrInteractions; scope = description; description = &#39;&#39;; } else if (typeof description === &#39;function&#39;) { callback = description; description = &#39;&#39;; } } skipTargets = skipTargets || [] // ignore actions on anchors by default, to prevent page navigation skipTargets.push(&#39;a&#39;) nbrInteractions = typeof nbrInteractions === &#39;number&#39; ? 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(&#39;Siesta.Test.Element&#39;); 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(&#39;monkeyNoExceptions&#39;), R.get(&#39;monkeyActionLog&#39;) + &quot;:&quot; + JSON.stringify(actionLog) ) return true }); var next = data.next data.next = function () { // callback to call after each monkey action stepCallback &amp;&amp; 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 &amp;&amp; me.normalizeElement(data.dragTo) for (var i = 0; i &lt; skipTargets.length; i++) { var toSkip = me.query(skipTargets[ i ], el) for (var j = 0; j &lt; toSkip.length; j++) { var skip = toSkip[ j ] if ( (target &amp;&amp; (target == skip || me.contains(skip, target))) || dragToTarget &amp;&amp; (dragToTarget == skip || me.contains(skip, dragToTarget)) ) { data.next() return true } } } return false } Joose.A.each(dummy, function (value, i) { // Inject { waitForSelector : &#39;body&#39; } before every monkey action to make sure we always have a body queue.addAsyncStep({ action : function (data) { me.waitForSelector(&#39;body&#39;, 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({ &#39;click&#39; : xy }) me.click(data.xy, data.next) } }, xy : xy }); break; case 1: queue.addAsyncStep({ action : function (data) { if (!ignoreAction(data)) { actionLog.push({ &#39;doubleclick&#39; : xy }) me.doubleClick(data.xy, data.next) } }, xy : xy }); break; case 2: queue.addAsyncStep({ action : function (data) { if (!ignoreAction(data)) { if (me.simulator.type != &#39;native&#39; &amp;&amp; &quot;oncontextmenu&quot; in window) { actionLog.push({ &#39;rightclick&#39; : xy }) me.rightClick(data.xy, data.next) } else { actionLog.push({ &#39;click&#39; : 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 : &#39;drag&#39;, 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({ &#39;click&#39; : xy }) me.click(data.xy, function () { me.waitForSelector(&#39;body&#39;, function () { actionLog.push({ &#39;type&#39; : text.replace(&quot;&#39;&quot;, &quot;\\&#39;&quot;) }) 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(&#39;monkeyNoExceptions&#39;), R.get(&#39;monkeyActionLog&#39;) + &quot;:&quot; + JSON.stringify(actionLog)) } else me.pass(description || R.get(&#39;monkeyNoExceptions&#39;), alwaysLogActions ? R.get(&#39;monkeyActionLog&#39;) + &quot;:&quot; + JSON.stringify(actionLog) : undefined) } this.on(&#39;beforetestfinalizeearly&#39;, assertionChecker) queue.run(function () { if (!checkerActivated) { me.un(&#39;beforetestfinalizeearly&#39;, 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(&#39;Siesta.Test.Element&#39;); el = this.normalizeElement(el); if (el.classList.contains(cls)) { this.pass(description, { descTpl : R.get(&#39;elementHasClass&#39;) + &#39; {cls}&#39;, cls : cls }); } else { this.fail(description, { assertionName : &#39;hasCls&#39;, got : el.className, gotDesc : R.get(&#39;elementClasses&#39;), need : cls, needDesc : R.get(&#39;needClass&#39;) }) } }, <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(&#39;Siesta.Test.Element&#39;); el = this.normalizeElement(el); if (!el.classList.contains(cls)) { this.pass(description, { descTpl : R.get(&#39;elementHasNoClass&#39;) + &#39; {cls}&#39;, cls : cls }); } else { this.fail(description, { assertionName : &#39;hasNotCls&#39;, got : el.className, gotDesc : R.get(&#39;elementClasses&#39;), annotation : R.get(&#39;elementHasClass&#39;) + &#39; [&#39; + cls + &#39;]&#39; }) } }, <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(&#39;Siesta.Test.Element&#39;); el = this.normalizeElement(el); if (this.$(el).css(property) === value) { this.pass(description, { descTpl : R.get(&#39;hasStyleDescTpl&#39;), value : value, property : property }); } else { this.fail(description, { assertionName : &#39;hasStyle&#39;, got : this.$(el).css(property), gotDesc : R.get(&#39;elementStyles&#39;), need : value, needDesc : R.get(&#39;needStyle&#39;) }); } }, <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(&#39;Siesta.Test.Element&#39;); el = this.normalizeElement(el); if (this.$(el).css(property) !== value) { this.pass(description, { descTpl : R.get(&#39;hasNotStyleDescTpl&#39;), value : value, property : property }); } else { this.fail(description, { assertionName : &#39;hasNotStyle&#39;, got : el.style.toString(), gotDesc : R.get(&#39;elementStyles&#39;), annotation : R.get(&#39;hasTheStyle&#39;) + &#39; [&#39; + property + &#39;]&#39; }); } }, <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(&#39;Siesta.Test.Element&#39;); if (!selector) throw R.get(&#39;noCssSelector&#39;); var me = this return this.waitFor({ method : function() { var el = me.elementFromPoint(xy[0], xy[1], true); if (el &amp;&amp; me.$(el).is(selector)) return el; }, callback : callback, scope : scope, timeout : timeout, assertionName : &#39;waitForSelectorAt&#39;, description : &#39; &#39; + R.get(&#39;selector&#39;) + &#39; &quot;&#39; + selector + &#39;&quot; &#39; + R.get(&#39;toAppearAt&#39;) + &#39;: [&#39; + xy.toString() + &#39;]&#39; }); }, <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(&#39;Siesta.Test.Element&#39;); var me = this; if (!selector) throw R.get(&#39;noCssSelector&#39;); if (this.typeOf(root) == &#39;Function&#39;) { 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 &gt; 0) return result; }, callback : callback, scope : scope, timeout : timeout, assertionName : &#39;waitForSelector&#39;, description : &#39; &#39; + R.get(&#39;selector&#39;) + &#39; &quot;&#39; + selector + &#39;&quot; &#39; + R.get(&#39;toAppear&#39;) }); }, <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(&#39;Siesta.Test.Element&#39;); if (selectors.length &lt; 1) throw R.get(&#39;waitForSelectorsBadInput&#39;); if (this.typeOf(root) == &#39;Function&#39;) { 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 : &#39;waitForSelectors&#39;, description : &#39; &#39; + R.get(&#39;selectors&#39;) + &#39; &quot;&#39; + selectors + &#39;&quot; &#39; + R.get(&#39;toAppear&#39;) }); }, <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(&#39;Siesta.Test.Element&#39;); var me = this; if (!selector) throw &#39;A CSS selector must be supplied&#39;; if (this.typeOf(root) == &#39;Function&#39;) { 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 : &#39;waitForSelectorNotFound&#39;, description : &#39; &#39; + R.get(&#39;selector&#39;) + &#39; &quot;&#39; + selector + &#39;&quot; &#39; + R.get(&#39;toDisappear&#39;) }); }, <span id='Siesta-Test-Element-method-waitForElementVisible'> /** </span> * Waits until the passed element becomes &quot;visible&quot; in the DOM and calls the provided callback. * Please note, that &quot;visible&quot; 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(&#39;Siesta.Test.Element&#39;); return this.waitFor({ method : function() { var normalized = this.normalizeElement(el, true); if (normalized &amp;&amp; this.isElementVisible(normalized)) return normalized; }, callback : callback, scope : scope, timeout : timeout, assertionName : &#39;waitForElementVisible&#39;, description : &#39; &#39; + R.get(&#39;element&#39;) + &#39; &quot;&#39; + el.toString() + &#39;&quot; &#39; + R.get(&#39;toAppear&#39;) }); }, <span id='Siesta-Test-Element-method-waitForElementNotVisible'> /** </span> * Waits until the passed element is becomes not &quot;visible&quot; in the DOM and call the provided callback. * Please note, that &quot;visible&quot; 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(&#39;Siesta.Test.Element&#39;); var me = this; return this.waitFor({ method : function() { return !me.isElementVisible(el) &amp;&amp; el; }, callback : callback, scope : scope, timeout : timeout, assertionName : &#39;waitForElementNotVisible&#39;, description : &#39; &#39; + R.get(&#39;element&#39;) + &#39; &quot;&#39; + el.toString() + &#39;&quot; &#39; + R.get(&#39;toDisappear&#39;) }); }, <span id='Siesta-Test-Element-method-waitForElementTop'> /** </span> * Waits until the passed element is the &#39;top&#39; 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(&#39;Siesta.Test.Element&#39;); return this.waitFor({ method : function() { var normalized = this.normalizeElement(el, true); if (normalized &amp;&amp; this.elementIsTop(normalized, true)) { return normalized; } }, callback : callback, scope : scope, timeout : timeout, assertionName : &#39;waitForElementTop&#39;, description : &#39; &#39; + R.get(&#39;element&#39;) + &#39; &quot;&#39; + el.toString() + &#39;&quot; &#39; + R.get(&#39;toBeTopEl&#39;) }); }, <span id='Siesta-Test-Element-method-waitForElementNotTop'> /** </span> * Waits until the passed element is not the &#39;top&#39; 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(&#39;Siesta.Test.Element&#39;); 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 : &#39;waitForElementNotTop&#39;, description : &#39; &#39; + R.get(&#39;element&#39;) + &#39; &quot;&#39; + el.toString() + &#39;&quot; &#39; + R.get(&#39;toNotBeTopEl&#39;) }); }, <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 &amp;&amp; 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 &#39;top&#39; element at its position. By default, &quot;top&quot; 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 &quot;offset&quot; 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 &quot;x&quot; and &quot;y&quot; 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&#39;t behave like normal DOM elements. jQuery always consider them invisible. // Decide based on visibility of the parent SELECT node if (el &amp;&amp; el.nodeName.toLowerCase() === &#39;option&#39;) { el = this.$(el).closest(&#39;select&#39;)[0]; } var fromPointAPI = this.getQueryableContainer(el) var localPoint = this.getTargetCoordinate(el, true, offset) var foundEl = fromPointAPI.elementFromPoint(localPoint[ 0 ], localPoint[ 1 ]); return foundEl &amp;&amp; (foundEl === el || (allowChildren &amp;&amp; this.$(foundEl).closest(el).length &gt; 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] &gt;= 0 &amp;&amp; offset[0] &lt; w &amp;&amp; offset[1] &gt;= 0 &amp;&amp; offset[1] &lt; 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(&#39;Siesta.Test.Element&#39;); if (!foundEl) { this.fail(description, { assertionName : &#39;elementIsAt&#39;, got : { x: xy[0], y : xy[1] }, gotDesc : R.get(&#39;Position&#39;), annotation : R.get(&#39;noElementAtPosition&#39;) }); } else if (allowChildren) { if (foundEl === el || this.$(foundEl).closest(el).length &gt; 0) { this.pass(description, { descTpl : R.get(&#39;elementIsAtDescTpl&#39;), x : xy[ 0 ], y : xy[ 1 ]