UNPKG

soda-test

Version:

Package for Unit and API tests

324 lines 14.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createSinon = void 0; const sinon_1 = require("sinon"); const rewire_1 = require("./rewire"); const testInfo_1 = require("./testInfo"); const setProperty_1 = require("./setProperty"); const testbed_1 = require("./testbed"); function setStub(stub, setStub, prevSinons) { if (setStub) { switch (setStub.type) { case testInfo_1.SetStubType.None: break; case testInfo_1.SetStubType.Fake: stub.callsFake(setStub.value); break; case testInfo_1.SetStubType.Return: stub.returns(setStub.value); break; case testInfo_1.SetStubType.Resolve: stub.resolves(setStub.value); break; case testInfo_1.SetStubType.Reject: stub.rejects(setStub.value); break; case testInfo_1.SetStubType.Access: if (setStub.value['getter']) { if (typeof (setStub.value['getter']) === 'function') { stub.get(setStub.value['getter']); } else { stub.get(() => setStub.value['getter']); } } if (setStub.value['setter']) { stub.set(setStub.value['setter']); } break; case testInfo_1.SetStubType.Construct: // create an object to return const rv = {}; for (const name in setStub.value) { rv[name] = prevSinons[setStub.value[name]]; } // the construct stub shall return the object with the other stubs stub.returns(rv); break; } } return stub; } function createSpyOrStubSinon(sinonInfo, prevSinons) { if (sinonInfo.kind !== testInfo_1.SinonKind.Spy && sinonInfo.kind !== testInfo_1.SinonKind.Stub) return null; let rewire; let target; if (typeof sinonInfo.target === "string") { // if 'sinonInfo.target' is a string, it is the name of the library of the target to spy/stub // get the libraray itself, and the 'rewire' object to access the libraray rewire = (0, rewire_1.getLibRewire)(sinonInfo.target, sinonInfo.caller); target = rewire.lib(); } else { // 'sinonInfo.target' is the 'target' object with the method to spy/stub // in this case 'rewire' is not defined target = sinonInfo.target; } let targetMethodName = sinonInfo.method; if (target && !targetMethodName) { // method name is not specified, maybe we have aggregation method if (target['__org_func__']) { targetMethodName = '__org_func__'; } else { // no aggregation, but maybe subbing/spying the deafult method is good enough if (target['default']) { targetMethodName = 'default'; } } } // create a spy/stub without puting it on exting object function emptySinon(orgMethod) { if (sinonInfo.kind == testInfo_1.SinonKind.Spy) { if (orgMethod) { // creting a spy that execute an exting method (but does not replace it) return (0, sinon_1.spy)(orgMethod); } // creating a spy that does nothing return (0, sinon_1.spy)(); } if (sinonInfo.kind == testInfo_1.SinonKind.Stub) { // creting a stub and setting its action as specify the 'sinonInfo.setStub' // the stub is returned, but is not connected to an object return setStub((0, sinon_1.stub)(), sinonInfo.setStub, prevSinons); } } // creting a spy/stub that is replacing an exting method on a target object // target: the target object the method exists on // method: the name of the method to spy/stub on 'target' object function bindSinon(target, method) { let sinon; if (sinonInfo.kind == testInfo_1.SinonKind.Spy) { // creating a spy on the 'method' that is on 'target' sinon = (0, sinon_1.spy)(target, method); } if (sinonInfo.kind == testInfo_1.SinonKind.Stub) { // creating a stub on the 'method' that is on 'target' and setting its action // as specfiied in 'sinonInfo.setStub' sinon = setStub((0, sinon_1.stub)(target, method), sinonInfo.setStub, prevSinons); } if (sinonInfo.setStub && sinonInfo.setStub.type === testInfo_1.SetStubType.Access) { // when using the access stub, we don't want to replace it with is current value return sinon; } // make sure the sinon is to the target object const restore = (0, setProperty_1.setProperty)(target, method, sinon); if (restore) { const _restore = sinon.restore; sinon.restore = () => { _restore(); restore(); }; } return sinon; } // rewired: whether the spy/stub method need to be set back using rewire object const rewired = rewire && rewire.isRewired(); // Temp code const sinonInfo1 = Object.assign({}, sinonInfo); sinonInfo1.target = target; sinonInfo1.method = targetMethodName; if (target && typeof sinonInfo1.target !== "string") { sinonInfo1.target = `[${typeof sinonInfo1.target}]`; } delete sinonInfo1.caller; delete sinonInfo1.context; sinonInfo1.kind = ((sinonInfo.kind === testInfo_1.SinonKind.Spy) ? "Spy" : (sinonInfo.kind === testInfo_1.SinonKind.Stub) ? "Stub" : "???"); sinonInfo1['rewired'] = rewired; // Temp code if (!rewired) { // we don't need to use 'rewire' to set the stub/spy to the 'target' if (!target && !targetMethodName) { // no 'target' and no 'targetMethodName' just return an empty spy/stub return emptySinon(); } if (target && targetMethodName) { // we have a target object and a name of a method const descriptor = Object.getOwnPropertyDescriptor(target, targetMethodName); if (!target['soda-test-star'] && (descriptor && (descriptor.value && descriptor.writable || descriptor.get && descriptor.set))) { // the method exists on the target as a writable or getter with setter property - create sinon as bining return bindSinon(target, targetMethodName); } if (sinonInfo.kind == testInfo_1.SinonKind.Stub && targetMethodName === 'super') { // incase the method name is 'super' we need to stub the prototype const stub = (0, sinon_1.stub)(); const prototype = Object.getPrototypeOf(target); Object.setPrototypeOf(sinonInfo.target, stub); const restore = stub.restore; stub.restore = function () { Object.setPrototypeOf(sinonInfo.target, prototype); if (restore) restore(); stub.restore = restore; }; return setStub(stub, sinonInfo.setStub, prevSinons); } // the target method does not exist as a writable property, create stub/spy instead of it const sinon = emptySinon(target[targetMethodName]); const restoreProp = (0, setProperty_1.setProperty)(target, targetMethodName, sinon); const restore = sinon.restore; sinon.restore = function () { restoreProp(); if (restore) restore(); sinon.restore = restore; }; return sinon; } // there is target or method name name but not both, not supported return undefined; //not supported sinon } // the libraray is rewired (has get/set) if (!sinonInfo.memberMethod) { // not having 'memberMethod' means we need to spy/stub the method itself // orgMethod2: the method of the libraray before spying/stubbing const orgMethod2 = rewire.safeget(targetMethodName); if (orgMethod2) { // the method to spy/stub exists in the libraray // orgMethod2: the method of the export object of the libraray (usally the same as orgMethod2) const orgMethod1 = target[targetMethodName]; // create a spy/stub offline const sinon = emptySinon(orgMethod2); // save the spy/stub in the libraray rewire.set(targetMethodName, sinon); // if the above same did not change the exported value, change it too if (orgMethod1 && target[targetMethodName] !== sinon) { target[targetMethodName] = sinon; } // set retore method: const restore = sinon.restore; sinon.restore = function () { // save the original method in the library rewire.set(targetMethodName, orgMethod2); // restore the export value if need to if (target[targetMethodName] !== orgMethod1) { target[targetMethodName] = orgMethod1; } sinon.restore = restore; if (restore) restore(); }; return sinon; } else { return bindSinon(target, targetMethodName); } } // need to spy/stub a class method // class2stub: the class where the 'memberMethod' to spy/stub exists on const class2stub = rewire.safeget(targetMethodName); if (class2stub) { const prototype = class2stub.prototype; if (prototype) { if (prototype.hasOwnProperty(sinonInfo.memberMethod)) { return bindSinon(prototype, sinonInfo.memberMethod); } else { // the class proeprty does not have the method member defined if (sinonInfo.memberMethod === 'super') { // stubbing the prototype const prototype = Object.getPrototypeOf(class2stub); const sinon = emptySinon(prototype); Object.setPrototypeOf(class2stub, sinon); const restore = sinon.restore; sinon.restore = function () { Object.setPrototypeOf(class2stub, prototype); sinon.restore = restore; if (restore) restore(); }; return sinon; } // creating a stub method on the prototype let sinon; let restore; if (sinonInfo.setStub && sinonInfo.setStub.type === testInfo_1.SetStubType.Access) { Object.defineProperty(prototype, sinonInfo.memberMethod, { get: () => null }); sinon = bindSinon(prototype, sinonInfo.memberMethod); restore = () => { delete prototype[sinonInfo.memberMethod]; }; } else { sinon = emptySinon(); restore = (0, setProperty_1.setProperty)(prototype, sinonInfo.memberMethod, sinon); } const _resotre = sinon.restore; sinon.restore = () => { restore && restore(); _resotre && _resotre(); }; return sinon; } } else { // The class does not have property console.log('############ not supported 2'); return undefined; //not supported sinon } } else { // the class to stub does not exist console.log('############ not supported 3'); return undefined; //not supported sinon } } function createSinon(sinonInfo, prevSinons) { if (!sinonInfo) { return null; } const sinon1 = createSpyOrStubSinon(sinonInfo, prevSinons); if (sinon1 === undefined) return null; if (sinon1) return sinon1; let rewire; if (typeof sinonInfo.target === "string") { rewire = (0, rewire_1.getLibRewire)(sinonInfo.target, sinonInfo.caller); } switch (sinonInfo.kind) { case testInfo_1.SinonKind.Spy: case testInfo_1.SinonKind.Stub: throw new Error("should handle Spy/Stub before this"); case testInfo_1.SinonKind.Rewire: case testInfo_1.SinonKind.RewireReload: if (rewire) { const reload = sinonInfo.kind == testInfo_1.SinonKind.RewireReload; if (reload) { const reloadrewire = (0, rewire_1.reloadLibRewire)(sinonInfo.target, sinonInfo.caller); return reloadrewire; } return rewire; } return null; case testInfo_1.SinonKind.Import: if (rewire) { return rewire.get(sinonInfo.method); } return null; case testInfo_1.SinonKind.Timers: const fakeTimers = (0, sinon_1.useFakeTimers)(sinonInfo.timersConfig); const tick = fakeTimers.tick; fakeTimers.atick = (ms) => { return Promise.resolve(tick(ms)); }; return fakeTimers; case testInfo_1.SinonKind.Fixture: const target = sinonInfo.target; return (0, testbed_1.createFixture)(target.component, target.options); case testInfo_1.SinonKind.Component: return (0, testbed_1.createComponent)(sinonInfo.target); } } exports.createSinon = createSinon; //# sourceMappingURL=sinons.js.map