Nickolay - Test.Run-0.10

Documentation | Source

Name

Test.Run.Test - Class, representing the individual test file

SYNOPSIS

    t.ok(1 == 1, 'Indeed')
    t.is(2 * 2, '4', 'Indeed')

    t.pass('Some assertion is correct')

    t.done()

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.

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. When used, it should be called before any assertions were checked.

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

done

void done()

This method indicates that you've done testing and all assertions have been ran. It should be called after any assertions were checked.

diag

void diag(String text)

This method output the diagnostic message. 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.

text - The description of the assertion

fail

void fail(String text)

This method add the failed assertion into results queue.

text - The description of the assertion

ok

void ok(Boolean condition, String text)

This assertion passes when the supplied condition evalutes to true and fails otherwise.

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

text - The description of the assertion

notOk

void notOk(Boolean condition, String text)

This is a reverse of ok (test passes when condition is false)

is

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

This assertion passes when comparison of 1st and 2nd arguments shows that they are equal. Comparison is performed with '==' operator

value1 - The 1st value for comparison

value2 - The 2nd value for comparison

text - The description of the assertion

isnt

void isnt(Object value1, Object value2, String text)

This method is a reverse of is (passes when the operands are different).

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 called after specified maxtime.

maxTime - the maximum time (in ms) to wait until explicitly finalize this async frame. Default time is 10000 ms.

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

like

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

This assertion passes when the passed str (1st argument) matches to a regular expression regex (2nd argument)

str - The string to test

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

text - The description of the assertion

unlike

