Nickolay - Test.Run-0.04

Documentation | Source
Class('Test.Run.Result', {
    
    have : {
        description : null
    },
    
    
    methods : {
        
        toString : function () {
            return this.description
        }
        
    }
        
})
//eof Test.Run.Result


/**

Name
====


Test.Run.Result - Abstract base class for individual result in test file



DESCRIPTION
===========

`Test.Run.Result` is an abstract base class, representint individual result in test file. 
Its not supposed to be created manually, instead, the various methods of [Test.Run.Test] will create it for you.


ISA
===

[Joose.Meta.Object](http://openjsan.org/go?l=Joose.Meta.Object)


DOES
====

None


ATTRIBUTES
==========

### description

> `String description`

> Description of the result



METHODS
=======

### toString

> `String toString()`

> *return* string presentation of this result. Defaults to value of [description] property 



SEE ALSO
========

[Test.Run.Result.Diagnostic]

[Test.Run.Result.Assertion]

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.Test]: Test.html
[Test.Run.Result.Diagnostic]: Result/Diagnostic.html
[Test.Run.Result.Assertion]: Result/Assertion.html

*/;
Class('Test.Run.Result.Diagnostic', {
    
    isa : Test.Run.Result
    
})


/**

Name
====


Test.Run.Result.Diagnostic - Subclass of [Test.Run.Result] representing a result of diagnostic message


DESCRIPTION
===========

`Test.Run.Result.Diagnostic` is not supposed to be created manually, instead, the `diag` method of [Test.Run.Test] will create it for you.


ISA
===

[Test.Run.Result]


DOES
====

None



SEE ALSO
========

[Test.Run.Result.Assertion]

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.Test]: ../Test.html
[Test.Run.Result]: ../Result.html
[Test.Run.Result.Assertion]: Assertion.html

*/
;
Class('Test.Run.Result.Assertion', {
    
    isa : Test.Run.Result,
    

    have : {
        pass : null,
        
        indx : null
    },
    
    
    methods : {
        
        toString : function () {
            return (this.pass ? 'ok' : 'not ok') + ' ' + this.indx + ' - ' + this.description
        }
        
    }
        
})


/**

Name
====


Test.Run.Result.Assertion - Subclass of [Test.Run.Result] representing a result of assertion check


DESCRIPTION
===========

`Test.Run.Result.Assertion` is not supposed to be created manually, instead, the various methods of [Test.Run.Test] will create it for you.


ISA
===

[Test.Run.Result]


DOES
====

None


ATTRIBUTES
==========

### pass

> `Boolean pass`

> Pass/fail sign



### indx

> `Number indx`

> The index of this result in the results queue


METHODS
=======

### toString

> `String toString()`

> *return* TAP presentation of the assertion 



SEE ALSO
========

[Test.Run.Result.Diagnostic]

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.Test]: ../Test.html
[Test.Run.Result]: ../Result.html
[Test.Run.Result.Diagnostic]: Diagnostic.html

*/
;
Class('Test.Run.Test', {
    
    have : {
        
        url : null,
        
        assertPlanned : null,
        assertCount : 0,
        
        diagCount : 0,
        
        passCount : 0,
        failCount : 0,
        
        results : null,
        
        run : null,
        
        harness : null,
        
        failed : false,
        failedException : null,
        
        startDate : null,
        execEnd : null,
        
        topScope : null,
        
        passThroughEx : false
    },
    
    
    after : {
    
        initialize : function (config) {
//            if (typeof this.run != 'function') throw "The body of test absent"
            
            this.results = []
        }
        
    },
    
    
    methods : {
        
        toString : function() {
            return this.url
        },
        
        
        plan : function (value) {
            if (this.assertPlanned != null) throw "Test plan can't be changed"
            
            this.assertPlanned = value; 
        },
        
        
        addResult : function (result) {
            if (this.assertPlanned == null && this.assertCount) throw "Plan wasn't setuped"
            
            this.results.push(result)
            
            this.harness.testUpdate(this, result)
        },
        
        
        diag : function (desc) {
            this.diagCount++
            
            this.addResult(new Test.Run.Result.Diagnostic({
                description : desc
            }))
        },
        
        
        //XXX pass&fail should be more flexible and overridable (for TODO, SKIP, etc)
        pass : function (desc) {
            this.passCount++
            
            this.addResult(new Test.Run.Result.Assertion({
                pass : true,
                
                description : desc,
                
                indx : ++this.assertCount
            }))
        },
        
        
        fail : function (desc) {
            this.failCount++
            
            this.addResult(new Test.Run.Result.Assertion({
                pass : false,
                
                description : desc,
                
                indx : ++this.assertCount
            }))
        },
        
        
        ok : function (condition, desc) {
            if (condition) this.pass(desc); else this.fail(desc)
        },
        
        
        is : function (got, expected, desc) {
            this.ok(got == expected, desc)
        },

        
        start : function () {
            this.startDate = new Date()
            
            this.harness.testStart(this)
            
            var me = this
            var run = this.run
            
            if (this.passThroughEx)
                run(me)
            else 
                var e = this.topScope.__EXCEPTION_CATCHER__(function(){
                    run(me)
                })
            
            if (e) {
                this.failed = true
                this.failedException = e
                
                this.harness.testFail(this, e)
                
                this.finalize()
                
//                if (this.passThroughEx) throw e
                
                return
            } 
            
            this.finalize()
        },
        
        
        finalize : function () {
            this.execEnd = new Date()
            
            this.harness.testEnd(this)
        },
        
        
        getSummaryMessage : function () {
            var res = []
            
            var passCount = this.passCount
            var failCount = this.failCount
            
            res.push('Passed: ' + passCount)
            res.push('Failed: ' + failCount)
            
            if (!this.failed) {
                if (failCount + passCount < this.assertPlanned) res.push('Looks like you planned ' + this.assertPlanned + ' tests, but ran only ' +  (failCount + passCount))
                if (failCount + passCount > this.assertPlanned) 
                    res.push('Looks like you planned ' + this.assertPlanned + ' tests, but ran ' +  (failCount + passCount - this.assertPlanned) + ' extra tests, ' + (failCount + passCount) + ' total.')
                
                if (passCount == this.assertPlanned && !failCount) res.push('All tests successfull')
            } else {
                res.push('Test suite threw an exception: ' + this.failedException)
            }
            
            return res
        },
        
        
        isPassed : function () {
            return !this.failed && !this.failCount && this.passCount >= this.assertPlanned
        }
        
    }
        
})
//eof Test.Run.Test


