Nickolay - Test.Run-0.06
Name
Test.Run.Harness.Browser - Abstract base class for test harness
SYNOPSIS
Test.Run.Harness.Browser.Multi.my.configure({
title : 'Module.Stub Test Suite',
passThroughEx : true,
preload : [
'/jsan/Task/Joose/Core.js',
"/jsan/JooseX/SimpleRequest.js",
'/jsan/Task/JooseX/Namespace/Depended/Web.js',
{
text : "JooseX.Namespace.Depended.Manager.my.INC = " + Ext.encode(INC)
}
]
})
Test.Run.Harness.Browser.Multi.my.start(
'010_sanity.t.js',
'020_basics.t.js'
)
DESCRIPTION
Test.Run.Harness.Browser is an abstract harness class in Test.Run hierarchy, providing the methods for running tests on browser platform.
This class still provides no UI, you should use one of it subclasses, for example Test.Run.Harness.Browser.Multi
This class is a pure static class - it defines only static properties. Please refer to Joose.Manual.Static
ISA
DOES
None
USAGE
This section describes the end-user interface of this class.
Configuration options
title
String titleThe title of the test suite
disableCaching
Boolean disableCachingThe sign whether the harness should surpress the browser caching for each loading operation. Defaults to 'true'
preload
Object[] preloadThe array which contain information about which files should be preloaded into each test's scope. The folloing rules applies during processing of the array:
- All string entries starting with '+' are replaced with corresponding components sequence.
- If the string entry represent a class name (for example : Test.Run.Test) it is converting to the url, like "../lib/Test/Run/Test.js"
- If the string entry ends with ".js", its supposed to be the url and is passing without modifications.
- If the entry is an Object with
textproperty, then the value of that property will be evaluted in the test's global scope directly.
Using Components.js
Components.js is a file, which is intended to provide some basic bundling of individual files in your distribution. The purpose of this bundling is mostly to optimize the loading time of the test suites, though its also useful for other things.
Here is an example of this file:
COMPONENTS = {
"Core" : [
"Test.Run.Result",
"Test.Run.Result.Diagnostic",
"Test.Run.Result.Assertion",
"Test.Run.Test",
"Test.Run.Test.More",
"Test.Run.Test.Browser",
"Test.Run.Harness",
"Test.Run.Harness.Browser",
"Test.Run.Harness.Browser.Single",
"Test.Run.Harness.Browser.Proven",
"Test.Run"
],
"ExtJS" : [
"+Core",
"Test.Run.Harness.Browser.UI.Viewport",
"Test.Run.Harness.Browser.UI.TestGrid",
"Test.Run.Harness.Browser.UI.AssertionGrid",
"Test.Run.Harness.Browser.Multi"
]
}
In this file, we assign the components description to global COMPONENTS variable. This variable will be picked up by harness,
and parts of components definition can be used in preload configuration option.
The 1st level entries in the COMPONENTS variable will be the components sequences. They can have arbitrary names, usually corresponding
to some deployment blocks of your distribution.
The 2nd level entries should be the arrays with packages names. Or, instead of package, another components sequence can be included, specified with
leading + sign.
For additional details on using Components.js file please refer to action task of Module::Build::JSAN::Installable
ATTRIBUTES
This information is intended mostly for authoring Test.Run extensions, and not for end-users.
testClass
Class testClassThe test class which will be used for running tests, defaults to Test.Run.Test.Browser.
isIE
Boolean isIE'true' if running under MS IE.
baseUrl
String baseUrlBase url of harness file.
baseHost
String baseHostHost of the baseUrl.
baseProtocol
String baseProtocolProtocol of the baseUrl.
METHODS
resolveUrl
String resolveUrl(String url, Boolean onlyResolve)This method returns the absolutized URL. "Absolutization" is performing against baseUrl property. As an addition, if 'onlyResolve' parameter is 'false' or not passed and disableCaching is set to 'true', the url will be appended with 'disableCaching' query parameter.
url - The url to resolve
onlyResolve - The sign, whether the url should be strictly only absolutized and not appended with disableCaching parameter
customizeIframe
void customizeIframe(DOMObject iframe)This method is called before appending the created iframe into DOM. Its intented to be overloaded in custom classes and performing additional customization of the iframe.
iframe - The iframe being customizing
SEE ALSO
Web page of this module: http://github.com/SamuraiJack/Module-Stub/
General documentation for Joose: http://openjsan.org/go/?l=Joose
BUGS
All complex software has bugs lurking in it, and this module is no exception.
Please report any bugs through the web interface at http://github.com/SamuraiJack/Module-Stub/issues
AUTHORS
Nickolay Platonov nplatonov@cpan.org
COPYRIGHT AND LICENSE
Copyright (c) 2009, Nickolay Platonov
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of Nickolay Platonov nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Class('Test.Run.Harness.Browser', {
isa : Test.Run.Harness,
my : {
have : {
testClass : Test.Run.Test.Browser,
title : null,
disableCaching : true,
isIE : /msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent),
baseUrl : window.location.href.replace(/\?.*$/,'').replace(/\/[^/]*$/, '/'),
baseHost : window.location.host,
baseProtocol : window.location.protocol,
preload : null,
componentsExpanded : false,
keepWindows : false
},
after : {
reRunTest : function (test) {
this.processUrl(test.url)
},
testEnd : function (test) {
var url = this.urls[test.url]
if (!this.keepWindows && !url.iframe) url.topScope.close()
}
},
before : {
recordTest : function (test) {
Joose.O.each(this.urls, function (value, url) {
if (value.topScope == test.topScope) test.url = url
})
},
start : function () {
if (!this.preload && window.COMPONENTS) this.setPreloadFromTaskName('core')
this.preload = this.expandPreload(this.preload)
},
configure : function (config) {
var browser = /browser=(.+)/.exec(window.location.search)
if (browser) {
Test.Run.Harness.Browser.meta.extend({
does : [ Test.Run.Harness.Browser.Proven ]
});
config.browser = browser[1]
}
if (typeof config.preload == 'string') {
this.setPreloadFromTaskName(config.preload)
delete config.preload
}
},
removeTest : function (test) {
var url = this.urls[test.url]
var iframe = url.iframe
if (iframe) {
//XXX cleanup 'load' listeners of iframe here
document.body.removeChild(iframe)
}
}
},
methods : {
expandComponents : function () {
if (this.componentsExpanded || !window.COMPONENTS) return
Joose.O.each(COMPONENTS, function (components, taskName) {
var res = []
Joose.A.each(components, function (comp) {
var match = /^\+(.+)/.exec(comp)
res = res.concat(match ? COMPONENTS[match[1]] : comp)
})
COMPONENTS[taskName] = res
})
this.componentsExpanded = true
},
setPreloadFromTaskName : function (taskName) {
this.preload = COMPONENTS[taskName]
},
expandPreload : function (preload) {
this.expandComponents()
var expanded = []
Joose.A.each(preload || [], function (comp) {
var match
if (typeof comp == 'object' || /\.js$/.test(comp))
expanded.push(comp)
else if (match = /^\+(.+)/.exec(comp))
expanded = expanded.concat(this.expandPreload(COMPONENTS[match[1]]))
else
expanded.push('../lib/' + comp.split('.').join('/') + '.js')
}, this)
return expanded
},
//webkit bug - base urls for iframes are broken
//https://bugs.webkit.org/show_bug.cgi?id=13364
resolveUrl : function (url, onlyResolve) {
var resolved
if (!/^http/.test(url))
if (!/^\//.test(url))
resolved = this.baseUrl + url
else
resolved = this.baseProtocol + '//' + this.baseHost + url
if (this.disableCaching && !onlyResolve) resolved += '?disableCaching=' + new Date().getTime()
return resolved
},
processUrl : function (testDescriptor) {
if (typeof testDescriptor == 'string')
testDescriptor = {
url : testDescriptor,
target : 'iframe'
}
var url = testDescriptor.url
var target = testDescriptor.target || 'iframe'
this.SUPER(url)
if (target == 'iframe')
this.processIframeTarget(url)
else
this.processWindowTarget(url)
},
processWindowTarget : function (url) {
var resolved = this.resolveUrl(url)
var me = this
var popup
__ON_STUB_LOADED__ = function () {
me.setupWindow(popup, resolved)
}
// 'resolveUrl' for stubs to disable synchronous loading in IE, which fires __ON_STUB_LOADED__
// even before assignment to 'popup' variable
var src = /\.html$/.test(url) ? resolved : this.resolveUrl('/jsan/Test/Run/static/stub-window.html')
popup = window.open(src, '_blank')
if (!popup) {
alert('Enable popups for the host with this test suite running')
throw 'Enable popups for the host with this test suite running'
}
//
//
// //ugh, some day we all will be happy )
//
// //IE
// if (popup.attachEvent)
// popup.attachEvent('onload', onload)
// else
// //chrom or safari (c) from http://rhio.tistory.com/tag/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%20%EC%8A%A4%EB%8B%88%ED%95%91
// //https://bugs.webkit.org/show_bug.cgi?id=28716
// if (/source/.test((/a/.toString + '')) || /a/.__proto__ == '//') {
// var checkPopupLoaded = function () {
// try {
// popup.document.body
//
// onload()
// } catch (e) {
// setTimeout(checkPopupLoaded, 50)
// }
// }
//
// checkPopupLoaded()
// } else
// //FF
// popup.onload = onload
this.urls[url].topScope = popup
},
processIframeTarget : function (url) {
var me = this
var resolved = this.resolveUrl(url)
var iframe = document.createElement('iframe')
var onload = function () {
me.setupWindow(iframe.contentWindow, resolved)
}
if (iframe.attachEvent)
iframe.attachEvent('onload', onload)
else
iframe.onload = onload
iframe.src = /\.html$/.test(url) ? resolved : '/jsan/Test/Run/static/stub.html'
this.customizeIframe(iframe)
document.body.appendChild(iframe)
this.urls[url].topScope = iframe.contentWindow
this.urls[url].iframe = iframe
},
customizeIframe : function (iframe) {
},
setupWindow : function (windowObj, resolvedUrl) {
var urlCopy = resolvedUrl.replace(/\?.*$/,'')
if (/\.js$/.test(urlCopy)) this.finalizeUrl(windowObj, resolvedUrl)
},
finalizeUrl : function (windowObj, resolvedUrl) {
var documentObj = windowObj.document
var bodyObj = documentObj.body
var scriptTags = []
if (this.preload) Joose.A.each(this.preload, function (preloadUrl) {
if (typeof preloadUrl == 'object') {
scriptTags.push([ preloadUrl.text, null, null, documentObj ])
} else {
preloadUrl = this.resolveUrl(preloadUrl)
scriptTags.push([ null, preloadUrl, null, documentObj ])
}
}, this)
scriptTags.push([
'StartTest = function(testFunc, testClass) { (window.parent.Test || window.opener.Test).Run.my.start(testFunc, testClass, this) }; ' +
'__EXCEPTION_CATCHER__ = function (func) { var ex; try { func() } catch (e) { ex = e; }; return ex; };',
null,
null,
documentObj
])
scriptTags.push([ null, resolvedUrl, null, documentObj ])
this.processScripTagQueue(bodyObj, scriptTags)
},
processScripTagQueue : function (container, queue) {
if (queue.length) {
var params = queue.shift()
//no text - loading via 'src'
if (!params[0]) {
var me = this
params[2] = function () {
me.processScripTagQueue(container, queue)
}
}
container.appendChild(this.createScriptTag.apply(this, params))
//have text - need to process further manually
if (params[0]) this.processScripTagQueue(container, queue)
}
},
createScriptTag : function (text, url, onload, doc) {
var node = (doc || document).createElement("script")
node.setAttribute("type", "text/javascript")
if (url) node.setAttribute("src", url)
if (text) node.text = text
if (onload) node.onload = node.onreadystatechange = function() {
if (!node.readyState || node.readyState == "loaded" || node.readyState == "complete" || node.readyState == 4 && node.status == 200)
//surely for IE6..
setTimeout(onload, 1)
}
return node
}
}
}
//eof my
})
//eof Test.Run.Harness.Browser
/**
Name
====
Test.Run.Harness.Browser - Abstract base class for test harness
SYNOPSIS
========
Test.Run.Harness.Browser.Multi.my.configure({
title : 'Module.Stub Test Suite',
passThroughEx : true,
preload : [
'/jsan/Task/Joose/Core.js',
"/jsan/JooseX/SimpleRequest.js",
'/jsan/Task/JooseX/Namespace/Depended/Web.js',
{
text : "JooseX.Namespace.Depended.Manager.my.INC = " + Ext.encode(INC)
}
]
})
Test.Run.Harness.Browser.Multi.my.start(
'010_sanity.t.js',
'020_basics.t.js'
)
DESCRIPTION
===========
`Test.Run.Harness.Browser` is an abstract harness class in Test.Run hierarchy, providing the methods for running tests on browser platform.
This class still provides no UI, you should use one of it subclasses, for example [Test.Run.Harness.Browser.Multi]
This class is a pure static class - it defines only static properties. Please refer to [Joose.Manual.Static](http://openjsan.org/go?l=Joose.Manual.Static)
ISA
===
[Test.Run.Harness]
DOES
====
None
USAGE
=====
This section describes the end-user interface of this class.
Configuration options
---------------------
### title
> `String title`
> The title of the test suite
### disableCaching
> `Boolean disableCaching`
> The sign whether the harness should surpress the browser caching for each loading operation. Defaults to 'true'
### preload
> `Object[] preload`
> The array which contain information about which files should be preloaded into each test's scope.
The folloing rules applies during processing of the array:
>1. All string entries starting with '+' are replaced with corresponding [components sequence][Using Components.js].
2. If the string entry represent a class name (for example : Test.Run.Test) it is converting to the url, like "../lib/Test/Run/Test.js"
3. If the string entry ends with ".js", its supposed to be the url and is passing without modifications.
4. If the entry is an Object with `text` property, then the value of that property will be evaluted in the test's global scope directly.
Using `Components.js`
=====================
Components.js is a file, which is intended to provide some basic bundling of individual files in your distribution.
The purpose of this bundling is mostly to optimize the loading time of the test suites, though its also useful
for other things.
Here is an example of this file:
COMPONENTS = {
"Core" : [
"Test.Run.Result",
"Test.Run.Result.Diagnostic",
"Test.Run.Result.Assertion",
"Test.Run.Test",
"Test.Run.Test.More",
"Test.Run.Test.Browser",
"Test.Run.Harness",
"Test.Run.Harness.Browser",
"Test.Run.Harness.Browser.Single",
"Test.Run.Harness.Browser.Proven",
"Test.Run"
],
"ExtJS" : [
"+Core",
"Test.Run.Harness.Browser.UI.Viewport",
"Test.Run.Harness.Browser.UI.TestGrid",
"Test.Run.Harness.Browser.UI.AssertionGrid",
"Test.Run.Harness.Browser.Multi"
]
}
In this file, we assign the components description to global `COMPONENTS` variable. This variable will be picked up by harness,
and parts of components definition can be used in [preload] configuration option.
The 1st level entries in the `COMPONENTS` variable will be the *components sequences*. They can have arbitrary names, usually corresponding
to some deployment blocks of your distribution.
The 2nd level entries should be the arrays with packages names. Or, instead of package, another components sequence can be included, specified with
leading `+` sign.
For additional details on using `Components.js` file please refer to [action `task` of Module::Build::JSAN::Installable](http://search.cpan.org/~nplatonov/Module-Build-JSAN-Installable-0.09/lib/Module/Build/JSAN/Installable.pm)
ATTRIBUTES
==========
This information is intended mostly for authoring Test.Run extensions, and not for end-users.
### testClass
> `Class testClass`
> The test class which will be used for running tests, defaults to [Test.Run.Test.Browser].
### isIE
> `Boolean isIE`
> 'true' if running under MS IE.
### baseUrl
> `String baseUrl`
> Base url of harness file.
### baseHost
> `String baseHost`
> Host of the [baseUrl].
### baseProtocol
> `String baseProtocol`
> Protocol of the [baseUrl].
METHODS
=======
### resolveUrl
> `String resolveUrl(String url, Boolean onlyResolve)`
> This method returns the absolutized URL. "Absolutization" is performing against [baseUrl] property. As an addition, if 'onlyResolve' parameter is 'false' or not passed
and [disableCaching] is set to 'true', the url will be appended with 'disableCaching' query parameter.
> **url** - The url to resolve
> **onlyResolve** - The sign, whether the url should be strictly only absolutized and not appended with disableCaching parameter
### customizeIframe
> `void customizeIframe(DOMObject iframe)`
> This method is called before appending the created iframe into DOM. Its intented to be overloaded in custom classes and performing additional customization of the iframe.
> **iframe** - The iframe being customizing
SEE ALSO
========
Web page of this module: [http://github.com/SamuraiJack/Module-Stub/](http://github.com/SamuraiJack/Module-Stub/)
General documentation for Joose: [http://openjsan.org/go/?l=Joose](http://openjsan.org/go/?l=Joose)
BUGS
====
All complex software has bugs lurking in it, and this module is no exception.
Please report any bugs through the web interface at [http://github.com/SamuraiJack/Module-Stub/issues](http://github.com/SamuraiJack/Module-Stub/issues)
AUTHORS
=======
Nickolay Platonov [nplatonov@cpan.org](mailto:nplatonov@cpan.org)
COPYRIGHT AND LICENSE
=====================
Copyright (c) 2009, Nickolay Platonov
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Nickolay Platonov nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[Test.Run.Harness]: ../Harness.html
[Test.Run.Harness.Browser.Multi]: Browser/Multi.html
[Test.Run.Test]: ../Test.html
[Test.Run.Test.Browser]: ../Test/Browser.html
*/