Jest - restore original moment function in one test - jestjs

How can I restore or implement mock in one test? I have a function:
const parser = (data)=>{
const now = moment().format('DD-MM-YYYY');
//more implementation
const parsedDate = moment(dateFromData).format('DD-MM-YYYY')
return {
date: isDateFromDataExists ? now : parsedDate
}
}
And now I would like to test my function. When isDateFromDataExists is true I would like to return current year (which should be mocked), but when isDateFromDataExists is false I would like to return parsedDate.
When I mocked globally
jest.mock('moment', () => () => ({format: () => '01-01-2020'}));
I can't "unlock" in one test.
How can I do this with Jest?
====EDIT====
This is my code.
const myParser = (single) => {
const obj = {};
const now = moment().format("DD-MM-YYYY");
obj.availableFrom = get(single, 'FreeFrom', '') !== '' ? moment(get(single, 'FreeFrom'), 'YYYY-MM-DD').format("DD-MM-YYYY") : now;
return obj;
}
This is my tests
test('Return correct json when FreeFrom is empty', async () => {
const xmlData = fs.readFileSync(path.join(__dirname, './testData/1.xml'), 'utf8');
var jsonObj = parser.parse(xmlData, options, true);
const result = require('./testData/1.json');
expect(myParser(jsonObj)).toEqual(result);
});
test('Return correct json when FreeFrom is available', async () => {
const xmlData = fs.readFileSync(path.join(__dirname, './testData/2.xml'), 'utf8');
var jsonObj = parser.parse(xmlData, options, true);
const result = require('./testData/2.json');
expect(myParser(jsonObj)).toEqual(result);
});
I have to cover two test cases.
When FreeFrom is not empty string then convert value with moment. Then format to my format.
When FreeFrom is empty string then use current date with moment. Then format to my format.
In case 2. I would like to mock current date to make tests independent of system date.

As suggested in the comments check related question
Here's how you could implement it
let dateFromData = null;
let isDateFromDataExists = null;
const parser = (data) => {
const now = moment().format('DD-MM-YYYY');
const parsedDate = moment(dateFromData, 'DD-MM-YYYY').format('DD-MM-YYYY')
return {
date: isDateFromDataExists ? now : parsedDate
}
}
const {
core: {
beforeEach,
describe,
it,
expect,
run
},
} = window.jestLite;
describe("parser", () => {
beforeEach(() => {
const currentTimestamp = moment().valueOf();
moment.now = jestLite.core.jest.fn().mockReturnValue(currentTimestamp);
});
it("return current date when isDateFromDataExists is true", () => {
isDateFromDataExists = true;
const currentDate = moment().format('DD-MM-YYYY');
expect(parser().date).toBe(currentDate);
});
it("return parsed date when isDateFromDataExists is false", () => {
isDateFromDataExists = false;
dateFromData = "02-01-2003";
expect(parser().date).toBe(dateFromData);
});
});
run().then(result => {
console.log(result);
})
<script src="https://unpkg.com/jest-lite#1.0.0-alpha.4/dist/core.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.1/moment.min.js"></script>

Related

Is there a way to easily compare 2 AST structures disregarding position?

Is there a way to easily compare 2 ast's on babel?
Consider a program like this as the source file:
// example.ts
import { foo } from "bar";
describe("Some test", () => {
let moduleFixture: any;
beforeAll(async () => {
moduleFixture = await foo.createTestingModule({}).compile();
});
afterAll(async () => {
await foo.tearDown();
});
});
And consider the following babel program
const babelParser = require("#babel/parser");
const { default: traverse } = require("#babel/traverse");
const { readFileSync } = require("fs");
const recast = require("recast");
const { default: template } = require("#babel/template");
const source = readFileSync(`./example.ts`, "utf-8");
const buildBeforeAll = template(
` beforeAll(async () => {
moduleFixture = await foo.createTestingModule({}).compile();
}); `,
{
plugins: ["typescript"],
}
);
const beforeAllAst = buildBeforeAll();
const ast = babelParser.parse(source, {
allowImportExportEverywhere: true,
plugins: ["typescript"],
});
traverse(ast, {
enter(path) {
const isBeforeAll = path.isIdentifier({ name: "beforeAll" });
if (isBeforeAll) {
// Somehow compare is beforeAllASt === path
console.log(`found an appropriate beforeall`);
path.replaceWithSourceString(`beforeEach`);
}
},
});
console.log(recast.print(ast).code);
What would be the best way to compare beforeAllAst with a traversed node?
The simplest way to made such modifications will be using 🐊Putout code transformer, I'm working on.
Here is how it looks like:
const source = `
beforeAll(async () => {
moduleFixture = await foo.createTestingModule({}).compile();
});
`;
module.exports.replace = () => ({
[source]: (vars, path) => {
path.node.callee.name = 'beforeEach'
return path;
},
});
This plugin searches for exact mach of beforeAll function call. It uses #putout/compare which can be used directly in your code:
const {compare} = require('#putout/compare');
traverse(ast, {
enter(path) {
const isBeforeAll = path.isIdentifier({ name: "beforeAll" });
if (compare(path, beforeAllAst)) {
// Somehow compare is beforeAllASt === path
console.log(`found an appropriate beforeall`);
path.replaceWithSourceString(`beforeEach`);
}
},
});
#putout/compare compares AST-node with given string or other AST-node

