From 0e07019c9c4bf1f44f2cc121784cea3d564d29db Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Thu, 11 Jan 2024 15:06:59 -0400 Subject: [PATCH 01/30] Added function to bast test suite to store global mocked functions --- framework/src/source/BaseTestSuite.bs | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/framework/src/source/BaseTestSuite.bs b/framework/src/source/BaseTestSuite.bs index eeb2fef4..ae63c1ef 100644 --- a/framework/src/source/BaseTestSuite.bs +++ b/framework/src/source/BaseTestSuite.bs @@ -2260,6 +2260,36 @@ namespace rooibos end for m.mocks = invalid rooibos.resetMocksByFunctionName() + + ' Clean up the global functions mocks as well + globalAa = getGlobalAa() + globalAa._globalMocks = invalid + end function + + ' /** + ' * @memberof module:BaseTestSuite + ' * @name mockGlobalFunction + ' * @function + ' * @instance + ' * @description Adds a function to the global mock for the current components scope + ' * @param {String|Function} functionOrFunctionName - a function by reference or the string name of a function to mock + ' * @param {Function} mockFunction - the function to use as a mock + ' */ + function mockGlobalFunction(functionOrFunctionName as dynamic, mockFunction as function) as void + if type(functionOrFunctionName) = "roFunction" or type(functionOrFunctionName) = "Function" + functionName = functionOrFunctionName.toStr().tokenize(" ").peek() + else if type(functionOrFunctionName) = "String" or type(functionOrFunctionName) = "roString" + functionName = functionOrFunctionName + else + throw "Did not provide a function or function name to mock" + end if + + globalAa = getGlobalAa() + if type(globalAa?._globalMocks) <> "roAssociativeArray" + globalAa._globalMocks = {} + end if + + globalAa._globalMocks[functionName] = mockFunction end function ' /** From 77d06a4937c04c9f8c135551a7eae16aa7f59be1 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Thu, 11 Jan 2024 15:07:44 -0400 Subject: [PATCH 02/30] Moved some logic to the utils --- bsc-plugin/src/lib/rooibos/MockUtil.ts | 4 ++-- bsc-plugin/src/lib/rooibos/Utils.ts | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.ts b/bsc-plugin/src/lib/rooibos/MockUtil.ts index 0af49936..c3d14725 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.ts @@ -12,7 +12,7 @@ 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 } from './Utils'; export class MockUtil { @@ -87,7 +87,7 @@ export class MockUtil { } const paramNames = functionStatement.func.parameters.map((param) => param.name.text).join(','); - 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'; + const returnStatement = functionRequiresReturnValue(functionStatement) ? 'return' : 'return result'; this.astEditor.addToArray(functionStatement.func.body.statements, 0, new RawCodeStatement(undent` if RBS_SM_${this.fileId}_getMocksByFunctionName()["${methodName}"] <> invalid result = RBS_SM_${this.fileId}_getMocksByFunctionName()["${methodName}"].callback(${paramNames}) diff --git a/bsc-plugin/src/lib/rooibos/Utils.ts b/bsc-plugin/src/lib/rooibos/Utils.ts index 7299297b..b507b7e7 100644 --- a/bsc-plugin/src/lib/rooibos/Utils.ts +++ b/bsc-plugin/src/lib/rooibos/Utils.ts @@ -1,4 +1,4 @@ -import type { BrsFile, ClassStatement, Expression, FunctionStatement, AnnotationExpression, AstEditor } from 'brighterscript'; +import { type BrsFile, type ClassStatement, type Expression, type FunctionStatement, type AnnotationExpression, type AstEditor, TokenKind } from 'brighterscript'; import * as brighterscript from 'brighterscript'; import { diagnosticCorruptTestProduced } from '../utils/Diagnostics'; @@ -36,6 +36,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; From 9f759249da51313ca61f58eff77737be0fc088ea Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Thu, 11 Jan 2024 15:19:24 -0400 Subject: [PATCH 03/30] Added logic to inject code that adds global mock checks at the start of function bodys --- bsc-plugin/src/lib/rooibos/MockUtil.ts | 41 ++++++++++++++++++++++++-- bsc-plugin/src/plugin.ts | 1 + 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.ts b/bsc-plugin/src/lib/rooibos/MockUtil.ts index c3d14725..d4529f95 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.ts @@ -1,7 +1,7 @@ /* 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 } from 'brighterscript'; +import { ParseMode, Parser, Position, TokenKind, WalkMode, createVisitor, isClassStatement, isNamespaceStatement } from 'brighterscript'; import * as brighterscript from 'brighterscript'; import type { RooibosConfig } from './RooibosConfig'; import { RawCodeStatement } from './RawCodeStatement'; @@ -186,4 +186,41 @@ export class MockUtil { } } + public addRuntimeGlobalFunctionMocks(file: BrsFile, astEditor: Editor) { + file.ast.walk(createVisitor({ + FunctionStatement: (statement, parent, owner, key) => { + if (!file.pkgPath.includes('rooibos/') && !file.pkgPath.endsWith('spec.brs')) { + let isDisabledFoMocking = statement.annotations?.find(x => x.name.toLowerCase() === 'disablemocking'); + let parentNamespace = statement.findAncestor(isNamespaceStatement); + while (parentNamespace && !isDisabledFoMocking) { + if (parentNamespace) { + isDisabledFoMocking = parentNamespace.annotations?.find(x => x.name.toLowerCase() === 'disablemocking'); + parentNamespace = parentNamespace.findAncestor(isNamespaceStatement); + } + } + + if (!isDisabledFoMocking) { + const funcName = statement.getName(ParseMode.BrightScript); + const returnResult = functionRequiresReturnValue(statement); + const globalAaName = '__mocks_globalAa'; + const storageName = '_globalMocks'; + const template = undent` + ${globalAaName} = getGlobalAa() + if type(${globalAaName}?.${storageName}?.${funcName}) = "roFunction" or type(${globalAaName}?.${storageName}?.${funcName}) = "Function" then + __mockedFunction = ${globalAaName}.${storageName}.${funcName} + __mockResult = __mockedFunction(${statement.func.parameters.map(x => x.name.text).join(', ')}) + return ${returnResult ? '__mockResult' : ''} + end if + `; + const mockStatements = Parser.parse(template).ast.statements; + astEditor.arrayUnshift(statement.func.body.statements, ...mockStatements); + file.needsTranspiled = true; + } + } + } + }), { + walkMode: WalkMode.visitAllRecursive + }); + } + } diff --git a/bsc-plugin/src/plugin.ts b/bsc-plugin/src/plugin.ts index d89721df..f9d35f18 100644 --- a/bsc-plugin/src/plugin.ts +++ b/bsc-plugin/src/plugin.ts @@ -165,6 +165,7 @@ export class RooibosPlugin implements CompilerPlugin { } if (isBrsFile(event.file)) { + this.mockUtil.addRuntimeGlobalFunctionMocks(event.file, event.editor); if (this.shouldAddCodeCoverageToFile(event.file)) { this.codeCoverageProcessor.addCodeCoverage(event.file, event.editor); } From 90edc04ac960df31dc967e87bf6af6f18faf910e Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Mon, 15 Jan 2024 10:59:27 -0400 Subject: [PATCH 04/30] Fixed a reversed check --- bsc-plugin/src/lib/rooibos/MockUtil.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.ts b/bsc-plugin/src/lib/rooibos/MockUtil.ts index d4529f95..b8b770fa 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.ts @@ -87,7 +87,7 @@ export class MockUtil { } const paramNames = functionStatement.func.parameters.map((param) => param.name.text).join(','); - const returnStatement = functionRequiresReturnValue(functionStatement) ? 'return' : 'return result'; + const returnStatement = functionRequiresReturnValue(functionStatement) ? 'return result' : 'return'; this.astEditor.addToArray(functionStatement.func.body.statements, 0, new RawCodeStatement(undent` if RBS_SM_${this.fileId}_getMocksByFunctionName()["${methodName}"] <> invalid result = RBS_SM_${this.fileId}_getMocksByFunctionName()["${methodName}"].callback(${paramNames}) From 2c4feef04deecf5d052c4c48a753a3ab7b7be1da Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Mon, 15 Jan 2024 11:04:32 -0400 Subject: [PATCH 05/30] Removed new api in favor of expanding stubcall and updated injection code --- bsc-plugin/src/lib/rooibos/MockUtil.ts | 25 ++++++++++---- bsc-plugin/src/plugin.ts | 1 - framework/src/source/BaseTestSuite.bs | 46 +++++++++++--------------- 3 files changed, 38 insertions(+), 34 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.ts b/bsc-plugin/src/lib/rooibos/MockUtil.ts index b8b770fa..cbecab22 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.ts @@ -85,15 +85,28 @@ export class MockUtil { for (let param of functionStatement.func.parameters) { param.asToken = null; } - const paramNames = functionStatement.func.parameters.map((param) => param.name.text).join(','); - const returnStatement = functionRequiresReturnValue(functionStatement) ? 'return result' : 'return'; - this.astEditor.addToArray(functionStatement.func.body.statements, 0, new RawCodeStatement(undent` + const funcName = functionStatement.getName(ParseMode.BrightScript); + 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'; + + console.log(funcName); + 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}?.${funcName}).endsWith("Function") + __stubFunction = ${globalAaName}.${storageName}.${funcName} + ${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); } diff --git a/bsc-plugin/src/plugin.ts b/bsc-plugin/src/plugin.ts index f9d35f18..d89721df 100644 --- a/bsc-plugin/src/plugin.ts +++ b/bsc-plugin/src/plugin.ts @@ -165,7 +165,6 @@ export class RooibosPlugin implements CompilerPlugin { } if (isBrsFile(event.file)) { - this.mockUtil.addRuntimeGlobalFunctionMocks(event.file, event.editor); if (this.shouldAddCodeCoverageToFile(event.file)) { this.codeCoverageProcessor.addCodeCoverage(event.file, event.editor); } diff --git a/framework/src/source/BaseTestSuite.bs b/framework/src/source/BaseTestSuite.bs index ae63c1ef..5424e86d 100644 --- a/framework/src/source/BaseTestSuite.bs +++ b/framework/src/source/BaseTestSuite.bs @@ -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[functionName] = stubOrReturnValue return invalid end function @@ -2266,31 +2283,6 @@ namespace rooibos globalAa._globalMocks = invalid end function - ' /** - ' * @memberof module:BaseTestSuite - ' * @name mockGlobalFunction - ' * @function - ' * @instance - ' * @description Adds a function to the global mock for the current components scope - ' * @param {String|Function} functionOrFunctionName - a function by reference or the string name of a function to mock - ' * @param {Function} mockFunction - the function to use as a mock - ' */ - function mockGlobalFunction(functionOrFunctionName as dynamic, mockFunction as function) as void - if type(functionOrFunctionName) = "roFunction" or type(functionOrFunctionName) = "Function" - functionName = functionOrFunctionName.toStr().tokenize(" ").peek() - else if type(functionOrFunctionName) = "String" or type(functionOrFunctionName) = "roString" - functionName = functionOrFunctionName - else - throw "Did not provide a function or function name to mock" - end if - - globalAa = getGlobalAa() - if type(globalAa?._globalMocks) <> "roAssociativeArray" - globalAa._globalMocks = {} - end if - - globalAa._globalMocks[functionName] = mockFunction - end function ' /** ' * @memberof module:BaseTestSuite From 9f6111ea2cc7f385caeda53e845e963f396308d7 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Mon, 15 Jan 2024 11:09:36 -0400 Subject: [PATCH 06/30] Updated some of the global function detection logic --- bsc-plugin/src/lib/rooibos/MockUtil.ts | 30 +++++++++++++++++++++++++ bsc-plugin/src/lib/rooibos/TestGroup.ts | 14 +++++++++--- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.ts b/bsc-plugin/src/lib/rooibos/MockUtil.ts index cbecab22..b62933b9 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.ts @@ -171,6 +171,36 @@ export class MockUtil { } //modify args let arg0 = callExpression.args[0]; + let arg1 = callExpression.args[1]; + + if (isStubCall) { + if (brighterscript.isFunctionExpression(arg1)) { + if (brighterscript.isDottedGetExpression(arg0)) { + let nameParts = getAllDottedGetParts(arg0); + let name = nameParts.pop(); + console.log(nameParts[0], namespaceLookup.has(nameParts[0].toLowerCase())); + + 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); + } + } + } else if (brighterscript.isVariableExpression(arg0)) { + const functionName = arg0.getName(ParseMode.BrightScript).toLowerCase(); + this.session.globalStubbedMethods.add(functionName); + } + } + } + if (brighterscript.isCallExpression(arg0) && brighterscript.isDottedGetExpression(arg0.callee)) { //is it a namespace? diff --git a/bsc-plugin/src/lib/rooibos/TestGroup.ts b/bsc-plugin/src/lib/rooibos/TestGroup.ts index b09da4c2..64d6189f 100644 --- a/bsc-plugin/src/lib/rooibos/TestGroup.ts +++ b/bsc-plugin/src/lib/rooibos/TestGroup.ts @@ -98,14 +98,22 @@ export class TestGroup extends TestBlock { private modifyModernRooibosExpectCallExpression(callExpression: CallExpression, editor: AstEditor, namespaceLookup: Map) { 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 && (brighterscript.isDottedGetExpression(arg0) || brighterscript.isVariableExpression(arg0)) && brighterscript.isFunctionExpression(arg1)) { + console.log('modifyModernRooibosExpectCallExpression', callExpression.callee.name); + 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? From fadbf2c450e71d03d21fa8c83f39b9184904d0cb Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Mon, 15 Jan 2024 12:00:49 -0400 Subject: [PATCH 07/30] Unit tests, fixes, sample test project update --- bsc-plugin/src/lib/rooibos/MockUtil.spec.ts | 133 +++++++++++++------- bsc-plugin/src/lib/rooibos/MockUtil.ts | 6 +- bsc-plugin/src/plugin.spec.ts | 91 ++++++++------ framework/src/source/BaseTestSuite.bs | 4 +- tests/src/source/NewExpectSyntax.spec.bs | 61 +++++++++ 5 files changed, 205 insertions(+), 90 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts b/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts index 0183f3ec..b14865c2 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts @@ -78,9 +78,14 @@ describe('MockUtil', () => { await builder.transpile(); let a = getContents('source/code.brs'); let b = trimLeading(`function sayHello(a1, a2) + __stubs_globalAa = getGlobalAa() if RBS_SM_1_getMocksByFunctionName()["sayhello"] <> invalid - result = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback(a1,a2) - return result + __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 @@ -109,9 +114,14 @@ describe('MockUtil', () => { await builder.transpile(); let a = getContents('source/code.brs'); let b = trimLeading(`function sayHello(a1, a2) + __stubs_globalAa = getGlobalAa() if RBS_SM_1_getMocksByFunctionName()["sayhello"] <> invalid - result = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback(a1,a2) - return result + __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 @@ -143,8 +153,13 @@ describe('MockUtil', () => { await builder.transpile(); let a = getContents('source/code.brs'); let b = trimLeading(`Sub RedLines_SetRulerLines(rulerLines) + __stubs_globalAa = getGlobalAa() if RBS_SM_1_getMocksByFunctionName()["redlines_setrulerlines"] <> invalid - result = RBS_SM_1_getMocksByFunctionName()["redlines_setrulerlines"].callback(rulerLines) + __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() @@ -153,9 +168,14 @@ describe('MockUtil', () => { end Sub Sub RedLines_AddLine(id, position, coords, node, childMap) as Object + __stubs_globalAa = getGlobalAa() if RBS_SM_1_getMocksByFunctionName()["redlines_addline"] <> invalid - result = RBS_SM_1_getMocksByFunctionName()["redlines_addline"].callback(id,position,coords,node,childMap) - return result + __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) @@ -183,8 +203,13 @@ describe('MockUtil', () => { await builder.transpile(); let a = getContents('source/code.brs'); let b = trimLeading(`sub sayHello(a1, a2) + __stubs_globalAa = getGlobalAa() if RBS_SM_1_getMocksByFunctionName()["sayhello"] <> invalid - result = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback(a1,a2) + __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" @@ -214,9 +239,14 @@ describe('MockUtil', () => { await builder.transpile(); let a = getContents('source/code.brs'); let b = trimLeading(`function person_utils_sayHello(a1, a2) + __stubs_globalAa = getGlobalAa() if RBS_SM_1_getMocksByFunctionName()["person_utils_sayhello"] <> invalid - result = RBS_SM_1_getMocksByFunctionName()["person_utils_sayhello"].callback(a1,a2) - return result + __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 @@ -245,8 +275,13 @@ describe('MockUtil', () => { await builder.transpile(); let a = getContents('source/code.brs'); let b = trimLeading(`sub person_utils_sayHello(a1, a2) + __stubs_globalAa = getGlobalAa() if RBS_SM_1_getMocksByFunctionName()["person_utils_sayhello"] <> invalid - result = RBS_SM_1_getMocksByFunctionName()["person_utils_sayhello"].callback(a1,a2) + __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" @@ -313,42 +348,52 @@ describe('MockUtil', () => { 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 + 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); diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.ts b/bsc-plugin/src/lib/rooibos/MockUtil.ts index b62933b9..28f808ff 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.ts @@ -86,21 +86,19 @@ export class MockUtil { param.asToken = null; } - const funcName = functionStatement.getName(ParseMode.BrightScript); 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'; - console.log(funcName); const template = undent` ${globalAaName} = getGlobalAa() if RBS_SM_${this.fileId}_getMocksByFunctionName()["${methodName}"] <> invalid ${resultName} = RBS_SM_${this.fileId}_getMocksByFunctionName()["${methodName}"].callback(${paramNames}) return${requiresReturnValue ? ` ${resultName}` : '' } - else if type(${globalAaName}?.${storageName}?.${funcName}).endsWith("Function") - __stubFunction = ${globalAaName}.${storageName}.${funcName} + else if type(${globalAaName}?.${storageName}?.${methodName}).endsWith("Function") + __stubFunction = ${globalAaName}.${storageName}.${methodName} ${resultName} = __stubFunction(${paramNames}) return${requiresReturnValue ? ` ${resultName}` : ''} end if diff --git a/bsc-plugin/src/plugin.spec.ts b/bsc-plugin/src/plugin.spec.ts index 45dc4d9e..ca66f89e 100644 --- a/bsc-plugin/src/plugin.spec.ts +++ b/bsc-plugin/src/plugin.spec.ts @@ -1021,19 +1021,24 @@ describe('RooibosPlugin', () => { 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() + 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`); + function RBS_SM_1_getMocksByFunctionName() + if m._rMocksByFunctionName = invalid + m._rMocksByFunctionName = {} + end if + return m._rMocksByFunctionName + end function`); }); it('correctly transpiles namespaced function calls', async () => { plugin.config.isGlobalMethodMockingEnabled = true; @@ -1066,50 +1071,56 @@ describe('RooibosPlugin', () => { expect( testText ).to.eql(undent` - item = { - id: "item" - } + 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 = 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 = 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 = 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 -`); + m.currentAssertLineNumber = 10 + m._expectCalled(utils_sayhello, "utils_sayhello", invalid, invalid, [ + "arg1" + "arg2" + ]) + if m.currentResult?.isFail = true then m.done() : return invalid + `); 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 + expect(codeText).to.equal(trimLeading(` + 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?.beings_sayHello).endsWith("Function") + __stubFunction = __stubs_globalAa.__globalStubs.beings_sayHello + __stubOrMockResult = __stubFunction() + 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 + if m._rMocksByFunctionName = invalid + m._rMocksByFunctionName = {} + end if + return m._rMocksByFunctionName end function`)); }); }); diff --git a/framework/src/source/BaseTestSuite.bs b/framework/src/source/BaseTestSuite.bs index 5424e86d..3f42a567 100644 --- a/framework/src/source/BaseTestSuite.bs +++ b/framework/src/source/BaseTestSuite.bs @@ -1820,7 +1820,7 @@ namespace rooibos globalAa.__globalStubs = {} end if - globalAa.__globalStubs[functionName] = stubOrReturnValue + globalAa.__globalStubs[lCase(functionName)] = stubOrReturnValue return invalid end function @@ -2280,7 +2280,7 @@ namespace rooibos ' Clean up the global functions mocks as well globalAa = getGlobalAa() - globalAa._globalMocks = invalid + globalAa.__globalStubs = invalid end function diff --git a/tests/src/source/NewExpectSyntax.spec.bs b/tests/src/source/NewExpectSyntax.spec.bs index 6b79ba06..7cf0e06a 100644 --- a/tests/src/source/NewExpectSyntax.spec.bs +++ b/tests/src/source/NewExpectSyntax.spec.bs @@ -164,6 +164,47 @@ namespace tests m.assertRunningTestIsPassed() end function + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @describe("stubCall local and namespace functions with new runtime functions") + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + @it("supports function stubbing with a new function at runtime") + function _() + m.wasCalled = false + m.stubCall(locationFunctionWithReturn, function() + m.wasCalled = true + return true + end function) + m.assertTrue(locationFunctionWithReturn()) + m.assertTrue(m.wasCalled) + m.wasCalled = false + + m.stubCall(locationFunctionWithoutReturn, sub() + m.wasCalled = true + end sub) + m.assertInvalid(locationFunctionWithoutReturn()) + m.assertTrue(m.wasCalled) + m.wasCalled = false + + m.wasCalled = false + m.stubCall(textNamespace.functionWithReturn, function() + m.wasCalled = true + return true + end function) + m.assertTrue(textNamespace.functionWithReturn()) + m.assertTrue(m.wasCalled) + m.wasCalled = false + + m.stubCall(textNamespace.functionWithoutReturn, sub() + m.wasCalled = true + end sub) + m.assertInvalid(textNamespace.functionWithoutReturn()) + m.assertTrue(m.wasCalled) + m.wasCalled = false + + m.assertRunningTestIsPassed() + end function + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @describe("stubCall callFunc functions") '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -391,4 +432,24 @@ namespace tests end class +end namespace + +function locationFunctionWithReturn() as dynamic + m.wasCalled = false + return false +end function + +sub locationFunctionWithoutReturn() + m.wasCalled = false +end sub + +namespace textNamespace + 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 From fc6ce1d708ccb96409fadf34b5accbb27b7cec00 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Mon, 15 Jan 2024 12:02:33 -0400 Subject: [PATCH 08/30] removed console logs --- bsc-plugin/src/lib/rooibos/MockUtil.ts | 1 - bsc-plugin/src/lib/rooibos/TestGroup.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.ts b/bsc-plugin/src/lib/rooibos/MockUtil.ts index 28f808ff..ee79d41c 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.ts @@ -176,7 +176,6 @@ export class MockUtil { if (brighterscript.isDottedGetExpression(arg0)) { let nameParts = getAllDottedGetParts(arg0); let name = nameParts.pop(); - console.log(nameParts[0], namespaceLookup.has(nameParts[0].toLowerCase())); if (name) { //is a namespace? diff --git a/bsc-plugin/src/lib/rooibos/TestGroup.ts b/bsc-plugin/src/lib/rooibos/TestGroup.ts index 64d6189f..50ae80d4 100644 --- a/bsc-plugin/src/lib/rooibos/TestGroup.ts +++ b/bsc-plugin/src/lib/rooibos/TestGroup.ts @@ -108,7 +108,6 @@ export class TestGroup extends TestBlock { isStubCall = nameText === 'stubCall'; if (isStubCall && (brighterscript.isDottedGetExpression(arg0) || brighterscript.isVariableExpression(arg0)) && brighterscript.isFunctionExpression(arg1)) { - console.log('modifyModernRooibosExpectCallExpression', callExpression.callee.name); return; } editor.setProperty(callExpression.callee.name, 'text', `_${nameText}`); From 8b3fee512c6ebdf2ebd9c0b939cc0a47ed404027 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Mon, 15 Jan 2024 13:39:51 -0400 Subject: [PATCH 09/30] Transpile the file if we had to touch modify it --- bsc-plugin/src/lib/rooibos/MockUtil.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.ts b/bsc-plugin/src/lib/rooibos/MockUtil.ts index ee79d41c..129be378 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.ts @@ -52,8 +52,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 +62,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; @@ -107,6 +107,7 @@ export class MockUtil { this.astEditor.arrayUnshift(functionStatement.func.body.statements, ...astCodeToInject); this.processedStatements.add(functionStatement); + file.needsTranspiled = true; } addBrsAPIText(file: BrsFile) { From 33079553dc415abb5cb119a7df358bb3d95acaea Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Mon, 15 Jan 2024 14:21:22 -0400 Subject: [PATCH 10/30] Fixed global mocks and stubs not clearing --- framework/src/source/BaseTestSuite.bs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/src/source/BaseTestSuite.bs b/framework/src/source/BaseTestSuite.bs index 3f42a567..170a5712 100644 --- a/framework/src/source/BaseTestSuite.bs +++ b/framework/src/source/BaseTestSuite.bs @@ -2268,6 +2268,10 @@ namespace rooibos ' * @description Cleans up all tracking data associated with mocks ' */ function cleanMocks() as void + ' Clean up the global functions mocks as well + globalAa = getGlobalAa() + globalAa.__globalStubs = invalid + if m.mocks = invalid return end if @@ -2277,10 +2281,6 @@ namespace rooibos end for m.mocks = invalid rooibos.resetMocksByFunctionName() - - ' Clean up the global functions mocks as well - globalAa = getGlobalAa() - globalAa.__globalStubs = invalid end function From a155a290be96f7d9111cbaca00fbe92b77bf72c6 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Tue, 16 Jan 2024 09:46:34 -0400 Subject: [PATCH 11/30] updated some of the checks around transforming stubcall functions --- bsc-plugin/src/lib/rooibos/MockUtil.ts | 2 +- bsc-plugin/src/lib/rooibos/TestGroup.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.ts b/bsc-plugin/src/lib/rooibos/MockUtil.ts index 129be378..61e6d09b 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.ts @@ -173,7 +173,7 @@ export class MockUtil { let arg1 = callExpression.args[1]; if (isStubCall) { - if (brighterscript.isFunctionExpression(arg1)) { + if (!brighterscript.isCallExpression(arg0)) { if (brighterscript.isDottedGetExpression(arg0)) { let nameParts = getAllDottedGetParts(arg0); let name = nameParts.pop(); diff --git a/bsc-plugin/src/lib/rooibos/TestGroup.ts b/bsc-plugin/src/lib/rooibos/TestGroup.ts index 50ae80d4..ad38bbe3 100644 --- a/bsc-plugin/src/lib/rooibos/TestGroup.ts +++ b/bsc-plugin/src/lib/rooibos/TestGroup.ts @@ -107,7 +107,7 @@ export class TestGroup extends TestBlock { isNotCalled = nameText === 'expectNotCalled'; isStubCall = nameText === 'stubCall'; - if (isStubCall && (brighterscript.isDottedGetExpression(arg0) || brighterscript.isVariableExpression(arg0)) && brighterscript.isFunctionExpression(arg1)) { + if (isStubCall && !brighterscript.isCallExpression(arg0)) { return; } editor.setProperty(callExpression.callee.name, 'text', `_${nameText}`); From a342165056d754ccd151267e0ab57d4842184cf2 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Tue, 16 Jan 2024 09:47:28 -0400 Subject: [PATCH 12/30] Made some code reusable and fixed some imports --- bsc-plugin/src/lib/rooibos/MockUtil.ts | 65 +++++++++----------- bsc-plugin/src/lib/rooibos/RooibosSession.ts | 14 +---- bsc-plugin/src/lib/rooibos/TestGroup.ts | 3 +- bsc-plugin/src/lib/rooibos/Utils.ts | 4 +- 4 files changed, 33 insertions(+), 53 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.ts b/bsc-plugin/src/lib/rooibos/MockUtil.ts index 61e6d09b..6da276a1 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-var-requires */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import type { BrsFile, Editor, NamespaceStatement, ProgramBuilder } from 'brighterscript'; -import { ParseMode, Parser, Position, TokenKind, WalkMode, createVisitor, isClassStatement, isNamespaceStatement } from 'brighterscript'; +import type { BrsFile, Editor, NamespaceContainer, NamespaceStatement, ProgramBuilder } from 'brighterscript'; +import { ParseMode, Parser, Position, WalkMode, createVisitor, isClassStatement, isNamespaceStatement } from 'brighterscript'; import * as brighterscript from 'brighterscript'; import type { RooibosConfig } from './RooibosConfig'; import { RawCodeStatement } from './RawCodeStatement'; @@ -175,22 +175,10 @@ export class MockUtil { if (isStubCall) { if (!brighterscript.isCallExpression(arg0)) { if (brighterscript.isDottedGetExpression(arg0)) { - let nameParts = getAllDottedGetParts(arg0); - let name = nameParts.pop(); + const functionName = this.getFinalNamespaceFunctionNameFromDottedGet(arg0, namespaceLookup); - 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); - } + if (functionName) { + this.session.globalStubbedMethods.add(functionName); } } else if (brighterscript.isVariableExpression(arg0)) { const functionName = arg0.getName(ParseMode.BrightScript).toLowerCase(); @@ -200,26 +188,10 @@ export class MockUtil { } if (brighterscript.isCallExpression(arg0) && brighterscript.isDottedGetExpression(arg0.callee)) { + const functionName = this.getFinalNamespaceFunctionNameFromDottedGet(arg0.callee, namespaceLookup); - //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); - } + if (functionName) { + this.session.globalStubbedMethods.add(functionName); } } else if (brighterscript.isCallExpression(arg0) && brighterscript.isVariableExpression(arg0.callee)) { let functionName = arg0.callee.getName(brighterscript.ParseMode.BrightScript).toLowerCase(); @@ -227,6 +199,27 @@ export class MockUtil { } } + + private getFinalNamespaceFunctionNameFromDottedGet(dg: brighterscript.DottedGetExpression, namespaceLookup: Map) { + //is it a namespace? + let nameParts = getAllDottedGetParts(dg); + let name = nameParts.pop(); + + 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); + return nameParts.join('_').toLowerCase(); + } + } + } + public addRuntimeGlobalFunctionMocks(file: BrsFile, astEditor: Editor) { file.ast.walk(createVisitor({ FunctionStatement: (statement, parent, owner, key) => { diff --git a/bsc-plugin/src/lib/rooibos/RooibosSession.ts b/bsc-plugin/src/lib/rooibos/RooibosSession.ts index 53e7fa88..70c9f73e 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) { diff --git a/bsc-plugin/src/lib/rooibos/TestGroup.ts b/bsc-plugin/src/lib/rooibos/TestGroup.ts index ad38bbe3..7e912c08 100644 --- a/bsc-plugin/src/lib/rooibos/TestGroup.ts +++ b/bsc-plugin/src/lib/rooibos/TestGroup.ts @@ -1,4 +1,4 @@ -import type { AstEditor, CallExpression, DottedGetExpression } from 'brighterscript'; +import type { AstEditor, CallExpression, DottedGetExpression, NamespaceContainer } from 'brighterscript'; import { ArrayLiteralExpression, createInvalidLiteral, createStringLiteral, createToken, isDottedGetExpression, TokenKind, isFunctionExpression, Parser } from 'brighterscript'; import * as brighterscript from 'brighterscript'; import { BrsTranspileState } from 'brighterscript/dist/parser/BrsTranspileState'; @@ -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 { diff --git a/bsc-plugin/src/lib/rooibos/Utils.ts b/bsc-plugin/src/lib/rooibos/Utils.ts index b507b7e7..df13beae 100644 --- a/bsc-plugin/src/lib/rooibos/Utils.ts +++ b/bsc-plugin/src/lib/rooibos/Utils.ts @@ -1,4 +1,5 @@ -import { type BrsFile, type ClassStatement, type Expression, type FunctionStatement, type AnnotationExpression, type AstEditor, TokenKind } from 'brighterscript'; +import type { AnnotationExpression, AstEditor, BrsFile, ClassStatement, Expression, FunctionStatement } from 'brighterscript'; +import { TokenKind } from 'brighterscript'; import * as brighterscript from 'brighterscript'; import { diagnosticCorruptTestProduced } from '../utils/Diagnostics'; @@ -52,7 +53,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)) { From 02c6f4e8d32a4eaba5f3cc233b0ce89805743a30 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Tue, 16 Jan 2024 10:51:48 -0400 Subject: [PATCH 13/30] Added back the disablemocking logic and removed unused code --- bsc-plugin/src/lib/rooibos/MockUtil.spec.ts | 37 +++++++++++++++ bsc-plugin/src/lib/rooibos/MockUtil.ts | 52 ++++++--------------- 2 files changed, 50 insertions(+), 39 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts b/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts index b14865c2..f0c44f26 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts @@ -399,6 +399,43 @@ describe('MockUtil', () => { }); + 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'); + let b = trimLeading(`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); + + }); + }); it('excludes files from coverage', async () => { diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.ts b/bsc-plugin/src/lib/rooibos/MockUtil.ts index 6da276a1..805c485f 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.ts @@ -79,6 +79,19 @@ 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! @@ -171,7 +184,6 @@ export class MockUtil { //modify args let arg0 = callExpression.args[0]; let arg1 = callExpression.args[1]; - if (isStubCall) { if (!brighterscript.isCallExpression(arg0)) { if (brighterscript.isDottedGetExpression(arg0)) { @@ -219,42 +231,4 @@ export class MockUtil { } } } - - public addRuntimeGlobalFunctionMocks(file: BrsFile, astEditor: Editor) { - file.ast.walk(createVisitor({ - FunctionStatement: (statement, parent, owner, key) => { - if (!file.pkgPath.includes('rooibos/') && !file.pkgPath.endsWith('spec.brs')) { - let isDisabledFoMocking = statement.annotations?.find(x => x.name.toLowerCase() === 'disablemocking'); - let parentNamespace = statement.findAncestor(isNamespaceStatement); - while (parentNamespace && !isDisabledFoMocking) { - if (parentNamespace) { - isDisabledFoMocking = parentNamespace.annotations?.find(x => x.name.toLowerCase() === 'disablemocking'); - parentNamespace = parentNamespace.findAncestor(isNamespaceStatement); - } - } - - if (!isDisabledFoMocking) { - const funcName = statement.getName(ParseMode.BrightScript); - const returnResult = functionRequiresReturnValue(statement); - const globalAaName = '__mocks_globalAa'; - const storageName = '_globalMocks'; - const template = undent` - ${globalAaName} = getGlobalAa() - if type(${globalAaName}?.${storageName}?.${funcName}) = "roFunction" or type(${globalAaName}?.${storageName}?.${funcName}) = "Function" then - __mockedFunction = ${globalAaName}.${storageName}.${funcName} - __mockResult = __mockedFunction(${statement.func.parameters.map(x => x.name.text).join(', ')}) - return ${returnResult ? '__mockResult' : ''} - end if - `; - const mockStatements = Parser.parse(template).ast.statements; - astEditor.arrayUnshift(statement.func.body.statements, ...mockStatements); - file.needsTranspiled = true; - } - } - } - }), { - walkMode: WalkMode.visitAllRecursive - }); - } - } From 02c64df7dcc7bbe715bd7f9cbdfc4b95b5ef0d07 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Tue, 16 Jan 2024 13:13:23 -0400 Subject: [PATCH 14/30] Fixed bad ast related to noEarlyExit --- bsc-plugin/src/lib/rooibos/TestGroup.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/TestGroup.ts b/bsc-plugin/src/lib/rooibos/TestGroup.ts index 7e912c08..bff566f9 100644 --- a/bsc-plugin/src/lib/rooibos/TestGroup.ts +++ b/bsc-plugin/src/lib/rooibos/TestGroup.ts @@ -74,10 +74,11 @@ export class TestGroup extends TestBlock { if (dge.name.text === 'expectCalled' || dge.name.text === 'expectNotCalled') { this.modifyModernRooibosExpectCallExpression(callExpression, editor, namespaceLookup); } - 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); } From 86656dd1258e111891905674c60eabcec933ce1e Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Tue, 16 Jan 2024 13:13:47 -0400 Subject: [PATCH 15/30] Updated bsc in tests app --- tests/package-lock.json | 754 +++++++++++++++++++++++++++++----------- tests/package.json | 4 +- 2 files changed, 546 insertions(+), 212 deletions(-) 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 +} From db7104879190447a7af9e91edd77dcd2d983b03f Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Tue, 16 Jan 2024 13:14:40 -0400 Subject: [PATCH 16/30] added tests for global stubcall on device --- tests/src/source/NewExpectSyntax.spec.bs | 195 +++++++++++++++++++++-- 1 file changed, 181 insertions(+), 14 deletions(-) diff --git a/tests/src/source/NewExpectSyntax.spec.bs b/tests/src/source/NewExpectSyntax.spec.bs index 7cf0e06a..a565db99 100644 --- a/tests/src/source/NewExpectSyntax.spec.bs +++ b/tests/src/source/NewExpectSyntax.spec.bs @@ -165,42 +165,209 @@ namespace tests end function '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - @describe("stubCall local and namespace functions with new runtime functions") + @describe("stubCall global functions with new runtime functions") '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - @it("supports function stubbing with a new function at runtime") + @it("stubs global with inline anon with return value") function _() m.wasCalled = false - m.stubCall(locationFunctionWithReturn, function() + m.stubCall(globalFunctionWithReturn, function() m.wasCalled = true return true end function) - m.assertTrue(locationFunctionWithReturn()) + + m.assertTrue(globalFunctionWithReturn()) m.assertTrue(m.wasCalled) + m.assertRunningTestIsPassed() + end function + + @it("stubs global with anon from variable with return value") + function _() m.wasCalled = false + stub = function() + m.wasCalled = true + return true + end function + m.stubCall(globalFunctionWithReturn, stub) - m.stubCall(locationFunctionWithoutReturn, sub() + m.assertTrue(globalFunctionWithReturn()) + m.assertTrue(m.wasCalled) + m.assertRunningTestIsPassed() + end function + + @it("stubs global with inline anon without return value") + function _() + m.wasCalled = false + m.stubCall(globalFunctionWithoutReturn, sub() m.wasCalled = true end sub) - m.assertInvalid(locationFunctionWithoutReturn()) + + m.assertInvalid(globalFunctionWithoutReturn()) m.assertTrue(m.wasCalled) + m.assertRunningTestIsPassed() + end function + + @it("stubs global with anon from variable without return value") + function _() m.wasCalled = false + m.stubCall(globalFunctionWithoutReturn, sub() + m.wasCalled = true + end sub) + + m.assertInvalid(globalFunctionWithoutReturn()) + m.assertTrue(m.wasCalled) + m.assertRunningTestIsPassed() + end function + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @describe("stubCall namespace functions with new runtime functions") + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + @it("stubs namespace with inline anon with return value") + function _() m.wasCalled = false - m.stubCall(textNamespace.functionWithReturn, function() + m.stubCall(testNamespace.functionWithReturn, function() m.wasCalled = true return true end function) - m.assertTrue(textNamespace.functionWithReturn()) + m.assertTrue(testNamespace.functionWithReturn()) + m.assertTrue(m.wasCalled) + m.assertRunningTestIsPassed() + end function + + @it("stubs namespace with anon from variable with return value") + function _() + m.wasCalled = false + stub = function() + m.wasCalled = true + return true + end function + m.stubCall(testNamespace.functionWithReturn, stub) + + m.assertTrue(testNamespace.functionWithReturn()) + m.assertTrue(m.wasCalled) + m.assertRunningTestIsPassed() + end function + + @it("stubs namespace with inline anon without return value") + function _() + m.wasCalled = false + m.stubCall(testNamespace.functionWithoutReturn, sub() + m.wasCalled = true + end sub) + m.assertInvalid(testNamespace.functionWithoutReturn()) + m.assertTrue(m.wasCalled) + m.assertRunningTestIsPassed() + end function + + @it("stubs namespace with anon from variable without return value") + function _() + m.wasCalled = false + m.stubCall(testNamespace.functionWithoutReturn, sub() + m.wasCalled = true + end sub) + m.assertInvalid(testNamespace.functionWithoutReturn()) + m.assertTrue(m.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 _() + m.wasCalled = false + m.stubCall(testNamespace.functionWithoutReturn, sub() + m.wasCalled = true + end sub) + + m.assertInvalid(testNamespace.functionWithoutReturn()) m.assertTrue(m.wasCalled) + + m.stubCall(testNamespace.functionWithoutReturn, sub() + m.wasCalled = 1 + end sub) + m.assertInvalid(testNamespace.functionWithoutReturn()) + m.assertEqual(m.wasCalled, 1) + + m.assertRunningTestIsPassed() + end function + + @it("stubs global multiple times in one test") + function _() m.wasCalled = false + m.stubCall(testNamespace.functionWithoutReturn, sub() + m.wasCalled = true + end sub) + + m.assertInvalid(testNamespace.functionWithoutReturn()) + m.assertTrue(m.wasCalled) + + m.stubCall(testNamespace.functionWithoutReturn, sub() + m.wasCalled = 1 + end sub) + m.assertInvalid(testNamespace.functionWithoutReturn()) + m.assertEqual(m.wasCalled, 1) + + m.assertRunningTestIsPassed() + end function - m.stubCall(textNamespace.functionWithoutReturn, sub() + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @describe("stubCall global can be cleaned") + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + @it("stubs namespace and then cleans it") + function _() + m.wasCalled = false + m.stubCall(testNamespace.functionWithoutReturn, sub() m.wasCalled = true end sub) - m.assertInvalid(textNamespace.functionWithoutReturn()) + + m.assertInvalid(testNamespace.functionWithoutReturn()) + m.assertTrue(m.wasCalled) + + m.cleanMocks() + + m.assertInvalid(testNamespace.functionWithoutReturn()) + m.assertFalse(m.wasCalled) + + m.assertRunningTestIsPassed() + end function + + @it("stubs global and then cleans it") + function _() + m.wasCalled = false + m.stubCall(globalFunctionWithoutReturn, sub() + m.wasCalled = true + end sub) + + m.assertInvalid(globalFunctionWithoutReturn()) m.assertTrue(m.wasCalled) + + m.cleanMocks() + + m.assertInvalid(globalFunctionWithoutReturn()) + m.assertFalse(m.wasCalled) + + m.assertRunningTestIsPassed() + end function + + @it("stubs global multiple times in one test") + function _() m.wasCalled = false + m.stubCall(globalFunctionWithoutReturn, sub() + m.wasCalled = true + end sub) + + m.assertInvalid(testNamespace.functionWithoutReturn()) + m.assertTrue(m.wasCalled) + + m.stubCall(testNamespace.functionWithoutReturn, sub() + m.wasCalled = 1 + end sub) + m.assertInvalid(testNamespace.functionWithoutReturn()) + m.assertEqual(m.wasCalled, 1) m.assertRunningTestIsPassed() end function @@ -434,22 +601,22 @@ namespace tests end namespace -function locationFunctionWithReturn() as dynamic +function globalFunctionWithReturn() as dynamic m.wasCalled = false return false end function -sub locationFunctionWithoutReturn() +sub globalFunctionWithoutReturn() m.wasCalled = false end sub -namespace textNamespace +namespace testNamespace function functionWithReturn() as dynamic m.wasCalled = false return false end function - sub FunctionWithoutReturn() + sub functionWithoutReturn() m.wasCalled = false end sub end namespace \ No newline at end of file From 4c3d753ac31aec95b46aa19c937f30d426b9ba51 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Tue, 16 Jan 2024 13:27:22 -0400 Subject: [PATCH 17/30] Fixed some device tests --- tests/src/source/Main.bs | 22 +++++- tests/src/source/NewExpectSyntax.spec.bs | 95 +++++++----------------- 2 files changed, 49 insertions(+), 68 deletions(-) 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 a565db99..1dc3aca4 100644 --- a/tests/src/source/NewExpectSyntax.spec.bs +++ b/tests/src/source/NewExpectSyntax.spec.bs @@ -170,20 +170,20 @@ namespace tests @it("stubs global with inline anon with return value") function _() - m.wasCalled = false + getGlobalAA().wasCalled = false m.stubCall(globalFunctionWithReturn, function() m.wasCalled = true return true end function) m.assertTrue(globalFunctionWithReturn()) - m.assertTrue(m.wasCalled) + m.assertTrue(getGlobalAA().wasCalled) m.assertRunningTestIsPassed() end function @it("stubs global with anon from variable with return value") function _() - m.wasCalled = false + getGlobalAA().wasCalled = false stub = function() m.wasCalled = true return true @@ -191,31 +191,31 @@ namespace tests m.stubCall(globalFunctionWithReturn, stub) m.assertTrue(globalFunctionWithReturn()) - m.assertTrue(m.wasCalled) + m.assertTrue(getGlobalAA().wasCalled) m.assertRunningTestIsPassed() end function @it("stubs global with inline anon without return value") function _() - m.wasCalled = false + getGlobalAA().wasCalled = false m.stubCall(globalFunctionWithoutReturn, sub() m.wasCalled = true end sub) m.assertInvalid(globalFunctionWithoutReturn()) - m.assertTrue(m.wasCalled) + m.assertTrue(getGlobalAA().wasCalled) m.assertRunningTestIsPassed() end function @it("stubs global with anon from variable without return value") function _() - m.wasCalled = false + getGlobalAA().wasCalled = false m.stubCall(globalFunctionWithoutReturn, sub() m.wasCalled = true end sub) m.assertInvalid(globalFunctionWithoutReturn()) - m.assertTrue(m.wasCalled) + m.assertTrue(getGlobalAA().wasCalled) m.assertRunningTestIsPassed() end function @@ -225,19 +225,19 @@ namespace tests @it("stubs namespace with inline anon with return value") function _() - m.wasCalled = false + getGlobalAA().wasCalled = false m.stubCall(testNamespace.functionWithReturn, function() m.wasCalled = true return true end function) m.assertTrue(testNamespace.functionWithReturn()) - m.assertTrue(m.wasCalled) + m.assertTrue(getGlobalAA().wasCalled) m.assertRunningTestIsPassed() end function @it("stubs namespace with anon from variable with return value") function _() - m.wasCalled = false + getGlobalAA().wasCalled = false stub = function() m.wasCalled = true return true @@ -245,29 +245,29 @@ namespace tests m.stubCall(testNamespace.functionWithReturn, stub) m.assertTrue(testNamespace.functionWithReturn()) - m.assertTrue(m.wasCalled) + m.assertTrue(getGlobalAA().wasCalled) m.assertRunningTestIsPassed() end function @it("stubs namespace with inline anon without return value") function _() - m.wasCalled = false + getGlobalAA().wasCalled = false m.stubCall(testNamespace.functionWithoutReturn, sub() m.wasCalled = true end sub) m.assertInvalid(testNamespace.functionWithoutReturn()) - m.assertTrue(m.wasCalled) + m.assertTrue(getGlobalAA().wasCalled) m.assertRunningTestIsPassed() end function @it("stubs namespace with anon from variable without return value") function _() - m.wasCalled = false + getGlobalAA().wasCalled = false m.stubCall(testNamespace.functionWithoutReturn, sub() m.wasCalled = true end sub) m.assertInvalid(testNamespace.functionWithoutReturn()) - m.assertTrue(m.wasCalled) + m.assertTrue(getGlobalAA().wasCalled) m.assertRunningTestIsPassed() end function @@ -277,38 +277,38 @@ namespace tests @it("stubs namespace multiple times in one test") function _() - m.wasCalled = false + getGlobalAA().wasCalled = false m.stubCall(testNamespace.functionWithoutReturn, sub() m.wasCalled = true end sub) m.assertInvalid(testNamespace.functionWithoutReturn()) - m.assertTrue(m.wasCalled) + m.assertTrue(getGlobalAA().wasCalled) m.stubCall(testNamespace.functionWithoutReturn, sub() m.wasCalled = 1 end sub) m.assertInvalid(testNamespace.functionWithoutReturn()) - m.assertEqual(m.wasCalled, 1) + m.assertEqual(getGlobalAA().wasCalled, 1) m.assertRunningTestIsPassed() end function @it("stubs global multiple times in one test") function _() - m.wasCalled = false + getGlobalAA().wasCalled = false m.stubCall(testNamespace.functionWithoutReturn, sub() m.wasCalled = true end sub) m.assertInvalid(testNamespace.functionWithoutReturn()) - m.assertTrue(m.wasCalled) + m.assertTrue(getGlobalAA().wasCalled) m.stubCall(testNamespace.functionWithoutReturn, sub() m.wasCalled = 1 end sub) m.assertInvalid(testNamespace.functionWithoutReturn()) - m.assertEqual(m.wasCalled, 1) + m.assertEqual(getGlobalAA().wasCalled, 1) m.assertRunningTestIsPassed() end function @@ -319,55 +319,36 @@ namespace tests @it("stubs namespace and then cleans it") function _() - m.wasCalled = false + getGlobalAA().wasCalled = false m.stubCall(testNamespace.functionWithoutReturn, sub() m.wasCalled = true end sub) m.assertInvalid(testNamespace.functionWithoutReturn()) - m.assertTrue(m.wasCalled) + m.assertTrue(getGlobalAA().wasCalled) m.cleanMocks() m.assertInvalid(testNamespace.functionWithoutReturn()) - m.assertFalse(m.wasCalled) + m.assertFalse(getGlobalAA().wasCalled) m.assertRunningTestIsPassed() end function @it("stubs global and then cleans it") function _() - m.wasCalled = false + getGlobalAA().wasCalled = false m.stubCall(globalFunctionWithoutReturn, sub() m.wasCalled = true end sub) m.assertInvalid(globalFunctionWithoutReturn()) - m.assertTrue(m.wasCalled) + m.assertTrue(getGlobalAA().wasCalled) m.cleanMocks() m.assertInvalid(globalFunctionWithoutReturn()) - m.assertFalse(m.wasCalled) - - m.assertRunningTestIsPassed() - end function - - @it("stubs global multiple times in one test") - function _() - m.wasCalled = false - m.stubCall(globalFunctionWithoutReturn, sub() - m.wasCalled = true - end sub) - - m.assertInvalid(testNamespace.functionWithoutReturn()) - m.assertTrue(m.wasCalled) - - m.stubCall(testNamespace.functionWithoutReturn, sub() - m.wasCalled = 1 - end sub) - m.assertInvalid(testNamespace.functionWithoutReturn()) - m.assertEqual(m.wasCalled, 1) + m.assertFalse(getGlobalAA().wasCalled) m.assertRunningTestIsPassed() end function @@ -600,23 +581,3 @@ namespace tests end class end namespace - -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 From b4024ee003626a9e38c2b3571db90e4720227437 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Tue, 16 Jan 2024 15:40:25 -0400 Subject: [PATCH 18/30] Fixed more on device tests --- bsc-plugin/src/lib/rooibos/TestGroup.ts | 48 ++++++++++++++++++++----- bsc-plugin/src/plugin.spec.ts | 6 ++-- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/TestGroup.ts b/bsc-plugin/src/lib/rooibos/TestGroup.ts index bff566f9..b9f081c1 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, NamespaceContainer } 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'; @@ -39,7 +39,6 @@ export class TestGroup extends TestBlock { } else { this.hasAsyncTests = testCase.isAsync; } - } public getTestCases(): TestCase[] { @@ -51,6 +50,7 @@ export class TestGroup extends TestBlock { //if assertion //wrap with if is not fail //add line number as last param + let scope = this.file.program.getFirstScopeForFile(this.file); const transpileState = new BrsTranspileState(this.file); try { let func = this.testSuite.classStatement.methods.find((m) => m.name.text.toLowerCase() === testCase.funcName.toLowerCase()); @@ -63,16 +63,16 @@ 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); } if (!noEarlyExit) { @@ -95,7 +95,7 @@ export class TestGroup extends TestBlock { } } - private modifyModernRooibosExpectCallExpression(callExpression: CallExpression, editor: AstEditor, namespaceLookup: Map) { + private modifyModernRooibosExpectCallExpression(callExpression: CallExpression, editor: AstEditor, namespaceLookup: Map, scope: Scope) { let isNotCalled = false; let isStubCall = false; @@ -107,7 +107,7 @@ export class TestGroup extends TestBlock { isNotCalled = nameText === 'expectNotCalled'; isStubCall = nameText === 'stubCall'; - if (isStubCall && !brighterscript.isCallExpression(arg0)) { + if (isStubCall && this.shouldNotModifyStubCall(arg0, namespaceLookup, scope)) { return; } editor.setProperty(callExpression.callee.name, 'text', `_${nameText}`); @@ -198,6 +198,38 @@ export class TestGroup extends TestBlock { } } + private shouldNotModifyStubCall(arg0: Expression, namespaceLookup: Map, scope: Scope) { + if (brighterscript.isDottedGetExpression(arg0)) { + let nameParts = getAllDottedGetParts(arg0); + let name = nameParts.pop(); + let functionName: string; + + 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); + functionName = nameParts.join('.').toLowerCase(); + } + } + + if (functionName && scope.getCallableByName(functionName)) { + return true; + } + } else if (brighterscript.isVariableExpression(arg0)) { + const functionName = arg0.getName(ParseMode.BrightScript).toLowerCase(); + if (scope.getCallableByName(functionName)) { + return true; + } + } + return false; + } + public asText(): string { let testCaseText = [...this.testCases.values()].filter((tc) => tc.isIncluded).map((tc) => tc.asText()); diff --git a/bsc-plugin/src/plugin.spec.ts b/bsc-plugin/src/plugin.spec.ts index ca66f89e..f2d9a761 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 @@ -1125,7 +1125,7 @@ describe('RooibosPlugin', () => { }); }); - describe.skip('stubCall transpilation', () => { + describe('stubCall transpilation', () => { it('correctly transpiles call funcs', async () => { program.setFile('source/test.spec.bs', ` @suite @@ -1267,7 +1267,7 @@ describe('RooibosPlugin', () => { }); }); - describe.skip('expectNotCalled transpilation', () => { + describe('expectNotCalled transpilation', () => { it('correctly transpiles call funcs', async () => { program.setFile('source/test.spec.bs', ` @suite From 67ec05ae087373616189f8c57088e83f1d18c183 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Tue, 16 Jan 2024 16:24:14 -0400 Subject: [PATCH 19/30] More test fixes as the result of moving to ast editor --- bsc-plugin/src/lib/rooibos/MockUtil.spec.ts | 430 ++++++++++---------- bsc-plugin/src/lib/rooibos/MockUtil.ts | 5 +- bsc-plugin/src/plugin.spec.ts | 396 ++++++++++-------- 3 files changed, 454 insertions(+), 377 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts b/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts index f0c44f26..8bb250b4 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,34 +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) - __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 + 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); }); @@ -105,195 +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) - __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 + 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) - __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 + 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) - __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 + 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) - __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 + 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) - __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 + 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); }); @@ -346,55 +357,56 @@ 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 + 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 + instance = __beings_Person_builder() + instance.new() + return instance 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" + __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() - __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" + __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 + if m._rMocksByFunctionName = invalid + m._rMocksByFunctionName = {} + end if + return m._rMocksByFunctionName 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 805c485f..3bd264e7 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.ts @@ -124,9 +124,8 @@ export class MockUtil { } 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); } diff --git a/bsc-plugin/src/plugin.spec.ts b/bsc-plugin/src/plugin.spec.ts index f2d9a761..a248e164 100644 --- a/bsc-plugin/src/plugin.spec.ts +++ b/bsc-plugin/src/plugin.spec.ts @@ -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,38 +1019,43 @@ 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'); @@ -1023,11 +1063,11 @@ describe('RooibosPlugin', () => { function sayHello(firstName = "", lastName = "") __stubs_globalAa = getGlobalAa() if RBS_SM_1_getMocksByFunctionName()["sayhello"] <> invalid - __stubOrMockResult = RBS_SM_1_getMocksByFunctionName()["sayhello"].callback(firstName,lastName) + __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() + __stubOrMockResult = __stubFunction(firstName, lastName) return __stubOrMockResult end if print firstName + " " + lastName @@ -1035,7 +1075,7 @@ describe('RooibosPlugin', () => { function RBS_SM_1_getMocksByFunctionName() if m._rMocksByFunctionName = invalid - m._rMocksByFunctionName = {} + m._rMocksByFunctionName = {} end if return m._rMocksByFunctionName end function`); @@ -1067,50 +1107,55 @@ 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 - - + 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 - - + 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 - - + 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 + if m.currentResult?.isFail = true then + m.done() + return invalid + end if `); - let codeText = trimLeading(getContents('code.brs')); - expect(codeText).to.equal(trimLeading(` + 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) + __stubOrMockResult = RBS_SM_1_getMocksByFunctionName()["utils_sayhello"].callback(firstName, lastName) return __stubOrMockResult - else if type(__stubs_globalAa?.__globalStubs?.beings_sayHello).endsWith("Function") - __stubFunction = __stubs_globalAa.__globalStubs.beings_sayHello - __stubOrMockResult = __stubFunction() + 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 @@ -1118,10 +1163,11 @@ describe('RooibosPlugin', () => { function RBS_SM_1_getMocksByFunctionName() if m._rMocksByFunctionName = invalid - m._rMocksByFunctionName = {} + m._rMocksByFunctionName = {} end if return m._rMocksByFunctionName - end function`)); + end function + `)); }); }); @@ -1291,22 +1337,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 `); }); @@ -1331,15 +1383,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); @@ -1376,7 +1431,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); @@ -1410,23 +1468,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 () => { @@ -1446,22 +1509,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 `); }); }); From bcfd24d91b57bba513bca471ebbd8a7d088001e1 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Tue, 16 Jan 2024 16:28:31 -0400 Subject: [PATCH 20/30] Fixed some indenting --- bsc-plugin/src/lib/rooibos/MockUtil.spec.ts | 48 +++++++++++---------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts b/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts index 8bb250b4..9d2f9de1 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts @@ -413,37 +413,39 @@ describe('MockUtil', () => { 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 - `); + @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'); - let b = trimLeading(`function beings_sayHello() - print "hello2" + let a = getContents('source/code.brs', false); + let b = undent(` + function beings_sayHello() + print "hello2" end function function aliens_sayHello() - print "hello3" + print "hello3" end function function sayHello() - print "hello4" - end function`); + print "hello4" + end function + `); expect(a).to.equal(b); }); From 9f4eabd9a5c36f641afff9b4f503f32ebe5eb975 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Wed, 17 Jan 2024 08:55:15 -0400 Subject: [PATCH 21/30] Fixed node test xml files being added after modifying assertions leading to crashes --- bsc-plugin/src/plugin.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bsc-plugin/src/plugin.ts b/bsc-plugin/src/plugin.ts index d89721df..0f6f2c14 100644 --- a/bsc-plugin/src/plugin.ts +++ b/bsc-plugin/src/plugin.ts @@ -154,14 +154,14 @@ export class RooibosPlugin implements CompilerPlugin { } testSuite.addDataFunctions(event.editor as any); + if (testSuite.isNodeTest) { + this.session.createNodeFile(event.program, testSuite); + } 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); } } - if (testSuite.isNodeTest) { - this.session.createNodeFile(event.program, testSuite); - } } if (isBrsFile(event.file)) { From 3acbf16d6041871dfa54e42738293ee56389a114 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Wed, 17 Jan 2024 10:00:31 -0400 Subject: [PATCH 22/30] Updated the modify stub detection logic for globals --- bsc-plugin/src/lib/rooibos/TestGroup.ts | 32 ++++++------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/TestGroup.ts b/bsc-plugin/src/lib/rooibos/TestGroup.ts index b9f081c1..946212d0 100644 --- a/bsc-plugin/src/lib/rooibos/TestGroup.ts +++ b/bsc-plugin/src/lib/rooibos/TestGroup.ts @@ -89,7 +89,7 @@ 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); } @@ -201,31 +201,13 @@ export class TestGroup extends TestBlock { private shouldNotModifyStubCall(arg0: Expression, namespaceLookup: Map, scope: Scope) { if (brighterscript.isDottedGetExpression(arg0)) { let nameParts = getAllDottedGetParts(arg0); - let name = nameParts.pop(); - let functionName: string; - - 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); - functionName = nameParts.join('.').toLowerCase(); - } - } - - if (functionName && scope.getCallableByName(functionName)) { - return true; - } + let functionName = nameParts.join('.'); + return scope.getCallableByName(functionName); } else if (brighterscript.isVariableExpression(arg0)) { - const functionName = arg0.getName(ParseMode.BrightScript).toLowerCase(); - if (scope.getCallableByName(functionName)) { - return true; - } + return ( + arg0.getSymbolTable().hasSymbol(arg0.getName(ParseMode.BrightScript)) || + scope.getCallableByName(arg0.getName(ParseMode.BrighterScript)) + ); } return false; } From 1be78f611bfa0fc6b247185dc78d2bc876b1772f Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Wed, 17 Jan 2024 10:01:04 -0400 Subject: [PATCH 23/30] more tests for global stub call modifications --- bsc-plugin/src/plugin.spec.ts | 311 +++++++++++++++++++++++++++++++++- 1 file changed, 306 insertions(+), 5 deletions(-) diff --git a/bsc-plugin/src/plugin.spec.ts b/bsc-plugin/src/plugin.spec.ts index a248e164..2038e661 100644 --- a/bsc-plugin/src/plugin.spec.ts +++ b/bsc-plugin/src/plugin.spec.ts @@ -1311,6 +1311,308 @@ 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('expectNotCalled transpilation', () => { @@ -1909,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) { From 3164492c7d86860890242053ad47e23f418093b0 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Wed, 17 Jan 2024 12:09:15 -0400 Subject: [PATCH 24/30] Fixed some race conditons and more global function detection refinments --- bsc-plugin/src/lib/rooibos/MockUtil.ts | 87 +++++++++----------- bsc-plugin/src/lib/rooibos/RooibosSession.ts | 3 + bsc-plugin/src/lib/rooibos/TestGroup.ts | 2 +- bsc-plugin/src/plugin.ts | 3 - 4 files changed, 44 insertions(+), 51 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.ts b/bsc-plugin/src/lib/rooibos/MockUtil.ts index 3bd264e7..ac8b9d0b 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.ts @@ -1,11 +1,9 @@ /* eslint-disable @typescript-eslint/no-var-requires */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import type { BrsFile, Editor, NamespaceContainer, NamespaceStatement, ProgramBuilder } from 'brighterscript'; -import { ParseMode, Parser, Position, WalkMode, createVisitor, isClassStatement, isNamespaceStatement } 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'; @@ -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; @@ -149,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); } } } @@ -171,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 = testSuite.file.program.getFirstScopeForFile(testSuite.file); const namespaceLookup = this.session.namespaceLookup; if (brighterscript.isDottedGetExpression(callExpression.callee)) { const nameText = callExpression.callee.name.text; @@ -183,51 +182,45 @@ export class MockUtil { //modify args let arg0 = callExpression.args[0]; let arg1 = callExpression.args[1]; - if (isStubCall) { - if (!brighterscript.isCallExpression(arg0)) { - if (brighterscript.isDottedGetExpression(arg0)) { - const functionName = this.getFinalNamespaceFunctionNameFromDottedGet(arg0, namespaceLookup); - if (functionName) { - this.session.globalStubbedMethods.add(functionName); - } - } else if (brighterscript.isVariableExpression(arg0)) { - const functionName = arg0.getName(ParseMode.BrightScript).toLowerCase(); - this.session.globalStubbedMethods.add(functionName); - } + if (isStubCall) { + let functionName = this.getGlobalFunctionName(arg0, scope); + if (functionName) { + this.session.globalStubbedMethods.add(functionName.toLowerCase()); + return; } } - if (brighterscript.isCallExpression(arg0) && brighterscript.isDottedGetExpression(arg0.callee)) { - const functionName = this.getFinalNamespaceFunctionNameFromDottedGet(arg0.callee, namespaceLookup); - + if (brighterscript.isCallExpression(arg0)) { + let functionName = this.getGlobalFunctionName(arg0.callee, scope); if (functionName) { - this.session.globalStubbedMethods.add(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 getFinalNamespaceFunctionNameFromDottedGet(dg: brighterscript.DottedGetExpression, namespaceLookup: Map) { - //is it a namespace? - let nameParts = getAllDottedGetParts(dg); - let name = nameParts.pop(); + 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; + } - 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); - return nameParts.join('_').toLowerCase(); + 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 70c9f73e..4410d7f5 100644 --- a/bsc-plugin/src/lib/rooibos/RooibosSession.ts +++ b/bsc-plugin/src/lib/rooibos/RooibosSession.ts @@ -46,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 946212d0..cf83f282 100644 --- a/bsc-plugin/src/lib/rooibos/TestGroup.ts +++ b/bsc-plugin/src/lib/rooibos/TestGroup.ts @@ -205,7 +205,7 @@ export class TestGroup extends TestBlock { return scope.getCallableByName(functionName); } else if (brighterscript.isVariableExpression(arg0)) { return ( - arg0.getSymbolTable().hasSymbol(arg0.getName(ParseMode.BrightScript)) || + scope.symbolTable.hasSymbol(arg0.getName(ParseMode.BrightScript)) || scope.getCallableByName(arg0.getName(ParseMode.BrighterScript)) ); } diff --git a/bsc-plugin/src/plugin.ts b/bsc-plugin/src/plugin.ts index 0f6f2c14..fc529240 100644 --- a/bsc-plugin/src/plugin.ts +++ b/bsc-plugin/src/plugin.ts @@ -154,9 +154,6 @@ export class RooibosPlugin implements CompilerPlugin { } testSuite.addDataFunctions(event.editor as any); - if (testSuite.isNodeTest) { - this.session.createNodeFile(event.program, testSuite); - } 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); From ec6a10561289bcaccfc5005a148feb9388a09e24 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Wed, 17 Jan 2024 13:25:19 -0400 Subject: [PATCH 25/30] Removed some raw code statements for the session and fixed test formatting at a result --- bsc-plugin/src/lib/rooibos/RooibosSession.ts | 35 ++++++++++---------- bsc-plugin/src/plugin.spec.ts | 28 +++++----------- 2 files changed, 26 insertions(+), 37 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/RooibosSession.ts b/bsc-plugin/src/lib/rooibos/RooibosSession.ts index 4410d7f5..adb07734 100644 --- a/bsc-plugin/src/lib/rooibos/RooibosSession.ts +++ b/bsc-plugin/src/lib/rooibos/RooibosSession.ts @@ -1,11 +1,10 @@ import * as path from 'path'; import type { BrsFile, ClassStatement, FunctionStatement, NamespaceContainer, NamespaceStatement, Program, ProgramBuilder, Scope } from 'brighterscript'; -import { isBrsFile, isCallExpression, isVariableExpression, ParseMode, WalkMode } from 'brighterscript'; +import { isBrsFile, isCallExpression, isVariableExpression, ParseMode, Parser, WalkMode } from 'brighterscript'; import type { AstEditor } from 'brighterscript/dist/astUtils/AstEditor'; import type { RooibosConfig } from './RooibosConfig'; import { SessionInfo } from './RooibosSessionInfo'; import { TestSuiteBuilder } from './TestSuiteBuilder'; -import { RawCodeStatement } from './RawCodeStatement'; import type { FileFactory } from './FileFactory'; import type { TestSuite } from './TestSuite'; import { diagnosticErrorNoMainFound as diagnosticWarnNoMainFound, diagnosticNoStagingDir } from '../utils/Diagnostics'; @@ -83,7 +82,7 @@ export class RooibosSession { walkMode: WalkMode.visitAllRecursive }); if (!initCall) { - editor.addToArray(mainFunction.func.body.statements, 0, new RawCodeStatement(`Rooibos_init("${this.config?.testSceneName ?? 'RooibosScene'}")`)); + editor.addToArray(mainFunction.func.body.statements, 0, Parser.parse(`Rooibos_init("${this.config?.testSceneName ?? 'RooibosScene'}")`).ast.statements[0]); } } } @@ -130,7 +129,7 @@ export class RooibosSession { editor.addToArray( method.func.body.statements, method.func.body.statements.length, - new RawCodeStatement(undent` + Parser.parse(undent` return { "reporter": "${this.config.reporter || ''}" "failFast": ${this.config.failFast ? 'true' : 'false'} @@ -144,8 +143,8 @@ export class RooibosSession { "catchCrashes": ${this.config.catchCrashes ? 'true' : 'false'} "throwOnFailedAssertion": ${this.config.throwOnFailedAssertion ? 'true' : 'false'} "keepAppOpen": ${this.config.keepAppOpen === undefined || this.config.keepAppOpen ? 'true' : 'false'} - }` - ) + } + `).ast.statements[0] ); } } @@ -156,7 +155,7 @@ export class RooibosSession { editor.addToArray( method.func.body.statements, method.func.body.statements.length, - new RawCodeStatement(`return "${pkg.version}"`) + Parser.parse(`return "${pkg.version}"`).ast.statements[0] ); } } @@ -164,24 +163,24 @@ export class RooibosSession { updateClassLookupFunction(classStatement: ClassStatement, editor: AstEditor) { let method = classStatement.methods.find((m) => m.name.text === 'getTestSuiteClassWithName'); if (method) { - editor.arrayPush(method.func.body.statements, new RawCodeStatement(undent` + editor.arrayPush(method.func.body.statements, ...Parser.parse(undent` if false ? "noop" ${this.sessionInfo.testSuitesToRun.map(suite => ` else if name = "${suite.name}" return ${suite.classStatement.getName(ParseMode.BrightScript)}`).join('')} end if - `)); + `).ast.statements); } } updateGetAllTestSuitesNames(classStatement: ClassStatement, editor: AstEditor) { let method = classStatement.methods.find((m) => m.name.text === 'getAllTestSuitesNames'); if (method) { - editor.arrayPush(method.func.body.statements, new RawCodeStatement([ + editor.arrayPush(method.func.body.statements, ...Parser.parse([ 'return [', ...this.sessionInfo.testSuitesToRun.map((s) => ` "${s.name}"`), ']' - ].join('\n'))); + ].join('\n')).ast.statements); } } @@ -204,10 +203,10 @@ export class RooibosSession { bsFile.needsTranspiled = true; } let brsFile = this.fileFactory.addFile(program, bsPath, undent` - import "pkg:/${suite.file.pkgPath}" - function init() - nodeRunner = Rooibos_TestRunner(m.top.getScene(), m) - m.top.rooibosTestResult = nodeRunner.runInNodeMode("${suite.name}") + import "pkg:/${suite.file.pkgPath}" + function init() + nodeRunner = Rooibos_TestRunner(m.top.getScene(), m) + m.top.rooibosTestResult = nodeRunner.runInNodeMode("${suite.name}") end function `); brsFile.parser.invalidateReferences(); @@ -248,14 +247,14 @@ export class RooibosSession { private createIgnoredTestsInfoFunction(cs: ClassStatement, editor: AstEditor) { let method = cs.methods.find((m) => m.name.text === 'getIgnoredTestInfo'); if (method) { - editor.arrayPush(method.func.body.statements, new RawCodeStatement([ + editor.arrayPush(method.func.body.statements, ...Parser.parse([ 'return {', ` "count": ${this.sessionInfo.ignoredCount}`, ` "items": [`, - ...this.sessionInfo.ignoredTestNames.map((name) => ` "${name}"`), + ...this.sessionInfo.ignoredTestNames.map((name) => `"${name}"`), ` ]`, `}` - ].join('\n'))); + ].join('\n')).ast.statements); } } } diff --git a/bsc-plugin/src/plugin.spec.ts b/bsc-plugin/src/plugin.spec.ts index 2038e661..cb644ebd 100644 --- a/bsc-plugin/src/plugin.spec.ts +++ b/bsc-plugin/src/plugin.spec.ts @@ -2004,24 +2004,24 @@ describe('RooibosPlugin', () => { expect(findMethod('getIgnoredTestInfo').func.body.statements).to.be.empty; await builder.transpile(); - let testContents = getTestFunctionContents(true); + let 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 + 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 + m.done() + return invalid end if `); @@ -2070,8 +2070,7 @@ describe('RooibosPlugin', () => { instance.getIgnoredTestInfo = function() return { "count": 0 - "items": [ - ] + "items": [] } end function return instance @@ -2209,29 +2208,20 @@ function getContents(filename: string) { ); } -function getTestFunctionContents(trimEveryLine = false) { +function getTestFunctionContents() { const contents = getContents('test.spec.brs'); let [, result] = /instance.[\w_]+\s?\= function\(\)\s?([\S\s]*|.*)(?=^\s*end function\s+instance\.)/img.exec(contents); - if (trimEveryLine) { - result = trim(result); - } return undent(result); } -function getTestSubContents(trimEveryLine = false) { +function getTestSubContents() { const contents = getContents('test.spec.brs'); const [, body] = /groupA_test1 \= sub\(\)([\S\s]*|.*)(?=end sub)/gim.exec(contents); let result = undent( body.split('end sub')[0] ); - if (trimEveryLine) { - result = trim(result); - } return result; } -function trimLeading(text: string) { - return text.split('\n').map((line) => line.trimStart()).join('\n'); -} From fb0e8529e1748131fca57b13f631ebe48cba3748 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Wed, 17 Jan 2024 13:32:43 -0400 Subject: [PATCH 26/30] removed trimLeading from mock util tests --- bsc-plugin/src/lib/rooibos/MockUtil.spec.ts | 91 ++++++++++----------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts b/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts index 9d2f9de1..8f963933 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts @@ -10,22 +10,15 @@ let tmpPath = s`${process.cwd()}/tmp`; let _rootDir = s`${tmpPath}/rootDir`; let _stagingFolderPath = s`${tmpPath}/staging`; -function trimLeading(text: string) { - return text.split('\n').map((line) => line.trimStart()).join('\n'); -} - describe('MockUtil', () => { let program: Program; let builder: ProgramBuilder; let plugin: RooibosPlugin; let options; - function getContents(filename: string, trim = true) { + function getContents(filename: string) { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument let contents = fsExtra.readFileSync(s`${_stagingFolderPath}/${filename}`).toString(); - if (trim) { - return trimLeading(contents); - } return contents; } @@ -81,7 +74,7 @@ describe('MockUtil', () => { program.validate(); expect(program.getDiagnostics()).to.be.empty; await builder.transpile(); - let a = getContents('source/code.brs', false); + let a = getContents('source/code.brs'); let b = undent(` function sayHello(a1, a2) __stubs_globalAa = getGlobalAa() @@ -118,7 +111,7 @@ describe('MockUtil', () => { program.validate(); expect(program.getDiagnostics()).to.be.empty; await builder.transpile(); - let a = getContents('source/code.brs', false); + let a = getContents('source/code.brs'); let b = undent(` function sayHello(a1, a2) __stubs_globalAa = getGlobalAa() @@ -158,7 +151,7 @@ describe('MockUtil', () => { program.validate(); expect(program.getDiagnostics()).to.be.empty; await builder.transpile(); - let a = getContents('source/code.brs', false); + let a = getContents('source/code.brs'); let b = undent(` Sub RedLines_SetRulerLines(rulerLines) __stubs_globalAa = getGlobalAa() @@ -209,7 +202,7 @@ describe('MockUtil', () => { program.validate(); expect(program.getDiagnostics()).to.be.empty; await builder.transpile(); - let a = getContents('source/code.brs', false); + let a = getContents('source/code.brs'); let b = undent(` sub sayHello(a1, a2) __stubs_globalAa = getGlobalAa() @@ -246,7 +239,7 @@ describe('MockUtil', () => { program.validate(); expect(program.getDiagnostics()).to.be.empty; await builder.transpile(); - let a = getContents('source/code.brs', false); + let a = getContents('source/code.brs'); let b = undent(` function person_utils_sayHello(a1, a2) __stubs_globalAa = getGlobalAa() @@ -283,7 +276,7 @@ describe('MockUtil', () => { program.validate(); expect(program.getDiagnostics()).to.be.empty; await builder.transpile(); - let a = getContents('source/code.brs', false); + let a = getContents('source/code.brs'); let b = undent(` sub person_utils_sayHello(a1, a2) __stubs_globalAa = getGlobalAa() @@ -321,20 +314,22 @@ describe('MockUtil', () => { expect(program.getDiagnostics()).to.be.empty; await builder.transpile(); let a = getContents('source/code.brs'); - let b = trimLeading(`function __Person_builder() - instance = {} - instance.new = sub() - end sub - instance.sayHello = sub(a1, a2) - print "hello" - end sub - return instance - end function - function Person() - instance = __Person_builder() - instance.new() - return instance - end function`); + let b = undent(` + function __Person_builder() + instance = {} + instance.new = sub() + end sub + instance.sayHello = sub(a1, a2) + print "hello" + end sub + return instance + end function + function Person() + instance = __Person_builder() + instance.new() + return instance + end function + `); expect(a).to.equal(b); }); @@ -357,7 +352,7 @@ describe('MockUtil', () => { program.validate(); expect(program.getDiagnostics()).to.be.empty; await builder.transpile(); - let a = getContents('source/code.brs', false); + let a = getContents('source/code.brs'); let b = undent(` function __beings_Person_builder() instance = {} @@ -433,7 +428,7 @@ describe('MockUtil', () => { program.validate(); expect(program.getDiagnostics()).to.be.empty; await builder.transpile(); - let a = getContents('source/code.brs', false); + let a = getContents('source/code.brs'); let b = undent(` function beings_sayHello() print "hello2" @@ -453,14 +448,16 @@ describe('MockUtil', () => { }); it('excludes files from coverage', async () => { - const source = `sub foo() - x = function(y) - if (true) then - return 1 - end if - return 0 - end function - end sub`; + const source = ` + sub foo() + x = function(y) + if (true) then + return 1 + end if + return 0 + end function + end sub + `; program.setFile('source/code.coverageExcluded.bs', source); program.validate(); @@ -468,14 +465,16 @@ describe('MockUtil', () => { await builder.transpile(); let a = getContents('source/code.coverageExcluded.brs'); - let b = `sub foo() -x = function(y) -if (true) then -return 1 -end if -return 0 -end function -end sub`; + let b = undent(` + sub foo() + x = function(y) + if (true) then + return 1 + end if + return 0 + end function + end sub + `); expect(a).to.equal(b); }); From 18c1c98f850f9d9a02a8b1e93b73d511d7ebe2e0 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Wed, 17 Jan 2024 13:52:27 -0400 Subject: [PATCH 27/30] Removed some raw code statements in code cov and updated test file format --- .../lib/rooibos/CodeCoverageProcessor.spec.ts | 554 +++++++++--------- .../src/lib/rooibos/CodeCoverageProcessor.ts | 53 +- 2 files changed, 311 insertions(+), 296 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.spec.ts b/bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.spec.ts index 4820007c..2171592e 100644 --- a/bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.spec.ts +++ b/bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.spec.ts @@ -4,15 +4,12 @@ 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`; let _stagingFolderPath = s`${tmpPath}/staging`; -function trimLeading(text: string) { - return text.split('\n').map((line) => line.trimStart()).join('\n'); -} - describe('RooibosPlugin', () => { let program: Program; let builder: ProgramBuilder; @@ -21,7 +18,8 @@ describe('RooibosPlugin', () => { function getContents(filename: string) { // 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(); + return undent(contents); } describe('CodeCoverageProcessor', () => { @@ -68,169 +66,89 @@ describe('RooibosPlugin', () => { // in `beforeEach`. This is because the compiler normally skips processing .brs files and copies them as-is. it('adds code coverage to a brs file', async () => { program.setFile('source/code.brs', ` - function new(a1, a2) - c = 0 - text = "" - for i = 0 to 10 - text = text + "hello" - c++ - c += 1 - if c = 2 - ? "is true" - end if + function new(a1, a2) + c = 0 + text = "" + for i = 0 to 10 + text = text + "hello" + c++ + c += 1 + if c = 2 + ? "is true" + end if - if c = 3 - ? "free" - else - ? "not free" - end if - end for - end function - `); + if c = 3 + ? "free" + else + ? "not free" + end if + end for + end function + `); program.validate(); expect(program.getDiagnostics()).to.be.empty; await builder.transpile(); let a = getContents('source/code.brs'); - let b = `function new(a1, a2) -RBS_CC_1_reportLine(2, 1) -c = 0 -RBS_CC_1_reportLine(3, 1) -text = "" -RBS_CC_1_reportLine(4, 1): for i = 0 to 10 -RBS_CC_1_reportLine(5, 1) -text = text + "hello" -RBS_CC_1_reportLine(6, 1) -c++ -RBS_CC_1_reportLine(7, 1) -c += 1 -if RBS_CC_1_reportLine(8, 3) and c = 2 -RBS_CC_1_reportLine(9, 1) -? "is true" -end if -if RBS_CC_1_reportLine(12, 3) and c = 3 -RBS_CC_1_reportLine(13, 1) -? "free" -else -RBS_CC_1_reportLine(14, 3) -RBS_CC_1_reportLine(15, 1) -? "not free" -end if -end for -end function - -function RBS_CC_1_reportLine(lineNumber, reportType = 1) -if m.global = invalid -'? "global is not available in this scope!! it is not possible to record coverage: #FILE_PATH#(lineNumber)" -return true -else -if m._rbs_ccn = invalid -'? "Coverage maps are not created - creating now" -if m.global._rbs_ccn = invalid -'? "Coverage maps are not created - creating now" -m.global.addFields({ -"_rbs_ccn": createObject("roSGNode", "CodeCoverage") -}) -end if -m._rbs_ccn = m.global._rbs_ccn -end if -end if - -m._rbs_ccn.entry = {"f":"1", "l":stri(lineNumber), "r":reportType} -return true -end function -`; - expect(a).to.equal(b); - - }); - }); - describe('basic bs tests', () => { - - it('adds code coverage to a bs file', async () => { - program.setFile('source/code.bs', ` - function new(a1, a2) - c = 0 - text = "" - for i = 0 to 10 - text = text + "hello" - c++ - c += 1 - if c = 2 - ? "is true" - end if + let b = undent(` + function new(a1, a2) + RBS_CC_1_reportLine(2, 1) + c = 0 + RBS_CC_1_reportLine(3, 1) + text = "" + RBS_CC_1_reportLine(4, 1): for i = 0 to 10 + RBS_CC_1_reportLine(5, 1) + text = text + "hello" + RBS_CC_1_reportLine(6, 1) + c++ + RBS_CC_1_reportLine(7, 1) + c += 1 + if RBS_CC_1_reportLine(8, 3) and c = 2 + RBS_CC_1_reportLine(9, 1) + ? "is true" + end if + if RBS_CC_1_reportLine(12, 3) and c = 3 + RBS_CC_1_reportLine(13, 1) + ? "free" + else + RBS_CC_1_reportLine(14, 3) + RBS_CC_1_reportLine(15, 1) + ? "not free" + end if + end for + end function - if c = 3 - ? "free" + function RBS_CC_1_reportLine(lineNumber, reportType = 1) + if m.global = invalid + '? "global is not available in this scope!! it is not possible to record coverage: #FILE_PATH#(lineNumber)" + return true else - ? "not free" + if m._rbs_ccn = invalid + '? "Coverage maps are not created - creating now" + if m.global._rbs_ccn = invalid + '? "Coverage maps are not created - creating now" + m.global.addFields({ + "_rbs_ccn": createObject("roSGNode", "CodeCoverage") + }) + end if + m._rbs_ccn = m.global._rbs_ccn + end if end if - end for - end function - `); - program.validate(); - expect(program.getDiagnostics()).to.be.empty; - await builder.transpile(); - let a = getContents('source/code.brs'); - let b = `function new(a1, a2) -RBS_CC_1_reportLine(2, 1) -c = 0 -RBS_CC_1_reportLine(3, 1) -text = "" -RBS_CC_1_reportLine(4, 1): for i = 0 to 10 -RBS_CC_1_reportLine(5, 1) -text = text + "hello" -RBS_CC_1_reportLine(6, 1) -c++ -RBS_CC_1_reportLine(7, 1) -c += 1 -if RBS_CC_1_reportLine(8, 3) and c = 2 -RBS_CC_1_reportLine(9, 1) -? "is true" -end if -if RBS_CC_1_reportLine(12, 3) and c = 3 -RBS_CC_1_reportLine(13, 1) -? "free" -else -RBS_CC_1_reportLine(14, 3) -RBS_CC_1_reportLine(15, 1) -? "not free" -end if -end for -end function - -function RBS_CC_1_reportLine(lineNumber, reportType = 1) -if m.global = invalid -'? "global is not available in this scope!! it is not possible to record coverage: #FILE_PATH#(lineNumber)" -return true -else -if m._rbs_ccn = invalid -'? "Coverage maps are not created - creating now" -if m.global._rbs_ccn = invalid -'? "Coverage maps are not created - creating now" -m.global.addFields({ -"_rbs_ccn": createObject("roSGNode", "CodeCoverage") -}) -end if -m._rbs_ccn = m.global._rbs_ccn -end if -end if - -m._rbs_ccn.entry = {"f":"1", "l":stri(lineNumber), "r":reportType} -return true -end function -`; + m._rbs_ccn.entry = { + "f": "1" + "l": stri(lineNumber) + "r": reportType + } + return true + end function + `); expect(a).to.equal(b); }); }); - - describe('basic tests', () => { + describe('basic bs tests', () => { it('adds code coverage to a bs file', async () => { program.setFile('source/code.bs', ` - class BasicClass - private field1 - public field2 - function new(a1, a2) c = 0 text = "" @@ -248,87 +166,179 @@ end function ? "not free" end if end for + end function + `); + program.validate(); + expect(program.getDiagnostics()).to.be.empty; + await builder.transpile(); + let a = getContents('source/code.brs'); + let b = undent(` + function new(a1, a2) + RBS_CC_1_reportLine(2, 1) + c = 0 + RBS_CC_1_reportLine(3, 1) + text = "" + RBS_CC_1_reportLine(4, 1): for i = 0 to 10 + RBS_CC_1_reportLine(5, 1) + text = text + "hello" + RBS_CC_1_reportLine(6, 1) + c++ + RBS_CC_1_reportLine(7, 1) + c += 1 + if RBS_CC_1_reportLine(8, 3) and c = 2 + RBS_CC_1_reportLine(9, 1) + ? "is true" + end if + if RBS_CC_1_reportLine(12, 3) and c = 3 + RBS_CC_1_reportLine(13, 1) + ? "free" + else + RBS_CC_1_reportLine(14, 3) + RBS_CC_1_reportLine(15, 1) + ? "not free" + end if + end for + end function + function RBS_CC_1_reportLine(lineNumber, reportType = 1) + if m.global = invalid + '? "global is not available in this scope!! it is not possible to record coverage: #FILE_PATH#(lineNumber)" + return true + else + if m._rbs_ccn = invalid + '? "Coverage maps are not created - creating now" + if m.global._rbs_ccn = invalid + '? "Coverage maps are not created - creating now" + m.global.addFields({ + "_rbs_ccn": createObject("roSGNode", "CodeCoverage") + }) + end if + m._rbs_ccn = m.global._rbs_ccn + end if + end if + m._rbs_ccn.entry = { + "f": "1" + "l": stri(lineNumber) + "r": reportType + } + return true end function + `); + expect(a).to.equal(b); + }); + }); - end class - `); + describe('basic tests', () => { + + it('adds code coverage to a bs file', async () => { + program.setFile('source/code.bs', ` + class BasicClass + private field1 + public field2 + + function new(a1, a2) + c = 0 + text = "" + for i = 0 to 10 + text = text + "hello" + c++ + c += 1 + if c = 2 + ? "is true" + end if + + if c = 3 + ? "free" + else + ? "not free" + end if + end for + + end function + + + end class + `); program.validate(); expect(program.getDiagnostics()).to.be.empty; await builder.transpile(); let a = getContents('source/code.brs'); - let b = `function __BasicClass_builder() -instance = {} -instance.new = function(a1, a2) -m.field1 = invalid -m.field2 = invalid -RBS_CC_1_reportLine(6, 1) -c = 0 -RBS_CC_1_reportLine(7, 1) -text = "" -RBS_CC_1_reportLine(8, 1): for i = 0 to 10 -RBS_CC_1_reportLine(9, 1) -text = text + "hello" -RBS_CC_1_reportLine(10, 1) -c++ -RBS_CC_1_reportLine(11, 1) -c += 1 -if RBS_CC_1_reportLine(12, 3) and c = 2 -RBS_CC_1_reportLine(13, 1) -? "is true" -end if -if RBS_CC_1_reportLine(16, 3) and c = 3 -RBS_CC_1_reportLine(17, 1) -? "free" -else -RBS_CC_1_reportLine(18, 3) -RBS_CC_1_reportLine(19, 1) -? "not free" -end if -end for -end function -return instance -end function -function BasicClass(a1, a2) -instance = __BasicClass_builder() -instance.new(a1, a2) -return instance -end function - -function RBS_CC_1_reportLine(lineNumber, reportType = 1) -if m.global = invalid -'? "global is not available in this scope!! it is not possible to record coverage: #FILE_PATH#(lineNumber)" -return true -else -if m._rbs_ccn = invalid -'? "Coverage maps are not created - creating now" -if m.global._rbs_ccn = invalid -'? "Coverage maps are not created - creating now" -m.global.addFields({ -"_rbs_ccn": createObject("roSGNode", "CodeCoverage") -}) -end if -m._rbs_ccn = m.global._rbs_ccn -end if -end if - -m._rbs_ccn.entry = {"f":"1", "l":stri(lineNumber), "r":reportType} -return true -end function -`; + let b = undent(` + function __BasicClass_builder() + instance = {} + instance.new = function(a1, a2) + m.field1 = invalid + m.field2 = invalid + RBS_CC_1_reportLine(6, 1) + c = 0 + RBS_CC_1_reportLine(7, 1) + text = "" + RBS_CC_1_reportLine(8, 1): for i = 0 to 10 + RBS_CC_1_reportLine(9, 1) + text = text + "hello" + RBS_CC_1_reportLine(10, 1) + c++ + RBS_CC_1_reportLine(11, 1) + c += 1 + if RBS_CC_1_reportLine(12, 3) and c = 2 + RBS_CC_1_reportLine(13, 1) + ? "is true" + end if + if RBS_CC_1_reportLine(16, 3) and c = 3 + RBS_CC_1_reportLine(17, 1) + ? "free" + else + RBS_CC_1_reportLine(18, 3) + RBS_CC_1_reportLine(19, 1) + ? "not free" + end if + end for + end function + return instance + end function + function BasicClass(a1, a2) + instance = __BasicClass_builder() + instance.new(a1, a2) + return instance + end function + + function RBS_CC_1_reportLine(lineNumber, reportType = 1) + if m.global = invalid + '? "global is not available in this scope!! it is not possible to record coverage: #FILE_PATH#(lineNumber)" + return true + else + if m._rbs_ccn = invalid + '? "Coverage maps are not created - creating now" + if m.global._rbs_ccn = invalid + '? "Coverage maps are not created - creating now" + m.global.addFields({ + "_rbs_ccn": createObject("roSGNode", "CodeCoverage") + }) + end if + m._rbs_ccn = m.global._rbs_ccn + end if + end if + m._rbs_ccn.entry = { + "f": "1" + "l": stri(lineNumber) + "r": reportType + } + return true + end function + `); expect(a).to.equal(b); }); it('correctly transpiles some statements', async () => { const source = `sub foo() - x = function(y) - if (true) then - return 1 - end if - return 0 - end function -end sub`; + x = function(y) + if (true) then + return 1 + end if + return 0 + end function + end sub`; program.setFile('source/code.bs', source); program.validate(); @@ -336,39 +346,43 @@ end sub`; await builder.transpile(); let a = getContents('source/code.brs'); - let b = `sub foo() -RBS_CC_1_reportLine(1, 1) -x = function(y) -if RBS_CC_1_reportLine(2, 3) and (true) then -RBS_CC_1_reportLine(3, 1) -return 1 -end if -RBS_CC_1_reportLine(5, 1) -return 0 -end function -end sub - -function RBS_CC_1_reportLine(lineNumber, reportType = 1) -if m.global = invalid -'? "global is not available in this scope!! it is not possible to record coverage: #FILE_PATH#(lineNumber)" -return true -else -if m._rbs_ccn = invalid -'? "Coverage maps are not created - creating now" -if m.global._rbs_ccn = invalid -'? "Coverage maps are not created - creating now" -m.global.addFields({ -"_rbs_ccn": createObject("roSGNode", "CodeCoverage") -}) -end if -m._rbs_ccn = m.global._rbs_ccn -end if -end if - -m._rbs_ccn.entry = {"f":"1", "l":stri(lineNumber), "r":reportType} -return true -end function -`; + let b = undent(` + sub foo() + RBS_CC_1_reportLine(1, 1) + x = function(y) + if RBS_CC_1_reportLine(2, 3) and (true) then + RBS_CC_1_reportLine(3, 1) + return 1 + end if + RBS_CC_1_reportLine(5, 1) + return 0 + end function + end sub + + function RBS_CC_1_reportLine(lineNumber, reportType = 1) + if m.global = invalid + '? "global is not available in this scope!! it is not possible to record coverage: #FILE_PATH#(lineNumber)" + return true + else + if m._rbs_ccn = invalid + '? "Coverage maps are not created - creating now" + if m.global._rbs_ccn = invalid + '? "Coverage maps are not created - creating now" + m.global.addFields({ + "_rbs_ccn": createObject("roSGNode", "CodeCoverage") + }) + end if + m._rbs_ccn = m.global._rbs_ccn + end if + end if + m._rbs_ccn.entry = { + "f": "1" + "l": stri(lineNumber) + "r": reportType + } + return true + end function + `); expect(a).to.equal(b); }); @@ -376,13 +390,13 @@ end function it('excludes files from coverage', async () => { const source = `sub foo() - x = function(y) - if (true) then - return 1 - end if - return 0 - end function - end sub`; + x = function(y) + if (true) then + return 1 + end if + return 0 + end function + end sub`; program.setFile('source/code.coverageExcluded.bs', source); program.validate(); @@ -390,14 +404,16 @@ end function await builder.transpile(); let a = getContents('source/code.coverageExcluded.brs'); - let b = `sub foo() -x = function(y) -if (true) then -return 1 -end if -return 0 -end function -end sub`; + let b = undent(` + sub foo() + x = function(y) + if (true) then + return 1 + end if + return 0 + end function + end sub + `); expect(a).to.equal(b); }); diff --git a/bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.ts b/bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.ts index b3356188..65567b1e 100644 --- a/bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.ts +++ b/bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.ts @@ -1,12 +1,11 @@ /* eslint-disable @typescript-eslint/no-var-requires */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import type { BrsFile, Editor, ExpressionStatement, Program, ProgramBuilder, Statement } from 'brighterscript'; -import { Parser, isIfStatement, Position, WalkMode, createVisitor } from 'brighterscript'; +import { Parser, isIfStatement, WalkMode, createVisitor } from 'brighterscript'; import * as brighterscript from 'brighterscript'; import type { RooibosConfig } from './RooibosConfig'; import { RawCodeStatement } from './RawCodeStatement'; import { BrsTranspileState } from 'brighterscript/dist/parser/BrsTranspileState'; -import { Range } from 'vscode-languageserver-types'; import { RawCodeExpression } from './RawCodeExpression'; import type { FileFactory } from './FileFactory'; @@ -20,27 +19,27 @@ export enum CodeCoverageLineType { export class CodeCoverageProcessor { private coverageBrsTemplate = ` - function RBS_CC_#ID#_reportLine(lineNumber, reportType = 1) - if m.global = invalid - '? "global is not available in this scope!! it is not possible to record coverage: #FILE_PATH#(lineNumber)" - return true - else - if m._rbs_ccn = invalid - '? "Coverage maps are not created - creating now" - if m.global._rbs_ccn = invalid - '? "Coverage maps are not created - creating now" - m.global.addFields({ - "_rbs_ccn": createObject("roSGNode", "CodeCoverage") - }) - end if - m._rbs_ccn = m.global._rbs_ccn - end if - end if - - m._rbs_ccn.entry = {"f":"#ID#", "l":stri(lineNumber), "r":reportType} - return true -end function -`; + function RBS_CC_#ID#_reportLine(lineNumber, reportType = 1) + if m.global = invalid + '? "global is not available in this scope!! it is not possible to record coverage: #FILE_PATH#(lineNumber)" + return true + else + if m._rbs_ccn = invalid + '? "Coverage maps are not created - creating now" + if m.global._rbs_ccn = invalid + '? "Coverage maps are not created - creating now" + m.global.addFields({ + "_rbs_ccn": createObject("roSGNode", "CodeCoverage") + }) + end if + m._rbs_ccn = m.global._rbs_ccn + end if + end if + + m._rbs_ccn.entry = {"f":"#ID#", "l":stri(lineNumber), "r":reportType} + return true + end function + `; constructor(builder: ProgramBuilder, fileFactory: FileFactory) { this.config = (builder.options as any).rooibos as RooibosConfig || {}; @@ -159,7 +158,7 @@ end function this.expectedCoverageMap[this.fileId.toString().trim()] = Array.from(this.coverageMap); this.filePathMap[this.fileId] = file.pkgPath; - this.addBrsAPIText(file); + this.addBrsAPIText(file, astEditor); } private convertStatementToCoverageStatement(statement: Statement, coverageType: CodeCoverageLineType, owner: any, key: any) { @@ -175,9 +174,9 @@ end function this.processedStatements.add(statement); } - public addBrsAPIText(file: BrsFile) { - const func = new RawCodeStatement(this.coverageBrsTemplate.replace(/\#ID\#/g, this.fileId.toString().trim()), file, Range.create(Position.create(1, 1), Position.create(1, 1))); - file.ast.statements.push(func); + public addBrsAPIText(file: BrsFile, astEditor: Editor) { + const astCodeToInject = Parser.parse(this.coverageBrsTemplate.replace(/\#ID\#/g, this.fileId.toString().trim())).ast.statements; + astEditor.arrayPush(file.ast.statements, ...astCodeToInject); } private addStatement(statement: Statement, lineNumber?: number) { From 4132da2e40c5fbcbddcb3ad5eca66cb216c7a72a Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Fri, 19 Jan 2024 16:49:51 -0400 Subject: [PATCH 28/30] Fixed some issues picking the wrong scope and make sure stubcall worked with async tests --- bsc-plugin/src/lib/rooibos/MockUtil.ts | 4 +- bsc-plugin/src/lib/rooibos/TestGroup.ts | 3 +- bsc-plugin/src/lib/rooibos/Utils.ts | 14 +++++- bsc-plugin/src/plugin.ts | 4 +- framework/src/source/BaseTestSuite.bs | 4 +- tests/src/components/NodeExample.bs | 31 +++++++++++-- tests/src/source/Async.spec.bs | 58 +++++++++++++++++++++++++ 7 files changed, 107 insertions(+), 11 deletions(-) create mode 100644 tests/src/source/Async.spec.bs diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.ts b/bsc-plugin/src/lib/rooibos/MockUtil.ts index ac8b9d0b..fa66034a 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.ts @@ -10,7 +10,7 @@ import type { RooibosSession } from './RooibosSession'; import { diagnosticErrorProcessingFile } from '../utils/Diagnostics'; import type { TestCase } from './TestCase'; import type { TestSuite } from './TestSuite'; -import { functionRequiresReturnValue, getAllDottedGetParts } from './Utils'; +import { functionRequiresReturnValue, getAllDottedGetParts, getScopeForSuite } from './Utils'; export class MockUtil { @@ -172,7 +172,7 @@ export class MockUtil { private processGlobalStubbedMethod(callExpression: brighterscript.CallExpression, testSuite: TestSuite) { let isNotCalled = false; let isStubCall = false; - const scope = testSuite.file.program.getFirstScopeForFile(testSuite.file); + const scope = getScopeForSuite(testSuite); const namespaceLookup = this.session.namespaceLookup; if (brighterscript.isDottedGetExpression(callExpression.callee)) { const nameText = callExpression.callee.name.text; diff --git a/bsc-plugin/src/lib/rooibos/TestGroup.ts b/bsc-plugin/src/lib/rooibos/TestGroup.ts index cf83f282..71df26f5 100644 --- a/bsc-plugin/src/lib/rooibos/TestGroup.ts +++ b/bsc-plugin/src/lib/rooibos/TestGroup.ts @@ -45,12 +45,11 @@ export class TestGroup extends TestBlock { 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 //add line number as last param - let scope = this.file.program.getFirstScopeForFile(this.file); const transpileState = new BrsTranspileState(this.file); try { let func = this.testSuite.classStatement.methods.find((m) => m.name.text.toLowerCase() === testCase.funcName.toLowerCase()); diff --git a/bsc-plugin/src/lib/rooibos/Utils.ts b/bsc-plugin/src/lib/rooibos/Utils.ts index df13beae..39bd00a8 100644 --- a/bsc-plugin/src/lib/rooibos/Utils.ts +++ b/bsc-plugin/src/lib/rooibos/Utils.ts @@ -1,7 +1,8 @@ import type { AnnotationExpression, AstEditor, BrsFile, ClassStatement, Expression, FunctionStatement } from 'brighterscript'; -import { TokenKind } 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 = ` @@ -103,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.ts b/bsc-plugin/src/plugin.ts index fc529240..74dfbc77 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 { @@ -148,6 +149,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`); @@ -156,7 +158,7 @@ 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); } } } diff --git a/framework/src/source/BaseTestSuite.bs b/framework/src/source/BaseTestSuite.bs index 170a5712..678e792c 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() 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..89972b78 --- /dev/null +++ b/tests/src/source/Async.spec.bs @@ -0,0 +1,58 @@ +namespace tests + @async(1000) + @SGNode("NodeExample") + @only + @suite + class AsyncNodeTests extends rooibos.BaseTestSuite + + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @describe("NodeExample tests") + '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + @beforeEach + function beforeEachGroup() + m.cleanMocks() + 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 From f435bba028a548f887c6e7627559317760b0c031 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Sat, 20 Jan 2024 13:19:25 -0400 Subject: [PATCH 29/30] Moved global stub clearing to clearStubs() --- framework/src/source/BaseTestSuite.bs | 8 ++++---- tests/src/source/Async.spec.bs | 3 +-- tests/src/source/NewExpectSyntax.spec.bs | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/framework/src/source/BaseTestSuite.bs b/framework/src/source/BaseTestSuite.bs index 678e792c..e85771cc 100644 --- a/framework/src/source/BaseTestSuite.bs +++ b/framework/src/source/BaseTestSuite.bs @@ -2268,10 +2268,6 @@ namespace rooibos ' * @description Cleans up all tracking data associated with mocks ' */ function cleanMocks() as void - ' Clean up the global functions mocks as well - globalAa = getGlobalAa() - globalAa.__globalStubs = invalid - if m.mocks = invalid return end if @@ -2292,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/src/source/Async.spec.bs b/tests/src/source/Async.spec.bs index 89972b78..49e78337 100644 --- a/tests/src/source/Async.spec.bs +++ b/tests/src/source/Async.spec.bs @@ -1,7 +1,6 @@ namespace tests @async(1000) @SGNode("NodeExample") - @only @suite class AsyncNodeTests extends rooibos.BaseTestSuite @@ -11,7 +10,7 @@ namespace tests @beforeEach function beforeEachGroup() - m.cleanMocks() + m.cleanStubs() end function @async diff --git a/tests/src/source/NewExpectSyntax.spec.bs b/tests/src/source/NewExpectSyntax.spec.bs index 1dc3aca4..efd6956b 100644 --- a/tests/src/source/NewExpectSyntax.spec.bs +++ b/tests/src/source/NewExpectSyntax.spec.bs @@ -327,7 +327,7 @@ namespace tests m.assertInvalid(testNamespace.functionWithoutReturn()) m.assertTrue(getGlobalAA().wasCalled) - m.cleanMocks() + m.cleanStubs() m.assertInvalid(testNamespace.functionWithoutReturn()) m.assertFalse(getGlobalAA().wasCalled) @@ -345,7 +345,7 @@ namespace tests m.assertInvalid(globalFunctionWithoutReturn()) m.assertTrue(getGlobalAA().wasCalled) - m.cleanMocks() + m.cleanStubs() m.assertInvalid(globalFunctionWithoutReturn()) m.assertFalse(getGlobalAA().wasCalled) From fcc95bc2b833bd20945fe516f39cb5084ae155d5 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Mon, 22 Jan 2024 14:29:57 -0400 Subject: [PATCH 30/30] Updates after merging master updates --- bsc-plugin/src/lib/rooibos/MockUtil.spec.ts | 2 +- bsc-plugin/src/plugin.spec.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts b/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts index 8b35a8d7..8f963933 100644 --- a/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts +++ b/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts @@ -16,7 +16,7 @@ describe('MockUtil', () => { let plugin: RooibosPlugin; let options; - function getContents(filename: string, trim = true) { + function getContents(filename: string) { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument let contents = fsExtra.readFileSync(s`${_stagingFolderPath}/${filename}`).toString(); return contents; diff --git a/bsc-plugin/src/plugin.spec.ts b/bsc-plugin/src/plugin.spec.ts index cb644ebd..e76df861 100644 --- a/bsc-plugin/src/plugin.spec.ts +++ b/bsc-plugin/src/plugin.spec.ts @@ -4,7 +4,6 @@ import { expect } from 'chai'; import { RooibosPlugin } from './plugin'; import * as fsExtra from 'fs-extra'; import * as path from 'path'; -import * as trim from 'trim-whitespace'; import undent from 'undent'; import { SourceMapConsumer } from 'source-map'; let tmpPath = s`${process.cwd()}/tmp`;