testplane
Version:
Tests framework based on mocha and wdio
233 lines • 9.95 kB
JavaScript
;
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.ShallowStackFrames = exports.filterExtraStackFrames = exports.applyStackTraceIfBetter = exports.getFrameRelevance = exports.FRAME_RELEVANCE = exports.captureRawStackFrames = exports.getErrorTitle = void 0;
const lodash_1 = __importDefault(require("lodash"));
const error_stack_parser_1 = __importDefault(require("error-stack-parser"));
const logger = __importStar(require("../../utils/logger"));
const fs_1 = require("../../utils/fs");
const constants_1 = require("./constants");
const getErrorTitle = (e) => {
let errorName = e.name;
if (!errorName && e.stack) {
const columnIndex = e.stack.indexOf(":");
if (columnIndex !== -1) {
errorName = e.stack.slice(0, columnIndex);
}
else {
errorName = e.stack.slice(0, e.stack.indexOf("\n"));
}
}
if (!errorName) {
errorName = "Error";
}
return e.message ? `${errorName}: ${e.message}` : errorName;
};
exports.getErrorTitle = getErrorTitle;
const getErrorRawStackFrames = (e) => {
const errorTitle = (0, exports.getErrorTitle)(e) + "\n";
const errorTitleStackIndex = e.stack.indexOf(errorTitle);
if (errorTitleStackIndex !== -1) {
return e.stack.slice(errorTitleStackIndex + errorTitle.length);
}
const errorString = e.toString ? e.toString() + "\n" : "";
const errorStringIndex = e.stack.indexOf(errorString);
if (errorString && errorStringIndex !== -1) {
return e.stack.slice(errorStringIndex + errorString.length);
}
const errorMessageStackIndex = e.stack.indexOf(e.message);
const errorMessageEndsStackIndex = e.stack.indexOf("\n", errorMessageStackIndex + e.message.length);
if (errorMessageStackIndex !== -1) {
return e.stack.slice(errorMessageEndsStackIndex + 1);
}
const stackTraceRegExpResult = constants_1.STACK_FRAME_REG_EXP.exec(e.stack);
return stackTraceRegExpResult ? e.stack.slice(stackTraceRegExpResult.index) : "";
};
const captureRawStackFrames = (filterFunc) => {
const savedStackTraceLimit = Error.stackTraceLimit;
const targetObj = {};
Error.stackTraceLimit = constants_1.WDIO_STACK_TRACE_LIMIT;
Error.captureStackTrace(targetObj, filterFunc || exports.captureRawStackFrames);
Error.stackTraceLimit = savedStackTraceLimit;
const rawFramesPosition = targetObj.stack.indexOf("\n") + 1; // crop out error message
return targetObj.stack.slice(rawFramesPosition);
};
exports.captureRawStackFrames = captureRawStackFrames;
/**
* @description
* Rank values:
*
* 0: Can't extract code snippet; useless
*
* 1: WebdriverIO internals: Better than nothing
*
* 2: Project internals: Better than WebdriverIO internals, but worse, than user code part
*
* 3: User code: Best choice
*/
exports.FRAME_RELEVANCE = {
repl: { value: 0, matcher: fileName => /^REPL\d+$/.test(fileName) },
nodeInternals: { value: 0, matcher: fileName => /^node:[a-zA-Z\-_]/.test(fileName) },
wdioInternals: { value: 1, matcher: fileName => fileName.includes("/node_modules/webdriverio/") },
projectInternals: { value: 2, matcher: fileName => fileName.includes("/node_modules/") },
userCode: { value: 3, matcher: () => true },
};
const getFrameRelevance = (frame) => {
if ([frame.fileName, frame.lineNumber, frame.columnNumber].some(lodash_1.default.isUndefined)) {
return 0;
}
const fileName = (0, fs_1.softFileURLToPath)(frame.fileName);
for (const factor in exports.FRAME_RELEVANCE) {
if (exports.FRAME_RELEVANCE[factor].matcher(fileName)) {
return exports.FRAME_RELEVANCE[factor].value;
}
}
return 0;
};
exports.getFrameRelevance = getFrameRelevance;
const getStackTraceRelevance = (error) => {
const framesParsed = error_stack_parser_1.default.parse(error);
return framesParsed.reduce((maxRelevance, frame) => {
return Math.max(maxRelevance, (0, exports.getFrameRelevance)(frame));
}, 0);
};
const createErrorWithStack = (stack, errorMessage = "") => {
const newError = new Error(errorMessage);
newError.stack = (0, exports.getErrorTitle)(newError) + "\n" + stack;
return newError;
};
const applyStackTrace = (error, stack) => {
if (!error || !error.message) {
return error;
}
error.stack = (0, exports.getErrorTitle)(error) + "\n" + stack;
return error;
};
const applyStackTraceIfBetter = (error, stack) => {
if (!error || !(error instanceof Error) || !error.message) {
return error;
}
try {
const newStackTraceRelevance = getStackTraceRelevance(createErrorWithStack(stack));
const currentStackTraceRelevance = getStackTraceRelevance(error);
if (newStackTraceRelevance > currentStackTraceRelevance) {
applyStackTrace(error, stack);
}
}
catch (err) {
logger.warn("Couldn't compare error stack traces");
}
return error;
};
exports.applyStackTraceIfBetter = applyStackTraceIfBetter;
const filterExtraStackFrames = (error) => {
if (!error || !error.message || !error.stack) {
return error;
}
try {
const rawFrames = getErrorRawStackFrames(error);
const rawFramesArr = rawFrames.split("\n").filter(frame => constants_1.STACK_FRAME_REG_EXP.test(frame));
const framesParsed = error_stack_parser_1.default.parse(error);
if (rawFramesArr.length !== framesParsed.length) {
return error;
}
// If we found something more relevant (e.g. user's code), we can remove useless testplane internal frames
// If we haven't, we keep testplane internal frames
const shouldDropTestplaneInternalFrames = getStackTraceRelevance(error) > exports.FRAME_RELEVANCE.projectInternals.value;
const isIgnoredWebdriverioFrame = (frame) => {
const isWebdriverioFrame = frame.fileName && frame.fileName.includes("/node_modules/webdriverio/");
const fnName = frame.functionName;
if (!isWebdriverioFrame || !fnName) {
return false;
}
return constants_1.WDIO_IGNORED_STACK_FUNCTIONS.some(fn => fn === fnName || "async " + fn === fnName);
};
const isWdioUtilsFrame = (frame) => {
return Boolean(frame.fileName && frame.fileName.includes("/node_modules/@testplane/wdio-utils/"));
};
const isTestplaneExtraInternalFrame = (frame) => {
const testplaneExtraInternalFramePaths = [
"/node_modules/testplane/src/browser/history/",
"/node_modules/testplane/src/browser/stacktrace/",
];
return Boolean(frame.fileName && testplaneExtraInternalFramePaths.some(path => frame.fileName?.includes(path)));
};
const shouldIncludeFrame = (frame) => {
if (isIgnoredWebdriverioFrame(frame) || isWdioUtilsFrame(frame)) {
return false;
}
if (shouldDropTestplaneInternalFrames && isTestplaneExtraInternalFrame(frame)) {
return false;
}
return true;
};
const framesFiltered = rawFramesArr.filter((_, i) => shouldIncludeFrame(framesParsed[i])).join("\n");
return applyStackTrace(error, framesFiltered);
}
catch (filterError) {
logger.warn("Couldn't filter out wdio frames", filterError);
return error;
}
};
exports.filterExtraStackFrames = filterExtraStackFrames;
class ShallowStackFrames {
constructor() {
this._framesMap = new Map();
this._key = 1;
}
getKey() {
return String(this._key++);
}
enter(key, frames) {
this._framesMap.set(key, frames);
}
leave(key) {
this._framesMap.delete(key);
}
_getParentStackFrame(childFrames) {
for (const parentFrames of this._framesMap.values()) {
if (childFrames.length !== parentFrames.length && childFrames.endsWith(parentFrames)) {
return parentFrames;
}
}
return null;
}
areInternal(childFrames) {
const parentStackFrame = this._getParentStackFrame(childFrames);
if (!parentStackFrame) {
return false;
}
const isNodeModulesFrame = (frame) => frame.includes("/node_modules/");
const isNodeInternalFrame = (frame) => frame.includes(" (node:");
const extraFrames = childFrames.slice(0, childFrames.length - parentStackFrame.length);
const extraFramesArray = extraFrames.split("\n");
return extraFramesArray.every(frame => !frame || isNodeModulesFrame(frame) || isNodeInternalFrame(frame));
}
}
exports.ShallowStackFrames = ShallowStackFrames;
//# sourceMappingURL=utils.js.map