fluentrest-ts
Version:
A lightweight, fluent TypeScript API testing library inspired by Java's RestAssured. Built on top of Axios, JSONPath, and Joi for powerful request handling and response validation.
168 lines (167 loc) • 6.38 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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__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.writeLogFile = writeLogFile;
exports.log = log;
exports.logRequest = logRequest;
exports.logResponse = logResponse;
exports.logError = logError;
exports.formatError = formatError;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const chalk_1 = __importDefault(require("chalk"));
const config_1 = require("./config");
function resolveLogFilePath() {
return path.resolve(process.cwd(), (0, config_1.getCurrentDefaults)().logFilePath);
}
function writeLogFile(label, data) {
const logPath = resolveLogFilePath();
if (!fs.existsSync(path.dirname(logPath))) {
fs.mkdirSync(path.dirname(logPath), { recursive: true });
}
const logEntry = `\n--- ${label} ---\n${JSON.stringify(data, null, 2)}\n`;
fs.appendFileSync(logPath, logEntry);
}
/**
* Determines if a message should be logged based on the current and required log levels.
* @param current - The current log level set for the system.
* @param required - The minimum log level required to perform the log.
* @returns boolean indicating whether the log should be executed.
*/
function shouldLog(current, required) {
const levels = { none: 0, info: 1, debug: 2 };
return levels[current] >= levels[required];
}
/**
* Returns a colorized chalk function based on the provided log level.
* Used to visually differentiate log messages in the terminal.
* @param level - The log level.
* @returns chalk color function for the given level.
*/
function getColor(level) {
return level === "debug" ? chalk_1.default.gray :
level === "info" ? chalk_1.default.cyan :
chalk_1.default.white; // fallback, should never happen
}
/**
* Logs a labeled data message to the console and optionally to a file.
* Logging respects the current log level and configured verbosity.
* @param label - Descriptive label for the log entry.
* @param data - Any payload to log (object, string, etc).
* @param logLevel - Current configured log level.
* @param logToFile - Flag indicating whether to write logs to a file.
* @param level - Log level required to execute the log (defaults to 'info').
*/
function log(label, data, logLevel, logToFile, level = "info") {
if (!shouldLog(logLevel, level))
return;
const color = getColor(level);
console.log(color(`\n--- ${label} ---`));
console.dir(data, { depth: null, colors: true });
if (logToFile) {
writeLogFile(label, data);
}
}
/**
* Logs request details including method, URL, headers, params, and body.
* Respects the log level (skips if below 'info').
*/
function logRequest(config, logLevel, logToFile) {
if (!shouldLog(logLevel, "info"))
return;
let requestLog = {
method: config.method,
baseURL: config.baseURL,
endpoint: config.url,
data: config.data,
};
if (shouldLog(logLevel, "debug")) {
requestLog = {
...requestLog,
headers: config.headers,
params: config.params,
data: config.data,
timeout: config.timeout,
baseURL: config.baseURL
};
}
log("Request", requestLog, logLevel, logToFile, "info");
}
/**
* Logs the full HTTP response: status, headers, and body.
* Skips logging unless log level is 'info' or 'debug'.
*/
function logResponse(response, logLevel, logToFile) {
if (!shouldLog(logLevel, "info"))
return;
let responseLog = {
status: response.status,
data: response.data,
};
if (shouldLog(logLevel, "debug")) {
responseLog = {
...responseLog,
headers: response.headers,
data: response.data,
statusText: response.statusText
};
}
log("Response", responseLog, logLevel, logToFile, "info");
}
/**
* Logs errors that occur during request execution or assertions.
* This always logs if log level is 'error' or higher.
* Optionally writes error details to file if `toFile` is enabled.
*/
function logError(error, label, logLevel, logToFile, responseBody, errorCode) {
const formatted = formatError(label, error, responseBody, errorCode);
// Always log errors regardless of logLevel — this is a test failure
log(label, { message: formatted }, logLevel, logToFile, "debug");
}
/**
* Helper to format error messages consistently for throwing or logging.
* Includes optional context like payload and custom error code.
*/
function formatError(message, error, responseBody, errorCode) {
const stack = error?.stack ?? "No stack trace";
const bodySnippet = responseBody
? `\nResponse Body:\n${JSON.stringify(responseBody, null, 2)}`
: "";
const codePrefix = errorCode ? `[${errorCode}] ` : "";
return `${codePrefix}${message}\n${stack}${bodySnippet}`;
}