UNPKG

@typed/test

Version:
176 lines 7.16 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const typescript_1 = require("typescript"); const flatten_1 = require("../common/flatten"); const dependencies_1 = require("../typescript/dependencies"); const traverseNode_1 = require("../typescript/traverseNode"); function parseTestMetadata(sourceFiles, // tslint:disable-next-line:ban-types typedTestSymbol, typeChecker) { const testNodes = findAllTestNodes(sourceFiles, typedTestSymbol, typeChecker); const exportedTestIdentifiers = findExportedTestIndentifiers(testNodes); const exportedTestNames = exportedTestIdentifiers.map(x => x.exported); const testDeclarationNodes = testNodes.filter(isVariableDeclarationNodeSource(exportedTestNames)); return testDeclarationNodes.map(findTestMetadataFromNodeSource(exportedTestIdentifiers, testNodes)); } exports.parseTestMetadata = parseTestMetadata; function isVariableDeclarationNodeSource(exportNames) { return (x) => typescript_1.isVariableDeclaration(x.node) && exportNames.includes(findIdentifingName(x.node)); } function findTestMetadataFromNodeSource(testIdentifiers, testNodes) { return (nodeSource) => { const { node, sourceFile } = nodeSource; const statement = node.parent.parent; const declarationName = findVariableDeclarationName(node); const exportNames = testIdentifiers .filter(x => x.sourceFile.fileName === sourceFile.fileName && x.local === declarationName) .map(x => x.exported); const subTestNodes = testNodes.filter(findSubTests(nodeSource)); const subTests = subTestNodes .map(findNodeMetadata) .sort(({ position: a }, { position: b }) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)); const meta = Object.assign(Object.assign({}, findNodeMetadata({ node: statement, sourceFile })), { exportNames, filePath: sourceFile.fileName, additionalTests: [] }); return findAdditional(meta, subTests); }; } function findAdditional(metadata, nodes) { const possibleTests = nodes .filter(x => isContainedBy(metadata, x)) .sort(({ position: a }, { position: b }) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)); if (possibleTests.length === 0) { return metadata; } const testsWithAddtional = possibleTests .map((x, i) => findAdditional(x, possibleTests.slice(i))) .filter(x => x.additionalTests.length > 0); const others = possibleTests .map((x, i) => findAdditional(x, possibleTests.slice(i))) .filter(x => !testsWithAddtional.some(y => isContainedBy(y, x))); const testsToUse = [ ...others, ...testsWithAddtional.filter(x => !testsWithAddtional.some(y => y === x || isContainedBy(y, x))), ]; return Object.assign(Object.assign({}, metadata), { additionalTests: testsToUse.sort(({ position: a }, { position: b }) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)) }); } function isContainedBy(parent, child) { return parent.position[0] < child.position[0] && parent.position[1] > child.position[1]; } function findSubTests({ node, sourceFile }) { return (x) => x.sourceFile === sourceFile && x.node !== node && x.node.getStart() > node.getStart() && x.node.getEnd() < node.getEnd(); } function findNodeMetadata({ node, sourceFile }) { const subPosition = [node.getStart(), node.getEnd()]; const subLine = sourceFile.text.slice(0, subPosition[0]).split(/\n/g).length; const subText = node.getText(); const subLines = subText.split(/\n/g).length; return { line: subLine, lines: subLines, position: subPosition, text: subText, additionalTests: [], }; } function findAllTestNodes(sourceFiles, symbol, typeChecker) { return flatten_1.flatten(sourceFiles.map(sourceFile => { if (sourceFile.isDeclarationFile || !dependencies_1.hasExports(sourceFile)) { return []; } return traverseNode_1.traverseNode(findTestNodes(symbol, typeChecker), [], sourceFile).map(node => ({ node, sourceFile, })); })); } function findExportedTestIndentifiers(nodes) { return nodes .filter(x => isExportedVariableDeclaration(x.node) || isExportedIdentifier(x.node)) .map(x => typescript_1.isVariableDeclaration(x.node) ? { exported: findVariableDeclarationName(x.node), local: findVariableDeclarationName(x.node), sourceFile: x.sourceFile, } : Object.assign(Object.assign({}, findIdentifierName(x.node)), { sourceFile: x.sourceFile })); } function isExportedVariableDeclaration(x) { return ((typescript_1.isVariableDeclaration(x) && x.parent && typescript_1.isVariableDeclarationList(x.parent) && x.parent.parent && typescript_1.isVariableStatement(x.parent.parent) && x.parent.parent.modifiers && x.parent.parent.modifiers.some(m => m.kind === typescript_1.SyntaxKind.ExportKeyword)) || false); } function isExportedIdentifier(x) { if (typescript_1.isIdentifier(x) && x.parent) { if (typescript_1.isExportAssignment(x.parent) || typescript_1.isExportSpecifier(x.parent)) { return true; } } return false; } function findTestNodes(testSymbol, typeChecker) { return (testNodes, node) => { const types = tryGetTypes(node, typeChecker); if (!types) { return testNodes; } return types.map(tryGetSymbol).some(x => x === testSymbol) ? testNodes.concat(node) : testNodes; }; } function findIdentifingName(x) { return typescript_1.isVariableDeclaration(x) ? findVariableDeclarationName(x) : findIdentifierName(x).exported; } function findIdentifierName(x) { if (x.parent && typescript_1.isExportAssignment(x.parent) && x.parent.getText().includes('export default ')) { return { exported: 'default', local: x.text }; } return { exported: x.text, local: x.text }; } function findVariableDeclarationName(x) { const nodes = dependencies_1.getNodes(x); for (const node of nodes) { if (typescript_1.isIdentifier(node)) { return node.text; } } if (typescript_1.isIdentifier(x.name)) { return x.name.text; } throw new Error('Unabled to find test name'); } function tryGetTypes(node, typeChecker) { try { const type = typeChecker.getWidenedType(typeChecker.getTypeAtLocation(node)); const types = [type]; const baseTypes = type.getBaseTypes(); if (baseTypes) { types.push(...baseTypes); } if (typescript_1.isCallLikeExpression(node)) { const returnType = typeChecker.getReturnTypeOfSignature(typeChecker.getResolvedSignature(node)); types.push(returnType); } return types; } catch (_a) { return undefined; } } function tryGetSymbol(type) { if (!type) { return; } try { return type.getSymbol(); } catch (_a) { return undefined; } } //# sourceMappingURL=parseTestMetadata.js.map