Skip to content

Commit 2d63f71

Browse files
committed
Testing Mutations
1 parent bad842e commit 2d63f71

6 files changed

Lines changed: 293 additions & 27 deletions

File tree

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { mount } from 'enzyme';
2+
import wait from 'waait';
3+
import toJSON from 'enzyme-to-json';
4+
import Router from 'next/router';
5+
import { MockedProvider } from 'react-apollo/test-utils';
6+
import CreateItem, { CREATE_ITEM_MUTATION } from '../components/CreateItem';
7+
import { fakeItem } from '../lib/testUtils';
8+
9+
const dogImage = 'https://dog.com/dog.jpg';
10+
11+
// mock the global fetch API
12+
global.fetch = jest.fn().mockResolvedValue({
13+
json: () => ({
14+
secure_url: dogImage,
15+
eager: [{ secure_url: dogImage }],
16+
}),
17+
});
18+
19+
describe('<CreateItem/>', () => {
20+
it('renders and matches snapshot', async () => {
21+
const wrapper = mount(
22+
<MockedProvider>
23+
<CreateItem />
24+
</MockedProvider>
25+
);
26+
const form = wrapper.find('form[data-test="form"]');
27+
expect(toJSON(form)).toMatchSnapshot();
28+
});
29+
30+
it('uploads a file when changed', async () => {
31+
const wrapper = mount(
32+
<MockedProvider>
33+
<CreateItem />
34+
</MockedProvider>
35+
);
36+
const input = wrapper.find('input[type="file"]');
37+
input.simulate('change', { target: { files: ['fakedog.jpg'] } });
38+
await wait();
39+
const component = wrapper.find('CreateItem').instance();
40+
expect(component.state.image).toEqual(dogImage);
41+
expect(component.state.largeImage).toEqual(dogImage);
42+
expect(global.fetch).toHaveBeenCalled();
43+
global.fetch.mockReset();
44+
});
45+
46+
it('handles state updating', async () => {
47+
const wrapper = mount(
48+
<MockedProvider>
49+
<CreateItem />
50+
</MockedProvider>
51+
);
52+
wrapper.find('#title').simulate('change', { target: { value: 'Testing', name: 'title' } });
53+
wrapper
54+
.find('#price')
55+
.simulate('change', { target: { value: 50000, name: 'price', type: 'number' } });
56+
wrapper
57+
.find('#description')
58+
.simulate('change', { target: { value: 'This is a really nice item', name: 'description' } });
59+
60+
expect(wrapper.find('CreateItem').instance().state).toMatchObject({
61+
title: 'Testing',
62+
price: 50000,
63+
description: 'This is a really nice item',
64+
});
65+
});
66+
it('creates an item when the form is submitted', async () => {
67+
const item = fakeItem();
68+
const mocks = [
69+
{
70+
request: {
71+
query: CREATE_ITEM_MUTATION,
72+
variables: {
73+
title: item.title,
74+
description: item.description,
75+
image: '',
76+
largeImage: '',
77+
price: item.price,
78+
},
79+
},
80+
result: {
81+
data: {
82+
createItem: {
83+
...fakeItem,
84+
id: 'abc123',
85+
__typename: 'Item',
86+
},
87+
},
88+
},
89+
},
90+
];
91+
92+
const wrapper = mount(
93+
<MockedProvider mocks={mocks}>
94+
<CreateItem />
95+
</MockedProvider>
96+
);
97+
// simulate someone filling out the form
98+
wrapper.find('#title').simulate('change', { target: { value: item.title, name: 'title' } });
99+
wrapper
100+
.find('#price')
101+
.simulate('change', { target: { value: item.price, name: 'price', type: 'number' } });
102+
wrapper
103+
.find('#description')
104+
.simulate('change', { target: { value: item.description, name: 'description' } });
105+
// mock the router
106+
Router.router = { push: jest.fn() };
107+
wrapper.find('form').simulate('submit');
108+
await wait(50);
109+
expect(Router.router.push).toHaveBeenCalled();
110+
expect(Router.router.push).toHaveBeenCalledWith({ pathname: '/item', query: { id: 'abc123' } });
111+
});
112+
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { mount } from 'enzyme';
2+
import wait from 'waait';
3+
import toJSON from 'enzyme-to-json';
4+
import { MockedProvider } from 'react-apollo/test-utils';
5+
import RequestReset, { REQUEST_RESET_MUTATION } from '../components/RequestReset';
6+
7+
const mocks = [
8+
{
9+
request: {
10+
query: REQUEST_RESET_MUTATION,
11+
variables: { email: 'wesbos@gmail.com' },
12+
},
13+
result: {
14+
data: { requestReset: { message: 'success', __typename: 'Message' } },
15+
},
16+
},
17+
];
18+
19+
describe('<RequestReset/>', () => {
20+
it('renders and matches snapshot', async () => {
21+
const wrapper = mount(
22+
<MockedProvider>
23+
<RequestReset />
24+
</MockedProvider>
25+
);
26+
const form = wrapper.find('form[data-test="form"]');
27+
expect(toJSON(form)).toMatchSnapshot();
28+
});
29+
30+
it('calls the mutation', async () => {
31+
const wrapper = mount(
32+
<MockedProvider mocks={mocks}>
33+
<RequestReset />
34+
</MockedProvider>
35+
);
36+
// simulate typing an email
37+
wrapper
38+
.find('input')
39+
.simulate('change', { target: { name: 'email', value: 'wesbos@gmail.com' } });
40+
// submit the form
41+
wrapper.find('form').simulate('submit');
42+
await wait();
43+
wrapper.update();
44+
expect(wrapper.find('p').text()).toContain('Success! Check your email for a reset link!');
45+
});
46+
});
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`<CreateItem/> renders and matches snapshot 1`] = `
4+
<form
5+
className="Form-sc-1xszr8q-0 krRxky"
6+
data-test="form"
7+
onSubmit={[Function]}
8+
>
9+
<DisplayError
10+
error={Object {}}
11+
/>
12+
<fieldset
13+
aria-busy={false}
14+
disabled={false}
15+
>
16+
<label
17+
htmlFor="file"
18+
>
19+
Image
20+
<input
21+
id="file"
22+
name="file"
23+
onChange={[Function]}
24+
placeholder="Upload an image"
25+
required={true}
26+
type="file"
27+
/>
28+
</label>
29+
<label
30+
htmlFor="title"
31+
>
32+
Title
33+
<input
34+
id="title"
35+
name="title"
36+
onChange={[Function]}
37+
placeholder="Title"
38+
required={true}
39+
type="text"
40+
value=""
41+
/>
42+
</label>
43+
<label
44+
htmlFor="price"
45+
>
46+
Price
47+
<input
48+
id="price"
49+
name="price"
50+
onChange={[Function]}
51+
placeholder="Price"
52+
required={true}
53+
type="number"
54+
value={0}
55+
/>
56+
</label>
57+
<label
58+
htmlFor="description"
59+
>
60+
Description
61+
<textarea
62+
id="description"
63+
name="description"
64+
onChange={[Function]}
65+
placeholder="Enter A Description"
66+
required={true}
67+
value=""
68+
/>
69+
</label>
70+
<button
71+
type="submit"
72+
>
73+
Submit
74+
</button>
75+
</fieldset>
76+
</form>
77+
`;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`<RequestReset/> renders and matches snapshot 1`] = `
4+
<form
5+
className="Form-sc-1xszr8q-0 krRxky"
6+
data-test="form"
7+
method="post"
8+
onSubmit={[Function]}
9+
>
10+
<fieldset
11+
aria-busy={false}
12+
disabled={false}
13+
>
14+
<h2>
15+
Request a password reset
16+
</h2>
17+
<DisplayError
18+
error={Object {}}
19+
/>
20+
<label
21+
htmlFor="email"
22+
>
23+
Email
24+
<input
25+
name="email"
26+
onChange={[Function]}
27+
placeholder="email"
28+
type="email"
29+
value=""
30+
/>
31+
</label>
32+
<button
33+
type="submit"
34+
>
35+
Request Reset!
36+
</button>
37+
</fieldset>
38+
</form>
39+
`;

