Skip to content

Commit

Permalink
Merge pull request #111 from rootstrap/feat/sign-in
Browse files Browse the repository at this point in the history
feat: sign in
  • Loading branch information
guillermomachado authored Jan 10, 2025
2 parents b89999f + c5f0875 commit c323aba
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 50 deletions.
11 changes: 6 additions & 5 deletions src/api/auth/use-login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { createMutation } from 'react-query-kit';
import { client } from '../common';

type Variables = {
email?: string; // optional because API doesn't require email
username: string;
email: string;
password: string;
expiresInMins?: number;
};
Expand All @@ -19,11 +18,13 @@ type Response = {

const login = async (variables: Variables) => {
const { data } = await client({
url: 'auth/login',
url: '/v1/users/sign_in',
method: 'POST',
data: {
username: variables.username,
password: variables.password,
user: {
email: variables.email,
password: variables.password,
},
},
headers: {
'Content-Type': 'application/json',
Expand Down
2 changes: 1 addition & 1 deletion src/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ export default function RootLayout() {
<Stack>
<Stack.Screen name="(app)" options={{ headerShown: false }} />
<Stack.Screen name="onboarding" options={{ headerShown: false }} />
<Stack.Screen name="login" options={{ headerShown: false }} />
<Stack.Screen name="forgot-password" />
<Stack.Screen name="sign-in" options={{ headerShown: false }} />
</Stack>
</Providers>
);
Expand Down
30 changes: 30 additions & 0 deletions src/app/sign-in.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useRouter } from 'expo-router';
import { showMessage } from 'react-native-flash-message';

import { useLogin } from '@/api/auth/use-login';
import type { LoginFormProps } from '@/components/login-form';
import { LoginForm } from '@/components/login-form';
import { useAuth } from '@/core';
import { FocusAwareStatusBar } from '@/ui';

export default function Login() {
const router = useRouter();
const signIn = useAuth.use.signIn();
const { mutate: login } = useLogin({
onSuccess: (data) => {
signIn({ access: data.accessToken, refresh: data.refreshToken });
router.push('/');
},
onError: (error) => showMessage({ message: error.message, type: 'danger' }),
});

const onSubmit: LoginFormProps['onSubmit'] = (data) => {
login(data);
};
return (
<>
<FocusAwareStatusBar />
<LoginForm onSubmit={onSubmit} />
</>
);
}
37 changes: 4 additions & 33 deletions src/components/login-form.test.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { cleanup, fireEvent, render, screen, waitFor } from '@/core/test-utils';
import { cleanup, fireEvent, render, screen } from '@/core/test-utils';

import type { LoginFormProps } from './login-form';
import { LoginForm } from './login-form';

afterEach(cleanup);

const onSubmitMock: jest.Mock<LoginFormProps['onSubmit']> = jest.fn();

describe('LoginForm Form ', () => {
const LOGIN_BUTTON = 'login-button';
it('renders correctly', async () => {
Expand All @@ -18,9 +15,9 @@ describe('LoginForm Form ', () => {
render(<LoginForm />);

const button = screen.getByTestId(LOGIN_BUTTON);
expect(screen.queryByText(/Username is required/i)).not.toBeOnTheScreen();
expect(screen.queryByText(/Email is required/i)).not.toBeOnTheScreen();
fireEvent.press(button);
expect(await screen.findByText(/Username is required/i)).toBeOnTheScreen();
expect(await screen.findByText(/Email is required/i)).toBeOnTheScreen();
expect(screen.getByText(/Password is required/i)).toBeOnTheScreen();
});

Expand All @@ -29,39 +26,13 @@ describe('LoginForm Form ', () => {

const button = screen.getByTestId(LOGIN_BUTTON);
const emailInput = screen.getByTestId('email-input');
const usernameInput = screen.getByTestId('username-input');
const passwordInput = screen.getByTestId('password-input');

fireEvent.changeText(emailInput, 'yyyy');
fireEvent.changeText(usernameInput, ' ');
fireEvent.changeText(passwordInput, 'test');
fireEvent.press(button);

expect(screen.queryByText(/Username is required/i)).not.toBeOnTheScreen();
expect(screen.queryByText(/Email is required/i)).not.toBeOnTheScreen();
expect(await screen.findByText(/Invalid Email Format/i)).toBeOnTheScreen();
});

it('Should call LoginForm with correct values when values are valid', async () => {
render(<LoginForm onSubmit={onSubmitMock} />);

const button = screen.getByTestId(LOGIN_BUTTON);
const emailInput = screen.getByTestId('username-input');
const passwordInput = screen.getByTestId('password-input');

fireEvent.changeText(emailInput, 'youssef');
fireEvent.changeText(passwordInput, 'password');
fireEvent.press(button);
await waitFor(() => {
expect(onSubmitMock).toHaveBeenCalledTimes(1);
});
// undefined because we don't use second argument of the SubmitHandler
expect(onSubmitMock).toHaveBeenCalledWith(
{
email: undefined,
username: 'youssef',
password: 'password',
},
undefined,
);
});
});
15 changes: 4 additions & 11 deletions src/components/login-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ import { Button, ControlledInput, Text, View } from '@/ui';

const MIN_CHARS = 6;
const schema = z.object({
username: z.string({
required_error: 'Username is required',
}),
email: z.string().email('Invalid email format').optional(),
email: z
.string({ required_error: 'Email is required' })
.email('Invalid email format'),
password: z
.string({
required_error: 'Password is required',
Expand Down Expand Up @@ -46,13 +45,7 @@ export const LoginForm = ({ onSubmit = () => {} }: LoginFormProps) => {
autoComplete="email"
control={control}
name="email"
label="Email (optional)"
/>
<ControlledInput
testID="username-input"
control={control}
name="username"
label="Username"
label="Email"
/>
<ControlledInput
testID="password-input"
Expand Down

0 comments on commit c323aba

Please sign in to comment.