-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(comment): add comment on article
- add comment reducer and actions tests and implementation - add comment form [starts #165273535]
- Loading branch information
Arthur Kalule
committed
Jun 15, 2019
1 parent
ad456ec
commit 950414d
Showing
16 changed files
with
776 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import React from 'react'; | ||
import { CommentForm, mapStateToProps, mapDispatchToProps } from 'components/Comment/CommentForm'; | ||
import { shallow } from 'enzyme'; | ||
import configureStore from 'redux-mock-store'; | ||
import thunk from 'redux-thunk'; | ||
import moxios from 'moxios'; | ||
|
||
|
||
describe('Comment component', () => { | ||
const props = { | ||
articleSlug: '', | ||
createNewComment: jest.fn(), | ||
username: 'dojo', | ||
}; | ||
|
||
const initialState = { | ||
commentBody: '', | ||
}; | ||
const mockStore = configureStore([thunk]); | ||
let wrapper; | ||
let store; | ||
|
||
|
||
beforeEach(() => { | ||
store = mockStore(initialState); | ||
moxios.install(); | ||
wrapper = shallow(<CommentForm store={store} {...props} />); | ||
}); | ||
|
||
afterEach(() => { | ||
moxios.uninstall(); | ||
}); | ||
|
||
it('should render without crashing', () => { | ||
expect(wrapper).toMatchSnapshot(); | ||
}); | ||
|
||
it('should limit commenting on articles to authenticated users', () => { | ||
wrapper.setProps({ username: '' }); | ||
expect(wrapper.text()).toBe('login to comment on this article'); | ||
}); | ||
|
||
it('should handle the comment body input change event', () => { | ||
const event = { | ||
target: { | ||
name: 'commentBody', | ||
value: 'My first comment', | ||
}, | ||
}; | ||
|
||
wrapper.instance().handleChange(event); | ||
expect(wrapper.instance().state.commentBody).toBe(event.target.value); | ||
}); | ||
|
||
it('should handle the onSubmit event', () => { | ||
const instance = wrapper.instance(); | ||
wrapper.setState({ commentBody: '' }); | ||
wrapper.setProps({ articleSlug: 'my_article_slug' }); | ||
const event = { | ||
target: { | ||
type: 'submit', | ||
name: 'addComment', | ||
}, | ||
preventDefault: jest.fn(), | ||
}; | ||
|
||
instance.handleCommentSubmit(event); | ||
|
||
expect(instance.props.createNewComment.mock.calls.length).toBe(0); | ||
|
||
wrapper.setState({ commentBody: 'My first comment' }); | ||
instance.handleCommentSubmit(event); | ||
|
||
expect(instance.props.createNewComment.mock.calls.length).toBe(1); | ||
expect(instance.props.createNewComment).toBeCalled(); | ||
}); | ||
|
||
it('should show an error on submitting a comment with on missing comment body', () => { | ||
wrapper.setState({ error: 'Comment body is required' }); | ||
expect(wrapper.find('.error-msg').text()).toBe('Comment body is required'); | ||
}); | ||
|
||
it('should show a success message on successfully commenting on an article', () => { | ||
wrapper.setProps({ success: true }); | ||
expect(wrapper.find('.success-msg').text()).toBe('Comment added successfully'); | ||
}); | ||
|
||
|
||
it('should map state to props', () => { | ||
const mockedState = { | ||
loginReducer: { | ||
user: { | ||
username: 'arthur' | ||
} | ||
}, | ||
commentReducer: { | ||
success: true, | ||
} | ||
}; | ||
|
||
const state = mapStateToProps(mockedState); | ||
|
||
expect(state.username).toEqual('arthur'); | ||
expect(state.success).toEqual(true); | ||
}); | ||
|
||
it('should map dispatch to props', () => { | ||
const mockedDispatch = jest.fn(); | ||
|
||
mapDispatchToProps(mockedDispatch).createNewComment(); | ||
expect(mockedDispatch).toHaveBeenCalled(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// react | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import DeleteComment from 'components/comment/DeleteComment'; | ||
|
||
|
||
const CommentBox = ( | ||
{ | ||
id, author: { username, }, created_at, body, articleSlug, deleteComment | ||
} | ||
) => ( | ||
<div className="comment"> | ||
<span> | ||
{' '} | ||
{id} | ||
</span> | ||
<h4> | ||
<b>username</b> | ||
{username} | ||
{' '} | ||
</h4> | ||
<p>{created_at}</p> | ||
<p>{body}</p> | ||
<DeleteComment | ||
author={username} | ||
id={id} | ||
articleSlug={articleSlug} | ||
deleteComment={deleteComment} | ||
/> | ||
</div> | ||
); | ||
|
||
CommentBox.propTypes = { | ||
deleteComment: PropTypes.func.isRequired, | ||
articleSlug: PropTypes.string.isRequired, | ||
id: PropTypes.number.isRequired, | ||
body: PropTypes.string.isRequired, | ||
created_at: PropTypes.string.isRequired, | ||
author: PropTypes.shape({ | ||
username: PropTypes.string.isRequired, | ||
}).isRequired, | ||
}; | ||
|
||
export default CommentBox; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import React, { Component } from 'react'; | ||
|
||
// third party libraries | ||
import PropTypes from 'prop-types'; | ||
import { connect } from 'react-redux'; | ||
|
||
// comment actions | ||
import createComment from 'store/actions/commentActions'; | ||
|
||
export class CommentForm extends Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
commentBody: '', | ||
error: '', | ||
}; | ||
} | ||
|
||
handleCommentSubmit = (e) => { | ||
e.preventDefault(); | ||
|
||
|
||
const { commentBody } = this.state; | ||
const { createNewComment, articleSlug } = this.props; | ||
|
||
if (articleSlug && commentBody) { | ||
createNewComment(articleSlug, commentBody); | ||
} | ||
}; | ||
|
||
handleChange = (e) => { | ||
const { name, value } = e.target; | ||
|
||
this.setState({ [name]: value }); | ||
}; | ||
|
||
renderCommentError = error => ( | ||
error ? <div className="error-msg text-danger">{ error }</div> : '' | ||
); | ||
|
||
renderCommentSuccess = success => (success ? <div className="success-msg text-success">Comment added successfully</div> : ''); | ||
|
||
|
||
render() { | ||
const { commentBody, error } = this.state; | ||
const { success, username } = this.props; | ||
if (!username) { | ||
return (<div>login to comment on this article</div>); | ||
} | ||
|
||
return ( | ||
<div> | ||
<div> | ||
<h2>Add your Comment Here</h2> | ||
{this.renderCommentError(error)} | ||
{this.renderCommentSuccess(success)} | ||
|
||
<form onSubmit={this.handleCommentSubmit}> | ||
<div> | ||
<textarea | ||
placeholder="Comment" | ||
rows="3" | ||
required | ||
name="commentBody" | ||
value={commentBody} | ||
onChange={this.handleChange} | ||
/> | ||
</div> | ||
<div> | ||
<button type="submit" name="addComment">Post Comment</button> | ||
</div> | ||
</form> | ||
</div> | ||
|
||
</div> | ||
); | ||
} | ||
} | ||
|
||
// default props | ||
CommentForm.defaultProps = { | ||
username: '', | ||
success: false, | ||
}; | ||
|
||
// Props Validation | ||
CommentForm.propTypes = { | ||
createNewComment: PropTypes.func.isRequired, | ||
articleSlug: PropTypes.string.isRequired, | ||
success: PropTypes.bool, | ||
username: PropTypes.string, | ||
}; | ||
|
||
// mapState to props | ||
export const mapStateToProps = (state) => { | ||
const { commentReducer: { success }, loginReducer: { user: { username } } } = state; | ||
return { success, username }; | ||
}; | ||
|
||
// map dispatch to props | ||
export const mapDispatchToProps = dispatch => ({ | ||
createNewComment(articleSlug, commentBody) { | ||
dispatch(createComment(articleSlug, commentBody)); | ||
} | ||
}); | ||
|
||
export default connect(mapStateToProps, mapDispatchToProps)(CommentForm); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// react | ||
import React, { Fragment } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { isAuthenticated } from 'utils'; | ||
|
||
|
||
const DeleteComment = ({ | ||
id, author, articleSlug, deleteComment | ||
}) => ( | ||
<Fragment> | ||
{isAuthenticated().username === author && ( | ||
( | ||
<button | ||
type="button" | ||
onClick={(event) => { | ||
event.preventDefault(); | ||
deleteComment(articleSlug, id); | ||
}} | ||
> | ||
Delete | ||
</button> | ||
) | ||
) | ||
} | ||
</Fragment> | ||
); | ||
|
||
DeleteComment.propTypes = { | ||
articleSlug: PropTypes.string.isRequired, | ||
id: PropTypes.number.isRequired, | ||
author: PropTypes.string.isRequired, | ||
deleteComment: PropTypes.func.isRequired, | ||
}; | ||
|
||
export default DeleteComment; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`Comment component should render without crashing 1`] = ` | ||
<div> | ||
<div> | ||
<h2> | ||
Add your Comment Here | ||
</h2> | ||
<form | ||
onSubmit={[Function]} | ||
> | ||
<div> | ||
<textarea | ||
name="commentBody" | ||
onChange={[Function]} | ||
placeholder="Comment" | ||
required={true} | ||
rows="3" | ||
value="" | ||
/> | ||
</div> | ||
<div> | ||
<button | ||
name="addComment" | ||
type="submit" | ||
> | ||
Post Comment | ||
</button> | ||
</div> | ||
</form> | ||
</div> | ||
</div> | ||
`; |
Oops, something went wrong.