How to unit test SPFx promises with chained property and methods - sinon

How can i do unit test on sample TypeSCript function below? I'm trying to incorporate unit test with SharePoint Framework using PNP/SP library and having issues with creating the unit test.
public getListItems(): Promise<CustomListObject[]> {
return new Promise<CustomListObject[]>((resolve, reject) => {
let listObjects: CustomListObject[] = [];
this.sp.web.lists.getByTitle('TestList').items
.select('Title')
.filter('IsValidItem eq \'Yes\'')
.get().then(
(allItems: any[]) => {
allItems.forEach(item => {
listObjects.push({ 'title': item.Title });
});
resolve(listObjects);
}).catch(
error => {
console.log(error);
reject(error);
}
);
});
}

You can start with Jest Async: https://jestjs.io/docs/en/tutorial-async

Related

How to disable chromeWebSecurity in a certain test suite, in cypress

I am using cypress, and I want to disable chromeWebSecurity in test cases, but dont want change cypress config.
before each:
beforeEach('before test', () => {
Cypress.config('chromeWebSecurity',false);
cy.createUser('type').then((response) => {
ssoId = response.id;
phone = response.phone;
});
});
If I add ""chromeWebSecurity": false" in cypress config (cypress.json) - it works, but i dont want disable this in all my test suites.
I’m trying to add "Cypress.config('chromeWebSecurity',false);" before "cy.createUser('type').then((response) => {" in before each like this:
beforeEach('before test', () => {
Cypress.config('chromeWebSecurity',false);
cy.createUser('type').then((response) => {
ssoId = response.id;
phone = response.phone;
});
});
but it doesn’t work
According to cypress docs, you can add it as an option to the describe or it:
describe(
'login',
{
chromeWebSecurity: true
},
() => {
it('With webSecurity', () => {
// ...
})
it('Without webSecurity',
{chromeWebSecurity: false},
() => {
//...
}
)
}
)

How to unit test gutenberg class/component method that uses wp.apiFetch

I am trying to unit test a method on a class/component that calls the wp.apiFetch method but when I run the test it always says TypeError: apiFetch is not a function the below structure has worked for other test cases, all be it they don't make any api calls.
Stripped down test setup
global.wp = {};
// some other packages
Object.defineProperty(global.wp, 'apiFetch', { get: () => require('#wordpress/api-fetch') });
Stripped down test
Shallow render the component
Call the method in question on the component instance - at this point here the tests error because of the error mentioned at the top.
Check that the method was called
it('createAuthor valid', () => {
// may have to mock window.fetch as wp.apiFetch uses it but that's currently not the issue
const componentProps = {
someFunc: jest.fn(),
};
const component = shallow(<NewAuthor {...componentProps} />);
component.setState({ name: 'john smith' });
component.instance().createAuthor();
expect(componentProps.someFunc).toHaveBeenCalledTimes(1);
});
Stripped down component
const { apiFetch } = wp;
// some more constants from window
class NewAuthor extends Component {
// constructor
// method to test
createAuthor = () => {
const { someFunc, someErrorFunc } = this.props;
const authorObject = {
name,
} = this.state;
const createPath = '/wp/v2/users';
apiFetch({
path: createPath,
data: authorObject,
method: 'POST',
})
.then((user) => {
console.log('success', user);
someFunc();
})
.catch((error) => {
console.log('error', error);
someErrorFunc();
});
};
// some more methods
// render
}
export default NewAuthor;

How to test router code that contains heavy logic using sinon and stubbing (nodeJS)