void unlike(String str, String|RegExp regex, String 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 description of the assertion

throwsOk

void throwsOk(Function func, String|RegExp expected, String text)

This assertion is passed, when the func function throws the exception during executing, and the stringified exception passes the 'like' assertion (with 'expected' parameter). This method has a synonym: throws_ok

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 description of the assertion

livesOk

void livesOk(Function func, String text)

This assertion passes, when the supplied func function doesn't throw the exception during execution. This method has a synonym: lives_ok

func - The function which supposed to not throw an exception

text - The description of the assertion

isaOk

void isaOk(Object value, Function/String class, String text)

This assertion passes, when the supplied value is the instance of the class. The check is performed with instanceof operator. The class parameter can be supplied as class constructor or as string, representing the class name. In this case the class will eval'ed to receive the class constructor.

This method has a synonym: isa_ok

value - The value to check for 'isa' relationship

class - The class to check for 'isa' relationship with value

text - The description of the assertion

skipIf

void skipIf(Boolean condition, String why, Function code, Number? howMany)

This methods check the supplied condition and if its true then do not executes the supplied code. Instead, it adds howMany pseudo-passed assertions to the test suite. If the condition is false, then it just run the code function.

This method is useful for skipping parts of the test suite, for example if the functionality being tested is not supported on the current platform.

condition - The boolean condition, indicating whether to run or skip the code

why - The reason for the skip

code - A function, wrapping the assertions which needs to be skipped

howMany - Optional. A number of pseudo-passed assertions to add, when skipping real ones. Defaults to 1.

skip

void skip(String why, Function code, Number? howMany)

Unconditional skipIf (always skips the code).

todo

void todo(String why, Function code)

With this method you can mark part of the test suite as "todo", assuming it most probably will fail, but its still worth to try run them.

The supplied code function will be run, it will receive a new test instance as the 1st argument, which must be used for assertions checks (not the primary test instance, received from StartTest).

Assertions, failed inside the code block will be treated by harness normally. Assertions, passed inside the code block will be treated by harness as bonus ones and highlighted.

why - The reason/description for the todo

code - A function, wrapping the "todo" assertions. This function will receive a special test class instance which should be used for assertions checks.

SEE ALSO

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/test.run/issues

AUTHORS

Nickolay Platonov nplatonov@cpan.org

COPYRIGHT AND LICENSE

Copyright (c) 2010, 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.Test', {
    
    does        : [ Test.Run.Test.More ],
    
    
    have : {
        url                 : null,
        
        assertPlanned       : null,
        assertCount         : 0,
        
        results             : null,
        
        run                 : null,
        
        harness             : null,
        
        failed              : false,
        failedException     : null,
        
        startDate           : null,
        endDate             : null,
        
        topScope            : null,
        
        transparentEx       : false,
        passThroughEx       : false,
        
        isDone              : false,
        
        timeoutsCount       : 0,
        timeoutIds          : null,
        processed           : false,
        
        callback            : null
    },
    
    
    after : {
    
        initialize : function (config) {
            if (Object.prototype.toString.call(this.run) != "[object Function]") throw "The body of test absent"
            
            this.results        = []
            this.timeoutIds     = {}
        }
        
    },
    
    
    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.isDone || this.isFinished()) throw "Adding assertions after the test has been already done"
            
            if (result instanceof Test.Run.Result.Assertion) result.index = ++this.assertCount
            
            this.results.push(result)
            
            this.harness.onTestUpdate(this, result)
        },
        
        
        diag : function (desc) {
            this.addResult(new Test.Run.Result.Diagnostic({
                description : desc
            }))
        },
        
        
        pass : function (desc) {
            this.addResult(new Test.Run.Result.Assertion({
                passed      : true,
                
                description : desc
            }))
        },
        
        
        fail : function (desc) {
            this.addResult(new Test.Run.Result.Assertion({
                passed      : false,
                
                description : desc
            }))
        },
        
        
        eachAssertion : function (func, scope) {
            scope       = scope || this
            
            var index   = 0
            
            Joose.A.each(this.results, function (result) {
                
                if (result instanceof Test.Run.Result.Assertion) func.call(scope, result, index++)
            })
        },
        
        
        ok : function (condition, desc) {
            if (condition) 
                this.pass(desc)
            else 
                this.fail(desc)
        },
        
        
        notOk : function (condition, desc) {
            this.ok(!condition, desc)
        },
        
        
        is : function (got, expected, desc) {
            this.ok(got == expected, desc)
        },

        
        isnt : function (got, expected, desc) {
            this.ok(got != expected, desc)
        },
        
        
        beginAsync : function (time) {
            var me = this
            
            // in NodeJS `setTimeout` returns an object and not a simple ID, so we try hard to store that object under unique index
            var timeoutId = this.topScope.setTimeout(function () {
                me.endAsync(timeoutId)
            }, time || 1e4)
            
            var index = this.timeoutsCount++
            
            this.timeoutIds[ index ] = timeoutId
            
            return index
        },
        
        
        endAsync : function (index) {
            var counter = 0
            
            if (index == null) Joose.O.each(this.timeoutIds, function (timeoutId, indx) {
                index = indx
                if (counter++) throw "Calls to endAsync without argument should only be performed if you have single beginAsync statement" 
            })
            
            var timeoutId = this.timeoutIds[ index ]
            
            this.topScope.clearTimeout(timeoutId)
            delete this.timeoutIds[ index ]
            
            var me = this
            
            if (this.processed)
                // to allow potential call to `done` after `endAsync`
                setTimeout(function (){
                    me.finalize()
                }, 1)
        },
        
        
        clearTimeouts : function () {
            var me = this
            
            Joose.O.each(this.timeoutIds, function (value, id) {
                me.topScope.clearTimeout(value)
            })
            
            this.timeoutIds = {}
        },
        
        
        skipIf : function (condition, why, code, howMany) {
            howMany = howMany || 1
            
            if (condition) {
                
                for (var i = 1; i <= howMany; i++) this.addResult(new Test.Run.Result.Assertion({
                    passed      : true,
                    isSkipped   : true,
                    
                    description : 'SKIPPED: ' + why
                }))    
                
            } else
                code()
        },
        
        
        skip : function (why, code, howMany) {
            this.skipIf(true, why, code, howMany)
        },
        
        
        todo : function (why, code) {
            var todo  = new Test.Run.Test.Todo({
                parent  : this,
                run     : function () {}
            })
            
            this.topScope.__EXCEPTION_CATCHER__(function(){
                code(todo)
            })
        },
        
        
        start : function (callback) {
            this.callback   = callback
            this.startDate  = new Date()
            
            this.harness.onTestStart(this)
            
            var me      = this
            var run     = this.run
            
            if (this.transparentEx)
                run(me)
            else 
                var e = this.topScope.__EXCEPTION_CATCHER__(function(){
                    run(me)
                })
            
            if (e) {
                this.failed             = true
                this.failedException    = e
                
                this.harness.onTestFail(this, e)
                
                this.finalize(true)
                
                if (this.passThroughEx) throw e
                
                return
            } 
            
            this.finalize()
        },
        
        
        finalize : function (force) {
            if (this.isFinished()) return
            
            this.processed = true
            
            if (force) this.clearTimeouts()
            
            if (!Joose.O.isEmpty(this.timeoutIds)) return
            
            this.endDate = new Date()
            
            this.harness.onTestEnd(this)
            
            this.callback && this.callback()
        },
        
        
        getSummaryMessage : function (lineBreaks) {
            var res = []
            
            var passCount       = this.getPassCount()
            var failCount       = this.getFailCount()
            var assertPlanned   = this.assertPlanned
            var total           = failCount + passCount
            
            res.push('Passed: ' + passCount)
            res.push('Failed: ' + failCount)
            
            if (!this.failed) {
                if (assertPlanned != null) {
                    if (total < assertPlanned) 
                        res.push('Looks like you planned ' + assertPlanned + ' tests, but ran only ' + total)
                        
                    if (total > assertPlanned) 
                        res.push('Looks like you planned ' + assertPlanned + ' tests, but ran ' +  (total - assertPlanned) + ' extra tests, ' + total + ' total.')
                    
                    if (total == assertPlanned && !failCount) res.push('All tests successfull')
                } else 
                    if (this.isDone && !failCount) res.push('All tests successfull')
                
            } else {
                res.push('Test suite threw an exception: ' + this.failedException)
            }
            
            return res.join(lineBreaks || '')
        },
        
        
        done : function () {
            this.isDone = true
            
            if (this.processed) this.finalize()
        },
        
        
        getPassCount : function () {
            var passCount = 0
            
            this.eachAssertion(function (assertion) {
                if (assertion.passed && !assertion.isTodo) passCount++
            })
            
            return passCount
        },
        
        
        getFailCount : function () {
            var failCount = 0
            
            this.eachAssertion(function (assertion) {
                if (!assertion.passed && !assertion.isTodo) failCount++
            })
            
            return failCount
        },
        
        
        isPassed : function () {
            var passCount       = this.getPassCount()
            var failCount       = this.getFailCount()
            var assertPlanned   = this.assertPlanned
            
            return !this.failed && !failCount && (
                (assertPlanned != null && passCount == assertPlanned)
                    ||
                (assertPlanned == null) && this.isDone
            )
        },
        
        
        isFinished : function () {
            return this.endDate != null
        },
        
        
        getTimeLength : function () {
            return this.endDate - this.startDate
        }
        
    }
        
})
//eof Test.Run.Test


