UNPKG

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.

156 lines (155 loc) 5.99 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResponseValidatorImpl = void 0; const assertions_1 = require("../assertions/assertions"); const utils_1 = require("./utils"); const logger_1 = require("./logger"); /** * A wrapper around the HTTP response (or error), * allowing safe validation and debugging. */ class ResponseValidatorImpl { constructor(response, error, config, logLevel = "info", logToFile = false, proxyOverride, proxyAgent) { this.response = response; this.error = error; this.config = config; this.logLevel = logLevel; this.logToFile = logToFile; this.proxyOverride = proxyOverride; this.proxyAgent = proxyAgent; } /** Returns true if the request failed due to error or missing response. */ wasFailure() { return !!this.error || !this.response; } /** Returns the raw Axios response object (throws if not available). */ getResponse() { if (!this.response) throw new Error("No response available."); return this.response; } /** Returns the Axios request config used to send the request. */ getRequestConfig() { return { ...this.config, ...(this.proxyOverride ? { proxy: this.proxyOverride } : {}), ...(this.proxyAgent ? { httpAgent: this.proxyAgent, httpsAgent: this.proxyAgent } : {}) }; } /** Asserts that the response status matches the expected value. */ thenExpectStatus(status) { if (!this.response) throw new Error("No response to validate status."); (0, assertions_1.expectStatus)(this.response, status, this.logLevel, this.logToFile); return this; } /** Asserts that a JSONPath value in the body matches the expected value. */ thenExpectBody(path, expected) { if (!this.response) throw new Error("No response to validate body."); (0, assertions_1.expectBody)(this.response, path, expected, this.logLevel, this.logToFile); return this; } /** Asserts that the body contains a specified fragment of key-values. */ thenExpectBodyContains(fragment) { if (!this.response) throw new Error("No response to validate body."); (0, assertions_1.expectBodyContains)(this.response, fragment, this.logLevel, this.logToFile); return this; } /** Validates the entire body against a Joi schema. */ thenValidateBody(schema) { if (!this.response) throw new Error("No response to validate schema."); (0, assertions_1.validateBody)(this.response, schema, this.logLevel, this.logToFile); return this; } /** Asserts that a response header matches the expected value. */ thenExpectHeader(key, value) { if (!this.response) throw new Error("No response to validate header."); (0, assertions_1.expectHeader)(this.response, key, value, this.logLevel, this.logToFile); return this; } // /** Extracts a value from the response body using a JSONPath. */ thenExtract(path) { if (!this.response) throw new Error("No response to extract from."); return (0, utils_1.extract)(this.response, path); } /** Returns the parsed JSON body of the response (throws if unavailable). */ thenJson() { if (!this.response) throw new Error("No response available to parse JSON."); return this.response.data; } /** * Executes a custom assertion or extraction callback * and logs failure context if it throws. */ catchAndLog(fn) { if (this.error) { const err = this.error instanceof Error ? this.error : new Error(String(this.error)); // Inject response body for context if (this.response?.data && typeof this.response.data === 'object') { err.message += `\nServer response: ${JSON.stringify(this.response.data)}`; } if (fn) fn(err); else throw err; return this; } try { fn?.(new Error("Unexpected call to catchAndLog without error")); } catch (err) { (0, logger_1.logError)(err, "Assertion Failed", this.logLevel, this.logToFile, this.response?.data); throw err; } return this; } /** Returns the raw error body (typically from server) if available. */ getErrorBody() { return this.response?.data; } /** * Runs multiple assertions on the current response context and aggregates any errors. * This allows for soft-failing multiple expectations without throwing after the first failure. * * Each assertion receives the current response object (`this`) and is expected * to throw an `Error` if it fails. All such errors are caught, aggregated, * and re-thrown at the end as a combined error with summary output. * * @param assertions - Array of functions to run, each receiving the response context. * @returns `this` for chaining. * * @example * await res.runAssertions([ * r => r.thenExpectStatus(404), * r => r.thenExpectBody('$.error', 'Not Found'), * ]).catchAndLog(...); */ runAssertions(assertions) { const errors = []; for (const fn of assertions) { try { fn(this); } catch (e) { if (e instanceof Error) { errors.push(e); } else { errors.push(new Error(String(e))); } } } if (errors.length > 0) { const summary = errors.map(e => e.message).join('\n'); throw new Error(`Multiple assertion failures:\n${summary}`); } return this; } } exports.ResponseValidatorImpl = ResponseValidatorImpl;