UNPKG

siesta-lite

Version:

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

1,020 lines (807 loc) 44 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-ExtJSCore'>/** </span> @class Siesta.Test.ExtJSCore A base mixin for testing Ext JS and Sencha Touch applications. Contains testing functionality that is common for both frameworks. This file is a reference only, for a getting start guide and manual, please refer to &lt;a href=&quot;#!/guide/getting_started_browser&quot;&gt;Siesta getting started in browser environment&lt;/a&gt; guide. */ Role(&#39;Siesta.Test.ExtJSCore&#39;, { does : Siesta.Util.Role.CanInstallCQRootPseudo, has : { waitForExtReady : true, waitForAppReady : false, waitForExtComponentQueryReady : true, loaderPath : null, requires : null, isExtOnReadyDone : false, onReadyWaitingStarted : false, isAppReadyDone : false, loaderWaitingStarted : false, requiringWaitingStarted : false, isRequiringDone : false, modelsDefinedInPreload : Joose.I.Object }, override : { onTestStart : function () { var me = this var sharedSandboxState = this.sharedSandboxState // `!this.reusingSandbox` - is true only for the 1st test in the &quot;shared sandbox&quot; group if (!this.reusingSandbox &amp;&amp; sharedSandboxState) { if (!sharedSandboxState.modelsDefinedInPreload) sharedSandboxState.modelsDefinedInPreload = {} this.forEachModelInAllSchemas(function (entity, entityName, className, schema) { sharedSandboxState.modelsDefinedInPreload[ className ] = true }) } }, // only called for the re-used contexts cleanupContextBeforeStartDom : function () { var Ext = this.getExt() if (!Ext) return this.SUPER() var me = this // if component query is present - try to unregister all components if (Ext.ComponentQuery) { var keep = {} var msgBox = Ext.MessageBox if (msgBox) { keep[ msgBox.id ] = true } // retrieve the top-level components var comps = Ext.ComponentQuery.query(&#39;{ownerCt == null}&#39;) // sort, so that containers goes first // the logic is, that containers have &quot;more logic&quot; and may affect components // use case - grid editing is active and the editor field is destroyed before the grid - // that throws exception in gantt code comps.sort(function (a, b) { a = (a instanceof Ext.Container) ? 0 : 1 b = (b instanceof Ext.Container) ? 0 : 1 return a - b }) Joose.A.each(comps, function (comp) { if (!keep[ comp.id ] &amp;&amp; !comp.isDestroyed) comp.destroy() }) } // if there&#39;s a class manager - unregister &quot;unexpected&quot; classes if (Ext.ClassManager &amp;&amp; Ext.undefine) { var index = {} Joose.O.each(Ext.ClassManager.classes, function (cls, name) { var global = name.split(&#39;.&#39;)[ 0 ] if (!me.isGlobalExpected(global, index)) Ext.undefine(name) }) } // if there&#39;s a store manager - also unregister stores (all stores except internal ext js store(s)) if (Ext.data &amp;&amp; Ext.data.StoreManager) { var toRemove = []; Ext.data.StoreManager.each(function(store) { if (store.storeId !== &quot;ext-empty-store&quot;) toRemove.push(store); }); Ext.data.StoreManager.unregister.apply(Ext.data.StoreManager, toRemove); } var sharedSandboxState = this.sharedSandboxState var modelsDefinedInPreload = sharedSandboxState &amp;&amp; sharedSandboxState.modelsDefinedInPreload modelsDefinedInPreload &amp;&amp; me.forEachModelInAllSchemas(function (entity, entityName, className, schema) { if (!modelsDefinedInPreload[ className ]) { Ext.undefine(className) // TODO also need to remove the associations delete schema.entityClasses[ className ] delete schema.entities[ entityName ] } }) Ext.resumeLayouts &amp;&amp; Ext.resumeLayouts() }, processSubTestConfig : function (config) { var res = this.SUPER(config) // sub tests should not wait for Ext.onReady or for application launch res.waitForAppReady = false res.waitForExtReady = false return res }, isReady : function () { var result = this.SUPERARG(arguments); if (!result.ready) return result; var me = this var Ext = this.getExt(); var R = Siesta.Resource(&#39;Siesta.Test.ExtJSCore&#39;); var loaderPath = this.loaderPath var StartTest = this.global.StartTest if (loaderPath &amp;&amp; Ext &amp;&amp; Ext.Loader &amp;&amp; !StartTest.loaderPathHookInstalled) { this.project.generateLoaderPathHook()(StartTest, Ext, loaderPath) } var requires = this.requires if (requires &amp;&amp; !this.requiringWaitingStarted &amp;&amp; Ext &amp;&amp; Ext.require) { this.requiringWaitingStarted = true Ext.require(requires, function () { me.isRequiringDone = true }) } // in microloaded apps, Ext.onReady may appear with some arbitrary delay if (this.waitForExtReady &amp;&amp; !this.onReadyWaitingStarted &amp;&amp; Ext &amp;&amp; Ext.onReady) { this.onReadyWaitingStarted = true Ext.onReady(function () { me.isExtOnReadyDone = true }) } // Sencha Touch has a weird intermediate state, where Ext object is already on the page, but it misses // almost every property people are used to, like &quot;ComponentQuery&quot;, &quot;onReady&quot; etc // detecting such state with &quot;Ext.blink&quot; property if ( this.waitForExtComponentQueryReady &amp;&amp; Ext // this indicates Ext&gt;=4, Ext3 does not have &quot;ComponentQuery&quot; concept &amp;&amp; (Ext.getVersion || Ext.blink || Ext.manifest || Ext.microloaded) &amp;&amp; !Ext.ComponentQuery ) return { ready : false, reason : R.get(&#39;waitedForComponentQuery&#39;) } if (requires &amp;&amp; !this.isRequiringDone) return { ready : false, reason : R.get(&#39;waitedForRequires&#39;) } if (this.waitForExtReady &amp;&amp; this.onReadyWaitingStarted &amp;&amp; !this.isExtOnReadyDone) return { ready : false, reason : R.get(&#39;waitedForExt&#39;) } if (this.waitForAppReady &amp;&amp; !this.isAppReadyDone &amp;&amp; Ext) { var name = Ext.manifest.name var isAppReadyDone = false try { isAppReadyDone = me.global[ name ].getApplication().launched } catch (e) { } if (isAppReadyDone) this.isAppReadyDone = isAppReadyDone else return { ready : false, reason : R.get(&#39;waitedForApp&#39;) } } if (Ext &amp;&amp; Ext.ComponentQuery) { // add :root pseudo CQ selector to be able to identify &#39;root&#39; level components that don&#39;t have // parent containers. value is 1-based this.installRootPseudoCQ(Ext) } return { ready : true } } }, methods : { initialize : function() { // Since this test is preloading Ext JS, we should let Siesta know what to &#39;expect&#39; this.expectGlobals(&#39;Ext&#39;, &#39;id&#39;); this.SUPER(); }, getSimulatorClass : function () { return Siesta.Test.SimulatorExtJS }, forEachModelInAllSchemas : function (func) { var Ext = this.getExt() if (Ext &amp;&amp; Ext.data &amp;&amp; Ext.data.schema &amp;&amp; Ext.data.schema.Schema &amp;&amp; Ext.undefine) { Joose.O.each(Ext.data.schema.Schema.instances, function (schema, name) { schema.eachEntity(function (entityName) { var entity = schema.getEntity(entityName) func(entity, entityName, entity.$className, schema) }) }) } }, doStart : function () { var me = this; var Ext = this.getExt(); if (!Ext) { // proceed to parent implementation disabling our &quot;can start&quot; checkers this.waitForAppReady = false this.waitForExtReady = false this.requires = null this.SUPERARG(arguments) return } // PROBABLY NOT NEEDED AS OF EXT5 // this flag will explain to Ext, that DOM ready event has already happened // Ext fails to set this flag if it was loaded dynamically, already after DOM ready // the test will start only after DOM ready anyway, so we just set this flag Ext.isReady = true if (!Ext.manifest || !Ext.manifest.name) this.waitForAppReady = false this.SUPERARG(arguments) }, <span id='Siesta-Test-ExtJSCore-method-getExt'> /** </span> * This method returns the `Ext` object from the scope of the test. When creating your own assertions for Ext JS code, you need * to make sure you are using this method to get the `Ext` instance. Otherwise, you&#39;ll be using the same &quot;top-level&quot; `Ext` * instance, used by the project for its UI. * * For example: * * elementHasProvidedCssClass : function (el, cls, desc) { * var Ext = this.getExt(); * * if (Ext.fly(el).hasCls(cls)) { * this.pass(desc); * } else { * this.fail(desc); * } * } * * @return {Object} The `Ext` object from the scope of test */ getExt : function (global) { return (global || this.global).Ext }, <span id='Siesta-Test-ExtJSCore-method-Ext'> /** </span> * The alias for {@link #getExt} * @method */ Ext : function () { return this.global.Ext }, isExtJSComponentQueryTarget : function (obj) { return obj.isComponent || obj.isWidget; }, <span id='Siesta-Test-ExtJSCore-method-componentQuery'> /** </span> * This method performs an ExtJS component query. The selector may start with `&gt;&gt;` which will be * trimmed. * * @param {String} selector A component query selector. The leading &#39;&gt;&gt;&#39; will be trimmed. * @param {Ext.Component} root A root for the component query. * @param {Object} options * @param {Boolean} options.ignoreNonVisible Whether to remove the hidden components from the results * * @return {Array[Ext.Component]} Array of matching components */ componentQuery : function (selector, root, options) { options = options || {} root = root || this.Ext() &amp;&amp; this.Ext().ComponentQuery if (!selector || !root || !root.query) return [] // strip out leading &gt;&gt; which is used as indicator of the ComponentQuery in ActionTarget string selector = selector.replace(/^(\s*&gt;&gt;)?/, &#39;&#39;).trim() var result = root.query(selector); if (options.ignoreNonVisible) { var me = this var onlyVisible = [] Joose.A.each(result, function (cmp) { // Sencha Touch components have no &quot;isVisible()&quot; method // we use `componentIsHidden` here which peforms just the &quot;hierarchical&quot; check (does not use &quot;elementIsTop&quot;) if (!me.componentIsHidden(cmp)) onlyVisible.push(cmp) }); result = onlyVisible } return result }, // Accepts Ext.Component or ComponentQuery normalizeComponent : function (component, allowEmpty, options, root) { options = options || {} var Ext = root || this.Ext() var me = this var matchingMultiple = false if (this.typeOf(component) === &#39;String&#39;) { var result = this.componentQuery(component, root, { ignoreNonVisible : options.ignoreNonVisible }) var R = Siesta.Resource(&#39;Siesta.Test.ExtJSCore&#39;); if (!allowEmpty &amp;&amp; result.length &lt; 1) this.warn(R.get(&#39;noComponentMatch&#39;).replace(&#39;{component}&#39;, component)); if (result.length &gt; 1) { matchingMultiple = true var text = R.get(&#39;multipleComponentMatch&#39;).replace(&#39;{component}&#39;, component); if (this.project.failOnMultipleComponentMatches) { this.fail(text); } else { this.warn(text); } } component = result[ 0 ]; } return options.detailed ? { comp : component, matchingMultiple : matchingMultiple } : component }, <span id='Siesta-Test-ExtJSCore-method-compToEl'> /** </span> * @private * * @param {Ext.Component} comp the Ext.Component * @param {Boolean} locateInputEl For form fields, try to find the inner input element by default. * If you want to target the containing Component element, pass false instead. * * @return {Ext.dom.Element} */ compToEl : function (comp, locateInputEl) { if (!comp) return null var Ext = this.Ext(); locateInputEl = locateInputEl !== false; // Handle editors, deal with the field directly if (Ext.Editor &amp;&amp; comp instanceof Ext.Editor &amp;&amp; comp.field) { comp = comp.field; } // Ext JS if (Ext &amp;&amp; Ext.form &amp;&amp; Ext.form.Field &amp;&amp; locateInputEl) { // Deal with bizarre markup in Ext 5.1.2+ if ( (Ext.form.Checkbox &amp;&amp; comp instanceof Ext.form.Checkbox || Ext.form.Radio &amp;&amp; comp instanceof Ext.form.Radio) &amp;&amp; comp.el ) { var displayEl = comp.displayEl; if (displayEl &amp;&amp; comp.boxLabel) { return displayEl; } var inputComponent = Ext.ComponentQuery.query(&#39;checkboxinput&#39;, comp)[ 0 ] if (inputComponent) return this.compToEl(inputComponent) // Ext6 Modern Ext6.7 Fallback return comp.el.down(&#39;.x-form-field&#39;) || comp.el.down(&#39;.x-field-input&#39;) || comp.el.down(&#39;.x-input-el&#39;) || comp.inputEl || comp.el; } if (comp instanceof Ext.form.Field &amp;&amp; comp.inputEl) { var field = comp.el.down(&#39;.x-form-field&#39;); return (field &amp;&amp; field.dom) ? field : comp.inputEl; } if (Ext.form.HtmlEditor &amp;&amp; comp instanceof Ext.form.HtmlEditor) { // Ext JS 3 Ext JS 4 return comp.iframe || comp.inputEl; } } if (Ext &amp;&amp; Ext.field &amp;&amp; Ext.field.Slider &amp;&amp; (comp instanceof Ext.field.Slider)) { return this.compToEl(Ext.ComponentQuery.query(&#39;slider&#39;, comp)[ 0 ]) } // Sencha Touch: Form fields can have a child input component if (Ext &amp;&amp; Ext.field &amp;&amp; Ext.field.Field &amp;&amp; comp instanceof Ext.field.Field &amp;&amp; locateInputEl &amp;&amp; comp.getComponent) { comp = comp.getComponent(); // some of the SenchaTouch fields uses &quot;masks&quot; - another DOM element, which is applied // on top of the field when it does not have focus // some of them have mask always (&quot;useMask === true&quot;), for such fields return mask element // as its the primary point of user interaction if (comp.getUseMask &amp;&amp; comp.getUseMask() === true &amp;&amp; comp.mask) return comp.mask if (locateInputEl &amp;&amp; comp.input) return comp.input if (comp.bodyElement) return comp.bodyElement } // Ext JS vs Sencha Touch return comp.getEl &amp;&amp; !comp.element ? comp.getEl() : locateInputEl &amp;&amp; comp.input || comp.el || comp.element; }, <span id='Siesta-Test-ExtJSCore-method-query'> /** </span> * This method resolves a query string, as defined by the {@link Siesta.Test.ActionTarget}. See the link for details, * here we&#39;ll just briefly mention, that by default string supposed to be a CSS query. If it starts with `&gt;&gt;` * it will be recognized as Component query. And if it contains the `=&gt;` characters, then it will be * considered a {@link compositeQuery compositeQuery}. * * ``` * await t.click(&#39;&gt;&gt;button&#39;); * await t.click(&#39;mypanel =&gt; .dataview .item1&#39;); * ``` * * You can also target Ext JS components in nested contexts like iframes: * * ``` * await t.click(&#39;.iframe1 -&gt; .iframe2 -&gt; &gt;&gt;button&#39;); * await t.click(&#39;.iframe1 -&gt; .iframe2 -&gt; myPanel =&gt; button&#39;); * ``` * * A few extra CSS pseudo selectors are also supported: `:contains()` and `:textEquals()` which makes it * possible to query elements by their exact (textEquals) or partial (contains) textual content. * * ``` * await t.click(&#39;.iframe1 -&gt; iframe2 -&gt; button:contains(Save)&#39;); * ``` * * @param {String} selector A Siesta ActionTarget selector * @param {Object} [root] The root element for the query (or shadow root) * @return {Array[Element]} */ query : function (selector, root) { var me = this; // Handle potential nested iframes root = root || this.getNestedRoot(selector); selector = selector.split(&#39;-&gt;&#39;).pop().trim(); if (selector.match(/=&gt;/)) { var rootExt = this.getExt(this.getGlobal(root)); return rootExt ? this.compositeQuery(selector, rootExt.ComponentQuery) : []; } else if (selector.match(/\s*&gt;&gt;/)) { var rootExt = this.getExt(this.getGlobal(root)); // Component query var result = rootExt ? me.componentQuery(selector, rootExt.ComponentQuery, { ignoreNonVisible : false }) : [] var elements = [] Joose.A.each(result, function (cmp) { var el = me.compToEl(cmp) if (el &amp;&amp; el.dom) elements.push(el.dom) }) return elements } return me.SUPERARG([selector, root]); }, // Accept Ext.Element and Ext.Component // If the &#39;shallow&#39; flag is true we should not &#39;reevaluate&#39; the target element - stop at the component element. normalizeElement : function(el, allowMissing, shallow, detailed, options) { if (!el) return null if (el.nodeName) return el; var matchingMultiple = false var queryResult var origEl = el; //var offset = options &amp;&amp; options.offset var stopAtComponentLevel = options &amp;&amp; options.stopAtComponentLevel var ignoreNonVisible = options &amp;&amp; options.hasOwnProperty(&#39;ignoreNonVisible&#39;) ? options.ignoreNonVisible : true if (typeof el === &#39;string&#39;) { var root = this.getNestedRoot(el), global = root &amp;&amp; this.getGlobal(root); el = el.split(&#39;-&gt;&#39;).pop().trim(); // A nested frame might not yet exist, or be ready if (!root || (el.match(/^\s*&gt;&gt;|=&gt;/) &amp;&amp; !this.getExt(global))) { return null; } if (el.match(/=&gt;/)) { var Ext = this.getExt(global); // Composite query queryResult = this.compositeQuery(el, Ext.ComponentQuery, allowMissing, ignoreNonVisible) el = queryResult[ 0 ] matchingMultiple = queryResult.length &gt; 1 } else if (el.match(/^\s*&gt;&gt;/)) { var Ext = this.getExt(global); var compRes = this.normalizeComponent(el, allowMissing, { ignoreNonVisible : ignoreNonVisible, detailed : true }, Ext.ComponentQuery) el = compRes.comp matchingMultiple = compRes.matchingMultiple } else { // string in unknown format, guessing it&#39;s a DOM query return this.SUPER(origEl, allowMissing, shallow, detailed); } if (!allowMissing &amp;&amp; !el) { var R = Siesta.Resource(&#39;Siesta.Test.ExtJSCore&#39;); var warning = R.get(&#39;noComponentFound&#39;) + &#39;: &#39; + origEl; this.warn(warning); throw warning; } } var rawResult = false if (el &amp;&amp; this.isExtJSComponentQueryTarget(el)) if (stopAtComponentLevel) rawResult = true else { el = this.compToEl(el); } // ExtJS Element if (el &amp;&amp; el.dom) if (stopAtComponentLevel) rawResult = true else el = el.dom // will also handle the case of conversion of array with coordinates to el var res = rawResult ? el : this.SUPER(el, allowMissing, shallow); return detailed ? { el : res, matchingMultiple : matchingMultiple } : res }, // this method generally has the same semantic as the &quot;normalizeElement&quot;, but resolved // till the component level only.. Which we prefer for the &quot;firesOk&quot; assertions family. // It&#39;s also being used in Siesta.Test.Action.Role.HasTarget to determine what to pass to the next step // from the previous step, which has been specified with the Siesta.Test.ActionTarget descriptor // // on the browser level the only possibility is DOM element // but on ExtJS level user can also use ComponentQuery and next step need to receive the // component instance normalizeActionTarget : function (el, allowMissing, ignoreNonVisible) { return this.normalizeElement( el, allowMissing, false, // shallow false, // detailed { ignoreNonVisible : ignoreNonVisible !== false, stopAtComponentLevel : true } // options ); }, <span id='Siesta-Test-ExtJSCore-method-knownBugIn'> /** </span> * This method allow assertions to fail silently for tests executed in versions of Ext JS up to a certain release. When you try to run this test on a newer * version of Ext JS and it fails, it will fail properly and force you to re-investigate. If it passes in the newer version, you should remove the * use of this method. * * See also {@link Siesta.Test#todo} * * @param {String} frameworkVersion The Ext JS framework version, e.g. &#39;4.0.7&#39; * @param {Function} fn The method covering the broken functionality * @param {String} reason The reason or explanation of the bug */ knownBugIn : function(frameworkVersion, fn, reason) { var Ext = this.getExt(); var version = Ext.versions.extjs || Ext.versions.touch; var R = Siesta.Resource(&#39;Siesta.Test.ExtJSCore&#39;); if (this.project.failKnownBugIn || version.isGreaterThan(frameworkVersion)) { fn.call(this.global, this); } else { this.todo(R.get(&#39;knownBugIn&#39;) + &#39; &#39; + frameworkVersion + &#39;: &#39; + (reason || &#39;&#39;), fn); } }, <span id='Siesta-Test-ExtJSCore-method-requireOk'> /** </span> * This method will load the specified classes with `Ext.require()` and call the provided callback. Additionally it will check that all classes have been loaded. * * This method accepts either variable number of arguments: * * t.requireOk(&#39;Some.Class1&#39;, &#39;Some.Class2&#39;, function () { ... }) * or array of class names: * * t.requireOk([ &#39;Some.Class1&#39;, &#39;Some.Class2&#39; ], function () { ... }) * * @param {String} className1 The name of the class to `require` * @param {String} className2 The name of the class to `require` * @param {String} classNameN The name of the class to `require` * @param {Function} fn The callback. Will be called even if the loading of some classes have failed. */ requireOk : function () { var me = this var global = this.global var Ext = this.getExt() var args = Array.prototype.concat.apply([], arguments) var R = Siesta.Resource(&#39;Siesta.Test.ExtJSCore&#39;); var callback if (this.typeOf(args[ args.length - 1 ]) == &#39;Function&#39;) callback = args.pop() // what to do when loading completed or timed-out var continuation = function () { me.endAsync(async) Joose.A.each(args, function (className) { var clsManager = Ext.ClassManager var cls = clsManager.get(className) <span id='Siesta-Test-ExtJSCore-property-'> /** </span> * Checks if the class being required is an override, which is not available * via Ext.ClassManager.get(). Only available in ExtJS 5+. * * See: https://www.assembla.com/spaces/bryntum/tickets/2201 */ var isOverride = clsManager.overrideMap &amp;&amp; clsManager.overrideMap[ className ] // override normal class singleton if (isOverride || cls &amp;&amp; (me.typeOf(cls) == &#39;Function&#39; || me.typeOf(cls.self) == &#39;Function&#39;)) me.pass(R.get(&#39;Class&#39;) + &quot;: &quot; + className + &quot; &quot; + R.get(&#39;wasLoaded&#39;)) else me.fail(R.get(&#39;Class&#39;) + &quot;: &quot; + className + &quot; &quot; + R.get(&#39;wasNotLoaded&#39;)) }) me.processCallbackFromTest(callback) } var timeout = this.defaultTimeout, async = this.beginAsync(timeout + 100) var hasTimedOut = false var originalSetTimeout = this.originalSetTimeout var originalClearTimeout = this.originalClearTimeout var timeoutId = originalSetTimeout(function () { hasTimedOut = true continuation() }, timeout) Ext.Loader.setConfig({ enabled : true }); Ext.require(args, function () { originalClearTimeout(timeoutId) if (!hasTimedOut) continuation() }) }, <span id='Siesta-Test-ExtJSCore-method-clickComponentQuery'> /** </span> * This method is a simple wrapper around the {@link #chainClick} - it performs a component query for provided `selector` starting from the `root` container * and then clicks on all found components, in order: * // click all buttons in the `panel` t.clickComponentQuery(&#39;button&#39;, panel, function () {}) * * The 2nd argument for this method can be omitted and method can be called with 2 arguments only. In this case a global component query will be performed: * // click all buttons in the application t.clickComponentQuery(&#39;button&#39;, function () {}) * * @param {String} selector The selector to perform a component query with * @param {Ext.Container} root The optional root container to start a query from. * @param {Function} callback The callback to call, after clicking all the found components */ clickComponentQuery : function (selector, root, callback) { if (arguments.length == 2 &amp;&amp; this.typeOf(arguments[ 1 ]) == &#39;Function&#39;) { callback = root root = this.Ext().ComponentQuery } if (arguments.length == 1) { root = this.Ext().ComponentQuery } var result = root.query(selector) this.chainClick(result, function () { callback &amp;&amp; callback.call(this, result) }) }, <span id='Siesta-Test-ExtJSCore-method-clickCQ'> /** </span> * An alias for {@link #clickComponentQuery}. * * @param {String} selector The selector to perform a component query with * @param {Ext.Container} root The optional root container to start a query from. * @param {Function} callback The callback to call, after clicking all the found components */ clickCQ : function () { this.clickComponentQuery.apply(this, arguments) }, <span id='Siesta-Test-ExtJSCore-method-compositeQuery'> /** </span> * This method performs a combination of `Ext.ComponentQuery` and DOM query, allowing to easily find the DOM elements, * matching a css selector, inside of some Ext.Component. * * Both queries should be combined with the `=&gt;` separator: * * gridpanel[title=Accounts] =&gt; .x-grid-row * * On the left side of such &quot;composite&quot; query should be a component query, on the right - DOM query (CSS selector) * * In case when component query returns more than one component, this method iterate through all of them and will try to * resolve the 2nd part of the query. The results from the 1st component with matching DOM nodes is returned. * * E.g. the composite query `gridpanel[title=Accounts] =&gt; .x-grid-row` will give you the grid row elements inside a grid panel * with `title` config matching &quot;Accounts&quot;. * * @param {String} selector The CompositeQuery selector * @param {Ext.Component} root The optional root component to start the component query from. If omitted, a global component query will be performed. * @param {Boolean} allowEmpty False to throw the exception from this method if no matching DOM element is found. Default is `true`. * * @return {HTMLElement[]} The array of DOM elements */ compositeQuery : function (selector, root, allowEmpty, onlyVisibleComponents) { allowEmpty = allowEmpty !== false var R = Siesta.Resource(&#39;Siesta.Test.ExtJSCore&#39;) var i // Try to find magic =&gt; selector for nested ComponentQuery and CSS selector var mainParts = selector.split(&#39;=&gt;&#39;); root = root || this.Ext() &amp;&amp; this.Ext().ComponentQuery; // Root might not exist, Ext could be loaded in bootstrap mode without CQ if (!root) return [] if (mainParts.length &lt; 2) throw R.get(&#39;invalidCompositeQuery&#39;) + &#39;: &#39; + selector var compQuery = mainParts[ 0 ] var domQuery = mainParts[ 1 ] var components if (compQuery.match(/\.\w+\(/)) { var match var re = /(.+?)\.(\w+)\(\)/g // complex case like: xtype1 xtype2.getPicker() xtype3 xtype4.someMethod() while (root &amp;&amp; (match = re.exec(compQuery)) != null) { // TODO assuming query is specific, targeting just one target root = root.query(match[ 1 ])[ 0 ] if (root &amp;&amp; match[ 2 ]) root = root[ match[ 2 ] ]() } if (!root &amp;&amp; !allowEmpty) throw R.get(&#39;invalidCompositeQuery&#39;) + &#39;: &#39; + selector components = [ root ] } else { components = root.query(compQuery) } if (!components.length) if (allowEmpty) return [] else throw R.get(&#39;ComponentQuery&#39;) + &#39; &#39; + compQuery + &#39; &#39; + R.get(&#39;matchedNoCmp&#39;); for (i = 0; i &lt; components.length; i++) { var cmp = components[i]; if ( cmp.rendered &amp;&amp; ( // Widgets don&#39;t implement isVisible/isHidden !onlyVisibleComponents || cmp.isWidget || (cmp.isVisible ? cmp.isVisible() : !cmp.isHidden()) ) ) { var result = this.compToEl(cmp, false); result = this.sizzle(domQuery, result.dom) if (result.length &gt; 0) { return result; } } } if (allowEmpty) { return []; } throw R.get(&#39;CompositeQuery&#39;) + &#39; &#39; + selector + &#39; matched no DOM elements&#39;; }, <span id='Siesta-Test-ExtJSCore-method-cq'> /** </span> * An alias for Ext.ComponentQuery.query. * * As a convenience, this method will strip leading `&gt;&gt;` characters from the query * (which denotes the component query in {@link Siesta.Test.ActionTarget}). * * @param {String} selector The selector to perform a component query with */ cq : function (selector) { return this.Ext().ComponentQuery.query(selector.replace(/^(\s*&gt;&gt;)?/, &#39;&#39;)); }, <span id='Siesta-Test-ExtJSCore-method-cq1'> /** </span> * An shorthand method to get the first result of any Ext.ComponentQuery.query * * As a convenience, this method will strip leading `&gt;&gt;` characters from the query * (which denotes the component query in {@link Siesta.Test.ActionTarget}). * * @param {String} selector The selector to perform a component query with */ cq1 : function (selector) { return this.Ext().ComponentQuery.query(selector.replace(/^(\s*&gt;&gt;)?/, &#39;&#39;))[ 0 ]; }, <span id='Siesta-Test-ExtJSCore-method-waitForTarget'> /** </span> * Waits until the passed action target is detected and no ongoing animations are found. This can be a string such as a component query, CSS query or a composite query. * * @param {String/Siesta.Test.ActionTarget} target The target presence to wait for * @param {Function} callback The callback to call after the target 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. */ waitForTarget : function(target, callback, scope, timeout, offset) { var SUPER = this.SUPER this.waitForAnimations(function () { SUPER.call(this, target, callback, scope, timeout, offset) }, this, timeout); }, <span id='Siesta-Test-ExtJSCore-method-messageBoxIsVisible'> /** </span> * This assertion passes if the singleton MessageBox instance is currently visible. * The assertion is relevant if you use one of the following methods Ext.Msg.alert, Ext.Msg.confirm, Ext.Msg.prompt. * * @param {String} [description] The description for the assertion */ messageBoxIsVisible : function(desc) { return this.notOk(this.Ext().Msg.isHidden(), desc || Siesta.Resource(&#39;Siesta.Test.ExtJSCore&#39;, &#39;messageBoxVisible&#39;)); }, <span id='Siesta-Test-ExtJSCore-method-messageBoxIsHidden'> /** </span> * This assertion passes if the singleton MessageBox instance is currently hidden. * The assertion is relevant if you use one of the following methods Ext.Msg.alert, Ext.Msg.confirm, Ext.Msg.prompt. * * @param {String} [description] The description for the assertion */ messageBoxIsHidden : function(desc) { return this.ok(this.Ext().Msg.isHidden(), desc || Siesta.Resource(&#39;Siesta.Test.ExtJSCore&#39;, &#39;messageBoxHidden&#39;)); }, <span id='Siesta-Test-ExtJSCore-method-cqExists'> /** </span> * This assertion passes if the passed component query matches at least one component. * * @param {String} query The component query * @param {String} [description] The description for the assertion */ cqExists : function(query, description) { this.ok(this.cq1(query), description); }, <span id='Siesta-Test-ExtJSCore-method-cqNotExists'> /** </span> * This assertion passes if the passed component query matches no components. * * @param {String} query The component query * @param {String} [description] The description for the assertion */ cqNotExists : function(query, description) { this.notOk(this.cq1(query), description); }, <span id='Siesta-Test-ExtJSCore-method-componentQueryExists'> /** </span> * This assertion passes if the passed component query matches at least one component. * * @param {String} query The component query * @param {String} [description] The description for the assertion */ componentQueryExists : function() { this.cqExists.apply(this, arguments); }, <span id='Siesta-Test-ExtJSCore-method-setValue'> /** </span> * Sets a value to an Ext Component. A faster way to set a value than manually calling &quot;type&quot; into * a text field for example. A value is set by calling either the `setRawValue` or `setValue` method * of the component. * * @param {Ext.Component/String} component A component instance or a component query to resolve * @param {Mixed} value */ setValue : function (component, value, callback, scope) { component = this.normalizeComponent(component); (component.setChecked || component.setRawValue || component.setValue).call(component, value); callback &amp;&amp; this.processCallbackFromTest(callback, null, scope) }, <span id='Siesta-Test-ExtJSCore-method-waitForAnimations'> /** </span> * Waits until no ongoing animations can be detected. * * @param {Function} callback The callback to call after the component becomes visible * @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. */ waitForAnimations: function (callback, scope, timeout) { var R = Siesta.Resource(&#39;Siesta.Test.ExtJS&#39;); var me = this; return this.waitFor({ method : function () { return !me.areAnimationsRunning(); }, callback : callback, scope : scope, timeout : timeout, assertionName : &#39;waitForAnimations&#39;, description : &#39; &#39; + R.get(&#39;animationsToFinalize&#39;) }); } } }) </pre> </body> </html>