siesta-lite
Version:
Stress-free JavaScript unit testing and functional testing tool, works in NodeJS and browsers
1,177 lines (954 loc) • 92.8 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-Project'>/**
</span>
@class Siesta.Project
`Siesta.Project` is an abstract base project class in Siesta hierarchy. This class provides no UI,
you should use one of it subclasses, for example {@link Siesta.Project.Browser} or {@link Siesta.Project.Browser.ExtJS}
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.
Synopsys
========
var project = new Siesta.Project.Browser.ExtJS();
project.configure({
title : 'Awesome Test Suite',
transparentEx : true,
autoCheckGlobals : true,
expectedGlobals : [
'Ext',
'Sch'
],
preload : [
"http://cdn.sencha.io/ext-4.0.2a/ext-all-debug.js",
"../awesome-project-all.js",
{
text : "console.log('preload completed')"
}
]
})
project.plan(
// simple string - url relative to project file
'sanity.t.js',
// test file descriptor with own configuration options
{
url : 'basic.t.js',
// replace `preload` option of project
preload : [
"http://cdn.sencha.io/ext-4.0.6/ext-all-debug.js",
"../awesome-project-all.js"
]
},
// groups ("folders") of test files (possibly with own options)
{
group : 'Sanity',
autoCheckGlobals : false,
items : [
'data/crud.t.js',
...
]
},
...
)
*/
Class('Siesta.Project', {
does : [
JooseX.Observable,
Siesta.Util.Role.CanGetType,
Siesta.Util.Role.CanDetectES6
],
has : {
<span id='Siesta-Project-cfg-title'> /**
</span> * @cfg {String} title The title of the test suite. Can contain HTML. When provided in the test file descriptor - will change the name of test in the project UI.
*/
title : null,
<span id='Siesta-Project-cfg-desc'> /**
</span> * @cfg {String} desc The description of the test. Can contain HTML. When provided, will be shown as the tooltip in the tests grid.
*/
desc : null,
<span id='Siesta-Project-cfg-testClass'> /**
</span> * @cfg {Class} testClass The test class which will be used for creating test instances, defaults to {@link Siesta.Test}.
* You can subclass {@link Siesta.Test} and provide a new class.
*
* This option can be also specified in the test file descriptor.
*/
testClass : Siesta.Test,
contentManagerClass : Siesta.Content.Manager,
// fields of test descriptor:
// - id - either `url` or wbs + group - computed
// - url
// - isMissing - true if test file is missing
// - testCode - a test code source (can be provided by user)
// - testConfig - config object provided to the StartTest
// - index - (in the group) computed
// - scopeProvider
// - scopeProviderConfig
// - preload
// - alsoPreload
// - parent - parent descriptor (or project for top-most ones) - computed
// - preset - computed by project - instance of Siesta.Content.Preset
// - forceDOMVisible - true to show the <iframe> on top of all others when running this test
// (required for IE when using "document.getElementFromPoint()")
// OR - object
// - group - group name
// - items - array of test descriptors
// - expanded - initial state of the group (true by default)
descriptors : Joose.I.Array,
descriptorsById : Joose.I.Object,
launchCounter : 0,
launches : Joose.I.Object,
scopesByURL : Joose.I.Object,
testsByURL : Joose.I.Object,
<span id='Siesta-Project-cfg-transparentEx'> /**
</span> * @cfg {Boolean} transparentEx When set to `true` project will not try to catch any exception, thrown from the test code.
* This is very useful for debugging - you can for example use the "break on error" option in Firebug.
* But, using this option may naturally lead to unhandled exceptions, which may leave the project in incosistent state -
* refresh the browser page in such case.
*
* Defaults to `false` - project will do its best to detect any exception thrown from the test code.
*
* This option can be also specified in the test file descriptor.
*/
transparentEx : false,
scopeProviderConfig : null,
scopeProvider : null,
<span id='Siesta-Project-cfg-runCore'> /**
</span> * @cfg {String} runCore Either `parallel` or `sequential`. Indicates how the individual tests should be run - several at once or one-by-one.
* Default value is "parallel". You do not need to change this option usually.
*/
runCore : 'parallel',
<span id='Siesta-Project-cfg-maxThreads'> /**
</span> * @cfg {Number} maxThreads The maximum number of tests running at the same time. Only applicable for `parallel` run-core.
*/
maxThreads : 4,
<span id='Siesta-Project-cfg-autoCheckGlobals'> /**
</span> * @cfg {Boolean} autoCheckGlobals When set to `true`, project will automatically issue an {@link Siesta.Test#verifyGlobals} assertion at the end of each test,
* so you won't have to manually specify it each time. The assertion will be triggered only if test completed successfully. Default value is `false`.
* See also {@link #expectedGlobals} configuration option and {@link Siesta.Test#expectGlobals} method.
*
* This option will be always disabled in Opera, since every DOM element with `id` is being added as a global symbol in it.
*
* This option can be also specified in the test file descriptor.
*/
autoCheckGlobals : false,
disableGlobalsCheck : false,
<span id='Siesta-Project-cfg-expectedGlobals'> /**
</span> * @cfg {Array} expectedGlobals An array of strings or regular expressions which are likely to present in the scope of each test. There is no need to provide the name
* of built-in globals - project will automatically scan them from the empty context. Only provide the names of global properties which will be created
* by your preload code.
*
* For example
*
project.configure({
title : 'Ext Scheduler Test Suite',
autoCheckGlobals : true,
expectedGlobals : [
'Ext',
'MyProject',
/jQuery\d+/, // Can use RegExp too!
],
...
})
* This option can be also specified in the test file descriptor.
*/
expectedGlobals : Joose.I.Array,
// will be populated by `populateCleanScopeGlobals`
cleanScopeGlobals : Joose.I.Array,
<span id='Siesta-Project-cfg-preload'> /**
</span> * @cfg {Array} preload
*
* The array which contains the *preload descriptors* describing which files/code should be preloaded into the scope of each test.
*
* Preload descriptor can be:
*
* - a string, containing an url to load (cross-domain urls are ok, if url ends with ".css" it will be loaded as CSS)
* - an object `{ type : 'css/js', url : '...' }` allowing to specify the CSS files with different extension
* - an object `{ type : 'css/js', content : '...' }` allowing to specify the inline content for script / style. The content should only be the tag content - not the tag itself, it'll be created by Siesta.
* - an object `{ text : '...' }` which is a shortcut for `{ type : 'js', content : '...' }`
*
* `preload` array can contain other nested arrays which will be flattened recursively. Any "empty" values
* (like `null`, empty string, false etc) will be ignored.
*
* For example:
*
project.configure({
title : 'Ext Scheduler Test Suite',
preload : [
'http://cdn.sencha.io/ext-4.0.2a/resources/css/ext-all.css',
'http://cdn.sencha.io/ext-4.0.2a/ext-all-debug.js',
{
text : 'MySpecialGlobalFunc = function () { if (typeof console != "undefined") ... }'
},
// simple conditional preload
someCondition ?
[
'http://mydomain.com/file.css',
'http://mydomain.com/file.js'
]
:
null
],
...
})
* This option can be also specified in the test file descriptor. **Note**, that if test descriptor has non-empty
* {@link Siesta.Project.Browser#pageUrl pageUrl} option, then *it will not inherit* the `preload` option
* from parent descriptors or project, **unless** it has the `preload` config set to string `inherit`.
* If both `pageUrl` and `preload` are set on the project level, `preload` value still will be inherited. For example:
*
project.configure({
pageUrl : 'general-page.html',
preload : [ 'my-file.js' ],
...
})
project.plan(
// this test will inherit both `pageUrl` and `preload`
'test1.js',
{
// no preloads inherited
pageUrl : 'host-page.html',
url : 'test2.js'
},
{
// inherit `preload` value from the upper level - [ 'my-file.js' ]
pageUrl : 'host-page.html',
preload : 'inherit',
url : 'test3.js'
},
{
group : 'Some group',
pageUrl : 'host-page2.html',
preload : 'inherit',
items : [
{
// inherit `pageUrl` value from the group
// inherit `preload` value from the upper level - [ 'my-file.js' ]
url : 'test3.js'
}
]
}
)
* When loading ES6 modules, one need to indicate this using the `isEcmaModule` property of the preload descriptor.
* In this case, the module `<script>` tag will be created with the `type` attribute set to `module`, instead of `text/javascript`.
*
project.configure({
preload : [
{
type : 'js',
url : 'some_file.js',
isEcmaModule : true
},
{
type : 'js',
content : 'import {something} from "another/module.js"',
isEcmaModule : true
}
],
...
})
*
*
*/
preload : Joose.I.Array,
<span id='Siesta-Project-cfg-alsoPreload'> /**
</span> * @cfg {Array} alsoPreload The array with preload descriptors describing which files/code should be preloaded **additionally**.
*
* This option can be also specified in the test file descriptor.
*/
<span id='Siesta-Project-cfg-listeners'> /**
</span> * @cfg {Object} listeners The object which keys corresponds to event names and values - to event handlers. If provided, the special key "scope" will be treated as the
* scope for all event handlers, otherwise the project itself will be used as scope.
*
* Note, that the events from individual {@link Siesta.Test test cases} instances will bubble up to the project - you can listen to all of them in one place:
*
project.configure({
title : 'Awesome Test Suite',
preload : [
'http://cdn.sencha.io/ext-4.1.0-gpl/resources/css/ext-all.css',
'http://cdn.sencha.io/ext-4.1.0-gpl/ext-all-debug.js',
'preload.js'
],
listeners : {
testsuitestart : function (event, project) {
log('Test suite is starting: ' + project.title)
},
testsuiteend : function (event, project) {
log('Test suite is finishing: ' + project.title)
},
teststart : function (event, test) {
log('Test case is starting: ' + test.url)
},
testupdate : function (event, test, result) {
log('Test case [' + test.url + '] has been updated: ' + result.description + (result.annotation ? ', ' + result.annotation : ''))
},
testfailedwithexception : function (event, test) {
log('Test case [' + test.url + '] has failed with exception: ' + test.failedException)
},
testfinalize : function (event, test) {
log('Test case [' + test.url + '] has completed')
}
}
})
*/
<span id='Siesta-Project-cfg-cachePreload'> /**
</span> * @cfg {Boolean} cachePreload When set to `true`, project will cache the content of the preload files and provide it for each test, instead of loading it
* from network each time. This option may give a slight speedup in tests execution (especially when running the suite from the remote server), but see the
* caveats below. Default value is `false`.
*
* Caveats: this option doesn't work very well for CSS (due to broken relative urls for images). Also its not "debugging-friendly" - as you will not be able
* to setup breakpoints for cached code.
*/
cachePreload : false,
mainPreset : null,
emptyPreset : null,
<span id='Siesta-Project-cfg-keepNLastResults'> /**
</span> * @cfg {Number} keepNLastResults
*
* Indicates the number of the test results which still should be kept, for user examination.
* Results are cleared when their total number exceed this value, based on FIFO order.
*/
keepNLastResults : 2,
lastResultsURLs : Joose.I.Array,
lastResultsByURL : Joose.I.Object,
<span id='Siesta-Project-cfg-breakOnFail'> /**
</span> * @cfg {Boolean} breakOnFail When set to `true`, the project will not start launching any further tests after
* detecting a failed assertion. When running in automation mode, test suite will be finalized immediately,
* ignoring the --rerun-failed option.
*
* Default value is `false`.
*/
breakOnFail : false,
<span id='Siesta-Project-cfg-breakTestOnFail'> /**
</span> * @cfg {Boolean} breakTestOnFail When set to `true`, the whole test file will be finalized after the 1st
* failed assertion is generated in any of its sub-tests (`it/describe` sections). The test finalization
* is performed by throwing an exception, if {@link #transparentEx} is not enabled you will see it
* in the console/debugger.
*
* To break the currently running sub-test only (`it/describe` section) see {@link #breakSubTestOnFail}.
*
* This option can be also specified in the test file descriptor.
*
* Default value is `false`.
*/
breakTestOnFail : false,
<span id='Siesta-Project-cfg-breakSubTestOnFail'> /**
</span> * @cfg {Boolean} breakSubTestOnFail When set to `true`, the currently running sub test (`it/describe` section)
* will be finalized after the 1st failed assertion is generated. The test finalization
* is performed by throwing an exception, if {@link #transparentEx} is not enabled you will see it
* in the console/debugger.
*
* To break the whole currently test file see {@link #breakTestOnFail}.
*
* This option can be also specified in the test file descriptor.
*
* Default value is `false`.
*/
breakSubTestOnFail : false,
<span id='Siesta-Project-cfg-overrideSetTimeout'> /**
</span> * @cfg {Boolean} overrideSetTimeout When set to `true`, the tests will override the native "setTimeout" from the context of each test
* for asynchronous code tracking. If setting it to `false`, you will need to use `beginAsync/endAsync` calls to indicate that test is still running.
*
* Note, that this option may not work reliably, when used for several sub tests launched simultaneously (for example
* for several sibling {@link Siesta.Test#todo} sections.
*
* This option can be also specified in the test file descriptor. Defaults to `false`.
*/
overrideSetTimeout : false,
<span id='Siesta-Project-cfg-needDone'> /**
</span> * @cfg {Boolean} needDone When set to `true`, the tests will must indicate that that they have reached the correct
* exit point with `t.done()` call, after which, adding any assertions is not allowed.
* Using this option will ensure that test did not exit prematurely with some exception silently caught.
*
* This option can be also specified in the test file descriptor.
*/
needDone : false,
// the default timeout for tests will be increased when launching more than this number of files
increaseTimeoutThreshold : 8,
// the start and end dates for the most recent `launch` method
startDate : null,
endDate : null,
<span id='Siesta-Project-cfg-waitForTimeout'> /**
</span> * @cfg {Number} waitForTimeout Default timeout for `waitFor` (in milliseconds). Default value is 10000.
*
* This option can be also specified in the test file descriptor.
*/
waitForTimeout : 10000,
<span id='Siesta-Project-cfg-defaultTimeout'> /**
</span> * @cfg {Number} defaultTimeout Default timeout for `beginAsync` operation (in milliseconds). Default value is 15000.
*
* This option can be also specified in the test file descriptor.
*/
defaultTimeout : 15000,
<span id='Siesta-Project-cfg-subTestTimeout'> /**
</span> * @cfg {Number} subTestTimeout Default timeout for sub tests. Default value is twice bigger than {@link #defaultTimeout}.
*
* This option can be also specified in the test file descriptor.
*/
subTestTimeout : null,
<span id='Siesta-Project-cfg-isReadyTimeout'> /**
</span> * @cfg {Number} isReadyTimeout Default timeout for test start (in milliseconds). Default value is 15000. See {@link Siesta.Test#isReady} for details.
*
* This option can be also specified in the test file descriptor.
*/
isReadyTimeout : 10000,
<span id='Siesta-Project-cfg-pauseBetweenTests'> /**
</span> * @cfg {Number} pauseBetweenTests Default timeout between tests (in milliseconds). Increase this settings for big test suites, to give browser time for memory cleanup.
*/
pauseBetweenTests : 10,
<span id='Siesta-Project-cfg-failOnExclusiveSpecsWhenAutomated'> /**
</span> * @cfg {Boolean} failOnExclusiveSpecsWhenAutomated When this option is enabled and Siesta is running in automation mode
* (using WebDriver or Puppeteer launcher) any exclusive BDD specs found (like {@link Siesta.Test#iit t.iit} or {@link Siesta.Test#ddescribe t.ddescribe}
* will cause a failing assertion. The idea behind this setting is that such "exclusive" specs should only be used during debugging
* and are often mistakenly committed in the codebase, leaving other specs not executed.
*
* This option can be also specified in the test file descriptor.
*/
failOnExclusiveSpecsWhenAutomated : false,
<span id='Siesta-Project-cfg-snooze'> /**
</span> * @cfg {Date/String} snooze
*
* Either a `Date` instance or a string, recognized by the [Date constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse).
*
* If test is running prior the specified date, the whole test will be made a "todo". See the {@link Siesta.Test#snooze} method.
*
* Example:
*
project.plan(
{
group : 'Some group',
snooze : '2016-10-11',
items : [
...
]
}
)
*
* This option can be also specified in the test file descriptor.
*/
snooze : null,
<span id='Siesta-Project-cfg-referenceUrl'> /**
</span> * @cfg {String} referenceUrl
*
* The url, containing additional information about the test. This option is inherited from the group configs,
* as other options. In the Siesta user interface, `CTRL+click` on the
* test row will open a new browser window, pointing to this url. Can be used to link the test with some external
* resource like ticket, screenshot, etc.
project.plan(
{
url : 'my_test.t.js',
referenceUrl : 'http://jira.com/jira_issue'
}
)
* This option can be also specified in the test file descriptor.
*/
referenceUrl : null,
<span id='Siesta-Project-cfg-suppressPassedWaitForAssertion'> /**
</span> * @cfg {Boolean} suppressPassedWaitForAssertion
*
* When enabled, the passed "waitFor" assertions won't be included in the tests.
*
* This option can be also specified in the test file descriptor.
*/
suppressPassedWaitForAssertion : false,
<span id='Siesta-Project-cfg-isEcmaModule'> /**
</span> * @cfg {Boolean} isEcmaModule
*
* This option can be specified in the test file descriptor and/or as the global project config. In the latter case it will affect all tests.
*
* When enabled, the test script file (the one containing the `StartTest()` function) will be loaded using
* `<script type="module">` instead of `<script type="text/javascript">`
*
* See also a note in the {@link Siesta.Project#preload preload} config.
*/
isEcmaModule : null,
setupDone : false,
sourceLineForAllAssertions : false,
currentLaunchId : null,
isAutomated : false,
autoLaunchTests : true,
configSynonyms : function () { return this.processConfigSynonyms(this.buildConfigSynonyms()) },
uniqueCounter : 0,
valueToHashIndicies : Joose.I.Object,
// lazy attribute, should be accessed with "getSandboxHashStructure" method
sandboxHashStructure : {
lazy : 'this.buildSandboxHashStructure'
},
<span id='Siesta-Project-cfg-sandbox'> /**
</span> * @cfg {Boolean} sandbox
*
* This option controls whether the individual tests should be run in isolation from each other. By default it is enabled,
* and every test file will be run inside of the newly created iframe (or in the separate Node.js process), so that it can not interfere with
* any other test. Such setup gives you predictable starting state for every test, removes the need for any kind of
* cleanup at the end of the test and is more robust in general.
*
* However, the setup of the new sandbox creates some overhead. If you are sure that your tests
* do not modify any global state (like global variable that can affect the other test) you may want to run
* all of them in the same context, saving the setup time. In this case, you may want to disable this option.
*
* Siesta collects all tests with this option disabled and split them into chunks. Every chunk will have exactly
* the same values for the configs that influence the initial setup of the page: {@link #preload}, {@link #alsoPreload},
* {@link #pageUrl}, {@link Siesta.Test.ExtJS#requires} and some others. The tests inside of every
* chunk will be run sequentially, in the same sandbox.
*
* **Important**: The 1st test in every chunk will be run normally. Starting from the 2nd one, tests
* will skip the {@link Siesta.Test#isReady} check and {@link Siesta.Test#setup} methods. This is because all the
* setup is supposed to be already done by the 1st test. This behavior may change (or made configurable) in the future.
*
* This option can be specified in the test file descriptor.
*
* See also {@link #sandboxBoundaryByGroup}, {@link #sandboxCleanup}
*/
sandbox : true,
<span id='Siesta-Project-cfg-sandboxBoundaryByGroup'> /**
</span> * @cfg {Boolean} sandboxBoundaryByGroup
*
* Only applicable for tests with the {@link #sandbox} option *disabled*.
*
* when this option is enabled, the tests to be run in the same context will be guaranteed to reside in the same group.
* If a new test group starts (even with the same "preload" config) - a fresh context for that group will be created
* by Siesta.
*
* For example, in the following setup, both "Group 1" and "Group 2" have sandboxing disabled and the
* same "preload" config. If `sandboxBoundaryByGroup` will be disabled all 4 individual tests will be run
* in the same context. If `sandboxBoundaryByGroup` will be enabled, separate fresh context will be created
* for the tests from each group.
*
project.configure({
preload : [ ... ]
});
project.plan(
{
group : 'Group 1',
sandbox : false,
items : [
'010-basics/010_sanity.t.js',
'010-basics/020_jshint.t.js'
]
},
{
group : 'Group 2',
sandbox : false,
items : [
'020-basics/010_sanity.t.js',
'020-basics/030_bdd.t.js'
]
},
...
)
*
*/
sandboxBoundaryByGroup : true,
<span id='Siesta-Project-cfg-sandboxCleanup'> /**
</span> * @cfg {Boolean} sandboxCleanup
*
* Only applicable for tests with the {@link #sandbox} option *disabled*. When enabled, test that runs
* in shared sandbox (the sandbox in which another test just has been run) will perform a cleanup.
*
* By default it will remove any "unexpected" globals (see {@link #expectedGlobals}) and clear the DOM.
*
* If you will disable this option, every new test in the "groups" will start from the state previous test
* has finished the execution. This will allow you split one big test scenario into several files
*
* This option can be specified in the test file descriptor.
*/
sandboxCleanup : true,
<span id='Siesta-Project-cfg-debuggerOnFail'> /**
</span> * @cfg {Boolean} debuggerOnFail When set to `true`, the project will issue a `debugger` statement after detecting a failed assertion, allowing you
* to inspect the internal state of the test in the browser's debugger. Default value is `false`.
*/
debuggerOnFail : false,
<span id='Siesta-Project-cfg-debuggerOnStart'> /**
</span> * @cfg {Boolean} debuggerOnStart When set to `true`, the project will issue a `debugger` statement before launching any test. Default value is `false`.
*/
debuggerOnStart : false,
<span id='Siesta-Project-cfg-ignoreException'> /**
</span> * @cfg {RegExp/String} ignoreException If this option provided, Siesta will ignore any exceptions, stringified value of which matches this option.
* If this option is provided as string, it is passed to the `RegExp` constructor.
*
* This option is supposed to be used only for some "strange" exceptions, originated from the browser itself. For the exceptions
* from the test code behavior of this option is undefined.
*
* Can also be specified in the test file descriptor.
*/
ignoreException : null
},
methods : {
initialize : function () {
var me = this
me.on('testupdate', function (event, test, result, parentResult) {
me.onTestUpdate(test, result, parentResult);
})
me.on('testfailedwithexception', function (event, test, exception, stack) {
me.onTestFail(test, exception, stack);
})
me.on('teststart', function (event, test) {
me.onTestStart(test);
})
me.on('testfinalize', function (event, test) {
me.onTestEnd(test);
})
},
buildConfigSynonyms : function () {
return {}
},
// creates a reference from every synonym to a full list of synonyms, including the main name itself
// { 'main' : [ 'main', 'syn1', 'syn2' ], 'syn1' : [ 'main', 'syn1', 'syn2' ], 'syn2' : [ 'main', 'syn1', 'syn2' ] }
processConfigSynonyms : function (synonyms) {
var result = {}
Joose.O.each(synonyms, function (synonymsList, mainName) {
if (synonymsList instanceof Array)
synonymsList.unshift(mainName)
else
synonymsList = [ mainName, synonymsList ]
Joose.A.each(synonymsList, function (synonym) {
result[ synonym ] = synonymsList
})
})
return result
},
onTestUpdate : function (test, result, parentResult) {
},
onTestFail : function (test, exception, stack) {
},
onTestStart : function (test) {
},
onTestEnd : function (test) {
},
onTestSuiteStart : function (descriptors, contentManager, launchState) {
this.startDate = new Date()
<span id='Siesta-Project-event-testsuitestart'> /**
</span> * This event is fired when the test suite starts. Note, that when running the test suite in the browser, this event can be fired several times
* (for each group of tests you've launched).
*
* You can subscribe to it, using regular ExtJS syntax:
*
* project.on('testsuitestart', function (event, project) {}, scope, { single : true })
*
* See also the "/examples/events"
*
* @event testsuitestart
* @member Siesta.Project
* @param {JooseX.Observable.Event} event The event instance
* @param {Siesta.Project} project The project that just has started
*/
this.fireEvent('testsuitestart', this, launchState)
},
onTestSuiteEnd : function (descriptors, contentManager, launchState) {
this.endDate = new Date()
<span id='Siesta-Project-event-testsuiteend'> /**
</span> * This event is fired when the test suite ends. Note, that when running the test suite in the browser, this event can be fired several times
* (for each group of tests you've launched).
*
* @event testsuiteend
* @member Siesta.Project
* @param {JooseX.Observable.Event} event The event instance
* @param {Siesta.Project} project The project that just has ended
*/
this.fireEvent('testsuiteend', this, launchState)
},
onBeforeScopePreload : function (scopeProvider, url) {
this.fireEvent('beforescopepreload', scopeProvider, url)
},
onAfterScopePreload : function (scopeProvider, url) {
this.fireEvent('afterscopepreload', scopeProvider, url)
},
onCachingError : function (descriptors, contentManager) {
},
<span id='Siesta-Project-method-configure'> /**
</span> * This method configures the project instance. It just copies the passed configuration option into project instance.
*
* @param {Object} config - configuration options (values of attributes for this class)
*/
configure : function (config) {
Joose.O.copy(config, this)
var me = this
if (config.listeners) Joose.O.each(config.listeners, function (value, name) {
if (name == 'scope') return
me.on(name, value, config.scope || me)
})
},
// backward compat
processPreloadArray : function (preload) {
var me = this
preload = this.flattenArray(preload, true)
Joose.A.each(preload, function (obj, index) {
// do not process { text : "" } preload descriptors
if (Object(obj) === obj) {
if (obj.url) obj.url = me.normalizeURL(obj.url)
} else
preload[ index ] = me.normalizeURL(obj)
})
return preload
},
populateCleanScopeGlobals : function (scopeProvider, callback) {
var scopeProviderClass = eval(scopeProvider)
var cleanScope = new scopeProviderClass()
var cleanScopeGlobals = this.cleanScopeGlobals
// we can also use "create" and not "setup" here
// create will only create the iframe (in browsers) and will not try to update its content
// the latter crashes IE8
cleanScope.setup(function () {
for (var name in cleanScope.scope) cleanScopeGlobals.push(name)
callback()
// this setTimeout seems to stop the spinning loading indicator in FF
// accorting to https://github.com/3rd-Eden/Socket.IO/commit/bad600fb1fb70238f42767c56f01256470fa3c15
// it only works *after* onload (this callback will be called *in* onload)
setTimeout(function () {
// will remove the iframe (in case of browser project) from DOM and stop loading indicator
cleanScope.cleanup()
}, 0)
})
},
startSingle : function (desc, callback) {
var me = this
this.__counter__ = this.__counter__ || 0
var startSingle = function () {
me.launch([ me.normalizeDescriptor(desc, me, me.__counter__++) ], callback)
}
me.setupDone ? startSingle() : this.setup(startSingle)
},
setup : function (callback) {
var me = this
this.mainPreset = new Siesta.Content.Preset({
preload : this.processPreloadArray(this.preload)
})
this.emptyPreset = new Siesta.Content.Preset()
// A system level descriptor used by the recorder
me.descriptors.push({
isSystemDescriptor : true,
url : '/'
});
me.normalizeDescriptors(me.descriptors)
this.populateCleanScopeGlobals(this.scopeProvider, callback)
},
<span id='Siesta-Project-method-plan'> /**
</span> * This method adds *test file descriptors* (test files), to the project. It can be called several times.
*
* A test file descritor is one of the following:
*
* - a string, containing a test file url. The url should be unique among all tests. If you need to re-use the same test
* file, you can add an arbitrary query string to it: `my_test.t.js?copy=1`
* - an object containing the `url` property `{ url : '...', option1 : 'value1', option2 : 'value2' }`. The `url` property should point to the test file.
* Other properties can contain values of some configuration options of the project (marked accordingly). In this case, they will **override** the corresponding values,
* provided to project or parent descriptor.
* - an object `{ group : 'groupName', items : [], expanded : true, option1 : 'value1' }` specifying the folder of test files. The `expanded` property
* sets the initial state of the folder - "collapsed/expanded". The `items` property can contain an array of test file descriptors.
* Other properties will override the applicable project options **for all child descriptors**.
*
* If test descriptor is `null` or other "falsy" value it is ignored.
*
* Groups (folder) may contain nested groups. Number of nesting levels is not limited.
*
* For example, one may easily have a special group of test files, having its own `preload` configuration (for example for testing on-demand loading). In the same
* time some test in that group may have its own preload, overriding others.
project.configure({
title : 'Ext Scheduler Test Suite',
preload : [
'http://cdn.sencha.io/ext-4.0.2a/resources/css/ext-all.css',
'http://cdn.sencha.io/ext-4.0.2a/ext-all-debug.js',
'../awesome-app-all-debug.js'
],
...
})
project.plan(
// regular file
'data/crud.t.js',
// a group with own "preload" config for its items
{
group : 'On-demand loading',
preload : [
'http://cdn.sencha.io/ext-4.0.2a/resources/css/ext-all.css',
'http://cdn.sencha.io/ext-4.0.2a/ext-all-debug.js',
],
items : [
'ondemand/sanity.t.js',
'ondemand/special-test.t.js',
// a test descriptor with its own "preload" config (have the most priority)
{
url : 'ondemand/4-0-6-compat.t.js',
preload : [
'http://cdn.sencha.io/ext-4.0.6/resources/css/ext-all.css',
'http://cdn.sencha.io/ext-4.0.6/ext-all-debug.js',
]
},
// sub-group
{
group : 'Sub-group',
items : [
...
]
}
]
},
...
)
* Additionally, you can provide a test descriptor in the test file itself, adding it as the 1st or 2nd argument for `StartTest` call:
*
StartTest({
autoCheckGlobals : false,
alsoPreload : [ 'some_additional_preload.js' ]
}, function (t) {
...
})
*
* Values from this object takes the highest priority and will override any other configuration.
*
* Test descriptor may contain special property - `config` which will be applied to the test instance created. Be careful not to overwrite
* standard properties and methods!
*
project.plan(
{
url : 'ondemand/4-0-6-compat.t.js',
config : {
myProperty1 : 'value1',
myProperty2 : 'value2'
}
},
...
)
StartTest(function (t) {
if (t.myProperty1 == 'value1') {
// do this
}
...
})
*
* @param {Array/Mixed} descriptor1 or an array of descriptors
* @param {Mixed} descriptor2
* @param {Mixed} descriptorN
*/
plan : function () {
var descriptors = this.flattenArray(arguments)
// A system level descriptor used by the recorder
this.descriptors.push.apply(this.descriptors, descriptors)
},
<span id='Siesta-Project-method-start'> /**
</span> * This method will launch a test suite.
*
* For backward compatibility, it also calls {@link #plan} with its arguments.
*/
start : function () {
var me = Siesta.my.activeHarness = this
me.plan(this.flattenArray(arguments))
this.setup(function () {
me.setupDone = true
me.fireEvent('setupdone')
if (me.autoLaunchTests) me.launch(me.descriptors)
})
},
<span id='Siesta-Project-method-startFromUrl'> /**
</span> * This method will read the content of the provided `url` then will try to parse it as JSON
* and pass to the regular {@link #start} method. The file on the `url` should contain
* a valid JSON array object with test descriptors.
*
* You can use this method in conjunction with the `bin/discover` utility, which can
* auto-discover the test files and generate a starter file for you. In such setup, it is convenient
* to specify the test configs in the test file itself (see {@link #start} method for details).
* However, in such setup, you can not use conditional processing of the descriptors set, so
* you decide what fits best to your needs.
*
* @param {String} url
*/
startFromUrl : function (url) {
var contentManager = new this.contentManagerClass({
project : this,
presets : [ new Siesta.Content.Preset({ preload : [ url ] }) ]
})
var me = this
contentManager.cache(function () {
var content = contentManager.getContentOf(url)
try {
var descriptors = JSON.parse(content)
} catch (e) {
alert("The content of: " + url + " is not a valid JSON")
return
}
if (me.typeOf(descriptors) == 'Array')
me.start(descriptors)
else {
alert("The content of: " + url + " is not an array")
}
}, function () {
alert("Can not load the content of: " + url)
})
},
// good to have this as a separate method for testing
normalizeDescriptors : function (descArray) {
var me = this
var descriptors = []
Joose.A.each(descArray, function (desc, index) {
if (desc) descriptors.push(me.normalizeDescriptor(desc, me, index))
})
me.descriptors = descriptors
},
launch : function (descriptors, callback, errback) {
var launchId = this.currentLaunchId = ++this.launchCounter
var me = this
//console.time('launch')
//console.time('launch-till-preload')
//console.time('launch-after-preload')
// no folders, only leafs
var flattenDescriptors = this.flattenDescriptors(descriptors)
// the preset for the test scripts files
var testScriptsPreset = new Siesta.Content.Preset()
var presets = [ testScriptsPreset, this.mainPreset ]
var notLaunchedByAutomationId = {}
Joose.A.each(flattenDescriptors, function (desc) {
if (desc.preset != me.mainPreset && desc.preset != me.emptyPreset) presets.push(desc.preset)
if (!desc.testCode) testScriptsPreset.addResource(desc.url)
me.deleteTestByURL(desc.url)
// only used in automation, where the `desc.automationElementId` is populated
notLaunchedByAutomationId[ desc.automationElementId ] = 1
})
// cache either everything (this.cachePreload) or only the test files (to be able to show missing files / show content)
var contentManager = new this.contentManagerClass({
project : this,
presets : [ testScriptsPreset ].concat(this.cachePreload ? presets : [])
})
var launchState = this.launches[ launchId ] = {
launchId : launchId,
increaseTimeout : this.runCore == 'parallel' && flattenDescriptors.length > this.increaseTimeoutThreshold,
descriptors : flattenDescriptors,
contentManager : contentManager,
needToStop : false,
notLaunchedByAutomationId : notLaunchedByAutomationId
}
//console.time('caching')
me.onTestSuiteStart(descriptors, contentManager, launchState)
contentManager.cache(function () {
//console.timeEnd('caching')
Joose.A.each(flattenDescriptors, function (desc) {
var url = desc.url
if (contentManager.hasContentOf(url)) {
// the test descriptor defined in the test file itself, takes the highest precendence
var testConfig = desc.testConfig = Siesta.getConfigForTestScript(contentManager.getContentOf(url))
// if testConfig contains the "preload" or "alsoPreload" key - then we need to update the preset of the descriptor
if (testConfig && (testConfig.preload || testConfig.alsoPreload)) desc.preset = me.getDescriptorPreset(desc)
} else
// if test code is provided, then test is considered not missing
// allow subclasses to define there own logic when found missing test file
if (!desc.testCode) me.markMissingFile(desc)
me.normalizeScopeProvider(desc)
})
me.fireEvent('testsuitelaunch', descriptors, contentManager, launchState)
me.runCoreGeneral(flattenDescriptors, contentManager, launchState, launchState.callback = function () {
me.onTestSuiteEnd(descriptors, contentManager, launchState)
callback &&