testcafe-legacy-api
Version:
Legacy API support for TestCafe
168 lines (167 loc) • 6.8 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;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.run = void 0;
const uglify_js_1 = require("../../tools/uglify-js/uglify-js");
const const_1 = __importDefault(require("../const"));
const Common = __importStar(require("../common"));
const Ast = __importStar(require("../ast"));
const ErrCodes = __importStar(require("../err_codes"));
function getLineIndent(line, baseIndent) {
var lineIndent = 0;
for (var j = 0; j < line.length && baseIndent >= lineIndent; j++) {
var ch = line[j];
if (ch === ' ' || ch === '\t')
lineIndent++;
else
break;
}
return Math.min(lineIndent, baseIndent);
}
//Call Analyzer
var CallAnalyzer = function (ast, filename, errs, isSharedCode, sourceIndex, src) {
this.ast = ast;
this.src = src;
this.filename = filename;
this.errs = errs;
this.isSharedCode = isSharedCode;
this.sourceIndex = sourceIndex;
this.astWasModified = false;
this.callInfo = {
astPath: null,
line: 0,
calleeName: null,
calleeOwnerObj: null
};
};
CallAnalyzer.prototype._walkCalls = function (onCall) {
var analyzer = this, walker = uglify_js_1.uglify.ast_walker();
walker.with_walkers({
'call': function () {
var isDot = Ast.getEntryName(this[1]) === 'dot';
analyzer.callInfo.astEntry = this;
analyzer.callInfo.astPath = walker.stack();
analyzer.callInfo.line = Ast.getCurrentSrcLineNum(analyzer.callInfo.astPath);
analyzer.callInfo.calleeName = isDot ? this[1][2] : this[1][1];
analyzer.callInfo.calleeOwnerObj = isDot ? this[1][1][1] : null;
analyzer.callInfo.args = this[2];
onCall();
}
}, function () {
walker.walk(analyzer.ast);
});
};
CallAnalyzer.prototype._err = function (type) {
this.errs.push({
type: type,
filename: this.filename,
line: this.callInfo.line
});
};
CallAnalyzer.prototype._isAsyncFuncCall = function () {
//NOTE: We do not verify that the object that owns the method, is a window.
//Otherwise we would have to resolve all scopes to determine if this object
//is an alias to window. So, we just do this dull but effective is some cases verification.
return (this.callInfo.calleeOwnerObj === 'window' || this.callInfo.calleeOwnerObj === 'wnd') &&
(this.callInfo.calleeName === 'setTimeout' || this.callInfo.calleeName === 'setInterval');
};
CallAnalyzer.prototype._isActionFuncCall = function () {
return this.callInfo.calleeOwnerObj === Common.ACTIONS_OWNER_OBJECT_IDENTIFIER &&
const_1.default.ACTION_FUNC_NAMES.indexOf(this.callInfo.calleeName) > -1;
};
CallAnalyzer.prototype._isAssertionCall = function () {
return !this.callInfo.calleeOwnerObj && const_1.default.ASSERTION_FUNC_NAMES.indexOf(this.callInfo.calleeName) > -1;
};
//NOTE: we should remove rudimentary indentation in multiline call expressions. E.g.:
//act.click('#someElement', {
// alt: true,
// });
//We should turn into:
//act.click('#someElement', {
// alt: true,
//});
CallAnalyzer.prototype._reformatCallSrc = function (callSrc, startPos) {
//1.Split src by new lines, to determine if expression is multiline
var lines = callSrc.split(/\r?\n/);
if (lines.length > 1) {
//2.If expression is multiline then count indentation size for the first line.
//For this traverse back this.src starting from the startPos
var baseIndent = 0;
for (var i = startPos - 1; i >= 0; i--) {
var ch = this.src[i];
if (ch === ' ' || ch === '\t')
baseIndent++;
else
break;
}
//3.If we have indentation for the first line, then we should remove it from other lines
if (baseIndent > 0) {
for (var j = 1; j < lines.length; j++) {
var lineIndent = getLineIndent(lines[j], baseIndent);
lines[j] = lines[j].substr(lineIndent);
}
}
callSrc = lines.join('\n');
}
return callSrc;
};
CallAnalyzer.prototype._addToSourceIndex = function () {
var pos = Ast.getSrcCodePosFromEntry(this.callInfo.astEntry), callSrc = this.src.substring(pos.start, pos.end + 1);
callSrc = this._reformatCallSrc(callSrc, pos.start);
this.sourceIndex.push(callSrc);
var idx = this.sourceIndex.length - 1;
this.callInfo.args.push(['string', Common.SOURCE_INDEX_ARG_PREFIX + idx]);
this.astWasModified = true;
};
CallAnalyzer.prototype._analyzeActionFuncCall = function () {
if (this.isSharedCode)
this._err(ErrCodes.ACTION_FUNC_CALL_IN_SHARED_CODE);
else if (!Ast.isPathMatch(Common.ACTION_FUNC_AST_PATH, this.callInfo.astPath, true))
this._err(ErrCodes.ACTION_FUNC_IS_NOT_A_LAST_ENTRY);
else
this._addToSourceIndex();
};
CallAnalyzer.prototype.run = function () {
var analyzer = this;
this._walkCalls(function () {
if (analyzer._isActionFuncCall())
analyzer._analyzeActionFuncCall();
//NOTE: async calls are allowed in the shared code
else if (!analyzer.isSharedCode && analyzer._isAsyncFuncCall())
analyzer._err(ErrCodes.ASYNC_FUNC_CALL);
else if (analyzer._isAssertionCall())
analyzer._addToSourceIndex();
});
return this.astWasModified;
};
function run(ast, filename, errs, isSharedCode, sourceIndex, src) {
var analyzer = new CallAnalyzer(ast, filename, errs, isSharedCode, sourceIndex, src);
return analyzer.run();
}
exports.run = run;
;