How to change const values while testing with Jest - node.js

Hello I'm working on writing a test for my node.js API and I'm running into an issue. I'm validating if an email exists inside of my code using "const = emailCount". If it does exists it returns an error JSON. If it does NOT exist it returns a success JSON. However I'm not sure how to mock the internal constant I declared in my code.
Here is the code:
async function registerUser(req, res) {
// Request
const email = req.body.email;
const password = req.body.password;
const firstName = req.body.firstName;
const lastName = req.body.lastName;
const inviteCode = req.body.inviteCode;
let errMessage = [];
if (!firstName) {
errMessage.push("first Name Required")
}
if (!lastName) {
errMessage.push("last Name Required")
}
if (!inviteCode) {
errMessage.push("inviteCode Required")
}
if (!email) {
errMessage.push("email Required")
}
if (!password) {
errMessage.push("password Required")
}
if (errMessage.length > 0) {
res.json({ code: "422", message: errMessage })
}
const accessToken = jwt.sign({
email: email,
firstName: firstName,
lastName: lastName
}, config.jwtSecret);
const emailCount = await db.doesEmailExists(email)
if (emailCount.doesEmailExists > 0) {
res.json({ Errors: "Account already exists" })
} else {
db.createUser({
username: email,
hashedPassword: password,
firstName: firstName,
lastName: lastName,
}).then(data => {
res.json({
id: data.insertId,
firstName: firstName,
lastName: lastName,
token: accessToken,
role: 'user'
})
}).catch(err => res.json({ Error: err }))
}
}
Here is my test code
test('POST /user/register', async () => {
//use super test to send post method with json payload of newUser
const res = await agent.post('/user/register').send(newUser);
expect(res.statusCode).toEqual(200)
expect(res.body).toHaveProperty('Errors') || expect(res.body).toHaveProperty('token');
})
Ultimately I want to change the value of emailCount within my test if possible to test for different responses if there is a user and if there is NOT a user.

You should not mock your code, but rather your dependencies and db is exactly that.
For example you can write your test scenario like this:
const db = require('./path/to/db.js');
// auto-create mock
jest.mock('./path/to/db.js')
describe('POST /user/register', () => {
describe('when email Exists'), () => {
// defining the "res" object here
// will allow you to execute the request one
// and separate the expectations in different
// test cases, which will provide better visibility
// on what exactly have failed (in the future)
let res;
beforeAll(async () => {
db.doesEmailExists.mockResolvedValue({
doesEmailExists: 789
});
res = await agent.post('/user/register').send(newUser);
});
it('should probably return something more than 200', () => {
expect(res.statusCode).toBeGreaterThanOrEqual(200)
});
it('should return Error in response Body', () => {
expect(res.body).toHaveProperty('Errors')
});
});
describe('when email DOES NOT Exists'), () => {
let res;
beforeAll(async () => {
db.doesEmailExists.mockResolvedValue({
doesEmailExists: 0
});
res = await agent.post('/user/register').send(newUser);
});
it('should probably return statusCode 200', () => {
expect(res.statusCode).toEqual(200)
});
it('should return token', () => {
expect(res.body).toHaveProperty('token')
});
});
});
Note: you'll also need to mock the return value of db.createUser as the auto-mock will generate a jest.fn() which returns undefined

Related

Service Oriented Architecture in node.js

I am building a Node.js project which has an advanced folder structure for future purposes.
user.register controller:
exports.register = async (req, res) => {
try {
var isValidated = await userService.validateInDatabase(req);
if (!isValidated)
return res
.status(409)
.json({ error: "Phone number or email is already registered" });
var user = await userService.create(req);
return user
} catch (e) {
console.trace(e);
return res.status(400).json({ message: e.message });
}
};
The services file code:
exports.create = async (user) => {
const hashedPassword = passwordHash.generate(user.password);
let new_user = new User({
phoneNumber,
email,
password: hashedPassword,
});
const payload = {
id: new_user._id,
};
let token = jwt.sign(payload, keys.JWToken, { expiresIn: 31556926 });
const userData = await new_user.save();
return userData;
};
exports.validateInDatabase = async (req) => {
let check_user = await User.findOne({
$or: [{ email: req.body.email }, { phoneNumber: req.body.phoneNumber }],
});
if (check_user) return false;
return true;
};
Now, whenever I send the request from the postman it says invalid password Why is that?

How to properly integrate Mongoose relational database

