232 lines
31 KiB
JavaScript
232 lines
31 KiB
JavaScript
|
"use strict";
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
exports.TestFileParserBase = exports.Test = exports.Fixture = void 0;
|
||
|
const promisified_functions_1 = require("../../utils/promisified-functions");
|
||
|
const util_1 = require("util");
|
||
|
const runtime_1 = require("../../errors/runtime");
|
||
|
const types_1 = require("../../errors/types");
|
||
|
const METHODS_SPECIFYING_NAME = ['only', 'skip'];
|
||
|
const COMPUTED_NAME_TEXT_TMP = '<computed name>(line: %s)';
|
||
|
const SKIP_PROPERTY_NAME = 'skip';
|
||
|
function getLoc(loc) {
|
||
|
// NOTE: Don't modify the Babel's parser data structure
|
||
|
const locCopy = Object.assign({}, loc);
|
||
|
// NOTE: 'fileName' and 'identifierName' fields with 'undefined' values added in the SourceLocation class constructor.
|
||
|
// https://github.com/babel/babel/blob/d51aa6d76177b544590cdfe3868f9f4d33d8813d/packages/babel-parser/src/util/location.js#L22
|
||
|
// Since this is useless information, we remove it.
|
||
|
delete locCopy.filename;
|
||
|
delete locCopy.identifierName;
|
||
|
delete locCopy.start.index;
|
||
|
delete locCopy.end.index;
|
||
|
return locCopy;
|
||
|
}
|
||
|
class Fixture {
|
||
|
constructor(name, start, end, loc, meta, isSkipped) {
|
||
|
this.name = name;
|
||
|
this.loc = getLoc(loc);
|
||
|
this.start = start;
|
||
|
this.end = end;
|
||
|
this.meta = meta;
|
||
|
this.tests = [];
|
||
|
this.isSkipped = !!isSkipped;
|
||
|
}
|
||
|
}
|
||
|
exports.Fixture = Fixture;
|
||
|
class Test {
|
||
|
constructor(name, start, end, loc, meta, isSkipped) {
|
||
|
this.name = name;
|
||
|
this.loc = getLoc(loc);
|
||
|
this.start = start;
|
||
|
this.end = end;
|
||
|
this.meta = meta;
|
||
|
this.isSkipped = !!isSkipped;
|
||
|
}
|
||
|
}
|
||
|
exports.Test = Test;
|
||
|
class TestFileParserBase {
|
||
|
constructor(tokenType) {
|
||
|
this.tokenType = tokenType;
|
||
|
}
|
||
|
static formatComputedName(line) {
|
||
|
return (0, util_1.format)(COMPUTED_NAME_TEXT_TMP, line);
|
||
|
}
|
||
|
isAsyncFn( /* token */) {
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
getRValue( /* token */) {
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
getFunctionBody( /* token */) {
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
formatFnData( /* name, value, token */) {
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
analyzeMemberExp( /* token */) {
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
formatFnArg( /* arg */) {
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
getFnCall( /* token */) {
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
getTaggedTemplateExp( /* token */) {
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
analyzeFnCall( /* token */) {
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
parse( /* filePath, code */) {
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
getTokenType( /* token */) {
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
getCalleeToken( /* token */) {
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
getMemberFnName() {
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
getKeyValue() {
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
getStringValue() {
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
isApiFn(fn) {
|
||
|
return fn === 'fixture' || fn === 'test';
|
||
|
}
|
||
|
serializeObjExp(token) {
|
||
|
if (this.getTokenType(token) !== this.tokenType.ObjectLiteralExpression)
|
||
|
return {};
|
||
|
return token.properties.reduce((obj, prop) => {
|
||
|
const { key, value } = this.getKeyValue(prop);
|
||
|
if (typeof value !== 'string')
|
||
|
return {};
|
||
|
obj[key] = value;
|
||
|
return obj;
|
||
|
}, {});
|
||
|
}
|
||
|
processMetaArgs(token) {
|
||
|
if (this.getTokenType(token) !== this.tokenType.CallExpression)
|
||
|
return null;
|
||
|
const args = token.arguments;
|
||
|
let meta = {};
|
||
|
if (args.length === 2) {
|
||
|
const value = this.getStringValue(args[1]);
|
||
|
if (typeof value !== 'string')
|
||
|
return {};
|
||
|
meta = { [this.formatFnArg(args[0])]: value };
|
||
|
}
|
||
|
else if (args.length === 1)
|
||
|
meta = this.serializeObjExp(args[0]);
|
||
|
return meta;
|
||
|
}
|
||
|
getMetaInfo(callStack) {
|
||
|
return callStack.reduce((metaCalls, exp) => {
|
||
|
if (this.getTokenType(exp) !== this.tokenType.CallExpression)
|
||
|
return metaCalls;
|
||
|
const callee = this.getCalleeToken(exp);
|
||
|
const calleeType = this.getTokenType(callee);
|
||
|
const isCalleeMemberExp = calleeType === this.tokenType.PropertyAccessExpression;
|
||
|
if (isCalleeMemberExp && this.getMemberFnName(exp) === 'meta')
|
||
|
return [this.processMetaArgs(exp)].concat(metaCalls);
|
||
|
return metaCalls;
|
||
|
}, []);
|
||
|
}
|
||
|
static isSkipped(originalToken, token = originalToken) {
|
||
|
var _a, _b;
|
||
|
const needSkip = ((_a = token === null || token === void 0 ? void 0 : token.property) === null || _a === void 0 ? void 0 : _a.name) === SKIP_PROPERTY_NAME || ((_b = token === null || token === void 0 ? void 0 : token.name) === null || _b === void 0 ? void 0 : _b.text) === SKIP_PROPERTY_NAME;
|
||
|
if (!needSkip) {
|
||
|
token = token.callee || token.tag || token.object || token.expression;
|
||
|
return token ? TestFileParserBase.isSkipped(originalToken, token) : false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
checkExpDefineTargetName(type, apiFn) {
|
||
|
//NOTE: fixture('fixtureName').chainFn or test('testName').chainFn
|
||
|
const isDirectCall = type === this.tokenType.Identifier;
|
||
|
//NOTE: fixture.skip('fixtureName'), test.only('testName') etc.
|
||
|
const isMemberCall = type === this.tokenType.PropertyAccessExpression &&
|
||
|
METHODS_SPECIFYING_NAME.indexOf(apiFn) > -1;
|
||
|
//NOTE: fixture.before().after()('fixtureName'), test.before()`testName`.after() etc.
|
||
|
const isTailCall = type === this.tokenType.CallExpression;
|
||
|
return isDirectCall || isMemberCall || isTailCall;
|
||
|
}
|
||
|
analyzeToken(token) {
|
||
|
const tokenType = this.tokenType;
|
||
|
const currTokenType = this.getTokenType(token);
|
||
|
switch (currTokenType) {
|
||
|
case tokenType.ExpressionStatement:
|
||
|
case tokenType.TypeAssertionExpression:
|
||
|
return this.analyzeToken(token.expression);
|
||
|
case tokenType.FunctionDeclaration:
|
||
|
case tokenType.FunctionExpression:
|
||
|
if (this.isAsyncFn(token))
|
||
|
return null;
|
||
|
return this.getFunctionBody(token).map(this.analyzeToken, this);
|
||
|
case tokenType.VariableDeclaration:
|
||
|
case tokenType.VariableStatement: {
|
||
|
const variableValue = this.getRValue(token); // Skip variable declarations like `var foo;`
|
||
|
return variableValue ? this.analyzeToken(variableValue) : null;
|
||
|
}
|
||
|
case tokenType.CallExpression:
|
||
|
case tokenType.PropertyAccessExpression:
|
||
|
case tokenType.TaggedTemplateExpression:
|
||
|
return this.analyzeFnCall(token);
|
||
|
case tokenType.ReturnStatement:
|
||
|
return token.argument ? this.analyzeToken(token.argument) : null;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
collectTestCafeCalls(astBody) {
|
||
|
let calls = [];
|
||
|
astBody.forEach(token => {
|
||
|
const callExps = this.analyzeToken(token);
|
||
|
if (callExps)
|
||
|
calls = calls.concat(callExps);
|
||
|
});
|
||
|
return calls;
|
||
|
}
|
||
|
analyze(astBody) {
|
||
|
const fixtures = [];
|
||
|
const testCafeAPICalls = this.collectTestCafeCalls(astBody);
|
||
|
testCafeAPICalls.forEach(call => {
|
||
|
if (!call || typeof call.value !== 'string')
|
||
|
return;
|
||
|
if (call.fnName === 'fixture') {
|
||
|
fixtures.push(new Fixture(call.value, call.start, call.end, call.loc, call.meta, call.isSkipped));
|
||
|
return;
|
||
|
}
|
||
|
if (!fixtures.length)
|
||
|
return;
|
||
|
// NOTE: If the fixture is skipped, mark all the tests in the fixture skipped, otherwise, use the current test identifier
|
||
|
const currentFixture = fixtures[fixtures.length - 1];
|
||
|
const testIsSkipped = currentFixture.isSkipped || call.isSkipped;
|
||
|
const test = new Test(call.value, call.start, call.end, call.loc, call.meta, testIsSkipped);
|
||
|
currentFixture.tests.push(test);
|
||
|
});
|
||
|
return fixtures;
|
||
|
}
|
||
|
async readFile(filePath) {
|
||
|
let fileContent = '';
|
||
|
try {
|
||
|
fileContent = await (0, promisified_functions_1.readFile)(filePath, 'utf8');
|
||
|
}
|
||
|
catch (err) {
|
||
|
throw new runtime_1.GeneralError(types_1.RUNTIME_ERRORS.cannotFindSpecifiedTestSource, filePath);
|
||
|
}
|
||
|
return fileContent;
|
||
|
}
|
||
|
async getTestList(filePath) {
|
||
|
const fileContent = await this.readFile(filePath);
|
||
|
return this.parse(fileContent);
|
||
|
}
|
||
|
getTestListFromCode(code) {
|
||
|
return this.parse(code);
|
||
|
}
|
||
|
}
|
||
|
exports.TestFileParserBase = TestFileParserBase;
|
||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdC1maWxlLXBhcnNlci1iYXNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2NvbXBpbGVyL3Rlc3QtZmlsZS90ZXN0LWZpbGUtcGFyc2VyLWJhc2UuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsNkVBQTZEO0FBQzdELCtCQUE4QjtBQUM5QixrREFBb0Q7QUFDcEQsOENBQW9EO0FBRXBELE1BQU0sdUJBQXVCLEdBQUcsQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLENBQUM7QUFDakQsTUFBTSxzQkFBc0IsR0FBSSwyQkFBMkIsQ0FBQztBQUM1RCxNQUFNLGtCQUFrQixHQUFRLE1BQU0sQ0FBQztBQUV2QyxTQUFTLE1BQU0sQ0FBRSxHQUFHO0lBQ2hCLHVEQUF1RDtJQUN2RCxNQUFNLE9BQU8sR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQztJQUV2QyxzSEFBc0g7SUFDdEgsOEhBQThIO0lBQzlILG1EQUFtRDtJQUNuRCxPQUFPLE9BQU8sQ0FBQyxRQUFRLENBQUM7SUFDeEIsT0FBTyxPQUFPLENBQUMsY0FBYyxDQUFDO0lBQzlCLE9BQU8sT0FBTyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUM7SUFDM0IsT0FBTyxPQUFPLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQztJQUV6QixPQUFPLE9BQU8sQ0FBQztBQUNuQixDQUFDO0FBRUQsTUFBYSxPQUFPO0lBQ2hCLFlBQWEsSUFBSSxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxTQUFTO1FBQy9DLElBQUksQ0FBQyxJQUFJLEdBQVEsSUFBSSxDQUFDO1FBQ3RCLElBQUksQ0FBQyxHQUFHLEdBQVMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzdCLElBQUksQ0FBQyxLQUFLLEdBQU8sS0FBSyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxHQUFHLEdBQVMsR0FBRyxDQUFDO1FBQ3JCLElB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
|