Skip to content

Commit

Permalink
Add Friends to Groups
Browse files Browse the repository at this point in the history
  • Loading branch information
lnhrdt committed Oct 30, 2017
1 parent e23cf3e commit e68e519
Show file tree
Hide file tree
Showing 94 changed files with 1,452 additions and 389 deletions.
1 change: 1 addition & 0 deletions kaapi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"react": "16.0.0",
"react-dom": "16.0.0",
"react-redux": "5.0.6",
"react-router-dom": "4.2.2",
"redux": "3.7.2",
"redux-thunk": "2.2.0"
},
Expand Down
9 changes: 8 additions & 1 deletion kaapi/src/App.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import React from 'react'
import {BrowserRouter as Router, Route} from 'react-router-dom'
import {Provider} from 'react-redux'
import store from './store'
import Page from './layout/Page'
import FriendListPage from './friends/components/FriendListPage'
import GroupChoosePage from './groups/components/GroupChoosePage'

export default () => (
<Provider store={store}>
<Page><FriendListPage/></Page>
<Router>
<Page>
<Route exact path="/" component={GroupChoosePage}/>
<Route path="/group/:groupId" component={FriendListPage}/>
</Page>
</Router>
</Provider>
)
40 changes: 40 additions & 0 deletions kaapi/src/forms/components/SingleFieldFormContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react'

export default (WrappedComponent) => {

return class extends React.Component {

constructor(props) {
super(props)
this.state = {
submitting: false,
value: '',
error: undefined
}
}

handleSubmit = () => {
this.setState({submitting: true})
return this.props.submit(this.state.value)
.then(() => {
this.setState({submitting: false, value: ''})
})
.catch(errors => {
this.setState({submitting: false, error: errors && errors.name})
})
}

handleValueChange = (event) => {
this.setState({value: event.target.value, error: undefined})
}

render() {
return <WrappedComponent submit={this.handleSubmit}
valueChange={this.handleValueChange}
value={this.state.value}
submitting={this.state.submitting}
error={this.state.error}
/>
}
}
}
106 changes: 106 additions & 0 deletions kaapi/src/forms/components/SingleFieldFormContainer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import {renderDecorator} from '../../support/testRender'
import FriendAdderContainer from './SingleFieldFormContainer'

beforeEach(() => jest.resetAllMocks())

describe('SingleFieldFormContainer', () => {

const mockProps = {submit: jest.fn()}

describe('when component loads', () => {
const {subject, mockWrappedComponent} = renderDecorator(FriendAdderContainer, mockProps)

it('should pass submitting=false', () => {
expect(subject.find(mockWrappedComponent).props().submitting).toEqual(false)
})

it('should pass value=""', () => {
expect(subject.find(mockWrappedComponent).props().value).toEqual('')
})

it('should pass error=undefined', () => {
expect(subject.find(mockWrappedComponent).props().error).toBeUndefined()
})

describe('when valueChange is called', () => {
beforeEach(() => {
subject.find(mockWrappedComponent).props().valueChange({target: {value: 'test-value-1'}})
subject.update()
})

it('should update value with the valued passed', () => {
expect(subject.find(mockWrappedComponent).props().value).toEqual('test-value-1')
})

describe('when submit is called', () => {
it('should pass submitting=true', () => {
mockProps.submit.mockReturnValue(Promise.resolve())
subject.find(mockWrappedComponent).props().submit()
subject.update()
expect(subject.find(mockWrappedComponent).props().submitting).toEqual(true)
})

it('should call submit with the value of value', () => {
mockProps.submit.mockReturnValue(Promise.resolve())
subject.find(mockWrappedComponent).props().submit()
expect(mockProps.submit).toHaveBeenCalledWith('test-value-1')
})

describe('when the submit call completes successfully', () => {
it('should pass submitting=false', () => {
mockProps.submit.mockReturnValue(Promise.resolve())
return subject.find(mockWrappedComponent).props().submit().then(() => {
subject.update()
expect(subject.find(mockWrappedComponent).props().submitting).toEqual(false)
})
})

it('should pass value=""', () => {
mockProps.submit.mockReturnValue(Promise.resolve())
return subject.find(mockWrappedComponent).props().submit().then(() => {
subject.update()
expect(subject.find(mockWrappedComponent).props().value).toEqual('')
})
})
})

describe('when the submit call completes unsuccessfully', () => {
it('should pass submitting=false', () => {
mockProps.submit.mockReturnValue(Promise.reject({name: 'It did not work.'}))
return subject.find(mockWrappedComponent).props().submit().then(() => {
subject.update()
expect(subject.find(mockWrappedComponent).props().submitting).toEqual(false)
})
})

it('should not clear value', () => {
mockProps.submit.mockReturnValue(Promise.reject({name: 'It did not work.'}))
return subject.find(mockWrappedComponent).props().submit().then(() => {
subject.update()
expect(subject.find(mockWrappedComponent).props().value).toEqual('test-value-1')
})
})

it('should pass the error', () => {
mockProps.submit.mockReturnValue(Promise.reject({name: 'It did not work.'}))
return subject.find(mockWrappedComponent).props().submit().then(() => {
subject.update()
expect(subject.find(mockWrappedComponent).props().error).toEqual('It did not work.')
})
})

describe('when valueChange is called', () => {
beforeEach(() => {
subject.find(mockWrappedComponent).props().valueChange({target: {value: 'test-value-2'}})
subject.update()
})

it('should clear the error', () => {
expect(subject.find(mockWrappedComponent).props().error).toBeUndefined()
})
})
})
})
})
})
})
4 changes: 2 additions & 2 deletions kaapi/src/friends/actions/friendAdd.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import friendCreate from '../api/friendCreate'
import friendsLoad from './friendsLoad'