can you help me look at this code to find out where I am making it wrong ?
I have my mongoose schema that am referencing child. What I want is to create a user and embed his payment details as a reference. my current result now, allows me to create anew user and the problems comes from when I want to update the user schema with payment details. If I send a request in postman, the request keep on loading until I cancel it. and what happend is if I try to get the userInfo, I will see that the payment_Id is reference in the schema but I can called .populate method on the parent schema. below is the....
export const expoSignUp = async (req, res) => {
const { firstname, lastname, middlename, username, email, phoneNo , password } = req.body
try {
const userReg = await Expo.findOne({ username })
if (userReg) {
res.status(403).json({ message: "Username already taken"})
} else {
const encryptPass = CryptoJS.AES.encrypt(password, 'secret key 123').toString();
const userData = await Expo.create({
firstname, lastname, middlename, username, email, phoneNo, password: encryptPass
});
const result = jwt.sign({firstname, lastname, middlename, username, email, phoneNo},"secrete", {expiresIn:"24"})
res.status(200).json({ userData, result })
}
} catch (err) {
res.status(500).json({ message: err.message });
}
}
export const expoSignIn = async (req, res) => {
const { password, email, username } = req.body;
try {
const userLogin = await Expo.findOne({ username })
if (!userLogin) {
res.status(401).json({ message: "username is incorrect"})
} else {
const decryptPass = CryptoJS.AES.decrypt(userLogin.password, 'secret key 123');
var originalPass = decryptPass.toString(CryptoJS.enc.Utf8);
if (password !== originalPass) {
res.status(403).json({ message: "Login credentials is incorrect" })
} else {
const result = jwt.sign({ username, email, _id: userLogin._id }, "secrete", { expiresIn: "24" });
const {password, ...others} = userLogin._doc
res.status(200).json({ others, result })
}
}
} catch (error) {
res.status(500).json({message: error.message})
}
}
export const getAllExpoUsers = async (req, res) => {
try {
const getUsers = await Expo.find()
res.status(200).json(getUsers)
} catch (err) {
res.status(err)
}
}
export const paymentInfo = async (req, res) => {
const {
userId,
tx_ref,
amount,
currency,
payment_options,
customer: {
email,
phonenumber,
fullname,
},
customizations: {
title,
description,
logo
}
} = req.body;
try {
const userPaymentInfo = await Payment.create({
tx_ref,
amount,
currency,
payment_options,
customer: {
email,
phonenumber,
fullname,
},
customizations: {
title,
description,
logo
}
})
const newPaymentInfo = await Expo.findById({ _id: userPayment })
res.status(newPaymentInfo)
} catch (error) {
res.status(500).json({message:error.message})
}
}

How to add Auth Custom claims

Firebase function
I am trying to set my user role to admin using a callable function:
export const addAdminRole = functions.https.onCall(async (data, context) => {
admin.auth().setCustomUserClaims(data.uid, {
admin: true,
seller: false,
});
});
Cient
And here is how I am calling the function on the client:
const register = (email: string, password: string) => {
createUserWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
// Signed in
const user = userCredential.user;
const addAdminRole = httpsCallable(functions, "addAdminRole");
addAdminRole({ email: user.email, uid: user.uid })
.then((result) => {
console.log(result);
})
.catch((error) => console.log(error));
history.push(`/home/${user.uid}`);
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
// ..
});
};
The user is created but, my Admin role is not added
The problem may come from the fact that you don't correctly handle the promise returned by the setCustomUserClaims() method in your Cloud Function and therefore the Cloud Function platform may clean up you CF before it reaches its terminating state. Correctly managing the life-cycle of your Cloud Function is key, as explained here in the doc.
The following should solve the problem:
export const addAdminRole = functions.https.onCall(async (data, context) => {
try {
await admin.auth().setCustomUserClaims(data.uid, {
admin: true,
seller: false,
});
return {result: "Success"}
} catch (error) {
// See https://firebase.google.com/docs/functions/callable#handle_errors
}
});
In addition, you can refactor your front-end code as follows to correctly chain the promises:
const register = (email: string, password: string) => {
createUserWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
// Signed in
const user = userCredential.user;
const addAdminRole = httpsCallable(functions, "addAdminRole");
return addAdminRole({ email: user.email, uid: user.uid });
})
.then((result) => {
console.log(result);
history.push(`/home/${user.uid}`);
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
// ..
});
};

TypeError: newUser.find is not a function

