Skip to content

Commit 1d889c6

Browse files
committed
test(comments): add tests for components
1 parent 228458c commit 1d889c6

File tree

2 files changed

+261
-1
lines changed

2 files changed

+261
-1
lines changed

src/features/comments/CommentList.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ function Comment({ comment }) {
6666
const isAuthor = useSelector(selectIsAuthor(comment.id));
6767

6868
return (
69-
<div className="card">
69+
<div className="card" data-testid="comment">
7070
<div className="card-block">
7171
<p className="card-text">{comment.body}</p>
7272
</div>
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
import {
2+
screen,
3+
waitForElementToBeRemoved,
4+
within,
5+
} from '@testing-library/react';
6+
import user from '@testing-library/user-event';
7+
import faker from 'faker';
8+
import { createMemoryHistory } from 'history';
9+
import fetchMock from 'jest-fetch-mock';
10+
import { Route } from 'react-router-dom';
11+
12+
import agent from '../../agent';
13+
import { makeStore } from '../../app/store';
14+
import { Status } from '../../common/utils';
15+
import render from '../../test/utils';
16+
import CommentSection from './CommentSection';
17+
18+
describe('<CommentSection />', () => {
19+
const unauthenticatedRootState = {
20+
articleList: {
21+
articlesCount: 1,
22+
articlesPerPage: 10,
23+
currentPage: 0,
24+
articles: [
25+
{
26+
slug: 'how-to-train-your-dragon-2',
27+
title: 'How to train your dragon 2',
28+
description: 'So toothless',
29+
body: 'It a dragon',
30+
tagList: ['dragons', 'training'],
31+
createdAt: '2016-02-18T03:22:56.637Z',
32+
updatedAt: '2016-02-18T03:48:35.824Z',
33+
favorited: false,
34+
favoritesCount: 0,
35+
author: {
36+
username: 'jake',
37+
bio: 'I work at statefarm',
38+
image: 'https://i.stack.imgur.com/xHWG8.jpg',
39+
following: false,
40+
},
41+
},
42+
],
43+
},
44+
auth: {
45+
status: Status.IDLE,
46+
},
47+
comments: {
48+
status: Status.IDLE,
49+
ids: [],
50+
entities: {},
51+
},
52+
};
53+
const authenticatedRootState = {
54+
articleList: {
55+
articlesCount: 1,
56+
articlesPerPage: 10,
57+
currentPage: 0,
58+
articles: [
59+
{
60+
slug: 'how-to-train-your-dragon-2',
61+
title: 'How to train your dragon 2',
62+
description: 'So toothless',
63+
body: 'It a dragon',
64+
tagList: ['dragons', 'training'],
65+
createdAt: '2016-02-18T03:22:56.637Z',
66+
updatedAt: '2016-02-18T03:48:35.824Z',
67+
favorited: true,
68+
favoritesCount: 1,
69+
author: {
70+
username: 'jake',
71+
bio: 'I work at statefarm',
72+
image: 'https://i.stack.imgur.com/xHWG8.jpg',
73+
following: true,
74+
},
75+
},
76+
],
77+
},
78+
auth: {
79+
status: Status.SUCCESS,
80+
token: '{"sub":"warren_boyd"}',
81+
user: {
82+
83+
username: 'warren_boyd',
84+
bio: 'Asperiores quos dolorem iure et.',
85+
image: 'https://cdn.fakercloud.com/avatars/sprayaga_128.jpg',
86+
},
87+
},
88+
comments: {
89+
status: Status.IDLE,
90+
ids: [],
91+
entities: {},
92+
},
93+
};
94+
const commentsResponse = JSON.stringify({
95+
comments: [
96+
{
97+
id: 1,
98+
createdAt: '2016-02-18T03:22:56.637Z',
99+
updatedAt: '2016-02-18T03:22:56.637Z',
100+
body: 'It takes a Jacobian',
101+
author: {
102+
username: 'jake',
103+
bio: 'I work at statefarm',
104+
image: 'https://i.stack.imgur.com/xHWG8.jpg',
105+
following: false,
106+
},
107+
},
108+
{
109+
id: 2,
110+
createdAt: '2018-07-28T03:13:47.736Z',
111+
updatedAt: '2018-07-28T03:13:47.736Z',
112+
body: 'Dolore explicabo veniam at quo qui vero qui voluptas.',
113+
author: {
114+
username: 'warren_boyd',
115+
bio: 'Asperiores quos dolorem iure et.',
116+
image: 'https://cdn.fakercloud.com/avatars/sprayaga_128.jpg',
117+
following: false,
118+
},
119+
},
120+
],
121+
});
122+
123+
beforeAll(() => {
124+
fetchMock.enableMocks();
125+
});
126+
127+
beforeEach(() => {
128+
fetchMock.resetMocks();
129+
});
130+
131+
afterAll(() => {
132+
fetchMock.disableMocks();
133+
});
134+
135+
it('should render the comment section', async () => {
136+
fetchMock.mockResponse(commentsResponse);
137+
const history = createMemoryHistory({
138+
initialEntries: ['/article/how-to-train-your-dragon-2'],
139+
});
140+
const store = makeStore(unauthenticatedRootState);
141+
142+
render(<Route path="/article/:slug" component={CommentSection} />, {
143+
history,
144+
store,
145+
});
146+
147+
expect(screen.getByRole('link', { name: /sign in/i })).toBeInTheDocument();
148+
expect(screen.getByRole('link', { name: /sign up/i })).toBeInTheDocument();
149+
150+
await waitForElementToBeRemoved(() => screen.getByText('Loading comments'));
151+
152+
await expect(screen.findAllByTestId('comment')).resolves.not.toHaveLength(
153+
0
154+
);
155+
});
156+
157+
it('should create a new comment', async () => {
158+
agent.setToken(authenticatedRootState.auth.token);
159+
fetchMock.mockResponses(commentsResponse, async (request) => {
160+
const { comment } = await request.json();
161+
162+
return JSON.stringify({
163+
comment: {
164+
id: Date.now() / (24 * 36e5),
165+
createdAt: new Date(),
166+
updatedAt: new Date(),
167+
body: comment.body,
168+
author: {
169+
...authenticatedRootState.auth.user,
170+
following: false,
171+
email: undefined,
172+
},
173+
},
174+
});
175+
});
176+
const history = createMemoryHistory({
177+
initialEntries: ['/article/how-to-train-your-dragon-2'],
178+
});
179+
const store = makeStore(authenticatedRootState);
180+
const comment = faker.lorem.sentence();
181+
182+
render(<Route path="/article/:slug" component={CommentSection} />, {
183+
history,
184+
store,
185+
});
186+
187+
expect(screen.queryByRole('list')).not.toBeInTheDocument();
188+
189+
await waitForElementToBeRemoved(() => screen.getByText('Loading comments'));
190+
191+
await expect(screen.findAllByTestId('comment')).resolves.not.toHaveLength(
192+
0
193+
);
194+
195+
user.type(screen.getByPlaceholderText('Write a comment...'), comment);
196+
user.click(screen.getByRole('button', { name: /post comment/i }));
197+
198+
within(screen.getAllByTestId('comment')[0]).getByText(comment);
199+
});
200+
201+
it('should show the validation errors', async () => {
202+
agent.setToken(authenticatedRootState.auth.token);
203+
fetchMock.mockResponses(commentsResponse, [
204+
`{
205+
"errors": {
206+
"body": ["can't be blank"]
207+
}
208+
}`,
209+
{ status: 422 },
210+
]);
211+
const history = createMemoryHistory({
212+
initialEntries: ['/article/how-to-train-your-dragon-2'],
213+
});
214+
const store = makeStore(authenticatedRootState);
215+
216+
render(<Route path="/article/:slug" component={CommentSection} />, {
217+
history,
218+
store,
219+
});
220+
221+
expect(screen.queryByRole('list')).not.toBeInTheDocument();
222+
223+
await waitForElementToBeRemoved(() => screen.getByText('Loading comments'));
224+
225+
await expect(screen.findAllByTestId('comment')).resolves.not.toHaveLength(
226+
0
227+
);
228+
229+
user.click(screen.getByRole('button', { name: /post comment/i }));
230+
231+
await expect(screen.findByRole('list')).resolves.not.toBeEmptyDOMElement();
232+
});
233+
234+
it('should remove a comment', async () => {
235+
agent.setToken(authenticatedRootState.auth.token);
236+
fetchMock.mockResponses(commentsResponse, '{}');
237+
const history = createMemoryHistory({
238+
initialEntries: ['/article/how-to-train-your-dragon-2'],
239+
});
240+
const store = makeStore(authenticatedRootState);
241+
242+
render(<Route path="/article/:slug" component={CommentSection} />, {
243+
history,
244+
store,
245+
});
246+
247+
expect(screen.queryByRole('list')).not.toBeInTheDocument();
248+
249+
await waitForElementToBeRemoved(() => screen.getByText('Loading comments'));
250+
251+
await expect(screen.findAllByTestId('comment')).resolves.not.toHaveLength(
252+
0
253+
);
254+
user.click(screen.getByRole('button', { name: /delete comment/i }));
255+
256+
await waitForElementToBeRemoved(
257+
screen.getByRole('button', { name: /delete comment/i })
258+
);
259+
});
260+
});

0 commit comments

Comments
 (0)