Why is my unit test saying a page has not been rendered when it clearly has been?

This is the unit test in question:
const chai = require('chai');
const sinonChai = require('sinon-chai');
const sinon = require('sinon');
const appRefAPI = require('../../../../../../app/services/api');
const { getMarkAsManual, submitMarkAsManual } = require('../../../../../../app/services/handler/manual/mark-as-manual-handler');
const appRefResult = JSON.parse(JSON.stringify(require('../../../response/application-received-full')));
const postData = JSON.parse(JSON.stringify(require('../../../response/post-app')));
const offerAccepted = JSON.parse(JSON.stringify(require('../../../response/exception-offer-accepted')));
const { expect } = chai;
chai.use(sinonChai);
describe('details/mark-as-manual-handler.js', () => {
let req;
let res;
let sandbox;
describe.only('submitMarkAsManual()', async () => {
before(() => {
res = {
render: () => ({})
};
req = {
session: {}
};
sandbox = sinon.createSandbox();
});
beforeEach(() => {
sandbox.stub(res, 'render').returns({});
sandbox.stub(appRefAPI, 'postClose').returns([200, postData]);
});
afterEach(() => {
sandbox.restore();
});
it('should render-manually-process-confirmation', () => {
req.session.application_reference = 'EZ123456';
req.session.data = offerAccepted
req.body = {
'manually-processed-day': '3',
'manually-processed-month': '3',
'manually-processed-year': '1999'
}
res.locals = {};
res.locals.application_reference = req.session.application_reference;
submitMarkAsManual(req, res);
console.log(res.render)
expect(res.render).to.have.been.calledOnceWith('pages/manually-process-confirmation');
});
});
});
This is the code it's looking at:
const submitMarkAsManual = async (req, res) => {
const errors = [];
let dd = req.body['manually-processed-day'];
let mm = req.body['manually-processed-month'];
let yyyy = req.body['manually-processed-year'];
if (dd.length === 1) dd = '0'+dd;
if (mm.length === 1) mm = '0'+mm;
const credit_date = `${dd}/${mm}/${yyyy}`
res.locals = req.session.data;
res.locals.credit_date = credit_date;
if (util.isValidDate(credit_date) === false) {
errors.push('date-invalid');
res.render('pages/mark-as-manual', { errors });
} else {
let data = {
"application_id": req.session.application_reference,
"closure_reason": "offer_response_processed_manually",
"credit_date": credit_date
}
const response = await callAPI.postClose(data);
if (response[0] === 200 && response[1].status === 'SUCCESS') {
console.log('success!!!!')
res.render('pages/manually-process-confirmation');
}else{
res.redirect('/budgeting-loans-ui/problem-with-service');
}
}
};
And from this I get the following message:
1) details/mark-as-manual-handler.js
submitMarkAsManual()
should render-manually-process-confirmation:
AssertionError: expected render to have been called exactly once with arguments pages/manually-process-confirmation
at Context.<anonymous> (test/unit/app/services/handler/manual/mark-as-manual-handler-test.js:85:45)
at processImmediate (node:internal/timers:463:21)
In the code being tested, just before the render, I've put in a console.log call that outputs 'success!!!!'. When I run the test, this pops out so I know it reaches (and presumably executes) the render.
Any suggestions?
Move the following lines to an asynchronous function:
submitMarkAsManual(req, res);
console.log(res.render)
expect(res.render).to.have.been.calledOnceWith('pages/manually-process-confirmation');
with a call to an asynchronous function:
const sub = async (req, res) => {
await submitMarkAsManual(req, res)
expect(res.render).to.have.been.calledOnceWith('pages/manually-process-confirmation');
}
(Thanks to IAmDranged for pointing the way.)

How to test while loop code using jest and nodejs

I'm having a hard time doing some test on while loop using jest. This is the code I want to test but don't know how to do it.
const SHA256 = require('crypto-js/sha256')
class Block {
constructor(index, timestamp, data, prevHash = "") {
this.index = index
this.timestamp = timestamp
this.data = data
this.prevHash = prevHash
this.hash = this.calculateHash()
this.nonce = 0
}
calculateHash() {
return SHA256(this.index + this.prevHash + this.timestamp + JSON.stringify(this.data) + this.nonce).toString()
}
mineBlock(difficulty) {
while(this.hash.substring(0, difficulty) !== Array(difficulty + 1).join('0')) {
this.nonce++
this.hash = this.calculateHash()
}
}
}
module.exports = Block
This is what I've done so far
const Block = require('../block')
const BlockClass = new Block()
describe('Block Class', () => {
it('constructor', () => {
const obj = new Block(1, 2, 3, 4, 0)
expect(obj.index).toBe(1)
expect(obj.timestamp).toBe(2)
expect(obj.data).toBe(3)
expect(obj.prevHash).toBe(4)
expect(obj.nonce).toBe(0)
})
})
describe('hash', () => {
it('should be string', () => {
expect(typeof BlockClass.calculateHash()).toBe('string')
})
})
I'm pretty new with jest and unit testing and I find it really nice skills to have.
You can do something like this.
const Block = require('../block')
describe('mineBlock', () => {
let block = new Block()
it('should do <something>', () => {
block.mineBlock(5)
expect(block.nonce).toBe('<something>')
expect(block.hash).toBe('<something>')
})
})
Please replace <something> with real values you want.

