UNPKG

testdouble

Version:

A minimal test double library for TDD with JavaScript

84 lines (83 loc) • 3.51 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const lodash_1 = require("../wrap/lodash"); const imitate_1 = require("../imitate"); const log_1 = require("../log"); const reset_1 = require("../reset"); const anything_1 = require("../stringify/anything"); function default_1(object, property, manualReplacement) { const isManual = arguments.length > 2; const realThingExists = object[property] || Object.prototype.hasOwnProperty.call(object, property); if (isManual || realThingExists) { const getter = findGetterOnly(object, property); if (getter && getter.isOwn && !getter.descriptor.configurable) { return log_1.default.error('td.replace', `"${property}" is defined with a getter that cannot be redefined. Compilers like SWC and Babel define ES module exports this way. To replace a module's export, replace the whole module with td.replace('path/to/module') instead.`); } const realThing = object[property]; return lodash_1.default.tap(getFake(isManual, property, manualReplacement, realThing), (fakeThing) => { if (getter) { defineValue(object, property, fakeThing, getter.descriptor.enumerable); } else { object[property] = fakeThing; } reset_1.default.onNextReset(() => { if (getter && getter.isOwn) { Object.defineProperty(object, property, getter.descriptor); } else if (getter) { delete object[property]; } else if (realThingExists) { object[property] = realThing; } else { delete object[property]; } }); }); } else { log_1.default.error('td.replace', `No "${property}" property was found.`); } } exports.default = default_1; // Assigning to a getter with no setter throws, so find any such accessor // (on the object or up its prototype chain) to replace it by definition const findGetterOnly = (object, property) => { let thing = object; while (thing != null) { const descriptor = Object.getOwnPropertyDescriptor(thing, property); if (descriptor) { return descriptor.get && !descriptor.set ? { descriptor, isOwn: thing === object } : undefined; } thing = Object.getPrototypeOf(thing); } }; const defineValue = (object, property, value, enumerable) => { Object.defineProperty(object, property, { configurable: true, writable: true, enumerable, value }); }; const getFake = (isManual, property, manualReplacement, realThing) => { if (isManual) { warnIfTypeMismatch(property, manualReplacement, realThing); return manualReplacement; } else { return (0, imitate_1.default)(realThing, [property]); } }; const warnIfTypeMismatch = (property, fakeThing, realThing) => { const fakeType = typeof fakeThing; const realType = typeof realThing; if (realThing !== undefined && fakeType !== realType) { log_1.default.warn('td.replace', `property "${property}" ${(0, anything_1.default)(realThing)} (${lodash_1.default.capitalize(realType)}) was replaced with ${(0, anything_1.default)(fakeThing)}, which has a different type (${lodash_1.default.capitalize(fakeType)}).`); } };