siesta-lite
Version:
Stress-free JavaScript unit testing and functional testing tool, works in NodeJS and browsers
1,020 lines (807 loc) • 44 kB
HTML
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>The source code</title>
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
<style type="text/css">
.highlight { display: block; background-color: #ddd; }
</style>
<script type="text/javascript">
function highlight() {
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
}
</script>
</head>
<body onload="prettyPrint(); highlight();">
<pre class="prettyprint lang-js">/*
Siesta 5.6.1
Copyright(c) 2009-2022 Bryntum AB
https://bryntum.com/contact
https://bryntum.com/products/siesta/license
*/
<span id='Siesta-Test-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 <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)
},
<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'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
},
<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 `>>` 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
},
<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 && 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;
},
<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'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
);
},
<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. '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);
}
},
<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('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)
<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 && 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()
})
},
<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('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) })
},
<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 `=>` 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';
},
<span id='Siesta-Test-ExtJSCore-method-cq'> /**
</span> * 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*>>)?/, ''));
},
<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 `>>` 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 ];
},
<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('Siesta.Test.ExtJSCore', 'messageBoxVisible'));
},
<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('Siesta.Test.ExtJSCore', 'messageBoxHidden'));
},
<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 "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)
},
<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('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')
});
}
}
})
</pre>
</body>
</html>