Getting document not a function

I am attempting to retrieve the boolean child (notificationsOn) of an object stored as a Firestore document to see if the rest of a function should be executed.
The overall function works to completion without this portion, but adding the portion from let threadDoc to the if statement presents a "threadDoc.get is not a function" error. I think my syntax is wrong but I don't know how, as a similar function works in a later part of the function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.sendDMNotification =functions.firestore.document('/dm_threads/{thread_id}/messages/{message_id}').onCreate((snapshot, context) => {
const newMessage = snapshot.data();
const senderName = newMessage.authorName;
const senderID = newMessage.authorUID;
const messageText = newMessage.message;
const recipientID = newMessage.recipientUID;
var notificationsOn = null;
let deviceTokenQuery = admin.firestore().collection(`/users/${recipientID}/device_tokens/`);
var idsToBeSorted = [senderID, recipientID];
idsToBeSorted.sort();
var threadID = idsToBeSorted[0] + idsToBeSorted[1];
console.log(recipientID);
console.log(threadID);
let threadDoc = admin.firestore().document(`users/${recipientID}/threads/${threadID}/`);
return threadDoc.get().then(doc => {
let notificationsOn = doc.data.notificationsOn;
console.log(notificationsOn);
if (notificationsOn !== false){
return deviceTokenQuery.get().then(querySnapshot => {
let tokenShapshot = querySnapshot.docs;
const notificationPromises = tokenShapshot.map(doc => {
let token_id = doc.data().tokenID;
const payload = {
data: {
title: senderName,
body: messageText,
senderID: senderID,
senderName: senderName
}
};
return admin.messaging().sendToDevice(token_id, payload).then(response => {
console.log("Notification sent: ", response);
})
.catch(error => {
console.log("Error sending message: ", error);
});
});
return Promise.all(notificationPromises);
});
}
return;
});
});
admin.firestore().document() was supposed to be admin.firestore().collection(...).doc(...)
This fixed my problem
I think you meant to say admin.firestore() instead of functions.firestore.

Why isn't sinon replacing the actual function call?

My function is:
const PhoneNumber = require('awesome-phonenumber');
const twilio = require('twilio')(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
const { twiml } = require('twilio');
exports.incoming = (requestBody) => {
const MessagingResponse = twiml.MessagingResponse;
const VoiceResponse = twiml.VoiceResponse;
const pn = new PhoneNumber(requestBody.Body, 'US');
return global.db.Conference.create({})
.then((dbCreate) => {
conferenceId = dbCreate.id;
// Call originator
return twilio.calls.create({
to: requestBody.From,
from: requestBody.To,
url: `${process.env.API_URL}/calls/conference?id=${conferenceId}`
});
})
My test is:
const _ = require('lodash');
const sinon = require('sinon');
const twilio = require('twilio')(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
const { twiml } = require('twilio');
const SmsController = require('../../../controllers/sms');
const twilioIncomingSmsReq = require('../../mocks/twilioIncomingSmsReq');
describe.only('Sms Controller', () => {
let messagingResponseMessageStub;
let conferenceCreateStub;
beforeEach(() => {
messagingResponseMessageStub = sinon.stub(twiml.MessagingResponse.prototype, 'message').returns(true);
conferenceCreateStub = sinon.stub(global.db.Conference, 'create').resolves({ id: 1 });
return;
});
afterEach(() => {
messagingResponseMessageStub.restore();
conferenceCreateStub.restore();
return;
});
it.only('should call the originator and recipient', () => {
let requestBody = _.clone(twilioIncomingSmsReq);
console.log(twilio.calls);
let twilioDialStub = sinon.stub(twilio.calls, 'create').resolves(true);
console.log(twilio.calls);
return SmsController.incoming(requestBody)
.then(() => {
sinon.assert.calledWith(twilioDialStub, {
to: requestBody.From,
from: requestBody.To,
url: `${process.env.API_URL}/calls?id=1`
});
sinon.assert.calledWith(twilioDialStub, {
to: '+number',
from: requestBody.From,
url: `${process.env.API_URL}/calls?id=1`
});
twilioDialStub.restore();
return;
});
});
});
But in my code, the actual twilion.calls.create function is called. Not the stub. What am I doing wrong?
twilio refer to different objects in these modules. require('twilio') is factory function. It's expected to create a new object even when it's called with same arguments, require('twilio')(...) !== require('twilio')(...).
Consider exporting twilio instance, at least for testing purposes:
const twilio = require('twilio')(process.env.TWILIO_ACCOUNT_SID,
...
exports.twilio = twilio;

Resources