Testing blackboxed APIs in JS/Vue with jest - javascript

Well, I have an interesting case...
I'm trying to test a Vuex action that uses the Okta SDK to log in a User. Nothing special there. But at the testing level, I'm stuck trying to catch the idea. I mean, I just want to know if the functions have been called, not anything else, here the code:
The service:
const signIn = async ({ username, password }) => {
const response = await authClient.signIn({ username, password })
if (response.status === 'SUCCESS') {
const tokens = await authClient.token.getWithoutPrompt({
responseType: 'id_token',
sessionToken: response.sessionToken
})
authClient.tokenManager.add('idToken', tokens.tokens.idToken)
return response
}
}
The action:
async logIn({ commit }, { username, password }) {
const loginData = await signIn({ username, password })
commit(mutationTypes.setUserData, {
...loginData.user.profile
})
}
The test:
const authClient = {
signIn() {
return new Promise(resolve => resolve())
},
token: {
getWithoutPrompt() {
return new Promise(resolve => resolve())
}
},
tokenManager: {
add() {
return new Promise(resolve => resolve())
}
}
}
jest.mock('authClient', () => authClient)
it('Auth -> actions.signIn', async () => {
const commit = jest.fn()
const username = 'user'
const password = 'pass'
await actions.signIn({ commit }, { username, password })
expect(authClient.signIn).toHaveBeenCalled()
expect(authClient.token.getWithoutPrompt).toHaveBeenCalled()
expect(authClient.tokenManager.add).toHaveBeenCalled()
expect(commit).toHaveBeenCalled()
})

Related

AWS amplify currentAuthenticatedUser shows unauthenticated status even after doing federatedSignIn

export const requestFederatedSignOn = async (type) => {
return new Promise((resolve, reject) => {
Auth.federatedSignIn({ provider: type })
.then(() => {
console.log("you have signed in");
authenticatedUserCred()
.then((userData) => {
console.log("you have been authenticated");
resolve(userData);
})
.catch((err) => console.error(err));
})
.catch((error) => reject(error));
});
};
export const authenticatedUserCred = async () => {
return new Promise((resolve, reject) => {
Auth.currentAuthenticatedUser()
.then((user) => {
const userAttributes = user.signInUserSession.idToken.payload;
const tokens = {
accessToken: user.signInUserSession.accessToken.jwtToken,
idToken: user.signInUserSession.idToken.jwtToken,
refreshToken: user.signInUserSession.refreshToken.token,
};
const userData = {
tokens: tokens,
userProfile: userAttributes,
};
resolve(userData);
})
.catch((err) => reject(err));
});
};
Basically, I am able to sign in using federatedSignIn and console log "you have signed in" is outputted but then authenticatedUserCred function for some reason is rejecting the promise. Please someone help me out!!!

Firebase cloud function to update a record and send a notification

