diff --git a/app/(main)/login/page.test.tsx b/app/(main)/login/page.test.tsx index 7ea58670..8c3dabc9 100644 --- a/app/(main)/login/page.test.tsx +++ b/app/(main)/login/page.test.tsx @@ -17,7 +17,13 @@ let continueButton: HTMLElement, emailInput: HTMLInputElement, passwordInput: HTMLInputElement; -const mockUseAuthContext = { +interface MockUseAuthContext { + getUser: jest.Mock; + isSignedIn: boolean | null; + login: jest.Mock; +} + +const mockUseAuthContext: MockUseAuthContext = { getUser, isSignedIn: false, login: mockLogin, @@ -46,20 +52,45 @@ describe('Login', () => { jest .spyOn(React, 'useState') .mockImplementation(() => [false, setIsLoading]); + }); + + it('should display GlobalSpinner while authenticating the user', async () => { + mockUseAuthContext.isSignedIn = null; + + render(); + + expect(screen.getByTestId('global-spinner')).toBeInTheDocument(); + }); + + it('should not display GlobalSpinner once authentication is complete', async () => { + mockUseAuthContext.isSignedIn = false; + + render(); + + expect(screen.queryByTestId('global-spinner')).not.toBeInTheDocument(); + }); + + it('should render the login page if the user is not logged in', () => { + mockUseAuthContext.isSignedIn = false; render(); continueButton = screen.getByTestId('continue-button'); emailInput = screen.getByTestId('email'); passwordInput = screen.getByTestId('password'); - }); - it('should render the login page', () => { + expect(continueButton).toBeInTheDocument(); expect(emailInput).toBeInTheDocument(); expect(passwordInput).toBeInTheDocument(); }); it('should update email and password fields and submit form', async () => { + mockUseAuthContext.isSignedIn = false; + + render(); + + const emailInput = screen.getByTestId('email'); + const passwordInput = screen.getByTestId('password'); const form = screen.getByTestId('login-form'); await act(async () => { @@ -80,16 +111,7 @@ describe('Login', () => { }); }); - it('redirects to /weeklyPicks when the button is clicked', () => { - mockUseAuthContext.isSignedIn = true; - - render(); - expect(mockUseAuthContext.getUser).toHaveBeenCalled(); - - mockUseAuthContext.isSignedIn = false; - }); - - it('redirects to /league/all when user navigates to /login', async () => { + it('redirects to /league/all when user navigates to /login and is logged in', async () => { mockUseAuthContext.isSignedIn = true; act(() => { @@ -106,6 +128,8 @@ describe('Login', () => { describe('Login loading spinner', () => { it('should show the loading spinner', async () => { + mockUseAuthContext.isSignedIn = false; + (useStateMock as jest.Mock).mockImplementation((init: boolean) => [ true, setIsLoading, @@ -117,6 +141,7 @@ describe('Login loading spinner', () => { expect(screen.queryByTestId('loading-spinner')).toBeInTheDocument(); }); }); + it('should not show the loading spinner', async () => { (useStateMock as jest.Mock).mockImplementation((init: boolean) => [ false, diff --git a/app/(main)/login/page.tsx b/app/(main)/login/page.tsx index c524d599..66b6fd9a 100644 --- a/app/(main)/login/page.tsx +++ b/app/(main)/login/page.tsx @@ -11,6 +11,7 @@ import { FormItem, FormMessage, } from '../../../components/Form/Form'; +import GlobalSpinner from '@/components/GlobalSpinner/GlobalSpinner'; import { Input } from '@/components/Input/Input'; import { useAuthContext } from '@/context/AuthContextProvider'; import { useRouter } from 'next/navigation'; @@ -51,16 +52,15 @@ type LoginUserSchemaType = z.infer; */ const Login = (): React.JSX.Element => { const router = useRouter(); - const { login, isSignedIn, getUser } = useAuthContext(); - const [isLoading, setIsLoading] = useState(false); + const { login, isSignedIn } = useAuthContext(); + const [isLoading, setIsLoading] = useState(false); useEffect(() => { - if (isSignedIn) { - getUser(); + if (isSignedIn === true) { router.push('/league/all'); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isSignedIn, getUser]); + }, [isSignedIn]); const form = useForm({ resolver: zodResolver(LoginUserSchema), @@ -99,98 +99,105 @@ const Login = (): React.JSX.Element => { }; return ( -
-
- -
-

- Thank you... fantasy football draft, for letting me know that even - in my fantasies, I am bad at sports. -

-

Jimmy Fallon

+
+ {(isSignedIn === null || isSignedIn === true) && + + } + {isSignedIn === false && + <> +
+ +
+

+ Thank you... fantasy football draft, for letting me know that even + in my fantasies, I am bad at sports. +

+

Jimmy Fallon

+
-
-
-
-

- Join Gridiron Survivor -

-

- Log in to your existing account or{' '} - sign up to get started - with a league -

+
+
+

+ Join Gridiron Survivor +

+

+ Log in to your existing account or{' '} + sign up to get started + with a league +

+
+
+ + } + name="email" + render={({ field }) => ( + + + + + {form.formState.errors?.email && ( + + {form.formState.errors.email.message} + + )} + + )} + /> + } + name="password" + render={({ field }) => ( + + + + + {form.formState.errors?.password && ( + + {form.formState.errors?.password.message} + + )} + + )} + /> +
-
- - } - name="email" - render={({ field }) => ( - - - - - {form.formState.errors?.email && ( - - {form.formState.errors.email.message} - - )} - - )} - /> - } - name="password" - render={({ field }) => ( - - - - - {form.formState.errors?.password && ( - - {form.formState.errors?.password.message} - - )} - - )} - /> -
+ + }
); }; diff --git a/context/AuthContextProvider.tsx b/context/AuthContextProvider.tsx index eb1adb9b..6e6964e5 100644 --- a/context/AuthContextProvider.tsx +++ b/context/AuthContextProvider.tsx @@ -23,7 +23,7 @@ type AuthContextType = { getUser: () => Promise; login: (user: UserCredentials) => Promise; // eslint-disable-line no-unused-vars logoutAccount: () => Promise; - isSignedIn: boolean; + isSignedIn: boolean | null; }; export const AuthContext = createContext(null); @@ -39,7 +39,7 @@ export const AuthContextProvider = ({ }: { children: React.ReactNode; }): JSX.Element => { - const [isSignedIn, setIsSignedIn] = useState(false); + const [isSignedIn, setIsSignedIn] = useState(null); const { updateUser, resetUser, user } = useDataStore( (state) => state, ); @@ -86,6 +86,7 @@ export const AuthContextProvider = ({ */ const getUser = async (): Promise => { if (!isSessionInLocalStorage()) { + setIsSignedIn(false); if (isAuthRequiredPath(pathname)) { router.push('/login'); } diff --git a/context/AuthHelper.interface.ts b/context/AuthHelper.interface.ts index 44791ed4..d62abed6 100644 --- a/context/AuthHelper.interface.ts +++ b/context/AuthHelper.interface.ts @@ -7,5 +7,5 @@ import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.share export interface ILogoutType { resetUser: React.Dispatch>; router: AppRouterInstance; - setIsSignedIn: React.Dispatch>; + setIsSignedIn: React.Dispatch>; }