/**

Name
====


Test.Run.Test - Base class for individual test file


SYNOPSIS
========

        t.plan(1)
       
       
        t.ok(1 == 1, 'Indeed')
        t.is(2 * 2, '4', 'Indeed')
        
        t.pass('Some assertion is correct')
        


DESCRIPTION
===========

`Test.Run.Test` is a base testing class in Test.Run hierarchy. Its not supposed to be created manually, instead, the harness will create it for you.

This is a very base class, determining mostly the interface for the test object, its subclasses implements more useful assertion checks.
See [Test.Run.Test.Browser] and [Test.Run.Test.More] 


ISA
===

[Joose.Meta.Object](http://openjsan.org/go?l=Joose.Meta.Object)


DOES
====

[Test.Run.Test.More]


USAGE
=====

Below is the list of methods, intended for usage in the individual tests.


### plan

> `void plan(Number tests)`

> This method setups test's plan. It should be called before any assertions were checked. 

> **tests** - a number of planned assertions in this test file.


### diag

> `void diag(String text)`

> This method add the diagnostic message into results queue. The actual presentation logic of the message is delegated to harness.

> **text** - The text of diagnostic message


### pass

> `void pass(String text)`

> This method add the passed assertion into results queue. The actual presentation logic of the passed assertion is delegated to harness.

> **text** - The text of the assertion


### fail

> `void fail(String text)`

> This method add the failed assertion into results queue. The actual presentation logic of the passed assertion is delegated to harness.

> **text** - The text of the assertion


### ok

> `void ok(Boolean condition, String text)`

> This method add the passed or failed assertion into results queue. The type of assertion to add is determined from 1st argument.

> **condition** - The boolean condition, indicating wheter assertions is passed or failed

> **text** - The text of the assertion

        
### is

> `void is(Object value1, Object value2, String text)`

> This method add the passed or failed assertion into results queue. The type of assertion to add is determined from comparison of 1st and 2nd arguments.
Comparison is performed with '==' operator

> **value1** - The 1st value for comparison

> **value2** - The 2nd value for comparison

> **text** - The text of the assertion



ATTRIBUTES
==========

### url

> `String url`

> Url of the test file.


### assertPlanned

> `Number assertPlanned`  

> Planned number of assertions to test.


### assertCount

> `Number assertCount` 

> Current number of processed assertions (not includes the diagnostic messages).
        

### diagCount

> `Number diagCount` 

> Current number of processed diagnostic messages.
        

### passCount

> `Number passCount` 

> Current number of passed assertions.


### failCount

> `Number failCount` 

> Current number of failed assertions.

        
### results

> `Test.Run.Result[] results` 

> Array of [Test.Run.Result] instances (represent either assertion or diagnostic message).

        
### run

> `Function  run` 

> The function, which contain test statements (is invoking with `StartTest` and will receive the instance of this class (or subclass) as a single argument)
        
        
### harness

> `Test.Run.Harness  harness`

> Reference to [Test.Run.Harness] (or subclass) instance, which represents the harness, under which the test is running.


#### failed

> `Boolean failed`

> The sign whether the test has threw an exception. 


### failedException

> `Object failedException`

> Thrown exception (see also [failed]) 


### startDate

> `Date startDate` 

> Timestamp of test start
        

### execEnd

> `Date execEnd` 

> Timestamp of test end

        
### topScope

> `Object topScope` 

> The top scope object in which test function was declared (an iframe or window in which test is running) 


### passThroughEx

> `Boolean passThroughEx`

> The sign whether the test should re-throw any exceptions caught (useful for debugging with FireBug). Defaults to 'false'



METHODS
=======

### toString

> `String toString()`

> Return string presentation of this test. Defaults to value of 'url' property 


### addResult

> `void addResult(Test.Run.Result result)`

> This method adds the instance of [Test.Run.Result] to results queue. The result can be either assertion ([Test.Run.Result.Assertion]) or diagnostic message ([Test.Run.Result.Diagnostic])

> **result** - The result instance to add


### start

> `protected void start()`

> This method starts the test execution. Execution is performing by running the function in the 'run' attribute.


### finalize

> `protected void finalize()`

> This method is called when the test finished execution. It setup the 'execEnd' attribute
        

### getSummaryMessage

> `protected String[] getSummaryMessage()`

> This method returns the summary message for this test. Should be called after the test has finished execution.

        

### isPassed

> `protected Boolean isPassed()`

> This method returns the sign whether the whole test passed or failed. The test is considered passed, when a) no exceptions were thrown,
b) there is no failing assertions c) the number of passed assertions is greater or equal to planned assertions number.



SEE ALSO
========

Subclass of this class, intended to run on browser platform: [Test.Run.Test.Browser]

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.Result]: Result.html
[Test.Run.Harness]: Harness.html
[Test.Run.Test.Browser]: Test/Browser.html
[Test.Run.Test.More]: Test/More.html

*/;
Role('Test.Run.Test.More', {
    
    methods : {
        
        like : function (string, regex, desc) {
            if (regex instanceof RegExp) 
                this.ok(string.match(regex), desc)
            else
                this.ok(string.indexOf(regex) != -1, desc)
        },
        
        
        unlike : function(string, regex, desc) {
            if (regex instanceof RegExp) 
                this.ok(!string.match(regex), desc)
            else
                this.ok(string.indexOf(regex) == -1, desc)
        },        
        
        
        throws_ok : function(func, expected, desc) {
            if (typeof func != 'function') throw 'throws_ok accepts a function as 1st argument'
            
            var e = this.topScope.__EXCEPTION_CATCHER__(func)
            
            if (e instanceof this.topScope.Error)
                //IE uses non-standard 'description' property for error msg
                e = e.message || e.description
            
            this.like('' + e, expected, desc + ' (got [' + e + '], expected [' + expected + '])')
        }    
        
    }
        
})
//eof Test.Run.Test.More