Evening All
Having a few problems performing two actions when a document is created
the below worked until i added the last "then" in the createDocument function where i attempt to send a notification to inform the use via fcm.
exports.createRequest = functions.firestore
.document('requests/{requestId}')
.onCreate((snap, context) => {
var email = snap.data().requestedFromEmail;
checkUserInFirebase(email)
.then((user) => {
//get user profile
return getUserProfile(user.user.uid);
})
.then(userProfile => {
return snap.ref.set({ requestedFromName: userProfile.data().fullName, requestedFromId: userProfile.id }, {merge:true});
})
.then(value=> {
return sendNotification(snap.data().requestedFromId, snap.data().requestedByName);
})
.catch(error => {return error;})
}
)
can anyone see where im going wrong, all the examples im finding send the fcm explicitly from using a exports. Ideally id like to pass the userProfile object through to the send notification function but um not sure how to do that and still set the changes to the document. Full code is below
async function checkUserInFirebase(email) {
return new Promise((resolve) => {
admin.auth().getUserByEmail(email)
.then((user) => {
return resolve({ isError: false, doesExist: true, user });
})
.catch((err) => {
return resolve({ isError: true, err });
});
});
}
async function getUserProfile(uid) {
return admin.firestore()
.collection("users")
.doc(uid)
.get();
}
async function sendNotification(uid, requestedByName) {
const querySnapshot = await db
.collection('users')
.doc(uid)
.collection('tokens')
.get();
const tokens = querySnapshot.docs.map(snap => snap.token);
console.info(tokens);
const payload = {
notification: {
title: 'New Request!',
body: `You received a new request from ${requestedByName}`,
icon: 'your-icon-url',
click_action: 'FLUTTER_NOTIFICATION_CLICK'
}
};
return fcm.sendToDevice(tokens, payload);
}
exports.createRequest = functions.firestore
.document('requests/{requestId}')
.onCreate((snap, context) => {
var email = snap.data().requestedFromEmail;
checkUserInFirebase(email)
.then((user) => {
//get user profile
return getUserProfile(user.user.uid);
})
.then(userProfile => {
return snap.ref.set({ requestedFromName: userProfile.data().fullName, requestedFromId: userProfile.id }, {merge:true});
})
.then(value=> {
return sendNotification(snap.data().requestedFromId, snap.data().requestedByName);
})
.catch(error => {return error;})
}
)
Your funciton needs to return a promise that resolves when all the async work is complete. Right now it returns nothing, which means that Cloud Functions might terminate it up before the work is done. You should return the promise chain:
return checkUserInFirebase(email)
.then((user) => {
//get user profile
return getUserProfile(user.user.uid);
})
.then(userProfile => {
return snap.ref.set({ requestedFromName: userProfile.data().fullName, requestedFromId: userProfile.id }, {merge:true});
})
.then(value=> {
return sendNotification(snap.data().requestedFromId, snap.data().requestedByName);
})
.catch(error => {return error;})
}
Note the return at the start of the whole thing.
See the documentation for more information.

how to stubbing a class with functions using Sinon.js

how do I simulate the creation of the Cognito class called CognitoUser to perform a unit test?
File adapter
const updatePasswordCognito = async ({ userName, password, newPassword }) => {
const authenticationDetails = new awsCognito.AuthenticationDetails({
Username: userName,
Password: password,
});
const cognitoUser = await createCognitoUser({ userName });
return new Promise((resolve, reject) => {
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess() {
cognitoUser.changePassword(password, newPassword, (err, result) => {
if (err) {
reject(err);
}
resolve(result);
});
},
onFailure(err) {
reject(err);
},
});
});
};
File spec.js
'use strict'
const { expect } = require('chai');
const sinon = require('sinon');
const jwt = require('jsonwebtoken');
const iamWrapper = require('../../../../../anypath');
let mockDependencies;
const config = {
userPoolId: 'test',
clientId: 'test',
};
class CognitoUser {
constructor(text) {
this.text = text;
}
authenticateUser() {
return { code: 'test' };
}
}
describe('Unit tests for iam adapter', () => {
beforeEach('create mock', () => {
mockDependencies = {
config,
jwt,
awsCognito: {
CognitoUser,
},
};
});
context('update password cognito', () => {
it('password success', async () => {
const userName = '12345678910';
const password = '102030';
const newPassword = '102040';
const result = await iamWrapper(mockDependencies).updatePasswordCognito({ userName, password, newPassword });
expect(result).to.be.an('object');
expect(result).to.have.property('corporateId');
});
it('should return error', async () => {
const result = await iamWrapper(mockDependencies).updatePasswordCognito(mockDependencies);
expect(result).to.be.an('object');
expect(result).to.have.property('statusCode');
});
});
});
Doing so when the awsCognito.CognitoUser() object is instantiated does not contain the authenticateUser function and returns undefined.
TypeError: Cannot read property 'authenticateUser' of undefined

authentication with react + localstorage

