Skip to content

Commit

Permalink
Merge e78ef11 into 7fefa84
Browse files Browse the repository at this point in the history
  • Loading branch information
akhilome committed Jan 20, 2019
2 parents 7fefa84 + e78ef11 commit 7137836
Show file tree
Hide file tree
Showing 9 changed files with 328 additions and 31 deletions.
21 changes: 21 additions & 0 deletions src/actions/index.js
Expand Up @@ -33,6 +33,27 @@ export const signUpUser = userData => async (dispatch) => {
}
};

export const logInUser = userData => async (dispatch) => {
dispatch(startFetching());
try {
const { status, user } = (await axios.post('/auth/login', userData)).data;
if (status === 'success') saveToken(user.auth_token);
const { userName: name, userStatus: role } = jwt.decode(user.auth_token);
dispatch({
type: actionTypes.LOG_IN,
payload: {
name,
role,
},
});
return dispatch(stopFetching());
} catch (error) {
return dispatch(
stopFetching(false, error.response ? error.response.data.message : 'something went wrong'),
);
}
};

export const checkAuthStatus = () => {
try {
const { userName: name, userStatus: role } = jwt.decode(getToken());
Expand Down
94 changes: 85 additions & 9 deletions src/components/LoginPage.jsx
@@ -1,12 +1,88 @@
import React from 'react';
import React, { Component, Fragment } from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';

const LoginPage = () => (
<div>
This is the login page.
<br />
<Link to="/">Go Home</Link>
</div>
);
import Nav from './Nav';
import { logInUser } from '../actions';

export default LoginPage;
export class Login extends Component {
state = { email: '', password: '' };

componentDidMount() {
const { isLoggedIn, fetching, history } = this.props;
if (!fetching && isLoggedIn) history.push('/menu');
}

onFormSubmit = async (e) => {
e.preventDefault();
const { email, password } = this.state;
const { logInUser: logIn } = this.props;

await logIn({ email, password });
const { isLoggedIn, fetching, history } = this.props;
if (!fetching && isLoggedIn) history.push('/menu');
};

render() {
const { email, password } = this.state;
return (
<Fragment>
<Nav />
<div className="wrapper auth">
<section className="container">
<h2>Access Your Account</h2>
<form onSubmit={this.onFormSubmit}>
<input
type="email"
onChange={e => this.setState({ email: e.target.value })}
value={email}
name="email"
placeholder="Your Email Address"
required
/>
<input
type="password"
onChange={e => this.setState({ password: e.target.value })}
value={password}
name="password"
placeholder="Your Password"
required
/>
<input type="submit" name="submit" value="Log In" className="btn-primary" />
</form>
<div>
<p>
No account yet?
{' '}
<Link to="/signup">Sign Up</Link>
</p>
</div>
</section>
</div>
</Fragment>
);
}
}

Login.propTypes = {
logInUser: PropTypes.func.isRequired,
isLoggedIn: PropTypes.bool,
fetching: PropTypes.bool,
history: PropTypes.oneOfType([PropTypes.object, PropTypes.func]).isRequired,
};

Login.defaultProps = {
fetching: false,
isLoggedIn: false,
};

const mapStateToProps = state => ({
isLoggedIn: state.user.isLoggedIn,
fetching: state.fetching.fetching,
});

export default connect(
mapStateToProps,
{ logInUser },
)(Login);
9 changes: 5 additions & 4 deletions src/reducers/authReducer.js
@@ -1,4 +1,4 @@
import actionsTypes from '../actions/types';
import types from '../actions/types';

const initialState = {
isLoggedIn: null,
Expand All @@ -8,15 +8,16 @@ const initialState = {

export default (state = initialState, action) => {
switch (action.type) {
case actionsTypes.SIGN_UP:
case actionsTypes.CHECK_AUTH_STATUS:
case types.SIGN_UP:
case types.LOG_IN:
case types.CHECK_AUTH_STATUS:
return {
...state,
isLoggedIn: true,
name: action.payload.name,
role: action.payload.role,
};
case actionsTypes.CHECK_AUTH_STATUS_FAIL:
case types.CHECK_AUTH_STATUS_FAIL:
default:
return state;
}
Expand Down
36 changes: 36 additions & 0 deletions src/tests/actions/logInUser.test.js
@@ -0,0 +1,36 @@
import axios from '../../services/axios';
import { logInUser } from '../../actions';
import jwt from '../../utils/jwt';

describe('logInUser()', () => {
afterEach(() => jest.resetAllMocks());

const dispatch = jest.fn();
const response = { data: { status: 'success', user: { token: 'something' } } };
jest
.spyOn(jwt, 'decode')
.mockImplementation(() => ({ userName: 'jamjum', userStatus: 'customer' }));

it('should login user successfully', async () => {
jest.spyOn(axios, 'post').mockImplementation(() => Promise.resolve(response));
await logInUser()(dispatch);
expect(dispatch).toHaveBeenCalledTimes(3);
expect(dispatch.mock.calls[1][0]).toEqual({
payload: { name: 'jamjum', role: 'customer' },
type: 'LOG_IN',
});
});

it('should fail to login user if errors exist', async () => {
jest
.spyOn(axios, 'post')
.mockImplementation(() => Promise.reject(new Error('something went wrong')));

await logInUser()(dispatch);
expect(dispatch).toHaveBeenCalledTimes(2);
expect(dispatch).toHaveBeenLastCalledWith({
payload: { error: true, message: 'something went wrong' },
type: 'STOP_FETCHING',
});
});
});
49 changes: 47 additions & 2 deletions src/tests/components/LoginPage.test.jsx
@@ -1,10 +1,55 @@
import React from 'react';
import { shallow } from 'enzyme';
import LoginPage from '../../components/LoginPage';

import { Login } from '../../components/LoginPage';
import { mountWrap } from '../helpers/contextWrapper';
import { updateInput } from '../helpers';

describe('LoginPage', () => {
const props = {
logInUser: jest.fn(),
history: { push: jest.fn() },
};

it('should render LoginPage correctly', () => {
const wrapper = shallow(<LoginPage />);
const wrapper = mountWrap(<Login {...props} />);
expect(wrapper).toMatchSnapshot();
wrapper.unmount();
});

it('should allow user to fill login form', () => {
const wrapper = shallow(<Login {...props} />);
const form = wrapper.find('form');
updateInput(form, 'input[name="email"]', 'an@email.address');
updateInput(form, 'input[name="password"]', 'secretpass');

expect(wrapper.state('email')).toEqual('an@email.address');
expect(wrapper.state('password')).toEqual('secretpass');
});

it('should correctly submit login form with user details', () => {
const wrapper = shallow(<Login {...props} />);
wrapper.find('form').simulate('submit', { preventDefault: () => undefined });

expect(props.logInUser).toHaveBeenCalled();
});

it('should redirect user after login', () => {
const wrapper = shallow(<Login {...{ ...props, isLoggedIn: true, fetching: false }} />);
const form = wrapper.find('form');
updateInput(form, 'input[name="email"]', 'an@email.address');
updateInput(form, 'input[name="password"]', 'secretpass');

form.simulate('submit', { preventDefault: () => undefined });
expect(props.logInUser).toHaveBeenCalledWith({
email: 'an@email.address',
password: 'secretpass',
});
expect(props.history.push).toHaveBeenCalled();
});

it('should redirect user already logged in', () => {
mountWrap(<Login {...{ ...props, isLoggedIn: true, fetching: false }} />);
expect(props.history.push).toHaveBeenCalled();
});
});
9 changes: 2 additions & 7 deletions src/tests/components/SignupPage.test.jsx
@@ -1,14 +1,9 @@
import React from 'react';
import { shallow } from 'enzyme';

import { SignupPage } from '../../components/SignupPage';
import { mountWrap } from '../helpers/contextWrapper';

const updateInput = (wrapper, instance, newValue) => {
const input = wrapper.find(instance);
input.simulate('change', {
target: { value: newValue },
});
};
import { updateInput } from '../helpers';

describe('SignupPage Component', () => {
const props = {
Expand Down
126 changes: 117 additions & 9 deletions src/tests/components/__snapshots__/LoginPage.test.jsx.snap
@@ -1,14 +1,122 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`LoginPage should render LoginPage correctly 1`] = `
<div>
This is the login page.
<br />
<Link
replace={false}
to="/"
<Login
fetching={false}
history={
Object {
"push": [MockFunction],
}
}
isLoggedIn={false}
logInUser={[MockFunction]}
>
<Nav>
<header
className="transparent"
>
<div
className="site-title"
>
<h2>
<Link
replace={false}
to="/"
>
<a
href="/"
onClick={[Function]}
>
Kiakia Food
</a>
</Link>
</h2>
</div>
<nav>
<ul>
<li>
<Link
replace={false}
to="/login"
>
<a
href="/login"
onClick={[Function]}
>
Log In
</a>
</Link>
</li>
<li>
<Link
replace={false}
to="/signup"
>
<a
href="/signup"
onClick={[Function]}
>
Sign Up
</a>
</Link>
</li>
</ul>
</nav>
</header>
</Nav>
<div
className="wrapper auth"
>
Go Home
</Link>
</div>
<section
className="container"
>
<h2>
Access Your Account
</h2>
<form
onSubmit={[Function]}
>
<input
name="email"
onChange={[Function]}
placeholder="Your Email Address"
required={true}
type="email"
value=""
/>
<input
name="password"
onChange={[Function]}
placeholder="Your Password"
required={true}
type="password"
value=""
/>
<input
className="btn-primary"
name="submit"
type="submit"
value="Log In"
/>
</form>
<div>
<p>
No account yet?
<Link
replace={false}
to="/signup"
>
<a
href="/signup"
onClick={[Function]}
>
Sign Up
</a>
</Link>
</p>
</div>
</section>
</div>
</Login>
`;

0 comments on commit 7137836

Please sign in to comment.