TestRunner.brs

' /**
'  * @module TestRunner
'  */
' /**
'  * @memberof module:TestRunner
'  * @name Rooibos_TestRunner
'  * @function
'  * @description Creates an instance of the test runner
'  * @param {Dynamic} args - contains the application launch args, and other settings required for test execution
'  */
function __Rooibos_TestRunner_builder()
    instance = {}
    instance.new = function(args = {})
        m.RunItGroups = Rooibos_TestRunnerMixin_RunItGroups
        m.RunTestCases = Rooibos_TestRunnerMixin_RunTestCases
        m.testScene = args.testScene
        m.nodeContext = args.nodeContext
        'bs:disable-next-line
        config = RBSFM_getRuntimeConfig()
        if (config = invalid or not Rooibos_Common_isAssociativeArray(config)) then
            print "WARNING : specified config is invalid - using default"
            config = {
                showOnlyFailures: false,
                failFast: false
            }
        end if
        'mix in parsed in args
        if (args.showOnlyFailures <> invalid) then
            config.showOnlyFailures = args.showOnlyFailures = "true"
        end if
        if (args.failFast <> invalid) then
            config.failFast = args.failFast = "true"
        end if
        m.testUtilsDecoratorMethodName = args.testUtilsDecoratorMethodName
        m.config = config
        ' Internal properties
        m.config.testsDirectory = config.testsDirectory
        m.logger = Rooibos_Logger(m.config)
        m.global = args.global
    end function
    ' /**
    '  * @memberof module:TestRunner
    '  * @name Run
    '  * @function
    '  * @instance
    '  * @description Executes all tests for a project, as per the config
    '  */
    instance.run = sub()
        if type(RBSFM_getTestSuitesForProject) <> "Function" then
            print " ERROR! RBSFM_getTestSuitesForProject is not found! That looks like you didn't run the preprocessor as part of your test process. Please refer to the docs."
            return
        end if
        totalStatObj = Rooibos_Stats_createTotalStatistic()
        m.runtimeConfig = Rooibos_UnitTestRuntimeConfig()
        m.runtimeConfig.global = m.global
        totalStatObj.testRunHasFailures = false
        for each metaTestSuite in m.runtimeConfig.suites
            if (m.runtimeConfig.hasSoloTests = true) then
                if (metaTestSuite.hasSoloTests <> true) then
                    if (m.config.logLevel = 2) then
                        print "TestSuite " ; metaTestSuite.name ; " Is filtered because it has no solo tests"
                    end if
                    goto skipSuite
                end if
            else if (m.runtimeConfig.hasSoloSuites) then
                if (metaTestSuite.isSolo <> true) then
                    if (m.config.logLevel = 2) then
                        print "TestSuite " ; metaTestSuite.name ; " Is filtered due to solo flag"
                    end if
                    goto skipSuite
                end if
            end if
            if (metaTestSuite.isIgnored = true) then
                if (m.config.logLevel = 2) then
                    print "Ignoring TestSuite " ; metaTestSuite.name ; " Due to Ignore flag"
                end if
                totalstatobj.ignored++
                totalStatObj.IgnoredTestNames.push("|-" + metaTestSuite.name + " [WHOLE SUITE]")
                goto skipSuite
            end if
            print ""
            print Rooibos_Common_fillText("> SUITE: " + metaTestSuite.name, ">", 80)
            if (metaTestSuite.isNodeTest = true and metaTestSuite.nodeTestFileName <> "") then
                print " +++++RUNNING NODE TEST"
                nodeType = metaTestSuite.nodeTestFileName
                print " node type is " ; nodeType
                node = m.testScene.CallFunc("Rooibos_CreateTestNode", nodeType)
                if (type(node) = "roSGNode" and node.subType() = nodeType) then
                    args = {
                        "metaTestSuite": metaTestSuite,
                        "testUtilsDecoratorMethodName": m.testUtilsDecoratorMethodName,
                        "config": m.config,
                        "runtimeConfig": m.runtimeConfig
                    }
                    nodeStatResults = node.callFunc("Rooibos_RunNodeTests", args)
                    if nodeStatResults <> invalid then
                        Rooibos_Stats_mergeTotalStatistic(totalStatObj, nodeStatResults)
                    else
                        print " ERROR! The node " ; nodeType ; " did not return stats from the Rooibos_RunNodeTests method. This usually means you are not importing rooibosDist.brs, or rooibosFunctionMap.brs. Please refer to : https://github.com/rokucommunity/rooibos/blob/master/docs/index.md#testing-scenegraph-nodes"
                    end if
                    m.testScene.RemoveChild(node)
                else
                    print " ERROR!! - could not create node required to execute tests for " ; metaTestSuite.name
                    print " Node of type " ; nodeType ; " was not found/could not be instantiated"
                end if
            else
                if (metaTestSuite.hasIgnoredTests) then
                    totalStatObj.IgnoredTestNames.push("|-" + metaTestSuite.name)
                end if
                m.RunItGroups(metaTestSuite, totalStatObj, m.testUtilsDecoratorMethodName, m.config, m.runtimeConfig, m.nodeContext)
            end if
            skipSuite:
        end for
        m.logger.PrintStatistic(totalStatObj)
        if Rooibos_Common_isFunction(RBS_ReportCodeCoverage) then
            'bs:disable-next-line
            RBS_ReportCodeCoverage()
            if m.config.printLcov = true then
                Rooibos_Coverage_printLCovInfo()
            end if
        end if
        m.sendHomeKeypress()
    end sub
    instance.sendHomeKeypress = sub()
        ut = CreateObject("roUrlTransfer")
        ut.SetUrl("http://localhost:8060/keypress/Home")
        ut.PostFromString("")
    end sub
    return instance
