summaryrefslogtreecommitdiffstats
path: root/finished-application/frontend/__tests__
diff options
context:
space:
mode:
Diffstat (limited to 'finished-application/frontend/__tests__')
-rw-r--r--finished-application/frontend/__tests__/AddToCart.test.js92
-rw-r--r--finished-application/frontend/__tests__/Cart.test.js46
-rw-r--r--finished-application/frontend/__tests__/CartCount.test.js21
-rw-r--r--finished-application/frontend/__tests__/CartItem.test.js33
-rw-r--r--finished-application/frontend/__tests__/CreateItem.test.js132
-rw-r--r--finished-application/frontend/__tests__/EditUser.test.js75
-rw-r--r--finished-application/frontend/__tests__/Item.test.js22
-rw-r--r--finished-application/frontend/__tests__/Nav.test.js66
-rw-r--r--finished-application/frontend/__tests__/Order.test.js41
-rw-r--r--finished-application/frontend/__tests__/Pagination.test.js94
-rw-r--r--finished-application/frontend/__tests__/PleaseSignIn.test.js80
-rw-r--r--finished-application/frontend/__tests__/RemoveFromCart.test.js78
-rw-r--r--finished-application/frontend/__tests__/ResetRequest.test.js54
-rw-r--r--finished-application/frontend/__tests__/Signup.test.js91
-rw-r--r--finished-application/frontend/__tests__/SingleItem.test.js67
-rw-r--r--finished-application/frontend/__tests__/TakeMyMoney.test.js98
-rw-r--r--finished-application/frontend/__tests__/__snapshots__/AddToCart.test.js.snap11
-rw-r--r--finished-application/frontend/__tests__/__snapshots__/Cart.test.js.snap32
-rw-r--r--finished-application/frontend/__tests__/__snapshots__/CartCount.test.js.snap205
-rw-r--r--finished-application/frontend/__tests__/__snapshots__/CartItem.test.js.snap33
-rw-r--r--finished-application/frontend/__tests__/__snapshots__/CreateItem.test.js.snap75
-rw-r--r--finished-application/frontend/__tests__/__snapshots__/EditUser.test.js.snap47
-rw-r--r--finished-application/frontend/__tests__/__snapshots__/Item.test.js.snap58
-rw-r--r--finished-application/frontend/__tests__/__snapshots__/Nav.test.js.snap39
-rw-r--r--finished-application/frontend/__tests__/__snapshots__/Order.test.js.snap130
-rw-r--r--finished-application/frontend/__tests__/__snapshots__/Pagination.test.js.snap71
-rw-r--r--finished-application/frontend/__tests__/__snapshots__/RemoveFromCart.test.js.snap12
-rw-r--r--finished-application/frontend/__tests__/__snapshots__/ResetRequest.test.js.snap35
-rw-r--r--finished-application/frontend/__tests__/__snapshots__/Signup.test.js.snap64
-rw-r--r--finished-application/frontend/__tests__/__snapshots__/SingleItem.test.js.snap32
-rw-r--r--finished-application/frontend/__tests__/__snapshots__/TakeMyMoney.test.js.snap67
-rw-r--r--finished-application/frontend/__tests__/formatMoney.test.js21
32 files changed, 2022 insertions, 0 deletions
diff --git a/finished-application/frontend/__tests__/AddToCart.test.js b/finished-application/frontend/__tests__/AddToCart.test.js
new file mode 100644
index 0000000..fe15f3f
--- /dev/null
+++ b/finished-application/frontend/__tests__/AddToCart.test.js
@@ -0,0 +1,92 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import toJSON from 'enzyme-to-json';
+import { ApolloConsumer } from 'react-apollo';
+import { MockedProvider } from 'react-apollo/test-utils';
+import wait from 'waait';
+import AddToCart, { ADD_TO_CART_MUTATION } from '../components/AddToCart';
+import { fakeCartItem, fakeUser } from '../lib/testUtils';
+import { CURRENT_USER_QUERY } from '../components/User';
+
+const mocks = [
+ {
+ request: { query: CURRENT_USER_QUERY },
+ result: {
+ data: {
+ me: {
+ ...fakeUser(),
+ cart: [],
+ },
+ },
+ },
+ },
+ {
+ request: { query: ADD_TO_CART_MUTATION, variables: { id: 'abc123' } },
+ result: {
+ data: {
+ addToCart: {
+ ...fakeCartItem(),
+ quantity: 1,
+ },
+ },
+ },
+ },
+];
+
+describe('<AddtoCart/>', () => {
+ it('renders and matches snapshot', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <AddToCart id="abc123" />
+ </MockedProvider>
+ );
+ await wait();
+ wrapper.update();
+
+ expect(toJSON(wrapper.find('button'))).toMatchSnapshot();
+ });
+
+ it('adds an item to the cart when clicked', async () => {
+ // 1. Grab a copy of the current client
+ let apolloClient;
+ // const wrapper = mountWithApollo(<RemoveFromCart id="abc123" />);
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <ApolloConsumer>
+ {client => {
+ apolloClient = client;
+ return <AddToCart id="abc123" />;
+ }}
+ </ApolloConsumer>
+ </MockedProvider>
+ );
+ await wait();
+ wrapper.update();
+
+ const { me } = apolloClient.readQuery({ query: CURRENT_USER_QUERY });
+ expect(me.cart).toHaveLength(0);
+
+ // add to cart
+ wrapper.find('button').simulate('click');
+ await wait();
+ // check if the item is in the cart
+ const { me: me2 } = apolloClient.readQuery({ query: CURRENT_USER_QUERY });
+
+ expect(me2.cart).toHaveLength(1);
+ expect(me2.cart[0].id).toBe('omg123');
+ expect(me2.cart[0].quantity).toBe(1);
+ });
+
+ it('changes to adding when clicked', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <AddToCart id="abc123" />
+ </MockedProvider>
+ );
+ await wait();
+ wrapper.update();
+ expect(wrapper.text()).toContain('πŸ›’ Add To Cart');
+ wrapper.find('button').simulate('click');
+ expect(wrapper.text()).toContain('πŸ›’ Adding To Cart');
+ });
+});
diff --git a/finished-application/frontend/__tests__/Cart.test.js b/finished-application/frontend/__tests__/Cart.test.js
new file mode 100644
index 0000000..255183e
--- /dev/null
+++ b/finished-application/frontend/__tests__/Cart.test.js
@@ -0,0 +1,46 @@
+import React from 'react';
+import { shallow, mount } from 'enzyme';
+import toJSON from 'enzyme-to-json';
+import mountOptions from '../lib/testUtils';
+import wait from 'waait';
+import Cart from '../components/Cart';
+import { MockedProvider } from 'react-apollo/test-utils';
+import { fakeCartItem, fakeUser } from '../lib/testUtils';
+import { CURRENT_USER_QUERY } from '../components/User';
+import { LOCAL_STATE_QUERY } from '../components/Cart';
+
+const mocks = [
+ {
+ request: { query: CURRENT_USER_QUERY },
+ result: {
+ data: {
+ me: {
+ ...fakeUser(),
+ cart: [fakeCartItem()],
+ },
+ },
+ },
+ },
+ {
+ request: { query: LOCAL_STATE_QUERY },
+ result: {
+ data: {
+ cartOpen: true,
+ },
+ },
+ },
+];
+
+describe('<Cart/>', () => {
+ it('renders', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <Cart />
+ </MockedProvider>
+ );
+ await wait();
+ wrapper.update();
+ expect(toJSON(wrapper.find('header'))).toMatchSnapshot();
+ expect(wrapper.find('CartItem')).toHaveLength(1);
+ });
+});
diff --git a/finished-application/frontend/__tests__/CartCount.test.js b/finished-application/frontend/__tests__/CartCount.test.js
new file mode 100644
index 0000000..dcb9ca8
--- /dev/null
+++ b/finished-application/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('<CartCount></CartCount>', () => {
+ it('renders okay', () => {
+ shallow(<CartCount count={10} />);
+ });
+
+ it('matches snapshot', () => {
+ const wrapper = shallow(<CartCount count={10} />);
+ expect(toJSON(wrapper)).toMatchSnapshot();
+ });
+ it('updates via props', () => {
+ const wrapper = mount(<CartCount count={50} />);
+ expect(toJSON(wrapper)).toMatchSnapshot();
+ wrapper.setProps({ count: 10 });
+ expect(toJSON(wrapper)).toMatchSnapshot();
+ });
+});
diff --git a/finished-application/frontend/__tests__/CartItem.test.js b/finished-application/frontend/__tests__/CartItem.test.js
new file mode 100644
index 0000000..42dd39b
--- /dev/null
+++ b/finished-application/frontend/__tests__/CartItem.test.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import { shallow, mount } from 'enzyme';
+import toJSON from 'enzyme-to-json';
+import CartItem from '../components/CartItem';
+
+const cartItem = {
+ item: {
+ price: 5000,
+ image: 'dog.jpg',
+ title: 'Fake Item',
+ },
+ id: 'abc123',
+ quantity: 20,
+};
+
+describe('<CartItem>', () => {
+ it('renders and matches snapshot', () => {
+ const wrapper = shallow(<CartItem cartItem={cartItem} />);
+ expect(toJSON(wrapper)).toMatchSnapshot();
+ });
+
+ it('renders a single quantity', () => {
+ const wrapper = shallow(<CartItem cartItem={{ ...cartItem, quantity: 1 }} />);
+ const p = wrapper.find('p');
+ expect(p.text()).toBe('$50 β€” 1 Γ— $50 each');
+ });
+
+ it('renders multiples', () => {
+ const wrapper = shallow(<CartItem cartItem={{ ...cartItem, quantity: 23423 }} />);
+ const p = wrapper.find('p');
+ expect(p.text()).toBe('$1,171,150 β€” 23423 Γ— $50 each');
+ });
+});
diff --git a/finished-application/frontend/__tests__/CreateItem.test.js b/finished-application/frontend/__tests__/CreateItem.test.js
new file mode 100644
index 0000000..56c3f10
--- /dev/null
+++ b/finished-application/frontend/__tests__/CreateItem.test.js
@@ -0,0 +1,132 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import toJSON from 'enzyme-to-json';
+import Router from 'next/router';
+import { MockedProvider } from 'react-apollo/test-utils';
+import CreateItem, { CREATE_ITEM_MUTATION } from '../components/CreateItem';
+import wait from 'waait';
+import { fakeItem } from '../lib/testUtils';
+
+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 }],
+ }),
+});
+
+describe('<Createitem/>', () => {
+ it('renders and matches snapshot', () => {
+ const wrapper = mount(
+ <MockedProvider>
+ <CreateItem />
+ </MockedProvider>
+ );
+ const form = wrapper.find('form[data-test]');
+ expect(toJSON(form)).toMatchSnapshot();
+ });
+
+ it('uploads a file when changed', async () => {
+ const wrapper = mount(
+ <MockedProvider>
+ <CreateItem />
+ </MockedProvider>
+ );
+ const component = wrapper.find('CreateItem').instance();
+
+ const input = wrapper.find('input[type="file"]');
+ // internally this does some setState() calls
+ input.simulate('change', { currentTarget: { files: ['fakedog.jpg'] } });
+ await wait(0);
+ expect(component.state.image).toEqual(dogImage);
+ expect(component.state.largeImage).toEqual(dogImage);
+ });
+
+ it('handles state updating', async () => {
+ const wrapper = mount(
+ <MockedProvider>
+ <CreateItem />
+ </MockedProvider>
+ );
+
+ 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',
+ },
+ });
+ const component = wrapper.find('CreateItem').instance();
+ expect(component.state).toMatchObject({
+ title: 'Testing',
+ description: 'This is a really nice item',
+ image: '',
+ largeImage: '',
+ price: 50000,
+ });
+ });
+
+ it('creates an item when form is submitted', async () => {
+ const item = fakeItem();
+ const mocks = [
+ {
+ request: {
+ query: CREATE_ITEM_MUTATION,
+ variables: {
+ title: item.title,
+ description: item.description,
+ image: '',
+ largeImage: '',
+ price: item.price,
+ },
+ },
+ result: {
+ data: {
+ createItem: {
+ ...fakeItem(),
+ __typeName: 'Item',
+ },
+ },
+ },
+ },
+ ];
+
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <CreateItem />
+ </MockedProvider>
+ );
+
+ wrapper.find('#title').simulate('change', { target: { value: item.title, name: 'title' } });
+ wrapper.find('#price').simulate('change', {
+ target: {
+ value: item.price,
+ name: 'price',
+ type: 'number',
+ },
+ });
+ wrapper.find('#description').simulate('change', {
+ target: {
+ value: item.description,
+ name: 'description',
+ },
+ });
+
+ Router.router = { push: jest.fn() };
+ // it submits and matches the mocks
+ wrapper.find('form').simulate('submit');
+ // it routes to the item page
+ await wait(50);
+ expect(Router.router.push).toHaveBeenCalled();
+ expect(Router.router.push).toHaveBeenCalledWith({ pathname: '/item', query: { id: '123' } });
+ });
+});
diff --git a/finished-application/frontend/__tests__/EditUser.test.js b/finished-application/frontend/__tests__/EditUser.test.js
new file mode 100644
index 0000000..a98cbb5
--- /dev/null
+++ b/finished-application/frontend/__tests__/EditUser.test.js
@@ -0,0 +1,75 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import toJSON from 'enzyme-to-json';
+import { MockedProvider } from 'react-apollo/test-utils';
+import wait from 'waait';
+import EditUser, { UPDATE_USER_MUTATION } from '../components/EditUser';
+import { fakeUser } from '../lib/testUtils';
+import { CURRENT_USER_QUERY } from '../components/User';
+
+const mocks = [
+ {
+ request: { query: CURRENT_USER_QUERY },
+ result: {
+ data: {
+ me: fakeUser(),
+ },
+ },
+ },
+ {
+ request: {
+ query: UPDATE_USER_MUTATION,
+ variables: {
+ name: 'Westopher',
+ },
+ },
+ result: {
+ updateUser: {
+ name: 'Westopher',
+ __typename: 'User',
+ },
+ },
+ },
+];
+
+describe('<EditUser/>', () => {
+ it('renders', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <EditUser />
+ </MockedProvider>
+ );
+ await wait();
+ wrapper.update();
+ expect(toJSON(wrapper.find('form'))).toMatchSnapshot();
+ });
+
+ it('displays changes', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <EditUser />
+ </MockedProvider>
+ );
+ await wait();
+ wrapper.update();
+ const nameInput = wrapper.find('[name="name"]');
+ nameInput.simulate('change', { target: { name: 'name', value: 'Scott' } });
+ const diff = wrapper.find('pre[data-test="change"]');
+ expect(diff.text()).toBe('{"name":"Scott"}');
+ });
+
+ it('calls update user when form is submitted', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <EditUser />
+ </MockedProvider>
+ );
+ await wait();
+ wrapper.update();
+ const nameInput = wrapper.find('[name="name"]');
+ nameInput.simulate('change', { target: { name: 'name', value: 'Westopher' } });
+ wrapper.find('form').simulate('submit');
+ wrapper.update();
+ expect(wrapper.find('[data-test="updated"]').text()).toBe('Updated!');
+ });
+});
diff --git a/finished-application/frontend/__tests__/Item.test.js b/finished-application/frontend/__tests__/Item.test.js
new file mode 100644
index 0000000..129efa9
--- /dev/null
+++ b/finished-application/frontend/__tests__/Item.test.js
@@ -0,0 +1,22 @@
+/* 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('<Item/>', () => {
+ it('Renders and matches snapshot', () => {
+ const wrapper = shallow(<ItemComponent item={fakeItem} />);
+ expect(toJSON(wrapper)).toMatchSnapshot();
+ });
+});
diff --git a/finished-application/frontend/__tests__/Nav.test.js b/finished-application/frontend/__tests__/Nav.test.js
new file mode 100644
index 0000000..5c61c56
--- /dev/null
+++ b/finished-application/frontend/__tests__/Nav.test.js
@@ -0,0 +1,66 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import toJSON from 'enzyme-to-json';
+import wait from 'waait';
+import Router from 'next/router';
+import Nav from '../components/Nav';
+import { MockedProvider } from 'react-apollo/test-utils';
+import { fakeUser } from '../lib/testUtils';
+import { CURRENT_USER_QUERY } from '../components/User';
+
+// Mock the router
+Router.router = { push() {} };
+
+const loggedOutMocks = [
+ {
+ request: { query: CURRENT_USER_QUERY },
+ delay: 0,
+ result: {
+ data: {
+ me: null,
+ },
+ },
+ },
+];
+
+const loggedInMocks = [
+ {
+ request: { query: CURRENT_USER_QUERY },
+ result: {
+ data: {
+ what: 'yah',
+ me: {
+ ...fakeUser(),
+ orders: [],
+ cart: [],
+ },
+ },
+ },
+ },
+];
+
+describe('<Nav></Nav>', () => {
+ it('Renders minimal nav when logged out', () => {
+ const wrapper = mount(
+ <MockedProvider mocks={loggedOutMocks}>
+ <Nav />
+ </MockedProvider>
+ );
+ const nav = wrapper.find('ul[data-test="nav"]');
+ expect(toJSON(nav)).toMatchSnapshot();
+ });
+
+ it('renders full nav when logged in', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={loggedInMocks}>
+ <Nav />
+ </MockedProvider>
+ );
+ await wait();
+ wrapper.update();
+ const nav = wrapper.find('ul[data-test="nav"]');
+ // account link
+ const accountLink = wrapper.find('a[href="/me"]');
+ expect(accountLink.length).toBe(1);
+ });
+});
diff --git a/finished-application/frontend/__tests__/Order.test.js b/finished-application/frontend/__tests__/Order.test.js
new file mode 100644
index 0000000..f3a4f06
--- /dev/null
+++ b/finished-application/frontend/__tests__/Order.test.js
@@ -0,0 +1,41 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import toJSON from 'enzyme-to-json';
+import wait from 'waait';
+import { MockedProvider } from 'react-apollo/test-utils';
+import Order, { SINGLE_ORDER_QUERY } from '../components/Order';
+import { fakeOrder } from '../lib/testUtils';
+
+const mocks = [
+ {
+ request: { query: SINGLE_ORDER_QUERY, variables: { id: '123' } },
+ result: {
+ data: {
+ order: fakeOrder(),
+ },
+ },
+ },
+];
+
+describe('<Order/>', () => {
+ it('Renders loading state', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <Order id="123" />
+ </MockedProvider>
+ );
+ expect(wrapper.text()).toContain('Loading...');
+ });
+
+ it('Renders the order', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <Order id="123" />
+ </MockedProvider>
+ );
+ await wait(50);
+ wrapper.update();
+ const order = wrapper.find('div[data-test="order"]');
+ expect(toJSON(order)).toMatchSnapshot();
+ });
+});
diff --git a/finished-application/frontend/__tests__/Pagination.test.js b/finished-application/frontend/__tests__/Pagination.test.js
new file mode 100644
index 0000000..29a57f9
--- /dev/null
+++ b/finished-application/frontend/__tests__/Pagination.test.js
@@ -0,0 +1,94 @@
+import React from 'react';
+import { shallow, mount } from 'enzyme';
+import toJSON from 'enzyme-to-json';
+import Pagination, { PAGINATION_QUERY } from '../components/Pagination';
+import { MockedProvider } from 'react-apollo/test-utils';
+import wait from 'waait';
+import { fakeItem } from '../lib/testUtils';
+
+import Router from 'next/router';
+
+// mock the router because there isn't really a router instance
+Router.router = { push() {}, prefetch() {} };
+
+function makeMocksFor(length) {
+ return [
+ {
+ request: { query: PAGINATION_QUERY, variables: { skip: 0, first: 4 } },
+ result: {
+ data: {
+ itemsConnection: {
+ __typename: 'aggregate',
+ aggregate: {
+ count: length,
+ __typename: 'count',
+ },
+ },
+ },
+ },
+ },
+ ];
+}
+
+describe('<Pagination/>', () => {
+ it('displays loading message', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={makeMocksFor(1)}>
+ <Pagination page={1} />
+ </MockedProvider>
+ );
+ await wait();
+ wrapper.update();
+ expect(toJSON(wrapper.find('div[data-test="pagination"]'))).toMatchSnapshot();
+ });
+
+ it('renders pagination for 18 items', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={makeMocksFor(18)}>
+ <Pagination page={1} />
+ </MockedProvider>
+ );
+ await wait();
+ wrapper.update();
+ expect(wrapper.find('.totalPages').text()).toEqual('5');
+ });
+
+ it('disables prev button on first page', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={makeMocksFor(18)}>
+ <Pagination page={1} />
+ </MockedProvider>
+ );
+ await wait();
+ wrapper.update();
+ // first page
+ expect(wrapper.find('a.prev').prop('aria-disabled')).toEqual(true);
+ expect(wrapper.find('a.next').prop('aria-disabled')).toEqual(false);
+ });
+
+ it('disables next button on last page', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={makeMocksFor(18)}>
+ <Pagination page={5} />
+ </MockedProvider>
+ );
+ await wait();
+ wrapper.update();
+ // first page
+ expect(wrapper.find('a.prev').prop('aria-disabled')).toEqual(false);
+ expect(wrapper.find('a.next').prop('aria-disabled')).toEqual(true);
+ });
+
+ it('enables all buttons on a middle page', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={makeMocksFor(18)}>
+ <Pagination page={2} />
+ </MockedProvider>
+ );
+ await wait();
+ wrapper.update();
+ // first page
+ expect(wrapper.find('a.prev').prop('aria-disabled')).toEqual(false);
+ expect(wrapper.find('a.next').prop('aria-disabled')).toEqual(false);
+ });
+});
diff --git a/finished-application/frontend/__tests__/PleaseSignIn.test.js b/finished-application/frontend/__tests__/PleaseSignIn.test.js
new file mode 100644
index 0000000..f516ea3
--- /dev/null
+++ b/finished-application/frontend/__tests__/PleaseSignIn.test.js
@@ -0,0 +1,80 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import wait from 'waait';
+import PleaseSignIn from '../components/PleaseSignIn';
+import { MockedProvider } from 'react-apollo/test-utils';
+import { fakeUser } from '../lib/testUtils';
+import { CURRENT_USER_QUERY } from '../components/User';
+
+const notSignedInMocks = [
+ {
+ request: { query: CURRENT_USER_QUERY },
+ result: {
+ data: {
+ me: null,
+ },
+ },
+ },
+];
+
+const signedInMocks = [
+ {
+ request: { query: CURRENT_USER_QUERY },
+ result: {
+ data: {
+ me: {
+ ...fakeUser(),
+ permissions: ['VIEW_HEY'],
+ },
+ },
+ },
+ },
+];
+// some dummy child component
+const Hey = () => <p>Hey!</p>;
+
+describe('<PleaseSignIn />', () => {
+ it('renders sign in dialog to logged out users', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={notSignedInMocks}>
+ <PleaseSignIn allowedPermissions={[]}>
+ <Hey />
+ </PleaseSignIn>
+ </MockedProvider>
+ );
+ await wait();
+ wrapper.update();
+ // find the error message
+ expect(wrapper.text()).toContain('Please sign in before continuing!');
+ // show the sign in dialog
+ expect(wrapper.find('Signin').exists()).toBeTruthy();
+ });
+
+ it('renders child for correct permissions', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={signedInMocks}>
+ <PleaseSignIn allowedPermissions={['ADMIN', 'VIEW_HEY']}>
+ <Hey />
+ </PleaseSignIn>
+ </MockedProvider>
+ );
+ await wait();
+ wrapper.update();
+ expect(wrapper.find('Hey').exists()).toBeTruthy();
+ });
+ it("renders error when permissions aren't met", async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={signedInMocks}>
+ <PleaseSignIn allowedPermissions={['ADMIN']}>
+ <Hey />
+ </PleaseSignIn>
+ </MockedProvider>
+ );
+ await wait();
+ wrapper.update();
+ // should tell you you can't access it
+ expect(wrapper.text()).toContain('Insufficient Permissions');
+ // it should not render the child component
+ expect(wrapper.find('Hey').exists()).toBeFalsy();
+ });
+});
diff --git a/finished-application/frontend/__tests__/RemoveFromCart.test.js b/finished-application/frontend/__tests__/RemoveFromCart.test.js
new file mode 100644
index 0000000..34229a5
--- /dev/null
+++ b/finished-application/frontend/__tests__/RemoveFromCart.test.js
@@ -0,0 +1,78 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import toJSON from 'enzyme-to-json';
+import { ApolloConsumer } from 'react-apollo';
+import { MockedProvider } from 'react-apollo/test-utils';
+import wait from 'waait';
+import RemoveFromCart, { REMOVE_FROM_CART_MUTATION } from '../components/RemoveFromCart';
+import { fakeCartItem, fakeUser } from '../lib/testUtils';
+import { CURRENT_USER_QUERY } from '../components/User';
+
+const mocks = [
+ {
+ request: { query: CURRENT_USER_QUERY },
+ result: {
+ data: {
+ me: {
+ ...fakeUser(),
+ cart: [fakeCartItem({ id: 'abc123' })],
+ orders: [],
+ },
+ },
+ },
+ },
+ {
+ request: {
+ // This is the mutation
+ query: REMOVE_FROM_CART_MUTATION,
+ variables: { id: 'abc123' },
+ },
+ result: {
+ data: {
+ removeFromCart: {
+ __typename: 'CartItem',
+ id: 'abc123',
+ },
+ },
+ },
+ },
+];
+
+describe('<RemoveFromCart/>', () => {
+ it('renders and matches snapshot', () => {
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <RemoveFromCart id="abc123" />
+ </MockedProvider>
+ );
+ expect(toJSON(wrapper.find('button'))).toMatchSnapshot();
+ });
+
+ it('removes an item from the cart', async () => {
+ // 1. Grab a copy of the current client
+ let apolloClient;
+ // const wrapper = mountWithApollo(<RemoveFromCart id="abc123" />);
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <ApolloConsumer>
+ {client => {
+ apolloClient = client;
+ return <RemoveFromCart id="abc123" />;
+ }}
+ </ApolloConsumer>
+ </MockedProvider>
+ );
+ // Fire Off that first mock
+ await apolloClient.query({ query: CURRENT_USER_QUERY });
+ // check if it worked
+ const { me } = apolloClient.readQuery({ query: CURRENT_USER_QUERY });
+ expect(me.cart).toHaveLength(1);
+ expect(me.cart[0].item.price).toBe(5000);
+
+ // simulate a click, which will fire REMOVE_FROM_CART mock
+ wrapper.find('button').simulate('click');
+ await wait();
+ const { me: me2 } = apolloClient.readQuery({ query: CURRENT_USER_QUERY });
+ expect(me2.cart).toHaveLength(0);
+ });
+});
diff --git a/finished-application/frontend/__tests__/ResetRequest.test.js b/finished-application/frontend/__tests__/ResetRequest.test.js
new file mode 100644
index 0000000..22a9508
--- /dev/null
+++ b/finished-application/frontend/__tests__/ResetRequest.test.js
@@ -0,0 +1,54 @@
+import { mount } from 'enzyme';
+import toJSON from 'enzyme-to-json';
+import { MockedProvider } from 'react-apollo/test-utils';
+import wait from 'waait';
+import ResetRequest, { REQUEST_RESET_MUTATION } from '../components/ResetRequest';
+
+const mocks = [
+ {
+ request: {
+ query: REQUEST_RESET_MUTATION,
+ variables: {
+ email: 'wesbos@gmail.com',
+ },
+ },
+ result: {
+ data: {
+ requestReset: { id: 'abc123', __typename: 'User' },
+ },
+ },
+ },
+];
+
+describe('<ResetRequest/>', () => {
+ it('renders and matches snapshot', async () => {
+ const wrapper = mount(
+ <MockedProvider>
+ <ResetRequest />
+ </MockedProvider>
+ );
+ const form = wrapper.find("form[data-test='ResetRequest']");
+ expect(toJSON(form)).toMatchSnapshot();
+ });
+
+ it('calls the mutation', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <ResetRequest />
+ </MockedProvider>
+ );
+
+ wrapper.find('input').simulate('change', {
+ target: { value: 'wesbos@gmail.com' },
+ });
+
+ // submit the form
+ const form = wrapper.find('form');
+ console.log(form);
+ form.simulate('submit');
+
+ await wait();
+ wrapper.update();
+ expect(wrapper.find('p').text()).toContain('Success! Check Your Email!');
+ });
+});
diff --git a/finished-application/frontend/__tests__/Signup.test.js b/finished-application/frontend/__tests__/Signup.test.js
new file mode 100644
index 0000000..2a668a6
--- /dev/null
+++ b/finished-application/frontend/__tests__/Signup.test.js
@@ -0,0 +1,91 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import toJSON from 'enzyme-to-json';
+import wait from 'waait';
+import { ApolloConsumer } from 'react-apollo';
+import { MockedProvider } from 'react-apollo/test-utils';
+import Signup, { SIGNUP_MUTATION } from '../components/Signup';
+import { fakeUser } from '../lib/testUtils';
+import { CURRENT_USER_QUERY } from '../components/User';
+
+function type(wrapper, name, value) {
+ wrapper.find(`input[name="${name}"]`).simulate('change', {
+ target: {
+ name,
+ value,
+ },
+ });
+}
+const me = fakeUser();
+const mocks = [
+ {
+ request: {
+ query: SIGNUP_MUTATION,
+ variables: {
+ email: me.email,
+ name: me.name,
+ password: 'wes',
+ },
+ },
+ result: {
+ data: {
+ signup: {
+ __typename: 'User',
+ id: 'abc123',
+ email: me.email,
+ name: me.name,
+ },
+ },
+ },
+ },
+ {
+ request: { query: CURRENT_USER_QUERY },
+ result: {
+ data: {
+ me: {
+ __typename: 'User',
+ ...me,
+ cart: [],
+ },
+ },
+ },
+ },
+];
+
+describe('<Signup/>', () => {
+ it('renders and matches snapshot', () => {
+ const wrapper = mount(
+ <MockedProvider>
+ <Signup />
+ </MockedProvider>
+ );
+ expect(toJSON(wrapper.find('form'))).toMatchSnapshot();
+ });
+
+ it('calls mutation with correct data', async () => {
+ let apolloClient;
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <ApolloConsumer>
+ {client => {
+ apolloClient = client;
+ return <Signup />;
+ }}
+ </ApolloConsumer>
+ </MockedProvider>
+ );
+
+ await wait();
+ wrapper.update();
+
+ type(wrapper, 'name', me.name);
+ type(wrapper, 'email', me.email);
+ type(wrapper, 'password', 'wes');
+
+ wrapper.update();
+ wrapper.find('form').simulate('submit');
+
+ const user = await apolloClient.query({ query: CURRENT_USER_QUERY });
+ expect(user.data.me).toMatchObject(me);
+ });
+});
diff --git a/finished-application/frontend/__tests__/SingleItem.test.js b/finished-application/frontend/__tests__/SingleItem.test.js
new file mode 100644
index 0000000..aeb8554
--- /dev/null
+++ b/finished-application/frontend/__tests__/SingleItem.test.js
@@ -0,0 +1,67 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import wait from 'waait';
+import toJSON from 'enzyme-to-json';
+import SingleItem, { SINGLE_ITEM_QUERY } from '../components/SingleItem';
+import { MockedProvider } from 'react-apollo/test-utils';
+import { fakeItem, fakeUser } from '../lib/testUtils';
+import { CURRENT_USER_QUERY } from '../components/User';
+
+const data = {
+ items: [fakeItem()],
+};
+
+describe('<SingleItem/>', () => {
+ it('Renders with Proper Data', async () => {
+ const mocks = [
+ {
+ request: { query: SINGLE_ITEM_QUERY, variables: { id: '123' } },
+ result: { data },
+ },
+ {
+ request: { query: CURRENT_USER_QUERY },
+ result: { data: { me: fakeUser() } },
+ },
+ ];
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <SingleItem id="123" />
+ </MockedProvider>
+ );
+ // Check for loading state
+ expect(wrapper.text()).toContain('Loading...');
+
+ // wait for response and refresh
+ await wait(55);
+ wrapper.update();
+ // Grab the piece we want
+ expect(toJSON(wrapper.find('h2'))).toMatchSnapshot();
+ expect(toJSON(wrapper.find('img'))).toMatchSnapshot();
+ expect(toJSON(wrapper.find('p'))).toMatchSnapshot();
+ });
+
+ it('Errors with a not found Item', async () => {
+ const mocks = [
+ {
+ request: { query: SINGLE_ITEM_QUERY, variables: { id: '123' } },
+ delay: 50,
+ // we can return data and an error
+ result: { data, errors: [{ message: 'Item not found!' }] },
+ // or just an error!
+ // error: new Error('offline!'),
+ },
+ ];
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <SingleItem id="123" />
+ </MockedProvider>
+ );
+
+ // wait for response
+ await wait(55);
+ wrapper.update();
+
+ const Item = wrapper.find('[data-test="graphql-error"]');
+ expect(toJSON(Item)).toMatchSnapshot();
+ });
+});
diff --git a/finished-application/frontend/__tests__/TakeMyMoney.test.js b/finished-application/frontend/__tests__/TakeMyMoney.test.js
new file mode 100644
index 0000000..1c41410
--- /dev/null
+++ b/finished-application/frontend/__tests__/TakeMyMoney.test.js
@@ -0,0 +1,98 @@
+import { mount } from 'enzyme';
+import toJSON from 'enzyme-to-json';
+import NProgress from 'nprogress';
+import Router from 'next/router';
+import wait from 'waait';
+import { MockedProvider } from 'react-apollo/test-utils';
+import TakeMyMoney from '../components/TakeMyMoney';
+import { fakeUser, fakeCartItem } from '../lib/testUtils';
+import { CURRENT_USER_QUERY } from '../components/User';
+
+Router.router = { push() {} };
+
+const mocks = [
+ {
+ request: { query: CURRENT_USER_QUERY },
+ result: {
+ data: {
+ me: {
+ ...fakeUser(),
+ cart: [fakeCartItem()],
+ },
+ },
+ },
+ },
+];
+describe('<TakeMyMoney />', () => {
+ it('renders', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <TakeMyMoney />
+ </MockedProvider>
+ );
+ // wait for it to load
+ await wait();
+ wrapper.update();
+ // find the button
+ const checkoutButton = wrapper.find('ReactStripeCheckout');
+ expect(toJSON(checkoutButton)).toMatchSnapshot();
+ });
+
+ it('creates an order onToken', async () => {
+ const createOrderSpy = jest.fn().mockResolvedValue({ data: { createOrder: { id: 'xyz789' } } });
+
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <TakeMyMoney />
+ </MockedProvider>
+ );
+ const component = wrapper.find('TakeMyMoney');
+ component.instance().onToken({ id: 'abc123' }, createOrderSpy);
+ // check it
+ expect(createOrderSpy).toBeCalled();
+ expect(createOrderSpy).toBeCalledWith({ variables: { token: 'abc123' } });
+ });
+
+ it('turns the progress bar on', async () => {
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <TakeMyMoney />
+ </MockedProvider>
+ );
+ await wait();
+ wrapper.update();
+ NProgress.start = jest.fn();
+
+ const createOrderSpy = jest.fn().mockResolvedValue({ data: { createOrder: { id: 'xyz789' } } });
+ const component = wrapper.find('TakeMyMoney');
+ component.instance().onToken({ id: 'abc123' }, createOrderSpy);
+ wrapper.find('button').simulate('click');
+ expect(NProgress.start).toHaveBeenCalled();
+ });
+
+ it('routes to the order page when completed', async () => {
+ // Mount the wrapper
+ const wrapper = mount(
+ <MockedProvider mocks={mocks}>
+ <TakeMyMoney />
+ </MockedProvider>
+ );
+ await wait();
+ wrapper.update();
+
+ // Spy on Router Push
+ Router.router.push = jest.fn();
+ const createOrderSpy = jest.fn().mockResolvedValue({ data: { createOrder: { id: 'xyz789' } } });
+
+ const component = wrapper.find('TakeMyMoney');
+ component.instance().onToken({ id: 'abc123' }, createOrderSpy);
+
+ // submit the form
+ wrapper.find('button').simulate('click');
+ await wait();
+ expect(Router.router.push).toHaveBeenCalledWith({
+ pathname: '/order',
+ query: { id: 'xyz789' },
+ });
+ });
+});
diff --git a/finished-application/frontend/__tests__/__snapshots__/AddToCart.test.js.snap b/finished-application/frontend/__tests__/__snapshots__/AddToCart.test.js.snap
new file mode 100644
index 0000000..e3f8229
--- /dev/null
+++ b/finished-application/frontend/__tests__/__snapshots__/AddToCart.test.js.snap
@@ -0,0 +1,11 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`<AddtoCart/> renders and matches snapshot 1`] = `
+<button
+ disabled={false}
+ onClick={[Function]}
+>
+ πŸ›’ Add
+ To Cart
+</button>
+`;
diff --git a/finished-application/frontend/__tests__/__snapshots__/Cart.test.js.snap b/finished-application/frontend/__tests__/__snapshots__/Cart.test.js.snap
new file mode 100644
index 0000000..9aeedfd
--- /dev/null
+++ b/finished-application/frontend/__tests__/__snapshots__/Cart.test.js.snap
@@ -0,0 +1,32 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`<Cart/> renders 1`] = `
+<header>
+ <styled.button
+ onClick={[Function]}
+ title="close"
+ >
+ <button
+ className="sc-EHOje jytiuo"
+ onClick={[Function]}
+ title="close"
+ >
+ Γ—
+ </button>
+ </styled.button>
+ <styled.h3>
+ <h3
+ className="sc-ifAKCX imryuh"
+ >
+ Miss Coleman Berge
+ 's Cart.
+ </h3>
+ </styled.h3>
+ <p>
+ You have
+ 1
+ item
+ in your cart.
+ </p>
+</header>
+`;
diff --git a/finished-application/frontend/__tests__/__snapshots__/CartCount.test.js.snap b/finished-application/frontend/__tests__/__snapshots__/CartCount.test.js.snap
new file mode 100644
index 0000000..224141b
--- /dev/null
+++ b/finished-application/frontend/__tests__/__snapshots__/CartCount.test.js.snap
@@ -0,0 +1,205 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`<CartCount></CartCount> matches snapshot 1`] = `
+<styled.span>
+ <TransitionGroup
+ childFactory={[Function]}
+ component="div"
+ >
+ <CSSTransition
+ className="count"
+ classNames="count"
+ key="10"
+ timeout={
+ Object {
+ "enter": 400,
+ "exit": 400,
+ }
+ }
+ unmountOnExit={true}
+ >
+ <styled.div>
+ 10
+ </styled.div>
+ </CSSTransition>
+ </TransitionGroup>
+</styled.span>
+`;
+
+exports[`<CartCount></CartCount> updates via props 1`] = `
+<CartCount
+ count={50}
+>
+ <styled.span>
+ <span
+ className="sc-bwzfXH fWZfOZ"
+ >
+ <TransitionGroup
+ childFactory={[Function]}
+ component="div"
+ >
+ <div>
+ <CSSTransition
+ className="count"
+ classNames="count"
+ in={true}
+ key=".$50"
+ onExited={[Function]}
+ timeout={
+ Object {
+ "enter": 400,
+ "exit": 400,
+ }
+ }
+ unmountOnExit={true}
+ >
+ <Transition
+ appear={false}
+ className="count"
+ enter={true}
+ exit={true}
+ in={true}
+ mountOnEnter={false}
+ onEnter={[Function]}
+ onEntered={[Function]}
+ onEntering={[Function]}
+ onExit={[Function]}
+ onExited={[Function]}
+ onExiting={[Function]}
+ timeout={
+ Object {
+ "enter": 400,
+ "exit": 400,
+ }
+ }
+ unmountOnExit={true}
+ >
+ <styled.div
+ className="count"
+ >
+ <div
+ className="count sc-bdVaJa ldHGxH"
+ >
+ 50
+ </div>
+ </styled.div>
+ </Transition>
+ </CSSTransition>
+ </div>
+ </TransitionGroup>
+ </span>
+ </styled.span>
+</CartCount>
+`;
+
+exports[`<CartCount></CartCount> updates via props 2`] = `
+<CartCount
+ count={10}
+>
+ <styled.span>
+ <span
+ className="sc-bwzfXH fWZfOZ"
+ >
+ <TransitionGroup
+ childFactory={[Function]}
+ component="div"
+ >
+ <div>
+ <CSSTransition
+ className="count"
+ classNames="count"
+ in={true}
+ key=".$10"
+ onExited={[Function]}
+ timeout={
+ Object {
+ "enter": 400,
+ "exit": 400,
+ }
+ }
+ unmountOnExit={true}
+ >
+ <Transition
+ appear={false}
+ className="count"
+ enter={true}
+ exit={true}
+ in={true}
+ mountOnEnter={false}
+ onEnter={[Function]}
+ onEntered={[Function]}
+ onEntering={[Function]}
+ onExit={[Function]}
+ onExited={[Function]}
+ onExiting={[Function]}
+ timeout={
+ Object {
+ "enter": 400,
+ "exit": 400,
+ }
+ }
+ unmountOnExit={true}
+ >
+ <styled.div
+ className="count"
+ >
+ <div
+ className="count sc-bdVaJa ldHGxH"
+ >
+ 10
+ </div>
+ </styled.div>
+ </Transition>
+ </CSSTransition>
+ <CSSTransition
+ className="count"
+ classNames="count"
+ in={false}
+ key=".$50"
+ onExited={[Function]}
+ timeout={
+ Object {
+ "enter": 400,
+ "exit": 400,
+ }
+ }
+ unmountOnExit={true}
+ >
+ <Transition
+ appear={false}
+ className="count"
+ enter={true}
+ exit={true}
+ in={false}
+ mountOnEnter={false}
+ onEnter={[Function]}
+ onEntered={[Function]}
+ onEntering={[Function]}
+ onExit={[Function]}
+ onExited={[Function]}
+ onExiting={[Function]}
+ timeout={
+ Object {
+ "enter": 400,
+ "exit": 400,
+ }
+ }
+ unmountOnExit={true}
+ >
+ <styled.div
+ className="count"
+ >
+ <div
+ className="count sc-bdVaJa ldHGxH"
+ >
+ 50
+ </div>
+ </styled.div>
+ </Transition>
+ </CSSTransition>
+ </div>
+ </TransitionGroup>
+ </span>
+ </styled.span>
+</CartCount>
+`;
diff --git a/finished-application/frontend/__tests__/__snapshots__/CartItem.test.js.snap b/finished-application/frontend/__tests__/__snapshots__/CartItem.test.js.snap
new file mode 100644
index 0000000..b8f5ad3
--- /dev/null
+++ b/finished-application/frontend/__tests__/__snapshots__/CartItem.test.js.snap
@@ -0,0 +1,33 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`<CartItem> renders and matches snapshot 1`] = `
+<styled.li
+ key="abc123"
+>
+ <img
+ alt="Fake Item"
+ src="dog.jpg"
+ width="100"
+ />
+ <div
+ className="cart-item-details"
+ >
+ <h3>
+ Fake Item
+ </h3>
+ <p>
+ $1,000
+ β€”
+ <em>
+ 20
+ Γ—
+ $50
+ each
+ </em>
+ </p>
+ </div>
+ <RemoveFromCart
+ id="abc123"
+ />
+</styled.li>
+`;
diff --git a/finished-application/frontend/__tests__/__snapshots__/CreateItem.test.js.snap b/finished-application/frontend/__tests__/__snapshots__/CreateItem.test.js.snap
new file mode 100644
index 0000000..89dbcd2
--- /dev/null
+++ b/finished-application/frontend/__tests__/__snapshots__/CreateItem.test.js.snap
@@ -0,0 +1,75 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`<Createitem/> renders and matches snapshot 1`] = `
+<form
+ className="sc-bwzfXH ikCwBB"
+ data-test={true}
+ onSubmit={[Function]}
+>
+ <h2>
+ Sell an Item.
+ </h2>
+ <DisplayError
+ error={Object {}}
+ />
+ <fieldset
+ aria-busy={false}
+ disabled={false}
+ >
+ <label
+ htmlFor="file"
+ >
+ Image
+ <input
+ accept=".png, .jpg, .jpeg"
+ id="file"
+ onChange={[Function]}
+ required={true}
+ type="file"
+ />
+ </label>
+ <label
+ htmlFor="title"
+ >
+ Title
+ <input
+ id="title"
+ name="title"
+ onChange={[Function]}
+ placeholder="Title"
+ required={true}
+ type="text"
+ value=""
+ />
+ </label>
+ <label
+ htmlFor="price"
+ >
+ Price
+ 0
+ <input
+ id="price"
+ min="0"
+ name="price"
+ onChange={[Function]}
+ required={true}
+ type="number"
+ value={0}
+ />
+ </label>
+ <textarea
+ id="description"
+ name="description"
+ onChange={[Function]}
+ placeholder="The desc for this item"
+ required={true}
+ value=""
+ />
+ <button
+ type="submit"
+ >
+ Submit
+ </button>
+ </fieldset>
+</form>
+`;
diff --git a/finished-application/frontend/__tests__/__snapshots__/EditUser.test.js.snap b/finished-application/frontend/__tests__/__snapshots__/EditUser.test.js.snap
new file mode 100644
index 0000000..bbb672d
--- /dev/null
+++ b/finished-application/frontend/__tests__/__snapshots__/EditUser.test.js.snap
@@ -0,0 +1,47 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`<EditUser/> renders 1`] = `
+<form
+ className="sc-bdVaJa LHejR"
+ onSubmit={[Function]}
+>
+ <DisplayError
+ error={Object {}}
+ />
+ <fieldset
+ aria-busy={false}
+ disabled={false}
+ >
+ <label
+ htmlFor="name"
+ >
+ Name:
+ <input
+ defaultValue="Miss Coleman Berge"
+ name="name"
+ onChange={[Function]}
+ type="text"
+ />
+ </label>
+ <button
+ type="submit"
+ >
+ Update
+ </button>
+ <strong>
+ me:
+ </strong>
+ <pre>
+ "Miss Coleman Berge"
+ </pre>
+ <strong>
+ Change:
+ </strong>
+ <pre
+ data-test="change"
+ >
+ {}
+ </pre>
+ </fieldset>
+</form>
+`;
diff --git a/finished-application/frontend/__tests__/__snapshots__/Item.test.js.snap b/finished-application/frontend/__tests__/__snapshots__/Item.test.js.snap
new file mode 100644
index 0000000..930c7a9
--- /dev/null
+++ b/finished-application/frontend/__tests__/__snapshots__/Item.test.js.snap
@@ -0,0 +1,58 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`<Item/> Renders and matches snapshot 1`] = `
+<styled.div
+ key="ABC123"
+>
+ <img
+ alt="A Cool Item"
+ src="dog.jpg"
+ />
+ <styled.h3>
+ <Link
+ href={
+ Object {
+ "pathname": "/item",
+ "query": Object {
+ "id": "ABC123",
+ },
+ }
+ }
+ >
+ <a>
+ A Cool Item
+ </a>
+ </Link>
+ </styled.h3>
+ <styled.span>
+ $50
+ </styled.span>
+ <p>
+ This item is really cool!
+ </p>
+ <div
+ className="buttonList"
+ >
+ <Link
+ href={
+ Object {
+ "pathname": "/update",
+ "query": Object {
+ "id": "ABC123",
+ },
+ }
+ }
+ >
+ <a>
+ Edit ✏️
+ </a>
+ </Link>
+ <AddToCart
+ id="ABC123"
+ />
+ <DeleteItem
+ id="ABC123"
+ />
+ </div>
+</styled.div>
+`;
diff --git a/finished-application/frontend/__tests__/__snapshots__/Nav.test.js.snap b/finished-application/frontend/__tests__/__snapshots__/Nav.test.js.snap
new file mode 100644
index 0000000..354fbc1
--- /dev/null
+++ b/finished-application/frontend/__tests__/__snapshots__/Nav.test.js.snap
@@ -0,0 +1,39 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`<Nav></Nav> Renders minimal nav when logged out 1`] = `
+<ul
+ className="sc-dnqmqq kxZizK"
+ data-test="nav"
+>
+ <Link
+ href="/items"
+ >
+ <a
+ href="/items"
+ onClick={[Function]}
+ >
+ Shop
+ </a>
+ </Link>
+ <Link
+ href="/sell"
+ >
+ <a
+ href="/sell"
+ onClick={[Function]}
+ >
+ Sell
+ </a>
+ </Link>
+ <Link
+ href="/signup"
+ >
+ <a
+ href="/signup"
+ onClick={[Function]}
+ >
+ Sign In
+ </a>
+ </Link>
+</ul>
+`;
diff --git a/finished-application/frontend/__tests__/__snapshots__/Order.test.js.snap b/finished-application/frontend/__tests__/__snapshots__/Order.test.js.snap
new file mode 100644
index 0000000..8e76546
--- /dev/null
+++ b/finished-application/frontend/__tests__/__snapshots__/Order.test.js.snap
@@ -0,0 +1,130 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`<Order/> Renders the order 1`] = `
+<div
+ className="sc-bwzfXH wJijb"
+ data-test="order"
+>
+ <SideEffect(Head)>
+ <Head />
+ </SideEffect(Head)>
+ <p>
+ <span>
+ Order Id:
+ </span>
+ <span>
+ ord123
+ </span>
+ </p>
+ <p>
+ <span>
+ Charge
+ </span>
+ <span>
+ ch_123
+ </span>
+ </p>
+ <p>
+ <span>
+ Date
+ </span>
+ <span>
+ March 90, 2018 8:00 A
+ </span>
+ </p>
+ <p>
+ <span>
+ Order Total
+ </span>
+ <span>
+ $400
+ </span>
+ </p>
+ <p>
+ <span>
+ Item Count
+ </span>
+ <span>
+ 2
+ </span>
+ </p>
+ <div
+ className="items"
+ >
+ <div
+ className="order-item"
+ key="3a430a73-c9e9-4bcc-b46a-41618965ffea"
+ >
+ <img
+ alt="soluta non omnis consequatur enim quia autem"
+ src="reprehenderit.jpg"
+ />
+ <div
+ className="item-details"
+ >
+ <h2>
+
+ soluta non omnis consequatur enim quia autem
+
+ </h2>
+ <p>
+ Qty:
+ 1
+ </p>
+ <p>
+ Each:
+ $42.34
+ </p>
+ <p>
+ Subtotal:
+ $42.34
+ </p>
+ <p>
+ SubTotal:
+ et modi tenetur amet modi reprehenderit omnis
+ </p>
+ <p>
+ et modi tenetur amet modi reprehenderit omnis
+ </p>
+ </div>
+ </div>
+ <div
+ className="order-item"
+ key="2393e090-592f-4d03-b808-c9d81098deec"
+ >
+ <img
+ alt="quia sed exercitationem omnis laborum exercitationem est"
+ src="sint.jpg"
+ />
+ <div
+ className="item-details"
+ >
+ <h2>
+
+ quia sed exercitationem omnis laborum exercitationem est
+
+ </h2>
+ <p>
+ Qty:
+ 1
+ </p>
+ <p>
+ Each:
+ $42.34
+ </p>
+ <p>
+ Subtotal:
+ $42.34
+ </p>
+ <p>
+ SubTotal:
+ sapiente laudantium molestias assumenda quasi adipisci mollitia
+ </p>
+ <p>
+ sapiente laudantium molestias assumenda quasi adipisci mollitia
+ </p>
+ </div>
+ </div>
+ </div>
+</div>
+`;
diff --git a/finished-application/frontend/__tests__/__snapshots__/Pagination.test.js.snap b/finished-application/frontend/__tests__/__snapshots__/Pagination.test.js.snap
new file mode 100644
index 0000000..c757968
--- /dev/null
+++ b/finished-application/frontend/__tests__/__snapshots__/Pagination.test.js.snap
@@ -0,0 +1,71 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`<Pagination/> displays loading message 1`] = `
+<div
+ className="sc-bdVaJa ijYKGd"
+ data-test="pagination"
+>
+ <SideEffect(Head)>
+ <Head />
+ </SideEffect(Head)>
+ <Link
+ href={
+ Object {
+ "pathname": "items",
+ "query": Object {
+ "page": 0,
+ },
+ }
+ }
+ prefetch={true}
+ >
+ <a
+ aria-disabled={true}
+ className="prev"
+ href="items?page=0"
+ onClick={[Function]}
+ >
+ ←Prev
+ </a>
+ </Link>
+ <p>
+ Page
+ <strong>
+ 1
+
+ </strong>
+ of
+ <strong
+ className="totalPages"
+ >
+ 1
+ </strong>
+ </p>
+ <p>
+ <strong>
+ 1
+ </strong>
+ Items Total
+ </p>
+ <Link
+ href={
+ Object {
+ "pathname": "items",
+ "query": Object {
+ "page": 2,
+ },
+ }
+ }
+ prefetch={true}
+ >
+ <a
+ aria-disabled={true}
+ className="next"
+ href="items?page=2"
+ onClick={[Function]}
+ >
+ Next β†’
+ </a>
+ </Link>
+</div>
+`;
diff --git a/finished-application/frontend/__tests__/__snapshots__/RemoveFromCart.test.js.snap b/finished-application/frontend/__tests__/__snapshots__/RemoveFromCart.test.js.snap
new file mode 100644
index 0000000..a80b678
--- /dev/null
+++ b/finished-application/frontend/__tests__/__snapshots__/RemoveFromCart.test.js.snap
@@ -0,0 +1,12 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`<RemoveFromCart/> renders and matches snapshot 1`] = `
+<button
+ className="sc-bdVaJa fXnNqM"
+ disabled={false}
+ onClick={[Function]}
+ title="Remove From Cart"
+>
+ Γ—
+</button>
+`;
diff --git a/finished-application/frontend/__tests__/__snapshots__/ResetRequest.test.js.snap b/finished-application/frontend/__tests__/__snapshots__/ResetRequest.test.js.snap
new file mode 100644
index 0000000..a6a0375
--- /dev/null
+++ b/finished-application/frontend/__tests__/__snapshots__/ResetRequest.test.js.snap
@@ -0,0 +1,35 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`<ResetRequest/> renders and matches snapshot 1`] = `
+<form
+ className="sc-bdVaJa LHejR"
+ data-test="ResetRequest"
+ onSubmit={[Function]}
+>
+ <DisplayError
+ error={Object {}}
+ />
+ <fieldset
+ aria-busy={false}
+ disabled={false}
+ >
+ <label
+ htmlFor="email"
+ >
+ Email
+ <input
+ name="email"
+ onChange={[Function]}
+ placeholder="email"
+ type="text"
+ value=""
+ />
+ </label>
+ <button
+ type="submit"
+ >
+ Request Reset!
+ </button>
+ </fieldset>
+</form>
+`;
diff --git a/finished-application/frontend/__tests__/__snapshots__/Signup.test.js.snap b/finished-application/frontend/__tests__/__snapshots__/Signup.test.js.snap
new file mode 100644
index 0000000..b3c057e
--- /dev/null
+++ b/finished-application/frontend/__tests__/__snapshots__/Signup.test.js.snap
@@ -0,0 +1,64 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`<Signup/> renders and matches snapshot 1`] = `
+<form
+ className="sc-bdVaJa LHejR"
+ method="post"
+ onSubmit={[Function]}
+>
+ <fieldset
+ aria-busy={false}
+ disabled={false}
+ >
+ <DisplayError
+ error={Object {}}
+ />
+ <h2>
+ Sign Up for an Account
+ </h2>
+ <label
+ htmlFor="email"
+ >
+ Email
+ <input
+ name="email"
+ onChange={[Function]}
+ placeholder="email"
+ type="text"
+ value=""
+ />
+ </label>
+ <label
+ htmlFor="name"
+ >
+ Name
+ <input
+ name="name"
+ onChange={[Function]}
+ placeholder="name"
+ type="text"
+ value=""
+ />
+ </label>
+ <label
+ htmlFor="signupPassword"
+ >
+ Password
+ <input
+ className="password"
+ id="signupPassword"
+ name="password"
+ onChange={[Function]}
+ placeholder="password"
+ type="password"
+ value=""
+ />
+ </label>
+ <button
+ type="submit"
+ >
+ Submit
+ </button>
+ </fieldset>
+</form>
+`;
diff --git a/finished-application/frontend/__tests__/__snapshots__/SingleItem.test.js.snap b/finished-application/frontend/__tests__/__snapshots__/SingleItem.test.js.snap
new file mode 100644
index 0000000..28e9174
--- /dev/null
+++ b/finished-application/frontend/__tests__/__snapshots__/SingleItem.test.js.snap
@@ -0,0 +1,32 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`<SingleItem/> Errors with a not found Item 1`] = `
+<p
+ data-test="graphql-error"
+>
+ <strong>
+ Shoot!
+ </strong>
+ Item not found!
+</p>
+`;
+
+exports[`<SingleItem/> Renders with Proper Data 1`] = `
+<h2>
+ Viewing
+ dogs are best
+</h2>
+`;
+
+exports[`<SingleItem/> Renders with Proper Data 2`] = `
+<img
+ alt="dogs are best"
+ src="dog.jpg"
+/>
+`;
+
+exports[`<SingleItem/> Renders with Proper Data 3`] = `
+<p>
+ dogs
+</p>
+`;
diff --git a/finished-application/frontend/__tests__/__snapshots__/TakeMyMoney.test.js.snap b/finished-application/frontend/__tests__/__snapshots__/TakeMyMoney.test.js.snap
new file mode 100644
index 0000000..00863d3
--- /dev/null
+++ b/finished-application/frontend/__tests__/__snapshots__/TakeMyMoney.test.js.snap
@@ -0,0 +1,67 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`<TakeMyMoney /> renders 1`] = `
+<ReactStripeCheckout
+ ComponentClass="span"
+ amount={15000}
+ className="StripeCheckout"
+ currency="USD"
+ description="Order of 3 Items From Sick Fits"
+ email="Delmer.Smith@yahoo.com"
+ image="dog-small.jpg"
+ label="Pay With Card"
+ locale="auto"
+ name="Sick Fits Haul"
+ reconfigureOnUpdate={false}
+ stripeKey="pk_lclTtThFp8CnO3QtEZSd8HA9mFUps"
+ token={[Function]}
+ triggerEvent="onClick"
+>
+ <button
+ className="StripeCheckout"
+ onBlur={[Function]}
+ onClick={[Function]}
+ onFocus={[Function]}
+ onMouseDown={[Function]}
+ onMouseOut={[Function]}
+ onMouseUp={[Function]}
+ style={
+ Object {
+ "background": "linear-gradient(#28a0e5,#015e94)",
+ "border": 0,
+ "borderRadius": 5,
+ "boxShadow": "0 1px 0 rgba(0,0,0,0.2)",
+ "cursor": "pointer",
+ "display": "inline-block",
+ "overflow": "hidden",
+ "padding": 1,
+ "textDecoration": "none",
+ "userSelect": "none",
+ "visibility": "visible",
+ }
+ }
+ >
+ <span
+ style={
+ Object {
+ "backgroundImage": "linear-gradient(#7dc5ee,#008cdd 85%,#30a2e4)",
+ "borderRadius": 4,
+ "boxShadow": "inset 0 1px 0 rgba(255,255,255,0.25)",
+ "color": "#fff",
+ "display": "block",
+ "fontFamily": "\\"Helvetica Neue\\",Helvetica,Arial,sans-serif",
+ "fontSize": 14,
+ "fontWeight": "bold",
+ "height": 30,
+ "lineHeight": "30px",
+ "padding": "0 12px",
+ "position": "relative",
+ "textShadow": "0 -1px 0 rgba(0,0,0,0.25)",
+ }
+ }
+ >
+ Pay With Card
+ </span>
+ </button>
+</ReactStripeCheckout>
+`;
diff --git a/finished-application/frontend/__tests__/formatMoney.test.js b/finished-application/frontend/__tests__/formatMoney.test.js
new file mode 100644
index 0000000..c5c79b6
--- /dev/null
+++ b/finished-application/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');
+ });
+});