I am new to using sinon, so sorry if my question is weird, I looked everywhere but can't find a way to do it.
I have app with express router. I want to write uint test for one of the routes. That route have an inner function that is 'heavy', meaning that it is async with promise, and in reality calls an external api. I want to stub that inner function in the test so that it will not use the api, and will return my own data instead of the original method.
This is the code so far:
routes/setOrder.js:
// the inner function I want to stub
var verifyPayment = function(saleId) {
return new Promise((resolve, reject) => {
logger.info(`verifyPayment: ${saleId}`);
externalAPICall.get( // <==this is the 'heavey part!!
saleId,
function (error, sale) {
if(error) {
return reject(`Error querying sale(${saleId}): ${error}`);
}
resolve(sale);
});
});
}
router.get('/paymentId/:paymentId', setOrderWithGet);
const setOrderWithGet =async function(req, res, next) {
const { paymentId } = req.params;
verifyPayment(paymentId)
.then(async sale => {
try {
console.log(`sale:${sale}`);
res.send(JSON.stringify({"status": "ok!" }));
} catch (err) {
logger.warn(err)
res.send(JSON.stringify({"status": "fail.."}));
}
})
.catch(reason => {
logger.warn(`[] Payment(${paymentId}) is not valid ${reason}`);
res.send(JSON.stringify({"status": "fail.."}));
});
}
module.exports = router;
module.exports.setOrderWithGet = setOrderWithGet;
module.exports.verifyPayment = verifyPayment;
setOrderTest.js:
const setOrderStub = require('../routes/setOrder');
describe("POST /setOrder", () => {
beforeEach(() => {
sinon.stub(setOrderStub, 'verifyPayment').resolves({....});
});
afterEach(() => {
sinon.restore();
});
describe("test1", () => {
it("setOrder first attempt", () => {
let req ={params : {'paymentId' : 'mypamentid1'}};
setOrderStub.setOrderWithGet(req,{});
});
});
});
This line:
sinon.stub(setOrderStub, 'verifyPayment').resolves({....});
...stubs the verifyPayment function on the module exports of the setOrder module.
Right now setOrderWithGet is calling the verifyPayment function directly, so it is unaffected by any changes to the module exports.
Change setOrderWithGet to call verifyPayment using the module exports:
const setOrderWithGet = async function(req, res, next) {
// ...
module.exports.verifyPayment(paymentId) // <= call the module export for verifyPayment
// ...
}
...and your stub will get called.

shouldn't I dispatch an action inside a .then statement?

I found a code on git which I'm trying to understand and in the code the guy have this function:
export function startAddTodo(text) {
return (dispatch, getState) => {
const UID = firebase.auth().currentUser.uid;
const todo = {
text,
isDone: false,
isStarred: false
};
const todoRef = firebaseRef.child(`todos/${UID}`).push(todo);
dispatch(addTodo({
id: todoRef.key,
...todo
}));
todoRef.then(snapshot => {
return;
}, error => {
Alert.alert(JSON.stringify(error.message));
});
};
}
Why shouldn't it be like
const todoRef = firebaseRef.child(`todos/${UID}`).push(todo);
todoRef.then(snapshot => {
dispatch(addTodo({
id: snapshot.key,
...todo
}));
})
I think this because the promise may be rejected, but in the first code he may get an error when trying to call todoRef.key inside the dispatch method.

Redux Chain Multiple Ajax Promises in one Action

I have a Redux action which needs to make 2 subsequent ajax calls.
The first calls googlemaps api: https://maps.googleapis.com/maps/api/geocode/json?address=${searchTerm}&key=${gmapsKey}
The second calls a local service based on those results
/api/content/stores/byDistance/${lat},${lng}/sort
I'm using superagent to do the ajax calls. Clearly I'm experience difficulties keeping track of the promises, and including failures.
Am I mis-undestanding a core concept of Promises? Is there a simpler way to write the below?
export function loadBySearch(searchTerm) {
const geoSearchUrl = `https://maps.googleapis.com/maps/api/geocode/json?address=${searchTerm}&key=${gmapsKey}`;
return {
types: [LOAD, LOAD_BY_LAT_LONG, LOAD_FAIL],
//Do I need to make this promise here?
promise: (client) => {
const promise = new Promise( (resolve, reject) => {
console.info('making google geocode request', geoSearchUrl);
superagent.get(geoSearchUrl)
.set('Accept', 'application/json')
.then( (successData1) =>{
const results = successData1.body.results;
if (!results.length) {
reject(`no results found for this search : ${searchTerm}`);
return;
}
const lat = results[0].geometry.location.lat;
const lng = results[0].geometry.location.lng;
const path = `/api/content/stores/byDistance/${lat},${lng}/sort`;
client.get(path).then(
(successData2) => {
resolve( {
searchTerm: searchTerm,
searchLocation: {
lat,
lng
},
data: successData2
});
},
(errorData2) => {
reject( {
searchTerm: searchTerm,
result: errorData2
});
},
);
},
(errorData1) => {
reject({
searchTerm: searchTerm,
result: errorData1
});
}
);
});
return promise;
}
};
}
I'm not using superagent, but I'm guessing something like this might just work:
superagent.get(geoSearchUrl)
.set('Accept', 'application/json')
.then(successData1 => {
const results = successData1.body.results;
if (!results.length) {
throw(`no results found for this search : ${searchTerm}`);
}
return Promise.resolve(results);
})
.then(results => {
const lat = results[0].geometry.location.lat;
const lng = results[0].geometry.location.lng;
const path = `/api/content/stores/byDistance/${lat},${lng}/sort`;
return client.get(path);
})
.then(successData2 => {
return Promise.resolve({
searchTerm: searchTerm,
searchLocation: {
lat,
lng
},
data: successData2
});
})
.catch(error => {
return Promise.reject({
searchTerm: searchTerm,
result: error
});
});
Haven't test it, but I hope at least it helps ;)

Resources