diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts b/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts index 0183f3ec..9d2f9de1 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts @@ -4,6 +4,7 @@ import { expect } from 'chai'; import PluginInterface from 'brighterscript/dist/PluginInterface'; import * as fsExtra from 'fs-extra'; import { RooibosPlugin } from '../../plugin'; +import undent from 'undent'; let tmpPath = s`${process.cwd()}/tmp`; let _rootDir = s`${tmpPath}/rootDir`; @@ -19,9 +20,13 @@ describe('MockUtil', () => { let plugin: RooibosPlugin; let options; - function getContents(filename: string) { + function getContents(filename: string, trim = true) { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - return trimLeading(fsExtra.readFileSync(s`${_stagingFolderPath}/${filename}`).toString()); + let contents = fsExtra.readFileSync(s`${_stagingFolderPath}/${filename}`).toString(); + if (trim) { + return trimLeading(contents); + } + return contents; } describe('MockUtil', () => { @@ -69,29 +74,35 @@ describe('MockUtil', () => { // in `beforeEach`. This is because the compiler normally skips processing .brs files and copies them as-is. it('adds util code to a brs file', async () => { program.setFile('source/code.brs', ` - function sayHello(a1, a2) - print "hello" - end function - `); + function sayHello(a1, a2) + print "hello" + end function + `); program.validate(); expect(program.getDiagnostics()).to.be.empty; await builder.transpile(); - let a = getContents('source/code.brs'); - let b = trimLeading(`function sayHello(a1, a2) - if RBS_SM_1_getMocksByFunctionName()["sayhello"] <> invalid - result = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback(a1,a2) - return result - end if - print "hello" - end function + let a = getContents('source/code.brs', false); + let b = undent(` + function sayHello(a1, a2) + __stubs_globalAa = getGlobalAa() + if RBS_SM_1_getMocksByFunctionName()["sayhello"] <> invalid + __stubOrMockResult = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback(a1, a2) + return __stubOrMockResult + else if type(__stubs_globalAa?.__globalStubs?.sayhello).endsWith("Function") + __stubFunction = __stubs_globalAa.__globalStubs.sayhello + __stubOrMockResult = __stubFunction(a1, a2) + return __stubOrMockResult + end if + print "hello" + end function - function RBS_SM_1_getMocksByFunctionName() - if m._rMocksByFunctionName = invalid - m._rMocksByFunctionName = {} - end if - return m._rMocksByFunctionName - end function -`); + function RBS_SM_1_getMocksByFunctionName() + if m._rMocksByFunctionName = invalid + m._rMocksByFunctionName = {} + end if + return m._rMocksByFunctionName + end function + `); expect(a).to.equal(b); }); @@ -100,165 +111,200 @@ describe('MockUtil', () => { it('enables mocking on global functions', async () => { program.setFile('source/code.bs', ` - function sayHello(a1, a2) - print "hello" - end function - `); + function sayHello(a1, a2) + print "hello" + end function + `); program.validate(); expect(program.getDiagnostics()).to.be.empty; await builder.transpile(); - let a = getContents('source/code.brs'); - let b = trimLeading(`function sayHello(a1, a2) - if RBS_SM_1_getMocksByFunctionName()["sayhello"] <> invalid - result = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback(a1,a2) - return result - end if - print "hello" - end function + let a = getContents('source/code.brs', false); + let b = undent(` + function sayHello(a1, a2) + __stubs_globalAa = getGlobalAa() + if RBS_SM_1_getMocksByFunctionName()["sayhello"] <> invalid + __stubOrMockResult = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback(a1, a2) + return __stubOrMockResult + else if type(__stubs_globalAa?.__globalStubs?.sayhello).endsWith("Function") + __stubFunction = __stubs_globalAa.__globalStubs.sayhello + __stubOrMockResult = __stubFunction(a1, a2) + return __stubOrMockResult + end if + print "hello" + end function - function RBS_SM_1_getMocksByFunctionName() - if m._rMocksByFunctionName = invalid - m._rMocksByFunctionName = {} - end if - return m._rMocksByFunctionName - end function -`); + function RBS_SM_1_getMocksByFunctionName() + if m._rMocksByFunctionName = invalid + m._rMocksByFunctionName = {} + end if + return m._rMocksByFunctionName + end function + `); expect(a).to.equal(b); }); it('weird raletracker task issue I saw', async () => { program.setFile('source/code.bs', ` - Sub RedLines_SetRulerLines(rulerLines) - For Each line In rulerLines.Items() - RedLines_AddLine(line.key, line.value.position, line.value.coords, m.node, m.childMap) - End For - end Sub - Sub RedLines_AddLine(id, position, coords, node, childMap) as Object - line = CreateObject("roSGNode", "Rectangle") - line.setField("id", id) - end sub - `); + Sub RedLines_SetRulerLines(rulerLines) + For Each line In rulerLines.Items() + RedLines_AddLine(line.key, line.value.position, line.value.coords, m.node, m.childMap) + End For + end Sub + Sub RedLines_AddLine(id, position, coords, node, childMap) as Object + line = CreateObject("roSGNode", "Rectangle") + line.setField("id", id) + end sub + `); program.validate(); expect(program.getDiagnostics()).to.be.empty; await builder.transpile(); - let a = getContents('source/code.brs'); - let b = trimLeading(`Sub RedLines_SetRulerLines(rulerLines) - if RBS_SM_1_getMocksByFunctionName()["redlines_setrulerlines"] <> invalid - result = RBS_SM_1_getMocksByFunctionName()["redlines_setrulerlines"].callback(rulerLines) - return - end if - For Each line In rulerLines.Items() - RedLines_AddLine(line.key, line.value.position, line.value.coords, m.node, m.childMap) - End For - end Sub - - Sub RedLines_AddLine(id, position, coords, node, childMap) as Object - if RBS_SM_1_getMocksByFunctionName()["redlines_addline"] <> invalid - result = RBS_SM_1_getMocksByFunctionName()["redlines_addline"].callback(id,position,coords,node,childMap) - return result - end if - line = CreateObject("roSGNode", "Rectangle") - line.setField("id", id) - end sub + let a = getContents('source/code.brs', false); + let b = undent(` + Sub RedLines_SetRulerLines(rulerLines) + __stubs_globalAa = getGlobalAa() + if RBS_SM_1_getMocksByFunctionName()["redlines_setrulerlines"] <> invalid + __stubOrMockResult = RBS_SM_1_getMocksByFunctionName()["redlines_setrulerlines"].callback(rulerLines) + return + else if type(__stubs_globalAa?.__globalStubs?.redlines_setrulerlines).endsWith("Function") + __stubFunction = __stubs_globalAa.__globalStubs.redlines_setrulerlines + __stubOrMockResult = __stubFunction(rulerLines) + return + end if + For Each line In rulerLines.Items() + RedLines_AddLine(line.key, line.value.position, line.value.coords, m.node, m.childMap) + End For + end Sub + + Sub RedLines_AddLine(id, position, coords, node, childMap) as Object + __stubs_globalAa = getGlobalAa() + if RBS_SM_1_getMocksByFunctionName()["redlines_addline"] <> invalid + __stubOrMockResult = RBS_SM_1_getMocksByFunctionName()["redlines_addline"].callback(id, position, coords, node, childMap) + return __stubOrMockResult + else if type(__stubs_globalAa?.__globalStubs?.redlines_addline).endsWith("Function") + __stubFunction = __stubs_globalAa.__globalStubs.redlines_addline + __stubOrMockResult = __stubFunction(id, position, coords, node, childMap) + return __stubOrMockResult + end if + line = CreateObject("roSGNode", "Rectangle") + line.setField("id", id) + end sub - function RBS_SM_1_getMocksByFunctionName() - if m._rMocksByFunctionName = invalid - m._rMocksByFunctionName = {} - end if - return m._rMocksByFunctionName - end function -`); + function RBS_SM_1_getMocksByFunctionName() + if m._rMocksByFunctionName = invalid + m._rMocksByFunctionName = {} + end if + return m._rMocksByFunctionName + end function + `); expect(a).to.equal(b); }); it('enables mocking on global sub', async () => { program.setFile('source/code.bs', ` - sub sayHello(a1, a2) - print "hello" - end sub - `); + sub sayHello(a1, a2) + print "hello" + end sub + `); program.validate(); expect(program.getDiagnostics()).to.be.empty; await builder.transpile(); - let a = getContents('source/code.brs'); - let b = trimLeading(`sub sayHello(a1, a2) - if RBS_SM_1_getMocksByFunctionName()["sayhello"] <> invalid - result = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback(a1,a2) - return - end if - print "hello" - end sub + let a = getContents('source/code.brs', false); + let b = undent(` + sub sayHello(a1, a2) + __stubs_globalAa = getGlobalAa() + if RBS_SM_1_getMocksByFunctionName()["sayhello"] <> invalid + __stubOrMockResult = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback(a1, a2) + return + else if type(__stubs_globalAa?.__globalStubs?.sayhello).endsWith("Function") + __stubFunction = __stubs_globalAa.__globalStubs.sayhello + __stubOrMockResult = __stubFunction(a1, a2) + return + end if + print "hello" + end sub - function RBS_SM_1_getMocksByFunctionName() - if m._rMocksByFunctionName = invalid - m._rMocksByFunctionName = {} - end if - return m._rMocksByFunctionName - end function -`); + function RBS_SM_1_getMocksByFunctionName() + if m._rMocksByFunctionName = invalid + m._rMocksByFunctionName = {} + end if + return m._rMocksByFunctionName + end function + `); expect(a).to.equal(b); }); it('enables mocking on namespaced function', async () => { program.setFile('source/code.bs', ` - namespace person.utils - function sayHello(a1, a2) - print "hello" - end function - end namespace - `); + namespace person.utils + function sayHello(a1, a2) + print "hello" + end function + end namespace + `); program.validate(); expect(program.getDiagnostics()).to.be.empty; await builder.transpile(); - let a = getContents('source/code.brs'); - let b = trimLeading(`function person_utils_sayHello(a1, a2) - if RBS_SM_1_getMocksByFunctionName()["person_utils_sayhello"] <> invalid - result = RBS_SM_1_getMocksByFunctionName()["person_utils_sayhello"].callback(a1,a2) - return result - end if - print "hello" - end function + let a = getContents('source/code.brs', false); + let b = undent(` + function person_utils_sayHello(a1, a2) + __stubs_globalAa = getGlobalAa() + if RBS_SM_1_getMocksByFunctionName()["person_utils_sayhello"] <> invalid + __stubOrMockResult = RBS_SM_1_getMocksByFunctionName()["person_utils_sayhello"].callback(a1, a2) + return __stubOrMockResult + else if type(__stubs_globalAa?.__globalStubs?.person_utils_sayhello).endsWith("Function") + __stubFunction = __stubs_globalAa.__globalStubs.person_utils_sayhello + __stubOrMockResult = __stubFunction(a1, a2) + return __stubOrMockResult + end if + print "hello" + end function - function RBS_SM_1_getMocksByFunctionName() - if m._rMocksByFunctionName = invalid - m._rMocksByFunctionName = {} - end if - return m._rMocksByFunctionName - end function -`); + function RBS_SM_1_getMocksByFunctionName() + if m._rMocksByFunctionName = invalid + m._rMocksByFunctionName = {} + end if + return m._rMocksByFunctionName + end function + `); expect(a).to.equal(b); }); it('enables mocking on namespaced sub', async () => { program.setFile('source/code.bs', ` - namespace person.utils - sub sayHello(a1, a2) - print "hello" - end sub - end namespace - `); + namespace person.utils + sub sayHello(a1, a2) + print "hello" + end sub + end namespace + `); program.validate(); expect(program.getDiagnostics()).to.be.empty; await builder.transpile(); - let a = getContents('source/code.brs'); - let b = trimLeading(`sub person_utils_sayHello(a1, a2) - if RBS_SM_1_getMocksByFunctionName()["person_utils_sayhello"] <> invalid - result = RBS_SM_1_getMocksByFunctionName()["person_utils_sayhello"].callback(a1,a2) - return - end if - print "hello" - end sub + let a = getContents('source/code.brs', false); + let b = undent(` + sub person_utils_sayHello(a1, a2) + __stubs_globalAa = getGlobalAa() + if RBS_SM_1_getMocksByFunctionName()["person_utils_sayhello"] <> invalid + __stubOrMockResult = RBS_SM_1_getMocksByFunctionName()["person_utils_sayhello"].callback(a1, a2) + return + else if type(__stubs_globalAa?.__globalStubs?.person_utils_sayhello).endsWith("Function") + __stubFunction = __stubs_globalAa.__globalStubs.person_utils_sayhello + __stubOrMockResult = __stubFunction(a1, a2) + return + end if + print "hello" + end sub - function RBS_SM_1_getMocksByFunctionName() - if m._rMocksByFunctionName = invalid - m._rMocksByFunctionName = {} - end if - return m._rMocksByFunctionName - end function -`); + function RBS_SM_1_getMocksByFunctionName() + if m._rMocksByFunctionName = invalid + m._rMocksByFunctionName = {} + end if + return m._rMocksByFunctionName + end function + `); expect(a).to.equal(b); }); @@ -311,45 +357,95 @@ describe('MockUtil', () => { program.validate(); expect(program.getDiagnostics()).to.be.empty; await builder.transpile(); - let a = getContents('source/code.brs'); - let b = trimLeading(`function __beings_Person_builder() - instance = {} - instance.new = sub() - end sub - instance.sayHello = sub(a1, a2) - print "hello" - end sub - return instance - end function - function beings_Person() - instance = __beings_Person_builder() - instance.new() - return instance - end function + let a = getContents('source/code.brs', false); + let b = undent(` + function __beings_Person_builder() + instance = {} + instance.new = sub() + end sub + instance.sayHello = sub(a1, a2) + print "hello" + end sub + return instance + end function + function beings_Person() + instance = __beings_Person_builder() + instance.new() + return instance + end function - function beings_sayHello() - if RBS_SM_1_getMocksByFunctionName()["beings_sayhello"] <> invalid - result = RBS_SM_1_getMocksByFunctionName()["beings_sayhello"].callback() - return result - end if - print "hello2" - end function + function beings_sayHello() + __stubs_globalAa = getGlobalAa() + if RBS_SM_1_getMocksByFunctionName()["beings_sayhello"] <> invalid + __stubOrMockResult = RBS_SM_1_getMocksByFunctionName()["beings_sayhello"].callback() + return __stubOrMockResult + else if type(__stubs_globalAa?.__globalStubs?.beings_sayhello).endsWith("Function") + __stubFunction = __stubs_globalAa.__globalStubs.beings_sayhello + __stubOrMockResult = __stubFunction() + return __stubOrMockResult + end if + print "hello2" + end function - function sayHello() - if RBS_SM_1_getMocksByFunctionName()["sayhello"] <> invalid - result = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback() - return result - end if - print "hello3" - end function + function sayHello() + __stubs_globalAa = getGlobalAa() + if RBS_SM_1_getMocksByFunctionName()["sayhello"] <> invalid + __stubOrMockResult = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback() + return __stubOrMockResult + else if type(__stubs_globalAa?.__globalStubs?.sayhello).endsWith("Function") + __stubFunction = __stubs_globalAa.__globalStubs.sayhello + __stubOrMockResult = __stubFunction() + return __stubOrMockResult + end if + print "hello3" + end function - function RBS_SM_1_getMocksByFunctionName() - if m._rMocksByFunctionName = invalid - m._rMocksByFunctionName = {} - end if - return m._rMocksByFunctionName - end function -`); + function RBS_SM_1_getMocksByFunctionName() + if m._rMocksByFunctionName = invalid + m._rMocksByFunctionName = {} + end if + return m._rMocksByFunctionName + end function + `); + expect(a).to.equal(b); + + }); + + it('will skip functions with the disableMocking annotation', async () => { + program.setFile('source/code.bs', ` + @disableMocking + namespace beings + function sayHello() + print "hello2" + end function + end namespace + namespace aliens + @disableMocking + function sayHello() + print "hello3" + end function + end namespace + @disableMocking + function sayHello() + print "hello4" + end function + `); + program.validate(); + expect(program.getDiagnostics()).to.be.empty; + await builder.transpile(); + let a = getContents('source/code.brs', false); + let b = undent(` + function beings_sayHello() + print "hello2" + end function + function aliens_sayHello() + print "hello3" + end function + + function sayHello() + print "hello4" + end function + `); expect(a).to.equal(b); }); diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.ts b/bsc-plugin/src/lib/rooibos/MockUtil.ts index 0af49936..fa66034a 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.ts @@ -1,18 +1,16 @@ /* eslint-disable @typescript-eslint/no-var-requires */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import type { BrsFile, Editor, ProgramBuilder } from 'brighterscript'; -import { Position, isClassStatement } from 'brighterscript'; +import type { BrsFile, Editor, NamespaceStatement, ProgramBuilder, Scope } from 'brighterscript'; +import { ParseMode, Parser, isClassStatement, isNamespaceStatement } from 'brighterscript'; import * as brighterscript from 'brighterscript'; import type { RooibosConfig } from './RooibosConfig'; -import { RawCodeStatement } from './RawCodeStatement'; -import { Range } from 'vscode-languageserver-types'; import type { FileFactory } from './FileFactory'; import undent from 'undent'; import type { RooibosSession } from './RooibosSession'; import { diagnosticErrorProcessingFile } from '../utils/Diagnostics'; import type { TestCase } from './TestCase'; import type { TestSuite } from './TestSuite'; -import { getAllDottedGetParts } from './Utils'; +import { functionRequiresReturnValue, getAllDottedGetParts, getScopeForSuite } from './Utils'; export class MockUtil { @@ -26,13 +24,13 @@ export class MockUtil { session: RooibosSession; private brsFileAdditions = ` - function RBS_SM_#ID#_getMocksByFunctionName() - if m._rMocksByFunctionName = invalid - m._rMocksByFunctionName = {} - end if - return m._rMocksByFunctionName - end function -`; + function RBS_SM_#ID#_getMocksByFunctionName() + if m._rMocksByFunctionName = invalid + m._rMocksByFunctionName = {} + end if + return m._rMocksByFunctionName + end function + `; private config: RooibosConfig; private fileId: number; @@ -52,8 +50,8 @@ export class MockUtil { this.processedStatements = new Set(); this.astEditor = astEditor; // console.log('processing global methods on ', file.pkgPath); - for (let fs of file.parser.references.functionStatements) { - this.enableMockOnFunction(fs); + for (let functionStatement of file.parser.references.functionStatements) { + this.enableMockOnFunction(file, functionStatement); } this.filePathMap[this.fileId] = file.pkgPath; @@ -62,7 +60,7 @@ export class MockUtil { } } - private enableMockOnFunction(functionStatement: brighterscript.FunctionStatement) { + private enableMockOnFunction(file: BrsFile, functionStatement: brighterscript.FunctionStatement) { if (isClassStatement(functionStatement.parent?.parent)) { // console.log('skipping class', functionStatement.parent?.parent?.name?.text); return; @@ -79,29 +77,53 @@ export class MockUtil { return; } + let isDisabledFoMocking = functionStatement.annotations?.find(x => x.name.toLowerCase() === 'disablemocking'); + let parentNamespace = functionStatement.findAncestor(isNamespaceStatement); + while (parentNamespace && !isDisabledFoMocking) { + if (parentNamespace) { + isDisabledFoMocking = parentNamespace.annotations?.find(x => x.name.toLowerCase() === 'disablemocking'); + parentNamespace = parentNamespace.findAncestor(isNamespaceStatement); + } + } + if (isDisabledFoMocking) { + // The developer has stated that this function is not safe to be mocked + return; + } + // console.log('processing stubbed method', methodName); // TODO check if the user has actually mocked or stubbed this function, otherwise leave it alone! for (let param of functionStatement.func.parameters) { param.asToken = null; } + const paramNames = functionStatement.func.parameters.map((param) => param.name.text).join(','); + const requiresReturnValue = functionRequiresReturnValue(functionStatement); + const globalAaName = '__stubs_globalAa'; + const resultName = '__stubOrMockResult'; + const storageName = '__globalStubs'; - const returnStatement = ((functionStatement.func.functionType?.kind === brighterscript.TokenKind.Sub && (functionStatement.func.returnTypeToken === undefined || functionStatement.func.returnTypeToken?.kind === brighterscript.TokenKind.Void)) || functionStatement.func.returnTypeToken?.kind === brighterscript.TokenKind.Void) ? 'return' : 'return result'; - this.astEditor.addToArray(functionStatement.func.body.statements, 0, new RawCodeStatement(undent` + const template = undent` + ${globalAaName} = getGlobalAa() if RBS_SM_${this.fileId}_getMocksByFunctionName()["${methodName}"] <> invalid - result = RBS_SM_${this.fileId}_getMocksByFunctionName()["${methodName}"].callback(${paramNames}) - ${returnStatement} + ${resultName} = RBS_SM_${this.fileId}_getMocksByFunctionName()["${methodName}"].callback(${paramNames}) + return${requiresReturnValue ? ` ${resultName}` : '' } + else if type(${globalAaName}?.${storageName}?.${methodName}).endsWith("Function") + __stubFunction = ${globalAaName}.${storageName}.${methodName} + ${resultName} = __stubFunction(${paramNames}) + return${requiresReturnValue ? ` ${resultName}` : ''} end if - `)); + `; + const astCodeToInject = Parser.parse(template).ast.statements; + this.astEditor.arrayUnshift(functionStatement.func.body.statements, ...astCodeToInject); this.processedStatements.add(functionStatement); + file.needsTranspiled = true; } addBrsAPIText(file: BrsFile) { - //TODO should use ast editor! - const func = new RawCodeStatement(this.brsFileAdditions.replace(/\#ID\#/g, this.fileId.toString().trim()), file, Range.create(Position.create(1, 1), Position.create(1, 1))); - file.ast.statements.push(func); + const func = Parser.parse(this.brsFileAdditions.replace(/\#ID\#/g, this.fileId.toString().trim())).ast.statements; + this.astEditor.arrayPush(file.ast.statements, ...func); } @@ -125,13 +147,13 @@ export class MockUtil { let assertRegex = /(?:fail|assert(?:[a-z0-9]*)|expect(?:[a-z0-9]*)|stubCall)/i; if (dge && assertRegex.test(dge.name.text)) { if (dge.name.text === 'stubCall') { - this.processGlobalStubbedMethod(callExpression); + this.processGlobalStubbedMethod(callExpression, testSuite); return expressionStatement; } else { if (dge.name.text === 'expectCalled' || dge.name.text === 'expectNotCalled') { - this.processGlobalStubbedMethod(callExpression); + this.processGlobalStubbedMethod(callExpression, testSuite); } } } @@ -147,9 +169,10 @@ export class MockUtil { } } - private processGlobalStubbedMethod(callExpression: brighterscript.CallExpression) { + private processGlobalStubbedMethod(callExpression: brighterscript.CallExpression, testSuite: TestSuite) { let isNotCalled = false; let isStubCall = false; + const scope = getScopeForSuite(testSuite); const namespaceLookup = this.session.namespaceLookup; if (brighterscript.isDottedGetExpression(callExpression.callee)) { const nameText = callExpression.callee.name.text; @@ -158,32 +181,46 @@ export class MockUtil { } //modify args let arg0 = callExpression.args[0]; - if (brighterscript.isCallExpression(arg0) && brighterscript.isDottedGetExpression(arg0.callee)) { - - //is it a namespace? - let dg = arg0.callee; - let nameParts = getAllDottedGetParts(dg); - let name = nameParts.pop(); - - // console.log('found expect with name', name); - if (name) { - //is a namespace? - if (nameParts[0] && namespaceLookup.has(nameParts[0].toLowerCase())) { - //then this must be a namespace method - let fullPathName = nameParts.join('.').toLowerCase(); - let ns = namespaceLookup.get(fullPathName); - if (!ns) { - //TODO this is an error condition! - } - nameParts.push(name); - let functionName = nameParts.join('_').toLowerCase(); - this.session.globalStubbedMethods.add(functionName); - } + let arg1 = callExpression.args[1]; + + if (isStubCall) { + let functionName = this.getGlobalFunctionName(arg0, scope); + if (functionName) { + this.session.globalStubbedMethods.add(functionName.toLowerCase()); + return; + } + } + + if (brighterscript.isCallExpression(arg0)) { + let functionName = this.getGlobalFunctionName(arg0.callee, scope); + if (functionName) { + this.session.globalStubbedMethods.add(functionName.toLowerCase()); } - } else if (brighterscript.isCallExpression(arg0) && brighterscript.isVariableExpression(arg0.callee)) { - let functionName = arg0.callee.getName(brighterscript.ParseMode.BrightScript).toLowerCase(); - this.session.globalStubbedMethods.add(functionName); } } + + private getGlobalFunctionName(expression: brighterscript.Expression, scope: Scope) { + let result: string; + if (brighterscript.isDottedGetExpression(expression)) { + let nameParts = getAllDottedGetParts(expression); + let functionName = nameParts.join('.'); + let callable = scope.getCallableByName(functionName); + if (callable) { + result = callable.getName(ParseMode.BrightScript); + } + } else if (brighterscript.isVariableExpression(expression)) { + let functionName = expression.getName(ParseMode.BrightScript); + if (scope.symbolTable.hasSymbol(functionName)) { + result = functionName; + } + + functionName = expression.getName(ParseMode.BrighterScript); + if (scope.getCallableByName(functionName)) { + result = functionName; + } + } + + return result; + } } diff --git a/bsc-plugin/src/lib/rooibos/RooibosSession.ts b/bsc-plugin/src/lib/rooibos/RooibosSession.ts index 53e7fa88..4410d7f5 100644 --- a/bsc-plugin/src/lib/rooibos/RooibosSession.ts +++ b/bsc-plugin/src/lib/rooibos/RooibosSession.ts @@ -1,5 +1,5 @@ import * as path from 'path'; -import type { BrsFile, BscFile, ClassStatement, FunctionStatement, NamespaceStatement, Program, ProgramBuilder, Scope, Statement } from 'brighterscript'; +import type { BrsFile, ClassStatement, FunctionStatement, NamespaceContainer, NamespaceStatement, Program, ProgramBuilder, Scope } from 'brighterscript'; import { isBrsFile, isCallExpression, isVariableExpression, ParseMode, WalkMode } from 'brighterscript'; import type { AstEditor } from 'brighterscript/dist/astUtils/AstEditor'; import type { RooibosConfig } from './RooibosConfig'; @@ -16,18 +16,6 @@ import type { MockUtil } from './MockUtil'; // eslint-disable-next-line const pkg = require('../../../package.json'); - -export interface NamespaceContainer { - file: BscFile; - fullName: string; - nameRange: Range; - lastPartName: string; - statements: Statement[]; - classStatements: Record; - functionStatements: Record; - namespaces: Record; -} - export class RooibosSession { constructor(builder: ProgramBuilder, fileFactory: FileFactory) { @@ -58,6 +46,9 @@ export class RooibosSession { console.log('Efficient global stubbing is enabled'); this.namespaceLookup = this.getNamespaces(program); for (let testSuite of this.sessionInfo.testSuitesToRun) { + if (testSuite.isNodeTest) { + this.createNodeFile(program, testSuite); + } mockUtil.gatherGlobalMethodMocks(testSuite); } diff --git a/bsc-plugin/src/lib/rooibos/TestGroup.ts b/bsc-plugin/src/lib/rooibos/TestGroup.ts index b09da4c2..71df26f5 100644 --- a/bsc-plugin/src/lib/rooibos/TestGroup.ts +++ b/bsc-plugin/src/lib/rooibos/TestGroup.ts @@ -1,5 +1,5 @@ -import type { AstEditor, CallExpression, DottedGetExpression } from 'brighterscript'; -import { ArrayLiteralExpression, createInvalidLiteral, createStringLiteral, createToken, isDottedGetExpression, TokenKind, isFunctionExpression, Parser } from 'brighterscript'; +import type { AstEditor, CallExpression, DottedGetExpression, Expression, NamespaceContainer, Scope } from 'brighterscript'; +import { ArrayLiteralExpression, createInvalidLiteral, createStringLiteral, createToken, isDottedGetExpression, TokenKind, isFunctionExpression, Parser, ParseMode } from 'brighterscript'; import * as brighterscript from 'brighterscript'; import { BrsTranspileState } from 'brighterscript/dist/parser/BrsTranspileState'; import { diagnosticErrorProcessingFile } from '../utils/Diagnostics'; @@ -8,7 +8,6 @@ import type { TestCase } from './TestCase'; import type { TestSuite } from './TestSuite'; import { TestBlock } from './TestSuite'; import { getAllDottedGetParts, getRootObjectFromDottedGet, getStringPathFromDottedGet, sanitizeBsJsonString } from './Utils'; -import type { NamespaceContainer } from './RooibosSession'; export class TestGroup extends TestBlock { @@ -40,14 +39,13 @@ export class TestGroup extends TestBlock { } else { this.hasAsyncTests = testCase.isAsync; } - } public getTestCases(): TestCase[] { return [...this.testCases.values()]; } - public modifyAssertions(testCase: TestCase, noEarlyExit: boolean, editor: AstEditor, namespaceLookup: Map) { + public modifyAssertions(testCase: TestCase, noEarlyExit: boolean, editor: AstEditor, namespaceLookup: Map, scope: Scope) { //for each method //if assertion //wrap with if is not fail @@ -64,21 +62,22 @@ export class TestGroup extends TestBlock { let assertRegex = /(?:fail|assert(?:[a-z0-9]*)|expect(?:[a-z0-9]*)|stubCall)/i; if (dge && assertRegex.test(dge.name.text)) { if (dge.name.text === 'stubCall') { - this.modifyModernRooibosExpectCallExpression(callExpression, editor, namespaceLookup); + this.modifyModernRooibosExpectCallExpression(callExpression, editor, namespaceLookup, scope); return expressionStatement; } else { if (dge.name.text === 'expectCalled' || dge.name.text === 'expectNotCalled') { - this.modifyModernRooibosExpectCallExpression(callExpression, editor, namespaceLookup); + this.modifyModernRooibosExpectCallExpression(callExpression, editor, namespaceLookup, scope); } if (dge.name.text === 'expectCalled' || dge.name.text === 'expectNotCalled') { - this.modifyModernRooibosExpectCallExpression(callExpression, editor, namespaceLookup); + this.modifyModernRooibosExpectCallExpression(callExpression, editor, namespaceLookup, scope); } - const trailingLine = Parser.parse(`${noEarlyExit ? '' : `if m.currentResult?.isFail = true then m.done() : return ${isSub ? '' : 'invalid'}`}`).ast.statements[0]; - - editor.arraySplice(owner, key + 1, 0, trailingLine); + if (!noEarlyExit) { + const trailingLine = Parser.parse(`if m.currentResult?.isFail = true then m.done() : return ${isSub ? '' : 'invalid'}`).ast.statements[0]; + editor.arraySplice(owner, key + 1, 0, trailingLine); + } const leadingLine = Parser.parse(`m.currentAssertLineNumber = ${callExpression.range.start.line}`).ast.statements[0]; editor.arraySplice(owner, key, 0, leadingLine); } @@ -89,23 +88,30 @@ export class TestGroup extends TestBlock { walkMode: brighterscript.WalkMode.visitStatementsRecursive }); } catch (e) { - // console.log(e); + console.error(e); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument diagnosticErrorProcessingFile(this.testSuite.file, e.message); } } - private modifyModernRooibosExpectCallExpression(callExpression: CallExpression, editor: AstEditor, namespaceLookup: Map) { + private modifyModernRooibosExpectCallExpression(callExpression: CallExpression, editor: AstEditor, namespaceLookup: Map, scope: Scope) { let isNotCalled = false; let isStubCall = false; + + //modify args + let arg0 = callExpression.args[0]; + let arg1 = callExpression.args[1]; if (isDottedGetExpression(callExpression.callee)) { const nameText = callExpression.callee.name.text; - editor.setProperty(callExpression.callee.name, 'text', `_${nameText}`); isNotCalled = nameText === 'expectNotCalled'; isStubCall = nameText === 'stubCall'; + + if (isStubCall && this.shouldNotModifyStubCall(arg0, namespaceLookup, scope)) { + return; + } + editor.setProperty(callExpression.callee.name, 'text', `_${nameText}`); } - //modify args - let arg0 = callExpression.args[0]; + if (brighterscript.isCallExpression(arg0) && isDottedGetExpression(arg0.callee)) { //is it a namespace? @@ -191,6 +197,20 @@ export class TestGroup extends TestBlock { } } + private shouldNotModifyStubCall(arg0: Expression, namespaceLookup: Map, scope: Scope) { + if (brighterscript.isDottedGetExpression(arg0)) { + let nameParts = getAllDottedGetParts(arg0); + let functionName = nameParts.join('.'); + return scope.getCallableByName(functionName); + } else if (brighterscript.isVariableExpression(arg0)) { + return ( + scope.symbolTable.hasSymbol(arg0.getName(ParseMode.BrightScript)) || + scope.getCallableByName(arg0.getName(ParseMode.BrighterScript)) + ); + } + return false; + } + public asText(): string { let testCaseText = [...this.testCases.values()].filter((tc) => tc.isIncluded).map((tc) => tc.asText()); diff --git a/bsc-plugin/src/lib/rooibos/Utils.ts b/bsc-plugin/src/lib/rooibos/Utils.ts index 7299297b..39bd00a8 100644 --- a/bsc-plugin/src/lib/rooibos/Utils.ts +++ b/bsc-plugin/src/lib/rooibos/Utils.ts @@ -1,6 +1,8 @@ -import type { BrsFile, ClassStatement, Expression, FunctionStatement, AnnotationExpression, AstEditor } from 'brighterscript'; +import type { AnnotationExpression, AstEditor, BrsFile, ClassStatement, Expression, FunctionStatement } from 'brighterscript'; +import { TokenKind, isXmlScope } from 'brighterscript'; import * as brighterscript from 'brighterscript'; import { diagnosticCorruptTestProduced } from '../utils/Diagnostics'; +import type { TestSuite } from './TestSuite'; export function addOverriddenMethod(file: BrsFile, annotation: AnnotationExpression, target: ClassStatement, name: string, source: string, editor: AstEditor): boolean { let functionSource = ` @@ -36,6 +38,12 @@ export function sanitizeBsJsonString(text: string) { return `"${text ? text.replace(/"/g, '\'') : ''}"`; } +export function functionRequiresReturnValue(statement: FunctionStatement) { + const returnTypeToken = statement.func.returnTypeToken; + const functionType = statement.func.functionType; + return !((functionType?.kind === TokenKind.Sub && (returnTypeToken === undefined || returnTypeToken?.kind === TokenKind.Void)) || returnTypeToken?.kind === TokenKind.Void); +} + export function getAllDottedGetParts(dg: brighterscript.DottedGetExpression) { let parts = [dg?.name?.text]; let nextPart = dg.obj; @@ -46,7 +54,6 @@ export function getAllDottedGetParts(dg: brighterscript.DottedGetExpression) { return parts.reverse(); } - export function getRootObjectFromDottedGet(value: brighterscript.DottedGetExpression) { let root; if (brighterscript.isDottedGetExpression(value) || brighterscript.isIndexedGetExpression(value)) { @@ -97,3 +104,14 @@ export function getPathValuePartAsString(expr: Expression) { } } } + +export function getScopeForSuite(testSuite: TestSuite) { + if (testSuite.isNodeTest) { + return testSuite.file.program.getScopesForFile(testSuite.file).find((scope)=> { + return isXmlScope(scope) && scope.xmlFile.componentName.text === testSuite.generatedNodeName; + }); + + } else { + return testSuite.file.program.getFirstScopeForFile(testSuite.file); + } +} diff --git a/bsc-plugin/src/plugin.spec.ts b/bsc-plugin/src/plugin.spec.ts index 45dc4d9e..2038e661 100644 --- a/bsc-plugin/src/plugin.spec.ts +++ b/bsc-plugin/src/plugin.spec.ts @@ -638,7 +638,7 @@ describe('RooibosPlugin', () => { expect(statements[0]).to.be.instanceof(PrintStatement); }); - describe.skip('expectCalled transpilation', () => { + describe('expectCalled transpilation', () => { it('correctly transpiles call funcs', async () => { program.setFile('source/test.spec.bs', ` @suite @@ -657,40 +657,46 @@ describe('RooibosPlugin', () => { expect(program.getDiagnostics()).to.be.empty; expect(plugin.session.sessionInfo.testSuitesToRun).to.not.be.empty; await builder.transpile(); - const testContents = getTestFunctionContents(true); + const testContents = getTestFunctionContents(); expect( testContents ).to.eql(undent` m.currentAssertLineNumber = 6 m._expectCalled(m.thing, "callFunc", m, "m.thing", [ - "getFunction" + "getFunction" ]) - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 7 m._expectCalled(m.thing, "callFunc", m, "m.thing", [ - "getFunction" + "getFunction" ], "return") - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 8 m._expectCalled(m.thing, "callFunc", m, "m.thing", [ - "getFunction" - "a" - "b" + "getFunction" + "a" + "b" ]) - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 9 m._expectCalled(m.thing, "callFunc", m, "m.thing", [ - "getFunction" - "a" - "b" + "getFunction" + "a" + "b" ], "return") - if m.currentResult?.isFail = true then m.done() : return invalid + if m.currentResult?.isFail = true then + m.done() + return invalid + end if `); }); @@ -715,12 +721,16 @@ describe('RooibosPlugin', () => { ).to.eql(undent` m.currentAssertLineNumber = 6 m._expectCalled(m.thing, "getFunctionField", m, "m.thing", invalid) - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 7 m._expectCalled(m.thing, "getFunctionField", m, "m.thing", invalid, "return") - if m.currentResult?.isFail = true then m.done() : return invalid + if m.currentResult?.isFail = true then + m.done() + return invalid + end if `); }); @@ -742,34 +752,40 @@ describe('RooibosPlugin', () => { await builder.transpile(); expect(program.getDiagnostics().filter((d) => d.code !== 'RBS2213')).to.be.empty; expect(plugin.session.sessionInfo.testSuitesToRun).to.not.be.empty; - const testContents = getTestFunctionContents(true); + const testContents = getTestFunctionContents(); expect( testContents ).to.eql(undent` m.currentAssertLineNumber = 6 m._expectCalled(m.thing, "getFunction", m, "m.thing", []) - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 7 m._expectCalled(m.thing, "getFunction", m, "m.thing", [], "return") - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 8 m._expectCalled(m.thing, "getFunction", m, "m.thing", [ - "arg1" - "arg2" + "arg1" + "arg2" ]) - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 9 m._expectCalled(m.thing, "getFunction", m, "m.thing", [ - "arg1" - "arg2" + "arg1" + "arg2" ], "return") - if m.currentResult?.isFail = true then m.done() : return invalid + if m.currentResult?.isFail = true then + m.done() + return invalid + end if `); }); it('does not produce crashing code for subs', async () => { @@ -790,34 +806,39 @@ describe('RooibosPlugin', () => { await builder.transpile(); expect(program.getDiagnostics().filter((d) => d.code !== 'RBS2213')).to.be.empty; expect(plugin.session.sessionInfo.testSuitesToRun).to.not.be.empty; - let a = getTestSubContents(true); expect( - getTestSubContents(true) + getTestSubContents() ).to.eql(undent` m.currentAssertLineNumber = 6 m._expectCalled(m.thing, "getFunction", m, "m.thing", []) - if m.currentResult?.isFail = true then m.done() : return - - + if m.currentResult?.isFail = true then + m.done() + return + end if m.currentAssertLineNumber = 7 m._expectCalled(m.thing, "getFunction", m, "m.thing", [], "return") - if m.currentResult?.isFail = true then m.done() : return - - + if m.currentResult?.isFail = true then + m.done() + return + end if m.currentAssertLineNumber = 8 m._expectCalled(m.thing, "getFunction", m, "m.thing", [ - "arg1" - "arg2" + "arg1" + "arg2" ]) - if m.currentResult?.isFail = true then m.done() : return - - + if m.currentResult?.isFail = true then + m.done() + return + end if m.currentAssertLineNumber = 9 m._expectCalled(m.thing, "getFunction", m, "m.thing", [ - "arg1" - "arg2" + "arg1" + "arg2" ], "return") - if m.currentResult?.isFail = true then m.done() : return + if m.currentResult?.isFail = true then + m.done() + return + end if `); }); it('does not break when validating again after a transpile', async () => { @@ -839,34 +860,40 @@ describe('RooibosPlugin', () => { program.validate(); expect(program.getDiagnostics().filter((d) => d.code !== 'RBS2213')).to.be.empty; expect(plugin.session.sessionInfo.testSuitesToRun).to.not.be.empty; - const testContents = getTestFunctionContents(true); + const testContents = getTestFunctionContents(); expect( testContents ).to.eql(undent` m.currentAssertLineNumber = 6 m._expectCalled(m.thing, "getFunction", m, "m.thing", []) - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 7 m._expectCalled(m.thing, "getFunction", m, "m.thing", [], "return") - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 8 m._expectCalled(m.thing, "getFunction", m, "m.thing", [ - "arg1" - "arg2" + "arg1" + "arg2" ]) - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 9 m._expectCalled(m.thing, "getFunction", m, "m.thing", [ - "arg1" - "arg2" + "arg1" + "arg2" ], "return") - if m.currentResult?.isFail = true then m.done() : return invalid + if m.currentResult?.isFail = true then + m.done() + return invalid + end if `); }); @@ -891,19 +918,22 @@ describe('RooibosPlugin', () => { expect(program.getDiagnostics()).to.be.empty; // expect(plugin.session.sessionInfo.testSuitesToRun).to.not.be.empty; await builder.transpile(); - const testContents = getTestFunctionContents(true); + const testContents = getTestFunctionContents(); expect( testContents ).to.eql(undent` b = { - someValue: "value" + someValue: "value" } - m.currentAssertLineNumber = 12 m.assertEqual(b, { - someValue: "value" + someValue: "value" }) - if m.currentResult?.isFail = true then m.done() : return invalid`); + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + `); }); it('correctly transpiles function invocations - simple object', async () => { @@ -925,38 +955,43 @@ describe('RooibosPlugin', () => { expect(program.getDiagnostics()).to.be.empty; expect(plugin.session.sessionInfo.testSuitesToRun).to.not.be.empty; await builder.transpile(); - const testContents = getTestFunctionContents(true); + const testContents = getTestFunctionContents(); expect( testContents ).to.eql(undent` item = { - id: "item" + id: "item" } - m.currentAssertLineNumber = 7 m._expectCalled(item, "getFunction", item, "item", []) - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 8 m._expectCalled(item, "getFunction", item, "item", [], "return") - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 9 m._expectCalled(item, "getFunction", item, "item", [ - "arg1" - "arg2" + "arg1" + "arg2" ]) - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 10 m._expectCalled(item, "getFunction", item, "item", [ - "arg1" - "arg2" + "arg1" + "arg2" ], "return") - if m.currentResult?.isFail = true then m.done() : return invalid + if m.currentResult?.isFail = true then + m.done() + return invalid + end if `); }); it('correctly transpiles global function calls', async () => { @@ -984,56 +1019,66 @@ describe('RooibosPlugin', () => { expect(program.getDiagnostics()).to.be.empty; expect(plugin.session.sessionInfo.testSuitesToRun).to.not.be.empty; await builder.transpile(); - const testText = getTestFunctionContents(true); + const testText = getTestFunctionContents(); expect( testText ).to.eql(undent` item = { - id: "item" + id: "item" } - m.currentAssertLineNumber = 7 m._expectCalled(sayHello, "sayHello", invalid, invalid, [ - "arg1" - "arg2" + "arg1" + "arg2" ], "return") - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 8 m._expectCalled(sayHello, "sayHello", invalid, invalid, []) - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 9 m._expectCalled(sayHello, "sayHello", invalid, invalid, [], "return") - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 10 m._expectCalled(sayHello, "sayHello", invalid, invalid, [ - "arg1" - "arg2" + "arg1" + "arg2" ]) - if m.currentResult?.isFail = true then m.done() : return invalid + if m.currentResult?.isFail = true then + m.done() + return invalid + end if `); let codeText = getContents('code.brs'); expect(codeText).to.equal(undent` function sayHello(firstName = "", lastName = "") + __stubs_globalAa = getGlobalAa() if RBS_SM_1_getMocksByFunctionName()["sayhello"] <> invalid - result = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback(firstName,lastName) - return result + __stubOrMockResult = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback(firstName, lastName) + return __stubOrMockResult + else if type(__stubs_globalAa?.__globalStubs?.sayhello).endsWith("Function") + __stubFunction = __stubs_globalAa.__globalStubs.sayhello + __stubOrMockResult = __stubFunction(firstName, lastName) + return __stubOrMockResult end if print firstName + " " + lastName end function - function RBS_SM_1_getMocksByFunctionName() - if m._rMocksByFunctionName = invalid + function RBS_SM_1_getMocksByFunctionName() + if m._rMocksByFunctionName = invalid m._rMocksByFunctionName = {} - end if - return m._rMocksByFunctionName - end function`); + end if + return m._rMocksByFunctionName + end function`); }); it('correctly transpiles namespaced function calls', async () => { plugin.config.isGlobalMethodMockingEnabled = true; @@ -1062,59 +1107,71 @@ describe('RooibosPlugin', () => { expect(program.getDiagnostics()).to.be.empty; expect(plugin.session.sessionInfo.testSuitesToRun).to.not.be.empty; await builder.transpile(); - const testText = getTestFunctionContents(true); + const testText = getTestFunctionContents(); expect( testText ).to.eql(undent` - item = { - id: "item" - } - - m.currentAssertLineNumber = 7 - m._expectCalled(utils_sayhello, "utils_sayhello", invalid, invalid, [ - "arg1" - "arg2" - ], "return") - if m.currentResult?.isFail = true then m.done() : return invalid - - - m.currentAssertLineNumber = 8 - m._expectCalled(utils_sayhello, "utils_sayhello", invalid, invalid, []) - if m.currentResult?.isFail = true then m.done() : return invalid - - - m.currentAssertLineNumber = 9 - m._expectCalled(utils_sayhello, "utils_sayhello", invalid, invalid, [], "return") - if m.currentResult?.isFail = true then m.done() : return invalid - - - m.currentAssertLineNumber = 10 - m._expectCalled(utils_sayhello, "utils_sayhello", invalid, invalid, [ - "arg1" - "arg2" - ]) - if m.currentResult?.isFail = true then m.done() : return invalid -`); + item = { + id: "item" + } + m.currentAssertLineNumber = 7 + m._expectCalled(utils_sayhello, "utils_sayhello", invalid, invalid, [ + "arg1" + "arg2" + ], "return") + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + m.currentAssertLineNumber = 8 + m._expectCalled(utils_sayhello, "utils_sayhello", invalid, invalid, []) + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + m.currentAssertLineNumber = 9 + m._expectCalled(utils_sayhello, "utils_sayhello", invalid, invalid, [], "return") + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + m.currentAssertLineNumber = 10 + m._expectCalled(utils_sayhello, "utils_sayhello", invalid, invalid, [ + "arg1" + "arg2" + ]) + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + `); - let codeText = trimLeading(getContents('code.brs')); - expect(codeText).to.equal(trimLeading(`function utils_sayHello(firstName = "", lastName = "") - if RBS_SM_1_getMocksByFunctionName()["utils_sayhello"] <> invalid - result = RBS_SM_1_getMocksByFunctionName()["utils_sayhello"].callback(firstName,lastName) - return result - end if - print firstName + " " + lastName + let codeText = getContents('code.brs'); + expect(codeText).to.equal(undent(` + function utils_sayHello(firstName = "", lastName = "") + __stubs_globalAa = getGlobalAa() + if RBS_SM_1_getMocksByFunctionName()["utils_sayhello"] <> invalid + __stubOrMockResult = RBS_SM_1_getMocksByFunctionName()["utils_sayhello"].callback(firstName, lastName) + return __stubOrMockResult + else if type(__stubs_globalAa?.__globalStubs?.utils_sayhello).endsWith("Function") + __stubFunction = __stubs_globalAa.__globalStubs.utils_sayhello + __stubOrMockResult = __stubFunction(firstName, lastName) + return __stubOrMockResult + end if + print firstName + " " + lastName end function function RBS_SM_1_getMocksByFunctionName() - if m._rMocksByFunctionName = invalid - m._rMocksByFunctionName = {} - end if - return m._rMocksByFunctionName - end function`)); + if m._rMocksByFunctionName = invalid + m._rMocksByFunctionName = {} + end if + return m._rMocksByFunctionName + end function + `)); }); }); - describe.skip('stubCall transpilation', () => { + describe('stubCall transpilation', () => { it('correctly transpiles call funcs', async () => { program.setFile('source/test.spec.bs', ` @suite @@ -1254,9 +1311,311 @@ describe('RooibosPlugin', () => { m._stubCall(item, "getFunction", item, "item", "return") `); }); + + it('correctly transpiles global function and inline anon function param', async () => { + program.setFile('source/test.spec.bs', ` + @suite + class ATest + @describe("groupA") + @it("stubs global with inline anon with return value") + function _() + getGlobalAA().wasCalled = false + m.stubCall(globalFunctionWithReturn, function() + m.wasCalled = true + return true + end function) + + m.assertTrue(globalFunctionWithReturn()) + m.assertTrue(getGlobalAA().wasCalled) + m.assertRunningTestIsPassed() + end function + end class + + function globalFunctionWithReturn() as dynamic + m.wasCalled = false + return false + end function + `); + program.validate(); + expect(program.getDiagnostics()).to.be.empty; + expect(plugin.session.sessionInfo.testSuitesToRun).to.not.be.empty; + await builder.transpile(); + const result = getTestFunctionContents(); + expect( + result + ).to.eql(undent` + getGlobalAA().wasCalled = false + m.stubCall(globalFunctionWithReturn, function() + m.wasCalled = true + return true + end function) + m.currentAssertLineNumber = 12 + m.assertTrue(globalFunctionWithReturn()) + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + m.currentAssertLineNumber = 13 + m.assertTrue(getGlobalAA().wasCalled) + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + m.currentAssertLineNumber = 14 + m.assertRunningTestIsPassed() + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + `); + }); + + it('correctly transpiles namespace function and inline anon function param', async () => { + program.setFile('source/test.spec.bs', ` + @suite + class ATest + @describe("groupA") + @it("stubs global with inline anon with return value") + function _() + getGlobalAA().wasCalled = false + m.stubCall(testNamespace.functionWithReturn, function() + m.wasCalled = true + return true + end function) + + m.assertTrue(testNamespace.functionWithReturn()) + m.assertTrue(getGlobalAA().wasCalled) + m.assertRunningTestIsPassed() + end function + end class + + namespace testNamespace + function functionWithReturn() as dynamic + m.wasCalled = false + return false + end function + end namespace + `); + program.validate(); + expect(program.getDiagnostics()).to.be.empty; + expect(plugin.session.sessionInfo.testSuitesToRun).to.not.be.empty; + await builder.transpile(); + const result = getTestFunctionContents(); + expect( + result + ).to.eql(undent` + getGlobalAA().wasCalled = false + m.stubCall(testNamespace_functionWithReturn, function() + m.wasCalled = true + return true + end function) + m.currentAssertLineNumber = 12 + m.assertTrue(testNamespace_functionWithReturn()) + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + m.currentAssertLineNumber = 13 + m.assertTrue(getGlobalAA().wasCalled) + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + m.currentAssertLineNumber = 14 + m.assertRunningTestIsPassed() + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + `); + }); + + it('correctly transpiles namespace function and variable anon function param', async () => { + program.setFile('source/test.spec.bs', ` + @suite + class ATest + @describe("groupA") + @it("stubs global with anon from variable with return value") + function _() + getGlobalAA().wasCalled = false + stub = function() + m.wasCalled = true + return true + end function + m.stubCall(testNamespace.functionWithReturn, stub) + + m.assertTrue(testNamespace.functionWithReturn()) + m.assertTrue(getGlobalAA().wasCalled) + m.assertRunningTestIsPassed() + end function + end class + + namespace testNamespace + function functionWithReturn() as dynamic + m.wasCalled = false + return false + end function + end namespace + `); + program.validate(); + expect(program.getDiagnostics()).to.be.empty; + expect(plugin.session.sessionInfo.testSuitesToRun).to.not.be.empty; + await builder.transpile(); + const result = getTestFunctionContents(); + expect( + result + ).to.eql(undent` + getGlobalAA().wasCalled = false + stub = function() + m.wasCalled = true + return true + end function + m.stubCall(testNamespace_functionWithReturn, stub) + m.currentAssertLineNumber = 13 + m.assertTrue(testNamespace_functionWithReturn()) + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + m.currentAssertLineNumber = 14 + m.assertTrue(getGlobalAA().wasCalled) + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + m.currentAssertLineNumber = 15 + m.assertRunningTestIsPassed() + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + `); + }); + + it('correctly transpiles namespace function by brightscript name and inline anon function param', async () => { + program.setFile('source/test.spec.bs', ` + @suite + class ATest + @describe("groupA") + @it("stubs global with inline anon with return value") + function _() + getGlobalAA().wasCalled = false + m.stubCall(testNamespace_functionWithReturn, function() + m.wasCalled = true + return true + end function) + + m.assertTrue(testNamespace_functionWithReturn()) + m.assertTrue(getGlobalAA().wasCalled) + m.assertRunningTestIsPassed() + end function + end class + + namespace testNamespace + function functionWithReturn() as dynamic + m.wasCalled = false + return false + end function + end namespace + `); + program.validate(); + expect(program.getDiagnostics()).to.be.empty; + expect(plugin.session.sessionInfo.testSuitesToRun).to.not.be.empty; + await builder.transpile(); + const result = getTestFunctionContents(); + expect( + result + ).to.eql(undent` + getGlobalAA().wasCalled = false + m.stubCall(testNamespace_functionWithReturn, function() + m.wasCalled = true + return true + end function) + m.currentAssertLineNumber = 12 + m.assertTrue(testNamespace_functionWithReturn()) + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + m.currentAssertLineNumber = 13 + m.assertTrue(getGlobalAA().wasCalled) + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + m.currentAssertLineNumber = 14 + m.assertRunningTestIsPassed() + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + `); + }); + + it('correctly transpiles namespace function by brightscript name and variable anon function param', async () => { + program.setFile('source/test.spec.bs', ` + @suite + class ATest + @describe("groupA") + @it("stubs global with anon from variable with return value") + function _() + getGlobalAA().wasCalled = false + stub = function() + m.wasCalled = true + return true + end function + m.stubCall(testNamespace_functionWithReturn, stub) + + m.assertTrue(testNamespace_functionWithReturn()) + m.assertTrue(getGlobalAA().wasCalled) + m.assertRunningTestIsPassed() + end function + end class + + namespace testNamespace + function functionWithReturn() as dynamic + m.wasCalled = false + return false + end function + end namespace + `); + program.validate(); + expect(program.getDiagnostics()).to.be.empty; + expect(plugin.session.sessionInfo.testSuitesToRun).to.not.be.empty; + await builder.transpile(); + const result = getTestFunctionContents(); + expect( + result + ).to.eql(undent` + getGlobalAA().wasCalled = false + stub = function() + m.wasCalled = true + return true + end function + m.stubCall(testNamespace_functionWithReturn, stub) + m.currentAssertLineNumber = 13 + m.assertTrue(testNamespace_functionWithReturn()) + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + m.currentAssertLineNumber = 14 + m.assertTrue(getGlobalAA().wasCalled) + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + m.currentAssertLineNumber = 15 + m.assertRunningTestIsPassed() + if m.currentResult?.isFail = true then + m.done() + return invalid + end if + `); + }); }); - describe.skip('expectNotCalled transpilation', () => { + describe('expectNotCalled transpilation', () => { it('correctly transpiles call funcs', async () => { program.setFile('source/test.spec.bs', ` @suite @@ -1280,22 +1639,28 @@ describe('RooibosPlugin', () => { ).to.eql(undent` m.currentAssertLineNumber = 6 m._expectNotCalled(m.thing, "callFunc", m, "m.thing") - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 7 m._expectNotCalled(m.thing, "callFunc", m, "m.thing", "return") - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 8 m._expectNotCalled(m.thing, "callFunc", m, "m.thing") - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 9 m._expectNotCalled(m.thing, "callFunc", m, "m.thing", "return") - if m.currentResult?.isFail = true then m.done() : return invalid + if m.currentResult?.isFail = true then + m.done() + return invalid + end if `); }); @@ -1320,15 +1685,18 @@ describe('RooibosPlugin', () => { getTestFunctionContents() ).to.eql(undent` thing = {} - m.currentAssertLineNumber = 7 m._expectNotCalled(thing, "callFunc", thing, "thing") - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 8 m._expectNotCalled(thing, "callFunc", thing, "thing") - if m.currentResult?.isFail = true then m.done() : return invalid + if m.currentResult?.isFail = true then + m.done() + return invalid + end if `); //verify original code does not remain modified after the transpile cycle const testMethod = ((file.ast.statements[0] as ClassStatement).memberMap['_'] as ClassMethodStatement); @@ -1365,7 +1733,10 @@ describe('RooibosPlugin', () => { ).to.eql(undent` m.currentAssertLineNumber = 6 m._expectNotCalled(m.thing, "getFunctionField", m, "m.thing") - if m.currentResult?.isFail = true then m.done() : return invalid + if m.currentResult?.isFail = true then + m.done() + return invalid + end if `); //verify original code does not remain modified after the transpile cycle const testMethod = ((file.ast.statements[0] as ClassStatement).memberMap['_'] as ClassMethodStatement); @@ -1399,23 +1770,28 @@ describe('RooibosPlugin', () => { ).to.eql(undent` m.currentAssertLineNumber = 6 m._expectNotCalled(m.thing, "getFunction", m, "m.thing") - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 7 m._expectNotCalled(m.thing, "getFunction", m, "m.thing", "return") - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 8 m._expectNotCalled(m.thing, "getFunction", m, "m.thing") - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 9 m._expectNotCalled(m.thing, "getFunction", m, "m.thing", "return") - if m.currentResult?.isFail = true then m.done() : return invalid - `); + if m.currentResult?.isFail = true then + m.done() + return invalid + end if `); }); it('correctly transpiles function invocations - simple object', async () => { @@ -1435,22 +1811,25 @@ describe('RooibosPlugin', () => { expect(program.getDiagnostics()).to.be.empty; expect(plugin.session.sessionInfo.testSuitesToRun).to.not.be.empty; await builder.transpile(); - const testContents = getTestFunctionContents(true); + const testContents = getTestFunctionContents(); expect( testContents ).to.eql(undent` item = { - id: "item" + id: "item" } - m.currentAssertLineNumber = 7 m._expectNotCalled(item, "getFunction", item, "item") - if m.currentResult?.isFail = true then m.done() : return invalid - - + if m.currentResult?.isFail = true then + m.done() + return invalid + end if m.currentAssertLineNumber = 8 m._expectNotCalled(item, "getFunction", item, "item") - if m.currentResult?.isFail = true then m.done() : return invalid + if m.currentResult?.isFail = true then + m.done() + return invalid + end if `); }); }); @@ -1832,14 +2211,13 @@ function getContents(filename: string) { function getTestFunctionContents(trimEveryLine = false) { const contents = getContents('test.spec.brs'); - const [, body] = /\= function\(\)([\S\s]*|.*)(?=end function)/gim.exec(contents); - let result = undent( - body.split('end function')[0] - ); + + let [, result] = /instance.[\w_]+\s?\= function\(\)\s?([\S\s]*|.*)(?=^\s*end function\s+instance\.)/img.exec(contents); + if (trimEveryLine) { result = trim(result); } - return result; + return undent(result); } function getTestSubContents(trimEveryLine = false) { diff --git a/bsc-plugin/src/plugin.ts b/bsc-plugin/src/plugin.ts index 79c93f50..8cb51bd9 100644 --- a/bsc-plugin/src/plugin.ts +++ b/bsc-plugin/src/plugin.ts @@ -22,6 +22,7 @@ import { FileFactory } from './lib/rooibos/FileFactory'; import type { RooibosConfig } from './lib/rooibos/RooibosConfig'; import * as minimatch from 'minimatch'; import { MockUtil } from './lib/rooibos/MockUtil'; +import { getScopeForSuite } from './lib/rooibos/Utils'; export class RooibosPlugin implements CompilerPlugin { @@ -147,6 +148,7 @@ export class RooibosPlugin implements CompilerPlugin { beforeFileTranspile(event: BeforeFileTranspileEvent) { let testSuite = this.session.sessionInfo.testSuitesToRun.find((ts) => ts.file.pkgPath === event.file.pkgPath); if (testSuite) { + const scope = getScopeForSuite(testSuite); let noEarlyExit = testSuite.annotation.noEarlyExit; if (noEarlyExit) { console.warn(`WARNING: testSuite "${testSuite.name}" is marked as noEarlyExit`); @@ -155,12 +157,9 @@ export class RooibosPlugin implements CompilerPlugin { testSuite.addDataFunctions(event.editor as any); for (let group of [...testSuite.testGroups.values()].filter((tg) => tg.isIncluded)) { for (let testCase of [...group.testCases.values()].filter((tc) => tc.isIncluded)) { - group.modifyAssertions(testCase, noEarlyExit, event.editor as any, this.session.namespaceLookup); + group.modifyAssertions(testCase, noEarlyExit, event.editor as any, this.session.namespaceLookup, scope); } } - if (testSuite.isNodeTest) { - this.session.createNodeFile(event.program, testSuite); - } } if (isBrsFile(event.file)) { diff --git a/framework/src/source/BaseTestSuite.bs b/framework/src/source/BaseTestSuite.bs index eeb2fef4..e85771cc 100644 --- a/framework/src/source/BaseTestSuite.bs +++ b/framework/src/source/BaseTestSuite.bs @@ -267,7 +267,7 @@ namespace rooibos if m.catchCrashes and not test.noCatch and not m.noCatch try test.run() - if m.isAutoAssertingMocks = true + if m.isAutoAssertingMocks = true and not test.isAsync m.AssertMocks() m.CleanMocks() m.CleanStubs() @@ -278,7 +278,7 @@ namespace rooibos end try else test.run() - if m.isAutoAssertingMocks = true + if m.isAutoAssertingMocks = true and not test.isAsync m.AssertMocks() m.CleanMocks() m.CleanStubs() @@ -1802,8 +1802,25 @@ namespace rooibos return invalid end function - function stubCall(invocation as dynamic, returnValue = invalid as dynamic) as object - 'mock function body - the plugin replaces this + function stubCall(invocation as dynamic, stubOrReturnValue = invalid as dynamic, functionName = "" as string) as object + ' When stubbing global functions this will be called. Other wise the test code will be updated to call m._stubCall() + if type(invocation).endsWith("Function") and functionName = "" + functionName = invocation.toStr().tokenize(" ").peek() + else + throw "Did not provide a function to be stubbed" + end if + + if not type(stubOrReturnValue).endsWith("Function") + throw "Did not provide a stub function" + end if + + ' Store the stub on the component scope + globalAa = getGlobalAa() + if type(globalAa?.__globalStubs) <> "roAssociativeArray" + globalAa.__globalStubs = {} + end if + + globalAa.__globalStubs[lCase(functionName)] = stubOrReturnValue return invalid end function @@ -2262,6 +2279,7 @@ namespace rooibos rooibos.resetMocksByFunctionName() end function + ' /** ' * @memberof module:BaseTestSuite ' * @name cleanStubs @@ -2270,6 +2288,10 @@ namespace rooibos ' * @description Cleans up all tracking data associated with stubs ' */ function cleanStubs() as void + ' Clean up the global functions mocks as well + globalAa = getGlobalAa() + globalAa.__globalStubs = invalid + if m.stubs = invalid return end if diff --git a/tests/package-lock.json b/tests/package-lock.json index 6a67ac3c..6ec48dfe 100644 --- a/tests/package-lock.json +++ b/tests/package-lock.json @@ -1,15 +1,15 @@ { "name": "rooibos-tests", - "version": "4.1.0", + "version": "4.1.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "rooibos-tests", - "version": "4.1.0", + "version": "4.1.1", "license": "MIT", "devDependencies": { - "brighterscript": "^0.64.0", + "brighterscript": "^0.65.15", "ts-node": "^10.7.0", "typescript": "^4.6.4" } @@ -86,6 +86,56 @@ "node": ">= 8" } }, + "node_modules/@postman/form-data": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@postman/form-data/-/form-data-3.1.1.tgz", + "integrity": "sha512-vjh8Q2a8S6UCm/KKs31XFJqEEgmbjBmpPNVV2eVav6905wyFAwaUOBGA1NPBI4ERH9MMZc6w0umFgM6WbEPMdg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@postman/tough-cookie": { + "version": "4.1.3-postman.1", + "resolved": "https://registry.npmjs.org/@postman/tough-cookie/-/tough-cookie-4.1.3-postman.1.tgz", + "integrity": "sha512-txpgUqZOnWYnUHZpHjkfb0IwVH4qJmyq77pPnJLlfhMtdCLMFTEeQHlzQiK906aaNCe4NEB5fGJHo9uzGbFMeA==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@postman/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/@postman/tunnel-agent": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.3.tgz", + "integrity": "sha512-k57fzmAZ2PJGxfOA4SGR05ejorHbVAa/84Hxh/2nAztjNXc4ZjOm9NUIk6/Z6LCrBvJZqjRZbN8e/nROVUPVdg==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/@rokucommunity/bslib": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@rokucommunity/bslib/-/bslib-0.1.1.tgz", @@ -211,6 +261,12 @@ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/array-flat-polyfill": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-flat-polyfill/-/array-flat-polyfill-1.0.1.tgz", @@ -265,6 +321,26 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -283,6 +359,12 @@ "node": ">=8" } }, + "node_modules/bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ==", + "dev": true + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -306,10 +388,11 @@ } }, "node_modules/brighterscript": { - "version": "0.64.3", - "resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.64.3.tgz", - "integrity": "sha512-W0k0ID3yCbFGba80d8RlggldvRs6u5Y9F6F+AQ4fWDdJcWeHJ3vy5/pabzDbYD9sen3trVxiZ2yXdA7XK35V5g==", + "version": "0.65.16", + "resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.65.16.tgz", + "integrity": "sha512-2RJMF8itkrPXtZ92JkJf3emvzcCl5ETXGVyPsdO/hMKbUGinEGMRFG07FR0bzjRotbALzsozGE/Hjzw412Sjww==", "dev": true, + "license": "MIT", "dependencies": { "@rokucommunity/bslib": "^0.1.1", "@xml-tools/parser": "^1.0.7", @@ -318,6 +401,7 @@ "chevrotain": "^7.0.1", "chokidar": "^3.5.1", "clear": "^0.1.0", + "coveralls-next": "^4.2.0", "cross-platform-clear-console": "^2.3.0", "debounce-promise": "^3.1.0", "eventemitter3": "^4.0.0", @@ -333,7 +417,7 @@ "parse-ms": "^2.1.0", "readline": "^1.3.0", "require-relative": "^0.8.7", - "roku-deploy": "^3.10.1", + "roku-deploy": "^3.11.2", "serialize-error": "^7.0.1", "source-map": "^0.7.4", "vscode-languageserver": "7.0.0", @@ -347,6 +431,15 @@ "bsc": "dist/cli.js" } }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "dev": true, + "dependencies": { + "base64-js": "^1.1.2" + } + }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -462,6 +555,25 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "node_modules/coveralls-next": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/coveralls-next/-/coveralls-next-4.2.0.tgz", + "integrity": "sha512-zg41a/4QDSASPtlV6gp+6owoU43U5CguxuPZR3nPZ26M5ZYdEK3MdUe7HwE+AnCZPkucudfhqqJZehCNkz2rYg==", + "dev": true, + "dependencies": { + "form-data": "4.0.0", + "js-yaml": "4.1.0", + "lcov-parse": "1.0.0", + "log-driver": "1.2.7", + "minimist": "1.2.7" + }, + "bin": { + "coveralls": "bin/coveralls.js" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -496,9 +608,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.7", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", - "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==", + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==", "dev": true }, "node_modules/debounce-promise": { @@ -587,9 +699,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -609,9 +721,9 @@ "dev": true }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -648,17 +760,17 @@ } }, "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dev": true, "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" }, "engines": { - "node": ">= 0.12" + "node": ">= 6" } }, "node_modules/fs-extra": { @@ -758,18 +870,17 @@ } }, "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", + "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", "dev": true, "dependencies": { "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "jsprim": "^2.0.2", + "sshpk": "^1.14.1" }, "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" + "node": ">=0.10" } }, "node_modules/immediate": { @@ -853,6 +964,18 @@ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -893,18 +1016,18 @@ } }, "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", "dev": true, + "engines": [ + "node >=0.6.0" + ], "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", "json-schema": "0.4.0", "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" } }, "node_modules/jszip": { @@ -919,6 +1042,15 @@ "setimmediate": "^1.0.5" } }, + "node_modules/lcov-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", + "integrity": "sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ==", + "dev": true, + "bin": { + "lcov-parse": "bin/cli.js" + } + }, "node_modules/lie": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", @@ -928,6 +1060,21 @@ "immediate": "~3.0.5" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/log-driver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", + "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", + "dev": true, + "engines": { + "node": ">=0.8.6" + } + }, "node_modules/long": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", @@ -1007,10 +1154,19 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "dev": true, "engines": { "node": "*" @@ -1110,6 +1266,39 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/postman-request": { + "version": "2.88.1-postman.33", + "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.33.tgz", + "integrity": "sha512-uL9sCML4gPH6Z4hreDWbeinKU0p0Ke261nU7OvII95NU22HN6Dk7T/SaVPaj6T4TsQqGKIFw6/woLZnH7ugFNA==", + "dev": true, + "dependencies": { + "@postman/form-data": "~3.1.1", + "@postman/tough-cookie": "~4.1.3-postman.1", + "@postman/tunnel-agent": "^0.6.3", + "aws-sign2": "~0.7.0", + "aws4": "^1.12.0", + "brotli": "^1.3.3", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "har-validator": "~5.1.3", + "http-signature": "~1.3.1", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "^2.1.35", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.3", + "safe-buffer": "^5.1.2", + "stream-length": "^1.0.2", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -1123,9 +1312,9 @@ "dev": true }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { "node": ">=6" @@ -1140,6 +1329,12 @@ "node": ">=0.6" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -1175,6 +1370,12 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -1199,38 +1400,6 @@ "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", "dev": true }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dev": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -1246,6 +1415,12 @@ "integrity": "sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==", "dev": true }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -1257,9 +1432,9 @@ } }, "node_modules/roku-deploy": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.10.1.tgz", - "integrity": "sha512-utPNny0a2m/N0AQT6zyVLXtrr81KR5QeJqPUbc59VBcqGM+WIi7rQ9hBLmp5dFqIo/7JVKQYFs+nFGebOA6F7w==", + "version": "3.11.2", + "resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.11.2.tgz", + "integrity": "sha512-3JDlnbTxv6Xk5GVolQoA3+d34MLZXXwZWMySprHwazZoWLP3LvulYHP92YvFOJAo/aI4IZp/TFA8kR82IrmHKA==", "dev": true, "dependencies": { "chalk": "^2.4.2", @@ -1270,10 +1445,11 @@ "is-glob": "^4.0.3", "jsonc-parser": "^2.3.0", "jszip": "^3.6.0", + "lodash": "^4.17.21", "micromatch": "^4.0.4", "moment": "^2.29.1", "parse-ms": "^2.1.0", - "request": "^2.88.0", + "postman-request": "^2.88.1-postman.32", "temp-dir": "^2.0.0", "xml2js": "^0.5.0" }, @@ -1319,10 +1495,24 @@ } }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -1331,9 +1521,9 @@ "dev": true }, "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", "dev": true }, "node_modules/serialize-error": { @@ -1367,9 +1557,9 @@ } }, "node_modules/sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", "dev": true, "dependencies": { "asn1": "~0.2.3", @@ -1391,6 +1581,15 @@ "node": ">=0.10.0" } }, + "node_modules/stream-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-length/-/stream-length-1.0.2.tgz", + "integrity": "sha512-aI+qKFiwoDV4rsXiS7WRoCt+v2RX1nUj17+KJC5r2gfh5xoSJIfP6Y3Do/HtvesFcTSWthIuJ3l1cvKQY/+nZg==", + "dev": true, + "dependencies": { + "bluebird": "^2.6.2" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -1400,6 +1599,12 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -1459,19 +1664,6 @@ "node": ">=8.0" } }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -1515,18 +1707,6 @@ } } }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", @@ -1576,6 +1756,16 @@ "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -1583,13 +1773,12 @@ "dev": true }, "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, "bin": { - "uuid": "bin/uuid" + "uuid": "dist/bin/uuid" } }, "node_modules/v8-compile-cache-lib": { @@ -1843,6 +2032,46 @@ "fastq": "^1.6.0" } }, + "@postman/form-data": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@postman/form-data/-/form-data-3.1.1.tgz", + "integrity": "sha512-vjh8Q2a8S6UCm/KKs31XFJqEEgmbjBmpPNVV2eVav6905wyFAwaUOBGA1NPBI4ERH9MMZc6w0umFgM6WbEPMdg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "@postman/tough-cookie": { + "version": "4.1.3-postman.1", + "resolved": "https://registry.npmjs.org/@postman/tough-cookie/-/tough-cookie-4.1.3-postman.1.tgz", + "integrity": "sha512-txpgUqZOnWYnUHZpHjkfb0IwVH4qJmyq77pPnJLlfhMtdCLMFTEeQHlzQiK906aaNCe4NEB5fGJHo9uzGbFMeA==", + "dev": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "dependencies": { + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true + } + } + }, + "@postman/tunnel-agent": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.3.tgz", + "integrity": "sha512-k57fzmAZ2PJGxfOA4SGR05ejorHbVAa/84Hxh/2nAztjNXc4ZjOm9NUIk6/Z6LCrBvJZqjRZbN8e/nROVUPVdg==", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, "@rokucommunity/bslib": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@rokucommunity/bslib/-/bslib-0.1.1.tgz", @@ -1948,6 +2177,12 @@ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "array-flat-polyfill": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-flat-polyfill/-/array-flat-polyfill-1.0.1.tgz", @@ -1993,6 +2228,12 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -2008,6 +2249,12 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ==", + "dev": true + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2028,9 +2275,9 @@ } }, "brighterscript": { - "version": "0.64.3", - "resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.64.3.tgz", - "integrity": "sha512-W0k0ID3yCbFGba80d8RlggldvRs6u5Y9F6F+AQ4fWDdJcWeHJ3vy5/pabzDbYD9sen3trVxiZ2yXdA7XK35V5g==", + "version": "0.65.16", + "resolved": "https://registry.npmjs.org/brighterscript/-/brighterscript-0.65.16.tgz", + "integrity": "sha512-2RJMF8itkrPXtZ92JkJf3emvzcCl5ETXGVyPsdO/hMKbUGinEGMRFG07FR0bzjRotbALzsozGE/Hjzw412Sjww==", "dev": true, "requires": { "@rokucommunity/bslib": "^0.1.1", @@ -2040,6 +2287,7 @@ "chevrotain": "^7.0.1", "chokidar": "^3.5.1", "clear": "^0.1.0", + "coveralls-next": "^4.2.0", "cross-platform-clear-console": "^2.3.0", "debounce-promise": "^3.1.0", "eventemitter3": "^4.0.0", @@ -2055,7 +2303,7 @@ "parse-ms": "^2.1.0", "readline": "^1.3.0", "require-relative": "^0.8.7", - "roku-deploy": "^3.10.1", + "roku-deploy": "^3.11.2", "serialize-error": "^7.0.1", "source-map": "^0.7.4", "vscode-languageserver": "7.0.0", @@ -2066,6 +2314,15 @@ "yargs": "^16.2.0" } }, + "brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "dev": true, + "requires": { + "base64-js": "^1.1.2" + } + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -2161,6 +2418,19 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "coveralls-next": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/coveralls-next/-/coveralls-next-4.2.0.tgz", + "integrity": "sha512-zg41a/4QDSASPtlV6gp+6owoU43U5CguxuPZR3nPZ26M5ZYdEK3MdUe7HwE+AnCZPkucudfhqqJZehCNkz2rYg==", + "dev": true, + "requires": { + "form-data": "4.0.0", + "js-yaml": "4.1.0", + "lcov-parse": "1.0.0", + "log-driver": "1.2.7", + "minimist": "1.2.7" + } + }, "create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -2189,9 +2459,9 @@ "dev": true }, "dayjs": { - "version": "1.11.7", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", - "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==", + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==", "dev": true }, "debounce-promise": { @@ -2265,9 +2535,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -2284,9 +2554,9 @@ "dev": true }, "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -2314,13 +2584,13 @@ "dev": true }, "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dev": true, "requires": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, @@ -2395,14 +2665,14 @@ "dev": true }, "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", + "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", "dev": true, "requires": { "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "jsprim": "^2.0.2", + "sshpk": "^1.14.1" } }, "immediate": { @@ -2471,6 +2741,15 @@ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -2511,9 +2790,9 @@ } }, "jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", "dev": true, "requires": { "assert-plus": "1.0.0", @@ -2534,6 +2813,12 @@ "setimmediate": "^1.0.5" } }, + "lcov-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz", + "integrity": "sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ==", + "dev": true + }, "lie": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", @@ -2543,6 +2828,18 @@ "immediate": "~3.0.5" } }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "log-driver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", + "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", + "dev": true + }, "long": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", @@ -2601,10 +2898,16 @@ "brace-expansion": "^1.1.7" } }, + "minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true + }, "moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "dev": true }, "normalize-path": { @@ -2674,6 +2977,36 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, + "postman-request": { + "version": "2.88.1-postman.33", + "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.33.tgz", + "integrity": "sha512-uL9sCML4gPH6Z4hreDWbeinKU0p0Ke261nU7OvII95NU22HN6Dk7T/SaVPaj6T4TsQqGKIFw6/woLZnH7ugFNA==", + "dev": true, + "requires": { + "@postman/form-data": "~3.1.1", + "@postman/tough-cookie": "~4.1.3-postman.1", + "@postman/tunnel-agent": "^0.6.3", + "aws-sign2": "~0.7.0", + "aws4": "^1.12.0", + "brotli": "^1.3.3", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "har-validator": "~5.1.3", + "http-signature": "~1.3.1", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "^2.1.35", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.3", + "safe-buffer": "^5.1.2", + "stream-length": "^1.0.2", + "uuid": "^8.3.2" + } + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -2687,9 +3020,9 @@ "dev": true }, "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true }, "qs": { @@ -2698,6 +3031,12 @@ "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "dev": true }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -2717,6 +3056,14 @@ "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } } }, "readdirp": { @@ -2740,34 +3087,6 @@ "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", "dev": true }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -2780,6 +3099,12 @@ "integrity": "sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==", "dev": true }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -2787,9 +3112,9 @@ "dev": true }, "roku-deploy": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.10.1.tgz", - "integrity": "sha512-utPNny0a2m/N0AQT6zyVLXtrr81KR5QeJqPUbc59VBcqGM+WIi7rQ9hBLmp5dFqIo/7JVKQYFs+nFGebOA6F7w==", + "version": "3.11.2", + "resolved": "https://registry.npmjs.org/roku-deploy/-/roku-deploy-3.11.2.tgz", + "integrity": "sha512-3JDlnbTxv6Xk5GVolQoA3+d34MLZXXwZWMySprHwazZoWLP3LvulYHP92YvFOJAo/aI4IZp/TFA8kR82IrmHKA==", "dev": true, "requires": { "chalk": "^2.4.2", @@ -2800,10 +3125,11 @@ "is-glob": "^4.0.3", "jsonc-parser": "^2.3.0", "jszip": "^3.6.0", + "lodash": "^4.17.21", "micromatch": "^4.0.4", "moment": "^2.29.1", "parse-ms": "^2.1.0", - "request": "^2.88.0", + "postman-request": "^2.88.1-postman.32", "temp-dir": "^2.0.0", "xml2js": "^0.5.0" }, @@ -2831,9 +3157,9 @@ } }, "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, "safer-buffer": { @@ -2843,9 +3169,9 @@ "dev": true }, "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", "dev": true }, "serialize-error": { @@ -2870,9 +3196,9 @@ "dev": true }, "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", "dev": true, "requires": { "asn1": "~0.2.3", @@ -2886,6 +3212,15 @@ "tweetnacl": "~0.14.0" } }, + "stream-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-length/-/stream-length-1.0.2.tgz", + "integrity": "sha512-aI+qKFiwoDV4rsXiS7WRoCt+v2RX1nUj17+KJC5r2gfh5xoSJIfP6Y3Do/HtvesFcTSWthIuJ3l1cvKQY/+nZg==", + "dev": true, + "requires": { + "bluebird": "^2.6.2" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -2893,6 +3228,14 @@ "dev": true, "requires": { "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } } }, "string-width": { @@ -2939,16 +3282,6 @@ "is-number": "^7.0.0" } }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, "ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -2970,15 +3303,6 @@ "yn": "3.1.1" } }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", @@ -3012,6 +3336,16 @@ "punycode": "^2.1.0" } }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -3019,9 +3353,9 @@ "dev": true }, "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true }, "v8-compile-cache-lib": { diff --git a/tests/package.json b/tests/package.json index fff341f3..2c9b4550 100644 --- a/tests/package.json +++ b/tests/package.json @@ -7,7 +7,7 @@ "watch": "npx bsc --project bsconfig.json --watch" }, "devDependencies": { - "brighterscript": "^0.64.0", + "brighterscript": "^0.65.15", "ts-node": "^10.7.0", "typescript": "^4.6.4" }, @@ -27,4 +27,4 @@ "url": "https://github.com/georgejecook/rooibos/issues" }, "homepage": "https://github.com/georgejecook/rooibos#readme" -} \ No newline at end of file +} diff --git a/tests/src/components/NodeExample.bs b/tests/src/components/NodeExample.bs index 7be0042b..2ab37287 100644 --- a/tests/src/components/NodeExample.bs +++ b/tests/src/components/NodeExample.bs @@ -1,3 +1,5 @@ +import "pkg:/source/Main.bs" + function Init() as void m.nameText = m.top.findNode("nameText") end function @@ -7,10 +9,10 @@ function HelloFromNode(name, age) as string end function function UpdateState(newState) as void - m.top.state = newState + m.top.state = newState end function -function SetLabelText(newText) as void +function SetLabelText(newText = "") as void m.nameText.text = newText end function @@ -28,10 +30,33 @@ function r_real_HelloFromNode(name, age) as string end function function r_real_UpdateState(newState) as void - m.top.state = newState + m.top.state = newState end function function r_real_SetLabelText(newText) as void m.nameText.text = newText end function +function delayCall(delay, callback) as void + timer = createObject("roSgNode", "Timer") + timer.update({ + duration: delay + repeat: false + id: createObject("roDeviceInfo").getRandomUUID() + }) + + m[timer.id] = { + timer: timer + callback: callback + } + + onDelayedFired = sub(event) + id = event.getNode() + callback = m[id].callback + callback() + m.delete(id) + end sub + + timer.observeFieldScoped("fire", onDelayedFired.toStr().tokenize(" ").peek()) + timer.control = "start" +end function diff --git a/tests/src/source/Async.spec.bs b/tests/src/source/Async.spec.bs new file mode 100644 index 00000000..49e78337 --- /dev/null +++ b/tests/src/source/Async.spec.bs @@ -0,0 +1,57 @@ +namespace tests + @async(1000) + @SGNode("NodeExample") + @suite + class AsyncNodeTests extends rooibos.BaseTestSuite + + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @describe("NodeExample tests") + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + @beforeEach + function beforeEachGroup() + m.cleanStubs() + end function + + @async + @it("normal async test") + function _() + callbackFunction = sub() + m.wasCalled = true + m.testSuite.assertTrue(true) + m.testSuite.done() + ? "done one" + end sub + + delayCall(0.02, callbackFunction) + end function + + @async + @it("async with stub call for source function") + function _() + getGlobalAA().wasCalled = false + m.stubCall(globalFunctionWithoutReturn, function() + m.wasCalled = true + m.testSuite.assertTrue(true) + m.testSuite.done() + ? "done two" + end function) + + delayCall(0.02, globalFunctionWithoutReturn) + end function + + @async + @it("async with stub call for node function") + function _() + m.stubCall(SetLabelText, function(text) + m.wasCalled = true + m.testSuite.assertEqual(text, "") + m.testSuite.assertTrue(true) + m.testSuite.done() + ? "done three" + end function) + + delayCall(0.02, SetLabelText) + end function + end class +end namespace diff --git a/tests/src/source/Main.bs b/tests/src/source/Main.bs index f669bacc..627ac475 100644 --- a/tests/src/source/Main.bs +++ b/tests/src/source/Main.bs @@ -3,4 +3,24 @@ function Main(args) ? "here is my code" ? "hello" -end function \ No newline at end of file +end function + +function globalFunctionWithReturn() as dynamic + m.wasCalled = false + return false +end function + +sub globalFunctionWithoutReturn() + m.wasCalled = false +end sub + +namespace testNamespace + function functionWithReturn() as dynamic + m.wasCalled = false + return false + end function + + sub functionWithoutReturn() + m.wasCalled = false + end sub +end namespace \ No newline at end of file diff --git a/tests/src/source/NewExpectSyntax.spec.bs b/tests/src/source/NewExpectSyntax.spec.bs index 6b79ba06..efd6956b 100644 --- a/tests/src/source/NewExpectSyntax.spec.bs +++ b/tests/src/source/NewExpectSyntax.spec.bs @@ -164,6 +164,195 @@ namespace tests m.assertRunningTestIsPassed() end function + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @describe("stubCall global functions with new runtime functions") + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + @it("stubs global with inline anon with return value") + function _() + getGlobalAA().wasCalled = false + m.stubCall(globalFunctionWithReturn, function() + m.wasCalled = true + return true + end function) + + m.assertTrue(globalFunctionWithReturn()) + m.assertTrue(getGlobalAA().wasCalled) + m.assertRunningTestIsPassed() + end function + + @it("stubs global with anon from variable with return value") + function _() + getGlobalAA().wasCalled = false + stub = function() + m.wasCalled = true + return true + end function + m.stubCall(globalFunctionWithReturn, stub) + + m.assertTrue(globalFunctionWithReturn()) + m.assertTrue(getGlobalAA().wasCalled) + m.assertRunningTestIsPassed() + end function + + @it("stubs global with inline anon without return value") + function _() + getGlobalAA().wasCalled = false + m.stubCall(globalFunctionWithoutReturn, sub() + m.wasCalled = true + end sub) + + m.assertInvalid(globalFunctionWithoutReturn()) + m.assertTrue(getGlobalAA().wasCalled) + m.assertRunningTestIsPassed() + end function + + @it("stubs global with anon from variable without return value") + function _() + getGlobalAA().wasCalled = false + m.stubCall(globalFunctionWithoutReturn, sub() + m.wasCalled = true + end sub) + + m.assertInvalid(globalFunctionWithoutReturn()) + m.assertTrue(getGlobalAA().wasCalled) + m.assertRunningTestIsPassed() + end function + + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @describe("stubCall namespace functions with new runtime functions") + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + @it("stubs namespace with inline anon with return value") + function _() + getGlobalAA().wasCalled = false + m.stubCall(testNamespace.functionWithReturn, function() + m.wasCalled = true + return true + end function) + m.assertTrue(testNamespace.functionWithReturn()) + m.assertTrue(getGlobalAA().wasCalled) + m.assertRunningTestIsPassed() + end function + + @it("stubs namespace with anon from variable with return value") + function _() + getGlobalAA().wasCalled = false + stub = function() + m.wasCalled = true + return true + end function + m.stubCall(testNamespace.functionWithReturn, stub) + + m.assertTrue(testNamespace.functionWithReturn()) + m.assertTrue(getGlobalAA().wasCalled) + m.assertRunningTestIsPassed() + end function + + @it("stubs namespace with inline anon without return value") + function _() + getGlobalAA().wasCalled = false + m.stubCall(testNamespace.functionWithoutReturn, sub() + m.wasCalled = true + end sub) + m.assertInvalid(testNamespace.functionWithoutReturn()) + m.assertTrue(getGlobalAA().wasCalled) + m.assertRunningTestIsPassed() + end function + + @it("stubs namespace with anon from variable without return value") + function _() + getGlobalAA().wasCalled = false + m.stubCall(testNamespace.functionWithoutReturn, sub() + m.wasCalled = true + end sub) + m.assertInvalid(testNamespace.functionWithoutReturn()) + m.assertTrue(getGlobalAA().wasCalled) + m.assertRunningTestIsPassed() + end function + + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @describe("stubCall global or namespace functions multiple times in a test") + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + @it("stubs namespace multiple times in one test") + function _() + getGlobalAA().wasCalled = false + m.stubCall(testNamespace.functionWithoutReturn, sub() + m.wasCalled = true + end sub) + + m.assertInvalid(testNamespace.functionWithoutReturn()) + m.assertTrue(getGlobalAA().wasCalled) + + m.stubCall(testNamespace.functionWithoutReturn, sub() + m.wasCalled = 1 + end sub) + m.assertInvalid(testNamespace.functionWithoutReturn()) + m.assertEqual(getGlobalAA().wasCalled, 1) + + m.assertRunningTestIsPassed() + end function + + @it("stubs global multiple times in one test") + function _() + getGlobalAA().wasCalled = false + m.stubCall(testNamespace.functionWithoutReturn, sub() + m.wasCalled = true + end sub) + + m.assertInvalid(testNamespace.functionWithoutReturn()) + m.assertTrue(getGlobalAA().wasCalled) + + m.stubCall(testNamespace.functionWithoutReturn, sub() + m.wasCalled = 1 + end sub) + m.assertInvalid(testNamespace.functionWithoutReturn()) + m.assertEqual(getGlobalAA().wasCalled, 1) + + m.assertRunningTestIsPassed() + end function + + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @describe("stubCall global can be cleaned") + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + @it("stubs namespace and then cleans it") + function _() + getGlobalAA().wasCalled = false + m.stubCall(testNamespace.functionWithoutReturn, sub() + m.wasCalled = true + end sub) + + m.assertInvalid(testNamespace.functionWithoutReturn()) + m.assertTrue(getGlobalAA().wasCalled) + + m.cleanStubs() + + m.assertInvalid(testNamespace.functionWithoutReturn()) + m.assertFalse(getGlobalAA().wasCalled) + + m.assertRunningTestIsPassed() + end function + + @it("stubs global and then cleans it") + function _() + getGlobalAA().wasCalled = false + m.stubCall(globalFunctionWithoutReturn, sub() + m.wasCalled = true + end sub) + + m.assertInvalid(globalFunctionWithoutReturn()) + m.assertTrue(getGlobalAA().wasCalled) + + m.cleanStubs() + + m.assertInvalid(globalFunctionWithoutReturn()) + m.assertFalse(getGlobalAA().wasCalled) + + m.assertRunningTestIsPassed() + end function + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @describe("stubCall callFunc functions") '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -391,4 +580,4 @@ namespace tests end class -end namespace \ No newline at end of file +end namespace