From 331da87bd53bc1fb79063d142764c75df9f32170 Mon Sep 17 00:00:00 2001
From: Wes Bos
Date: Wed, 14 Mar 2018 12:19:54 -0400
Subject: tests
---
frontend/.babelrc | 9 -
frontend/__tests__/CartCount.test.js | 21 +++
frontend/__tests__/CreateItem.test.js | 85 +++++++++
frontend/__tests__/Item.test.js | 31 ++++
frontend/__tests__/Nav.test.js | 35 ++++
frontend/__tests__/Pagination.test.js | 46 +++++
.../__tests__/__snapshots__/CartCount.test.js.snap | 201 +++++++++++++++++++++
.../__snapshots__/CreateItem.test.js.snap | 58 ++++++
frontend/__tests__/__snapshots__/Item.test.js.snap | 60 ++++++
frontend/__tests__/__snapshots__/Nav.test.js.snap | 69 +++++++
.../__snapshots__/Pagination.test.js.snap | 67 +++++++
frontend/__tests__/formatMoney.test.js | 21 +++
frontend/components/AddToCart.js | 41 +----
frontend/components/CartCount.js | 1 +
frontend/components/Count.js | 4 +-
frontend/components/CreateItem.js | 70 +++----
frontend/components/Item.js | 5 +-
frontend/components/Items.js | 3 +-
frontend/components/Nav.js | 9 +-
frontend/components/Pagination.js | 32 +++-
frontend/components/Search.js | 10 +-
frontend/components/SingleItem.js | 18 +-
frontend/components/styles/Form.js | 3 +
frontend/enhancers/enhancers.js | 2 +-
frontend/lib/formatMoney.js | 12 +-
frontend/package.json | 20 +-
26 files changed, 815 insertions(+), 118 deletions(-)
delete mode 100644 frontend/.babelrc
create mode 100644 frontend/__tests__/CartCount.test.js
create mode 100644 frontend/__tests__/CreateItem.test.js
create mode 100644 frontend/__tests__/Item.test.js
create mode 100644 frontend/__tests__/Nav.test.js
create mode 100644 frontend/__tests__/Pagination.test.js
create mode 100644 frontend/__tests__/__snapshots__/CartCount.test.js.snap
create mode 100644 frontend/__tests__/__snapshots__/CreateItem.test.js.snap
create mode 100644 frontend/__tests__/__snapshots__/Item.test.js.snap
create mode 100644 frontend/__tests__/__snapshots__/Nav.test.js.snap
create mode 100644 frontend/__tests__/__snapshots__/Pagination.test.js.snap
create mode 100644 frontend/__tests__/formatMoney.test.js
diff --git a/frontend/.babelrc b/frontend/.babelrc
deleted file mode 100644
index d9c3f91..0000000
--- a/frontend/.babelrc
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "presets": ["next/babel"],
- "plugins": [
- [
- "styled-components",
- { "ssr": true, "displayName": true, "preprocess": false }
- ]
- ]
-}
diff --git a/frontend/__tests__/CartCount.test.js b/frontend/__tests__/CartCount.test.js
new file mode 100644
index 0000000..f3c4978
--- /dev/null
+++ b/frontend/__tests__/CartCount.test.js
@@ -0,0 +1,21 @@
+import React from 'react';
+import { shallow, mount } from 'enzyme';
+import toJSON from 'enzyme-to-json';
+import { CartCount } from '../components/CartCount';
+
+describe('', () => {
+ it('renders okay', () => {
+ shallow();
+ });
+
+ it('matches snapshot', () => {
+ const wrapper = shallow();
+ expect(toJSON(wrapper)).toMatchSnapshot();
+ });
+ it('updates via props', () => {
+ const wrapper = mount();
+ expect(toJSON(wrapper)).toMatchSnapshot();
+ wrapper.setProps({ count: 10 });
+ expect(toJSON(wrapper)).toMatchSnapshot();
+ });
+});
diff --git a/frontend/__tests__/CreateItem.test.js b/frontend/__tests__/CreateItem.test.js
new file mode 100644
index 0000000..0994d9c
--- /dev/null
+++ b/frontend/__tests__/CreateItem.test.js
@@ -0,0 +1,85 @@
+import React from 'react';
+import { shallow, mount } from 'enzyme';
+import toJSON from 'enzyme-to-json';
+import { CreateItem } from '../components/CreateItem';
+
+// Wait func
+const wait = amount => new Promise(resolve => setTimeout(resolve, amount));
+
+const dogImage = 'https://dog.com/dog.jpg';
+
+// Mock fetch to resolve data
+global.fetch = jest.fn().mockResolvedValue({
+ json: () => ({
+ secure_url: dogImage,
+ eager: [{ secure_url: dogImage }],
+ }),
+});
+
+const fakeItem = {
+ title: 'Testing',
+ description: 'This is a really nice item',
+ image: dogImage,
+ largeImage: dogImage,
+ price: 50000,
+};
+
+describe('', () => {
+ it('renders the form out', () => {
+ const createItemMutation = jest.fn();
+ const wrapper = shallow();
+ expect(toJSON(wrapper)).toMatchSnapshot();
+ });
+
+ it('uploads a file when changed', async () => {
+ const createItemMutation = jest.fn();
+ const wrapper = shallow();
+
+ const input = wrapper.find('input[type="file"]');
+ // internally this does some setState() calls
+ input.simulate('change', { currentTarget: { files: ['fakedog.jpg'] } });
+ await wait(0);
+ expect(wrapper.state('image')).toEqual(dogImage);
+ expect(wrapper.state('largeImage')).toEqual(dogImage);
+ });
+
+ it('handles state updating', async () => {
+ const createItemMutation = jest.fn();
+ const wrapper = shallow();
+
+ wrapper.find('#title').simulate('change', { target: { value: 'Testing', name: 'title' } });
+ wrapper.find('#price').simulate('change', {
+ target: {
+ value: 50000,
+ name: 'price',
+ type: 'number',
+ },
+ });
+ wrapper.find('#description').simulate('change', {
+ target: {
+ value: 'This is a really nice item',
+ name: 'description',
+ },
+ });
+ expect(wrapper.state().item).toMatchObject({
+ title: 'Testing',
+ description: 'This is a really nice item',
+ image: '',
+ largeImage: '',
+ price: 50000,
+ });
+ });
+
+ it('creates an item with all the inputs', () => {
+ const createItemMutation = jest.fn(() => Promise.resolve({}));
+ const wrapper = shallow();
+ wrapper.setState({ item: fakeItem });
+ const form = wrapper.find('Form');
+ // console.log(form.debug());
+ form.simulate('submit', {
+ preventDefault() {},
+ });
+ expect(createItemMutation).toHaveBeenCalled();
+ expect(createItemMutation).toHaveBeenCalledWith({ variables: fakeItem });
+ });
+});
diff --git a/frontend/__tests__/Item.test.js b/frontend/__tests__/Item.test.js
new file mode 100644
index 0000000..2990b1f
--- /dev/null
+++ b/frontend/__tests__/Item.test.js
@@ -0,0 +1,31 @@
+/* eslint-env jest */
+
+import React from 'react';
+import Enzyme, { shallow, mount } from 'enzyme';
+import toJSON from 'enzyme-to-json';
+import { ItemComponent } from '../components/Item';
+
+const fakeItem = {
+ id: 'ABC123',
+ title: 'A Cool Item',
+ price: 5000,
+ description: 'This item is really cool!',
+ image: 'dog.jpg',
+ largeImage: 'largedog.jpg',
+};
+
+describe(' ', () => {
+ it('Renders an item', () => {
+ const removeItem = jest.fn();
+ const wrapper = shallow();
+ expect(toJSON(wrapper)).toMatchSnapshot();
+ });
+ it('handles button clicks', async () => {
+ const removeItem = jest.fn();
+ global.confirm = jest.fn().mockReturnValue(true);
+ const wrapper = shallow();
+ wrapper.find('button').simulate('click');
+ expect(removeItem).toHaveBeenCalled();
+ expect(global.confirm).toHaveBeenCalled();
+ });
+});
diff --git a/frontend/__tests__/Nav.test.js b/frontend/__tests__/Nav.test.js
new file mode 100644
index 0000000..49d16d6
--- /dev/null
+++ b/frontend/__tests__/Nav.test.js
@@ -0,0 +1,35 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import toJSON from 'enzyme-to-json';
+import { Nav } from '../components/Nav';
+
+const currentUserLoggedOut = {
+ refetch() {},
+};
+
+const currentUser = {
+ me: {},
+ refetch() {},
+};
+
+describe('', () => {
+ it('renders', () => {
+ shallow();
+ });
+
+ it('Renders minimal nav when logged out', () => {
+ const wrapper = shallow();
+ expect(toJSON(wrapper)).toMatchSnapshot();
+ });
+
+ it('renders full nav when logged in', () => {
+ const wrapper = shallow();
+ expect(toJSON(wrapper)).toMatchSnapshot();
+ });
+
+ it('tries to refetch the current user when it mounts', () => {
+ const refetch = jest.fn();
+ shallow();
+ expect(refetch).toHaveBeenCalled();
+ });
+});
diff --git a/frontend/__tests__/Pagination.test.js b/frontend/__tests__/Pagination.test.js
new file mode 100644
index 0000000..e2ef7af
--- /dev/null
+++ b/frontend/__tests__/Pagination.test.js
@@ -0,0 +1,46 @@
+import React from 'react';
+import { shallow, mount } from 'enzyme';
+import toJSON from 'enzyme-to-json';
+import { Pagination } from '../components/Pagination';
+
+const fakeQuery = {
+ itemsConnection: { aggregate: { count: 18 } },
+};
+
+describe('', () => {
+ it('displays loading message', () => {
+ const wrapper = shallow();
+ expect(toJSON(wrapper)).toMatchSnapshot();
+ });
+
+ it('renders pagination for 18 items', () => {
+ const wrapper = shallow();
+ expect(toJSON(wrapper)).toMatchSnapshot();
+ expect(wrapper.find('.totalPages').text()).toEqual('2');
+ });
+
+ it('renders pagination for 28 items', () => {
+ const fakeQuery2 = {
+ itemsConnection: { aggregate: { count: 28 } },
+ };
+ const wrapper = shallow();
+ expect(wrapper.find('.totalPages').text()).toEqual('4');
+ });
+
+ it('disables and enables next/prev buttons', () => {
+ const fakeQuery3 = {
+ itemsConnection: { aggregate: { count: 100 } },
+ };
+ const wrapper = shallow();
+
+ expect(wrapper.find('a.prev').props()['aria-disabled']).toEqual(true);
+ expect(wrapper.find('a.next').props()['aria-disabled']).toEqual(false);
+ wrapper.setProps({ page: 2 });
+ expect(wrapper.find('a.prev').props()['aria-disabled']).toEqual(false);
+ expect(wrapper.find('a.next').props()['aria-disabled']).toEqual(true);
+ // when in the middle, both should work
+ wrapper.setProps({ itemsQuery: fakeQuery3, page: 3 });
+ expect(wrapper.find('a.prev').props()['aria-disabled']).toEqual(false);
+ expect(wrapper.find('a.next').props()['aria-disabled']).toEqual(false);
+ });
+});
diff --git a/frontend/__tests__/__snapshots__/CartCount.test.js.snap b/frontend/__tests__/__snapshots__/CartCount.test.js.snap
new file mode 100644
index 0000000..ddb6710
--- /dev/null
+++ b/frontend/__tests__/__snapshots__/CartCount.test.js.snap
@@ -0,0 +1,201 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` matches snapshot 1`] = `
+
+
+
+
+ 10
+
+
+
+
+`;
+
+exports[` updates via props 1`] = `
+
+
+
+
+
+
+
+
+
+`;
+
+exports[` updates via props 2`] = `
+
+
+
+
+
+
+
+
+
+ 10
+
+
+
+
+
+
+
+
+ 50
+
+
+
+
+
+
+
+
+
+`;
diff --git a/frontend/__tests__/__snapshots__/CreateItem.test.js.snap b/frontend/__tests__/__snapshots__/CreateItem.test.js.snap
new file mode 100644
index 0000000..66b7965
--- /dev/null
+++ b/frontend/__tests__/__snapshots__/CreateItem.test.js.snap
@@ -0,0 +1,58 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` renders the form out 1`] = `
+
+`;
diff --git a/frontend/__tests__/__snapshots__/Item.test.js.snap b/frontend/__tests__/__snapshots__/Item.test.js.snap
new file mode 100644
index 0000000..f088121
--- /dev/null
+++ b/frontend/__tests__/__snapshots__/Item.test.js.snap
@@ -0,0 +1,60 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` Renders an item 1`] = `
+
+
+
+
+
+ A Cool Item
+
+
+
+
+ $50
+
+
+ This item is really cool!
+
+
+
+`;
diff --git a/frontend/__tests__/__snapshots__/Nav.test.js.snap b/frontend/__tests__/__snapshots__/Nav.test.js.snap
new file mode 100644
index 0000000..d8520ac
--- /dev/null
+++ b/frontend/__tests__/__snapshots__/Nav.test.js.snap
@@ -0,0 +1,69 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` Renders minimal nav when logged out 1`] = `
+
+
+
+ Shop
+
+
+
+
+ Sell
+
+
+
+
+ Sign In
+
+
+
+`;
+
+exports[` renders full nav when logged in 1`] = `
+
+
+
+ Shop
+
+
+
+
+ Sell
+
+
+
+
+
+ Orders
+
+
+
+
+ My Account
+
+
+
+ <[object Object] />
+
+
+`;
diff --git a/frontend/__tests__/__snapshots__/Pagination.test.js.snap b/frontend/__tests__/__snapshots__/Pagination.test.js.snap
new file mode 100644
index 0000000..f2abcdf
--- /dev/null
+++ b/frontend/__tests__/__snapshots__/Pagination.test.js.snap
@@ -0,0 +1,67 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` displays loading message 1`] = `
+
+ Loading...
+
+`;
+
+exports[` renders pagination for 18 items 1`] = `
+
+
+
+ ←Prev
+
+
+
+ Page
+
+ 1
+
+
+ of
+
+ 2
+
+
+
+
+ 18
+
+ Items Total
+
+
+
+ Next →
+
+
+
+`;
diff --git a/frontend/__tests__/formatMoney.test.js b/frontend/__tests__/formatMoney.test.js
new file mode 100644
index 0000000..c5c79b6
--- /dev/null
+++ b/frontend/__tests__/formatMoney.test.js
@@ -0,0 +1,21 @@
+import formatMoney from '../lib/formatMoney';
+
+describe('formatMoney', () => {
+ it('works with fractional dollars', () => {
+ expect(formatMoney(1)).toEqual('$0.01');
+ expect(formatMoney(10)).toEqual('$0.10');
+ });
+
+ it('leaves cents off for whole dollars', () => {
+ expect(formatMoney(5000)).toEqual('$50');
+ expect(formatMoney(100)).toEqual('$1');
+ expect(formatMoney(50000000)).toEqual('$500,000');
+ });
+
+ it('works with whole and fractional dollars', () => {
+ expect(formatMoney(5012)).toEqual('$50.12');
+ expect(formatMoney(101)).toEqual('$1.01');
+ expect(formatMoney(110)).toEqual('$1.10');
+ expect(formatMoney(54345345)).toEqual('$543,453.45');
+ });
+});
diff --git a/frontend/components/AddToCart.js b/frontend/components/AddToCart.js
index b87d4e9..9c52f83 100644
--- a/frontend/components/AddToCart.js
+++ b/frontend/components/AddToCart.js
@@ -1,37 +1,8 @@
import { Component } from 'react';
import { graphql, compose } from 'react-apollo';
-import Transition from 'react-transition-group/Transition';
import styled from 'styled-components';
import PropTypes from 'prop-types';
-import { CURRENT_USER_QUERY, SINGLE_ITEM_QUERY } from '../queries';
-import { removeFromCartEnhancer, userEnhancer, addtoCartEnhancer } from '../enhancers/enhancers';
-
-const JumpImg = styled.img`
- border: 0 solid black;
- max-width: 100px;
- transition: all 0.5s;
- position: fixed;
- left: ${props => props.x}px;
- top: -100%;
- &.jump-entered {
- border-color: green;
- transform-origin: 0 0;
- transform: scale(0);
- top: ${props => props.y}px;
- left: ${props => props.x}px;
- }
- &.jump-entering {
- border-color: yellow;
- top: ${props => props.y}px;
- left: ${props => props.x}px;
- }
- &.jump-exited {
- border-color: red;
- }
- &.jump-exiting {
- border-color: yellow;
- }
-`;
+import { removeFromCartEnhancer, userEnhancer, addToCartEnhancer, singleItemEnhancer } from '../enhancers/enhancers';
class AddToCart extends Component {
static propTypes = {
@@ -57,12 +28,4 @@ class AddToCart extends Component {
}
}
-const singleItemEnhancer = graphql(SINGLE_ITEM_QUERY, {
- name: 'singleItemQuery',
- options: ({ id }) => ({
- variables: {
- id,
- },
- }),
-});
-export default compose(userEnhancer, addtoCartEnhancer, removeFromCartEnhancer, singleItemEnhancer)(AddToCart);
+export default compose(userEnhancer, addToCartEnhancer)(AddToCart);
diff --git a/frontend/components/CartCount.js b/frontend/components/CartCount.js
index 245b07b..7949307 100644
--- a/frontend/components/CartCount.js
+++ b/frontend/components/CartCount.js
@@ -50,3 +50,4 @@ const CartCount = ({ count }) => (
);
export default CartCount;
+export { CartCount };
diff --git a/frontend/components/Count.js b/frontend/components/Count.js
index 800a902..7dfd33f 100644
--- a/frontend/components/Count.js
+++ b/frontend/components/Count.js
@@ -1,3 +1,4 @@
+// TODO THis is not needed
import React, { Component } from 'react'
import { graphql, gql } from 'react-apollo'
@@ -33,5 +34,4 @@ class Count extends Component {
// We export the graphQL HOC - this will fetch the data and inject it into the Count compeont via props
-export { ALL_ITEMS_QUERY }
-export default graphql(ALL_ITEMS_QUERY, { name: 'allLinksQuery' }) (Count)
+export default graphql(ALL_ITEMS_QUERY, { name: 'allLinksQuery' })(Count)
diff --git a/frontend/components/CreateItem.js b/frontend/components/CreateItem.js
index 26b2c88..74a6cee 100644
--- a/frontend/components/CreateItem.js
+++ b/frontend/components/CreateItem.js
@@ -1,33 +1,41 @@
import React, { Component } from 'react';
import { graphql, gql } from 'react-apollo';
-import { ALL_ITEMS_QUERY, CREATE_ITEM_MUTATION } from '../queries';
+import { CREATE_ITEM_MUTATION } from '../queries';
import ErrorMessage from './ErrorMessage';
-import { fileEndpoint } from '../config';
import Form from './styles/Form';
+import PropTypes from 'prop-types';
+
+class CreateItem extends Component {
+ static propTypes = {
+ createItemMutation: PropTypes.func.isRequired,
+ };
-class CreateLink extends Component {
state = {
- description: 'test',
- title: 'testing title',
- image: '',
- largeImage: '',
- price: 500,
- fullPrice: 0,
+ item: {
+ title: '',
+ description: '',
+ image: '',
+ largeImage: '',
+ price: 0,
+ },
loading: false,
error: {
- message: '',
+ message: null,
},
};
- componentWillReceiveProps(nextProps) {
- console.log(nextProps);
- }
+ handleChange = e => {
+ const { name, value, type } = e.target;
+ const updatedItem = {
+ ...this.state.item,
+ [name]: type === 'number' ? parseFloat(value) : value,
+ };
+ this.setState({ item: updatedItem });
+ };
uploadFile = async e => {
this.setState({ loading: true });
-
const files = e.currentTarget.files;
-
const data = new FormData();
data.append('file', files[0]);
data.append('upload_preset', 'sickfits');
@@ -38,33 +46,23 @@ class CreateLink extends Component {
body: data,
});
const file = await res.json();
- console.log(file);
this.setState({ image: file.secure_url, largeImage: file.eager[0].secure_url, loading: false });
};
createItem = async e => {
e.preventDefault();
- // pull the values from state
- const { description, title, price, image, largeImage } = this.state;
- // create a mutation
// TODO: handle any errors
// turn loading on
this.setState({ loading: true });
try {
- console.log('About to call create item mutation');
const res = await this.props.createItemMutation({
// pass in those variables from state
variables: {
- description,
- title,
- image,
- largeImage,
- price: parseInt(price),
+ ...this.state.item,
},
});
} catch (error) {
this.setState({ error });
- console.log(error);
}
this.setState({ loading: false });
};
@@ -84,23 +82,29 @@ class CreateLink extends Component {
Title
this.setState({ title: e.target.value })}
+ onChange={this.handleChange}
type="text"
+ name="title"
+ id="title"
placeholder="Title"
/>