Jest sessionStorage cannot be mocked - javascript

I have a util function that calls the sessionStorage. it fails saying received calls are 0. It fails due to sessionStorage accessing. It works if that part is commented out. I tried mocking the sessionStorage but still no luck.
Here's the test,
it("should be to search a composition query", async () => {
const compositionQuery = "1 and 2";
jest.mock("./composition.utils", () => {
const original = jest.requireActual("./composition.utils");
return {
...original,
createCompositionQuery: jest
.fn()
.mockImplementation(() => mockCompositionQuery),
};
});
Storage.prototype.getItem = jest
.fn()
.mockReturnValue(mockSearchHistory.toString());
const user = userEvent.setup();
const submit = jest.fn();
render(<Composition onSubmit={submit} />);
const compositionInput = screen.getByTestId("composition-query");
await user.click(compositionInput);
await waitFor(() => user.type(compositionInput, compositionQuery));
await waitFor(() => user.click(screen.getByTestId("search-btn")));
await waitFor(() =>
expect(submit).toBeCalledWith(
mockCompositionQuery,
`Composition of ${compositionQuery}`
)
);
});
Here's the util function that I'm mocking.
export const createCompositionQuery = (search: string) => {
const query: Query = {
type: "",
columns: addColumnsToDisplay(),
rowFilters: [],
customRowFilterCombination: "",
};
const searchTokens = search.split(/\s+/);
searchTokens.forEach((eachToken) => {
const history = JSON.parse(
window.sessionStorage.getItem("openmrsHistory")
);
const operandQuery = history[parseInt(eachToken) - 1];
const jsonRequestObject = operandQuery.parameters;
jsonRequestObject.customRowFilterCombination = formatFilterCombination(
jsonRequestObject.customRowFilterCombination,
query.rowFilters.length
});
return { query };
};
Here's the github link for the full implementation

Related

In Jest, how do I cause a function called within the function to return a specific value

This is the function I am testing (stripped down for simplicity's sake):
populate.js->
const { createSessionID } = require('./populate-template-utilities');
const createFile = async () => {
const responseHeader = {};
responseHeader.SessionID = createSessionID();
return responseHeader;
};
module.exports = {
createFile,
};
The function this function calls:
populate-template-utilities ->
const createSessionID = () => {
const digits = (Math.floor(Math.random() * 9000000000) + 1000000000).toString();
return `PAX${digits}`;
};
module.exports = {
createSessionID,
};
And my unit test (again stripped down):
const { createSessionID } = require('../app/lib/populate-template-utilities');
describe('create XML for output files', () => {
const mockID = jest
.spyOn(createSessionID)
.mockImplementation(() => 'PAX123456');
it('should create a PAX File', async () => {
const result = await createFile();
expect(result).toEqual(getFile);
});
});
I want createSessionID to return 'PAX123456' and think mockID should do it, but it's erroring with:
Cannot spy the undefined property because it is not a function; undefined given instead
The spyOn method needs at least two parameters: object and methodName.
Try sth like this:
import * as populateTemplateUtils from "../sessionStuff";
import { createFile } from "../createFile";
describe('create XML for output files', () => {
it('should create a PAX File', async () => {
jest
.spyOn(populateTemplateUtils, 'createSessionID')
.mockReturnValue('PAX123456');
const result = await createFile();
expect(result).toEqual({"SessionID": "PAX123456"});
});
});
It all started to work when I changed the:
module.exports = {
createSessionID,
};
to:
export const createSessionID = () => {

How to set up Jest with a setup file to run beforeEach across multiple test files

I have two Models which I am testing in individual files and in each file I am using beforeEach to clear the DB and seed the DB. I am also using afterEach to drop the DB and close the connection to MongoDB.
I am getting a MongoDB error for a duplicate key of my users so it seems that the beforeEach isn't running perfectly beforeEach test. If I run the files isolated then no issues. It is only when I run the whole test suite.
user.test.js
const mongoose = require('mongoose');
const request = require('supertest');
const app = require('../app');
const User = require('../models/user.model');
const userData = require('../db/data/userData');
// Valid User for Loggin In
// {
// username: 'Bcrypt User',
// password: 'YECm9jCO8b',
// };
beforeEach((done) => {
mongoose.connect(process.env.DB_URI, () => {
const seedDB = async () => {
await User.deleteMany({});
await User.insertMany(userData);
};
seedDB().then(() => {
done();
});
});
});
afterEach((done) => {
mongoose.connection.db.dropDatabase(() => {
mongoose.connection.close(() => done());
});
});
message.test.js
const mongoose = require('mongoose');
const request = require('supertest');
const app = require('../app');
const User = require('../models/user.model');
const userData = require('../db/data/userData');
const messageData = require('../db/data/messageData');
const Message = require('../models/message.model');
const validUserAccount = {
username: 'Bcrypt User',
password: 'YECm9jCO8b',
};
beforeEach((done) => {
mongoose.connect(process.env.DB_URI, () => {
const seedDB = async () => {
await User.deleteMany({});
await User.insertMany(userData);
await Message.deleteMany({});
await Message.insertMany(messageData);
};
seedDB().then(() => {
done();
});
});
});
afterEach((done) => {
mongoose.connection.db.dropDatabase(() => {
mongoose.connection.close(() => done());
});
});
So far I have tried using a jest.config.js and also tried add config to package.json but can't seem to figure out a global file to run before each
Have you checked setupFilesAfterEnv option https://jestjs.io/docs/configuration#setupfilesafterenv-array?

Trouble understanding Cloud Function Error

I am new to cloud funcations node.js and type script. I am running the below code and getting the error below and can't make sense of it after watch a ton of videos about promises and searching other questions.
any help would be appreciated.
Function returned undefined, expected Promise or value
exports.compReqUpdated = functions.firestore
.document('/compRequests/{id}')
.onUpdate((change, contex)=>{
const newData = change.after.data();
//const oldData = change.before.data();
const dbConst = admin.firestore();
const reqStatus:string = newData.requestStatus;
const compId:string = newData.compID;
const reqActive:boolean = newData.requestActive;
if (reqStatus == "CANCELED" && reqActive){
const query = dbConst.collection('compRequests').where('compID', '==', compId);
const batch = dbConst.batch();
query.get().then(querySnapshot => {
const docs = querySnapshot.docs;
for (const doc of docs) {
console.log(`Document found at path: ${doc.ref.path}`);
console.log(doc.id);
const docRef = dbConst.collection('compID').doc(doc.id);
batch.update(docRef, {requestStatus: 'CANCELED',requestActive: false});
};
return batch.commit()
})
.catch(result => {console.log(result)});
}else{
return
}
});
The firebase docs state that the callback passed to the onUpdate function should return PromiseLike or any value, but you aren't returning anything right now. If you change your code to something as follows I reckon it should work as expected:
exports.compReqUpdated = functions.firestore
.document('/compRequests/{id}')
.onUpdate((change, contex) => {
const newData = change.after.data();
//const oldData = change.before.data();
const dbConst = admin.firestore();
const reqStatus: string = newData.requestStatus;
const compId: string = newData.compID;
const reqActive: boolean = newData.requestActive;
if (reqStatus == "CANCELED" && reqActive) {
const query = dbConst.collection('compRequests').where('compID', '==', compId);
const batch = dbConst.batch();
return query.get().then(querySnapshot => {
const docs = querySnapshot.docs;
for (const doc of docs) {
console.log(`Document found at path: ${doc.ref.path}`);
console.log(doc.id);
const docRef = dbConst.collection('compID').doc(doc.id);
batch.update(docRef, { requestStatus: 'CANCELED', requestActive: false });
};
return batch.commit()
}).catch(result => { console.log(result) });
} else {
return false;
}
});

Trouble mocking a function in a module

I'm trying to create a simple test for a module. I got some firestore triggers in a module (see the module file down below). In the onDelete trigger I want to just test to see if the deleteColletion is called. To do that I need to mock out just the deleteCollection function. In my test (see onDelete should also delete the sub-collections trails and downloads in the test file) I mock the deleteCollection function and call the firestore trigger and checks if deleteCollection is called. This is the failing response I get from the test:
Error: expect(jest.fn()).toBeCalled()
Expected number of calls: >= 1
Received number of calls: 0
It seems like jest don't match one the function I mock. What am I doing wrong?
NB! I now that this test in it self is not a good test ;)
Test file
const functions = require('firebase-functions-test');
const admin = require('firebase-admin');
const triggers = require("../../data/protocol/triggers");
const {createEvent} = require("../test_utilities");
const testEnv = functions();
const mockUpdate = jest.fn();
mockUpdate.mockReturnValue(true);
jest.mock("firebase-admin", () => ({
initializeApp: jest.fn(),
firestore: () => ({
batch: jest.fn(() => ({commit: jest.fn()})),
collection: () => (
{
doc: () => ({update: mockUpdate}),
orderBy: () => ({
limit: jest.fn(() => ({
get: jest.fn(() => ({
size: 0,
docs: {
forEach: jest.fn(),
}
}))
}))
})
}
)
}),
})
);
jest.mock('../../data/protocol/triggers', () => ({
...(jest.requireActual('../../data/protocol/triggers')),
deleteCollection: jest.fn(() => [])
})
);
describe("Protocol trigger", () => {
let adminStub, triggersStub, api;
const context = {
params: {
protocolId: 0,
}
};
beforeAll(() => {
adminStub = jest.spyOn(admin, "initializeApp");
//triggersStub = jest.spyOn(triggers, 'deleteCollection');
api = require("../../index");
});
beforeEach(() => jest.clearAllMocks());
afterAll(() => {
adminStub.mockRestore();
//triggersStub.mockRestore();
testEnv.cleanup();
});
...
it('`onDelete` should also delete the sub-collections `trails` and `downloads`', async () =>
{
const onDeleteProtocol = testEnv.wrap(api.onDeleteProtocol);
const event = {id: 0};
await onDeleteProtocol(event, {});
expect(triggers.deleteCollection).toBeCalled();
expect(onDeleteProtocol(event, {})).toBe([]);
});
});
Module
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const {lastModifiedNeedsUpdate} = require("../utilities/event");
function deleteCollection(db, collectionPath, batchSize) {
return deleteQueryBatch(db, db.collection(collectionPath).orderBy('__name__').limit(batchSize), batchSize, []);
}
...
const deleteProtocol = () => functions.firestore
.document('protocols/{protocolId}')
.onDelete((event, context) => {
return deleteCollection(admin.firestore(), `protocols/${event.id}/trails`, 1);
});
module.exports = {
deleteProtocol,
createProtocol,
updateProtocol,
deleteCollection,
};
-- Frode
I resolved this by moving the helper functions (deleteCollection and deleteQueryBatch) into it's own module and mock that module.
--
Frode

How to use the beforeEach in node-tap?

Can someone provide an example on how to use the beforeEach? http://www.node-tap.org/api/
Ideally, an example of the promise version, but a callback version example would also be nice.
Here is a test I created which works fine:
'use strict';
const t = require('tap');
const tp = require('tapromise');
const app = require('../../../server/server');
const Team = app.models.Team;
t.test('crupdate', t => {
t = tp(t);
const existingId = '123';
const existingData = {externalId: existingId, botId: 'b123'};
const existingTeam = Team.create(existingData);
return existingTeam.then(() => {
stubCreate();
const newId = 'not 123'
const newData = {externalId: newId, whatever: 'value'};
const newResult = Team.crupdate({externalId: newId}, newData);
const existingResult = Team.crupdate({externalId: existingId}, existingData);
return Promise.all([
t.equal(newResult, newData, 'Creates new Team when the external ID is different'),
t.match(existingResult, existingTeam, 'Finds existing Team when the external ID exists')
]);
});
})
.then(() => {
process.exit();
})
.catch(t.threw);
function stubCreate() {
Team.create = data => Promise.resolve(data);
}
Before I do anything, I want to persist existingTeam. After it's saved, I want to stub Team.create. After these two things, I want to start actually testing. I think it would be cleaner if instead of using a Promise.all or perhaps duplicating the test code, I could use beforeEach.
How would I convert this to use beforeEach? Or what is an example of its usage?
Simple, just return promise from callback function
const t = require('tap');
const tp = require('tapromise');
const app = require('../../../server/server');
const Team = app.models.Team;
const existingId = '123';
const existingData = {
externalId: existingId,
botId: 'b123'
};
t.beforeEach(() => {
return Team.create(existingData).then(() => stubCreate());
});
t.test('crupdate', t => {
t = tp(t);
const newId = 'not 123'
const newData = {
externalId: newId,
whatever: 'value'
};
const newResult = Team.crupdate({
externalId: newId
}, newData);
const existingResult = Team.crupdate({
externalId: existingId
}, existingData);
return Promise.all([
t.equal(newResult, newData, 'Creates new Team when the external ID is different'),
t.match(existingResult, existingTeam, 'Finds existing Team when the external ID exists')
]);
}).then(() => {
process.exit();
}).catch(t.threw);
function stubCreate() {
Team.create = data => Promise.resolve(data);
}

Categories

Resources