Skip to content

Commit

Permalink
feat(core): add support for declaring tests as functions. Resolves #235
Browse files Browse the repository at this point in the history
… (#236)
  • Loading branch information
georgejecook authored Jul 23, 2023
1 parent 4807866 commit ab4b3c7
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 3 deletions.
7 changes: 4 additions & 3 deletions bsc-plugin/src/lib/rooibos/TestGroup.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { AstEditor, CallExpression, DottedGetExpression, Expression } from 'brighterscript';
import { isCallExpression, isCallfuncExpression, isIndexedGetExpression, ArrayLiteralExpression, createInvalidLiteral, createStringLiteral, createToken, isDottedGetExpression, TokenKind, isLiteralExpression, isVariableExpression } from 'brighterscript';
import { isCallExpression, isCallfuncExpression, isIndexedGetExpression, ArrayLiteralExpression, createInvalidLiteral, createStringLiteral, createToken, isDottedGetExpression, TokenKind, isLiteralExpression, isVariableExpression, isFunctionExpression } from 'brighterscript';
import * as brighterscript from 'brighterscript';
import { BrsTranspileState } from 'brighterscript/dist/parser/BrsTranspileState';
import { TranspileState } from 'brighterscript/dist/parser/TranspileState';
Expand Down Expand Up @@ -58,10 +58,11 @@ export class TestGroup extends TestBlock {
try {
let func = this.testSuite.classStatement.methods.find((m) => m.name.text.toLowerCase() === testCase.funcName.toLowerCase());
func.walk(brighterscript.createVisitor({
ExpressionStatement: (expressionStatement) => {
ExpressionStatement: (expressionStatement, parent, owner) => {
let callExpression = expressionStatement.expression as CallExpression;
if (brighterscript.isCallExpression(callExpression) && brighterscript.isDottedGetExpression(callExpression.callee)) {
let dge = callExpression.callee;
let isSub = isFunctionExpression(callExpression.parent.parent.parent) && callExpression.parent.parent.parent.functionType.kind === TokenKind.Sub;
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') {
Expand All @@ -77,7 +78,7 @@ export class TestGroup extends TestBlock {
overrideAstTranspile(editor, expressionStatement, '\n' + undent`
m.currentAssertLineNumber = ${callExpression.range.start.line}
${callExpression.transpile(transpileState).join('')}
${noEarlyExit ? '' : 'if m.currentResult?.isFail = true then m.done() : return invalid'}
${noEarlyExit ? '' : `if m.currentResult?.isFail = true then m.done() : return ${isSub ? '' : 'invalid'}`}
` + '\n');
}
}
Expand Down
60 changes: 60 additions & 0 deletions bsc-plugin/src/plugin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,54 @@ describe('RooibosPlugin', () => {
if m.currentResult?.isFail = true then m.done() : return invalid
`);
});
it('does not produce crashing code for subs', async () => {
program.setFile('source/test.spec.bs', `
@suite
class ATest
@describe("groupA")
@it("test1")
sub _()
m.expectCalled(m.thing.getFunction())
m.expectCalled(m.thing.getFunction(), "return")
m.expectCalled(m.thing.getFunction("arg1", "arg2"))
m.expectCalled(m.thing.getFunction("arg1", "arg2"), "return")
end sub
end class
`);
program.validate();
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)
).to.eql(undent`
m.currentAssertLineNumber = 6
m._expectCalled(m.thing, "getFunction", m, "m.thing", [])
if m.currentResult?.isFail = true then m.done() : return
m.currentAssertLineNumber = 7
m._expectCalled(m.thing, "getFunction", m, "m.thing", [], "return")
if m.currentResult?.isFail = true then m.done() : return
m.currentAssertLineNumber = 8
m._expectCalled(m.thing, "getFunction", m, "m.thing", [
"arg1"
"arg2"
])
if m.currentResult?.isFail = true then m.done() : return
m.currentAssertLineNumber = 9
m._expectCalled(m.thing, "getFunction", m, "m.thing", [
"arg1"
"arg2"
], "return")
if m.currentResult?.isFail = true then m.done() : return
`);
});
it('does not break when validating again after a transpile', async () => {
program.setFile('source/test.spec.bs', `
@suite
Expand Down Expand Up @@ -1518,3 +1566,15 @@ function getTestFunctionContents(trimEveryLine = false) {
}
return result;
}

function getTestSubContents(trimEveryLine = false) {
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;
}
27 changes: 27 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,33 @@ namespace tests
end namespace
```

Note, you can also use sub, if you wish

```
namespace tests
@suite("basic tests")
class BasicTests extends Rooibos.BaseTestSuite
'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@describe("tests the node context is available for a Node scope function")
'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@it("does something in node scope")
sub _()
m.assertNotInvalid(m.node)
Tests.doSomethingInNodeScope(true)
m.assertIonvalid(m._isNodeScopeVarSet)
m.assertTrue(m.node._isNodeScopeVarSet)
end sub
end class
end namespace
```



#### Simple example Notes

1. The `++++++++++++`'s around the `@describe` declaration are not required; but I find it makes the file much easier to read, and recommend it (or something similar, such as `'*****` as a best practice.
Expand Down

0 comments on commit ab4b3c7

Please sign in to comment.