soda-test
Version:
Package for Unit and API tests
324 lines • 14.3 kB
JavaScript
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
;