Skip to content

Commit

Permalink
chore: Attempting to type "wrapTestSTubReplaceFn".
Browse files Browse the repository at this point in the history
Added the changes on the index.js to coincide with the ones on TS. All JS tests should STILL work, to avoid regressions.
  • Loading branch information
LuchoTurtle committed Sep 27, 2023
1 parent 33fb7d0 commit e407ff3
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 68 deletions.
102 changes: 62 additions & 40 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,52 +35,62 @@ 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];
};

/**
* Stubs the service and registers the method that needs to be re-mocked.
*/
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];
}

/**
Expand All @@ -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;
}


};

/**
Expand Down
76 changes: 48 additions & 28 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<C extends ClientName, M extends MethodName<C>> = {
replace: ReplaceFn<C, M>,
stub?: SinonStubStatic
stub?: SinonStubStatic,
}

type MethodMock = {
Expand Down Expand Up @@ -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<ClientName, MethodName<ClientName>> | 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<ClientName, MethodName<ClientName>> | {}, cb: AWSCallback<ClientName, MethodName<ClientName>> | {}) => {

// 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);
}
};
};
}
}

/**
Expand All @@ -231,7 +246,9 @@ function mockServiceMethod(service: ClientName, client: Client<ClientName>, 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];
Expand All @@ -240,6 +257,7 @@ function mockServiceMethod(service: ClientName, client: Client<ClientName>, meth
}
const havePromises = typeof AWS.Promise === 'function';
let promise, resolve, reject, storedResult;

const tryResolveFromStored = function() {
if (storedResult && promise) {
if (typeof storedResult.then === 'function') {
Expand All @@ -251,6 +269,7 @@ function mockServiceMethod(service: ClientName, client: Client<ClientName>, meth
}
}
};

const callback = function(err, data) {
if (!storedResult) {
if (err) {
Expand All @@ -264,6 +283,7 @@ function mockServiceMethod(service: ClientName, client: Client<ClientName>, meth
}
tryResolveFromStored();
};

const request = {
promise: havePromises ? function() {
if (!promise) {
Expand Down

0 comments on commit e407ff3

Please sign in to comment.