Skip to content

Commit

Permalink
Merge pull request #1024 from US-Trustee-Program/CAMS-476-logout-all-…
Browse files Browse the repository at this point in the history
…tabs

CAMS-476 - Add broadcast logout functionality
  • Loading branch information
amorrow-flexion authored Nov 11, 2024
2 parents 31b0f4b + 8b07678 commit 84ee298
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 0 deletions.
19 changes: 19 additions & 0 deletions user-interface/src/lib/humble/broadcast-channel-humble.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export class BroadcastChannelHumble {
private channel: BroadcastChannel;

constructor(channelName: string) {
this.channel = new BroadcastChannel(channelName);
}

onMessage(handler: (event: MessageEvent) => void) {
this.channel.onmessage = handler;
}

postMessage(message: unknown) {
this.channel.postMessage(message);
}

close() {
this.channel.close();
}
}
2 changes: 2 additions & 0 deletions user-interface/src/login/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import ApiConfiguration from '@/configuration/apiConfiguration';
import { CamsUser } from '@common/cams/users';
import { CamsSession } from '@common/cams/session';
import { SUPERUSER } from '@common/cams/test-utilities/mock-user';
import { initializeBroadcastLogout } from '@/login/broadcast-logout';

export type LoginProps = PropsWithChildren & {
provider?: LoginProvider;
Expand All @@ -42,6 +43,7 @@ export function Login(props: LoginProps): React.ReactNode {

addApiAfterHook(http401Hook);
initializeInactiveLogout();
initializeBroadcastLogout();

const session: CamsSession | null = LocalStorage.getSession();
if (session) {
Expand Down
2 changes: 2 additions & 0 deletions user-interface/src/login/SessionEnd.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Button from '@/lib/components/uswds/Button';
import { LocalStorage } from '@/lib/utils/local-storage';
import { LOGIN_PATH, LOGOUT_SESSION_END_PATH } from './login-library';
import { BlankPage } from './BlankPage';
import { broadcastLogout } from '@/login/broadcast-logout';

export function SessionEnd() {
const location = useLocation();
Expand All @@ -16,6 +17,7 @@ export function SessionEnd() {

LocalStorage.removeSession();
LocalStorage.removeAck();
broadcastLogout();

useEffect(() => {
if (location.pathname !== LOGOUT_SESSION_END_PATH) {
Expand Down
48 changes: 48 additions & 0 deletions user-interface/src/login/broadcast-logout.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { BroadcastChannelHumble } from '@/lib/humble/broadcast-channel-humble';
import {
broadcastLogout,
handleLogoutBroadcast,
initializeBroadcastLogout,
} from '@/login/broadcast-logout';

describe('Broadcast Logout', () => {
test('should handle broadcast-logout properly', () => {
const postMessageSpy = vi
.spyOn(BroadcastChannelHumble.prototype, 'postMessage')
.mockReturnValue();
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
let onMessageFn: Function = vi.fn();
const onMessageSpy = vi
.spyOn(BroadcastChannelHumble.prototype, 'onMessage')
.mockImplementation((arg) => {
onMessageFn = arg;
});

const closeSpy = vi.spyOn(BroadcastChannelHumble.prototype, 'close').mockReturnValue();

Object.defineProperty(global, 'window', Object.create(window));
global.window.location = {
host: 'some-host',
protocol: 'http:',
assign: function (_url: string | URL): void {},
} as unknown as Location;
const assignSpy = vi.spyOn(global.window.location, 'assign');
const expectedUrl =
global.window.location.protocol + '//' + global.window.location.host + '/logout';

initializeBroadcastLogout();
broadcastLogout();

// Validate broadcastLogout function
expect(postMessageSpy).toHaveBeenCalledWith('Logout all windows');

// Validate initializeBroadcastLogout function
expect(onMessageSpy).toHaveBeenCalledWith(handleLogoutBroadcast);
expect(onMessageFn).toEqual(handleLogoutBroadcast);

// Validate handleLogout function
onMessageFn();
expect(closeSpy).toHaveBeenCalled();
expect(assignSpy).toHaveBeenCalledWith(expectedUrl);
});
});
20 changes: 20 additions & 0 deletions user-interface/src/login/broadcast-logout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { BroadcastChannelHumble } from '@/lib/humble/broadcast-channel-humble';
import { LOGOUT_PATH } from '@/login/login-library';

let channel: BroadcastChannelHumble;

export function handleLogoutBroadcast() {
const { host, protocol } = window.location;
const logoutUri = protocol + '//' + host + LOGOUT_PATH;
window.location.assign(logoutUri);
channel?.close();
}

export function initializeBroadcastLogout() {
channel = new BroadcastChannelHumble('CAMS_logout');
channel.onMessage(handleLogoutBroadcast);
}

export function broadcastLogout() {
channel?.postMessage('Logout all windows');
}
1 change: 1 addition & 0 deletions user-interface/vitest.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default defineConfig({
'**/data-verification/consolidation/ConsolidationOrderAccordionView.tsx',
'**/data-verification/consolidation/*Mock.ts',
'**/*.d.ts',
'**/**humble.ts',
...coverageConfigDefaults.exclude,
],
thresholds: {
Expand Down

0 comments on commit 84ee298

Please sign in to comment.