Test.Run.Test.meta.extend({
    does : [ Test.Run.Test.More ]
})


/**

Name
====

Test.Run.Test.More - A role with additional assertion check, which can be used in tests.



SYNOPSIS
========

            t.plan(3)
            
            t.diag('Starting..')
            
            t.like('this', /this/, 'Probably similar things')
            
            t.unlike('this', 'that', 'Unlikely..')
            
            t.throws_ok(function(){
                throw "Ups"
            }, /ups/i, 'Ups was thrown correctly :)')
        


DESCRIPTION
===========

`Test.Run.Test.More` is a role, consumed by the [Test.Run.Test] and providing some general-purpose (platform independed) assertions checks.


DOES
====

None


USAGE
=====

Below is the list of methods, intended for usage in the individual tests.


### like

> `void like(String str, String|RegExp regex, text)`

> This method add the passed or failed assertion into results queue. The type of assertion to add is determined from testing the passed string (1st argument)
against a regular expression  (2nd argument)

> **str** - The string to test

> **regex** - The regex against which to test the string, can be also a plain string

> **text** - The text of the assertion


### unlike

> `void unlike(String str, String|RegExp regex, text)`

> This method is the opposite of 'like', it adds failed assertion, when the string matches the passed regex.

> **str** - The string to test

> **regex** - The regex against which to test the string, can be also a plain string

> **text** - The text of the assertion



### throws_ok

> `void throws_ok(Function func, String|RegExp expected, text)`

> This method add the passed or failed assertion into results queue. The type of assertion to add is determined from the following:
The passed 'func' function is executing, and its expected to throw an exception. Then the exception object is stringified and passed to 'like' method along with 'expected' parameter.  

> **func** - The function which supposed to throw an exception

> **expected** - The regex against which to test the *stringified* exception, can be also a plain string

> **text** - The text of the assertion


SEE ALSO
========

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.Test]: ../Test.html

*/
;
Class('Test.Run.Test.Browser', {
    
    isa : Test.Run.Test,
    
    have : {
        iframe          : null,
        
        timeoutIds      : null,
        
        finished        : false
    },
    
    
    after : {
        
        initialize : function () {
            this.timeoutIds = {}
        }
        
    },
    
    
    methods : {
        
        beginAsync : function (time) {
            var me = this
            
            var timeoutId = this.topScope.setTimeout(function () {
                me.endAsync(timeoutId)
            }, time || 1e4)
            
            this.timeoutIds[timeoutId] = true
            
            return timeoutId
        },
        
        
        endAsync : function (timeoutId) {
            var counter = 0
            
            if (!timeoutId) Joose.O.each(this.timeoutIds, function (value, name) {
                timeoutId = name
                if (counter++) throw "Calls to endAsync without argument should only be performed if you have single beginAsync statement" 
            })
            
            this.topScope.clearTimeout(timeoutId)
            delete this.timeoutIds[timeoutId]
            
            if (this.finished) this.finalize()
        },
        
        
        finalize : function () {
            this.finished = true
            
            if (!Joose.O.isEmpty(this.timeoutIds)) return
            
            this.SUPER()
        }
        
    }
    
})
//eof Test.Run.Test.Browser


