Skip to content

Commit

Permalink
Merge 3fb3030 into 653ad9c
Browse files Browse the repository at this point in the history
  • Loading branch information
raymond42 committed Oct 12, 2019
2 parents 653ad9c + 3fb3030 commit eba829d
Show file tree
Hide file tree
Showing 22 changed files with 2,836 additions and 1,921 deletions.
3,977 changes: 2,095 additions & 1,882 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,20 @@
"jwt-decode": "^2.2.0",
"node-sass": "^4.12.0",
"querystring": "^0.2.0",
"lodash": "^4.17.15",
"moxios": "^0.4.0",
"prop-types": "^15.7.2",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"react-redux": "^7.1.1",
"react-redux-modal-flex": "^2.0.0",
"react-router-dom": "^5.0.1",
"react-scripts": "3.1.2",
"react-toastify": "^5.4.0",
"redux": "^4.0.4",
"redux-devtools-extension": "^2.13.8",
"redux-promise-middleware": "^6.1.1",
"reactstrap": "^8.0.1",
"redux-thunk": "^2.3.0",
"sass-loader": "^8.0.0",
"redux-mock-store": "^1.5.3"
Expand All @@ -41,11 +46,15 @@
"coveralls": "^3.0.6",
"cypress": "^3.4.1",
"eslint": "^6.1.0",
"@typescript-eslint/eslint-plugin": "^2.3.2",
"@typescript-eslint/parser": "^2.3.2",
"eslint-config-airbnb": "^18.0.1",
"eslint-config-airbnb-base": "^14.0.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-react": "^7.14.3",
"moxios": "^0.4.0",
"eslint-plugin-react-hooks": "^1.7.0",
"start-server-and-test": "^1.10.2"
},
"eslintConfig": {
Expand Down
34 changes: 21 additions & 13 deletions src/app/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,38 @@
import React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { ToastContainer, toast } from 'react-toastify';
import { Provider } from 'react-redux';
import 'react-toastify/dist/ReactToastify.css';
import './App.scss';
import store from './store/index';
import Home from '../feature/Home';
import Nav from './routes/Nav';
import SignUp from '../feature/auth/signup/SignUpComponent';
import Login from '../feature/auth/login/LoginComponent';
import ForgotPassword from '../feature/Reset Password/forgot password/ForgotPasswordComponent';
import ResetPassword from '../feature/Reset Password/reset password/ResetPasswordComponent';

toast.configure();

function App() {
return (
<div className="App">
<BrowserRouter>
<header className="App-header">
<Nav />
</header>
<ToastContainer />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/login" component={Login} />
<Route path="/Signup" component={SignUp} />
</Switch>
</BrowserRouter>
</div>
<Provider store={store}>
<div className="App">
<BrowserRouter>
<header className="App-header">
<Nav />
</header>
<ToastContainer />
<Switch>
<Route path="/forgot" component={ForgotPassword} />
<Route path="/users/reset-password/:token" component={ResetPassword} />
<Route exact path="/" component={Home} />
<Route path="/login" component={Login} />
<Route path="/Signup" component={SignUp} />
</Switch>
</BrowserRouter>
</div>
</Provider>
);
}

Expand Down
6 changes: 5 additions & 1 deletion src/app/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ import { combineReducers } from 'redux';
import loginReducer from '../../feature/auth/login/LoginReducer';
import logoutReducer from '../../feature/auth/logout/LogoutReducer';
import SignUpReducer from '../../feature/auth/signup/SignUpReducer';
import forgotPasswordReducer from '../../feature/Reset Password/forgot password/forgotPasswordReducers';
import resetPasswordReducer from '../../feature/Reset Password/reset password/resetPasswordReducer';

export default combineReducers({
login: loginReducer,
logout: logoutReducer,
signup: SignUpReducer
signup: SignUpReducer,
forgotPassword: forgotPasswordReducer,
resetPassword: resetPasswordReducer
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/* eslint-disable no-shadow */
import React, { Component } from 'react';
import { connect } from 'react-redux';
import forgotPasswordAction from './forgotPasswordAction';
import '../resetPassword.scss';

export class ForgotPassword extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
};
}

onChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
}

onSubmit = (e) => {
e.preventDefault();
const { email } = this.state;
const { forgotPasswordAction } = this.props;
forgotPasswordAction(email);
}

render() {
const { email } = this.state;
return (
<div className="form">
<br />
<div className="reset">
<h1>
Reset Password
</h1>
<p>
Please provide your email address. You will receive a link to reset your password.
</p>
</div>
<form onSubmit={this.onSubmit}>
<div className="inputForm">
<input id="email" type="email" name="email" placeholder="Email" onChange={this.onChange} value={email} />
</div>
<div className="formButton">
<button type="submit" className="send">Send</button>
</div>
</form>
</div>
);
}
}

const mapStateToProps = state => ({
email: state.email,
});
export default connect(mapStateToProps, { forgotPasswordAction })(ForgotPassword);
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import moxios from 'moxios';
import forgotPasswordAction from '../forgotPasswordAction';