I write following code to authenticate with react but have one problem. after user logged in, set token in local storage and redirect the user to the dashboard and in this page check token from local storage and if not exist redirect the user to the login page and my problem is here!! when come in dashboard page can't get token because return null to me but if refresh the page return token!!
In fact, I'm can't get token after set that.
My codes is:
export const userLogin = (data) => {
return (dispatch) => {
return axios.post(API.USER_LOGIN,data).then((response) => {
dispatch(loginSuccess());
setToken(response.data.token);
dispatch(setUserToken(response.data.token))
}).then(() => {
dispatch(fetchUser());
}).catch((error) => {
dispatch(setFormErrors({errors: error.response.data}));
})
}
};
export const fetchUser = () => {
return (dispatch, getState) => {
dispatch(authLoading(true));
const { userToken } = getState().auth;
return axios.post(API.CURRENT_USER, null, {
headers: { authorization: `Bearer ${userToken}` },
}).then((response) => {
const { id, name, username, email } = response.data.user;
dispatch(setCurrentUser({current_user: { id, name, username, email }}));
dispatch(authLoading(false));
}).catch((error) => {
if(error.response.status) {
dispatch(logout());
}
});
};
};
export const checkToken = () => {
return async (dispatch, getState) => {
const token = await getToken();
const { userToken } = getState().auth;
if (token || userToken) {
dispatch(loginSuccess());
dispatch(fetchUser());
}
};
}
export const setToken = token => localStorage.setItem('token', token);
export const getToken = () => localStorage.getItem('token');
export const clearToken = () => localStorage.removeItem('token');

ReactJS/Enzyme: How to test a submit function, which calls a graphQL mutation via prop

I'm trying to write a unit test (enzyme/jest) for this function:
_onSubmit = (event) => {
event.preventDefault()
const { username, password } = this.state
this.props.createUserMutation({
variables: { username, password }
}).then(response => {
const token = response.data.createUser.token
if (token) {
this.setState({ token })
}
}).catch(error => {
console.warn(error)
})
}
But I don't know how to handle this.props.createUserMutation() for proper testing.
Of course my current attempt throws a TypeError: _this.props.createUserMutation is not a function error
Unit test
it('_onSubmit() should submit form and reset state object', () => {
const wrapper = shallow(<CreateAccount />)
wrapper.setState({ username: 'Username', password: 'Password' })
wrapper.find(Form).simulate('submit', {
preventDefault: () => {}
})
const state = wrapper.instance().state
expect(state).toEqual({ token: 'test' })
})
Updated unit test
it('_onSubmit() should submit data and get result dataset', () => {
const createUserMutation = () => {
return Promise.resolve({
data: {
createUser: { token: 'token' }
}
})
}
const wrapper = shallow(<CreateAccount createUserMutation={createUserMutation} />)
wrapper.find(Form).simulate('submit', {
preventDefault: () => {}
})
const state = wrapper.instance().state
expect(state).toEqual({ token: 'token' })
})
console.log(state) doesn't give me a token.
I'm assuming createUserMutation is injected to props by the compose function.
So, what you can do is import the non-default component in your test instead of the 'connected' one.
import {CreateAccount} from './CreateAcount';
Then, you can pass createUserMutation as props directly to the component when mounting it.
it('_onSubmit() should submit form and reset state object', () => {
const createUserMutation = () => {
return Promise.resolve({
username: '',
password: ''
});
};
const wrapper = shallow(<CreateAccount createUserMutation={createUserMutation} />)
...
});
Given that createUserMutation seems to be a function that returns a promise, you can assign it Promise.resolve() in order to execute the then function which changes the state. Which is basically what you are testing.
Moreover, you're not able to test the token change in the state because the verification in the test is being run before the state changing in the method _onSubmit. (You can check this by putting console statements in both places and see which executes first).
What you need to do is return the promise in your _onSubmit method.
_onSubmit = (event) => {
event.preventDefault()
const { username, password } = this.state
return this.props.createUserMutation({ // returning the promise
variables: { username, password }
}).then(response => {
const token = response.data.createUser.token
if (token) {
this.setState({ token })
}
}).catch(error => {
console.warn(error)
})
}
Then, in your unit test you need to simulate the submit event by calling the function directly from props in order to put your verifications inside a then function. By doing this, you would run your expect statement after the state has been changed.
it('_onSubmit() should submit data and get result dataset', () => {
const createUserMutation = () => {
return Promise.resolve({
data: {
createUser: { token: 'token' }
}
})
}
const wrapper = shallow(<CreateAccount createUserMutation={createUserMutation} />)
wrapper.find(Form).props().onSubmit({
preventDefault: () => {}
}).then(() =< {
const state = wrapper.instance().state
expect(state).toEqual({ token: 'token' })
})
})

Categories

Resources