Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow custom test reporter #265

Merged
merged 14 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bsc-plugin/src/lib/rooibos/Annotation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,4 +219,4 @@ export class RooibosAnnotation {

export function getAnnotationType(text: string): AnnotationType {
return annotationLookup[text.toLowerCase()] || AnnotationType.None;
}
}
2 changes: 1 addition & 1 deletion bsc-plugin/src/lib/rooibos/MockUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export class MockUtil {
${globalAaName} = getGlobalAa()
if RBS_SM_${this.fileId}_getMocksByFunctionName()["${methodName}"] <> invalid
${resultName} = RBS_SM_${this.fileId}_getMocksByFunctionName()["${methodName}"].callback(${paramNames})
return${requiresReturnValue ? ` ${resultName}` : '' }
return${requiresReturnValue ? ` ${resultName}` : ''}
else if type(${globalAaName}?.${storageName}?.${methodName}).endsWith("Function")
__stubFunction = ${globalAaName}.${storageName}.${methodName}
${resultName} = __stubFunction(${paramNames})
Expand Down
5 changes: 5 additions & 0 deletions bsc-plugin/src/lib/rooibos/RooibosConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ export interface RooibosConfig {
catchCrashes?: boolean;
throwOnFailedAssertion?: boolean;
sendHomeOnFinish?: boolean;

/**
* @deprecated Use the `reporters` array instead
*/
reporter?: string;
reporters?: string[];
keepAppOpen?: boolean;
testSceneName?: string;

Expand Down
26 changes: 25 additions & 1 deletion bsc-plugin/src/lib/rooibos/RooibosSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export class RooibosSession {
method.func.body.statements.length,
Parser.parse(undent`
return {
"reporter": "${this.config.reporter || ''}"
"reporters": ${this.getReportersList()}
"failFast": ${this.config.failFast ? 'true' : 'false'}
"sendHomeOnFinish": ${this.config.sendHomeOnFinish ? 'true' : 'false'}
"logLevel": ${this.config.logLevel ?? 0}
Expand All @@ -156,6 +156,30 @@ export class RooibosSession {
}
}

getReportersList() {
let reporters = this.config.reporters;
if (!Array.isArray(reporters)) {
reporters = [];
}
if (this.config.reporter) {
// @todo: warn that `reporter` is deprecated and to use `reporters` instead
reporters.push(this.config.reporter);
}
if (reporters.length < 1) {
reporters.push('console');
}
return `[${reporters.map(this.sanitiseReporterName).toString()}]`;
}

sanitiseReporterName(name: string) {
switch (name.toLowerCase()) {
case 'console': return 'rooibos_ConsoleTestReporter';
case 'junit': return 'rooibos_JUnitTestReporter';
}
// @todo: check if function name is valid
return name;
}

updateVersionTextFunction(classStatement: ClassStatement, editor: AstEditor) {
let method = classStatement.methods.find((m) => m.name.text === 'getVersionText');
if (method) {
Expand Down
2 changes: 1 addition & 1 deletion bsc-plugin/src/lib/rooibos/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export function getPathValuePartAsString(expr: Expression) {

export function getScopeForSuite(testSuite: TestSuite) {
if (testSuite.isNodeTest) {
return testSuite.file.program.getScopesForFile(testSuite.file).find((scope)=> {
return testSuite.file.program.getScopesForFile(testSuite.file).find((scope) => {
return isXmlScope(scope) && scope.xmlFile.componentName.text === testSuite.generatedNodeName;
});

Expand Down
167 changes: 110 additions & 57 deletions bsc-plugin/src/plugin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,13 @@ describe('RooibosPlugin', () => {
let program: Program;
let builder: ProgramBuilder;
let plugin: RooibosPlugin;
let options;
beforeEach(() => {
plugin = new RooibosPlugin();
options = {
rootDir: _rootDir,
stagingFolderPath: _stagingFolderPath,
stagingDir: _stagingFolderPath,
rooibos: {
isGlobalMethodMockingEnabled: true
}
};

function setupProgram(options) {
fsExtra.ensureDirSync(_stagingFolderPath);
fsExtra.ensureDirSync(_rootDir);
fsExtra.ensureDirSync(tmpPath);

plugin = new RooibosPlugin();
builder = new ProgramBuilder();
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
builder.options = util.normalizeAndResolveConfig(options);
Expand All @@ -40,13 +32,28 @@ describe('RooibosPlugin', () => {
plugin.beforeProgramCreate(builder);
plugin.fileFactory['options'].frameworkSourcePath = path.resolve(path.join('../framework/src/source'));
plugin.afterProgramCreate(program);
});
}

afterEach(() => {
function destroyProgram() {
fsExtra.ensureDirSync(tmpPath);
fsExtra.emptyDirSync(tmpPath);
builder.dispose();
program.dispose();
}

beforeEach(() => {
setupProgram({
rootDir: _rootDir,
stagingFolderPath: _stagingFolderPath,
stagingDir: _stagingFolderPath,
rooibos: {
isGlobalMethodMockingEnabled: true
}
});
});

afterEach(() => {
destroyProgram();
});

describe('basic tests', () => {
Expand Down Expand Up @@ -594,29 +601,15 @@ describe('RooibosPlugin', () => {


it('adds launch hook with custom scene', async () => {
options = {
setupProgram({
rootDir: _rootDir,
stagingFolderPath: _stagingFolderPath,
stagingDir: _stagingFolderPath,
rooibos: {
testSceneName: 'CustomRooibosScene'
}
};
plugin = new RooibosPlugin();
fsExtra.ensureDirSync(_stagingFolderPath);
fsExtra.ensureDirSync(_rootDir);
fsExtra.ensureDirSync(tmpPath);

builder = new ProgramBuilder();
builder.options = util.normalizeAndResolveConfig(options);
builder.program = new Program(builder.options);
program = builder.program;
program.plugins.add(plugin);
program.createSourceScope(); //ensure source scope is created
plugin.beforeProgramCreate(builder);
plugin.fileFactory['options'].frameworkSourcePath = path.resolve(path.join('../framework/src/source'));
plugin.afterProgramCreate(program);
// program.validate();
});

const file = program.setFile<BrsFile>('source/main.bs', `
sub main()
print "main"
Expand Down Expand Up @@ -1908,34 +1901,14 @@ describe('RooibosPlugin', () => {
`;

beforeEach(() => {
plugin = new RooibosPlugin();
options = {
setupProgram({
rootDir: _rootDir,
stagingFolderPath: _stagingFolderPath
};
fsExtra.ensureDirSync(_stagingFolderPath);
fsExtra.ensureDirSync(_rootDir);
fsExtra.ensureDirSync(tmpPath);

builder = new ProgramBuilder();
builder.options = util.normalizeAndResolveConfig(options);
builder.program = new Program(builder.options);
program = builder.program;
builder.program = new Program(builder.options);
program = builder.program;
program.plugins.add(plugin);
program.createSourceScope(); //ensure source scope is created
plugin.beforeProgramCreate(builder);
plugin.fileFactory['options'].frameworkSourcePath = path.resolve(path.join('../framework/src/source'));
plugin.afterProgramCreate(program);
// program.validate();
});
});

afterEach(() => {
fsExtra.ensureDirSync(tmpPath);
fsExtra.emptyDirSync(tmpPath);
builder.dispose();
program.dispose();
destroyProgram();
});

it('tag one', async () => {
Expand Down Expand Up @@ -2057,9 +2030,9 @@ describe('RooibosPlugin', () => {
expect(findMethod('getIgnoredTestInfo').func.body.statements).to.be.empty;

await builder.transpile();
let testContents = getTestFunctionContents();

expect(
testContents
getTestFunctionContents()
).to.eql(undent`
item = {
id: "item"
Expand All @@ -2078,7 +2051,6 @@ describe('RooibosPlugin', () => {
end if
`);

let a = getContents('rooibos/RuntimeConfig.brs');
expect(
getContents('rooibos/RuntimeConfig.brs')
).to.eql(undent`
Expand All @@ -2091,7 +2063,9 @@ describe('RooibosPlugin', () => {
end function
instance.getRuntimeConfig = function()
return {
"reporter": ""
"reporters": [
rooibos_ConsoleTestReporter
]
"failFast": true
"sendHomeOnFinish": true
"logLevel": 0
Expand Down Expand Up @@ -2143,6 +2117,85 @@ describe('RooibosPlugin', () => {
expect(findMethod('getAllTestSuitesNames').func.body.statements).to.be.empty;
expect(findMethod('getIgnoredTestInfo').func.body.statements).to.be.empty;
});

const sep = '\n ';
const params: [string[], string][] = [
[[], 'rooibos_ConsoleTestReporter'],
[['CONSOLE'], 'rooibos_ConsoleTestReporter'],
[['MyCustomReporter'], 'MyCustomReporter'],
[['JUnit', 'MyCustomReporter'], `rooibos_JUnitTestReporter${sep}MyCustomReporter`]
];
it('adds custom test reporters', async () => {
for (const [reporters, expected] of params) {
setupProgram({
rootDir: _rootDir,
stagingFolderPath: _stagingFolderPath,
stagingDir: _stagingFolderPath,
rooibos: {
reporters: reporters
}
});

program.validate();
expect(program.getDiagnostics()).to.be.empty;

await builder.transpile();

expect(
getContents('rooibos/RuntimeConfig.brs')
).to.eql(undent`
function __rooibos_RuntimeConfig_builder()
instance = {}
instance.new = sub()
end sub
instance.getVersionText = function()
return "${version}"
end function
instance.getRuntimeConfig = function()
return {
"reporters": [
${expected}
]
"failFast": true
"sendHomeOnFinish": true
"logLevel": 0
"showOnlyFailures": true
"printTestTimes": true
"lineWidth": 60
"printLcov": false
"port": "invalid"
"catchCrashes": true
"throwOnFailedAssertion": false
"keepAppOpen": true
"isRecordingCodeCoverage": false
}
end function
instance.getTestSuiteClassWithName = function(name)
if false
? "noop"
end if
end function
instance.getAllTestSuitesNames = function()
return []
end function
instance.getIgnoredTestInfo = function()
return {
"count": 0
"items": []
}
end function
return instance
end function
function rooibos_RuntimeConfig()
instance = __rooibos_RuntimeConfig_builder()
instance.new()
return instance
end function
`);

destroyProgram();
}
});
});

describe.skip('run a local project', () => {
Expand Down
4 changes: 4 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,11 @@ Here is the information converted into a Markdown table:
| isGlobalMethodMockingEnabled | boolean | Default is false. Enables mocking and stubbing support for global and namespace functions |
| isGlobalMethodMockingEfficientMode | boolean | default to true, when set causes rooibos to modify only those functions that were mocked or stubbed |
| globalMethodMockingExcludedFiles | string[] | Files that rooibos will not modify when adding global function or namespace function mocking support |
| reporter? @deprecated <sup>1</sup> | string | The built-in reporter to use. Defaults to empty. Possible values are `console` and `junit`. |
| reporters? <sup>2</sup> | string[] | An array of factory functions/classes which implement `rooibos.BaseTestReporter`. Built-in reporters include `console` and `junit`. Defaults to `["console"]`. |

**<sup>1</sup>** This parameter is deprecated, use `reporters` instead. When specified, the reporter will be appended to the list of `reporters`.
**<sup>2</sup>** Custom reporters are not currently supported on [node-based tests](#testing-nodes), because rooibos does not know which files it should include in the generated test components. This will be addressed in a future Rooibos version (see issue [#266](https://github.com/georgejecook/rooibos/issues/266)).

## Creating test suites
<a name="organize-tests-by-suites-groups-and-cases"></a>
Expand Down
12 changes: 6 additions & 6 deletions framework/src/source/BaseTestReporter.bs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
namespace rooibos
interface ITestReporterOnEndEvent
stats as rooibos.Stats
end interface

class BaseTestReporter

public testRunner = invalid
Expand All @@ -11,15 +15,11 @@ namespace rooibos
m.allStats = runner.stats
end function

function reportResults(allStats as dynamic)
'override me
end function

function testLogInfo(text as string)
function onBegin(ev as dynamic)
'override me
end function

function testLogError(text as string)
function onEnd(ev as rooibos.ITestReporterOnEndEvent)
'override me
end function

Expand Down
12 changes: 2 additions & 10 deletions framework/src/source/ConsoleTestReporter.bs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ namespace rooibos
end if
end function

override function reportResults(allStats)
m.allStats = allStats
override function onEnd(ev as rooibos.ITestReporterOnEndEvent)
m.allStats = ev.stats
m.startReport()
for each testSuite in m.testRunner.testSuites
if not m.allStats.hasFailures or ((not m.config.showOnlyFailures) or testSuite.stats.failedCount > 0 or testSuite.stats.crashedCount > 0)
Expand Down Expand Up @@ -186,14 +186,6 @@ namespace rooibos
'++ printing
'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

override function testLogInfo(text)
? "INFO " ; text
end function

override function testLogError(text)
? "ERROR " ; text
end function

function printLine(depth = 0, text = "")
? " " ; text
end function
Expand Down
6 changes: 1 addition & 5 deletions framework/src/source/JUnitTestReporter.bs
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
namespace rooibos
class JUnitTestReporter extends rooibos.BaseTestReporter

function new(testRunner as dynamic)
super(testRunner)
end function

override function reportResults(allStats as dynamic)
override function onEnd(ev as rooibos.ITestReporterOnEndEvent)
root = createObject("roXMLElement")
root.SetName("testsuites")
properties = root.addElement("properties")
Expand Down
Loading