testcafe-legacy-api
Version:
Legacy API support for TestCafe
171 lines (170 loc) • 7.5 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const Ast = __importStar(require("../ast"));
const Common = __importStar(require("../common"));
const ErrCodes = __importStar(require("../err_codes"));
const CallAnalyzer = __importStar(require("./call_analyzer"));
function StepsAnalyzer(isMixin, rawStepData, errs, sourceIndex) {
this.rawStepData = rawStepData;
this.isMixin = isMixin;
this.errs = errs;
this.sourceIndex = sourceIndex;
this.src = null;
this.filename = null;
this.ERR = {};
this.ERR.MISPLACED_DECLARATION = isMixin ? ErrCodes.MISPLACED_MIXIN_DECLARATION : ErrCodes.MISPLACED_TEST_DECLARATION;
this.ERR.EMPTY_NAME = isMixin ? ErrCodes.EMPTY_MIXIN_NAME : ErrCodes.EMPTY_TEST_NAME;
this.ERR.DUPLICATE_NAME = isMixin ? ErrCodes.DUPLICATE_MIXIN_NAME : ErrCodes.DUPLICATE_TEST_NAME;
this.ERR.INVALID_ASSIGNMENT = isMixin ? ErrCodes.INVALID_MIXIN_ASSIGNMENT : ErrCodes.INVALID_TEST_ASSIGNMENT;
this.ERR.FACILITY_IS_EMPTY = isMixin ? ErrCodes.MIXIN_IS_EMPTY : ErrCodes.TEST_IS_EMPTY;
this.ERR.MIXIN_USED_IN_MIXIN = ErrCodes.MIXIN_USED_IN_MIXIN;
this.ERR.INIFRAME_FUNCTION_SHOULD_ACCEPT_TWO_ARGS = ErrCodes.INIFRAME_FUNCTION_SHOULD_ACCEPT_TWO_ARGS;
this.ERR.STEP_IS_NOT_A_FUNCTION = isMixin ? ErrCodes.MIXIN_STEP_IS_NOT_A_FUNCTION : ErrCodes.TEST_STEP_IS_NOT_A_FUNCTION_OR_MIXIN;
this.ERR.STEP_IS_EMPTY = isMixin ? ErrCodes.MIXIN_STEP_IS_EMPTY : ErrCodes.TEST_STEP_IS_EMPTY;
}
exports.default = StepsAnalyzer;
;
StepsAnalyzer.prototype.run = function (astPath, filename, src) {
this.src = src;
this.filename = filename;
if (!Ast.isPathMatch(Common.TEST_AND_MIXIN_DECLARATION_AST_PATH, astPath)) {
this._err(this.ERR.MISPLACED_DECLARATION, astPath);
return;
}
//NOTE: [sub, [WE_ARE_HERE], ['string', NAME]]
var name = Ast.getAncestorByName('sub', astPath)[2][1];
if (!name) {
this._err(this.ERR.EMPTY_NAME, astPath);
return;
}
var isDuplicate = this.rawStepData[name];
if (isDuplicate) {
this._err(this.ERR.DUPLICATE_NAME, astPath, { name: name });
return;
}
//NOTE: ['assign', true, ['sub', [WE_ARE_HERE], ...], [TEST_OBJ]]
var obj = Ast.getAncestorByName('assign', astPath)[3];
if (Ast.getEntryName(obj) !== 'object') {
this._err(this.ERR.INVALID_ASSIGNMENT, astPath);
return;
}
var steps = obj[1];
if (!steps.length)
this._err(this.ERR.FACILITY_IS_EMPTY, astPath);
else
this._analyzeSteps(name, steps);
};
StepsAnalyzer.prototype._tryExtractTestCasesDirectiveAst = function (steps) {
var firstStep = steps[0];
if (firstStep[0] === Common.TEST_CASES_DIRECTIVE) {
steps.splice(0, 1);
return firstStep[1];
}
return null;
};
StepsAnalyzer.prototype._analyzeSteps = function (facilityName, steps) {
var analyzer = this;
this.rawStepData[facilityName] = {
names: [],
asts: []
};
if (!this.isMixin)
this.rawStepData[facilityName].testCasesDirectiveAst = this._tryExtractTestCasesDirectiveAst(steps);
steps.forEach(function (step) {
var stepAst = step[1], stepEntryName = Ast.getEntryName(stepAst);
if (stepEntryName !== 'function') {
//NOTE: we have mixin insertion point candidate
if (stepEntryName === 'string' &&
analyzer._tryAnalyzeAsMixinInsertion(stepEntryName, stepAst, facilityName, step)) {
return;
}
//NOTE: we have inIFrame call candidate
if (stepEntryName === 'call' && stepAst[1][1] === 'inIFrame')
analyzer._analyzeAsInIframeCall(stepAst, facilityName, step);
//NOTE: it's not mixin or inIframe - just fail
else
analyzer._err(analyzer.ERR.STEP_IS_NOT_A_FUNCTION, stepAst);
return;
}
analyzer._analyzeStepContent(stepAst);
analyzer._insertStep(facilityName, step[0], stepAst);
});
};
StepsAnalyzer.prototype._tryAnalyzeAsMixinInsertion = function (stepEntryName, stepAst, facilityName, step) {
var match = Common.DIRECTIVE_EXPRESSION_PATTERN.exec(stepAst[1]);
if (match && match[1] === Common.MIXIN_INSERTION_POINT_DIRECTIVE_LVALUE) {
if (this.isMixin)
this._err(this.ERR.MIXIN_USED_IN_MIXIN, stepAst);
else {
this._insertStep(facilityName, step[0], {
isMixinInsertionPoint: true,
mixinName: match[2],
line: Ast.getCurrentSrcLineNum(stepAst)
});
}
return true;
}
return false;
};
StepsAnalyzer.prototype._analyzeAsInIframeCall = function (stepAst, facilityName, step) {
if (stepAst[2].length < 2)
this._err(this.ERR.INIFRAME_FUNCTION_SHOULD_ACCEPT_TWO_ARGS, stepAst);
else {
var selectorArgAst = stepAst[2][0], stepBodyAst = stepAst[2][1];
if (Ast.getEntryName(stepBodyAst) !== 'function')
this._err(this.ERR.STEP_IS_NOT_A_FUNCTION, stepAst);
else {
this._analyzeStepContent(stepBodyAst);
if (selectorArgAst[0].name !== 'function') {
//NOTE: modify iFrame selector to a function
stepAst[2][0] = ['function', null, [], [
['return', selectorArgAst]
]];
}
this._insertStep(facilityName, step[0], stepAst);
}
}
};
StepsAnalyzer.prototype._analyzeStepContent = function (stepAst) {
//NOTE: Mark the last entry in a function body with flag to know
//that action function is situated at the end of the function body
var funcBody = stepAst[3], funcLastEntr = funcBody[funcBody.length - 1];
if (!funcLastEntr) {
this._err(this.ERR.STEP_IS_EMPTY, stepAst);
return;
}
funcLastEntr[0].last = true;
//NOTE: Traverse step function AST and validate calls of the action and async functions.
CallAnalyzer.run(stepAst, this.filename, this.errs, false, this.sourceIndex, this.src);
};
StepsAnalyzer.prototype._insertStep = function (facilityName, name, ast) {
this.rawStepData[facilityName].names.push(name);
this.rawStepData[facilityName].asts.push(ast);
};
StepsAnalyzer.prototype._err = function (type, currentAst, additionalFields) {
var line = Ast.getCurrentSrcLineNum(currentAst);
this.errs.push(Common.createErrorObj(type, this.filename, line, additionalFields));
};