UNPKG

siesta-lite

Version:

Stress-free JavaScript unit testing and functional testing tool, works in NodeJS and browsers

1,167 lines (954 loc) 74.1 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-More'>/** </span>@class Siesta.Test.More A mixin with additional generic assertion methods, which can work cross-platform between browsers and NodeJS. Is being consumed by {@link Siesta.Test}, so all of them are available in all tests. */ Role(&#39;Siesta.Test.More&#39;, { requires : [ &#39;isFailed&#39;, &#39;typeOf&#39;, &#39;on&#39; ], has : { autoCheckGlobals : false, expectedGlobals : Joose.I.Array, disableGlobalsCheck : false, browserGlobals : { init : [ &#39;console&#39;, &#39;getInterface&#39;, &#39;ExtBox1&#39;, &#39;__IE_DEVTOOLBAR_CONSOLE_COMMAND_LINE&#39;, /__BROWSERTOOLS/, // IE11 with console open &#39;seleniumAlert&#39;, &#39;onload&#39;, &#39;onerror&#39;, &#39;StartTest&#39;, &#39;startTest&#39;, &#39;__loaderInstrumentationHookInstalled__&#39;, &#39;describe&#39;, // will be reported in IE8 after overriding &#39;setTimeout&#39;, &#39;clearTimeout&#39;, &#39;requestAnimationFrame&#39;, &#39;cancelAnimationFrame&#39;, &#39;__coverage__&#39;, /cov_\w+/ ] }, <span id='Siesta-Test-More-cfg-waitForTimeout'> /** </span> * @cfg {Number} waitForTimeout Default timeout for `waitFor` (in milliseconds). Default value is 10000. */ waitForTimeout : 10000, waitForPollInterval : 100, suppressPassedWaitForAssertion : false }, methods : { <span id='Siesta-Test-More-method-isGreater'> /** </span> * This assertion passes, when the comparison of 1st with 2nd, using `&gt;` operator will return `true` and fails otherwise. * * @param {Number/Date} value1 The 1st value to compare * @param {Number/Date} value2 The 2nd value to compare * @param {String} [desc] The description of the assertion */ isGreater : function (value1, value2, desc) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); if (value1 &gt; value2) this.pass(desc, { descTpl : R.get(&#39;isGreaterPassTpl&#39;), value1 : value1, value2 : value2 }) else this.fail(desc, { assertionName : &#39;isGreater&#39;, got : value1, need : value2, needDesc : R.get(&#39;needGreaterThan&#39;) }) }, <span id='Siesta-Test-More-method-isLess'> /** </span> * This assertion passes, when the comparison of 1st with 2nd, using `&lt;` operator will return `true` and fails otherwise. * * @param {Number/Date} value1 The 1st value to compare * @param {Number/Date} value2 The 2nd value to compare * @param {String} [desc] The description of the assertion */ isLess : function (value1, value2, desc) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); if (value1 &lt; value2) this.pass(desc, { descTpl : R.get(&#39;isLessPassTpl&#39;), value1 : value1, value2 : value2 }) else this.fail(desc, { assertionName : &#39;isLess&#39;, got : value1, need : value2, needDesc : R.get(&#39;needLessThan&#39;) }) }, isGE : function () { this.isGreaterOrEqual.apply(this, arguments) }, <span id='Siesta-Test-More-method-isGreaterOrEqual'> /** </span> * This assertion passes, when the comparison of 1st with 2nd, using `&gt;=` operator will return `true` and fails otherwise. * * It has a synonym - `isGE`. * * @param {Number/Date} value1 The 1st value to compare * @param {Number/Date} value2 The 2nd value to compare * @param {String} [desc] The description of the assertion */ isGreaterOrEqual : function (value1, value2, desc) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); if (value1 &gt;= value2) this.pass(desc, { descTpl : R.get(&#39;isGreaterEqualPassTpl&#39;), value1 : value1, value2 : value2 }) else this.fail(desc, { assertionName : &#39;isGreaterOrEqual&#39;, got : value1, need : value2, needDesc : R.get(&#39;needGreaterEqualTo&#39;) }) }, isLE : function () { this.isLessOrEqual.apply(this, arguments) }, <span id='Siesta-Test-More-method-isLessOrEqual'> /** </span> * This assertion passes, when the comparison of 1st with 2nd, using `&lt;=` operator will return `true` and fails otherwise. * * It has a synonym - `isLE`. * * @param {Number/Date} value1 The 1st value to compare * @param {Number/Date} value2 The 2nd value to compare * @param {String} [desc] The description of the assertion */ isLessOrEqual : function (value1, value2, desc) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); if (value1 &lt;= value2) this.pass(desc, { descTpl : R.get(&#39;isLessEqualPassTpl&#39;), value1 : value1, value2 : value2 }) else this.fail(desc, { assertionName : &#39;isLessOrEqual&#39;, got : value1, need : value2, needDesc : R.get(&#39;needLessEqualTo&#39;) }) }, <span id='Siesta-Test-More-method-isApprox'> /** </span> * This assertion suppose to compare the numeric values. It passes when the passed values are approximately the same (the difference * is withing a threshold). A threshold can be provided explicitly (when assertion is called with 4 arguments), * or it will be set to 5% from the 1st value (when calling assertion with 3 arguments). * * @param {Number} value1 The 1st value to compare * @param {Number} value2 The 2nd value to compare * @param {Number} threshold The maximum allowed difference between values. This argument can be omitted. * @param {String} [desc] The description of the assertion */ isApprox : function (value1, value2, threshold, desc) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); if (arguments.length == 2) threshold = Math.abs(value1 * 0.05) if (arguments.length == 3) { if (this.typeOf(threshold) == &#39;String&#39;) { desc = threshold threshold = Math.abs(value1 * 0.05) } } // this function normalizes the fractional numbers to fixed point presentation // for example in JS: 1.05 - 1 = 0.050000000000000044 // so what we do is: (1.05 * 10^2 - 1 * 10^2) / 10^2 = (105 - 100) / 100 = 0.05 var subtract = function (value1, value2) { var fractionalLength = function (v) { var afterPointPart = (v + &#39;&#39;).split(&#39;.&#39;)[ 1 ] return afterPointPart &amp;&amp; afterPointPart.length || 0 } var maxLength = Math.max(fractionalLength(value1), fractionalLength(value2)) var k = Math.pow(10, maxLength); return (value1 * k - value2 * k) / k; }; if (Math.abs(subtract(value2, value1)) &lt;= threshold) this.pass(desc, { descTpl : R.get(&#39;isApproxToPassTpl&#39;), value1 : value1, value2 : value2, annotation : value2 == value1 ? R.get(&#39;exactMatch&#39;) : (R.get(&#39;withinThreshold&#39;) + &#39;: &#39; + threshold) }) else this.fail(desc, { assertionName : &#39;isApprox&#39;, got : value1, need : value2, needDesc : R.get(&#39;needApprox&#39;), annotation : R.get(&#39;thresholdIs&#39;) + &#39;: &#39; + threshold }) }, <span id='Siesta-Test-More-method-like'> /** </span> * This assertion passes when the passed `string` matches to a regular expression `regex`. When `regex` is a string, * assertion will check that it is a substring of `string` * * @param {String} string The string to check for &quot;likeness&quot; * @param {String/RegExp} regex The regex against which to test the string, can be also a plain string * @param {String} [desc] The description of the assertion */ like : function (string, regex, desc) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); if (this.typeOf(regex) == &quot;RegExp&quot;) if (string.match(regex)) this.pass(desc, { descTpl : R.get(&#39;stringMatchesRe&#39;), string : string, regex : regex }) else this.fail(desc, { assertionName : &#39;like&#39;, got : string, need : regex, needDesc : R.get(&#39;needStringMatching&#39;) }) else if (string.indexOf(regex) != -1) this.pass(desc, { descTpl : R.get(&#39;stringHasSubstring&#39;), string : string, regex : regex }) else this.fail(desc, { assertionName : &#39;like&#39;, got : string, need : regex, needDesc : R.get(&#39;needStringContaining&#39;) }) }, <span id='Siesta-Test-More-method-unlike'> /** </span> * This method is the opposite of &#39;like&#39;, it adds failed assertion, when the string matches the passed regex. * * @param {String} string The string to check for &quot;unlikeness&quot; * @param {String/RegExp} regex The regex against which to test the string, can be also a plain string * @param {String} [desc] The description of the assertion */ unlike : function(string, regex, desc) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); if (this.typeOf(regex) == &quot;RegExp&quot;) if (!string.match(regex)) this.pass(desc, { descTpl : R.get(&#39;stringNotMatchesRe&#39;), string : string, regex : regex }) else this.fail(desc, { assertionName : &#39;unlike&#39;, got : string, need : regex, needDesc : R.get(&#39;needStringNotMatching&#39;) }) else if (string.indexOf(regex) == -1) this.pass(desc, { descTpl : R.get(&#39;stringHasNoSubstring&#39;), string : string, regex : regex }) else this.fail(desc, { assertionName : &#39;unlike&#39;, got : string, need : regex, needDesc : R.get(&#39;needStringNotContaining&#39;) }) }, &quot;throws&quot; : function () { this.throwsOk.apply(this, arguments) }, throws_ok : function () { this.throwsOk.apply(this, arguments) }, <span id='Siesta-Test-More-method-throwsOk'> /** </span> * This assertion passes if the `func` function throws an exception during executing, and the * stringified exception passes the &#39;like&#39; assertion (with &#39;expected&#39; parameter). * * It has synonyms - `throws_ok` and `throws`. * * t.throwsOk(function(){ * throw &quot;oopsie&quot;; * }, &#39;oopsie&#39;, &#39;Some description text&#39;); * * See also {@link Siesta.Test#livesOk} method. * * @param {Function} func The function which should throw an exception * @param {String/RegExp} expected The regex against which to test the stringified exception, can be also a plain string * @param {String} [desc] The description of the assertion */ throwsOk : function (func, expected, desc) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); if (this.typeOf(func) != &#39;Function&#39;) throw new Error(R.get(&#39;throwsOkInvalid&#39;)) var e = this.getExceptionCatcher()(func) // assuming no one will throw undefined exception.. if (e === undefined) { this.fail(desc, { assertionName : &#39;throws_ok&#39;, annotation : R.get(&#39;didntThrow&#39;) }) return } if (e instanceof this.getTestErrorClass()) //IE uses non-standard &#39;description&#39; property for error msg e = e.message || e.description e = &#39;&#39; + e if (this.typeOf(expected) == &quot;RegExp&quot;) if (e.match(expected)) this.pass(desc, { descTpl : R.get(&#39;exMatchesRe&#39;), expected : expected }) else this.fail(desc, { assertionName : &#39;throws_ok&#39;, got : e, gotDesc : R.get(&#39;exceptionStringifiesTo&#39;), need : expected, needDesc : R.get(&#39;needStringMatching&#39;) }) else if (e.indexOf(expected) != -1) this.pass(desc, { descTpl : R.get(&#39;exContainsSubstring&#39;), expected : expected }) else this.fail(desc, { assertionName : &#39;throws_ok&#39;, got : e, gotDesc : R.get(&#39;exceptionStringifiesTo&#39;), need : expected, needDesc : R.get(&#39;needStringContaining&#39;) }) }, lives_ok : function () { this.livesOk.apply(this, arguments) }, lives : function () { this.livesOk.apply(this, arguments) }, <span id='Siesta-Test-More-method-livesOk'> /** </span> * This assertion passes, when the supplied `func` function doesn&#39;t throw an exception during execution. * * See also {@link Siesta.Test#throwsOk} method. * * This method has two synonyms: `lives_ok` and `lives` * * @param {Function} func The function which is not supposed to throw an exception * @param {String} [desc] The description of the assertion */ livesOk : function (func, desc) { if (this.typeOf(func) != &#39;Function&#39;) { func = [ desc, desc = func ][ 0 ] } var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); var e = this.getExceptionCatcher()(func) if (e === undefined) this.pass(desc, { descTpl : R.get(&#39;fnDoesntThrow&#39;) }) else this.fail(desc, { assertionName : &#39;lives_ok&#39;, annotation : R.get(&#39;fnThrew&#39;) + &#39;: &#39; + e }) }, isa_ok : function (value, className, desc) { this.isInstanceOf(value, className, desc) }, isaOk : function (value, className, desc) { this.isInstanceOf(value, className, desc) }, <span id='Siesta-Test-More-method-isInstanceOf'> /** </span> * This assertion passes, when the supplied `value` is the instance of the `className`. The check is performed with * `instanceof` operator. The `className` parameter can be supplied as class constructor or as string, representing the class * name. In the latter case the `class` will eval&#39;ed to receive the class constructor. * * This method has synonyms: `isaOk`, `isa_ok` * * @param {Mixed} value The value to check for &#39;isa&#39; relationship * @param {Class/String} className The class to check for &#39;isa&#39; relationship with `value` * @param {String} [desc] The description of the assertion */ isInstanceOf : function (value, className, desc) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); try { if (this.typeOf(className) == &#39;String&#39;) className = this.global.eval(className) } catch (e) { this.fail(desc, { assertionName : &#39;isa_ok&#39;, annotation : Siesta.Resource(&#39;Siesta.Test.Function&#39;, &#39;exceptionEvalutingClass&#39;) }) return } if (value instanceof className) this.pass(desc, { descTpl : R.get(&#39;isInstanceOfPass&#39;) }) else this.fail(desc, { assertionName : &#39;isa_ok&#39;, got : value, need : String(className), needDesc : R.get(&#39;needInstanceOf&#39;) }) }, <span id='Siesta-Test-More-method-isString'> /** </span> * This assertion passes, if supplied value is a String. * * @param {Mixed} value The value to check. * @param {String} [desc] The description of the assertion */ isString : function (value, desc) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); if (this.typeOf(value) == &#39;String&#39;) this.pass(desc, { descTpl : R.get(&#39;isAString&#39;), value : value }) else this.fail(desc, { got : value, need : R.get(&#39;aStringValue&#39;) }) }, <span id='Siesta-Test-More-method-isObject'> /** </span> * This assertion passes, if supplied value is an Object * * @param {Mixed} value The value to check. * @param {String} [desc] The description of the assertion */ isObject : function (value, desc) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); if (this.typeOf(value) == &#39;Object&#39;) this.pass(desc, { descTpl : R.get(&#39;isAnObject&#39;), value : value }) else this.fail(desc, { got : value, need : R.get(&#39;anObject&#39;) }) }, <span id='Siesta-Test-More-method-isArray'> /** </span> * This assertion passes, if supplied value is an Array * * @param {Mixed} value The value to check. * @param {String} [desc] The description of the assertion */ isArray : function (value, desc) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); if (this.typeOf(value) == &#39;Array&#39;) this.pass(desc, { descTpl : R.get(&#39;isAnArray&#39;), value : value }) else this.fail(desc, { got : value, need : R.get(&#39;anArrayValue&#39;) }) }, <span id='Siesta-Test-More-method-isNumber'> /** </span> * This assertion passes, if supplied value is a Number. * * @param {Mixed} value The value to check. * @param {String} [desc] The description of the assertion */ isNumber : function (value, desc) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); if (this.typeOf(value) == &#39;Number&#39;) this.pass(desc, { descTpl : R.get(&#39;isANumber&#39;), value : value }) else this.fail(desc, { got : value, need : R.get(&#39;aNumberValue&#39;) }) }, <span id='Siesta-Test-More-method-isBoolean'> /** </span> * This assertion passes, if supplied value is a Boolean. * * @param {Mixed} value The value to check. * @param {String} [desc] The description of the assertion */ isBoolean : function (value, desc) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); if (this.typeOf(value) == &#39;Boolean&#39;) this.pass(desc, { descTpl : R.get(&#39;isABoolean&#39;), value : value }) else this.fail(desc, { got : value, need : R.get(&#39;aBooleanValue&#39;) }) }, <span id='Siesta-Test-More-method-isDate'> /** </span> * This assertion passes, if supplied value is a Date. * * @param {Mixed} value The value to check. * @param {String} [desc] The description of the assertion */ isDate : function (value, desc) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); if (this.typeOf(value) == &#39;Date&#39;) this.pass(desc, { descTpl : R.get(&#39;isADate&#39;), value : value }) else this.fail(desc, { got : value, need : R.get(&#39;aDateValue&#39;) }) }, <span id='Siesta-Test-More-method-isRegExp'> /** </span> * This assertion passes, if supplied value is a RegExp. * * @param {Mixed} value The value to check. * @param {String} [desc] The description of the assertion */ isRegExp : function (value, desc) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); if (this.typeOf(value) == &#39;RegExp&#39;) this.pass(desc, { descTpl : R.get(&#39;isARe&#39;), value : value }) else this.fail(desc, { got : value, need : R.get(&#39;aReValue&#39;) }) }, <span id='Siesta-Test-More-method-isFunction'> /** </span> * This assertion passes, if supplied value is a Function. * * @param {Mixed} value The value to check. * @param {String} [desc] The description of the assertion */ isFunction : function (value, desc) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); if (this.typeOf(value) == &#39;Function&#39; || this.typeOf(value) == &#39;AsyncFunction&#39;) this.pass(desc, { descTpl : R.get(&#39;isAFunction&#39;), value : value }) else this.fail(desc, { got : value, need : R.get(&#39;aFunctionValue&#39;) }) }, is_deeply : function (obj1, obj2, desc) { this.isDeeply.apply(this, arguments) }, <span id='Siesta-Test-More-method-isDeeply'> /** </span> * This assertion passes when in-depth comparison of 1st and 2nd arguments (which are assumed to be JSON objects) shows that they are equal. * Comparison is performed with &#39;==&#39; operator, so `[ 1 ]` and `[ &quot;1&quot; ] objects will be equal. The objects should not contain cyclic references. * * This method works correctly with the *placeholders* generated with method {@link #any}. * * This method has a synonym: `is_deeply` * * @param {Object} obj1 The 1st object to compare * @param {Object} obj2 The 2nd object to compare * @param {String} [desc] The description of the assertion */ isDeeply : function (obj1, obj2, desc) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); var diff if (this.typeOf(obj1) === this.typeOf(obj2) &amp;&amp; this.compareObjects(obj1, obj2)) { this.pass(desc, { descTpl : R.get(&#39;isDeeplyPassTpl&#39;), obj1 : obj1, obj2 : obj2 }) } // DeepDiff Not supported in IE8 else if (typeof DeepDiff != &#39;undefined&#39; &amp;&amp; (diff = DeepDiff(obj1, obj2))) { if (diff.length &gt; 5) { this.diag(R.get(&#39;tooManyDifferences&#39;, { num : 5, total : diff.length})) } for (var i = 0; i &lt; Math.min(diff.length, 5); i++) { var diffItem = diff[i]; var path = (diffItem.path || []).join(&#39;.&#39;); var saw = path ? (path + &#39;: &#39; + diffItem.lhs) : obj1; var expected = path ? (path + &#39;: &#39; + diffItem.rhs) : obj2; this.fail(desc, { assertionName : &#39;isDeeply&#39;, got : saw, need : expected }) // Also log it to console for easy inspection window.console &amp;&amp; console.log(&#39;DIFF RESULT:&#39;, diffItem); } } else { this.fail(desc, { assertionName : &#39;isDeeply&#39;, got : obj1, need : obj2 }) } }, <span id='Siesta-Test-More-method-isDeeplyStrict'> /** </span> * This assertion passes when in-depth comparison of 1st and 2nd arguments (which are assumed to be JSON objects) shows that they are equal. * Comparison is performed with &#39;===&#39; operator, so `[ 1 ]` and `[ &quot;1&quot; ] objects will be different. The objects should not contain cyclic references. * * This method works correctly with the *placeholders* generated with method {@link #any}. * * @param {Object} obj1 The 1st object to compare * @param {Object} obj2 The 2nd object to compare * @param {String} [desc] The description of the assertion */ isDeeplyStrict : function (obj1, obj2, desc) { if (this.typeOf(obj1) === this.typeOf(obj2) &amp;&amp; this.compareObjects(obj1, obj2, true)) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); this.pass(desc, { descTpl : R.get(&#39;isDeeplyStrictPassTpl&#39;), obj1 : obj1, obj2 : obj2 }) } else this.fail(desc, { assertionName : &#39;isDeeplyStrict&#39;, got : obj1, need : obj2 }) }, expectGlobal : function () { this.expectGlobals.apply(this, arguments) }, <span id='Siesta-Test-More-method-expectGlobals'> /** </span> * This method accepts a variable number of names of expected properties in the global scope. When verifying the globals with {@link #verifyGlobals} * assertions, the expected gloabls will not be counted as failed assertions. * * This method has a synonym with singular name: `expectGlobal` * * @param {String/RegExp} name1 The name of global property or the regular expression to match several properties * @param {String/RegExp} name2 The name of global property or the regular expression to match several properties * @param {String/RegExp} nameN The name of global property or the regular expression to match several properties */ expectGlobals : function () { this.expectedGlobals.push.apply(this.expectedGlobals, arguments) }, isGlobalExpected : function (name, index) { var me = this if (!index || index &amp;&amp; !index.expectedStrings) { if (!index) index = {} Joose.O.extend(index, { expectedStrings : {}, expectedRegExps : [] }) Joose.A.each(this.expectedGlobals.concat(this.browserGlobals), function (value) { if (me.typeOf(value) == &#39;RegExp&#39;) index.expectedRegExps.push(value) else index.expectedStrings[ value ] = true }) } if (index.expectedStrings[ name ]) return true var imageWithIdCreatesGlobalEnumerable = Siesta.Project.Browser.FeatureSupport().supports.imageWithIdCreatesGlobalEnumerable; // remove after https://bugzilla.mozilla.org/show_bug.cgi?id=959992 will be fixed if (imageWithIdCreatesGlobalEnumerable) { var domEl = this.global.document.getElementById(name) if (domEl &amp;&amp; domEl.tagName.toLowerCase() == &#39;img&#39;) return true; } for (var i = 0; i &lt; index.expectedRegExps.length; i++) if (index.expectedRegExps[ i ].test(name)) return true return false }, forEachUnexpectedGlobal : function (func, scope) { scope = scope || this var index = {} for (var name in this.global) if (!this.isGlobalExpected(name, index)) { if (func.call(scope, name) === false) { break; } } }, <span id='Siesta-Test-More-method-verifyGlobals'> /** </span> * This method accepts a variable number of names of expected properties in the global scope and then performs a globals check. * * It will scan all globals properties in the scope of test and compare them with the list of expected globals. Expected globals can be provided with: * {@link #expectGlobals} method or {@link Siesta.Project#expectedGlobals expectedGlobals} configuration option of project. * * You can enable this assertion to automatically happen at the end of each test, using {@link Siesta.Project#autoCheckGlobals autoCheckGlobals} option of the project. * * @param {String/RegExp} name1 The name of global property or the regular expression to match several properties * @param {String/RegExp} name2 The name of global property or the regular expression to match several properties * @param {String/RegExp} nameN The name of global property or the regular expression to match several properties */ verifyGlobals : function () { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); if (this.disableGlobalsCheck) { this.diag(R.get(&#39;globalCheckNotSupported&#39;)); return } this.expectGlobals.apply(this, arguments) this.diag(R.get(&#39;globalVariables&#39;)) var failed = false var i = 0 this.forEachUnexpectedGlobal(function (name) { this.fail( R.get(&#39;globalFound&#39;), R.get(&#39;globalName&#39;) + &#39;: &#39; + name + &#39;, &#39; + R.get(&#39;value&#39;) + &#39;: &#39; + Siesta.Util.Serializer.stringify(this.global[ name ]) ) failed = true return i++ &lt; 50 // Only report first 50 globals to protect against legacy apps with thousands of globals }) if (!failed) this.pass(R.get(&#39;noGlobalsFound&#39;)) }, // will create a half-realized, &quot;phantom&quot;, &quot;isWaitFor&quot; assertion, which is only purposed // for user to get the instant feedback about &quot;waitFor&quot; actions // this assertion will be &quot;finalized&quot; and added to the test results in the &quot;finalizeWaiting&quot; startWaiting : function (description, sourceLine) { var result = new Siesta.Result.Assertion({ description : description, isWaitFor : true, sourceLine : sourceLine }); this.fireEvent(&#39;testupdate&#39;, this, result, this.getResults()) return result; }, finalizeWaiting : function (result, passed, desc, annotation, errback, suppressPassedWaitForAssertion) { // Treat this is an ordinary assertion from now on result.completed = true; if (passed) { if (this.suppressPassedWaitForAssertion || suppressPassedWaitForAssertion) { // Make sure UI is updated and the &quot;noise&quot; is removed this.fireEvent(&#39;assertiondiscard&#39;, this, result) } else { this.pass(desc, annotation, result) } } else { this.fail(desc, annotation, result); errback &amp;&amp; errback() } }, conditionCheckerToString : function (checker) { if (this.typeOf(checker) !== &#39;Function&#39;) return &#39;&#39; var sources = checker.toString().split(&#39;\n&#39;) var minCommonLeadingWhitespace = Infinity Joose.A.each(sources, function (line, index) { // ignore first line, which won&#39;t have the common leading whitespace if (index === 0) return var leadingWhitespaceMatch = /^(\s*)/.exec(line) if (leadingWhitespaceMatch) { var leadingWhitespace = leadingWhitespaceMatch[ 1 ] // ignore whitespace-only lines if (leadingWhitespace === line) return if (leadingWhitespace.length &lt; minCommonLeadingWhitespace) minCommonLeadingWhitespace = leadingWhitespace.length } }) if (minCommonLeadingWhitespace &lt; Infinity) Joose.A.each(sources, function (line, index) { // ignore first line, which won&#39;t have the common leading whitespace if (index === 0) return sources[ index ] = line.slice(minCommonLeadingWhitespace) }) return &#39;[code]&#39; + sources.join(&#39;\n&#39;) + &#39;[/code]&#39; }, <span id='Siesta-Test-More-method-waitFor'> /** </span> * Waits for passed checker method to return true (or any non-false value, like for example DOM element or array), and calls the callback when this happens. * As an additional feature, the callback will receive the result from the checker method as the 1st argument. * t.waitFor( function () { return document.getElementById(&#39;someEl&#39;) }, function (el) { // waited for element #someEl to appear // element will be available in the callback as 1st argument &quot;el&quot; } ) * You can also call this method with a single Object having the following properties: `method`, `callback`, `scope`, `timeout`, `interval`, `description`: t.waitFor({ method : function () { return document.getElementById(&#39;someEl&#39;) }, callback : function (el) { // waited for element #someEl to appear // element will be available in the callback as 1st argument &quot;el&quot; } }) * * @param {Function/Number/Object} condition Either a function which should return true (or any other &quot;truthy&quot; value) when a certain condition has been fulfilled, * or a number of ms to wait before calling the callback. Can be also an object with the following properties: * @param {Function} condition.callback A function to call when the condition has been met. Will receive a result from checker function. * @param {Function} condition.method A condition checker function. * @param {Object} condition.scope The scope for the callback. * @param {Number} condition.timeout The maximum amount of time (in milliseconds) to wait for the condition to be fulfilled. * @param {Number} condition.interval The polling interval (in milliseconds) * @param {String} condition.description The assertion description * * @param {Function} callback A function to call when the condition has been met. Will receive a result from checker function. * @param {Object} scope The scope for the callback * @param {Int} timeout The maximum amount of time (in milliseconds) to wait for the condition to be fulfilled. * Defaults to the {@link Siesta.Test.ExtJS#waitForTimeout} value. If condition is not fullfilled within this time, a failed assertion will be added to the test. * @param {Int} [interval=100] The polling interval (in milliseconds) * * @return {Promise} A promise which will be resolved when wait completes (either successfully or by timeout). In case of successfull resolution * promise will be resolved to the result from the checker function. Additionally it has a `force` property as noted below. * @return {Function} return.force A function, that will force this wait operation to immediately complete (and call the callback). * No call to checker will be performed and callback will not receive a result from it. */ waitFor : function (method, callback, scope, timeout, interval) { var R = Siesta.Resource(&#39;Siesta.Test.More&#39;); var description = &#39; &#39; + R.get(&#39;conditionToBeFulfilled&#39;); var assertionName = &#39;waitFor&#39;; var me = this; var sourceLine = me.getSourceLine(); var originalSetTimeout = me.originalSetTimeout; var originalClearTimeout = me.originalClearTimeout; var errback; var suppressAssertion; if (arguments.length === 1 &amp;&amp; this.typeOf(method) == &#39;Object&#39;) { var options = method; method = options.method; callback = options.callback; scope = options.scope; timeout = options.timeout; interval = options.interval description = options.description || description; assertionName = options.assertionName || assertionName; suppressAssertion = options.suppressAssertion; // errback is called in case &quot;waitFor&quot; has failed errback = options.errback } else options = {} var isWaitingForTime = this.typeOf(method) == &#39;Number&#39; callback = callback || function () {} description = isWaitingForTime ? (method + &#39; &#39; + R.get(&#39;ms&#39;)) : description; var pollTimeout // early notification about the started &quot;waitFor&quot; operation var waitAssertion = me.startWaiting(R.get(&#39;waitingFor&#39;) + &#39; &#39; + description, sourceLine); interval = interval || this.waitForPollInterval timeout = timeout || this.waitForTimeout var resolve var res = new Promise(function (resolution) { resolve = resolution }) // this async frame is not supposed to fail, because it&#39;s delayed to `timeout + 3 * interval` // failure supposed to be generated in the &quot;pollFunc&quot; and this async frame to be closed // however, in IE the async frame may end earlier than failure from &quot;pollFunc&quot; // in such case we report the same error as in &quot;pollFunc&quot; var async = this.beginAsync((isWaitingForTime ? method : timeout) + 3 * interval, function () { isDone = true originalClearTimeout(pollTimeout) me.finalizeWaiting(waitAssertion, false, R.get(&#39;waitedTooLong&#39;) + &#39;: &#39; + description, { assertionName : assertionName, annotation : me.typeOf(options.annotation) === &#39;Function&#39; ? options.annotation() : R.get(&#39;conditionNotFulfilled&#39;) + &#39; &#39; + timeout + R.get(&#39;ms&#39;) + &#39;. \n\n&#39; + me.conditionCheckerToString(method) }, errback, suppressAssertion) resolve() return true }) var isDone = false var beforeFinalizeListener // stop polling, if this test instance has finalized (probably because of exception) this.on(&#39;beforetestfinalize&#39;, beforeFinalizeListener = function () { if (!isDone) { isDone = true me.finalizeWaiting(waitAssertion, false, R.get(&#39;waitingAborted&#39;), null, null, suppressAssertion); me.endAsync(async) originalClearTimeout(pollTimeout) } }, null, { single : true }) if (isWaitingForTime) { if (method &lt; 0) { throw new Error(&#39;Cannot wait for a negative amount of time&#39;); } pollTimeout = originalSetTimeout(function() { isDone = true me.un(&#39;beforetestfinalize&#39;, beforeFinalizeListener) me.finalizeWaiting(waitAssertion, true, R.get(&#39;Waited&#39;) + &#39; &#39; + method + &#39; &#39; + R.get(&#39;ms&#39;), null, null, suppressAssertion || method === 0); me.endAsync(async); me.processCallbackFromTest(callback, [], scope || me) resolve() }, method); } else { var result; var startDate = new Date() var pollFunc = function () { var time = new Date() - startDate; if (time &gt; timeout) { me.endAsync(async); isDone = true try { me.un(&#39;beforetestfinalize&#39;, beforeFinalizeListener) me.finalizeWaiting(waitAssertion, false, R.get(&#39;waitedTooLong&#39;) + &#39;: &#39; + description, { assertionName : assertionName, annotation : me.typeOf(options.annotation) === &#39;Function&#39; ? options.annotation() : R.get(&#39;conditionNotFulfilled&#39;) + &#39; &#39; + timeout + R.get(&#39;ms&#39;) + &#39;. \n\n&#39; + me.conditionCheckerToString(method) }, errback, suppressAssertion) } catch (e) { if (!/__SIESTA_TEST_EXIT_EXCEPTION__/.test(String(e))) throw e } resolve() return } try { result = method.call(scope || me); } catch (e) { me.endAsync(async); try { me.un(&#39;beforetestfinalize&#39;, beforeFinalizeListener) me.finalizeWaiting(waitAssertion, false, assertionName + &#39; &#39; + R.get(&#39;checkerException&#39;), { assertionName : assertionName, annotation : me.stringifyException(e) }, errback, suppressAssertion) } catch (e) { if (!/__SIESTA_TEST_EXIT_EXCEPTION__/.test(String(e))) throw e } isDone = true resolve() return } if (result != null &amp;&amp; result !== false) { me.endAsync(async); isDone = true me.un(&#39;beforetestfinalize&#39;, beforeFinalizeListener) me.finalizeWaiting( waitAssertion, true, R.get(&#39;Waited&#39;) + &#39; &#39; + time + &#39; &#39; + R.get(&#39;msFor&#39;) + &#39; &#39; + description, me.typeOf(options.annotation) === &#39;Function&#39; ? options.annotation() : null, null, // always add assertion (set &quot;suppress&quot; to false), if user has provided description