diff --git a/bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.spec.ts b/bsc-plugin/src/lib/rooibos/CodeCoverageProcessor.spec.ts index 4820007..2171592 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 b335618..65567b1 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) { diff --git a/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts b/bsc-plugin/src/lib/rooibos/MockUtil.spec.ts index 9d2f9de..8f96393 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); }); diff --git a/bsc-plugin/src/lib/rooibos/RooibosSession.ts b/bsc-plugin/src/lib/rooibos/RooibosSession.ts index 4410d7f..adb0773 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 2038e66..e76df86 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`; @@ -2004,24 +2003,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 +2069,7 @@ describe('RooibosPlugin', () => { instance.getIgnoredTestInfo = function() return { "count": 0 - "items": [ - ] + "items": [] } end function return instance @@ -2209,29 +2207,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'); -}