const email = 'raymond@gmail.com';
const middleware = [thunk];

const mockStore = configureStore(middleware);

const store = mockStore({});

describe('Forgot password', () => {
beforeEach(() => {
moxios.install();
});
afterEach(() => {
moxios.uninstall();
store.clearActions();
});

it('should send a reset password link', async () => {
moxios.wait(() => {
const resetPasswordLink = moxios.requests.mostRecent();
resetPasswordLink.respondWith({
status: 200,
response: {
data: {
message: 'Email sent, please check your email'
}
}
});
});
return store.dispatch(forgotPasswordAction(email)).then(() => {
expect(store.getActions().length).toEqual(1);
});
});
it('should throw an error when an email sent is an invalid', () => {
moxios.wait(() => {
const resetPasswordLink = moxios.requests.mostRecent();
resetPasswordLink.respondWith({
status: 404,
response: {
data: {
error: `User with email: ${email} not found..`
}
}
});
});
return store.dispatch(forgotPasswordAction('fddsfa@fdvdf.com')).then(() => {
expect(store.getActions().length).toEqual(1);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* eslint-disable react/jsx-props-no-spreading */
import React from 'react';
import { shallow } from 'enzyme';
import { ForgotPassword } from '../ForgotPasswordComponent';

const forgotPassword = args => {
const initialProps = {
email: '',
forgotPasswordAction: () => {},
handleSubmit: () => {}
};
const props = { ...initialProps, ...args };
return shallow(<ForgotPassword {...props} />);
};

let wrapper;

beforeAll(() => {
wrapper = forgotPassword();
wrapper.instance().onChange = jest.fn();
wrapper.instance().onChange();
});

describe('Input tests', () => {
it('Should type in the Email field', () => {
const Email = wrapper.find('input[name="email"]');
Email.simulate('change', {
target: { value: 'raymond@gmail.com', name: 'email' },
});
expect(wrapper.state('email')).toEqual('raymond@gmail.com');
});
});

describe('Send button test', () => {
let instance;
let submitButton;
beforeAll(() => {
instance = wrapper.instance();
submitButton = wrapper.find('.send');
submitButton.simulate('click');
});
it('Should make a request to the server', () => {
instance.forceUpdate();
wrapper.update();
const event = {
preventDefault: jest.fn(),
};
instance.onSubmit(event);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* eslint-disable import/named */
import forgotPasswordReducers, { initialState } from '../forgotPasswordReducers';
import { FORGOT_PASSWORD_SUCCESS, FORGOT_PASSWORD_FAIL } from '../forgotPasswordActionTypes';

describe('forgot password reducer', () => {
test('EMAIL_SENT_SUCCESSFULLY', () => {
const reducer = forgotPasswordReducers(initialState, {
type: FORGOT_PASSWORD_SUCCESS,
message: { payload: { sendEmail: 'raymond@gmail.com' } }
});
expect(reducer).toHaveProperty('message');
});
test('EMAIL_FAILED_TO_BE_SENT', () => {
const reducer = forgotPasswordReducers(initialState, {
type: FORGOT_PASSWORD_FAIL,
message: { payload: { sendEmail: 'raymondsdf' } }
});
expect(reducer).toHaveProperty('message');
});
});
29 changes: 29 additions & 0 deletions src/feature/Reset Password/forgot password/forgotPasswordAction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import axios from 'axios';
import { toast } from 'react-toastify';
import { BACKEND_URL } from '../../../app/common/config/appConfig';
import { FORGOT_PASSWORD_SUCCESS, FORGOT_PASSWORD_FAIL } from './forgotPasswordActionTypes';

const forgotPasswordAction = (email) => async (dispatch) => {
const userMessage = {
email,
};
try {
const res = await axios.post(
`${BACKEND_URL}/users/send-email`,
userMessage,
);
const { message } = res.data;
toast.success(message, { position: toast.POSITION.TOP_CENTER });
dispatch({
type: FORGOT_PASSWORD_SUCCESS,
payload: res.data,

});
} catch (error) {
const erroMessage = (await error.response) ? error.response.data.error : 'Network Error';
toast.error(erroMessage, { position: toast.POSITION.TOP_CENTER });
dispatch({ type: FORGOT_PASSWORD_FAIL, payload: erroMessage });
}
};

export default forgotPasswordAction;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const FORGOT_PASSWORD_SUCCESS = 'FORGOT_PASSWORD_SUCCESS';
export const FORGOT_PASSWORD_FAIL = 'FORGOT_PASSWORD_FAIL';
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { FORGOT_PASSWORD_SUCCESS, FORGOT_PASSWORD_FAIL } from './forgotPasswordActionTypes';

const initialState = {
message: '',
};
export default function (state = initialState, action) {
const { type, payload } = action;
switch (type) {
case FORGOT_PASSWORD_SUCCESS:
return {
...state,
message: payload,
};
case FORGOT_PASSWORD_FAIL:
return {
...state,
message: payload,
};
default:
return state;
}
}
Loading

0 comments on commit eba829d

Please sign in to comment.