From 9913147ad3137978a2d100a6da33f16e1b7cb58d Mon Sep 17 00:00:00 2001 From: Ian Magenta <59981318+ianmagenta@users.noreply.github.com> Date: Wed, 18 Sep 2024 10:36:58 -0500 Subject: [PATCH] Added new SessionTimeoutModal Tests for Code Coverage (#31976) --- .../components/SessionTimeoutModal.jsx | 2 +- .../SessionTimeoutModal.unit.spec.js | 226 +++++++++++++++++- 2 files changed, 225 insertions(+), 3 deletions(-) diff --git a/src/platform/user/authentication/components/SessionTimeoutModal.jsx b/src/platform/user/authentication/components/SessionTimeoutModal.jsx index 9436799336d9..932f853af844 100644 --- a/src/platform/user/authentication/components/SessionTimeoutModal.jsx +++ b/src/platform/user/authentication/components/SessionTimeoutModal.jsx @@ -135,7 +135,7 @@ export class SessionTimeoutModal extends React.Component { } } -const mapStateToProps = state => { +export const mapStateToProps = state => { return { isLoggedIn: isLoggedIn(state), authenticatedWithOAuth: isAuthenticatedWithOAuth(state), diff --git a/src/platform/user/tests/authentication/components/SessionTimeoutModal.unit.spec.js b/src/platform/user/tests/authentication/components/SessionTimeoutModal.unit.spec.js index 19d89ac29b90..af644af6d702 100644 --- a/src/platform/user/tests/authentication/components/SessionTimeoutModal.unit.spec.js +++ b/src/platform/user/tests/authentication/components/SessionTimeoutModal.unit.spec.js @@ -2,8 +2,13 @@ import React from 'react'; import { expect } from 'chai'; import { shallow } from 'enzyme'; import sinon from 'sinon'; - -import { SessionTimeoutModal } from 'platform/user/authentication/components/SessionTimeoutModal'; +import localStorage from 'platform/utilities/storage/localStorage'; +import { + SessionTimeoutModal, + mapStateToProps, +} from 'platform/user/authentication/components/SessionTimeoutModal'; +import * as oauthUtilities from 'platform/utilities/oauth/utilities'; +import * as authenticationUtilities from 'platform/user/authentication/utilities'; const defaultProps = { isLoggedIn: true, @@ -21,4 +26,221 @@ describe('SessionTimeoutModal', () => { expect(buttons.length).to.eql(2); component.unmount(); }); + + it('should map state to props', () => { + const state = { + user: { + login: { + currentlyLoggedIn: true, + }, + profile: { + session: { + authBroker: 'sis', + }, + signIn: { + serviceName: 'logingov', + }, + }, + }, + }; + const props = mapStateToProps(state); + + expect(props.isLoggedIn).to.eql(true); + expect(props.authenticatedWithOAuth).to.eql(true); + expect(props.serviceName).to.eql('logingov'); + }); + + it('extends the session when the modal is closed', () => { + const onExtendSessionSpy = sinon.spy(); + const props = { + ...defaultProps, + onExtendSession: onExtendSessionSpy, + }; + const component = shallow(); + + component.find('Modal').prop('onClose')(); + + expect(onExtendSessionSpy.calledOnce).to.be.true; + + component.unmount(); + }); + + it('should call refresh when extendSession is called and authenticatedWithOAuth is true', () => { + const refreshStub = sinon.stub(oauthUtilities, 'refresh'); + const props = { + ...defaultProps, + authenticatedWithOAuth: true, + }; + const component = shallow(); + + component.instance().extendSession(); + + expect(refreshStub.calledOnce).to.be.true; + expect(refreshStub.calledWith({ type: 'logingov' })).to.be.true; + + refreshStub.restore(); + component.unmount(); + }); + + it('should call logoutUrlSiS when signing out while authenticatedWithOAuth is true', () => { + const logoutUrlSiSStub = sinon.stub(oauthUtilities, 'logoutUrlSiS'); + const props = { + ...defaultProps, + authenticatedWithOAuth: true, + }; + + const component = shallow(); + + component.instance().signOut(); + + expect(logoutUrlSiSStub.calledOnce).to.be.true; + + logoutUrlSiSStub.restore(); + component.unmount(); + }); + + it('should call IAMLogout when signing out while authenticatedWithOAuth is false', () => { + const IAMLogoutStub = sinon.stub(authenticationUtilities, 'logout'); + + const component = shallow(); + + component.instance().signOut(); + + expect(IAMLogoutStub.calledOnce).to.be.true; + + IAMLogoutStub.restore(); + component.unmount(); + }); + + it('should call logoutUrlSiS when the session expires while authenticatedWithOAuth is true', () => { + const logoutUrlSiSStub = sinon.stub(oauthUtilities, 'logoutUrlSiS'); + const props = { ...defaultProps, authenticatedWithOAuth: true }; + + const component = shallow(); + + component.instance().expireSession(); + + expect(logoutUrlSiSStub.calledOnce).to.be.true; + + logoutUrlSiSStub.restore(); + component.unmount(); + }); + + it('should call IAMLogout when the session expires while authenticatedWithOAuth is false', () => { + const IAMLogoutStub = sinon.stub(authenticationUtilities, 'logout'); + + const component = shallow(); + + component.instance().expireSession(); + + expect(IAMLogoutStub.calledOnce).to.be.true; + + IAMLogoutStub.restore(); + component.unmount(); + }); + + it('should clear interval and return when not logged in', () => { + const props = { ...defaultProps, isLoggedIn: false }; + const component = shallow(); + const instance = component.instance(); + + const clearIntervalSpy = sinon.spy(instance, 'clearInterval'); + + instance.checkExpiration(); + + expect(clearIntervalSpy.calledOnce).to.be.true; + + clearIntervalSpy.restore(); + component.unmount(); + }); + + it('should return when there is no expirationDate', () => { + const component = shallow(); + const instance = component.instance(); + + instance.checkExpiration(); + + // Ensure no state changes or method calls + expect(instance.state.countdown).to.be.null; + + component.unmount(); + }); + + it('should call expireSession when countdown is less than 0', () => { + const pastDate = new Date(Date.now() - 60000).toISOString(); // 1 minute ago + localStorage.setItem('sessionExpiration', pastDate); + const component = shallow(); + const instance = component.instance(); + const expireSessionSpy = sinon.spy(instance, 'expireSession'); + + instance.checkExpiration(); + + expect(expireSessionSpy.calledOnce).to.be.true; + + localStorage.clear(); + expireSessionSpy.restore(); + component.unmount(); + }); + + it('should set state.countdown when countdown is gte 0 and lte MODAL_DURATION', () => { + const futureDate = new Date(Date.now() + 15000).toISOString(); // 15 seconds in future + localStorage.setItem('sessionExpiration', futureDate); + const component = shallow(); + const instance = component.instance(); + const setStateSpy = sinon.spy(instance, 'setState'); + + instance.checkExpiration(); + + expect(setStateSpy.calledOnce).to.be.true; + expect(instance.state.countdown).to.be.within(0, 30); // MODAL_DURATION is 30 + + localStorage.clear(); + setStateSpy.restore(); + component.unmount(); + }); + + it('should do nothing when countdown is greater than MODAL_DURATION', () => { + const futureDate = new Date(Date.now() + 60000).toISOString(); // 60 seconds in future + localStorage.setItem('sessionExpiration', futureDate); + const component = shallow(); + const instance = component.instance(); + const setStateSpy = sinon.spy(instance, 'setState'); + const expireSessionSpy = sinon.spy(instance, 'expireSession'); + + instance.checkExpiration(); + + expect(setStateSpy.notCalled).to.be.true; + expect(expireSessionSpy.notCalled).to.be.true; + + localStorage.clear(); + setStateSpy.restore(); + expireSessionSpy.restore(); + component.unmount(); + }); + + it('does not set interval when expirationInterval is already set', () => { + const component = shallow(); + const instance = component.instance(); + instance.expirationInterval = 123; + const setIntervalSpy = sinon.spy(global, 'setInterval'); + + instance.componentDidUpdate(); + + expect(setIntervalSpy.notCalled).to.be.true; + + setIntervalSpy.restore(); + component.unmount(); + }); + + it('sets the service name to an empty string when it is undefined', () => { + const props = { ...defaultProps, serviceName: undefined }; + const component = shallow(); + const instance = component.instance(); + + instance.componentDidUpdate(); + + expect(instance.serviceName).to.eql(''); + + component.unmount(); + }); });