summaryrefslogtreecommitdiffstats
path: root/stepped-solutions/66/frontend
diff options
context:
space:
mode:
authorWes Bos <wesbos@gmail.com>2018-09-13 14:18:56 -0400
committerWes Bos <wesbos@gmail.com>2018-09-13 14:18:56 -0400
commit8a5825d52271d712ec7c8348447d433067e13ef2 (patch)
treeba07c218c1b2d4e59c154f2059b69fd858ae65fe /stepped-solutions/66/frontend
parentb0d25a9ad3cc1df2fcc11ddb84e4603b94520b09 (diff)
DONE
Diffstat (limited to 'stepped-solutions/66/frontend')
-rwxr-xr-xstepped-solutions/66/frontend/__tests__/Order.test.js27
-rwxr-xr-xstepped-solutions/66/frontend/__tests__/TakeMyMoney.test.js98
-rwxr-xr-xstepped-solutions/66/frontend/__tests__/__snapshots__/Order.test.js.snap118
-rwxr-xr-xstepped-solutions/66/frontend/__tests__/__snapshots__/TakeMyMoney.test.js.snap67
-rwxr-xr-xstepped-solutions/66/frontend/components/Order.js92
-rwxr-xr-xstepped-solutions/66/frontend/components/TakeMyMoney.js79
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 };