diff options
| author | Wes Bos <wesbos@gmail.com> | 2018-09-13 14:18:56 -0400 |
|---|---|---|
| committer | Wes Bos <wesbos@gmail.com> | 2018-09-13 14:18:56 -0400 |
| commit | 8a5825d52271d712ec7c8348447d433067e13ef2 (patch) | |
| tree | ba07c218c1b2d4e59c154f2059b69fd858ae65fe /stepped-solutions/66/frontend | |
| parent | b0d25a9ad3cc1df2fcc11ddb84e4603b94520b09 (diff) | |
DONE
Diffstat (limited to 'stepped-solutions/66/frontend')
6 files changed, 481 insertions, 0 deletions
diff --git a/stepped-solutions/66/frontend/__tests__/Order.test.js b/stepped-solutions/66/frontend/__tests__/Order.test.js new file mode 100755 index 0000000..71accfa --- /dev/null +++ b/stepped-solutions/66/frontend/__tests__/Order.test.js @@ -0,0 +1,27 @@ +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: 'ord123' } }, + result: { data: { order: fakeOrder() } }, + }, +]; + +describe('<Order/>', () => { + it('renders the order', async () => { + const wrapper = mount( + <MockedProvider mocks={mocks}> + <Order id="ord123" /> + </MockedProvider> + ); + await wait(); + wrapper.update(); + const order = wrapper.find('div[data-test="order"]'); + expect(toJSON(order)).toMatchSnapshot(); + }); +}); diff --git a/stepped-solutions/66/frontend/__tests__/TakeMyMoney.test.js b/stepped-solutions/66/frontend/__tests__/TakeMyMoney.test.js new file mode 100755 index 0000000..3d68502 --- /dev/null +++ b/stepped-solutions/66/frontend/__tests__/TakeMyMoney.test.js @@ -0,0 +1,98 @@ +import { mount } from 'enzyme'; +import wait from 'waait'; +import toJSON from 'enzyme-to-json'; +import NProgress from 'nprogress'; +import Router from 'next/router'; +import { MockedProvider } from 'react-apollo/test-utils'; +import { ApolloConsumer } from 'react-apollo'; +import TakeMyMoney, { CREATE_ORDER_MUTATION } from '../components/TakeMyMoney'; +import { CURRENT_USER_QUERY } from '../components/User'; +import { fakeUser, fakeCartItem } from '../lib/testUtils'; + +Router.router = { push() {} }; + +const mocks = [ + { + request: { query: CURRENT_USER_QUERY }, + result: { + data: { + me: { + ...fakeUser(), + cart: [fakeCartItem()], + }, + }, + }, + }, +]; + +describe('<TakeMyMoney/>', () => { + it('renders and matches snapshot', async () => { + const wrapper = mount( + <MockedProvider mocks={mocks}> + <TakeMyMoney /> + </MockedProvider> + ); + await wait(); + wrapper.update(); + const checkoutButton = wrapper.find('ReactStripeCheckout'); + expect(toJSON(checkoutButton)).toMatchSnapshot(); + }); + it('creates an order ontoken', async () => { + const createOrderMock = jest.fn().mockResolvedValue({ + data: { createOrder: { id: 'xyz789' } }, + }); + const wrapper = mount( + <MockedProvider mocks={mocks}> + <TakeMyMoney /> + </MockedProvider> + ); + const component = wrapper.find('TakeMyMoney').instance(); + // manully call that onToken method + component.onToken({ id: 'abc123' }, createOrderMock); + expect(createOrderMock).toHaveBeenCalled(); + expect(createOrderMock).toHaveBeenCalledWith({ 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 createOrderMock = jest.fn().mockResolvedValue({ + data: { createOrder: { id: 'xyz789' } }, + }); + const component = wrapper.find('TakeMyMoney').instance(); + // manully call that onToken method + component.onToken({ id: 'abc123' }, createOrderMock); + expect(NProgress.start).toHaveBeenCalled(); + }); + + it('routes to the order page when completed', async () => { + const wrapper = mount( + <MockedProvider mocks={mocks}> + <TakeMyMoney /> + </MockedProvider> + ); + await wait(); + wrapper.update(); + const createOrderMock = jest.fn().mockResolvedValue({ + data: { createOrder: { id: 'xyz789' } }, + }); + const component = wrapper.find('TakeMyMoney').instance(); + Router.router.push = jest.fn(); + // manully call that onToken method + component.onToken({ id: 'abc123' }, createOrderMock); + await wait(); + expect(Router.router.push).toHaveBeenCalled(); + expect(Router.router.push).toHaveBeenCalledWith({ + pathname: '/order', + query: { + id: 'xyz789', + }, + }); + }); +}); diff --git a/stepped-solutions/66/frontend/__tests__/__snapshots__/Order.test.js.snap b/stepped-solutions/66/frontend/__tests__/__snapshots__/Order.test.js.snap new file mode 100755 index 0000000..9dce80b --- /dev/null +++ b/stepped-solutions/66/frontend/__tests__/__snapshots__/Order.test.js.snap @@ -0,0 +1,118 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`<Order/> renders the order 1`] = ` +<div + className="OrderStyles-sc-4oqalm-0 eDzsVm" + 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 31, 2018 8:00 PM + </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> + 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> + sapiente laudantium molestias assumenda quasi adipisci mollitia + </p> + </div> + </div> + </div> +</div> +`; diff --git a/stepped-solutions/66/frontend/__tests__/__snapshots__/TakeMyMoney.test.js.snap b/stepped-solutions/66/frontend/__tests__/__snapshots__/TakeMyMoney.test.js.snap new file mode 100755 index 0000000..02a112e --- /dev/null +++ b/stepped-solutions/66/frontend/__tests__/__snapshots__/TakeMyMoney.test.js.snap @@ -0,0 +1,67 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`<TakeMyMoney/> renders and matches snapshot 1`] = ` +<ReactStripeCheckout + ComponentClass="span" + amount={15000} + className="StripeCheckout" + currency="USD" + description="Order of 3 items!" + email="Delmer.Smith@yahoo.com" + image="dog-small.jpg" + label="Pay With Card" + locale="auto" + name="Sick Fits" + reconfigureOnUpdate={false} + stripeKey="pk_test_Vtknn6vSdcZWSG2JWvEiWSqC" + 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/stepped-solutions/66/frontend/components/Order.js b/stepped-solutions/66/frontend/components/Order.js new file mode 100755 index 0000000..5177c0a --- /dev/null +++ b/stepped-solutions/66/frontend/components/Order.js @@ -0,0 +1,92 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Query } from 'react-apollo'; +import { format } from 'date-fns'; +import Head from 'next/head'; +import gql from 'graphql-tag'; +import formatMoney from '../lib/formatMoney'; +import Error from './ErrorMessage'; +import OrderStyles from './styles/OrderStyles'; + +const SINGLE_ORDER_QUERY = gql` + query SINGLE_ORDER_QUERY($id: ID!) { + order(id: $id) { + id + charge + total + createdAt + user { + id + } + items { + id + title + description + price + image + quantity + } + } + } +`; + +class Order extends React.Component { + static propTypes = { + id: PropTypes.string.isRequired, + }; + render() { + return ( + <Query query={SINGLE_ORDER_QUERY} variables={{ id: this.props.id }}> + {({ data, error, loading }) => { + if (error) return <Error error={error} />; + if (loading) return <p>Loading...</p>; + const order = data.order; + return ( + <OrderStyles data-test="order"> + <Head> + <title>Sick Fits - Order {order.id}</title> + </Head> + <p> + <span>Order ID:</span> + <span>{this.props.id}</span> + </p> + <p> + <span>Charge</span> + <span>{order.charge}</span> + </p> + <p> + <span>Date</span> + <span>{format(order.createdAt, 'MMMM d, YYYY h:mm a')}</span> + </p> + <p> + <span>Order Total</span> + <span>{formatMoney(order.total)}</span> + </p> + <p> + <span>Item Count</span> + <span>{order.items.length}</span> + </p> + <div className="items"> + {order.items.map(item => ( + <div className="order-item" key={item.id}> + <img src={item.image} alt={item.title} /> + <div className="item-details"> + <h2>{item.title}</h2> + <p>Qty: {item.quantity}</p> + <p>Each: {formatMoney(item.price)}</p> + <p>SubTotal: {formatMoney(item.price * item.quantity)}</p> + <p>{item.description}</p> + </div> + </div> + ))} + </div> + </OrderStyles> + ); + }} + </Query> + ); + } +} + +export default Order; +export { SINGLE_ORDER_QUERY }; diff --git a/stepped-solutions/66/frontend/components/TakeMyMoney.js b/stepped-solutions/66/frontend/components/TakeMyMoney.js new file mode 100755 index 0000000..ca87832 --- /dev/null +++ b/stepped-solutions/66/frontend/components/TakeMyMoney.js @@ -0,0 +1,79 @@ +import React from 'react'; +import StripeCheckout from 'react-stripe-checkout'; +import { Mutation } from 'react-apollo'; +import Router from 'next/router'; +import NProgress from 'nprogress'; +import PropTypes from 'prop-types'; +import gql from 'graphql-tag'; +import calcTotalPrice from '../lib/calcTotalPrice'; +import Error from './ErrorMessage'; +import User, { CURRENT_USER_QUERY } from './User'; + +const CREATE_ORDER_MUTATION = gql` + mutation createOrder($token: String!) { + createOrder(token: $token) { + id + charge + total + items { + id + title + } + } + } +`; + +function totalItems(cart) { + return cart.reduce((tally, cartItem) => tally + cartItem.quantity, 0); +} + +class TakeMyMoney extends React.Component { + onToken = async (res, createOrder) => { + NProgress.start(); + // manually call the mutation once we have the stripe token + const order = await createOrder({ + variables: { + token: res.id, + }, + }).catch(err => { + alert(err.message); + }); + Router.push({ + pathname: '/order', + query: { id: order.data.createOrder.id }, + }); + }; + render() { + return ( + <User> + {({ data: { me }, loading }) => { + if (loading) return null; + return ( + <Mutation + mutation={CREATE_ORDER_MUTATION} + refetchQueries={[{ query: CURRENT_USER_QUERY }]} + > + {createOrder => ( + <StripeCheckout + amount={calcTotalPrice(me.cart)} + name="Sick Fits" + description={`Order of ${totalItems(me.cart)} items!`} + image={me.cart.length && me.cart[0].item && me.cart[0].item.image} + stripeKey="pk_test_Vtknn6vSdcZWSG2JWvEiWSqC" + currency="USD" + email={me.email} + token={res => this.onToken(res, createOrder)} + > + {this.props.children} + </StripeCheckout> + )} + </Mutation> + ); + }} + </User> + ); + } +} + +export default TakeMyMoney; +export { CREATE_ORDER_MUTATION }; |
