diff --git a/index.js b/index.js index 2b79dd6..0cfb9fb 100644 --- a/index.js +++ b/index.js @@ -35,31 +35,35 @@ AWS.setSDKInstance = function(sdk) { AWS.mock = function(service, method, replace) { // If the service does not exist yet, we need to create and stub it. if (!services[service]) { - services[service] = {}; - - /** - * Save the real constructor so we can invoke it later on. - * Uses traverse for easy access to nested services (dot-separated) - */ - services[service].Constructor = traverse(_AWS).get(service.split('.')); - services[service].methodMocks = {}; - services[service].invoked = false; + + const service_to_add = { + // Save the real constructor so we can invoke it later on. + // Uses traverse for easy access to nested services (dot-separated) + Constructor: traverse(_AWS).get(service.split('.')), + methodMocks: {}, + invoked: false + } + + services[service] = service_to_add; mockService(service); } + const service_obj = services[service] // Register the method to be mocked out. - if (!services[service].methodMocks[method]) { - services[service].methodMocks[method] = { replace: replace }; + if (!service_obj?.methodMocks[method]) { + + if(service_obj !== undefined) + service_obj.methodMocks[method] = { replace: replace }; // If the constructor was already invoked, we need to mock the method here. - if (services[service].invoked) { - services[service].clients.forEach(client => { + if (service_obj?.invoked) { + service_obj?.clients.forEach(client => { mockServiceMethod(service, client, method, replace); }) } } - return services[service].methodMocks[method]; + return service_obj?.methodMocks[method]; }; /** @@ -67,20 +71,26 @@ AWS.mock = function(service, method, replace) { */ AWS.remock = function(service, method, replace) { - if (services[service].methodMocks[method]) { + // If the method is inside the service, we restore the method + if (services[service]?.methodMocks[method]) { restoreMethod(service, method); - services[service].methodMocks[method] = { - replace: replace - }; + + const service_obj = services[service] + if(service_obj !== undefined) { + service_obj.methodMocks[method] = { + replace: replace + }; + } } - if (services[service].invoked) { - services[service].clients.forEach(client => { + // We check if the service was invoked or not. If it was, we mock the service method with the `replace` function + if (services[service]?.invoked) { + services[service]?.clients?.forEach(client => { mockServiceMethod(service, client, method, replace); }) } - return services[service].methodMocks[method]; + return services[service]?.methodMocks[method]; } /** @@ -89,28 +99,40 @@ AWS.remock = function(service, method, replace) { */ function mockService(service) { const nestedServices = service.split('.'); + const method = nestedServices.pop(); const object = traverse(_AWS).get(nestedServices); - const serviceStub = sinon.stub(object, method).callsFake(function(...args) { - services[service].invoked = true; - - /** - * Create an instance of the service by calling the real constructor - * we stored before. E.g. const client = new AWS.SNS() - * This is necessary in order to mock methods on the service. - */ - const client = new services[service].Constructor(...args); - services[service].clients = services[service].clients || []; - services[service].clients.push(client); - - // Once this has been triggered we can mock out all the registered methods. - for (const key in services[service].methodMocks) { - mockServiceMethod(service, client, key, services[service].methodMocks[key].replace); - }; - return client; - }); - services[service].stub = serviceStub; + // Method type guard + if(!method) + return + + const service_obj = services[service] + + if(service_obj) { + const serviceStub = sinon.stub(object, method).callsFake(function(...args) { + + service_obj.invoked = true; + + /** + * Create an instance of the service by calling the real constructor + * we stored before. E.g. const client = new AWS.SNS() + * This is necessary in order to mock methods on the service. + */ + const client = new service_obj.Constructor(...args); + service_obj.clients = service_obj.clients || []; + service_obj.clients.push(client); + + // Once this has been triggered we can mock out all the registered methods. + for (const key in service_obj.methodMocks) { + mockServiceMethod(service, client, key, service_obj.methodMocks[key].replace); + }; + return client; + }); + service_obj.stub = serviceStub; + } + + }; /** diff --git a/index.ts b/index.ts index a4ea0c4..54f4ff6 100644 --- a/index.ts +++ b/index.ts @@ -18,21 +18,27 @@ import traverse from 'traverse' import {default as _AWS_SDK} from 'aws-sdk'; import {Readable} from 'stream' -import { ReplaceFn, ClientName, MethodName, mock, remock, restore, setSDK, setSDKInstance, Client } from '.'; +import { ReplaceFn, ClientName, MethodName, mock, remock, restore, setSDK, setSDKInstance, Client, AWSCallback, AWSRequest } from '.'; // TYPES ----------------------------------- // AWS type that is to serve as a mock +type AWS_Stub = { + _isMockFunction: boolean, + isSinonProxy: boolean +} + type AWS_MOCK = { mock?: typeof mock, remock?: typeof remock, restore?: typeof restore, setSDK?: typeof setSDK, setSDKInstance?: typeof setSDKInstance, + Promise?: Function } type Replace> = { replace: ReplaceFn, - stub?: SinonStubStatic + stub?: SinonStubStatic, } type MethodMock = { @@ -181,35 +187,44 @@ function mockService(service: ClientName) { /** * Wraps a sinon stub or jest mock function as a fully functional replacement function */ -function wrapTestStubReplaceFn(replace) { - if (typeof replace !== 'function' || !(replace._isMockFunction || replace.isSinonProxy)) { - return replace; - } - return (params, cb) => { - // If only one argument is provided, it is the callback - if (!cb) { - cb = params; - params = {}; +//TODO funciona no sandbox. Tem de ser um problema no vs ou qualquer cena +function wrapTestStubReplaceFn(replace: ReplaceFn> | AWS_Stub ) { + + if(typeof replace !== "function") { + if(!(replace._isMockFunction || replace.isSinonProxy)) { + return replace } - // Spy on the users callback so we can later on determine if it has been called in their replace - const cbSpy = sinon.spy(cb); - try { - // Call the users replace, check how many parameters it expects to determine if we should pass in callback only, or also parameters - const result = replace.length === 1 ? replace(cbSpy) : replace(params, cbSpy); - // If the users replace already called the callback, there's no more need for us do it. - if (cbSpy.called) { - return; + } + + else { + return (params: AWSRequest> | {}, cb: AWSCallback> | {}) => { + + // If only one argument is provided, it is the callback + if (!cb) { + cb = params; + params = {}; } - if (typeof result.then === 'function') { - result.then(val => cb(undefined, val), err => cb(err)); - } else { - cb(undefined, result); + + // Spy on the users callback so we can later on determine if it has been called in their replace + const cbSpy = sinon.spy(cb); + try { + // Call the users replace, check how many parameters it expects to determine if we should pass in callback only, or also parameters + const result = replace.length === 1 ? replace(cbSpy) : replace(params, cbSpy); + // If the users replace already called the callback, there's no more need for us do it. + if (cbSpy.called) { + return; + } + if (typeof result.then === 'function') { + result.then(val => cb(undefined, val), err => cb(err)); + } else { + cb(undefined, result); + } + } catch (err) { + cb(err); } - } catch (err) { - cb(err); - } - }; + }; + } } /** @@ -231,7 +246,9 @@ function mockServiceMethod(service: ClientName, client: Client, meth service_obj.methodMocks[method].stub = sinon.stub(client, method).callsFake(function() { const args = Array.prototype.slice.call(arguments); - let userArgs, userCallback; + let userArgs: string[] + let userCallback: Function; + if (typeof args[(args.length || 1) - 1] === 'function') { userArgs = args.slice(0, -1); userCallback = args[(args.length || 1) - 1]; @@ -240,6 +257,7 @@ function mockServiceMethod(service: ClientName, client: Client, meth } const havePromises = typeof AWS.Promise === 'function'; let promise, resolve, reject, storedResult; + const tryResolveFromStored = function() { if (storedResult && promise) { if (typeof storedResult.then === 'function') { @@ -251,6 +269,7 @@ function mockServiceMethod(service: ClientName, client: Client, meth } } }; + const callback = function(err, data) { if (!storedResult) { if (err) { @@ -264,6 +283,7 @@ function mockServiceMethod(service: ClientName, client: Client, meth } tryResolveFromStored(); }; + const request = { promise: havePromises ? function() { if (!promise) {