end function
function Rooibos_TestRunner(args = {})
    instance = __Rooibos_TestRunner_builder()
    instance.new(args)
    return instance
end function

'TODO convert to namespace when Bron fixes bug that does not allow ns resoluiton on assignment
' namespace Rooibos.TestRunnerMixin
sub Rooibos_TestRunnerMixin_RunItGroups(metaTestSuite, totalStatObj, testUtilsDecoratorMethodName, config, runtimeConfig, nodeContext = invalid)
    if (testUtilsDecoratorMethodName <> invalid) then
        testUtilsDecorator = Rooibos_Common_getFunctionBruteForce(testUtilsDecoratorMethodName)
        if (not Rooibos_Common_isFunction(testUtilsDecorator)) then
            print "[ERROR] Test utils decorator method `" ; testUtilsDecoratorMethodName ; "` was not in scope! for testSuite: " + metaTestSuite.name
        end if
    end if
    for each itGroup in metaTestSuite.itGroups
        testSuite = Rooibos_ItGroup_GetRunnableTestSuite(itGroup)
        if (nodeContext <> invalid) then
            testSuite.node = nodeContext
            testSuite.global = nodeContext.global
            testSuite.top = nodeContext.top
        end if
        if (Rooibos_Common_isFunction(testUtilsDecorator)) then
            testUtilsDecorator(testSuite)
        end if
        totalStatObj.Ignored = totalStatObj.Ignored + itGroup.ignoredTestCases.count()
        if (itGroup.isIgnored = true) then
            if (config.logLevel = 2) then
                print "Ignoring itGroup " ; itGroup.name ; " Due to Ignore flag"
            end if
            totalStatObj.ignored = totalStatObj.ignored + itGroup.testCases.count()
            totalStatObj.IgnoredTestNames.push("  |-" + itGroup.name + " [WHOLE GROUP]")
            goto skipItGroup
        else
            if (itGroup.ignoredTestCases.count() > 0) then
                totalStatObj.IgnoredTestNames.push("  |-" + itGroup.name)
                totalStatObj.ignored = totalStatObj.ignored + itGroup.ignoredTestCases.count()
                for each testCase in itGroup.ignoredTestCases
                    if (testcase.isParamTest <> true) then
                        totalStatObj.IgnoredTestNames.push("  | |--" + testCase.name)
                    else if (testcase.paramTestIndex = 0) then
                        testCaseName = testCase.Name
                        if (len(testCaseName) > 1 and right(testCaseName, 1) = "0") then
                            testCaseName = left(testCaseName, len(testCaseName) - 1)
                        end if
                        totalStatObj.IgnoredTestNames.push("  | |--" + testCaseName)
                    end if
                end for
            end if
        end if
        if (runtimeConfig.hasSoloTests) then
            if (itGroup.hasSoloTests <> true) then
                if (config.logLevel = 2) then
                    print "Ignoring itGroup " ; itGroup.name ; " Because it has no solo tests"
                end if
                goto skipItGroup
            end if
        else if (runtimeConfig.hasSoloGroups) then
            if (itGroup.isSolo <> true) then
                goto skipItGroup
            end if
        end if
        if (testSuite.testCases.Count() = 0) then
            if (config.logLevel = 2) then
                print "Ignoring TestSuite " ; itGroup.name ; " - NO TEST CASES"
            end if
            goto skipItGroup
        end if
        print ""
        print Rooibos_Common_fillText("> GROUP: " + itGroup.name, ">", 80)
        if Rooibos_Common_isFunction(testSuite.SetUp) then
            testSuite.SetUp()
        end if
        Rooibos_TestRunnerMixin_RunTestCases(metaTestSuite, itGroup, testSuite, totalStatObj, config, runtimeConfig)
        if Rooibos_Common_isFunction(testSuite.TearDown) then
            testSuite.TearDown()
        end if
        if (totalStatObj.testRunHasFailures = true and config.failFast = true) then
            exit for
        end if
        skipItGroup:
    end for