I am very new to the MERN stack and I would like some help figuring out this error. I'm trying to check if an email is already in the database upon creating a new user. Can anyone tell me why I am getting this error?
The model and scheme
//schema
const Schema = mongoose.Schema;
const VerificationSchema = new Schema({
FullName: String,
email: String,
password: String,
date: Date,
isVerified: Boolean,
});
// Model
const User = mongoose.model("Users", VerificationSchema);
module.exports = User;
The Api
const express = require("express");
const router = express.Router();
const User = require("../Models/User");
router.get("/VerifyEmail", (req, res) => {
console.log("Body:", req.body);
const data = req.body;
const newUser = new User();
newUser.find({ email: data.email }, function (err, newUser) {
if (err) console.log(err);
if (newUser) {
console.log("ErrorMessage: This email already exists");
} else {
console.log("This email is valid");
}
});
res.json({
msg: "We received your data!!!",
});
});
module.exports = router;
The api caller using axios
const isEmailValid = (value) => {
const info = {
email: value,
};
axios({
url: "http://localhost:3001/api/VerifyEmail",
method: "get",
data: info,
})
.then(() => {
console.log("Data has been sent");
console.log(info);
})
.catch(() => {
console.log("Internal server error");
});
};
if you have body in your request, change the type of request to POST...
after that for use find don't need to create a instance of model, use find with Model
router.get("/VerifyEmail", (req, res) => {
console.log("Body:", req.body);
const data = req.body;
User.find({ email: data.email }, function (err, newUser) {
if (err) console.log(err);
if (newUser) {
console.log("ErrorMessage: This email already exists");
} else {
console.log("This email is valid");
}
});
res.json({
msg: "We received your data!!!",
});
});
I prefer to use async/await and don't use Uppercase world for routing check the article: like this
router.post("/verify-email", async (req, res) => {
try {
let { email } = req.body;
let newUser = await User.findOne({ email });
if (newUser) {
console.log("ErrorMessage: This email already exists");
} else {
console.log("This email is valid");
}
} catch (error) {
res.json({
msg: "somthing went wrong",
});
}
res.json({
msg: "We received your data!!!",
});
});
The proper way to query a Model is like so:
const User = mongoose.model('Users');
User.find({<query>}, function (err, newUser) {...
So you need to get the model into a variable (in this case User) and then run the find function directly against it, as opposed to running it against an object you instantiate from it. So this is incorrect:
const newUser = new User();
newUser.find(...
So assuming all your files and modules are linked up correctly, this should work:
const User = require("../Models/User");
User.find({<query>}, function (err, newUser) {...
The problem wasn't actually the mongoose function but I needed to parse the object being sent.
let { email } = JSON.parse(req.body);
Before parsing the object looked like {"email" : "something#gmail.com"}
and after parsing the object looked like {email: 'something#gmail.com'}
I also changed the request from 'get' to 'post' and instead of creating a new instance of the model I simply used User.find() instead of newUser.find()

How to fix this "Cannot read property of undefined" when testing?

I'm trying to run a test using Jest for this Nodejs API. Although everything works when I run the app, it's failing and returning the error
TypeError: Cannot read property 'store' of undefined
The test code:
const request = require('supertest')
const server = require('../../server')
const { User } = require('../../app/models/User')
describe('User', () => {
test('should create user', async () => {
const user = await User.store({
name: 'Marcelo',
email: 'marcelo#vuttr.com',
password: '123456'
})
const response = await request(server)
.post('/user')
.send({
name: user.name,
email: user.email,
password: user.password
})
expect(response.status).toBe(200)
})
})
The controller:
const User = require('../models/User')
class UserController {
async store (req, res) {
const { email } = req.body
if (await User.findOne({ email })) {
return res.status(400).json({ error: 'User already exists' })
}
const user = await User.create(req.body)
return res.json(user)
}
}
module.exports = new UserController()
For more details, I share this project in github.
try this code
the Test Code
const request = require('supertest')
const server = require('../../server')
const { User } = require('../../app/models/User')
const { UserController } = require('../../UserController')
describe('User', () => {
test('should create user', async () => {
const user = await UserController({
name: 'Marcelo',
email: 'marcelo#vuttr.com',
password: '123456'
})
const response = await request(server)
.post('/user')
.send({
name: user.name,
email: user.email,
password: user.password
})
expect(response.status).toBe(200)
})
})
The controller:
const User = require('../models/User')
const UserController = async(req, res) => {
const { email } = req.body
if (await User.findOne({ email })) {
return res.status(400).json({ error: 'User already exists' })
}
const user = await User.create(req.body);
return res.status(201).json(user);
}
module.exports = UserController;

Resources