Skip to content

Commit c323aba

Browse files
Merge pull request #111 from rootstrap/feat/sign-in
feat: sign in
2 parents b89999f + c5f0875 commit c323aba

File tree

5 files changed

+45
-50
lines changed

5 files changed

+45
-50
lines changed

src/api/auth/use-login.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import { createMutation } from 'react-query-kit';
33
import { client } from '../common';
44

55
type Variables = {
6-
email?: string; // optional because API doesn't require email
7-
username: string;
6+
email: string;
87
password: string;
98
expiresInMins?: number;
109
};
@@ -19,11 +18,13 @@ type Response = {
1918

2019
const login = async (variables: Variables) => {
2120
const { data } = await client({
22-
url: 'auth/login',
21+
url: '/v1/users/sign_in',
2322
method: 'POST',
2423
data: {
25-
username: variables.username,
26-
password: variables.password,
24+
user: {
25+
email: variables.email,
26+
password: variables.password,
27+
},
2728
},
2829
headers: {
2930
'Content-Type': 'application/json',

src/app/_layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ export default function RootLayout() {
3333
<Stack>
3434
<Stack.Screen name="(app)" options={{ headerShown: false }} />
3535
<Stack.Screen name="onboarding" options={{ headerShown: false }} />
36-
<Stack.Screen name="login" options={{ headerShown: false }} />
3736
<Stack.Screen name="forgot-password" />
37+
<Stack.Screen name="sign-in" options={{ headerShown: false }} />
3838
</Stack>
3939
</Providers>
4040
);

src/app/sign-in.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { useRouter } from 'expo-router';
2+
import { showMessage } from 'react-native-flash-message';
3+
4+
import { useLogin } from '@/api/auth/use-login';
5+
import type { LoginFormProps } from '@/components/login-form';
6+
import { LoginForm } from '@/components/login-form';
7+
import { useAuth } from '@/core';
8+
import { FocusAwareStatusBar } from '@/ui';
9+
10+
export default function Login() {
11+
const router = useRouter();
12+
const signIn = useAuth.use.signIn();
13+
const { mutate: login } = useLogin({
14+
onSuccess: (data) => {
15+
signIn({ access: data.accessToken, refresh: data.refreshToken });
16+
router.push('/');
17+
},
18+
onError: (error) => showMessage({ message: error.message, type: 'danger' }),
19+
});
20+
21+
const onSubmit: LoginFormProps['onSubmit'] = (data) => {
22+
login(data);
23+
};
24+
return (
25+
<>
26+
<FocusAwareStatusBar />
27+
<LoginForm onSubmit={onSubmit} />
28+
</>
29+
);
30+
}

src/components/login-form.test.tsx

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
import { cleanup, fireEvent, render, screen, waitFor } from '@/core/test-utils';
1+
import { cleanup, fireEvent, render, screen } from '@/core/test-utils';
22

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

65
afterEach(cleanup);
76

8-
const onSubmitMock: jest.Mock<LoginFormProps['onSubmit']> = jest.fn();
9-
107
describe('LoginForm Form ', () => {
118
const LOGIN_BUTTON = 'login-button';
129
it('renders correctly', async () => {
@@ -18,9 +15,9 @@ describe('LoginForm Form ', () => {
1815
render(<LoginForm />);
1916

2017
const button = screen.getByTestId(LOGIN_BUTTON);
21-
expect(screen.queryByText(/Username is required/i)).not.toBeOnTheScreen();
18+
expect(screen.queryByText(/Email is required/i)).not.toBeOnTheScreen();
2219
fireEvent.press(button);
23-
expect(await screen.findByText(/Username is required/i)).toBeOnTheScreen();
20+
expect(await screen.findByText(/Email is required/i)).toBeOnTheScreen();
2421
expect(screen.getByText(/Password is required/i)).toBeOnTheScreen();
2522
});
2623

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

3027
const button = screen.getByTestId(LOGIN_BUTTON);
3128
const emailInput = screen.getByTestId('email-input');
32-
const usernameInput = screen.getByTestId('username-input');
3329
const passwordInput = screen.getByTestId('password-input');
3430

3531
fireEvent.changeText(emailInput, 'yyyy');
36-
fireEvent.changeText(usernameInput, ' ');
3732
fireEvent.changeText(passwordInput, 'test');
3833
fireEvent.press(button);
3934

40-
expect(screen.queryByText(/Username is required/i)).not.toBeOnTheScreen();
35+
expect(screen.queryByText(/Email is required/i)).not.toBeOnTheScreen();
4136
expect(await screen.findByText(/Invalid Email Format/i)).toBeOnTheScreen();
4237
});
43-
44-
it('Should call LoginForm with correct values when values are valid', async () => {
45-
render(<LoginForm onSubmit={onSubmitMock} />);
46-
47-
const button = screen.getByTestId(LOGIN_BUTTON);
48-
const emailInput = screen.getByTestId('username-input');
49-
const passwordInput = screen.getByTestId('password-input');
50-
51-
fireEvent.changeText(emailInput, 'youssef');
52-
fireEvent.changeText(passwordInput, 'password');
53-
fireEvent.press(button);
54-
await waitFor(() => {
55-
expect(onSubmitMock).toHaveBeenCalledTimes(1);
56-
});
57-
// undefined because we don't use second argument of the SubmitHandler
58-
expect(onSubmitMock).toHaveBeenCalledWith(
59-
{
60-
email: undefined,
61-
username: 'youssef',
62-
password: 'password',
63-
},
64-
undefined,
65-
);
66-
});
6738
});

src/components/login-form.tsx

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ import { Button, ControlledInput, Text, View } from '@/ui';
88

99
const MIN_CHARS = 6;
1010
const schema = z.object({
11-
username: z.string({
12-
required_error: 'Username is required',
13-
}),
14-
email: z.string().email('Invalid email format').optional(),
11+
email: z
12+
.string({ required_error: 'Email is required' })
13+
.email('Invalid email format'),
1514
password: z
1615
.string({
1716
required_error: 'Password is required',
@@ -46,13 +45,7 @@ export const LoginForm = ({ onSubmit = () => {} }: LoginFormProps) => {
4645
autoComplete="email"
4746
control={control}
4847
name="email"
49-
label="Email (optional)"
50-
/>
51-
<ControlledInput
52-
testID="username-input"
53-
control={control}
54-
name="username"
55-
label="Username"
48+
label="Email"
5649
/>
5750
<ControlledInput
5851
testID="password-input"

0 commit comments

Comments
 (0)