UNPKG

@sprucelabs/test-utils

Version:

Helpful utilities to make asserting more complicated conditions quick and easy! ⚡️

195 lines (194 loc) 8.58 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.NULL_PLACEHOLDER = exports.CIRCULAR_PLACEHOLDER = exports.FUNCTION_PLACEHOLDER = exports.UNDEFINED_PLACEHOLDER = void 0; const chalk_1 = __importDefault(require("chalk")); const escapeRegExp_1 = __importDefault(require("lodash/escapeRegExp")); const get_1 = __importDefault(require("lodash/get")); const includes_1 = __importDefault(require("lodash/includes")); const isObject_1 = __importDefault(require("lodash/isObject")); const isObjectLike_1 = __importDefault(require("lodash/isObjectLike")); const AssertionError_1 = __importDefault(require("../AssertionError")); exports.UNDEFINED_PLACEHOLDER = '_____________undefined_____________'; exports.FUNCTION_PLACEHOLDER = '_____________function_____________'; exports.CIRCULAR_PLACEHOLDER = '_____________circular_____________'; exports.NULL_PLACEHOLDER = '_____________null_____________'; const assertUtil = { fail(message, stack) { throw new AssertionError_1.default(message ?? 'Fail!', stack); }, stringify(object) { let stringified; if (Array.isArray(object)) { stringified = `[\n ${object.map((o) => this.stringify(o).split('\n').join('\n '))}\n]`; } else if (typeof object === 'number') { // this hack allows the Spruce Test Reporter to render number errors (they got eaten by terminal-kit's style regex) stringified = chalk_1.default.bgBlack.white(` ${object} `); } else if (object instanceof Error) { stringified = `${object.stack ?? object.message}`; } else if (object instanceof RegExp) { stringified = `${object.toString()}`; } else if (typeof object === 'undefined') { stringified = 'undefined'; } else if (typeof object === 'string') { stringified = `"${object}"`; } else { stringified = JSON.stringify(assertUtil.dropInPlaceholders(object), undefined, 2).replace(/\\/g, ''); } if (stringified.length > 5000) { stringified = stringified.substr(0, 1000) + '\n\n... big object ...\n\n' + stringified.substr(stringified.length - 1000); } stringified = assertUtil.replacePlaceholders(stringified); return `${chalk_1.default.bold(stringified)}`; }, replacePlaceholders(str) { return str .replace(new RegExp(`"${exports.UNDEFINED_PLACEHOLDER}"`, 'g'), chalk_1.default.italic('undefined')) .replace(new RegExp(`"${exports.FUNCTION_PLACEHOLDER}"`, 'g'), chalk_1.default.italic('Function')) .replace(new RegExp(`"${exports.NULL_PLACEHOLDER}"`, 'g'), chalk_1.default.italic('NULL')); }, dropInPlaceholders(obj) { const checkedObjects = [ { obj, depth: 0 }, ]; let updated = this.dropInPlaceholder(obj, (obj, depth) => { if ((0, isObject_1.default)(obj) && checkedObjects.some((checked) => { return checked.obj === obj && checked.depth < depth; })) { return true; } checkedObjects.push({ obj, depth }); return false; }, exports.CIRCULAR_PLACEHOLDER); updated = this.dropInPlaceholder(updated, (obj) => typeof obj === 'undefined', exports.UNDEFINED_PLACEHOLDER); updated = this.dropInPlaceholder(updated, (obj) => typeof obj === 'function', exports.FUNCTION_PLACEHOLDER); updated = this.dropInPlaceholder(updated, (obj) => obj === null, exports.NULL_PLACEHOLDER); return updated; }, dropInPlaceholder(obj, checker, placeholder, depth = 1) { if (!(0, isObject_1.default)(obj)) { return obj; } const updated = Array.isArray(obj) ? [] : {}; Object.keys(obj).forEach((key) => { //@ts-ignore updated[key] = // @ts-ignore checker(obj[key], depth) ? placeholder : obj[key]; //@ts-ignore if (typeof updated[key] !== 'function' && (0, isObject_1.default)(updated[key])) { //@ts-ignore updated[key] = this.dropInPlaceholder( //@ts-ignore updated[key], checker, placeholder, depth + 1); } }); return updated; }, doHaystacksPassCheck(haystacks, needle, check) { return !!haystacks.find((haystack) => { try { check(haystack, needle); return true; } catch { return false; } }); }, assertTypeof(actual, type, message) { if (typeof actual !== type) { this.fail(message ?? `${JSON.stringify(actual)} is not a ${type}`); } }, assertErrorIncludes(matcher, err, msg) { const originalErrorMessage = err.message ?? ''; const errorMessage = originalErrorMessage.toLowerCase(); const needle = typeof matcher === 'string' ? matcher.toLowerCase() : matcher; if (typeof needle === 'string' && errorMessage.search(needle) === -1 && !errorMessage.includes(needle)) { this.fail(msg ?? `Expected thrown error whose message contains: \n\n${chalk_1.default.bold(matcher)}\n\nbut got back:\n\n\`${chalk_1.default.bold(originalErrorMessage)}\`.`, '\n\nStack: ' + err.stack); } else if (needle instanceof RegExp && errorMessage.search(needle) === -1 && originalErrorMessage.search(needle) === -1) { this.fail(msg ?? `Expected thrown error whose message matches the regex: \n\n${chalk_1.default.bold(matcher)}\n\nbut got back:\n\n\`${chalk_1.default.bold(originalErrorMessage)}\`.`, '\n\nStack: ' + err.stack); } }, partialContains(object, subObject) { const objProps = object ? Object.getOwnPropertyNames(object) : []; const subProps = subObject ? Object.getOwnPropertyNames(subObject) : []; if (objProps.length == 0 || subProps.length === 0) { return; } if (subProps.length > objProps.length) { return false; } for (const subProp of subProps) { if (!Object.prototype.hasOwnProperty.call(object, subProp)) { return false; } if ((!(0, isObjectLike_1.default)(object[subProp]) || !(0, isObjectLike_1.default)(subObject[subProp])) && object[subProp] !== subObject[subProp]) { return false; } if ((0, isObjectLike_1.default)(object[subProp]) && (0, isObjectLike_1.default)(subObject[subProp]) && !this.partialContains(object[subProp], subObject[subProp])) { return false; } } return true; }, valueAtPath(object, path) { return (0, get_1.default)(object, path); }, parseIncludeNeedle(needle) { const path = Object.keys(needle)[0]; const expected = path && needle[path]; const needleHasArrayNotation = !!(path && path.search(/\[\]\./) > -1); return { needleHasArrayNotation, path, expected }; }, splitPathBasedOnArrayNotation(path, haystack) { const pathParts = path.split('[].'); const pathToFirstArray = pathParts.shift() ?? ''; const pathAfterFirstArray = pathParts.join('[].'); const actualBeforeArray = this.valueAtPath(haystack, pathToFirstArray); return { actualBeforeArray, pathAfterFirstArray }; }, foundUsing3rdPartyIncludes(haystack, needle, isHaystackObject) { let passed = false; const escapedNeedle = typeof needle === 'string' ? (0, escapeRegExp_1.default)(needle) : needle; if (typeof haystack === 'string' && typeof needle === 'string' && haystack.search(escapedNeedle) > -1) { passed = true; } if (isHaystackObject && (0, includes_1.default)(haystack, escapedNeedle)) { passed = true; } if (isHaystackObject && this.partialContains(haystack, escapedNeedle)) { passed = true; } return passed; }, }; exports.default = assertUtil;