Skip to content

Commit

Permalink
Merge 9893f5a into 0f449d1
Browse files Browse the repository at this point in the history
  • Loading branch information
mattwr18 committed Jan 17, 2019
2 parents 0f449d1 + 9893f5a commit 8f07b6e
Show file tree
Hide file tree
Showing 14 changed files with 364 additions and 3 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"dependencies": {
"axios": "^0.18.0",
"global": "^4.3.2",
"izitoast": "^1.4.0",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-redux": "^6.0.0",
Expand Down
20 changes: 20 additions & 0 deletions src/actions/postSignUpInfoAction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import axios from 'axios'
import { POST_SIGNUP_INFO } from '../types'

export let getUser = user => ({ type: POST_SIGNUP_INFO, payload: user })

export let postSignUpInfo = props => dispatch => {
return axios({
method: 'post',
url: '/users',
data: {
user: {
email: props.email,
password: props.password,
password_confirmation: props.passwordConfirmation
}
}
}).then(response => {
dispatch(getUser(response.data))
})
}
17 changes: 17 additions & 0 deletions src/assets/SignUp.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.signup-h1 {
margin-top: 70px !important;
}

.signup-h4 {
margin-top: 150px !important;
}

.signup-form {
margin-top: 20px !important;
}

.signup-form .button {
background-color: #34495e !important;
color: white !important;
width: 50%;
}
6 changes: 6 additions & 0 deletions src/assets/iziToast.min.css

Large diffs are not rendered by default.

110 changes: 110 additions & 0 deletions src/containers/SignUp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import React, { Component, Fragment } from 'react'
import { Button, Form, Header, Grid, Checkbox } from 'semantic-ui-react'
import { connect } from 'react-redux'
import { postSignUpInfo } from '../actions/postSignUpInfoAction'
import iziToast from 'izitoast'
import '../assets/iziToast.min.css'
import '../assets/SignUp.scss'

export class SignUp extends Component {
state = {
email: '',
password: '',
passwordConfirmation: ''
};

handleChange = (e, { name, value }) => this.setState({ [name]: value });
handleSignUp = async e => {
const { email, password, passwordConfirmation } = this.state
e.preventDefault()
await this.props
.postSignUpInfo({ email, password, passwordConfirmation })
.then(() => {
this.props.history.push('/')
iziToast.show({
theme: 'light',
title: 'Success',
message: 'Welcome, take a look around',
position: 'topRight',
color: 'green',
backgroundColor: 'lime',
timeout: 3000,
balloon: true
})
})
.catch(() => {
iziToast.show({
theme: 'light',
title: 'Sorry',
message: 'This email already has an account, please sign in',
position: 'topRight',
color: 'red',
backgroundColor: 'lightcoral',
timeout: 3000,
balloon: true
})
})
};

render () {
const { password, email, passwordConfirmation } = this.state
return (
<Fragment>
<Header as='h1' textAlign='center' className='signup-h1'>
Sign Up
</Header>
<Header as='h4' textAlign='center' className='signup-h4'>
Already a member? <a href='/login'>Log In</a>
</Header>
<Header as='h4' textAlign='center'>
<a href='/users/password/new'>Forgot your password?</a>
</Header>
<Grid centered>
<Grid.Row>
<Grid.Column width={8}>
<Form
onSubmit={this.handleSignUp}
className='signup-form'
size='large'
>
<Form.Input
label='Email'
placeholder='Email'
name='email'
value={email}
onChange={this.handleChange}
/>
<Form.Input
label='Password'
placeholder='Password'
name='password'
value={password}
onChange={this.handleChange}
type='password'
/>
<Form.Input
label='Confirm Password'
placeholder='Repeat password'
name='passwordConfirmation'
value={passwordConfirmation}
onChange={this.handleChange}
type='password'
/>
<Form.Field>
<Checkbox label='I give permission for AV to send me occasional emails.' />
</Form.Field>
<Button type='submit'>Sign Up</Button>
</Form>
</Grid.Column>
</Grid.Row>
</Grid>
</Fragment>
)
}
}

const mapStateToProps = store => ({ users: store.users })
export default connect(
mapStateToProps,
{ postSignUpInfo }
)(SignUp)
32 changes: 32 additions & 0 deletions src/fixtures/signUp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export default [
{
id: 107,
email: 'newuser2@user.com',
created_at: '2019-01-13T13:10:59.803Z',
updated_at: '2019-01-13T13:10:59.803Z',
first_name: null,
last_name: null,
display_email: null,
youtube_id: null,
slug: 'newuser2',
display_profile: true,
latitude: null,
longitude: null,
country_name: null,
city: null,
region: null,
youtube_user_name: null,
github_profile_url: null,
display_hire_me: null,
bio: null,
receive_mailings: false,
country_code: null,
timezone_offset: null,
status_count: 0,
deleted_at: null,
event_participation_count: 0,
can_see_dashboard: false,
skill_list: [],
title_list: []
}
]
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Provider } from 'react-redux'
import store from './store'
import { Container } from 'semantic-ui-react'
import Homepage from './components/homepage/Homepage'
import SignUp from './containers/SignUp'
import './assets/semantic.css'
import axios from 'axios'
axios.defaults.baseURL = 'https://develop.websiteone.agileventures.org/'
Expand All @@ -17,6 +18,7 @@ render(
<Switch>
<Route path='/' exact component={Homepage} />
<Route path='/users' component={UsersList} />
<Route path='/signup' component={SignUp} />
</Switch>
</Container>
</Provider>
Expand Down
6 changes: 4 additions & 2 deletions src/reducers/usersReducer.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { GET_USERS } from '../types'
import { GET_USERS, POST_SIGNUP_INFO } from '../types'
import initialState from './initialState'

const usersReducer = (state = initialState.users, action) => {
switch (action.type) {
case GET_USERS:
return [ ...action.payload ]
return [...action.payload]
case POST_SIGNUP_INFO:
return [...action.payload]
default:
return state
}
Expand Down
43 changes: 43 additions & 0 deletions src/tests/actions/postSignUpInfoAction.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import moxios from 'moxios'
import thunk from 'redux-thunk'
import configureMockStore from 'redux-mock-store'
import { postSignUpInfo } from '../../actions/postSignUpInfoAction'
import { POST_SIGNUP_INFO } from '../../types'
import signUpResponse from '../../fixtures/signUp'

const middlewares = [thunk]
const mockStore = configureMockStore(middlewares)
let store

describe('postSignUpInfo action', () => {
beforeEach(() => {
moxios.install()
store = mockStore({})
})

afterEach(() => {
moxios.uninstall()
})

it('posts signup info to an external api', () => {
const expectedActions = [
{ type: POST_SIGNUP_INFO, payload: signUpResponse }
]
moxios.stubRequest('/users', {
status: 200,
response: signUpResponse
})

return store
.dispatch(
postSignUpInfo({
email: 'premium@premi.um',
password: 'premium123',
password_confirmation: 'premium123'
})
)
.then(() => {
expect(store.getActions()).toEqual(expectedActions)
})
})
})
105 changes: 105 additions & 0 deletions src/tests/containers/SignUp.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import React from 'react'
import { SignUp } from '../../containers/SignUp'
import { mount } from 'enzyme'

describe('SignUp', () => {
const existingUser = {
id: 1,
email: 'existing-user@example.com',
password: 'password'
}

const newUser = {
id: 2,
email: 'new-user@example.com',
password: 'password'
}

let wrapper
let props
beforeEach(() => {
props = {
history: { push: jest.fn() },
postSignUpInfo: jest.fn(
signUpInfo =>
new Promise((resolve, reject) => {
if (
signUpInfo.email !== existingUser.email &&
signUpInfo.password.length >= 8 &&
signUpInfo.passwordConfirmation === signUpInfo.password
) {
resolve(newUser)
} else {
reject(
new Error('This email already has an account, please sign in')
)
}
})
)
}
wrapper = mount(<SignUp {...props} />)
})

it('should call handleSignUp and postSignUpInfo when the form is submitted', () => {
const spy = jest.spyOn(wrapper.instance(), 'handleSignUp')
wrapper.instance().forceUpdate()
const emailInput = wrapper.find('input').filterWhere(item => {
return item.prop('name') === 'email'
})

const passwordInput = wrapper.find('input').filterWhere(item => {
return item.prop('name') === 'password'
})

const passwordConfirmationInput = wrapper
.find('input')
.filterWhere(item => {
return item.prop('name') === 'passwordConfirmation'
})

emailInput.simulate('change', {
target: { value: 'new-user@example.com' }
})

passwordInput.simulate('change', {
target: { value: 'password' }
})

passwordConfirmationInput.simulate('change', {
target: { value: 'password' }
})

const submitForm = wrapper.find('Form')
submitForm.simulate('submit')

expect(spy).toHaveBeenCalledTimes(1)
expect(props.postSignUpInfo).toBeCalledTimes(1)
})

it('returns the user if postSignUpInfo is called with the correct credentials', () => {
expect.assertions(1)
props
.postSignUpInfo({
email: 'new-user@example.com',
password: 'password',
passwordConfirmation: 'password'
})
.then(() => expect(props.history.push).toBeCalledTimes(1))
})

it('throws an error if postSignUpInfo is called with incorrect credentials', () => {
expect.assertions(1)
const submitForm = wrapper.find('Form')
submitForm.simulate('submit')
props
.postSignUpInfo({
email: 'existing-user@example.com',
password: ''
})
.catch(e =>
expect(e).toEqual(
Error('This email already has an account, please sign in')
)
)
})
})
8 changes: 7 additions & 1 deletion src/tests/reducers/usersReducer.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import usersReducer from '../../reducers/usersReducer'
import { GET_USERS } from '../../types'
import { GET_USERS, POST_SIGNUP_INFO } from '../../types'

describe('reduces users', () => {
it('defaults to empty projects if none are passed in', () => {
Expand All @@ -14,4 +14,10 @@ describe('reduces users', () => {
})
).toEqual(['User to be added to store'])
})
expect(
usersReducer([], {
type: POST_SIGNUP_INFO,
payload: ['User to be added to store']
})
).toEqual(['User to be added to store'])
})

0 comments on commit 8f07b6e

Please sign in to comment.