rooibos-roku
Version:
simple, flexible, fun brightscript test framework for roku scenegraph apps - roku brighterscript plugin
188 lines • 9.41 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MockUtil = void 0;
const brighterscript_1 = require("brighterscript");
const undent_1 = require("undent");
const Diagnostics_1 = require("../utils/Diagnostics");
const Utils_1 = require("./Utils");
class MockUtil {
constructor(builder, session) {
this.brsFileAdditions = `
function RBS_SM_#ID#_getMocksByFunctionName()
if m._rMocksByFunctionName = invalid
m._rMocksByFunctionName = {}
end if
return m._rMocksByFunctionName
end function
`;
this.config = builder.options.rooibos || {};
this.filePathMap = {};
this.fileId = 0;
this.session = session;
}
enableGlobalMethodMocks(file, astEditor) {
if (this.config.isGlobalMethodMockingEnabled) {
this._processFile(file, astEditor);
}
}
_processFile(file, astEditor) {
this.fileId++;
this.processedStatements = new Set();
this.astEditor = astEditor;
// console.log('processing global methods on ', file.pkgPath);
for (let functionStatement of file.parser.references.functionStatements) {
this.enableMockOnFunction(file, functionStatement);
}
this.filePathMap[this.fileId] = file.pkgPath;
if (this.processedStatements.size > 0) {
this.addBrsAPIText(file);
}
}
enableMockOnFunction(file, functionStatement) {
var _a, _b, _c;
if ((0, brighterscript_1.isClassStatement)((_a = functionStatement.parent) === null || _a === void 0 ? void 0 : _a.parent)) {
// console.log('skipping class', functionStatement.parent?.parent?.name?.text);
return;
}
if (this.processedStatements.has(functionStatement)) {
// console.log('skipping processed expression');
return;
}
const methodName = (functionStatement === null || functionStatement === void 0 ? void 0 : functionStatement.getName(brighterscript_1.ParseMode.BrightScript).toLowerCase()) || '';
// console.log('MN', methodName);
if (this.config.isGlobalMethodMockingEfficientMode && !this.session.globalStubbedMethods.has(methodName)) {
// console.log('skipping method that is not stubbed', methodName);
return;
}
let isDisabledFoMocking = (_b = functionStatement.annotations) === null || _b === void 0 ? void 0 : _b.find(x => x.name.toLowerCase() === 'disablemocking');
let parentNamespace = functionStatement.findAncestor(brighterscript_1.isNamespaceStatement);
while (parentNamespace && !isDisabledFoMocking) {
if (parentNamespace) {
isDisabledFoMocking = (_c = parentNamespace.annotations) === null || _c === void 0 ? void 0 : _c.find(x => x.name.toLowerCase() === 'disablemocking');
parentNamespace = parentNamespace.findAncestor(brighterscript_1.isNamespaceStatement);
}
}
if (isDisabledFoMocking) {
// The developer has stated that this function is not safe to be mocked
return;
}
// console.log('processing stubbed method', methodName);
// TODO check if the user has actually mocked or stubbed this function, otherwise leave it alone!
for (let param of functionStatement.func.parameters) {
param.asToken = null;
}
const paramNames = functionStatement.func.parameters.map((param) => param.name.text).join(',');
const requiresReturnValue = (0, Utils_1.functionRequiresReturnValue)(functionStatement);
const globalAaName = '__stubs_globalAa';
const resultName = '__stubOrMockResult';
const storageName = '__globalStubs';
const template = (0, undent_1.default) `
${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}?.${methodName}).endsWith("Function")
__stubFunction = ${globalAaName}.${storageName}.${methodName}
${resultName} = __stubFunction(${paramNames})
return${requiresReturnValue ? ` ${resultName}` : ''}
else if ${globalAaName}?.${storageName} <> invalid and ${globalAaName}.${storageName}.doesExist("${methodName}")
value = ${globalAaName}.${storageName}.${methodName}
return${requiresReturnValue ? ` value` : ''}
end if
`;
const astCodeToInject = brighterscript_1.Parser.parse(template).ast.statements;
this.astEditor.arrayUnshift(functionStatement.func.body.statements, ...astCodeToInject);
this.processedStatements.add(functionStatement);
file.needsTranspiled = true;
}
addBrsAPIText(file) {
const func = brighterscript_1.Parser.parse(this.brsFileAdditions.replace(/\#ID\#/g, this.fileId.toString().trim())).ast.statements;
this.astEditor.arrayPush(file.ast.statements, ...func);
}
gatherGlobalMethodMocks(testSuite) {
// console.log('gathering global method mocks for testSuite', testSuite.name);
for (let group of [...testSuite.testGroups.values()].filter((tg) => tg.isIncluded)) {
for (let testCase of [...group.testCases].filter((tc) => tc.isIncluded)) {
this.gatherMockedGlobalMethods(testSuite, testCase);
}
}
}
gatherMockedGlobalMethods(testSuite, testCase) {
try {
let func = testSuite.classStatement.methods.find((m) => m.name.text.toLowerCase() === testCase.funcName.toLowerCase());
func.walk((0, brighterscript_1.createVisitor)({
ExpressionStatement: (expressionStatement, parent, owner) => {
let callExpression = expressionStatement.expression;
if ((0, brighterscript_1.isCallExpression)(callExpression) && (0, brighterscript_1.isDottedGetExpression)(callExpression.callee)) {
let dge = callExpression.callee;
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, testSuite);
return expressionStatement;
}
else {
if (dge.name.text === 'expectCalled' || dge.name.text === 'expectNotCalled') {
this.processGlobalStubbedMethod(callExpression, testSuite);
}
}
}
}
}
}), {
walkMode: brighterscript_1.WalkMode.visitStatementsRecursive
});
}
catch (e) {
console.log(e);
(0, Diagnostics_1.diagnosticErrorProcessingFile)(testSuite.file, e.message);
}
}
processGlobalStubbedMethod(callExpression, testSuite) {
let isStubCall = false;
const scope = (0, Utils_1.getScopeForSuite)(testSuite);
if ((0, brighterscript_1.isDottedGetExpression)(callExpression.callee)) {
const nameText = callExpression.callee.name.text;
isStubCall = nameText === 'stubCall';
}
//modify args
let arg0 = callExpression.args[0];
if (isStubCall) {
let functionName = this.getGlobalFunctionName(arg0, scope);
if (functionName) {
this.session.globalStubbedMethods.add(functionName.toLowerCase());
return;
}
}
if ((0, brighterscript_1.isCallExpression)(arg0)) {
let functionName = this.getGlobalFunctionName(arg0.callee, scope);
if (functionName) {
this.session.globalStubbedMethods.add(functionName.toLowerCase());
}
}
}
getGlobalFunctionName(expression, scope) {
let result;
if ((0, brighterscript_1.isDottedGetExpression)(expression)) {
let nameParts = (0, Utils_1.getAllDottedGetParts)(expression);
let functionName = nameParts.join('.');
let callable = scope.getCallableByName(functionName);
if (callable) {
result = callable.getName(brighterscript_1.ParseMode.BrightScript);
}
}
else if ((0, brighterscript_1.isVariableExpression)(expression)) {
let functionName = expression.getName(brighterscript_1.ParseMode.BrightScript);
if (scope.symbolTable.hasSymbol(functionName)) {
result = functionName;
}
functionName = expression.getName(brighterscript_1.ParseMode.BrighterScript);
if (scope.getCallableByName(functionName)) {
result = functionName;
}
}
return result;
}
}
exports.MockUtil = MockUtil;
//# sourceMappingURL=MockUtil.js.map