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();
+ });
});