/**

Name
====

Test.Run.Test.Browser - Base class for test file, running on the browser platform.


SYNOPSIS
========

            t.plan(1)
            
            var async0 = t.beginAsync()
            
            use('Module.Stub', function () {
                
                //=========================================
                t.diag('Sanity')
                
                t.ok(Module.Stub, "Module.Stub is here")
                
                t.endAsync(async0)
            })
        


DESCRIPTION
===========

`Test.Run.Test.Browser` is a base testing class for tests, running on browser. It assumes the presence of `setTimeout/clearTimeout` functions and provides the ability of asynchornous testsing.


ISA
===

[Test.Run.Test]


DOES
====

None


USAGE
=====

Below is the list of methods, intended for usage in the individual tests.


### beginAsync

> `Number beginAsync(Number? maxTime)`

> This method starts the "asynchronous frame". The test will not finished, until the frame will not be finished with [endAsync] call.
[endAsync] will be automatically call after specified `maxtime`.

> **maxTime** - the maximum time (in ms) to wait until explicitly finalize this async frame.

> *return* - The timeoutId, which can be used in [endAsync] call



### endAsync

> `void endAsync(Number timeoutId)`

> This method finalize the "asynchronous frame" started with [beginAsync].

> **timeoutId** - The timeoutId, returned by [beginAsync] call


ATTRIBUTES
==========

### timeoutIds

> `Object timeoutIds`

> The hash which keep the timeout ids, generated by [beginAsync] call



### finished

> `Boolean finished`

> The sign whether this test has finished execution (its 'run' function was executed). Test still may be active, until there are active "asynchronous frames"



SEE ALSO
========

Additional general-purpose assertions checks: [Test.Run.Test.More]

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.Test]: ../Test.html
[Test.Run.Test.More]: More.html


*/
;
Class('Test.Run.Harness', {
    
    my : {
        
        have : {
            testClass       : Test.Run.Test,
            
            tests           : null,
            testsByURL      : null,
            
            passThroughEx   : false,
            
            urls            : null,
            testsGiven      : 0
        },
        
        
        after : {
            
            initialize : function () {
                this.testsByURL = {} 
                this.tests      = []
                this.urls       = {}
            }
        },
        
        
        methods : {
            
            getTest : function (url) {
                return this.testsByURL[url]
            },
            
            
            testUpdate : function (test, result) {
            },
            
            
            testFail : function (test, exception) {
            },
            
            
            testStart : function (test) {
            },
            
            
            testEnd : function (test) {
                if (!this.isRunning()) this.testSuiteEnd()
            },
            
            
            testSuiteEnd : function () {
            },
            
            
            configure : function (config) {
                Joose.O.copy(config, this)
            },
            
            
            startTest : function (func, testClass, topScope) {
                var test = new (testClass || this.testClass)({
                    harness         : this,
                    run             : func,
                    topScope        : topScope,
                    passThroughEx   : this.passThroughEx
                })
                
                this.recordTest(test)
                
                test.start()
            },
            
            
            removeTest : function (test) {
                delete this.testsByURL[test.url]
                this.tests = Joose.A.remove(this.tests, test)
            },
            
            
            reRunTest : function (test) {
                this.removeTest(test)
            },
            
            
            reRunSuite : function () {
                Joose.A.each(this.tests, function (test) {
                    this.reRunTest(test)
                }, this)
            },
            
            
            recordTest : function (test) {
                this.tests.push(test)
                this.testsByURL[test.url] = test
            },
            
            
            start : function () {
                this.testSuiteStart()
                
                this.testsGiven = arguments.length
                
                Joose.A.each(arguments, function (url) {
                    
                    this.processUrl(url)
                    
                }, this)
                
                this.testSuiteProcessed()
            },
            
            
            testSuiteStart : function () {
                Test.Run.my.configure({
                    harness : this
                })
            },
            
            
            testSuiteProcessed : function () {
            },
            
            
            processUrl : function (url) {
                this.urls[url] = {}
            },
            
            
            isPassed : function () {
                var res = true
                
                Joose.A.each(this.tests, function (test) {
                    if (!test.isPassed()) res = false
                })
                
                return res
            },
            
            
            isRunning : function () {
                if (this.testsGiven != this.tests.length) return true
                
                var res = false
                
                Joose.A.each(this.tests, function (test) {
                    if (!test.execEnd) res = true
                })
                
                return res
            }
            
        }
        
    }
    //eof my
})
//eof Test.Run.Harness



