Skip to content

Commit

Permalink
Merge branch 'develop' into clue355/implement-menu-dropdown
Browse files Browse the repository at this point in the history
  • Loading branch information
Danielle254 authored Nov 19, 2024
2 parents 83680b5 + 491d856 commit 22393ba
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 111 deletions.
51 changes: 38 additions & 13 deletions app/(main)/login/page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(<Login />);

expect(screen.getByTestId('global-spinner')).toBeInTheDocument();
});

it('should not display GlobalSpinner once authentication is complete', async () => {
mockUseAuthContext.isSignedIn = false;

render(<Login />);

expect(screen.queryByTestId('global-spinner')).not.toBeInTheDocument();
});

it('should render the login page if the user is not logged in', () => {
mockUseAuthContext.isSignedIn = false;

render(<Login />);

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(<Login />);

const emailInput = screen.getByTestId('email');
const passwordInput = screen.getByTestId('password');
const form = screen.getByTestId('login-form');

await act(async () => {
Expand All @@ -80,16 +111,7 @@ describe('Login', () => {
});
});

it('redirects to /weeklyPicks when the button is clicked', () => {
mockUseAuthContext.isSignedIn = true;

render(<Login />);
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(() => {
Expand All @@ -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,
Expand All @@ -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,
Expand Down
197 changes: 102 additions & 95 deletions app/(main)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -51,16 +52,15 @@ type LoginUserSchemaType = z.infer<typeof LoginUserSchema>;
*/
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<boolean>(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<LoginUserSchemaType>({
resolver: zodResolver(LoginUserSchema),
Expand Down Expand Up @@ -99,98 +99,105 @@ const Login = (): React.JSX.Element => {
};

return (
<section className="grid xl:grid-cols-2 xl:grid-rows-none">
<div className="row-span-1 grid w-full place-items-center from-[#4E160E] to-zinc-950 bg-gradient-to-b xl:h-screen xl:bg-gradient-to-b">
<Logo className="mx-auto w-52 xl:w-64 xl:place-self-end" src={logo} />
<div className="mx-auto grid gap-4 place-self-end px-8 pb-8 text-foreground">
<p className="hidden leading-7 xl:block">
Thank you... fantasy football draft, for letting me know that even
in my fantasies, I am bad at sports.
</p>
<p className="hidden leading-7 xl:block">Jimmy Fallon</p>
<section className={`grid ${isSignedIn === null ? '' : 'xl:grid-cols-2'} xl:grid-rows-none`}>
{(isSignedIn === null || isSignedIn === true) &&
<GlobalSpinner data-testid="global-spinner" />
}
{isSignedIn === false &&
<>
<div className="row-span-1 grid w-full place-items-center from-[#4E160E] to-zinc-950 bg-gradient-to-b xl:h-screen xl:bg-gradient-to-b">
<Logo className="mx-auto w-52 xl:w-64 xl:place-self-end" src={logo} />
<div className="mx-auto grid gap-4 place-self-end px-8 pb-8 text-foreground">
<p className="hidden leading-7 xl:block">
Thank you... fantasy football draft, for letting me know that even
in my fantasies, I am bad at sports.
</p>
<p className="hidden leading-7 xl:block">Jimmy Fallon</p>
</div>
</div>
</div>
<div className="row-span-1 mx-auto grid max-w-sm justify-center space-y-4 px-4 xl:flex xl:flex-col">
<div>
<h1 className="text-5xl font-extrabold tracking-tight">
Join Gridiron Survivor
</h1>
<p className="pb-4 font-normal leading-7 text-muted-foreground">
Log in to your existing account or{' '}
<LinkCustom href="/register">sign up</LinkCustom> to get started
with a league
</p>
<div className="row-span-1 mx-auto grid max-w-sm justify-center space-y-4 px-4 xl:flex xl:flex-col">
<div>
<h1 className="text-5xl font-extrabold tracking-tight">
Join Gridiron Survivor
</h1>
<p className="pb-4 font-normal leading-7 text-muted-foreground">
Log in to your existing account or{' '}
<LinkCustom href="/register">sign up</LinkCustom> to get started
with a league
</p>
</div>
<Form {...form}>
<form
id="input-container"
className="grid gap-3"
data-testid="login-form"
onSubmit={form.handleSubmit(onSubmit)}
>
<FormField
control={form.control as Control<object>}
name="email"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
data-testid="email"
type="email"
placeholder="Email"
{...field}
/>
</FormControl>
{form.formState.errors?.email && (
<FormMessage>
{form.formState.errors.email.message}
</FormMessage>
)}
</FormItem>
)}
/>
<FormField
control={form.control as Control<object>}
name="password"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
data-testid="password"
type="password"
placeholder="Password"
{...field}
/>
</FormControl>
{form.formState.errors?.password && (
<FormMessage>
{form.formState.errors?.password.message}
</FormMessage>
)}
</FormItem>
)}
/>
<Button
data-testid="continue-button"
label={
isLoading ? (
<LoadingSpinner data-testid="loading-spinner" />
) : (
'Continue'
)
}
type="submit"
disabled={!email || !password || isLoading}
/>
<LinkCustom href="/register">
Sign up to get started with a league
</LinkCustom>
<LinkCustom href="/recover-password">
Forgot your password?
</LinkCustom>
</form>
</Form>
</div>
<Form {...form}>
<form
id="input-container"
className="grid gap-3"
data-testid="login-form"
onSubmit={form.handleSubmit(onSubmit)}
>
<FormField
control={form.control as Control<object>}
name="email"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
data-testid="email"
type="email"
placeholder="Email"
{...field}
/>
</FormControl>
{form.formState.errors?.email && (
<FormMessage>
{form.formState.errors.email.message}
</FormMessage>
)}
</FormItem>
)}
/>
<FormField
control={form.control as Control<object>}
name="password"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
data-testid="password"
type="password"
placeholder="Password"
{...field}
/>
</FormControl>
{form.formState.errors?.password && (
<FormMessage>
{form.formState.errors?.password.message}
</FormMessage>
)}
</FormItem>
)}
/>
<Button
data-testid="continue-button"
label={
isLoading ? (
<LoadingSpinner data-testid="loading-spinner" />
) : (
'Continue'
)
}
type="submit"
disabled={!email || !password || isLoading}
/>
<LinkCustom href="/register">
Sign up to get started with a league
</LinkCustom>
<LinkCustom href="/recover-password">
Forgot your password?
</LinkCustom>
</form>
</Form>
</div>
</>
}
</section>
);
};
Expand Down
5 changes: 3 additions & 2 deletions context/AuthContextProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type AuthContextType = {
getUser: () => Promise<IUser | undefined>;
login: (user: UserCredentials) => Promise<void | Error>; // eslint-disable-line no-unused-vars
logoutAccount: () => Promise<void | Error>;
isSignedIn: boolean;
isSignedIn: boolean | null;
};

export const AuthContext = createContext<AuthContextType | null>(null);
Expand All @@ -39,7 +39,7 @@ export const AuthContextProvider = ({
}: {
children: React.ReactNode;
}): JSX.Element => {
const [isSignedIn, setIsSignedIn] = useState<boolean>(false);
const [isSignedIn, setIsSignedIn] = useState<boolean | null>(null);
const { updateUser, resetUser, user } = useDataStore<DataStore>(
(state) => state,
);
Expand Down Expand Up @@ -86,6 +86,7 @@ export const AuthContextProvider = ({
*/
const getUser = async (): Promise<IUser | undefined> => {
if (!isSessionInLocalStorage()) {
setIsSignedIn(false);
if (isAuthRequiredPath(pathname)) {
router.push('/login');
}
Expand Down
2 changes: 1 addition & 1 deletion context/AuthHelper.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.share
export interface ILogoutType {
resetUser: React.Dispatch<React.SetStateAction<void>>;
router: AppRouterInstance;
setIsSignedIn: React.Dispatch<React.SetStateAction<boolean>>;
setIsSignedIn: React.Dispatch<React.SetStateAction<boolean | null>>;
}

0 comments on commit 22393ba

Please sign in to comment.