UNPKG

amazon-cognito-identity-js

Version:

Amazon Cognito Identity Provider JavaScript SDK

1,660 lines (1,375 loc) • 50.9 kB
import CognitoUser from '../src/CognitoUser'; import CognitoUserPool from '../src/CognitoUserPool'; import AuthenticationDetails from '../src/AuthenticationDetails'; import AuthenticationHelper from '../src/AuthenticationHelper'; import Client from '../src/Client'; import CognitoIdToken from '../src/CognitoIdToken'; import CognitoAccessToken from '../src/CognitoAccessToken'; import CognitoRefreshToken from '../src/CognitoRefreshToken'; import { callback, authHelperMock, netRequestMockSuccess, } from '../__mocks__/mocks'; import { clientId, userPoolId, authDetailData, vCognitoUserSession, deviceName, totpCode, ivCognitoUserSession, networkError, genHashDevices, getSalt, getVerifiers, passwordErr, vRefreshToken, ivRefreshToken, } from './constants'; import { CognitoUserSession } from 'amazon-cognito-identity-js'; const minimalData = { UserPoolId: userPoolId, ClientId: clientId }; const cognitoUserPool = new CognitoUserPool(minimalData); const userDefaults = { Username: 'username', Pool: cognitoUserPool, }; describe('CognitoUser constructor', () => { test('constructor throws error when bad (or no) data is passed', () => { const errorMsg = 'Username and Pool information are required.'; // no data at all expect(() => { new CognitoUser({}); }).toThrow(errorMsg); // missing Pool expect(() => { new CognitoUser({ Username: 'username', Pool: null, }); }).toThrow(errorMsg); // missing Username expect(() => { new CognitoUser({ Username: null, Pool: userPoolId, }); }).toThrow(errorMsg); }); test('happy case constructor', () => { const spyon = jest.spyOn(cognitoUserPool, 'getClientId'); expect(() => { new CognitoUser({ ...userDefaults }); }).not.toThrowError(); expect(spyon).toBeCalled(); }); }); describe('getters and setters', () => { const user = new CognitoUser({ ...userDefaults }); const refreshTokenKey = 'CognitoIdentityServiceProvider.3pu8tnp684l4lmlfoth25ojmd2.username.refreshToken'; describe('setSignInUserSession()', () => { const cacheSpy = jest.spyOn(CognitoUser.prototype, 'clearCachedUserData'); const cacheTokenSpy = jest.spyOn(CognitoUser.prototype, 'cacheTokens'); test('happy path should have an empty cache, after user session is set, cache tokens', () => { // precondition expect(user.getSignInUserSession()).toEqual(null); expect(user.storage.getItem('')).toEqual(null); user.setSignInUserSession(vCognitoUserSession); expect(cacheSpy).toBeCalled(); expect(cacheTokenSpy).toBeCalled(); // post-condition expect(user.getSignInUserSession()).toEqual(vCognitoUserSession); expect(user.storage.getItem(refreshTokenKey)).toEqual( vRefreshToken.token ); }); test('checking that tokens are cleared properly', () => { // precondition expect(user.getSignInUserSession()).toEqual(vCognitoUserSession); user.setSignInUserSession(ivCognitoUserSession); expect(cacheSpy).toBeCalled(); expect(cacheTokenSpy).toBeCalled(); // post-condition expect(user.getSignInUserSession()).toEqual(ivCognitoUserSession); expect(user.storage.getItem(refreshTokenKey)).toEqual( ivRefreshToken.token ); }); }); test('getUsername()', () => { expect(user.getUsername()).toEqual(user.username); }); test('get and set authenticationFlowType', () => { // initial state expect(user.getAuthenticationFlowType()).toEqual('USER_SRP_AUTH'); // setting explicitly user.setAuthenticationFlowType('TEST_FLOW_TYPE'); // getter after set explicitly expect(user.getAuthenticationFlowType()).toEqual('TEST_FLOW_TYPE'); }); }); describe('initiateAuth()', () => { let user; beforeEach(() => { user = new CognitoUser({ ...userDefaults }); }); afterAll(() => { jest.restoreAllMocks(); }); afterEach(() => { callback.onFailure.mockClear(); callback.onSuccess.mockClear(); callback.customChallenge.mockClear(); }); test('Client request called once and throws an error', async () => { netRequestMockSuccess(false); const authDetails = new AuthenticationDetails(authDetailData); user.initiateAuth(authDetails, callback); expect(callback.onFailure.mock.calls.length).toBe(1); }); test('Client request called once with challenge name and params', async () => { netRequestMockSuccess(true, { ChallengeName: 'CUSTOM_CHALLENGE', Session: vCognitoUserSession, ChallengeParameters: 'Custom challenge params', }); const authDetails = new AuthenticationDetails(authDetailData); user.initiateAuth(authDetails, callback); expect(user.Session).toMatchObject(vCognitoUserSession); expect(callback.customChallenge.mock.calls.length).toBe(1); expect(callback.customChallenge).toBeCalledWith('Custom challenge params'); }); test('Client request sets signInUserSession and is successful', async () => { netRequestMockSuccess(true, { AuthenticationResult: 'great success' }); const getCognitoUserSessionSpy = jest.spyOn(user, 'getCognitoUserSession'); const cacheTokensSpy = jest.spyOn(user, 'cacheTokens'); const authDetails = new AuthenticationDetails(authDetailData); user.initiateAuth(authDetails, callback); expect(getCognitoUserSessionSpy).toBeCalledWith('great success'); expect(cacheTokensSpy).toBeCalled(); expect(callback.onSuccess.mock.calls.length).toBe(1); }); }); describe('authenticateUser()', () => { afterEach(() => { callback.onFailure.mockClear(); callback.onSuccess.mockClear(); callback.customChallenge.mockClear(); jest.restoreAllMocks(); }); const user = new CognitoUser({ ...userDefaults }); const authDetails = new AuthenticationDetails(authDetailData); test('USER_PASSWORD_AUTH flow type', () => { const spyon = jest.spyOn(user, 'authenticateUserPlainUsernamePassword'); user.setAuthenticationFlowType('USER_PASSWORD_AUTH'); user.authenticateUser(authDetails, callback); expect(spyon).toHaveBeenCalledWith(authDetails, callback); }); test('USER_SRP_AUTH and CUSTOM_AUTH flow types', () => { const spyon = jest.spyOn(user, 'authenticateUserDefaultAuth'); user.setAuthenticationFlowType('USER_SRP_AUTH'); user.authenticateUser(authDetails, callback); expect(spyon).toHaveBeenCalledWith(authDetails, callback); user.setAuthenticationFlowType('CUSTOM_AUTH'); user.authenticateUser(authDetails, callback); expect(spyon).toHaveBeenCalledWith(authDetails, callback); }); test('throws error for invalid Authentication flow type', () => { user.setAuthenticationFlowType('WRONG_AUTH_FLOW_TYPE'); user.authenticateUser(authDetails, callback); expect(callback.onFailure.mock.calls.length).toBe(1); }); }); describe('authenticateUserDefaultAuth()', () => { const user = new CognitoUser({ ...userDefaults }); const authDetails = new AuthenticationDetails(authDetailData); afterEach(() => { jest.restoreAllMocks(); callback.onFailure.mockClear(); callback.customChallenge.mockClear(); }); afterAll(() => { jest.clearAllMocks(); }); test('Happy case default initialization process', () => { expect(() => { user.authenticateUserDefaultAuth(authDetails, callback); }).not.toThrowError(); }); test('errOnAValue fails gracefully', () => { jest .spyOn(AuthenticationHelper.prototype, 'getLargeAValue') .mockImplementation(cb => cb('test error', 12345)); user.authenticateUserDefaultAuth(authDetails, callback); expect(callback.onFailure.mock.calls.length).toBe(1); }); test('Client request fails gracefully', () => { netRequestMockSuccess(false); user.authenticateUserDefaultAuth(authDetails, callback); expect(callback.onFailure.mock.calls.length).toBe(1); }); }); describe('authenticateUserPlainUsernamePassword()', () => { const user = new CognitoUser({ ...userDefaults }); afterEach(() => { jest.restoreAllMocks(); callback.onFailure.mockClear(); }); test('Missing password throws an error', () => { const authDetails = new AuthenticationDetails({ Username: 'user@amzn.com', Password: undefined, }); user.authenticateUserPlainUsernamePassword(authDetails, callback); expect(callback.onFailure).toBeCalledWith( new Error('PASSWORD parameter is required') ); }); test('Client request fails gracefully', () => { netRequestMockSuccess(false); const authDetails = new AuthenticationDetails(authDetailData); user.authenticateUserPlainUsernamePassword(authDetails, callback); expect(callback.onFailure.mock.calls.length).toBe(1); expect(callback.onFailure).toBeCalledWith(networkError); }); test('Authenticate user happy case', () => { const userSpy = jest.spyOn(user, 'getCachedDeviceKeyAndPassword'); const userSpy2 = jest.spyOn(user, 'getUserContextData'); userSpy2.mockReturnValue(true); const userSpy3 = jest.spyOn(user, 'authenticateUserInternal'); userSpy3.mockReturnValue('test return value'); netRequestMockSuccess(true, 'test auth result'); const authDetails = new AuthenticationDetails(authDetailData); user.authenticateUserPlainUsernamePassword(authDetails, callback); expect(userSpy).toBeCalled(); expect(userSpy3).toBeCalledWith( 'test auth result', userSpy3.mock.calls[0][1], callback ); expect(userSpy3.mock.results[0].value).toBe('test return value'); }); }); describe('authenticateUserInternal()', () => { const user = new CognitoUser({ ...userDefaults }); // same approach as used in CognitoUser.js const authHelper = new AuthenticationHelper(user.pool.getUserPoolName()); const authData = Object.assign(vCognitoUserSession, { ChallengeParameters: { userAttributes: '[]', requiredAttributes: '[]', }, AuthenticationResult: { NewDeviceMetadata: { DeviceGroupKey: 'abc123', DeviceKey: '123abc', }, }, Session: vCognitoUserSession, }); afterEach(() => { jest.restoreAllMocks(); jest.clearAllMocks(); }); test.each([ ['SMS_MFA', callback.mfaRequired], ['SELECT_MFA_TYPE', callback.selectMFAType], ['MFA_SETUP', callback.mfaSetup], ['SOFTWARE_TOKEN_MFA', callback.totpRequired], ['CUSTOM_CHALLENGE', callback.customChallenge], ])( '%s challenge sets user session and calls the corresponding cb', (challengeName, cbMethod) => { Object.assign(authData, { ChallengeName: challengeName }); user.authenticateUserInternal(authData, authHelper, callback); expect(user.Session).toMatchObject(vCognitoUserSession); if (challengeName === 'CUSTOM_CHALLENGE') { // this cb signature only expects one arg expect(cbMethod).toHaveBeenCalledWith(authData.ChallengeParameters); } else { // the rest expect two args expect(cbMethod).toHaveBeenCalledWith( challengeName, authData.ChallengeParameters ); } } ); test('user and required attributes get parsed and call newPasswordRequired', () => { Object.assign(authData, { ChallengeName: 'NEW_PASSWORD_REQUIRED' }); expect(user.Session).toMatchObject(vCognitoUserSession); const spyon = jest.spyOn( authHelper, 'getNewPasswordRequiredChallengeUserAttributePrefix' ); user.authenticateUserInternal(authData, authHelper, callback); expect(spyon).toHaveBeenCalledTimes(1); expect(callback.newPasswordRequired).toHaveBeenCalledTimes(1); callback.newPasswordRequired.mockClear(); }); test('DEVICE_SRP_AUTH calls getDeviceResponse and sends session', () => { const clientSpy = jest .spyOn(Client.prototype, 'request') .mockImplementation((...args) => {}); const authDataGetDeviceResponse = { ...authData, ChallengeName: 'DEVICE_SRP_AUTH', Session: 'abcd', }; const spyon = jest.spyOn(user, 'getDeviceResponse'); user.authenticateUserInternal( authDataGetDeviceResponse, authHelper, callback ); expect(clientSpy.mock.calls[0][1]).toMatchObject({ Session: 'abcd' }); expect(spyon).toHaveBeenCalledTimes(1); }); test('All other challenge names trigger method calls and success cb', () => { Object.assign(authData, { AuthenticationResult: { NewDeviceMetadata: null, }, ChallengeName: 'random challenge', }); const spyon = jest.spyOn(user, 'getCognitoUserSession'); const spyon2 = jest.spyOn(user, 'cacheTokens'); user.authenticateUserInternal(authData, authHelper, callback); expect(user.challengeName).toBe(authData.ChallengeName); expect(spyon).toHaveBeenCalledWith(authData.AuthenticationResult); expect(spyon2).toBeCalledTimes(1); const signInUserSession = user.getCognitoUserSession( authData.AuthenticationResult ); expect(callback.onSuccess).toBeCalledWith(signInUserSession); }); test('AuthHelper generateHashDevice is called and can log errors properly', () => { Object.assign(authData, { AuthenticationResult: { NewDeviceMetadata: { DeviceGroupKey: 'abc123', DeviceKey: '123abc', }, }, ChallengeName: 'random challenge', }); netRequestMockSuccess(false); user.authenticateUserInternal(authData, authHelper, callback); expect(callback.onFailure).toBeCalledWith(networkError); }); test('AuthHelper generateHashDevice with no error calls auth methods', () => { const randomPasswordSpy = jest.spyOn( AuthenticationHelper.prototype, 'getRandomPassword' ); const getVerifierDevicesSpy = jest.spyOn( AuthenticationHelper.prototype, 'getVerifierDevices' ); const hashDeviceSpy = jest.spyOn( AuthenticationHelper.prototype, 'generateHashDevice' ); user.authenticateUserInternal(authData, authHelper, callback); expect(randomPasswordSpy).toBeCalledTimes(1); expect(getVerifierDevicesSpy).toBeCalledTimes(1); expect(hashDeviceSpy).toBeCalledTimes(1); }); test('Client request fails gracefully', () => { netRequestMockSuccess(false); user.authenticateUserInternal(authData, authHelper, callback); expect(callback.onFailure).toBeCalledWith(networkError); }); test('Successful client request passes data properly to cb', () => { netRequestMockSuccess(true, { UserConfirmationNecessary: true }); user.authenticateUserInternal(authData, authHelper, callback); expect(callback.onSuccess).toBeCalledWith(user.signInUserSession, true); }); test('Successful client request passing empty object to cb', () => { netRequestMockSuccess(true, { UserConfirmationNecessary: true, }); user.authenticateUserInternal(authData, authHelper, callback); expect(callback.onSuccess).toBeCalledWith(user.signInUserSession, true); }); }); describe('completeNewPasswordChallenge()', () => { const user = new CognitoUser({ ...userDefaults }); const requiredAttributeData = { attr1: true, attr2: 'important', attr3: [123, 'abc', true], }; const clientMetadata = { meta1: false, meta2: 'test val', meta3: [456, 'xyz', false], }; afterEach(() => { jest.restoreAllMocks(); callback.onFailure.mockClear(); }); test('No newPassword triggers an error', () => { user.completeNewPasswordChallenge(null, null, callback, null); expect(callback.onFailure).toBeCalledWith(passwordErr); }); test('completeNewPasswordChallenge calls expected helper methods', () => { const spyon = jest.spyOn( AuthenticationHelper.prototype, 'getNewPasswordRequiredChallengeUserAttributePrefix' ); const spyon2 = jest.spyOn(user, 'getUserContextData'); user.completeNewPasswordChallenge( 'NEWp@ssw0rd', requiredAttributeData, callback, clientMetadata ); expect(spyon).toBeCalledTimes(1); expect(spyon2).toHaveBeenCalled(); }); test('Client request fails gracefully', () => { const err = new Error('Respond to auth challenge error.'); jest .spyOn(Client.prototype, 'request') .mockImplementationOnce((...args) => { args[2](err, {}); }); user.completeNewPasswordChallenge( 'NEWp@ssw0rd', requiredAttributeData, callback, clientMetadata ); expect(callback.onFailure).toBeCalledWith(err); }); test('Client request happy path', () => { jest .spyOn(Client.prototype, 'request') .mockImplementationOnce((...args) => { args[2](null, vCognitoUserSession); }); const spyon2 = jest.spyOn(user, 'authenticateUserInternal'); user.completeNewPasswordChallenge( 'NEWp@ssw0rd', requiredAttributeData, callback, clientMetadata ); expect(spyon2).toBeCalledTimes(1); }); }); describe('getDeviceResponse()', () => { const user = new CognitoUser({ ...userDefaults }); afterAll(() => { jest.restoreAllMocks(); jest.onFailure.mockClear(); jest.onSuccess.mockClear(); }); afterEach(() => { jest.clearAllMocks(); }); test('incorrect value for getLargeAValue fails gracefully', () => { const err = new Error('Cannot get large A value for some reason.'); jest .spyOn(AuthenticationHelper.prototype, 'getLargeAValue') .mockImplementation(cb => cb(err, 12345)); user.getDeviceResponse(callback, {}); expect(callback.onFailure).toBeCalledWith(err); }); test('Auth helper getLargeAValue happy path', () => { jest .spyOn(AuthenticationHelper.prototype, 'getLargeAValue') .mockImplementation(cb => cb(null, 12345)); const spyon2 = jest.spyOn(user, 'getUserContextData'); user.getDeviceResponse(callback, {}); expect(callback.onFailure).not.toBeCalled(); expect(spyon2).toBeCalled(); }); test('Client request RespondToAuthChallenge fails gracefully', () => { netRequestMockSuccess(false); user.getDeviceResponse(callback, {}); expect(callback.onFailure).toBeCalledWith(networkError); }); /**TODO: Check this clientRequestSpy */ describe('RespondToAuthChallenge nested Client method suite', () => { let clientRequestSpy; let defaultConfig = { ChallengeName: 'CUSTOM_CHALLENGE', Session: vCognitoUserSession, ChallengeParameters: { USER_ID_FOR_SRP: 'abc123', SRP_B: 'abc123', SALT: 'abc123', SECRET_BLOCK: 'verysecret', }, }; beforeEach(() => { user.deviceGroupKey = 'abc123'; user.deviceKey = '123abc'; clientRequestSpy = jest .spyOn(Client.prototype, 'request') .mockImplementation((...args) => { args[2](null, defaultConfig); }); }); afterEach(() => { jest.clearAllMocks(); }); test('Client request RespondToAuthChallenge - getPasswordAuthenticationKey cb fails gracefully', () => { const err = new Error('errHkdf Error'); jest .spyOn(AuthenticationHelper.prototype, 'getPasswordAuthenticationKey') .mockImplementation((...args) => { args[4](err, null); }); user.getDeviceResponse(callback, {}); expect(callback.onFailure).toBeCalledWith(err); }); test('Client request RespondToAuthChallenge - getPasswordAuthenticationKey CryptoJS code runs smoothly', () => { jest .spyOn(AuthenticationHelper.prototype, 'getPasswordAuthenticationKey') .mockImplementation((...args) => { args[4](null, 'hkdf value'); }); const spyon2 = jest.spyOn(user, 'getUserContextData'); user.getDeviceResponse(callback, {}); expect(spyon2).toBeCalled(); }); test('Client request RespondToAuthChallenge nested client fails gracefully', () => { netRequestMockSuccess(false); user.getDeviceResponse(callback, {}); expect(callback.onFailure).toBeCalledWith(networkError); }); test('Client request RespondToAuthChallenge nested client calls success callbacks', () => { const spyon = jest.spyOn(user, 'getCognitoUserSession'); const spyon2 = jest.spyOn(user, 'cacheTokens'); user.getDeviceResponse(callback, {}); expect(spyon).toBeCalledTimes(1); expect(spyon2).toBeCalledTimes(1); expect(callback.onSuccess).toBeCalledWith(user.signInUserSession); }); }); }); describe('confirmRegistration()', () => { const user = new CognitoUser({ ...userDefaults }); const callback = jest.fn(); let [confirmationCode, forceAliasCreation] = ['abc123', true]; const clientMetadata = { meta1: 'value 1', meta2: 'value 2' }; afterEach(() => { jest.clearAllMocks(); }); test('ConfirmSignUp fails gracefully', () => { netRequestMockSuccess(false); const spyon2 = jest.spyOn(user, 'getUserContextData'); user.confirmRegistration( confirmationCode, forceAliasCreation, callback, clientMetadata ); expect(spyon2).toBeCalled(); expect(callback).toBeCalledWith(networkError, null); }); test('ConfirmSignUp returns SUCCESS', () => { jest.spyOn(Client.prototype, 'request').mockImplementation((...args) => { args[2](null); }); const spyon2 = jest.spyOn(user, 'getUserContextData'); user.confirmRegistration( confirmationCode, forceAliasCreation, callback, clientMetadata ); expect(spyon2).toBeCalled(); expect(callback).toBeCalledWith(null, 'SUCCESS'); }); }); describe('sendCustomChallengeAnswer()', () => { const user = new CognitoUser({ ...userDefaults }); let answerChallenge = 'the answer'; let clientMetadata = { meta1: 'value 1', meta2: 'value2' }; user.Session = vCognitoUserSession; Object.assign(vCognitoUserSession, { AuthenticationResult: { NewDeviceMetadata: { DeviceGroupKey: 'abc123', DeviceKey: '123abc', }, }, ChallengeName: 'random challenge', }); afterEach(() => { jest.clearAllMocks(); }); test('send custom challenge happy path', () => { jest.spyOn(Client.prototype, 'request').mockImplementation((...args) => { args[2](null, vCognitoUserSession); }); const spyon2 = jest.spyOn(user, 'getCachedDeviceKeyAndPassword'); const spyon3 = jest.spyOn(user, 'authenticateUserInternal'); user.sendCustomChallengeAnswer(answerChallenge, callback, clientMetadata); expect(spyon2).toBeCalledTimes(1); expect(spyon3).toBeCalledWith( vCognitoUserSession, expect.any(AuthenticationHelper), callback ); }); test('send custom challenge fails gracefully', () => { const err = new Error('RespondToAuthChallenge error.'); jest.spyOn(Client.prototype, 'request').mockImplementation((...args) => { args[2](err, vCognitoUserSession); }); user.sendCustomChallengeAnswer(answerChallenge, callback, clientMetadata); expect(callback.onFailure).toBeCalledWith(err); }); }); describe('verifySoftwareToken()', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); afterEach(() => { callback.onSuccess.mockClear(); callback.onFailure.mockClear(); }); afterAll(() => { jest.restoreAllMocks(); }); test('happy case should callback onSuccess with the token', () => { netRequestMockSuccess(true); netRequestMockSuccess(true); cognitoUser.verifySoftwareToken(totpCode, deviceName, callback); expect(callback.onSuccess.mock.calls.length).toBe(1); }); test('Verify software token first callback fails', () => { netRequestMockSuccess(false); cognitoUser.verifySoftwareToken(totpCode, deviceName, callback); expect(callback.onFailure.mock.calls.length).toBe(1); }); test('Verify Software Token second callback fails', () => { netRequestMockSuccess(true); netRequestMockSuccess(false); cognitoUser.verifySoftwareToken(totpCode, deviceName, callback); expect(callback.onFailure.mock.calls.length).toBe(1); }); test('Happy case for signed in user session', () => { cognitoUser.setSignInUserSession(vCognitoUserSession); netRequestMockSuccess(true); cognitoUser.verifySoftwareToken(totpCode, deviceName, callback); expect(callback.onSuccess.mock.calls.length).toBe(1); }); test('Error case for signed in user session', () => { netRequestMockSuccess(false); cognitoUser.verifySoftwareToken(totpCode, deviceName, callback); expect(callback.onFailure.mock.calls.length).toBe(1); }); }); describe('associateSoftwareToken()', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); afterAll(() => { jest.restoreAllMocks(); }); afterEach(() => { callback.associateSecretCode.mockClear(); callback.onFailure.mockClear(); }); test('Happy path for associate software token without a userSession ', () => { netRequestMockSuccess(true); cognitoUser.associateSoftwareToken(callback); expect(callback.associateSecretCode.mock.calls.length).toBe(1); }); test('Failing in the first requeset to client', () => { netRequestMockSuccess(false); cognitoUser.associateSoftwareToken(callback); expect(callback.onFailure.mock.calls.length).toBe(1); }); test('Happy path for a user with a validUserSession ', () => { netRequestMockSuccess(true); cognitoUser.setSignInUserSession(vCognitoUserSession); cognitoUser.associateSoftwareToken(callback); expect(callback.associateSecretCode.mock.calls.length).toBe(1); }); test('Error path for a user with a validUserSession ', () => { netRequestMockSuccess(false); cognitoUser.associateSoftwareToken(callback); expect(callback.onFailure.mock.calls.length).toBe(1); }); }); describe('sendMFASelectionAnswer()', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); afterAll(() => { jest.restoreAllMocks(); }); test('happy case with SMS_MFA', () => { netRequestMockSuccess(true, { Session: 'sessionData' }); cognitoUser.sendMFASelectionAnswer('SMS_MFA', callback); expect(callback.mfaRequired.mock.calls.length).toEqual(1); }); test('happy case with software token MFA', () => { netRequestMockSuccess(true, { Session: 'sessionData' }); cognitoUser.sendMFASelectionAnswer('SOFTWARE_TOKEN_MFA', callback); expect(callback.totpRequired.mock.calls.length).toEqual(1); }); test('error case with software token MFA', () => { netRequestMockSuccess(false); cognitoUser.sendMFASelectionAnswer('SOFTWARE_TOKEN_MFA', callback); expect(callback.onFailure.mock.calls.length).toEqual(1); }); test('error case with undefined answer challenge', () => { netRequestMockSuccess(true, { Session: 'sessionData' }); const res = cognitoUser.sendMFASelectionAnswer('WRONG_CHALLENGE', callback); expect(res).toEqual(undefined); }); }); describe('signOut() and globalSignOut', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); afterAll(() => { jest.restoreAllMocks(); }); afterEach(() => { callback.onSuccess.mockClear(); callback.onFailure.mockClear(); }); test('signOut expected to set signinUserSession to equal null', () => { cognitoUser.signOut(); expect(cognitoUser.storage.getItem('')).toEqual(null); expect(cognitoUser.signInUserSession).toEqual(null); }); test('global signOut Happy Path', () => { netRequestMockSuccess(true); cognitoUser.setSignInUserSession(vCognitoUserSession); cognitoUser.globalSignOut(callback); expect(callback.onSuccess.mock.calls.length).toEqual(1); }); test('global signOut catching an error', () => { netRequestMockSuccess(false); cognitoUser.globalSignOut(callback); expect(callback.onFailure.mock.calls.length).toEqual(1); }); test('Global signout when user session is null', () => { cognitoUser.setSignInUserSession(ivCognitoUserSession); cognitoUser.globalSignOut(callback); expect(callback.onFailure.mock.calls.length).toEqual(1); }); }); describe('listDevices', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); afterAll(() => { jest.restoreAllMocks(); }); afterEach(() => { callback.onSuccess.mockClear(); callback.onFailure.mockClear(); }); cognitoUser.setSignInUserSession(vCognitoUserSession); test('Happy path for device list', () => { netRequestMockSuccess(true, ['deviceName', 'device2Name']); cognitoUser.listDevices(1, 'paginationToken', callback); expect(callback.onSuccess.mock.calls.length).toEqual(1); }); test('Client request throws an error', () => { netRequestMockSuccess(false); cognitoUser.listDevices(1, null, callback); expect(callback.onFailure.mock.calls.length).toEqual(1); }); test('Invalid userSession throws an error', () => { cognitoUser.setSignInUserSession(ivCognitoUserSession); cognitoUser.listDevices(1, null, callback); expect(callback.onFailure.mock.calls.length).toEqual(1); }); }); describe('setDeviceStatus[remembered,notRemembered]()', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); cognitoUser.setSignInUserSession(vCognitoUserSession); afterAll(() => { jest.restoreAllMocks(); }); afterEach(() => { callback.onSuccess.mockClear(); callback.onFailure.mockClear(); }); test('Happy path should callback success', () => { netRequestMockSuccess(true); cognitoUser.setDeviceStatusNotRemembered(callback); expect(callback.onSuccess.mock.calls.length).toEqual(1); }); test('Callback catches an error from client request', () => { netRequestMockSuccess(false); cognitoUser.setDeviceStatusNotRemembered(callback); expect(callback.onFailure.mock.calls.length).toEqual(1); }); test('Client request does not work and method returns undefined', () => { cognitoUser.setSignInUserSession(vCognitoUserSession); expect(cognitoUser.setDeviceStatusNotRemembered(callback)).toEqual( undefined ); }); test('Happy path for setDeviceStatusRemembered should callback with onSuccess ', () => { netRequestMockSuccess(true); cognitoUser.setDeviceStatusRemembered(callback); expect(callback.onSuccess.mock.calls.length).toEqual(1); }); test('Client throws and error should callback onFailure', () => { netRequestMockSuccess(false); cognitoUser.setDeviceStatusRemembered(callback); expect(callback.onFailure.mock.calls.length).toEqual(1); }); test('Client request does not work and method returns undefined', () => { expect(cognitoUser.setDeviceStatusRemembered(callback)).toEqual(undefined); }); test('Invalid user session throws an error', () => { cognitoUser.setSignInUserSession(ivCognitoUserSession); cognitoUser.setDeviceStatusNotRemembered(callback); expect(callback.onFailure.mock.calls.length).toEqual(1); }); test('Invalid user session throws an error', () => { cognitoUser.setDeviceStatusRemembered(callback); expect(callback.onFailure.mock.calls.length).toEqual(1); }); }); describe('forgetDevices()', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); cognitoUser.setSignInUserSession(vCognitoUserSession); afterAll(() => { jest.restoreAllMocks(); }); afterEach(() => { callback.onSuccess.mockClear(); callback.onFailure.mockClear(); }); test('Forget specific device happy path should callback onSuccess', () => { netRequestMockSuccess(true); cognitoUser.forgetSpecificDevice('deviceKey', callback); expect(callback.onSuccess.mock.calls.length).toEqual(1); }); test('Client request throws an error for forget specific device', () => { netRequestMockSuccess(false); cognitoUser.forgetSpecificDevice('deviceKey', callback); expect(callback.onFailure.mock.calls.length).toEqual(1); }); test('Returns undefined when client request does not work properly', () => { expect(cognitoUser.forgetSpecificDevice('deviceKey', callback)).toEqual( undefined ); }); test('forgetSpecificDevice happy path should callback onSuccess', () => { netRequestMockSuccess(true); cognitoUser.forgetDevice(callback); expect(callback.onSuccess.mock.calls.length).toEqual(1); }); test('Invalid user session throws and error for forget specific device', () => { cognitoUser.setSignInUserSession(ivCognitoUserSession); cognitoUser.forgetSpecificDevice('deviceKey', callback); expect(callback.onFailure.mock.calls.length).toEqual(1); }); }); describe('getDevice()', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); cognitoUser.setSignInUserSession(vCognitoUserSession); afterAll(() => { jest.restoreAllMocks(); }); afterEach(() => { callback.onSuccess.mockClear(); callback.onFailure.mockClear(); }); test('Happy path for getDevice should callback onSuccess', () => { netRequestMockSuccess(true); cognitoUser.getDevice(callback); expect(callback.onSuccess.mock.calls.length).toEqual(1); }); test('client request returns an error and onFailure is called', () => { netRequestMockSuccess(false); cognitoUser.getDevice(callback); expect(callback.onFailure.mock.calls.length).toEqual(1); }); test('No client request method implementations, return undefined', () => { expect(cognitoUser.getDevice(callback)).toEqual(undefined); }); test('invalid user session should callback onFailure', () => { cognitoUser.setSignInUserSession(ivCognitoUserSession); cognitoUser.getDevice(callback); expect(callback.onFailure.mock.calls.length).toEqual(1); }); }); describe('verifyAttribute(), getAttributeVerificationCode', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); cognitoUser.setSignInUserSession(vCognitoUserSession); const verifyAttributeDefaults = ['username', '123456', callback]; afterAll(() => { jest.restoreAllMocks(); }); afterEach(() => { jest.clearAllMocks(); }); test('Happy path for verifyAttribute should callback onSuccess', () => { netRequestMockSuccess(true); cognitoUser.verifyAttribute(...verifyAttributeDefaults); expect(callback.onSuccess.mock.calls.length).toEqual(1); }); test('client request returns an error and onFailure is called', () => { netRequestMockSuccess(false); cognitoUser.verifyAttribute(...verifyAttributeDefaults); expect(callback.onFailure.mock.calls.length).toEqual(1); }); test('No client request method implementations, return undefined', () => { expect(cognitoUser.verifyAttribute(...verifyAttributeDefaults)).toEqual( undefined ); }); const getAttrsVerifCodeDefaults = ['username', callback, {}]; test('happy path for getAttributeVerificationCode', () => { //callback.inputVerification needs to be set to null before the call to avoid the conditional in the method. callback.inputVerificationCode = null; netRequestMockSuccess(true); cognitoUser.getAttributeVerificationCode(...getAttrsVerifCodeDefaults); callback.inputVerificationCode = jest.fn(); expect(callback.onSuccess).toHaveBeenCalledWith('SUCCESS'); }); test('when inputVerificationCode exists in the callback, call inputVerifier with the data', () => { netRequestMockSuccess(true); cognitoUser.getAttributeVerificationCode(...getAttrsVerifCodeDefaults); expect(callback.inputVerificationCode.mock.calls.length).toEqual(1); }); test('when inputVerificationCode with a failed network request', () => { netRequestMockSuccess(false); cognitoUser.getAttributeVerificationCode(...getAttrsVerifCodeDefaults); expect(callback.onFailure.mock.calls.length).toEqual(1); }); test('invalid user session should callback onFailure for verifyAttributes', () => { cognitoUser.setSignInUserSession(ivCognitoUserSession); cognitoUser.verifyAttribute(...verifyAttributeDefaults); expect(callback.onFailure.mock.calls.length).toEqual(1); }); test('invalid user session should callback onFailure for getAttrsVerifCodeDefaults', () => { cognitoUser.getAttributeVerificationCode(...getAttrsVerifCodeDefaults); expect(callback.onFailure.mock.calls.length).toEqual(1); }); }); describe('confirmPassword() and forgotPassword()', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); cognitoUser.setSignInUserSession(vCognitoUserSession); const confirmPasswordDefaults = [ 'confirmCode', 'newSecurePassword', callback, {}, ]; const forgotPasswordDefaults = [callback, {}]; afterAll(() => { jest.restoreAllMocks(); }); afterEach(() => { jest.clearAllMocks(); }); test('happy path should callback onSuccess', () => { netRequestMockSuccess(true); cognitoUser.confirmPassword(...confirmPasswordDefaults); expect(callback.onSuccess).toHaveBeenCalledWith('SUCCESS'); }); test('client request throws an error', () => { netRequestMockSuccess(false); cognitoUser.confirmPassword(...confirmPasswordDefaults); expect(callback.onFailure.mock.calls.length).toEqual(1); }); test('happy path should callback onSuccess', () => { callback.inputVerificationCode = null; netRequestMockSuccess(true); cognitoUser.forgotPassword(...forgotPasswordDefaults); expect(callback.onSuccess.mock.calls.length).toEqual(1); }); test('inputVerification code is a function should callback inputVerificationCode', () => { callback.inputVerificationCode = jest.fn(); netRequestMockSuccess(true); cognitoUser.forgotPassword(...forgotPasswordDefaults); expect(callback.inputVerificationCode.mock.calls.length).toEqual(1); }); test('client returning an error should call onFailure', () => { netRequestMockSuccess(false); cognitoUser.forgotPassword(...forgotPasswordDefaults); expect(callback.onFailure.mock.calls.length).toEqual(1); }); }); describe('MFA test suite', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); cognitoUser.setSignInUserSession(vCognitoUserSession); const sendMfaDefaults = ['abc123', callback, 'SMS_MFA', {}]; afterAll(() => { jest.restoreAllMocks(); }); afterEach(() => { jest.clearAllMocks(); }); const payload = { ChallengeName: 'SMS_MFA', AuthenticationResult: { NewDeviceMetadata: 'deviceMetaData' }, }; /** sendMFA() */ test('Happy path for sendMFACode should call onSuccess', () => { netRequestMockSuccess(true, payload); authHelperMock(genHashDevices); authHelperMock(getSalt); authHelperMock(getVerifiers); netRequestMockSuccess(true, { UserConfirmationNecessary: false }); cognitoUser.sendMFACode(...sendMfaDefaults); expect(callback.onSuccess.mock.calls.length).toEqual(1); }); test('when userConfirmation is true, should callback onSuccess', () => { netRequestMockSuccess(true, payload); authHelperMock(genHashDevices); authHelperMock(getSalt); authHelperMock(getVerifiers); netRequestMockSuccess(true, { UserConfirmationNecessary: true }); cognitoUser.sendMFACode(...sendMfaDefaults); expect(callback.onSuccess.mock.calls.length).toEqual(1); }); test('second client request fails so sendMFACode should call onFailure', () => { netRequestMockSuccess(true, payload); authHelperMock(genHashDevices); authHelperMock(getSalt); authHelperMock(getVerifiers); netRequestMockSuccess(false); cognitoUser.sendMFACode(...sendMfaDefaults); expect(callback.onFailure.mock.calls.length).toEqual(1); }); test('when generateHashDevice fails, sendMFACode should call onFailure', () => { netRequestMockSuccess(true, payload); jest .spyOn(AuthenticationHelper.prototype, 'generateHashDevice') .mockImplementationOnce((...args) => { args[2](new Error('Network Error'), null); }); cognitoUser.sendMFACode(...sendMfaDefaults); expect(callback.onFailure.mock.calls.length).toEqual(1); }); test('when AuthenticationResult.NewDeviceMetadata == null, callback onSuccess', () => { jest .spyOn(Client.prototype, 'request') .mockImplementationOnce((...args) => { args[2](null, { ChallengeName: 'SMS_MFA', AuthenticationResult: { NewDeviceMetadata: null }, }); }); cognitoUser.sendMFACode(...sendMfaDefaults); expect(callback.onSuccess.mock.calls.length).toEqual(1); }); test('first network request throws an error calls onFailure', () => { netRequestMockSuccess(false); cognitoUser.sendMFACode(...sendMfaDefaults); expect(callback.onFailure.mock.calls.length).toEqual(1); }); test('first client request does not exist so sendMFACode should return undefined', () => { expect(cognitoUser.sendMFACode(...sendMfaDefaults)).toEqual(undefined); }); }); describe('enableMFA()', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); cognitoUser.setSignInUserSession(vCognitoUserSession); const callback = jest.fn(); afterAll(() => { jest.restoreAllMocks(); }); afterEach(() => { callback.mockClear(); }); test('enableMFA happy path should callback on success ', () => { netRequestMockSuccess(true); cognitoUser.enableMFA(callback); expect(callback.mock.calls[0][1]).toEqual('SUCCESS'); }); test('enableMFA should have an error when client request fails', () => { netRequestMockSuccess(false); cognitoUser.enableMFA(callback); expect(callback.mock.calls[0][0]).toMatchObject(networkError); }); test('enableMFA should return undefined when no client request is defined', () => { expect(cognitoUser.enableMFA(callback)).toEqual(undefined); }); test('enableMFA should callback with an error when userSession is invalid', () => { cognitoUser.setSignInUserSession(ivCognitoUserSession); cognitoUser.enableMFA(callback); expect(callback.mock.calls[0][0]).toMatchObject( Error('User is not authenticated') ); }); }); describe('setUserMfaPreference', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); cognitoUser.setSignInUserSession(vCognitoUserSession); const callback = jest.fn(); const setUserMfaPreferenceDefaults = [ 'smsMFASetting', 'swTokenMFASetting', callback, ]; afterAll(() => { jest.restoreAllMocks(); }); afterEach(() => { callback.mockClear(); }); test('happy path for setUserMfaPreferences should callback(null,SUCCESS)', () => { netRequestMockSuccess(true); cognitoUser.setUserMfaPreference(...setUserMfaPreferenceDefaults); expect(callback.mock.calls[0][1]).toEqual('SUCCESS'); }); test('client request throws an error path for setUserMfaPreferences should callback(null,SUCCESS)', () => { netRequestMockSuccess(false); cognitoUser.setUserMfaPreference(...setUserMfaPreferenceDefaults); expect(callback.mock.calls[0][0]).toMatchObject(Error('Network Error')); }); test('happy path for setUserMfaPreferences should callback(null,SUCCESS)', () => { expect( cognitoUser.setUserMfaPreference(...setUserMfaPreferenceDefaults) ).toEqual(undefined); }); test('should callback error when cognito user session is invalid', () => { cognitoUser.setSignInUserSession(ivCognitoUserSession); cognitoUser.setUserMfaPreference(...setUserMfaPreferenceDefaults); expect(callback.mock.calls[0][0]).toMatchObject( new Error('User is not authenticated') ); }); }); describe('disableMFA()', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); cognitoUser.setSignInUserSession(vCognitoUserSession); const callback = jest.fn(); afterAll(() => { jest.restoreAllMocks(); }); afterEach(() => { callback.mockClear(); }); test('happy path should callback with (null, SUCCESS)', () => { netRequestMockSuccess(true); cognitoUser.disableMFA(callback); expect(callback.mock.calls[0][1]).toEqual('SUCCESS'); }); test('client request throws an error and should callback with (err, null)', () => { netRequestMockSuccess(false); cognitoUser.disableMFA(callback); expect(callback.mock.calls[0][0]).toMatchObject(new Error('Network Error')); }); test('client request does not exist and disableMFA should callback with (err, null)', () => { expect(cognitoUser.disableMFA(callback)).toEqual(undefined); }); test('when user is invalid, return callback with error', () => { cognitoUser.setSignInUserSession(ivCognitoUserSession); cognitoUser.disableMFA(callback); expect(callback.mock.calls[0][0]).toMatchObject( new Error('User is not authenticated') ); }); }); describe('getMFAOptions()', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); cognitoUser.setSignInUserSession(vCognitoUserSession); const callback = jest.fn(); afterAll(() => { jest.restoreAllMocks(); }); afterEach(() => { callback.mockClear(); }); test('happy path for getMFAOptions should callback onSuccess', () => { netRequestMockSuccess(true, { MFAOptions: 'SMS_MFA' }); cognitoUser.getMFAOptions(callback); expect(callback.mock.calls[0][1]).toEqual('SMS_MFA'); }); test('client request throws an error and should callback with (err, null)', () => { netRequestMockSuccess(false); cognitoUser.getMFAOptions(callback); expect(callback.mock.calls[0][0]).toMatchObject(new Error('Network Error')); }); test('when user is invalid, return callback with error', () => { cognitoUser.setSignInUserSession(ivCognitoUserSession); cognitoUser.getMFAOptions(callback); expect(callback.mock.calls[0][0]).toMatchObject( new Error('User is not authenticated') ); }); }); describe('deleteUser()', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); cognitoUser.setSignInUserSession(vCognitoUserSession); const callback = jest.fn(); afterAll(() => { jest.restoreAllMocks(); }); afterEach(() => { callback.mockClear(); }); test('happy path should callback SUCCESS', () => { netRequestMockSuccess(true, null); cognitoUser.deleteUser(callback, {}); expect(callback.mock.calls[0][1]).toEqual('SUCCESS'); }); test('client request throws an error', () => { netRequestMockSuccess(false); cognitoUser.deleteUser(callback, {}); expect(callback.mock.calls[0][0]).toMatchObject(new Error('Network Error')); }); test('having an invalid user session should callback with a new error', () => { cognitoUser.setSignInUserSession(ivCognitoUserSession); cognitoUser.deleteUser(callback, {}); expect(callback.mock.calls[0][0]).toMatchObject( new Error('User is not authenticated') ); }); }); describe('getUserAttributes()', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); cognitoUser.setSignInUserSession(vCognitoUserSession); const callback = jest.fn(); afterAll(() => { jest.restoreAllMocks(); }); afterEach(() => { callback.mockClear(); }); test('happy path for getUserAttributes', () => { const userAttributesObject = { UserAttributes: [{ Name: 'name1', Value: 'value1' }], }; netRequestMockSuccess(true, userAttributesObject); cognitoUser.getUserAttributes(callback); expect(callback.mock.calls[0][1]).toMatchObject( userAttributesObject.UserAttributes ); }); test('client request throws an error', () => { netRequestMockSuccess(false); cognitoUser.getUserAttributes(callback); expect(callback.mock.calls[0][0]).toMatchObject(new Error('Network Error')); }); test('having an invalid user session should callback with a new error', () => { cognitoUser.setSignInUserSession(ivCognitoUserSession); cognitoUser.getUserAttributes(callback); expect(callback.mock.calls[0][0]).toMatchObject( new Error('User is not authenticated') ); }); }); describe('deleteAttributes()', () => { const callback = jest.fn(); const cognitoUser = new CognitoUser({ ...userDefaults }); cognitoUser.setSignInUserSession(vCognitoUserSession); afterAll(() => { jest.restoreAllMocks(); }); afterEach(() => { callback.mockClear(); }); test('happy path for deleteAttrbutes should call getUserData to update cache', () => { netRequestMockSuccess(true); const getUserDataSpy = jest .spyOn(cognitoUser, 'getUserData') .mockImplementationOnce(cb => cb()); cognitoUser.deleteAttributes([], callback); expect(getUserDataSpy).toBeCalled(); expect(callback.mock.calls[0][1]).toEqual('SUCCESS'); }); test('client request throws an error', () => { netRequestMockSuccess(false); cognitoUser.deleteAttributes([], callback); expect(callback.mock.calls[0][0]).toMatchObject(new Error('Network Error')); }); test('having an invalid user session should callback with a new error', () => { cognitoUser.setSignInUserSession(ivCognitoUserSession); cognitoUser.deleteAttributes([], callback); expect(callback.mock.calls[0][0]).toMatchObject( new Error('User is not authenticated') ); }); }); describe('getCognitoUserSession()', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); const idToken = new CognitoIdToken(); const accessToken = new CognitoAccessToken(); const refreshToken = new CognitoRefreshToken(); const sessionData = { IdToken: idToken, AccessToken: accessToken, RefreshToken: refreshToken, }; test('happy path should return a new CognitoUserSession', () => { expect(cognitoUser.getCognitoUserSession({ sessionData })).toMatchObject( new CognitoUserSession(sessionData) ); }); }); describe('refreshSession()', () => { const cognitoUser = new CognitoUser({ ...userDefaults }); const callback = jest.fn(); const refreshSessionDefaults = [new CognitoRefreshToken(), callback, {}]; const keyPrefix = `CognitoIdentityServiceProvider.${cognitoUser.pool.getClientId()}.${ cognitoUser.username }`; const idTokenKey = `${keyPrefix}.idToken`; const accessTokenKey = `${keyPrefix}.accessToken`; const refreshTokenKey = `${keyPrefix}.refreshToken`; const clockDriftKey = `${keyPrefix}.clockDrift`; const idToken = new CognitoIdToken(); const accessToken = new CognitoAccessToken(); const refreshToken = new CognitoRefreshToken(); const sessionData = { IdToken: idToken, AccessToken: accessToken, RefreshToken: refreshToken, }; afterAll(() => { jest.restoreAllMocks(); }); afterEach(() => { callback.mockClear(); }); test('happy path for refresh session ', () => { netRequestMockSuccess(true, { AuthenticationResult: { RefreshToken: null }, }); cognitoUser.refreshSession(...refreshSessionDefaults); expect(callback.mock.calls[0][1]).toMatchObject( new CognitoUserSession(sessionData) ); }); test('client throws an error ', () => { netRequestMockSuccess(false); cognitoUser.refreshSession(...refreshSessionDefaults); expect(callback.mock.calls[0][0]).toMatchObject(new Error('Network Error')); }); test('getSession()', () => { cognitoUser.setSignInUserSession(ivCognitoUserSession); cognitoUser.storage.setItem( idTokenKey, vCognitoUserSession.getIdToken().getJwtToken() ); cognitoUser.storage.setItem( accessTokenKey, vCognitoUserSession.getAccessToken().getJwtToken() ); cognitoUser.storage.setItem( refreshTokenKey, vCognitoUserSession.getRefreshToken().getToken() ); cognitoUser.storage.setItem( clockDriftKey, vCognitoUserSession.getClockDrift() ); cognitoUser.getSession(callback); expect(callback.mock.calls[0][0]).toEqual(null); }); test('when a valid userSession exists, return callback(null, signInUserSession) from instance vars', () => { cognitoUser.setSignInUserSession(vCognitoUserSession); cognitoUser.getSession(callback); expect(callback.mock.calls[0][1]).toMatchObject( cognitoUser.signInUserSession ); }); test('when a username is null, callback with an error', () => { cognitoUser.username = null; cognitoUser.getSession(callback); expect(callback.mock.calls[0][0]).toMatchObject( new Error('Username is nul