/**

Name
====


Test.Run.Harness - 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` is an abstract base harness class in Test.Run hierarchy. This class 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
===

[Joose.Meta.Object](http://openjsan.org/go?l=Joose.Meta.Object)


DOES
====

None


USAGE
=====

This section describes the end-user interface of this class.


Methods
-------

### configure

> `void configure(Object options)`

> This method configure the harness instance. It just copies the passed configuration option into static instance. 

> **options** - configuration options (values of attributes for this class, see below for details)


### start

> `void start(String url1, String url2, ...)`

> This method starts a whole test suite 

> **url1, url2, ...** - the variable number of test files urls


Configuration options
---------------------

### passThroughEx

> `Boolean passThroughEx`

> The sign whether the each test in suite should re-throw any exceptions caught (useful for debugging with FireBug). Defaults to 'false'.
If set to true, exceptions will not be displayed with UI, and should be traced manually.


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].


### tests

> `Test.Run.Test[] tests`

> The array of all test instances in harness.


### testsByURL

> `Test.Run.Test{} testsByURL`

> The hash of all test instances in harness, keyed by [url] attribute.


### urls

> `Object urls`

> Hash of test files, keyed by url. Also contain arbitrary helper info about the tests.



### testsGiven

> `Number testsGiven`

> The number of test files, given in 'start'



METHODS
=======

This information is intended mostly for authoring Test.Run extensions, and not for end-users.


### testUpdate

> `void testUpdate(Test.Run.Test test, Test.Run.Result result)`

> This method is called by the individual tests (instances of [Test.Run.Test]), each time the it has been updated (new result appeared in results queue).
The harness is supposed to provide some UI representation of the update.

> **test** - The test instance which has been updated

> **result** - The results instance which just has been added to the results queue of test


### testFail

> `void testFail(Test.Run.Test test, Object exception)`

> This method is called by the individual test (instances of [Test.Run.Test]), each time it has been failed (the exception was thrown).
The harness is supposed to provide some UI representation of this event.

> **test** - The test instance which has been failed

> **exception** - The exception thrown 



### testStart

> `void testStart(Test.Run.Test test)`

> This method is called by the individual test (instances of [Test.Run.Test]), each time the test in the test suite has been started.
The harness is supposed to provide some UI representation of this event.

> **test** - The test instance which has been failed


### testEnd

> `void testEnd(Test.Run.Test test)`

> This method is called by the individual test (instances of [Test.Run.Test]), each time the test in the test suite has been finished execution.
The harness is supposed to provide some UI representation of this event.

> **test** - The test instance which has been finished



### testSuiteStart

> `void testSuiteStart()`

> This method is called when the whole test suite has been started


### testSuiteEnd

> `void testSuiteEnd()`

> This method is called when the whole test suite has been finished


### testSuiteProcessed

> `void testSuiteProcessed()`

> This method is called when the whole test suite was processed. Note that at this point some tests may still running. See also [testSuiteEnd]



### startTest

> `void startTest(Function func, Class? testClass, Object topScope)`

> This method starts the execution of a single test file.

> **func** - The function, which contain test statements. The instance of test will be passed to it as 1st argument

> **testClass** - The class which should be used for instantiation of test (defaults to value of 'testClass' attribute)

> **topScope** - The top scope in which this test was declared. This arguments is very important, because it allows the test to correctly catch the exceptions.



### recordTest

> `void recordTest(Test.Run.Test test)`

> This method records a test into internal structures of harness.

> **test** - The test instance being recorded



### removeTest

> `void removeTest(Test.Run.Test test)`

> This method removes the test from harness. All necesery cleanup should be done here. 

> **test** - The test instance being removed



### reRunTest

> `void reRunTest(Test.Run.Test test)`

> This method re-runs a single test from harness. Note - passed test instance will be removed and re-created.

> **test** - The test instance being re-ran



### reRunSuite

> `void reRunSuite()`

> This method re-runs a whole test suite.  


### processUrl

> `void processUrl(String url)`

> This method process a single test file url (it should create the global scope for it and launch the code)  

> **test** - The test file url



### isPassed

> `Boolean isPassed()`

> This method return 'true' if all tests in test suite were passed succefully, 'false' otherwise  


### isRunning

> `Boolean isRunning()`

> This method return 'true' if there is any test in test suite which is still running, 'false' otherwise  



SEE ALSO
========

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.Browser.Multi]: Harness/Browser/Multi.html
[Test.Run.Test]: Test.html

*/
;
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