/**

Name
====


Test.Run.Test - Class, representing the individual test file


SYNOPSIS
========

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


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.


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. When used, it should be called before any assertions were checked. 

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


### done

> `void done()`

> This method indicates that you've done testing and all assertions have been ran.
It should be called after any assertions were checked.  



### diag

> `void diag(String text)`

> This method output the diagnostic message. 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. 

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


### fail

> `void fail(String text)`

> This method add the failed assertion into results queue. 

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


### ok

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

> This assertion passes when the supplied `condition` evalutes to `true` and fails otherwise. 

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

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


### notOk

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

> This is a reverse of `ok` (test passes when condition is false) 

        
### is

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

> This assertion passes when comparison of 1st and 2nd arguments shows that they are equal.
Comparison is performed with '==' operator

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

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

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


### isnt

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

> This method is a reverse of `is` (passes when the operands are different).


### 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 called after specified `maxtime`.

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

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


### like

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

> This assertion passes when the passed `str` (1st argument) matches to a regular expression `regex` (2nd argument)

> **str** - The string to test

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

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


### unlike

> `void unlike(String str, String|RegExp regex, String 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 description of the assertion



### throwsOk

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

> This assertion is passed, when the `func` function throws the exception during executing, and the 
stringified exception passes the 'like' assertion (with 'expected' parameter). This method has a synonym: throws_ok

> **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 description of the assertion


### livesOk

> `void livesOk(Function func, String text)`

> This assertion passes, when the supplied `func` function doesn't throw the exception during execution. 
This method has a synonym: lives_ok

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

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


### isaOk

> `void isaOk(Object value, Function/String class, String text)`

> This assertion passes, when the supplied `value` is the instance of the `class`. The check is performed with
`instanceof` operator. The `class` parameter can be supplied as class constructor or as string, representing the class
name. In this case the `class` will eval'ed to receive the class constructor.

This method has a synonym: isa_ok

> **value** - The value to check for 'isa' relationship

> **class** - The class to check for 'isa' relationship with `value`

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


### skipIf

> `void skipIf(Boolean condition, String why, Function code, Number? howMany)`

> This methods check the supplied `condition` and if its *true* then *do not* executes the supplied code.
Instead, it adds `howMany` pseudo-passed assertions to the test suite. If the condition is *false*, then
it just run the `code` function.

This method is useful for skipping parts of the test suite, for example if the functionality being tested
is not supported on the current platform.

> **condition** - The boolean condition, indicating whether to run or skip the `code`

> **why** - The reason for the skip

> **code** - A function, wrapping the assertions which needs to be skipped

> **howMany** - Optional. A number of pseudo-passed assertions to add, when skipping real ones. Defaults to 1.


### skip

> `void skip(String why, Function code, Number? howMany)`

> Unconditional `skipIf` (always skips the code).


### todo

> `void todo(String why, Function code)`

> With this method you can mark part of the test suite as "todo", assuming it most probably will fail,
but its still worth to try run them. 

>The supplied `code` function will be run, it will receive a new test instance as the 1st argument, 
which *must* be used for assertions checks (not the primary test instance, received from `StartTest`). 

>Assertions, failed inside the `code` block will be treated by harness normally.
Assertions, passed inside the `code` block will be treated by harness as bonus ones and highlighted. 

> **why** - The reason/description for the todo

> **code** - A function, wrapping the "todo" assertions. This function will receive a special test class instance
which should be used for assertions checks.



SEE ALSO
========

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/test.run/issues>



AUTHORS
=======

Nickolay Platonov [nplatonov@cpan.org](mailto:nplatonov@cpan.org)



COPYRIGHT AND LICENSE
=====================

Copyright (c) 2010, 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

*/