/* Siesta 5.6.1 Copyright(c) 2009-2022 Bryntum AB https://bryntum.com/contact https://bryntum.com/products/siesta/license */ /** @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 <a href="#!/guide/getting_started_browser">Siesta getting started in browser environment</a> guide. */ Role('Siesta.Test.ExtJSCore', { 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 "shared sandbox" group if (!this.reusingSandbox && 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('{ownerCt == null}') // sort, so that containers goes first // the logic is, that containers have "more logic" 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 ] && !comp.isDestroyed) comp.destroy() }) } // if there's a class manager - unregister "unexpected" classes if (Ext.ClassManager && Ext.undefine) { var index = {} Joose.O.each(Ext.ClassManager.classes, function (cls, name) { var global = name.split('.')[ 0 ] if (!me.isGlobalExpected(global, index)) Ext.undefine(name) }) } // if there's a store manager - also unregister stores (all stores except internal ext js store(s)) if (Ext.data && Ext.data.StoreManager) { var toRemove = []; Ext.data.StoreManager.each(function(store) { if (store.storeId !== "ext-empty-store") toRemove.push(store); }); Ext.data.StoreManager.unregister.apply(Ext.data.StoreManager, toRemove); } var sharedSandboxState = this.sharedSandboxState var modelsDefinedInPreload = sharedSandboxState && sharedSandboxState.modelsDefinedInPreload modelsDefinedInPreload && 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 && 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('Siesta.Test.ExtJSCore'); var loaderPath = this.loaderPath var StartTest = this.global.StartTest if (loaderPath && Ext && Ext.Loader && !StartTest.loaderPathHookInstalled) { this.project.generateLoaderPathHook()(StartTest, Ext, loaderPath) } var requires = this.requires if (requires && !this.requiringWaitingStarted && Ext && 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 && !this.onReadyWaitingStarted && Ext && 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 "ComponentQuery", "onReady" etc // detecting such state with "Ext.blink" property if ( this.waitForExtComponentQueryReady && Ext // this indicates Ext>=4, Ext3 does not have "ComponentQuery" concept && (Ext.getVersion || Ext.blink || Ext.manifest || Ext.microloaded) && !Ext.ComponentQuery ) return { ready : false, reason : R.get('waitedForComponentQuery') } if (requires && !this.isRequiringDone) return { ready : false, reason : R.get('waitedForRequires') } if (this.waitForExtReady && this.onReadyWaitingStarted && !this.isExtOnReadyDone) return { ready : false, reason : R.get('waitedForExt') } if (this.waitForAppReady && !this.isAppReadyDone && 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('waitedForApp') } } if (Ext && Ext.ComponentQuery) { // add :root pseudo CQ selector to be able to identify 'root' level components that don'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 'expect' this.expectGlobals('Ext', 'id'); this.SUPER(); }, getSimulatorClass : function () { return Siesta.Test.SimulatorExtJS }, forEachModelInAllSchemas : function (func) { var Ext = this.getExt() if (Ext && Ext.data && Ext.data.schema && Ext.data.schema.Schema && 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 "can start" 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) }, /** * 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'll be using the same "top-level" `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 }, /** * The alias for {@link #getExt} * @method */ Ext : function () { return this.global.Ext }, isExtJSComponentQueryTarget : function (obj) { return obj.isComponent || obj.isWidget; }, /** * This method performs an ExtJS component query. The selector may start with `>>` which will be * trimmed. * * @param {String} selector A component query selector. The leading '>>' 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() && this.Ext().ComponentQuery if (!selector || !root || !root.query) return [] // strip out leading >> which is used as indicator of the ComponentQuery in ActionTarget string selector = selector.replace(/^(\s*>>)?/, '').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 "isVisible()" method // we use `componentIsHidden` here which peforms just the "hierarchical" check (does not use "elementIsTop") 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) === 'String') { var result = this.componentQuery(component, root, { ignoreNonVisible : options.ignoreNonVisible }) var R = Siesta.Resource('Siesta.Test.ExtJSCore'); if (!allowEmpty && result.length < 1) this.warn(R.get('noComponentMatch').replace('{component}', component)); if (result.length > 1) { matchingMultiple = true var text = R.get('multipleComponentMatch').replace('{component}', component); if (this.project.failOnMultipleComponentMatches) { this.fail(text); } else { this.warn(text); } } component = result[ 0 ]; } return options.detailed ? { comp : component, matchingMultiple : matchingMultiple } : component }, /** * @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 && comp instanceof Ext.Editor && comp.field) { comp = comp.field; } // Ext JS if (Ext && Ext.form && Ext.form.Field && locateInputEl) { // Deal with bizarre markup in Ext 5.1.2+ if ( (Ext.form.Checkbox && comp instanceof Ext.form.Checkbox || Ext.form.Radio && comp instanceof Ext.form.Radio) && comp.el ) { var displayEl = comp.displayEl; if (displayEl && comp.boxLabel) { return displayEl; } var inputComponent = Ext.ComponentQuery.query('checkboxinput', comp)[ 0 ] if (inputComponent) return this.compToEl(inputComponent) // Ext6 Modern Ext6.7 Fallback return comp.el.down('.x-form-field') || comp.el.down('.x-field-input') || comp.el.down('.x-input-el') || comp.inputEl || comp.el; } if (comp instanceof Ext.form.Field && comp.inputEl) { var field = comp.el.down('.x-form-field'); return (field && field.dom) ? field : comp.inputEl; } if (Ext.form.HtmlEditor && comp instanceof Ext.form.HtmlEditor) { // Ext JS 3 Ext JS 4 return comp.iframe || comp.inputEl; } } if (Ext && Ext.field && Ext.field.Slider && (comp instanceof Ext.field.Slider)) { return this.compToEl(Ext.ComponentQuery.query('slider', comp)[ 0 ]) } // Sencha Touch: Form fields can have a child input component if (Ext && Ext.field && Ext.field.Field && comp instanceof Ext.field.Field && locateInputEl && comp.getComponent) { comp = comp.getComponent(); // some of the SenchaTouch fields uses "masks" - another DOM element, which is applied // on top of the field when it does not have focus // some of them have mask always ("useMask === true"), for such fields return mask element // as its the primary point of user interaction if (comp.getUseMask && comp.getUseMask() === true && comp.mask) return comp.mask if (locateInputEl && comp.input) return comp.input if (comp.bodyElement) return comp.bodyElement } // Ext JS vs Sencha Touch return comp.getEl && !comp.element ? comp.getEl() : locateInputEl && comp.input || comp.el || comp.element; }, /** * This method resolves a query string, as defined by the {@link Siesta.Test.ActionTarget}. See the link for details, * here we'll just briefly mention, that by default string supposed to be a CSS query. If it starts with `>>` * it will be recognized as Component query. And if it contains the `=>` characters, then it will be * considered a {@link compositeQuery compositeQuery}. * * ``` * await t.click('>>button'); * await t.click('mypanel => .dataview .item1'); * ``` * * You can also target Ext JS components in nested contexts like iframes: * * ``` * await t.click('.iframe1 -> .iframe2 -> >>button'); * await t.click('.iframe1 -> .iframe2 -> myPanel => button'); * ``` * * 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('.iframe1 -> iframe2 -> button:contains(Save)'); * ``` * * @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('->').pop().trim(); if (selector.match(/=>/)) { var rootExt = this.getExt(this.getGlobal(root)); return rootExt ? this.compositeQuery(selector, rootExt.ComponentQuery) : []; } else if (selector.match(/\s*>>/)) { 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 && el.dom) elements.push(el.dom) }) return elements } return me.SUPERARG([selector, root]); }, // Accept Ext.Element and Ext.Component // If the 'shallow' flag is true we should not 'reevaluate' 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 && options.offset var stopAtComponentLevel = options && options.stopAtComponentLevel var ignoreNonVisible = options && options.hasOwnProperty('ignoreNonVisible') ? options.ignoreNonVisible : true if (typeof el === 'string') { var root = this.getNestedRoot(el), global = root && this.getGlobal(root); el = el.split('->').pop().trim(); // A nested frame might not yet exist, or be ready if (!root || (el.match(/^\s*>>|=>/) && !this.getExt(global))) { return null; } if (el.match(/=>/)) { var Ext = this.getExt(global); // Composite query queryResult = this.compositeQuery(el, Ext.ComponentQuery, allowMissing, ignoreNonVisible) el = queryResult[ 0 ] matchingMultiple = queryResult.length > 1 } else if (el.match(/^\s*>>/)) { 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's a DOM query return this.SUPER(origEl, allowMissing, shallow, detailed); } if (!allowMissing && !el) { var R = Siesta.Resource('Siesta.Test.ExtJSCore'); var warning = R.get('noComponentFound') + ': ' + origEl; this.warn(warning); throw warning; } } var rawResult = false if (el && this.isExtJSComponentQueryTarget(el)) if (stopAtComponentLevel) rawResult = true else { el = this.compToEl(el); } // ExtJS Element if (el && 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 "normalizeElement", but resolved // till the component level only.. Which we prefer for the "firesOk" assertions family. // It'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 ); }, /** * 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. '4.0.7' * @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('Siesta.Test.ExtJSCore'); if (this.project.failKnownBugIn || version.isGreaterThan(frameworkVersion)) { fn.call(this.global, this); } else { this.todo(R.get('knownBugIn') + ' ' + frameworkVersion + ': ' + (reason || ''), fn); } }, /** * 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('Some.Class1', 'Some.Class2', function () { ... }) * or array of class names: * * t.requireOk([ 'Some.Class1', 'Some.Class2' ], 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('Siesta.Test.ExtJSCore'); var callback if (this.typeOf(args[ args.length - 1 ]) == 'Function') 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) /** * 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 && clsManager.overrideMap[ className ] // override normal class singleton if (isOverride || cls && (me.typeOf(cls) == 'Function' || me.typeOf(cls.self) == 'Function')) me.pass(R.get('Class') + ": " + className + " " + R.get('wasLoaded')) else me.fail(R.get('Class') + ": " + className + " " + R.get('wasNotLoaded')) }) 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() }) }, /** * 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('button', 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('button', 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 && this.typeOf(arguments[ 1 ]) == 'Function') { callback = root root = this.Ext().ComponentQuery } if (arguments.length == 1) { root = this.Ext().ComponentQuery } var result = root.query(selector) this.chainClick(result, function () { callback && callback.call(this, result) }) }, /** * 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) }, /** * 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 `=>` separator: * * gridpanel[title=Accounts] => .x-grid-row * * On the left side of such "composite" 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] => .x-grid-row` will give you the grid row elements inside a grid panel * with `title` config matching "Accounts". * * @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('Siesta.Test.ExtJSCore') var i // Try to find magic => selector for nested ComponentQuery and CSS selector var mainParts = selector.split('=>'); root = root || this.Ext() && this.Ext().ComponentQuery; // Root might not exist, Ext could be loaded in bootstrap mode without CQ if (!root) return [] if (mainParts.length < 2) throw R.get('invalidCompositeQuery') + ': ' + 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 && (match = re.exec(compQuery)) != null) { // TODO assuming query is specific, targeting just one target root = root.query(match[ 1 ])[ 0 ] if (root && match[ 2 ]) root = root[ match[ 2 ] ]() } if (!root && !allowEmpty) throw R.get('invalidCompositeQuery') + ': ' + selector components = [ root ] } else { components = root.query(compQuery) } if (!components.length) if (allowEmpty) return [] else throw R.get('ComponentQuery') + ' ' + compQuery + ' ' + R.get('matchedNoCmp'); for (i = 0; i < components.length; i++) { var cmp = components[i]; if ( cmp.rendered && ( // Widgets don'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 > 0) { return result; } } } if (allowEmpty) { return []; } throw R.get('CompositeQuery') + ' ' + selector + ' matched no DOM elements'; }, /** * An alias for Ext.ComponentQuery.query. * * As a convenience, this method will strip leading `>>` 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*>>)?/, '')); }, /** * An shorthand method to get the first result of any Ext.ComponentQuery.query * * As a convenience, this method will strip leading `>>` 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*>>)?/, ''))[ 0 ]; }, /** * 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); }, /** * 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('Siesta.Test.ExtJSCore', 'messageBoxVisible')); }, /** * 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('Siesta.Test.ExtJSCore', 'messageBoxHidden')); }, /** * 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); }, /** * 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); }, /** * 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); }, /** * Sets a value to an Ext Component. A faster way to set a value than manually calling "type" 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 && this.processCallbackFromTest(callback, null, scope) }, /** * 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('Siesta.Test.ExtJS'); var me = this; return this.waitFor({ method : function () { return !me.areAnimationsRunning(); }, callback : callback, scope : scope, timeout : timeout, assertionName : 'waitForAnimations', description : ' ' + R.get('animationsToFinalize') }); } } })