diff --git a/src/components/header/header.test.tsx b/src/components/header/header.test.tsx index 1d573f31..f6d956e6 100644 --- a/src/components/header/header.test.tsx +++ b/src/components/header/header.test.tsx @@ -1,4 +1,4 @@ -import { act, render, screen } from '@testing-library/react'; +import { act, fireEvent, render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { BrowserRouter } from 'react-router-dom'; @@ -8,6 +8,12 @@ import * as useAuthMock from '../../hooks/use-auth'; import { User } from '../../types/user'; import { Header } from './header'; +// Mock the navigation module +jest.mock('@uswds/uswds/js/usa-header', () => ({ + on: jest.fn(), + off: jest.fn(), +})); + describe('Header', () => { const headerComponent = ( @@ -41,13 +47,7 @@ describe('Header', () => { expect(window.location.pathname).toBe('/dashboard'); }); - test('should Sign In', async () => { - Object.defineProperty(window, 'location', { - configurable: true, - enumerable: true, - value: new URL(window.location.href), - }); - + test('should handle sign in', async () => { jest.spyOn(useAuthMock, 'default').mockReturnValue({ isSignedIn: false, currentUserData: {} as User, @@ -56,18 +56,14 @@ describe('Header', () => { signOut: jest.fn(), }); - render(headerComponent); - await userEvent.click(screen.getByText('Sign In', { selector: 'a' })); - expect(window.location.href).toBeTruthy(); - }); - - test('should Sign Out', async () => { - Object.defineProperty(window, 'location', { - configurable: true, - enumerable: true, - value: new URL(window.location.href), + const { baseElement } = render(headerComponent); + await act(async () => { + expect(baseElement).toBeTruthy(); + fireEvent.click(screen.getByText('Sign In')); }); + }); + test('should handle sign out', async () => { jest.spyOn(useAuthMock, 'default').mockReturnValue({ isSignedIn: true, currentUserData: {} as User, @@ -76,10 +72,11 @@ describe('Header', () => { signOut: jest.fn(), }); - render(headerComponent); - - await userEvent.click(screen.getByText('Sign Out', { selector: 'a' })); - expect(window.location.href).toBeTruthy(); + const { baseElement } = render(headerComponent); + await act(async () => { + expect(baseElement).toBeTruthy(); + fireEvent.click(screen.getByText('Sign Out')); + }); }); test('should display menu when button is clicked', async () => { @@ -100,4 +97,18 @@ describe('Header', () => { }); expect(baseElement.querySelector('.usa-nav')).toBeDefined(); }); + + test('should render menu successfully', () => { + render(headerComponent); + + expect(screen.getByText('Skip to main content')).toBeTruthy(); + expect(screen.getByText('Menu')).toBeTruthy(); + }); + + test('should toggle the menu on button click', () => { + render(headerComponent); + + const menuButton = screen.getByText('Menu'); + fireEvent.click(menuButton); + }); }); diff --git a/src/hooks/use-auth-sso.test.tsx b/src/hooks/use-auth-sso.test.tsx new file mode 100644 index 00000000..5715a871 --- /dev/null +++ b/src/hooks/use-auth-sso.test.tsx @@ -0,0 +1,142 @@ +import { act, renderHook } from '@testing-library/react'; +import { RecoilRoot } from 'recoil'; +import useAuth from './use-auth'; // Import your useAuth function + +interface ContextWrapperProps { + children: React.ReactNode; +} + +jest.mock('react-oidc-context', () => ({ + useAuth: jest.fn().mockImplementation(() => ({ + signinRedirect: jest.fn().mockResolvedValue(true), + signoutRedirect: jest.fn().mockResolvedValue(true), + isAuthenticated: true, + user: { + profile: undefined, + }, + })), +})); + +jest.mock('@src/utils/auth', () => ({ + getSignInRedirectUrl: jest.fn(() => 'mocked-redirect-url'), // Replace with the expected URL +})); + +describe('useAuth', () => { + const OLD_ENV = process.env; + beforeEach(() => { + process.env = { ...OLD_ENV }; + jest.clearAllMocks(); + }); + + const contextWrapper = ({ children }: ContextWrapperProps) => ( + {children} + ); + + test('should call signIn with SSO and no configs', async () => { + const { result } = renderHook(() => useAuth(), { + wrapper: contextWrapper, + }); + + await act(async () => { + result.current.signIn(true); + }); + expect(result.current.signIn).toBeTruthy(); + }); + + test('should call signIn with SSO and available configs', async () => { + process.env.SSO_AUTHORITY = 'http://localhost'; + process.env.SSO_CLIENT_ID = 'dev-client'; + + const { result } = renderHook(() => useAuth(), { + wrapper: contextWrapper, + }); + + await act(async () => { + result.current.signIn(true); + }); + expect(result.current.signIn).toBeTruthy(); + }); + + it('should set isSignedIn to true when authenticated with sso', () => { + const { result } = renderHook(() => useAuth(), { + wrapper: contextWrapper, + }); + + act(() => { + result.current.signIn(true); + }); + + expect(result.current.isSignedIn).toBe(true); + }); + + it('should not authenticated with sso and error', () => { + jest.mock('react-oidc-context', () => ({ + useAuth: jest.fn().mockImplementationOnce(() => ({ + signinRedirect: jest.fn().mockRejectedValue(true), + signoutRedirect: jest.fn(), + isAuthenticated: false, + })), + })); + const { result } = renderHook(() => useAuth(), { + wrapper: contextWrapper, + }); + + act(() => { + result.current.signIn(true); + }); + + expect(result.current.isSignedIn).toBe(true); + }); + + it('should set isSignedIn to true when authenticated without sso', () => { + const { result } = renderHook(() => useAuth(), { + wrapper: contextWrapper, + }); + + act(() => { + result.current.signIn(false); + }); + + expect(result.current.isSignedIn).toBe(true); + }); + + it('should sign out and set isSignedIn to false when authenticated', () => { + const { result } = renderHook(() => useAuth(), { + wrapper: contextWrapper, + }); + + act(() => { + result.current.signOut(); + }); + + expect(result.current.isSignedIn).toBe(false); + }); + + it('should set isSignedIn to true when authenticated and with profile', () => { + jest.mock('react-oidc-context', () => ({ + useAuth: jest.fn().mockImplementationOnce(() => ({ + signinRedirect: jest.fn().mockResolvedValue(true), + signoutRedirect: jest.fn().mockResolvedValue(true), + isAuthenticated: true, + user: { + profile: { + firstName: 'John', + lastName: 'Doe', + displayName: 'John Doe', + emailAddress: 'jdoe@test.com', + phoneNumber: '1234567890', + }, + }, + })), + })); + const { result } = renderHook(() => useAuth(), { + wrapper: contextWrapper, + }); + + act(() => { + result.current.signIn(false); + }); + + expect(result.current.isSignedIn).toBe(true); + }); +}); diff --git a/src/hooks/use-auth.test.tsx b/src/hooks/use-auth.test.tsx index 3afcdb60..bb499fe6 100644 --- a/src/hooks/use-auth.test.tsx +++ b/src/hooks/use-auth.test.tsx @@ -31,31 +31,6 @@ describe('useAuth', () => { expect(result.current.signIn).toBeTruthy(); }); - test('should call signIn with SSO and no configs', async () => { - const { result } = renderHook(() => useAuth(), { - wrapper: contextWrapper, - }); - - await act(async () => { - result.current.signIn(true); - }); - expect(result.current.signIn).toBeTruthy(); - }); - - test('should call signIn with SSO and available configs', async () => { - process.env.SSO_AUTHORITY = 'http://localhost'; - process.env.SSO_CLIENT_ID = 'dev-client'; - - const { result } = renderHook(() => useAuth(), { - wrapper: contextWrapper, - }); - - await act(async () => { - result.current.signIn(true); - }); - expect(result.current.signIn).toBeTruthy(); - }); - test('should call signOut successfully', async () => { const { result } = renderHook(() => useAuth(), { wrapper: contextWrapper, diff --git a/src/pages/dashboard/dashboard.test.tsx b/src/pages/dashboard/dashboard.test.tsx index 272cb5ce..97f7b409 100644 --- a/src/pages/dashboard/dashboard.test.tsx +++ b/src/pages/dashboard/dashboard.test.tsx @@ -1,3 +1,4 @@ +import { mockData } from '@src/data/spacecraft'; import axios from '@src/utils/axios'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { act, render } from '@testing-library/react'; @@ -38,7 +39,7 @@ describe('Dashboard', () => { }); test('should render successfully', async () => { - mock.onGet(new RegExp('/api/spacecraft')).reply(200, { items: [] }); + mock.onGet(new RegExp('/spacecraft')).reply(200, mockData); jest.spyOn(useAuthMock, 'default').mockReturnValue({ isSignedIn: true, currentUserData: {} as User, @@ -51,6 +52,7 @@ describe('Dashboard', () => { await act(async () => { expect(baseElement).toBeTruthy(); }); + expect(baseElement.querySelector('h1')?.textContent).toEqual('Dashboard'); expect(baseElement.querySelectorAll('.VictoryContainer')).toHaveLength(2); expect(baseElement.querySelector('.usa-table')).toBeDefined(); @@ -60,7 +62,9 @@ describe('Dashboard', () => { }); test('should render with error', async () => { - mock.onGet(new RegExp('/?format=json')).networkError(); + mock + .onGet(new RegExp('/spacecraft')) + .reply(500, { message: 'Internal Server Error' }); const { baseElement } = render(componentWrapper); await act(async () => { expect(baseElement).toBeTruthy(); diff --git a/src/pages/details/details.test.tsx b/src/pages/details/details.test.tsx index 0d9f8d8a..a41efc6f 100644 --- a/src/pages/details/details.test.tsx +++ b/src/pages/details/details.test.tsx @@ -44,7 +44,7 @@ describe('Details', () => { }); test('should render successfully', async () => { - mock.onGet(new RegExp('/api/spacecraft/*')).reply(200, mockData.items[0]); + mock.onGet(new RegExp('/spacecraft/*')).reply(200, mockData.items[0]); jest.spyOn(useAuthMock, 'default').mockReturnValue({ isSignedIn: true, currentUserData: {} as User, @@ -61,7 +61,9 @@ describe('Details', () => { }); test('should render with error', async () => { - mock.onGet(new RegExp('/api/spacecraft/*')).networkError(); + mock + .onGet(new RegExp('/spacecraft/*')) + .reply(500, { message: 'Internal Server Error' }); const { baseElement } = render(componentWrapper); await act(async () => { expect(baseElement).toBeTruthy();