loadmill
Version:
A node.js module for running load tests and functional tests on loadmill.com
214 lines (184 loc) • 6.88 kB
text/typescript
import * as fs from "fs";
import isEmpty = require('lodash/isEmpty');
import isAString = require('lodash/isString');
import * as util from 'util';
const CLI_COLORS = {
RED: '\x1b[31m',
GREEN: '\x1b[32m',
YELLOW: '\x1b[33m',
GREY: '\x1b[90m',
DEFAULT: '\x1b[0m'
}
const STATUSES = {
PASSED: "PASSED",
FAILED: "FAILED",
STOPPED: "STOPPED",
FLAKY: "FLAKY",
};
export enum FLOW_STATUS {
DISABLED = 'Disabled',
DRAFT = 'Draft',
EVALUATING = 'Evaluating',
ACTIVE = 'Active',
};
const HALF_TAB = " ";
function failedFlowLine(f: any, colors: any): string {
return `${HALF_TAB}${HALF_TAB}Flow ${f.description} - ${coloredFlowLine(f.status, colors)}`;
}
const FAILED_STATUS_LINE = `status: '${CLI_COLORS.RED}FAILED${CLI_COLORS.DEFAULT}'`;
const PASSED_STATUS_LINE = `status: '${CLI_COLORS.GREEN}PASSED${CLI_COLORS.DEFAULT}'`;
export const getObjectAsString = (obj, colors) => {
// trim response body to length of 255
if (obj.response && obj.response.text && obj.response.text.length > 1024) {
obj.response.text = obj.response.text.substring(0, 1024) + ' [trimmed]'
}
let str = util.inspect(obj, { showHidden: false, depth: null, compact: false } as any);
if (colors) {
str = str
.replace(/status:\s*'FAILED'/g, FAILED_STATUS_LINE)
.replace(/status:\s*'PASSED'/g, PASSED_STATUS_LINE);
}
return str;
}
const coloredFlowLine = (status, colors) => {
if (!colors) {
return status;
}
return `${getStatusColor(status)}${status}${CLI_COLORS.DEFAULT}`;
}
export const printOnlyFailedFlowRunsReport = (testSuitesRuns, logger, colors) => {
if (Array.isArray(testSuitesRuns) && testSuitesRuns.length > 0) {
let total = 0;
let totalFailed = 0;
let lines: Array<string> =[]
testSuitesRuns.forEach(suiteRun => {
const { flowRuns } = suiteRun;
if (flowRuns && Array.isArray(flowRuns)) {
total += flowRuns.length;
const suiteLines: Array<string> =[]
flowRuns.forEach((f) => {
if (f.status === STATUSES.FAILED && f.flowStatus === FLOW_STATUS.ACTIVE) {
suiteLines.push(failedFlowLine(f, colors))
}
});
if (suiteLines.length > 0) {
lines.push("");
lines.push(`${HALF_TAB}Test Suite ${suiteRun.description} has failed flow:`);
lines = lines.concat(suiteLines);
lines.push(`${HALF_TAB}More info can be found at ${suiteRun.url}`);
totalFailed += suiteLines.length;
}
}
});
if (lines.length >0) {
logger.log("");
logger.log(`Test Plan errors report - ${CLI_COLORS.RED}${totalFailed} flows have failed ${CLI_COLORS.DEFAULT} (out of ${total} total).`)
lines.forEach(l=> logger.log(l));
}
}
}
export const printTestSuitesRunsReport = (testPlanDescription, testSuitesRuns, logger, colors) => {
if (Array.isArray(testSuitesRuns) && testSuitesRuns.length > 0) {
logger.log("");
logger.log(`Test Plan [${testPlanDescription}] Suites Runs report:`);
testSuitesRuns.map(
ts => logger.log(`Test Suite ${ts.description} - ${coloredFlowLine(ts.status, colors)}`));
}
}
export const convertStrToArr = (strWithCommas) => {
return typeof strWithCommas !== "string" ? null : strWithCommas.split(",");
}
export const convertArrToLabelQueryParams = (arr: Array<string | number>): string => {
return '&label=' + arr.join('&label=');
}
export const filterLabels = (labels: Array<number | string>) => {
if (!Array.isArray(labels)) {
throw new Error(`lables need be in array format i.e. ['my label', 'another label']. Got ${labels}`);
}
if (labels.every(l => l == '')) {
return null;
}
return labels.filter(l => (typeof l === 'string' || typeof l === 'number') && l !== '');
}
export const filterTags = (tags: Array<string>) => {
if (!Array.isArray(tags)) {
throw new Error(`Tags need be in array format i.e. ['tag1', 'another tag']. Got ${tags}`);
}
if (tags.every(l => l == '')) {
return null;
}
return tags.filter(l => (typeof l === 'string') && l !== '');
}
export const isEmptyObj = (obj) => isEmpty(obj);
export const isString = (obj) => isAString(obj);
export const isUUID = s =>
/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(s);
export const toLoadmillParams = (rawParams: string[]) => {
const parameters = {};
rawParams.forEach(pair => {
if (!pair) {
return;
}
const pivot = pair.indexOf('=');
if (pivot <= 0) {
throw new Error(`Invalid parameter assignment: ${pair}`);
}
const name = pair.slice(0, pivot);
parameters[name] = pair.slice(pivot + 1, pair.length);
});
return parameters;
}
export const readRawParams = (filePath: string): string[] => {
try {
return fs.readFileSync(filePath, 'utf-8').split(/\r?\n/);
} catch (err) {
throw new Error(`Couldn't find file '${filePath}'. Please check file path and permissions.`);
}
};
export const sleep = async (ms: number) => {
await new Promise(r => setTimeout(r, ms));
};
export class Logger {
private readonly verb: boolean = false;
private readonly colors: boolean = false;
constructor(verbose: boolean, colors: boolean) {
this.verb = verbose;
this.colors = colors;
}
log = (msg, ...args) => console.log(msg, ...args);
error = (err) => {
if (this.colors) {
console.log(CLI_COLORS.RED, err, CLI_COLORS.DEFAULT);
} else {
console.log(err);
}
};
warn = (wrn) => {
if (this.colors) {
console.log(CLI_COLORS.YELLOW, wrn, CLI_COLORS.DEFAULT);
} else {
console.log(wrn);
}
};
verbose = (msg, ...args) => this.verb ? console.log(msg, ...args) : void (0);
}
export const getLogger = (testArgs) => {
const verbose = testArgs && testArgs.verbose ? testArgs.verbose : false;
const colors = testArgs && testArgs.colors ? testArgs.colors : false;
return new Logger(verbose, colors);
}
const getStatusColor = (status) => {
switch (status) {
case STATUSES.PASSED:
return CLI_COLORS.GREEN;
case STATUSES.FAILED:
return CLI_COLORS.RED;
case STATUSES.STOPPED:
return CLI_COLORS.GREY;
case STATUSES.FLAKY:
return CLI_COLORS.YELLOW;
default:
return CLI_COLORS.DEFAULT;
}
}
export const TESTING_ORIGIN = process.env.LOADMILL_ORIGIN || "https://app.loadmill.com";