UNPKG

mockzilla

Version:

A mocking toolkit leveraging the power of TypeScript to enhance your jest experience.

216 lines (215 loc) 8.26 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MockzillaNode = void 0; const chalk_1 = __importDefault(require("chalk")); const jest_diff_1 = require("jest-diff"); const expect_utils_1 = require("@jest/expect-utils"); const error_1 = require("./error"); const EXPECTATION = chalk_1.default.green("Expectation"); const ERROR = chalk_1.default.red("ERROR"); class MockzillaNode { constructor(path) { this.disabled = false; this.children = {}; this.pathTo = (key) => (key ? `${this.path}.${key}` : this.path); this.path = path; this.traps = { ownKeys: () => { this.disabledCheck("ownKeys"); return Object.getOwnPropertyNames(this.children); }, has: (target, prop) => this.disabledCheck(prop) || prop in this.children || false, get: (target, prop) => { this.disabledCheck(prop); if (prop in this.children) return this.children[prop].value; this.notImplemented(prop); }, // not to be called apply: () => this.disabledCheckNotImplemented("apply", undefined), getPrototypeOf: () => this.disabledCheckNotImplemented("getPrototypeOf", null), setPrototypeOf: () => this.disabledCheckNotImplemented("setPrototypeOf", false), isExtensible: () => this.disabledCheckNotImplemented("isExtensible", false), preventExtensions: () => this.disabledCheckNotImplemented("preventExtensions", false), set: () => this.disabledCheckNotImplemented("set", false), deleteProperty: () => this.disabledCheckNotImplemented("deleteProperty", false), construct: () => this.disabledCheckNotImplemented("construct", {}), getOwnPropertyDescriptor: () => this.disabledCheckNotImplemented("getOwnPropertyDescriptor", undefined), defineProperty: () => this.disabledCheckNotImplemented("defineProperty", false), }; this.proxy = new Proxy({}, this.traps); } notImplemented(what) { throw new error_1.MockzillaError(`Mock "${this.pathTo(what)}" is not implemented`); } disabledCheck(what) { if (this.disabled) throw new error_1.MockzillaError(`Mock "${this.pathTo(what)}" has been used after tests have finished!`); return false; } disabledCheckNotImplemented(what, ret) { this.disabledCheck(what) || this.notImplemented(what); return ret; } getNested(keys) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const nestedKey = keys.pop(); const nestedNode = keys.reduce((node, key) => node.getChildNode(key), this); return [nestedKey, nestedNode]; } allow(key) { const parts = key.split("."); if (parts.length > 1) { const [nestedKey, nestedNode] = this.getNested(parts); nestedNode.allow(nestedKey); return; } this.getChildNode(key); } setValue(key, value) { const parts = key.split("."); if (parts.length > 1) { const [nestedKey, nestedNode] = this.getNested(parts); nestedNode.setValue(nestedKey, value); return; } let child = this.getChild(key, "value"); if (!child) { child = { type: "value", value, }; this.children[key] = child; } } addExpectation(key, expectation) { const parts = key.split("."); if (parts.length > 1) { const [nestedKey, nestedConfig] = this.getNested(parts); nestedConfig.addExpectation(nestedKey, expectation); return; } let child = this.getChild(key, "method"); if (!child) { child = this.createMethod(key); this.children[key] = child; } if (expectation) child.expectations.push(expectation); } getCalls(key) { const parts = key.split("."); if (parts.length > 1) { const [nestedKey, nestedConfig] = this.getNested(parts); return nestedConfig.getCalls(nestedKey); } let child = this.getChild(key, "method"); if (!child) { child = this.createMethod(key); this.children[key] = child; } return child.calls; } getChild(key, type) { const child = this.children[key]; if (!child) return null; if (child.type !== type) throw new error_1.MockzillaError(`Expect a mock ${type}, but found a mock ${child.type} at ${this.path}.${key}`); return child; } createMethod(key) { const name = chalk_1.default.dim(`${this.pathTo(key)}()`); const expectations = []; const calls = []; return { type: "method", value(...args) { calls.push(args); const expectation = expectations.shift(); if (!expectation) throw new error_1.MockzillaError(`${ERROR}: ${name} has been called unexpectedly!`); if (expectation.spy) return expectation.spy(...args); if (expectation.args) { if (!(0, expect_utils_1.equals)(args, expectation.args, [expect_utils_1.iterableEquality])) { const title = `Expectation of call to ${name} did not match invocation`; const expectationStack = (0, error_1.colorizeStack)(expectation.stack, true); const argsDiff = (0, jest_diff_1.diff)(expectation.args, args); throw new error_1.MockzillaError(`${EXPECTATION}: ${expectationStack}\n \n${ERROR}: ${title}:\n${argsDiff}`); } } if (expectation.throws) throw expectation.throws; return expectation.returns; }, calls, expectations, }; } getChildNode(key) { let child = this.getChild(key, "node"); if (!child) { const node = new MockzillaNode(this.pathTo(key)); child = { type: "node", value: node.proxy, node, }; this.children[key] = child; } return child.node; } getProxy() { return this.proxy; } disable() { if (!this.disabled) { this.disabled = true; for (const key of Object.keys(this.children)) { const child = this.children[key]; if (child.type === "node") child.node.disable(); } this.children = {}; } } enable() { this.disabled = false; } isDisabled() { return this.disabled; } verify() { if (this.disabled) return; for (const key of Object.keys(this.children)) { const child = this.children[key]; if (child.type === "node") { child.node.verify(); } else if (child.type === "method") { const { expectations } = child; if (expectations.length) { const callName = `${this.pathTo(key)}()`; const uniqueExpectations = expectations.filter((e, i, self) => self.indexOf(e) === i); throw new error_1.MockzillaError(`Missing ${expectations.length} calls to ${chalk_1.default.dim(callName)}:\n${uniqueExpectations .map((expectation) => (0, error_1.colorizeStack)(expectation.stack, true)) .join("\n")}`, true); } } } } verifyAndDisable() { try { this.verify(); } finally { this.disable(); } } } exports.MockzillaNode = MockzillaNode;