Skip to content

Commit

Permalink
Step 13.6: Split AuthScreen into SignInForm and SignUpForm
Browse files Browse the repository at this point in the history
  • Loading branch information
Urigo committed May 20, 2020
1 parent b6d97e1 commit 5873de7
Show file tree
Hide file tree
Showing 6 changed files with 688 additions and 138 deletions.
161 changes: 161 additions & 0 deletions src/components/AuthScreen/SignInForm.test.tsx
@@ -0,0 +1,161 @@
import { createMemoryHistory } from 'history';
import React from 'react';
import { ApolloProvider } from '@apollo/react-hooks';
import {
act,
cleanup,
render,
fireEvent,
waitFor,
} from '@testing-library/react';
import SignInForm from './SignInForm';
import { SignInDocument } from '../../graphql/types';
import { mockApolloClient } from '../../test-helpers';

describe('SignInForm', () => {
afterEach(cleanup);

it('enables sign-in button when filled in', async () => {
const history = createMemoryHistory();
const client = mockApolloClient();

let getByTestId: any = null;

act(() => {
getByTestId = render(
<ApolloProvider client={client}>
<SignInForm history={history} />
</ApolloProvider>
).getByTestId;
});

const signInButton = await waitFor(
() => getByTestId('sign-in-button') as HTMLButtonElement
);
const usernameInput = await waitFor(() =>
getByTestId('username-input').querySelector('input')
);
const passwordInput = await waitFor(() =>
getByTestId('password-input').querySelector('input')
);

expect(signInButton.disabled).toEqual(true);

act(() => {
fireEvent.change(usernameInput, { target: { value: 'username' } });
fireEvent.change(passwordInput, { target: { value: 'password' } });
});

await waitFor(() => expect(signInButton.disabled).toEqual(false));
});

it('prints server error if input was wrong', async () => {
const history = createMemoryHistory();

const client = mockApolloClient([
{
request: {
query: SignInDocument,
variables: {
username: 'username',
password: 'password',
},
},
get result() {
throw Error('sign-in failed');
},
},
]);

let getByTestId: any = null;

act(() => {
getByTestId = render(
<ApolloProvider client={client}>
<SignInForm history={history} />
</ApolloProvider>
).getByTestId;
});

const signInButton = await waitFor(
() => getByTestId('sign-in-button') as HTMLButtonElement
);
const usernameInput = await waitFor(() =>
getByTestId('username-input').querySelector('input')
);
const passwordInput = await waitFor(() =>
getByTestId('password-input').querySelector('input')
);

act(() => {
fireEvent.change(usernameInput, { target: { value: 'username' } });
fireEvent.change(passwordInput, { target: { value: 'password' } });
});

await waitFor(() => expect(usernameInput.value).toEqual('username'));

await waitFor(() => expect(passwordInput.value).toEqual('password'));

act(() => {
fireEvent.click(signInButton);
});

const errorMessage = await waitFor(() => getByTestId('error-message'));

await waitFor(() =>
expect(errorMessage.innerHTML).toContain('sign-in failed')
);
});

it('navigates to /chats if everything went right', async () => {
const history = createMemoryHistory();

const client = mockApolloClient([
{
request: {
query: SignInDocument,
variables: {
username: 'username',
password: 'password',
},
},
result: { data: {} },
},
]);

let getByTestId: any = null;

act(() => {
getByTestId = render(
<ApolloProvider client={client}>
<SignInForm history={history} />
</ApolloProvider>
).getByTestId;
});

const usernameInput = await waitFor(() =>
getByTestId('username-input').querySelector('input')
);
const passwordInput = await waitFor(() =>
getByTestId('password-input').querySelector('input')
);
const signInButton = await waitFor(
() => getByTestId('sign-in-button') as HTMLButtonElement
);

act(() => {
fireEvent.change(usernameInput, { target: { value: 'username' } });
fireEvent.change(passwordInput, { target: { value: 'password' } });
});

await waitFor(() => expect(usernameInput.value).toEqual('username'));

await waitFor(() => expect(passwordInput.value).toEqual('password'));

act(() => {
fireEvent.click(signInButton);
});

await waitFor(() => expect(history.location.pathname).toEqual('/chats'));
});
});
83 changes: 83 additions & 0 deletions src/components/AuthScreen/SignInForm.tsx
@@ -0,0 +1,83 @@
import React from 'react';
import { useCallback, useState } from 'react';
import { useSignIn } from '../../services/auth.service';
import {
SignForm,
ActualForm,
Legend,
Section,
TextField,
Button,
ErrorMessage,
} from './form-components';
import { RouteComponentProps } from 'react-router-dom';

const SignInForm: React.FC<RouteComponentProps<any>> = ({ history }) => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const [signIn] = useSignIn();

const onUsernameChange = useCallback(({ target }) => {
setError('');
setUsername(target.value);
}, []);

const onPasswordChange = useCallback(({ target }) => {
setError('');
setPassword(target.value);
}, []);

const maySignIn = useCallback(() => {
return !!(username && password);
}, [username, password]);

const handleSignIn = useCallback(() => {
signIn({ variables: { username, password } })
.then(() => {
history.replace('/chats');
})
.catch((error) => {
setError(error.message || error);
});
}, [username, password, history, signIn]);

return (
<SignForm>
<ActualForm>
<Legend>Sign in</Legend>
<Section style={{ width: '100%' }}>
<TextField
data-testid="username-input"
label="Username"
value={username}
onChange={onUsernameChange}
margin="normal"
placeholder="Enter your username"
/>
<TextField
data-testid="password-input"
label="Password"
type="password"
value={password}
onChange={onPasswordChange}
margin="normal"
placeholder="Enter your password"
/>
</Section>
<Button
data-testid="sign-in-button"
type="button"
color="secondary"
variant="contained"
disabled={!maySignIn()}
onClick={handleSignIn}>
Sign in
</Button>
<ErrorMessage data-testid="error-message">{error}</ErrorMessage>
</ActualForm>
</SignForm>
);
};

export default SignInForm;

0 comments on commit 5873de7

Please sign in to comment.