diff options
| author | Wes Bos <wesbos@gmail.com> | 2018-09-10 14:29:32 -0400 |
|---|---|---|
| committer | Wes Bos <wesbos@gmail.com> | 2018-09-10 14:29:32 -0400 |
| commit | b0d25a9ad3cc1df2fcc11ddb84e4603b94520b09 (patch) | |
| tree | af1fa8c77e099f52a7fbce3a6ff3e11dec9f24df /stepped-solutions/62 | |
| parent | 093c172d16732ba8d01faa313e0af99d26902205 (diff) | |
almost done
Diffstat (limited to 'stepped-solutions/62')
7 files changed, 436 insertions, 0 deletions
diff --git a/stepped-solutions/62/frontend/__tests__/Nav.test.js b/stepped-solutions/62/frontend/__tests__/Nav.test.js new file mode 100755 index 0000000..d30cb39 --- /dev/null +++ b/stepped-solutions/62/frontend/__tests__/Nav.test.js @@ -0,0 +1,76 @@ +import { mount } from 'enzyme'; +import wait from 'waait'; +import toJSON from 'enzyme-to-json'; +import Nav from '../components/Nav'; +import { CURRENT_USER_QUERY } from '../components/User'; +import { MockedProvider } from 'react-apollo/test-utils'; +import { fakeUser, fakeCartItem } from '../lib/testUtils'; + +const notSignedInMocks = [ + { + request: { query: CURRENT_USER_QUERY }, + result: { data: { me: null } }, + }, +]; + +const signedInMocks = [ + { + request: { query: CURRENT_USER_QUERY }, + result: { data: { me: fakeUser() } }, + }, +]; + +const signedInMocksWithCartItems = [ + { + request: { query: CURRENT_USER_QUERY }, + result: { + data: { + me: { + ...fakeUser(), + cart: [fakeCartItem(), fakeCartItem(), fakeCartItem()], + }, + }, + }, + }, +]; + +describe('<Nav/>', () => { + it('renders a minimal nav when signed out', async () => { + const wrapper = mount( + <MockedProvider mocks={notSignedInMocks}> + <Nav /> + </MockedProvider> + ); + await wait(); + wrapper.update(); + // console.log(wrapper.debug()); + const nav = wrapper.find('ul[data-test="nav"]'); + expect(toJSON(nav)).toMatchSnapshot(); + }); + + it('renders full nav when signed in', async () => { + const wrapper = mount( + <MockedProvider mocks={signedInMocks}> + <Nav /> + </MockedProvider> + ); + await wait(); + wrapper.update(); + const nav = wrapper.find('ul[data-test="nav"]'); + expect(nav.children().length).toBe(6); + expect(nav.text()).toContain('Sign Out'); + }); + + it('renders the amount of items in the cart', async () => { + const wrapper = mount( + <MockedProvider mocks={signedInMocksWithCartItems}> + <Nav /> + </MockedProvider> + ); + await wait(); + wrapper.update(); + const nav = wrapper.find('[data-test="nav"]'); + const count = nav.find('div.count'); + expect(toJSON(count)).toMatchSnapshot(); + }); +}); diff --git a/stepped-solutions/62/frontend/__tests__/Pagination.test.js b/stepped-solutions/62/frontend/__tests__/Pagination.test.js new file mode 100755 index 0000000..7117db8 --- /dev/null +++ b/stepped-solutions/62/frontend/__tests__/Pagination.test.js @@ -0,0 +1,89 @@ +import { mount } from 'enzyme'; +import wait from 'waait'; +import toJSON from 'enzyme-to-json'; +import Router from 'next/router'; +import Pagination, { PAGINATION_QUERY } from '../components/Pagination'; +import { MockedProvider } from 'react-apollo/test-utils'; + +Router.router = { + push() {}, + prefetch() {}, +}; + +function makeMocksFor(length) { + return [ + { + request: { query: PAGINATION_QUERY }, + result: { + data: { + itemsConnection: { + __typename: 'aggregate', + aggregate: { + count: length, + __typename: 'count', + }, + }, + }, + }, + }, + ]; +} + +describe('<Pagination/>', () => { + it('displays a loading message', () => { + const wrapper = mount( + <MockedProvider mocks={makeMocksFor(1)}> + <Pagination page={1} /> + </MockedProvider> + ); + const pagination = wrapper.find('[data-test="pagination"]'); + expect(wrapper.text()).toContain('Loading...'); + }); + + 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'); + const pagination = wrapper.find('div[data-test="pagination"]'); + expect(toJSON(pagination)).toMatchSnapshot(); + }); + + it('disables prev button on first page', async () => { + const wrapper = mount( + <MockedProvider mocks={makeMocksFor(18)}> + <Pagination page={1} /> + </MockedProvider> + ); + await wait(); + wrapper.update(); + 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(); + 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={3} /> + </MockedProvider> + ); + await wait(); + wrapper.update(); + expect(wrapper.find('a.prev').prop('aria-disabled')).toEqual(false); + expect(wrapper.find('a.next').prop('aria-disabled')).toEqual(false); + }); +}); diff --git a/stepped-solutions/62/frontend/__tests__/PleaseSignIn.test.js b/stepped-solutions/62/frontend/__tests__/PleaseSignIn.test.js new file mode 100755 index 0000000..7d476b4 --- /dev/null +++ b/stepped-solutions/62/frontend/__tests__/PleaseSignIn.test.js @@ -0,0 +1,51 @@ +import { mount } from 'enzyme'; +import wait from 'waait'; +import PleaseSignIn from '../components/PleaseSignIn'; +import { CURRENT_USER_QUERY } from '../components/User'; +import { MockedProvider } from 'react-apollo/test-utils'; +import { fakeUser } from '../lib/testUtils'; + +const notSignedInMocks = [ + { + request: { query: CURRENT_USER_QUERY }, + result: { data: { me: null } }, + }, +]; + +const signedInMocks = [ + { + request: { query: CURRENT_USER_QUERY }, + result: { data: { me: fakeUser() } }, + }, +]; + +describe('<PleaseSignIn/>', () => { + it('renders the sign in dialog to logged out users', async () => { + const wrapper = mount( + <MockedProvider mocks={notSignedInMocks}> + <PleaseSignIn /> + </MockedProvider> + ); + await wait(); + wrapper.update(); + expect(wrapper.text()).toContain('Please Sign In before Continuing'); + const SignIn = wrapper.find('Signin'); + expect(SignIn.exists()).toBe(true); + }); + + it('renders the child component when the user is signed in', async () => { + const Hey = () => <p>Hey!</p>; + const wrapper = mount( + <MockedProvider mocks={signedInMocks}> + <PleaseSignIn> + <Hey /> + </PleaseSignIn> + </MockedProvider> + ); + + await wait(); + wrapper.update(); + // expect(wrapper.find('Hey').exists()).toBe(true); + expect(wrapper.contains(<Hey />)).toBe(true); + }); +}); diff --git a/stepped-solutions/62/frontend/__tests__/__snapshots__/Nav.test.js.snap b/stepped-solutions/62/frontend/__tests__/__snapshots__/Nav.test.js.snap new file mode 100755 index 0000000..9a5a14c --- /dev/null +++ b/stepped-solutions/62/frontend/__tests__/__snapshots__/Nav.test.js.snap @@ -0,0 +1,37 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`<Nav/> renders a minimal nav when signed out 1`] = ` +<ul + className="NavStyles-s11c0d2g-0 ddqXnG" + data-test="nav" +> + <Link + href="/items" + > + <a + href="/items" + onClick={[Function]} + > + Shop + </a> + </Link> + <Link + href="/signup" + > + <a + href="/signup" + onClick={[Function]} + > + Sign In + </a> + </Link> +</ul> +`; + +exports[`<Nav/> renders the amount of items in the cart 1`] = ` +<div + className="count CartCount__Dot-xxvp4g-1 fJsVOg" +> + 9 +</div> +`; diff --git a/stepped-solutions/62/frontend/__tests__/__snapshots__/Pagination.test.js.snap b/stepped-solutions/62/frontend/__tests__/__snapshots__/Pagination.test.js.snap new file mode 100755 index 0000000..110f59d --- /dev/null +++ b/stepped-solutions/62/frontend/__tests__/__snapshots__/Pagination.test.js.snap @@ -0,0 +1,67 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`<Pagination/> renders pagination for 18 items 1`] = ` +<div + className="PaginationStyles-aduuar-0 ewJsCc" + 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 + 1 + of + <span + className="totalPages" + > + 5 + </span> + ! + </p> + <p> + 18 + Items Total + </p> + <Link + href={ + Object { + "pathname": "items", + "query": Object { + "page": 2, + }, + } + } + prefetch={true} + > + <a + aria-disabled={false} + className="next" + href="items?page=2" + onClick={[Function]} + > + Next → + </a> + </Link> +</div> +`; diff --git a/stepped-solutions/62/frontend/components/Nav.js b/stepped-solutions/62/frontend/components/Nav.js new file mode 100755 index 0000000..118db5a --- /dev/null +++ b/stepped-solutions/62/frontend/components/Nav.js @@ -0,0 +1,49 @@ +import Link from 'next/link'; +import { Mutation } from 'react-apollo'; +import { TOGGLE_CART_MUTATION } from './Cart'; +import NavStyles from './styles/NavStyles'; +import User from './User'; +import CartCount from './CartCount'; +import Signout from './Signout'; + +const Nav = () => ( + <User> + {({ data: { me } }) => ( + <NavStyles data-test="nav"> + <Link href="/items"> + <a>Shop</a> + </Link> + {me && ( + <> + <Link href="/sell"> + <a>Sell</a> + </Link> + <Link href="/orders"> + <a>Orders</a> + </Link> + <Link href="/me"> + <a>Account</a> + </Link> + <Signout /> + <Mutation mutation={TOGGLE_CART_MUTATION}> + {(toggleCart) => ( + <button onClick={toggleCart}> + My Cart + <CartCount count={me.cart.reduce((tally, cartItem) => tally + cartItem.quantity, 0)}></CartCount> + </button> + )} + </Mutation> + </> + )} + {!me && ( + <Link href="/signup"> + <a>Sign In</a> + </Link> + + )} + </NavStyles> + )} + </User> +); + +export default Nav; diff --git a/stepped-solutions/62/frontend/components/Pagination.js b/stepped-solutions/62/frontend/components/Pagination.js new file mode 100755 index 0000000..b84af76 --- /dev/null +++ b/stepped-solutions/62/frontend/components/Pagination.js @@ -0,0 +1,67 @@ +import React from 'react'; +import gql from 'graphql-tag'; +import { Query } from 'react-apollo'; +import Head from 'next/head'; +import Link from 'next/link'; +import PaginationStyles from './styles/PaginationStyles'; +import { perPage } from '../config'; + +const PAGINATION_QUERY = gql` + query PAGINATION_QUERY { + itemsConnection { + aggregate { + count + } + } + } +`; + +const Pagination = props => ( + <Query query={PAGINATION_QUERY}> + {({ data, loading, error }) => { + if (loading) return <p>Loading...</p>; + const count = data.itemsConnection.aggregate.count; + const pages = Math.ceil(count / perPage); + const page = props.page; + return ( + <PaginationStyles data-test="pagination"> + <Head> + <title> + Sick Fits! — Page {page} of {pages} + </title> + </Head> + <Link + prefetch + href={{ + pathname: 'items', + query: { page: page - 1 }, + }} + > + <a className="prev" aria-disabled={page <= 1}> + ← Prev + </a> + </Link> + <p> + Page {props.page} of + <span className="totalPages">{pages}</span>! + </p> + <p>{count} Items Total</p> + <Link + prefetch + href={{ + pathname: 'items', + query: { page: page + 1 }, + }} + > + <a className="next" aria-disabled={page >= pages}> + Next → + </a> + </Link> + </PaginationStyles> + ); + }} + </Query> +); + +export default Pagination; +export { PAGINATION_QUERY }; |