frontend/components/CreateItem.js

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ const CREATE_ITEM_MUTATION = gql`
2828

2929
class CreateItem extends Component {
3030
state = {
31-
title: 'Cool Shoes',
32-
description: 'I love those shoes',
33-
image: 'dog.jpg',
34-
largeImage: 'large-dog.jpg',
35-
price: 1000
31+
title: '',
32+
description: '',
33+
image: '',
34+
largeImage: '',
35+
price: 0,
3636
};
3737
handleChange = e => {
3838
const { name, type, value } = e.target;
@@ -41,31 +41,27 @@ class CreateItem extends Component {
4141
};
4242

4343
uploadFile = async e => {
44-
console.log('uploading file...');
4544
const files = e.target.files;
4645
const data = new FormData();
4746
data.append('file', files[0]);
4847
data.append('upload_preset', 'sickfits');
4948

50-
const res = await fetch(
51-
'https://api.cloudinary.com/v1_1/cavanwagg/image/upload',
52-
{
53-
method: 'POST',
54-
body: data
55-
}
56-
);
49+
const res = await fetch('https://api.cloudinary.com/v1_1/wesbostutorial/image/upload', {
50+
method: 'POST',
51+
body: data,
52+
});
5753
const file = await res.json();
58-
console.log(file);
5954
this.setState({
6055
image: file.secure_url,
61-
largeImage: file.eager[0].secure_url
56+
largeImage: file.eager[0].secure_url,
6257
});
6358
};
6459
render() {
6560
return (
6661
<Mutation mutation={CREATE_ITEM_MUTATION} variables={this.state}>
6762
{(createItem, { loading, error }) => (
6863
<Form
64+
data-test="form"
6965
onSubmit={async e => {
7066
// Stop the form from submitting
7167
e.preventDefault();
@@ -75,7 +71,7 @@ class CreateItem extends Component {
7571
console.log(res);
7672
Router.push({
7773
pathname: '/item',
78-
query: { id: res.data.createItem.id }
74+
query: { id: res.data.createItem.id },
7975
});
8076
}}
8177
>
@@ -92,11 +88,7 @@ class CreateItem extends Component {
9288
onChange={this.uploadFile}
9389
/>
9490
{this.state.image && (
95-
<img
96-
width="200"
97-
src={this.state.image}
98-
alt="Upload Preview"
99-
/>
91+
<img width="200" src={this.state.image} alt="Upload Preview" />
10092
)}
10193
</label>
10294

frontend/components/RequestReset.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ const REQUEST_RESET_MUTATION = gql`
1212
}
1313
`;
1414

15-
class Signin extends Component {
15+
class RequestReset extends Component {
1616
state = {
17-
email: ''
17+
email: '',
1818
};
1919
saveToState = e => {
2020
this.setState({ [e.target.name]: e.target.value });
@@ -25,6 +25,7 @@ class Signin extends Component {
2525
{(reset, { error, loading, called }) => (
2626
<Form
2727
method="post"
28+
data-test="form"
2829
onSubmit={async e => {
2930
e.preventDefault();
3031
await reset();
@@ -34,9 +35,7 @@ class Signin extends Component {
3435
<fieldset disabled={loading} aria-busy={loading}>
3536
<h2>Request a password reset</h2>
3637
<Error error={error} />
37-
{!error && !loading && called && (
38-
<p>Success! Check your email for a reset link!</p>
39-
)}
38+
{!error && !loading && called && <p>Success! Check your email for a reset link!</p>}
4039
<label htmlFor="email">
4140
Email
4241
<input
@@ -57,4 +56,5 @@ class Signin extends Component {
5756
}
5857
}
5958

60-
export default Signin;
59+
export default RequestReset;
60+
export { REQUEST_RESET_MUTATION };

0 commit comments

Comments
 (0)