source/rooibos/TestGroup.bs

namespace rooibos
    class TestGroup
        'test state
        name = "Unnamed Suite"

        testSuite = invalid
        setupFunctionName = invalid
        tearDownFunctionName = invalid
        beforeEachFunctionName = invalid
        afterEachFunctionName = invalid
        isSolo = false
        isLegacy = false
        isIgnored = false
        stats = invalid
        scene = invalid
        lineNumber = 00
        top = invalid
        valid = false
        hasFailures = false
        isNodeTest = false
        nodeName = invalid
        testsData = invalid
        tests = []
        public deferred = invalid

        function new(testSuite as rooibos.BaseTestSuite, data as roAssociativeArray)
            m.testSuite = testSuite
            m.name = data.name
            m.valid = data.valid
            m.hasFailures = testSuite.hasFailures
            m.isSolo = data.isSolo
            m.isIgnored = data.isIgnored
            m.isAsync = data.isAsync
            m.asyncTimeout = data.asyncTimeout
            m.testsData = data.testCases
            m.isNodeTest = testSuite.isNodeTest
            m.nodeName = invalid
            m.setupFunctionName = data.setupFunctionName
            m.tearDownFunctionName = data.tearDownFunctionName
            m.beforeEachFunctionName = data.beforeEachFunctionName
            m.afterEachFunctionName = data.afterEachFunctionName
            m.lineNumber = data.lineNumber

            if m.isNodeTest then
                m.deferred = rooibos.promises.create()
            end if

            m.global = testSuite.global
            m.top = testSuite.top
            m.scene = testSuite.scene
            m.stats = new rooibos.Stats()
        end function

        '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        '++ running
        '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        private currentTestIndex = 0
        'TODO CONVERT THIS TO ASYNC
        function run() as dynamic
            rooibos.common.logTrace(">>>>>>>>>>>>")
            rooibos.common.logTrace("RUNNING TEST GROUP")
            m.testRunner = m.testSuite.testRunner
            m.notifyReportersOnTestGroupBegin()
            if m.testSuite.isNodeTest = true then
                rooibos.common.logTrace("THIS GROUP IS ASYNC")
                m.runAsync()
                return m.deferred
            else
                rooibos.common.logTrace("THIS GROUP IS SYNC")
                m.runSync()
                return true
            end if
        end function

        function runSync()
            isOk = m.runSuiteFunction(m.setupFunctionName, "setup")

            if isOk then
                for each testData in m.testsData
                    test = new rooibos.Test(m, testData)
                    m.tests.push(test)

                    if test.isIgnored then
                        m.notifyReportersOnTestBegin(test)
                        m.testSuite.runTest(test)
                        m.notifyReportersOnTestComplete(test)
                        m.stats.appendTestResult(test.result)
                        continue for
                    end if

                    isOk = m.runSuiteFunction(m.beforeEachFunctionName, "beforeEach", test)

                    if isOk then
                        m.notifyReportersOnTestBegin(test)
                        m.testSuite.runTest(test)
                        m.notifyReportersOnTestComplete(test)
                    end if

                    m.runSuiteFunction(m.afterEachFunctionName, "afterEach", test)

                    m.stats.appendTestResult(test.result)

                    if m.stats.hasFailures and m.testSuite.isFailingFast then
                        rooibos.common.logTrace("Terminating group due to failed test")
                        exit for
                    end if
                end for
            else
                rooibos.common.logError("ERROR running test setup function")
            end if
            m.notifyReportersOnTestGroupComplete()
            m.runSuiteFunction(m.tearDownFunctionName, "tearDown")
        end function


        function runAsync()
            isOk = m.runSuiteFunction(m.setupFunctionName, "setup")
            m.testTimer = createObject("roTimespan")

            if isOk then
                m.testRunner.currentGroup = m
                for each testData in m.testsData
                    test = new rooibos.Test(m, testData)
                    m.tests.push(test)
                end for
                m.currentTestIndex = -1
                m.runNextAsync()
            else
                rooibos.common.logError("ERROR running test setup function")
                m.runSuiteFunction(m.tearDownFunctionName, "tearDown")
            end if
        end function

        private function runNextAsync() as void
            rooibos.common.logTrace("Getting next async test")
            m.currentTestIndex++
            m.currentTest = m.tests[m.currentTestIndex]
            m.testSuite.isDoneCalled = false
            m.testTimer.mark()
            if m.currentTest = invalid then
                rooibos.common.logTrace("All tests are finished")
                m.finishAsyncTests()
            else
                test = m.currentTest
                ' Check to see if the test is ignored or if the suite is timed out
                ' and skip the before and after hooks
                if test.isIgnored or m.testSuite.isSuiteTimedOut() then
                    m.notifyReportersOnTestBegin(test)
                    m.testSuite.runTest(test)
                    m.onAsyncTestComplete()
                    return
                end if

                isOk = m.runSuiteFunction(m.beforeEachFunctionName, "beforeEach", m.currentTest)
                if isOk then
                    m.notifyReportersOnTestBegin(test)

                    m.testSuite.runTest(test)
                    rooibos.common.logDebug("Waiting on deferred test results")

                    rooibos.promises.chain(test.deferred, { self: m, test: test }).then(function(_ as dynamic, context as roAssociativeArray)
                        rooibos.common.logDebug("Promise resolved")
                        context.self.testSuite.done()
                    end function).catch(function(error as dynamic, context as roAssociativeArray)
                        rooibos.common.logDebug("Promise rejected")
                        if rooibos.common.isAssociativeArray(error) and not error.isEmpty() and rooibos.common.isArray(error.backtrace) and not error.backtrace.isEmpty() and rooibos.common.isBoolean(error.rethrown) then
                            context.self.testSuite.failCrash(error)
                        else
                            context.self.testSuite.fail("Test failed due to promise rejection")
                        end if
                        context.self.testSuite.done()
                    end function).finally(function(context as roAssociativeArray)
                        context.self.onAsyncTestComplete()
                    end function)
                else
                    rooibos.common.logTrace("Error running test before each function")
                    m.isTestFailedDueToEarlyExit = true
                    m.finishAsyncTests()
                end if
            end if
        end function

        private function onAsyncTestComplete()
            rooibos.common.logTrace("++ CURRENT TEST COMPLETED")
            m.notifyReportersOnTestComplete(m.currentTest)
            m.runSuiteFunction(m.afterEachFunctionName, "afterEach", m.currentTest)
            m.stats.appendTestResult(m.currentTest.result)

            if m.stats.hasFailures and m.testSuite.isFailingFast then
                rooibos.common.logTrace("Terminating group due to failed test")
                m.isTestFailedDueToEarlyExit = true
                m.finishAsyncTests()
            else
                m.runNextAsync()
            end if

        end function

        private function finishAsyncTests()
            m.runSuiteFunction(m.tearDownFunctionName, "tearDown")
            rooibos.common.logTrace("Indicating test suite is done")
            m.notifyReportersOnTestGroupComplete()
            rooibos.promises.resolve(true, m.deferred)
        end function

        private function runSuiteFunction(methodName as string, defaultMethodName as string, test = invalid as rooibos.Test) as boolean

            if methodName = invalid or methodName = "" then
                methodName = defaultMethodName
            end if
            if m.testSuite.catchCrashes and not m.testSuite.noCatch and not (test <> invalid and test.noCatch) then
                ' Add the users suite functions execution time to the suites current execution time
                timespan = createObject("roTimespan")
                try
                    m.testSuite[methodName]()
                    m.testSuite.currentExecutionTime += timespan.totalMilliseconds()
                    return true
                catch error
                    if test <> invalid then
                        test.result.crash("function " + methodName + "crashed!", error)
                        m.testSuite.currentExecutionTime += timespan.totalMilliseconds()
                    end if
                end try
            else
                timespan = createObject("roTimespan")
                m.testSuite[methodName]()
                m.testSuite.currentExecutionTime += timespan.totalMilliseconds()
                return true
            end if

            return false
        end function

        private function notifyReportersOnTestGroupBegin()
            for each reporter in m.testSuite.testReporters
                if rooibos.common.isFunction(reporter.onTestGroupBegin) then
                    reporter.onTestGroupBegin({ group: m })
                end if
            end for
        end function

        private function notifyReportersOnTestBegin(test as rooibos.Test)
            for each reporter in m.testSuite.testReporters
                if rooibos.common.isFunction(reporter.onTestBegin) then
                    reporter.onTestBegin({ test: test })
                end if
            end for
        end function

        private function notifyReportersOnTestComplete(test as rooibos.Test)
            if test.result.time > 0 then
                ' Add the test execution time to the suites current execution time
                m.testSuite.currentExecutionTime += test.result.time
            end if

            for each reporter in m.testSuite.testReporters
                if rooibos.common.isFunction(reporter.onTestComplete) then
                    reporter.onTestComplete({ test: test })
                end if
            end for
        end function

        private function notifyReportersOnTestGroupComplete()
            for each reporter in m.testSuite.testReporters
                if rooibos.common.isFunction(reporter.onTestGroupComplete) then
                    reporter.onTestGroupComplete({ group: m })
                end if
            end for
        end function
    end class
end namespace