Nickolay - Test.Run-0.04

Documentation | Source
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

*/