*/
;
Class('Test.Run.Harness.Browser.Single', {
    
    isa : Test.Run.Harness.Browser,
    
    my : {
        
        after : {
        
            testUpdate : function (test, result) {
                this.print(result)
            },
            
            
            testFail : function (test, exception) {
                this.print('Test threw an exception: ' + exception)
            },
            
            
            testEnd : function (test) {
                this.print(test.getSummaryMessage().join('<br>')); 
            }
        },

        
        methods : {
            
            print : function (text) {
                var div = document.createElement('div')
                div.innerHTML = text
                document.body.appendChild(div)
            }
            
        }
        
    }
    //eof my
})
//eof Test.Run.Harness.Browser.Single;
Role('Test.Run.Harness.Browser.Proven', {
    
    my : {
        
        have : {
            browser : null
        },
        
        after : {
            
//          testStart : function (test) {
//          },
//            
//            
//          testUpdate : function (test, result) {
//          },
//            
//            
//          testFail : function (test, exception) {
//          },
//
//            
//          testEnd : function (test) {
//          },
            
            
            testSuiteEnd : function () {
                var req = new JooseX.SimpleRequest()
                var provenUrl = '/proven/' + this.browser + '/' + (this.isPassed() ? 'pass' : 'fail')
                
                try {
                    req.getText(this.resolveUrl(provenUrl))
                } catch (e) {
                }
            }
            
        }
        
    }
    //eof my
})
//eof Test.Run.Harness.Browser.Proven;
Class('Test.Run', {
    
    my : {
        
        have : {
            harness : null
        },
        
        
        methods : {
            
            configure : function (config) {
                Joose.O.copy(config, this)
            },

            
            start : function (testFunc, testClass, topScope) {
                (this.harness || window.parent.Test.Run.my.harness || window.opener.Test.Run.my.harness || Test.Run.Harness.Browser.Single.my).startTest(testFunc, testClass, topScope)
            }
            
        }
        
    }
    //eof my
})
//eof Test.Run


//starter, which also capture the top scope - it will be re-defined in Test.Run.Harness.Browser
StartTest = function (testFunc, testClass) {
    Test.Run.my.start(testFunc, testClass, this)
}

__EXCEPTION_CATCHER__ = function (func) { var ex; try { func() } catch (e) { ex = e; }; return ex; }
;