export default (name) => (dispatch) => {
return friendCreate({name}).then(() => dispatch(friendsLoad()))
export default (friend) => (dispatch) => {
return friendCreate(friend).then(() => dispatch(friendsLoad(friend.groupId)))
}
6 changes: 4 additions & 2 deletions kaapi/src/friends/actions/friendAdd.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ describe('friendAdd', () => {
friendsLoad.mockReturnValueOnce('mock friendsLoad')
const mockDispatch = jest.fn()

return friendAdd('Johnathon Britz')(mockDispatch).then(() => {
expect(friendCreate).toBeCalledWith({name: 'Johnathon Britz'})
const request = {name: 'Johnathon Britz', groupId: 'abc123'}
return friendAdd(request)(mockDispatch).then(() => {
expect(friendCreate).toBeCalledWith(request)
expect(friendsLoad).toBeCalledWith('abc123')
expect(mockDispatch).toBeCalledWith('mock friendsLoad')
})
})
Expand Down
4 changes: 2 additions & 2 deletions kaapi/src/friends/actions/friendsLoad.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import friendsReceived from './friendsReceived'
import friendsGet from '../api/friendsGet'

export default () => (dispatch) => {
return friendsGet().then(response => dispatch(friendsReceived(response)))
export default (groupId) => (dispatch) => {
return friendsGet(groupId).then(friends => dispatch(friendsReceived({friends, groupId})))
}
5 changes: 3 additions & 2 deletions kaapi/src/friends/actions/friendsLoad.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ describe('friendsLoad', () => {
it('should dispatch friendsReceived with response from friendsGet', () => {
const mockDispatch = jest.fn()

return friendsLoad()(mockDispatch).then(() => {
return friendsLoad('abc123')(mockDispatch).then(() => {
expect(friendsGet).toBeCalledWith('abc123')
expect(mockDispatch).toBeCalledWith('mock friendsReceived')
expect(friendsReceived).toBeCalledWith('mock friendsGet')
expect(friendsReceived).toBeCalledWith({friends: 'mock friendsGet', groupId: 'abc123'})
})
})
})
Expand Down
4 changes: 2 additions & 2 deletions kaapi/src/friends/actions/friendsReceived.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import types from './types'

export default (friends) => ({
export default ({friends, groupId}) => ({
type: types.FRIENDS_RECEIVED,
data: friends
data: {friends, groupId}
})
4 changes: 2 additions & 2 deletions kaapi/src/friends/actions/friendsReceived.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import types from './types'

describe('friendsReceived', () => {
it('should build the action', () => {
expect(friendsReceived('mock friends')).toEqual({
expect(friendsReceived({friends: 'mock friends', groupId: 'abc123'})).toEqual({
type: types.FRIENDS_RECEIVED,
data: 'mock friends'
data: {friends: 'mock friends', groupId: 'abc123'}
})
})
})
2 changes: 1 addition & 1 deletion kaapi/src/friends/actions/recordCoffee.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import coffeeCreate from '../api/coffeeCreate'
import friendsLoad from './friendsLoad'

export default (friend) => (dispatch) => {
return coffeeCreate({friendId: friend.id}).then(() => dispatch(friendsLoad()))
return coffeeCreate({friendId: friend.id}).then(() => dispatch(friendsLoad(friend.groupId)))
}
3 changes: 2 additions & 1 deletion kaapi/src/friends/actions/recordCoffee.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ describe('recordCoffee', () => {
friendsLoad.mockReturnValueOnce('mock friendsLoad')
const mockDispatch = jest.fn()

return recordCoffee({id: 'abc123'})(mockDispatch).then(() => {
return recordCoffee({id: 'abc123', groupId: 'def456'})(mockDispatch).then(() => {
expect(coffeeCreate).toBeCalledWith({friendId: 'abc123'})
expect(friendsLoad).toBeCalledWith('def456')
expect(mockDispatch).toBeCalledWith('mock friendsLoad')
})
})
Expand Down
4 changes: 2 additions & 2 deletions kaapi/src/friends/api/friendsGet.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import parseResponse from './parseResponse'

export default () => {
return fetch('/api/friends', {
export default (groupId) => {
return fetch(`/api/groups/${groupId}/friends`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
Expand Down
59 changes: 26 additions & 33 deletions kaapi/src/friends/api/friendsGet.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,35 @@ describe('friendsGet', () => {
{id: 3, name: 'John Ryan'}
]

describe('when the API returns friends', () => {
it('should fetch friends from the API', () => {
fetchMock.mock({
method: 'GET',
matcher: '/api/friends',
response: {
body: {data: friends},
status: 200
}
})

return friendsGet().then(() => {
expect(fetchMock.lastUrl()).toEqual('/api/friends')
expect(fetchMock.lastOptions().method).toEqual('GET')
expect(fetchMock.lastOptions().headers).toEqual({'Content-Type': 'application/json'})
})
it('should fetch friends from the API', () => {
fetchMock.mock({
method: 'GET',
matcher: '/api/groups/abc123/friends',
response: {
body: {data: friends},
status: 200
}
})

describe('when the responds successfully', () => {
beforeEach(() => fetchMock.mock({
method: 'GET',
matcher: '/api/friends',
response: {
body: {data: friends},
status: 200
}
}))

it('should return friends from the API', () => {
// TODO jest v20
// return friendsGet().resolves.toEqual(friends)
return friendsGet('abc123').then(() => {
expect(fetchMock.lastUrl()).toEqual('/api/groups/abc123/friends')
expect(fetchMock.lastOptions().method).toEqual('GET')
expect(fetchMock.lastOptions().headers).toEqual({'Content-Type': 'application/json'})
})
})

return friendsGet().then((result) => {
expect(result).toEqual(friends)
})
})
describe('when the API responds successfully', () => {
beforeEach(() => fetchMock.mock({
method: 'GET',
matcher: '/api/groups/abc123/friends',
response: {
body: {data: friends},
status: 200
}
}))

it('should return friends from the API', () => {
return expect(friendsGet('abc123')).resolves.toEqual(friends)
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ import friendAdd from '../../actions/friendAdd'

export default (WrappedComponent) => {

const mapDispatchToProps = (dispatch) => ({
friendAdd: (...args) => Promise.resolve(dispatch(friendAdd(...args)))
})
const mapDispatchToProps = (dispatch, ownProps) => {
const groupId = ownProps.match.params.groupId
return {
friendAdd: (name) => Promise.resolve(dispatch(friendAdd({name, groupId})))
}
}

return connect(undefined, mapDispatchToProps)(WrappedComponent)
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ describe('FriendAdderConnector', () => {
const mockFriendsLoadAction = {type: 'mock friendAdd'}
friendAdd.mockReturnValueOnce(mockFriendsLoadAction)

const {subject, mockWrappedComponent, mockStore} = renderDecorator(FriendAdderConnector)
const mockProps = {match: {params: {groupId: 'abc123'}}}
const {subject, mockWrappedComponent, mockStore} = renderDecorator(FriendAdderConnector, mockProps)

return subject.find(mockWrappedComponent).props().friendAdd('Will Read')
.then(() => {
expect(friendAdd).toHaveBeenCalledWith('Will Read')
expect(friendAdd).toHaveBeenCalledWith({name: 'Will Read', groupId: 'abc123'})
expect(mockStore.getActions()).toContainEqual(mockFriendsLoadAction)
})
})
Expand Down
Loading

0 comments on commit e68e519

Please sign in to comment.