end sub

sub Rooibos_TestRunnerMixin_RunTestCases(metaTestSuite, itGroup, testSuite, totalStatObj, config, runtimeConfig)
    suiteStatObj = Rooibos_Stats_createSuiteStatistic(itGroup.Name)
    testSuite.global = runtimeConfig.global
    for each testCase in testSuite.testCases
        metaTestCase = itGroup.testCaseLookup[testCase.Name]
        metaTestCase.time = 0
        if (runtimeConfig.hasSoloTests and not metaTestCase.isSolo) then
            goto skipTestCase
        end if
        print ""
        print Rooibos_Common_fillText("> TEST: " + testCase.Name + " ", ">", 80)
        if Rooibos_Common_isFunction(testSuite.beforeEach) then
            testSuite.beforeEach()
        end if
        testTimer = CreateObject("roTimespan")
        testCaseTimer = CreateObject("roTimespan")
        testStatObj = Rooibos_Stats_createTestStatistic(testCase.Name)
        testSuite.testCase = testCase.Func
        testStatObj.filePath = metaTestSuite.filePath
        testStatObj.metaTestCase = metaTestCase
        testSuite.currentResult = Rooibos_UnitTestResult()
        testStatObj.metaTestCase.testResult = testSuite.currentResult
        if (metaTestCase.isParamsValid) then
            if (metaTestCase.isParamTest) then
                testCaseParams = []
                for paramIndex = 0 to metaTestCase.rawParams.count()
                    paramValue = metaTestCase.rawParams[paramIndex]
                    if type(paramValue) = "roString" and len(paramValue) >= 8 and left(paramValue, 8) = "#RBSNode" then
                        nodeType = "ContentNode"
                        paramDirectiveArgs = paramValue.split("|")
                        if paramDirectiveArgs.count() > 1 then
                            nodeType = paramDirectiveArgs[1]
                        end if
                        paramValue = createObject("roSGNode", nodeType)
                    end if
                    testCaseParams.push(paramValue)
                end for
                testCaseTimer.mark()
                'up to 10 param args supported for now
                if (metaTestCase.expectedNumberOfParams = 1) then
                    testSuite.testCase(testCaseParams[0])
                else if (metaTestCase.expectedNumberOfParams = 2) then
                    testSuite.testCase(testCaseParams[0], testCaseParams[1])
                else if (metaTestCase.expectedNumberOfParams = 3) then
                    testSuite.testCase(testCaseParams[0], testCaseParams[1], testCaseParams[2])
                else if (metaTestCase.expectedNumberOfParams = 4) then
                    testSuite.testCase(testCaseParams[0], testCaseParams[1], testCaseParams[2], testCaseParams[3])
                else if (metaTestCase.expectedNumberOfParams = 5) then
                    testSuite.testCase(testCaseParams[0], testCaseParams[1], testCaseParams[2], testCaseParams[3], testCaseParams[4])
                else if (metaTestCase.expectedNumberOfParams = 6) then
                    testSuite.testCase(testCaseParams[0], testCaseParams[1], testCaseParams[2], testCaseParams[3], testCaseParams[4], testCaseParams[5])
                else if (metaTestCase.expectedNumberOfParams = 7) then
                    testSuite.testCase(testCaseParams[0], testCaseParams[1], testCaseParams[2], testCaseParams[3], testCaseParams[4], testCaseParams[5], testCaseParams[6])
                else if (metaTestCase.expectedNumberOfParams = 8) then
                    testSuite.testCase(testCaseParams[0], testCaseParams[1], testCaseParams[2], testCaseParams[3], testCaseParams[4], testCaseParams[5], testCaseParams[6], testCaseParams[7])
                else if (metaTestCase.expectedNumberOfParams = 9) then
                    testSuite.testCase(testCaseParams[0], testCaseParams[1], testCaseParams[2], testCaseParams[3], testCaseParams[4], testCaseParams[5], testCaseParams[6], testCaseParams[7], testCaseParams[8])
                else if (metaTestCase.expectedNumberOfParams = 10) then
                    testSuite.testCase(testCaseParams[0], testCaseParams[1], testCaseParams[2], testCaseParams[3], testCaseParams[4], testCaseParams[5], testCaseParams[6], testCaseParams[7], testCaseParams[8], testCaseParams[9])
                else if (metaTestCase.expectedNumberOfParams = 11) then
                    testSuite.testCase(testCaseParams[0], testCaseParams[1], testCaseParams[2], testCaseParams[3], testCaseParams[4], testCaseParams[5], testCaseParams[6], testCaseParams[7], testCaseParams[8], testCaseParams[9], testCaseParams[10])
                else if (metaTestCase.expectedNumberOfParams = 12) then
                    testSuite.testCase(testCaseParams[0], testCaseParams[1], testCaseParams[2], testCaseParams[3], testCaseParams[4], testCaseParams[5], testCaseParams[6], testCaseParams[7], testCaseParams[8], testCaseParams[9], testCaseParams[10], testCaseParams[11])
                else if (metaTestCase.expectedNumberOfParams > 12) then
                    testSuite.fail("Test case had more than 12 params. Max of 12 params is supported")
                end if
                metaTestCase.time = testCaseTimer.totalMilliseconds()
            else
                testCaseTimer.mark()
                testSuite.testCase()
                metaTestCase.time = testCaseTimer.totalMilliseconds()
            end if
        else
            testSuite.Fail("Could not parse args for test ")
        end if
        if testSuite.isAutoAssertingMocks = true then
            testSuite.AssertMocks()
            testSuite.CleanMocks()
            testSuite.CleanStubs()
        end if
        runResult = testSuite.currentResult.getResult()
        if runResult <> "" then
            testStatObj.result = "Fail"
            testStatObj.error.Code = 1
            testStatObj.error.Message = runResult
        else
            testStatObj.result = "Success"
        end if
        testStatObj.time = testTimer.totalMilliseconds()
        Rooibos_Stats_appendTestStatistic(suiteStatObj, testStatObj)
        if Rooibos_Common_isFunction(testSuite.afterEach) then
            testSuite.afterEach()
        end if
        if testStatObj.result <> "Success" then
            totalStatObj.testRunHasFailures = true
        end if
        if testStatObj.result = "Fail" and config.failFast = true then
            exit for
        end if
        skipTestCase:
    end for
    suiteStatObj.metaTestSuite = metaTestSuite
    Rooibos_Stats_appendSuiteStatistic(totalStatObj, suiteStatObj)
end sub
' end namespace