diff --git a/README.md b/README.md index 618f105..6364582 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,9 @@
+[![NPM Version][npm-image]][npm-url] [![Build Status][travis]][travis] +[![Coverage Status][coverage-badge]][coverage-link]
@@ -44,6 +46,6 @@ A game about space and junk. [npm-downloads]: https://img.shields.io/npm/dt/hermes-gaame.svg [npm-url]: https://www.npmjs.com/package/hermes-gaame [size-image]: https://img.shields.io/bundlephobia/minzip/hermes-game.svg -[coverage-badge]: https://coveralls.io/repos/github/alexlee-dev/hermes/badge.svg?branch=master +[coverage-badge]: https://coveralls.io/repos/github/alexlee-dev/hermes/badge.svg?branch=develop [coverage-link]: https://coveralls.io/github/alexlee-dev/hermes?branch=master [travis]: https://travis-ci.org/alexlee-dev/hermes.svg?branch=master diff --git a/package.json b/package.json index cd55434..70abeaa 100644 --- a/package.json +++ b/package.json @@ -34,16 +34,22 @@ "uuid": "^3.3.3" }, "devDependencies": { + "@testing-library/dom": "^6.1.0", + "@testing-library/react": "^9.1.3", "coveralls": "^3.0.6", "enzyme": "^3.10.0", - "enzyme-adapter-react-16": "^1.14.0" + "enzyme-adapter-react-16": "^1.14.0", + "jsdom": "^15.1.1", + "jsdom-global": "^3.0.2", + "redux-mock-store": "^1.5.3" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "eject": "react-scripts eject", "test": "react-scripts test", - "test:coveralls": "jest --coverage --collectCoverageFrom=src/**/* --coverageReporters=text-lcov | coveralls" + "test:coverage": "yarn test --coverage --watchAll=false --collectCoverageFrom=src/components/**/* --collectCoverageFrom=src/views/**/*", + "test:coveralls": "yarn test --coverage --watchAll=false --collectCoverageFrom=src/components/**/* --collectCoverageFrom=src/views/**/* --coverageReporters=text-lcov | coveralls" }, "eslintConfig": { "extends": "react-app" diff --git a/src/__tests__/App.test.js b/src/__tests__/App.test.js index 2980574..e506e07 100644 --- a/src/__tests__/App.test.js +++ b/src/__tests__/App.test.js @@ -1,5 +1,14 @@ -describe('Fake Test', () => { - it('Should pass.', () => { - expect(1 + 1).toBe(2) +import React from 'react' +import { customRender } from '../test-utils' +import App from '../App' + +jest.mock('../components/ItemTimer', () => { + return () => MockedTime +}) + +describe('', () => { + it('Should render the component.', () => { + const container = customRender({ component: App }) + expect(container.asFragment()).toMatchSnapshot() }) }) diff --git a/src/__tests__/__snapshots__/App.test.js.snap b/src/__tests__/__snapshots__/App.test.js.snap new file mode 100644 index 0000000..9951797 --- /dev/null +++ b/src/__tests__/__snapshots__/App.test.js.snap @@ -0,0 +1,100 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` Should render the component. 1`] = ` + +
+

+ Hermes +

+ + MockedTime + +
+

+ Cash: +

+ + 100 + +
+
+ +
+
+

+ Your Ship +

+

+ Cargo +

+
+ + Volume Remaining: + + + 5 + +
+

+ Location: +

+
+ + Value: + +
+ +
+ + Name: + +
+ +
+
+
+ +`; diff --git a/src/__tests__/components/CashDisplay.test.js b/src/__tests__/components/CashDisplay.test.js new file mode 100644 index 0000000..4acc538 --- /dev/null +++ b/src/__tests__/components/CashDisplay.test.js @@ -0,0 +1,9 @@ +import { customRender } from '../../test-utils' +import CashDisplay from '../../components/CashDisplay' + +describe('', () => { + it('Should render the component.', () => { + const container = customRender({ component: CashDisplay }) + expect(container.asFragment()).toMatchSnapshot() + }) +}) diff --git a/src/__tests__/components/ItemTimer.test.js b/src/__tests__/components/ItemTimer.test.js new file mode 100644 index 0000000..00359e2 --- /dev/null +++ b/src/__tests__/components/ItemTimer.test.js @@ -0,0 +1,17 @@ +import { customRender } from '../../test-utils' +import ItemTimer from '../../components/ItemTimer' + +jest.mock('../../util', () => { + const moment = require('moment') + const mockCreateDuration = () => moment.duration({ minutes: 60, seconds: 0 }) + return { + createDuration: mockCreateDuration + } +}) + +describe('', () => { + it('Should render the component.', () => { + const container = customRender({ component: ItemTimer }) + expect(container.asFragment()).toMatchSnapshot() + }) +}) diff --git a/src/__tests__/components/Title.test.js b/src/__tests__/components/Title.test.js new file mode 100644 index 0000000..12387e1 --- /dev/null +++ b/src/__tests__/components/Title.test.js @@ -0,0 +1,9 @@ +import Title from '../../components/Title' +import { customRender } from '../../test-utils' + +describe('', () => { + it('Should render the <Title /> component.', () => { + const container = customRender({ component: Title }) + expect(container.asFragment()).toMatchSnapshot() + }) +}) diff --git a/src/__tests__/components/ViewSelector.test.js b/src/__tests__/components/ViewSelector.test.js new file mode 100644 index 0000000..1b3e507 --- /dev/null +++ b/src/__tests__/components/ViewSelector.test.js @@ -0,0 +1,20 @@ +import { fireEvent, waitForElement } from '@testing-library/react' +import { customRender } from '../../test-utils' +import ViewSelector from '../../components/ViewSelector' + +describe('<ViewSelector />', () => { + it('Should render the <ViewSelector /> component.', () => { + const container = customRender({ component: ViewSelector }) + expect(container.asFragment()).toMatchSnapshot() + }) + + it('Should handle changing the ViewSelector.', async () => { + const { getByTestId } = customRender({ component: ViewSelector }) + + const selector = await waitForElement(() => getByTestId('view-selector')) + + expect(getByTestId('view-selector').value).toBe('Ship') + fireEvent.change(selector, { target: { value: 'Planets' } }) + expect(getByTestId('view-selector').value).toBe('Planets') + }) +}) diff --git a/src/__tests__/components/__snapshots__/CashDisplay.test.js.snap b/src/__tests__/components/__snapshots__/CashDisplay.test.js.snap new file mode 100644 index 0000000..93483c7 --- /dev/null +++ b/src/__tests__/components/__snapshots__/CashDisplay.test.js.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`<CashDisplay /> Should render the <CashDisplay /> component. 1`] = ` +<DocumentFragment> + <div + class="StyledBox-sc-13pk1d4-0 CAsMy" + > + <h2 + class="StyledHeading-sc-1rdh4aw-0 gBloPj" + > + Cash: + </h2> + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + 100 + </span> + </div> +</DocumentFragment> +`; diff --git a/src/__tests__/components/__snapshots__/ItemTimer.test.js.snap b/src/__tests__/components/__snapshots__/ItemTimer.test.js.snap new file mode 100644 index 0000000..776afe6 --- /dev/null +++ b/src/__tests__/components/__snapshots__/ItemTimer.test.js.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`<ItemTimer /> Should render the <ItemTimer /> component. 1`] = ` +<DocumentFragment> + <span> + 0 minutes 0 seconds + </span> +</DocumentFragment> +`; diff --git a/src/__tests__/components/__snapshots__/Title.test.js.snap b/src/__tests__/components/__snapshots__/Title.test.js.snap new file mode 100644 index 0000000..fbee73a --- /dev/null +++ b/src/__tests__/components/__snapshots__/Title.test.js.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`<Title /> Should render the <Title /> component. 1`] = ` +<DocumentFragment> + <h1 + class="StyledHeading-sc-1rdh4aw-0 rfVJR" + > + Hermes + </h1> +</DocumentFragment> +`; diff --git a/src/__tests__/components/__snapshots__/ViewSelector.test.js.snap b/src/__tests__/components/__snapshots__/ViewSelector.test.js.snap new file mode 100644 index 0000000..4b99a6c --- /dev/null +++ b/src/__tests__/components/__snapshots__/ViewSelector.test.js.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`<ViewSelector /> Should render the <ViewSelector /> component. 1`] = ` +<DocumentFragment> + <div + class="StyledBox-sc-13pk1d4-0 dfMVfp" + > + <select + data-testid="view-selector" + > + <option> + Ship + </option> + <option> + Planets + </option> + </select> + </div> +</DocumentFragment> +`; diff --git a/src/__tests__/views/__snapshots__/planets.js.snap b/src/__tests__/views/__snapshots__/planets.js.snap new file mode 100644 index 0000000..9e3e64e --- /dev/null +++ b/src/__tests__/views/__snapshots__/planets.js.snap @@ -0,0 +1,1032 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`<PlanetsView /> Should render the <PlanetsView /> component. 1`] = ` +<DocumentFragment> + <div> + <div> + <div + class="StyledBox-sc-13pk1d4-0 ffeAql" + > + <h2 + class="StyledHeading-sc-1rdh4aw-0 yhirM" + > + Test Planet 1 - Home Planet + </h2> + </div> + <span> + Items: + </span> + <div + class="StyledBox-sc-13pk1d4-0 xqboQ" + > + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Name: Test Item + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Quantity: + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Space: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Value: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Destination: Test Planet 2 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <button + class="StyledButton-sc-323bzc-0 jJyttA" + data-testid="add-button-0" + disabled="" + type="button" + > + <svg + aria-label="Add" + class="StyledIcon-ofa7kd-0 iOkQrb" + viewBox="0 0 24 24" + > + <path + d="M12,22 L12,2 M2,12 L22,12" + fill="none" + stroke="#000" + stroke-width="2" + /> + </svg> + </button> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hPsznL" + /> + <label + for="quantity-input-0" + > + Quantity to Add + </label> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hPsznL" + /> + <input + id="quantity-input-0" + max="4" + min="0" + type="number" + value="0" + /> + </div> + </div> + <div + class="StyledBox-sc-13pk1d4-0 xqboQ" + > + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Name: Test Item + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Quantity: + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Space: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Value: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Destination: Test Planet 2 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <button + class="StyledButton-sc-323bzc-0 jJyttA" + data-testid="add-button-1" + disabled="" + type="button" + > + <svg + aria-label="Add" + class="StyledIcon-ofa7kd-0 iOkQrb" + viewBox="0 0 24 24" + > + <path + d="M12,22 L12,2 M2,12 L22,12" + fill="none" + stroke="#000" + stroke-width="2" + /> + </svg> + </button> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hPsznL" + /> + <label + for="quantity-input-1" + > + Quantity to Add + </label> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hPsznL" + /> + <input + id="quantity-input-1" + max="4" + min="0" + type="number" + value="0" + /> + </div> + </div> + <div + class="StyledBox-sc-13pk1d4-0 xqboQ" + > + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Name: Test Item + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Quantity: + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Space: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Value: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Destination: Test Planet 2 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <button + class="StyledButton-sc-323bzc-0 jJyttA" + data-testid="add-button-2" + disabled="" + type="button" + > + <svg + aria-label="Add" + class="StyledIcon-ofa7kd-0 iOkQrb" + viewBox="0 0 24 24" + > + <path + d="M12,22 L12,2 M2,12 L22,12" + fill="none" + stroke="#000" + stroke-width="2" + /> + </svg> + </button> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hPsznL" + /> + <label + for="quantity-input-2" + > + Quantity to Add + </label> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hPsznL" + /> + <input + id="quantity-input-2" + max="4" + min="0" + type="number" + value="0" + /> + </div> + </div> + <div + class="StyledBox-sc-13pk1d4-0 xqboQ" + > + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Name: Test Item + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Quantity: + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Space: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Value: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Destination: Test Planet 2 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <button + class="StyledButton-sc-323bzc-0 jJyttA" + data-testid="add-button-3" + disabled="" + type="button" + > + <svg + aria-label="Add" + class="StyledIcon-ofa7kd-0 iOkQrb" + viewBox="0 0 24 24" + > + <path + d="M12,22 L12,2 M2,12 L22,12" + fill="none" + stroke="#000" + stroke-width="2" + /> + </svg> + </button> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hPsznL" + /> + <label + for="quantity-input-3" + > + Quantity to Add + </label> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hPsznL" + /> + <input + id="quantity-input-3" + max="4" + min="0" + type="number" + value="0" + /> + </div> + </div> + <div + class="StyledBox-sc-13pk1d4-0 xqboQ" + > + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Name: Test Item + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Quantity: + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Space: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Value: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Destination: Test Planet 2 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <button + class="StyledButton-sc-323bzc-0 jJyttA" + data-testid="add-button-4" + disabled="" + type="button" + > + <svg + aria-label="Add" + class="StyledIcon-ofa7kd-0 iOkQrb" + viewBox="0 0 24 24" + > + <path + d="M12,22 L12,2 M2,12 L22,12" + fill="none" + stroke="#000" + stroke-width="2" + /> + </svg> + </button> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hPsznL" + /> + <label + for="quantity-input-4" + > + Quantity to Add + </label> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hPsznL" + /> + <input + id="quantity-input-4" + max="4" + min="0" + type="number" + value="0" + /> + </div> + </div> + </div> + <div> + <div + class="StyledBox-sc-13pk1d4-0 ffeAql" + > + <h2 + class="StyledHeading-sc-1rdh4aw-0 yhirM" + > + Test Planet 2 + </h2> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hyyRSo" + /> + <button + class="StyledButton-sc-323bzc-0 jAPRVi" + data-testid="travel-button-Test Planet 2" + type="button" + > + <svg + aria-label="Target" + class="StyledIcon-ofa7kd-0 iOkQrb" + viewBox="0 0 24 24" + > + <path + d="M23,12 C23,18.075 18.075,23 12,23 C5.925,23 1,18.075 1,12 C1,5.925 5.925,1 12,1 C18.075,1 23,5.925 23,12 L23,12 Z M18,12 C18,8.691 15.309,6 12,6 C8.691,6 6,8.691 6,12 C6,15.309 8.691,18 12,18 C15.309,18 18,15.309 18,12 L18,12 Z M13,12 C13,11.448 12.552,11 12,11 C11.448,11 11,11.448 11,12 C11,12.552 11.448,13 12,13 C12.552,13 13,12.552 13,12 L13,12 Z" + fill="none" + stroke="#000" + stroke-width="2" + /> + </svg> + </button> + </div> + <span> + Items: + </span> + <div + class="StyledBox-sc-13pk1d4-0 xqboQ" + > + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Name: Test Item + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Quantity: + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Space: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Value: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Destination: Test Planet 1 + </span> + </div> + </div> + <div + class="StyledBox-sc-13pk1d4-0 xqboQ" + > + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Name: Test Item + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Quantity: + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Space: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Value: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Destination: Test Planet 1 + </span> + </div> + </div> + <div + class="StyledBox-sc-13pk1d4-0 xqboQ" + > + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Name: Test Item + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Quantity: + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Space: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Value: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Destination: Test Planet 1 + </span> + </div> + </div> + <div + class="StyledBox-sc-13pk1d4-0 xqboQ" + > + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Name: Test Item + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Quantity: + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Space: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Value: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Destination: Test Planet 1 + </span> + </div> + </div> + <div + class="StyledBox-sc-13pk1d4-0 xqboQ" + > + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Name: Test Item + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Quantity: + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Space: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Value: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Destination: Test Planet 1 + </span> + </div> + </div> + </div> + <div> + <div + class="StyledBox-sc-13pk1d4-0 ffeAql" + > + <h2 + class="StyledHeading-sc-1rdh4aw-0 yhirM" + > + Test Planet 3 + </h2> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hyyRSo" + /> + <button + class="StyledButton-sc-323bzc-0 jAPRVi" + data-testid="travel-button-Test Planet 3" + type="button" + > + <svg + aria-label="Target" + class="StyledIcon-ofa7kd-0 iOkQrb" + viewBox="0 0 24 24" + > + <path + d="M23,12 C23,18.075 18.075,23 12,23 C5.925,23 1,18.075 1,12 C1,5.925 5.925,1 12,1 C18.075,1 23,5.925 23,12 L23,12 Z M18,12 C18,8.691 15.309,6 12,6 C8.691,6 6,8.691 6,12 C6,15.309 8.691,18 12,18 C15.309,18 18,15.309 18,12 L18,12 Z M13,12 C13,11.448 12.552,11 12,11 C11.448,11 11,11.448 11,12 C11,12.552 11.448,13 12,13 C12.552,13 13,12.552 13,12 L13,12 Z" + fill="none" + stroke="#000" + stroke-width="2" + /> + </svg> + </button> + </div> + <span> + Items: + </span> + <div + class="StyledBox-sc-13pk1d4-0 xqboQ" + > + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Name: Test Item + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Quantity: + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Space: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Value: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Destination: Test Planet 2 + </span> + </div> + </div> + <div + class="StyledBox-sc-13pk1d4-0 xqboQ" + > + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Name: Test Item + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Quantity: + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Space: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Value: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Destination: Test Planet 2 + </span> + </div> + </div> + <div + class="StyledBox-sc-13pk1d4-0 xqboQ" + > + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Name: Test Item + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Quantity: + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Space: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Value: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Destination: Test Planet 2 + </span> + </div> + </div> + <div + class="StyledBox-sc-13pk1d4-0 xqboQ" + > + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Name: Test Item + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Quantity: + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Space: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Value: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Destination: Test Planet 2 + </span> + </div> + </div> + <div + class="StyledBox-sc-13pk1d4-0 xqboQ" + > + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Name: Test Item + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Quantity: + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Space: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Value: 1 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 hroMnY" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Destination: Test Planet 2 + </span> + </div> + </div> + </div> + </div> +</DocumentFragment> +`; diff --git a/src/__tests__/views/__snapshots__/ship.js.snap b/src/__tests__/views/__snapshots__/ship.js.snap new file mode 100644 index 0000000..af80e3f --- /dev/null +++ b/src/__tests__/views/__snapshots__/ship.js.snap @@ -0,0 +1,122 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`<ShipView /> Should render the <ShipView /> component. 1`] = ` +<DocumentFragment> + <div> + <h2> + Your Ship + </h2> + <h3> + Cargo + </h3> + <div + class="StyledBox-sc-13pk1d4-0 IRSNj" + > + <span + class="StyledText-sc-1sadyjn-0 bnqisW" + > + Volume Remaining: + </span> + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + 4 + </span> + </div> + <div + class="StyledBox-sc-13pk1d4-0 ffeAql" + > + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Test Item + </span> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hyyRSo" + /> + <span + class="StyledText-sc-1sadyjn-0 bnqisW" + > + Destination: + </span> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hyyRSo" + /> + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + Test Planet 2 + </span> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hyyRSo" + /> + <span + class="StyledText-sc-1sadyjn-0 bnqisW" + > + Quantity: + </span> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hyyRSo" + /> + <span + class="StyledText-sc-1sadyjn-0 fWSbXS" + > + 1 + </span> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hyyRSo" + /> + <button + class="StyledButton-sc-323bzc-0 jAPRVi" + data-testid="remove-button-0" + type="button" + > + <svg + aria-label="Subtract" + class="StyledIcon-ofa7kd-0 iOkQrb" + viewBox="0 0 24 24" + > + <path + d="M2,12 L22,12" + fill="none" + stroke="#000" + stroke-width="2" + /> + </svg> + </button> + </div> + <h3> + Location: + </h3> + <div + class="StyledBox-sc-13pk1d4-0 MmWcm" + > + <span + class="StyledText-sc-1sadyjn-0 isVaoK" + > + Value: + </span> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hPsznL" + /> + <span + class="StyledText-sc-1sadyjn-0 gNZQxf" + /> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hPsznL" + /> + <span + class="StyledText-sc-1sadyjn-0 isVaoK" + > + Name: + </span> + <div + class="StyledBox__StyledBoxGap-sc-13pk1d4-1 hPsznL" + /> + <span + class="StyledText-sc-1sadyjn-0 gNZQxf" + /> + </div> + </div> +</DocumentFragment> +`; diff --git a/src/__tests__/views/planets.js b/src/__tests__/views/planets.js new file mode 100644 index 0000000..8e3d2ee --- /dev/null +++ b/src/__tests__/views/planets.js @@ -0,0 +1,58 @@ +import { customRender } from '../../test-utils' +import { defaultState } from '../../fixtures' +import PlanetsView from '../../views/planets' +import { waitForElement, fireEvent } from '@testing-library/dom' + +const customState = { + ...defaultState, + ship: { + ...defaultState.ship, + location: { name: 'Test Planet 1', value: 0 }, + cargo: { + items: [ + { + name: 'Test Item', + space: 1, + quantity: 1, + value: 1, + id: '0', + destination: { + name: 'Test Planet 2', + value: 50 + } + } + ], + volumeRemaining: 4 + } + } +} + +describe('<PlanetsView />', () => { + it('Should render the <PlanetsView /> component.', () => { + const container = customRender({ + component: PlanetsView, + state: customState + }) + expect(container.asFragment()).toMatchSnapshot() + }) + + it('Should handle traveling.', async () => { + const { getByTestId } = customRender({ + component: PlanetsView, + state: customState + }) + const button = await waitForElement(() => + getByTestId('travel-button-Test Planet 2') + ) + fireEvent.click(button) + }) + + it('Should handle adding an item to ship cargo.', async () => { + const { getByTestId } = customRender({ + component: PlanetsView, + state: customState + }) + const button = await waitForElement(() => getByTestId('add-button-1')) + fireEvent.click(button) + }) +}) diff --git a/src/__tests__/views/ship.js b/src/__tests__/views/ship.js new file mode 100644 index 0000000..ee7bef5 --- /dev/null +++ b/src/__tests__/views/ship.js @@ -0,0 +1,43 @@ +import { customRender } from '../../test-utils' +import ShipView from '../../views/ship' +import { defaultState } from '../../fixtures' +import { waitForElement, fireEvent } from '@testing-library/dom' + +const customState = { + ...defaultState, + ship: { + ...defaultState.ship, + cargo: { + items: [ + { + name: 'Test Item', + space: 1, + value: 1, + id: '0', + quantity: 1, + destination: { + name: 'Test Planet 2', + value: 50 + } + } + ], + volumeRemaining: 4 + } + } +} + +describe('<ShipView />', () => { + it('Should render the <ShipView /> component.', () => { + const container = customRender({ component: ShipView, state: customState }) + expect(container.asFragment()).toMatchSnapshot() + }) + + it('Should handle removing an item from ship cargo.', async () => { + const { getByTestId } = customRender({ + component: ShipView, + state: customState + }) + const button = await waitForElement(() => getByTestId('remove-button-0')) + fireEvent.click(button) + }) +}) diff --git a/src/components/ItemDisplay.js b/src/components/ItemDisplay.js new file mode 100644 index 0000000..29e4047 --- /dev/null +++ b/src/components/ItemDisplay.js @@ -0,0 +1,49 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { Box, Text } from 'grommet' +import { connect } from 'react-redux' + +import ItemDisplayInput from './ItemDisplayInput' + +const ItemDisplay = ({ item, location, shipLocationValue }) => { + const { destination, id, name, quantity, space, value } = item + + return ( + <Box + align="center" + direction="row" + fill="horizontal" + justify="start" + key={id} + > + <Box pad="medium"> + <Text>Name: {name}</Text> + </Box> + <Box pad="medium"> + <Text>Quantity: {quantity}</Text> + </Box> + <Box pad="medium"> + <Text>Space: {space}</Text> + </Box> + <Box pad="medium"> + <Text>Value: {value}</Text> + </Box> + <Box pad="medium"> + <Text>Destination: {destination.name}</Text> + </Box> + {shipLocationValue === location && <ItemDisplayInput item={item} />} + </Box> + ) +} + +ItemDisplay.propTypes = { + item: PropTypes.object.isRequired, + location: PropTypes.number.isRequired, + shipLocationValue: PropTypes.number.isRequired +} + +const mapStateToProps = ({ ship }) => ({ + shipLocationValue: ship.location.value +}) + +export default connect(mapStateToProps)(ItemDisplay) diff --git a/src/components/ItemDisplayInput.js b/src/components/ItemDisplayInput.js new file mode 100644 index 0000000..71941a4 --- /dev/null +++ b/src/components/ItemDisplayInput.js @@ -0,0 +1,67 @@ +import React, { useState } from 'react' +import PropTypes from 'prop-types' +import { Box, Button } from 'grommet' +import { Add } from 'grommet-icons' +import { connect } from 'react-redux' +import { storeCargo } from '../redux/actions/ship' +import { removeItem } from '../redux/actions/world' + +const ItemDisplayInput = ({ + handleStoreCargo, + item, + shipCargoVolumeRemaining +}) => { + const { id, quantity } = item + + const [value, setValue] = useState(0) + + return ( + <Box gap="small" pad="medium"> + <Button + data-testid={`add-button-${id}`} + disabled={shipCargoVolumeRemaining === 0 || value === 0} + hoverIndicator + icon={<Add />} + onClick={() => handleStoreCargo(item, value)} + plain + /> + <label htmlFor={`quantity-input-${id}`}>Quantity to Add</label> + <input + id={`quantity-input-${id}`} + max={ + shipCargoVolumeRemaining > quantity + ? quantity + : shipCargoVolumeRemaining + } + min={0} + onChange={e => setValue(Number(e.target.value))} + type="number" + value={value} + /> + </Box> + ) +} + +ItemDisplayInput.propTypes = { + handleStoreCargo: PropTypes.func.isRequired, + item: PropTypes.object.isRequired, + shipCargoVolumeRemaining: PropTypes.number.isRequired +} + +const mapStateToProps = ({ ship }) => ({ + shipCargoVolumeRemaining: ship.cargo.volumeRemaining +}) + +const mapDispatchToProps = dispatch => ({ + handleStoreCargo: (item, quantity) => { + // * dispatch an action to store the item in ship cargo + dispatch(storeCargo(item, quantity)) + // * dispatch an action to remove the item from the list of stored items on this planet + dispatch(removeItem(item, quantity)) + } +}) + +export default connect( + mapStateToProps, + mapDispatchToProps +)(ItemDisplayInput) diff --git a/src/components/ItemTimer.js b/src/components/ItemTimer.js index bfe2250..5c5e1c9 100644 --- a/src/components/ItemTimer.js +++ b/src/components/ItemTimer.js @@ -1,17 +1,18 @@ import React, { useState, useEffect } from 'react' import PropTypes from 'prop-types' import { connect } from 'react-redux' -import moment from 'moment' import { setTimerRunning, clearItems, refreshItems } from '../redux/actions/world' +import { createDuration } from '../util' const ItemTimer = ({ handleTimerStarted, handleTimerStopped, world }) => { const { isTimerRunning } = world - let duration = moment.duration({ minutes: 10, seconds: 0 }) + let duration = createDuration() + const [timeLeft, setTimeLeft] = useState( `${duration.minutes()} minutes ${duration.seconds()} seconds` ) diff --git a/src/components/PlanetDisplay.js b/src/components/PlanetDisplay.js new file mode 100644 index 0000000..1f7f0f3 --- /dev/null +++ b/src/components/PlanetDisplay.js @@ -0,0 +1,89 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { connect } from 'react-redux' +import { Box, Button, Heading } from 'grommet' +import { Target } from 'grommet-icons' +import { + setShipLocationName, + setShipLocationValue, + removeCargo +} from '../redux/actions/ship' +import { addCash } from '../redux/actions/user' +import ItemDisplay from './ItemDisplay' + +const PlanetDisplay = ({ + handleShipTravel, + planet, + shipCargo, + shipLocationValue +}) => { + const { isHomePlanet, items, location, name } = planet + + return ( + <div key={name}> + <Box direction="row" gap="medium"> + <Heading level="2"> + {isHomePlanet ? name + ' - Home Planet' : name} + </Heading> + {shipLocationValue !== location && ( + <Button + data-testid={`travel-button-${name}`} + hoverIndicator + icon={<Target />} + onClick={() => + handleShipTravel({ name, value: location }, shipCargo) + } + plain + /> + )} + </Box> + <span>Items:</span> + {items.map(item => ( + <ItemDisplay key={item.id} item={item} location={location} /> + ))} + </div> + ) +} + +PlanetDisplay.propTypes = { + handleShipTravel: PropTypes.func.isRequired, + planet: PropTypes.object.isRequired, + shipCargo: PropTypes.object.isRequired, + shipLocationValue: PropTypes.number.isRequired +} + +const mapStateToProps = ({ ship }) => ({ + shipCargo: ship.cargo, + shipLocationValue: ship.location.value +}) + +const mapDispatchToProps = dispatch => ({ + handleShipTravel: (destination, shipCargo) => { + const sellableItems = shipCargo.items.filter( + item => item.destination.value === destination.value + ) + + let profit = 0 + + sellableItems.forEach(item => { + const { quantity, value } = item + const itemProfit = quantity * value + profit += itemProfit + }) + + dispatch(addCash(profit)) + + sellableItems.forEach(item => { + // * Remove the item from the ship cargo + dispatch(removeCargo(item)) + }) + + dispatch(setShipLocationName(destination.name)) + dispatch(setShipLocationValue(destination.value)) + } +}) + +export default connect( + mapStateToProps, + mapDispatchToProps +)(PlanetDisplay) diff --git a/src/components/ViewSelector.js b/src/components/ViewSelector.js index f9d74dc..b55e2e2 100644 --- a/src/components/ViewSelector.js +++ b/src/components/ViewSelector.js @@ -10,7 +10,7 @@ const ViewSelector = ({ handleSetView, view }) => { return ( <Box width="small"> - <select onChange={handleChange}> + <select data-testid="view-selector" onChange={handleChange}> <option>{view}</option> {Object.keys(views).map((viewName, i) => { if (viewName !== view) { diff --git a/src/fixtures.js b/src/fixtures.js new file mode 100644 index 0000000..a02fa42 --- /dev/null +++ b/src/fixtures.js @@ -0,0 +1,194 @@ +export const defaultState = { + ship: { + cargo: { + items: [], + volumeRemaining: 5 + }, + location: { + name: null, + value: null + } + }, + ui: { + view: 'Ship' + }, + user: { + cash: 100 + }, + world: { + isTimerRunning: false, + planets: [ + { + isHomePlanet: true, + location: 0, + items: [ + { + name: 'Test Item', + space: 1, + value: 1, + id: '0', + destination: { + name: 'Test Planet 2', + value: 50 + } + }, + { + name: 'Test Item', + space: 1, + value: 1, + id: '1', + destination: { + name: 'Test Planet 2', + value: 50 + } + }, + { + name: 'Test Item', + space: 1, + value: 1, + id: '2', + destination: { + name: 'Test Planet 2', + value: 50 + } + }, + { + name: 'Test Item', + space: 1, + value: 1, + id: '3', + destination: { + name: 'Test Planet 2', + value: 50 + } + }, + { + name: 'Test Item', + space: 1, + value: 1, + id: '4', + destination: { + name: 'Test Planet 2', + value: 50 + } + } + ], + name: 'Test Planet 1' + }, + { + isHomePlanet: false, + location: 50, + items: [ + { + name: 'Test Item', + space: 1, + value: 1, + id: '5', + destination: { + name: 'Test Planet 1', + value: 0 + } + }, + { + name: 'Test Item', + space: 1, + value: 1, + id: '6', + destination: { + name: 'Test Planet 1', + value: 0 + } + }, + { + name: 'Test Item', + space: 1, + value: 1, + id: '7', + destination: { + name: 'Test Planet 1', + value: 0 + } + }, + { + name: 'Test Item', + space: 1, + value: 1, + id: '8', + destination: { + name: 'Test Planet 1', + value: 0 + } + }, + { + name: 'Test Item', + space: 1, + value: 1, + id: '9', + destination: { + name: 'Test Planet 1', + value: 0 + } + } + ], + name: 'Test Planet 2' + }, + { + isHomePlanet: false, + location: 100, + items: [ + { + name: 'Test Item', + space: 1, + value: 1, + id: '10', + destination: { + name: 'Test Planet 2', + value: 50 + } + }, + { + name: 'Test Item', + space: 1, + value: 1, + id: '11', + destination: { + name: 'Test Planet 2', + value: 50 + } + }, + { + name: 'Test Item', + space: 1, + value: 1, + id: '12', + destination: { + name: 'Test Planet 2', + value: 50 + } + }, + { + name: 'Test Item', + space: 1, + value: 1, + id: '13', + destination: { + name: 'Test Planet 2', + value: 50 + } + }, + { + name: 'Test Item', + space: 1, + value: 1, + id: '14', + destination: { + name: 'Test Planet 2', + value: 50 + } + } + ], + name: 'Test Planet 3' + } + ] + } +} diff --git a/src/redux/actions/ship.js b/src/redux/actions/ship.js index af6c7dd..9176fbd 100644 --- a/src/redux/actions/ship.js +++ b/src/redux/actions/ship.js @@ -22,10 +22,11 @@ export const setShipLocationValue = value => ({ payload: { value } }) -export const storeCargo = item => ({ +export const storeCargo = (item, quantity) => ({ type: STORE_CARGO, payload: { - item + item, + quantity } }) diff --git a/src/redux/actions/world.js b/src/redux/actions/world.js index dfbaab4..410cef1 100644 --- a/src/redux/actions/world.js +++ b/src/redux/actions/world.js @@ -10,10 +10,11 @@ export const clearItems = () => ({ type: CLEAR_ITEMS }) export const refreshItems = () => ({ type: REFRESH_ITEMS }) -export const removeItem = item => ({ +export const removeItem = (item, quantity) => ({ type: REMOVE_ITEM, payload: { - item + item, + quantity } }) diff --git a/src/redux/reducers/ship.js b/src/redux/reducers/ship.js index 423af2d..3b8c82c 100644 --- a/src/redux/reducers/ship.js +++ b/src/redux/reducers/ship.js @@ -1,5 +1,8 @@ const shipDefaultState = { - cargo: [], + cargo: { + items: [], + volumeRemaining: 5 + }, location: { name: null, value: null @@ -11,7 +14,14 @@ export default (state = shipDefaultState, action) => { case 'REMOVE_CARGO': return { ...state, - cargo: state.cargo.filter(item => item.id !== action.payload.item.id) + cargo: { + ...state.cargo, + items: state.cargo.items.filter( + item => item.id !== action.payload.item.id + ), + volumeRemaining: + state.cargo.volumeRemaining + action.payload.item.quantity + } } case 'SET_SHIP_LOCATION_NAME': return { @@ -24,7 +34,38 @@ export default (state = shipDefaultState, action) => { location: { ...state.location, value: action.payload.value } } case 'STORE_CARGO': - return { ...state, cargo: [...state.cargo, action.payload.item] } + let updatedItems = state.cargo.items.map(currentItem => { + if (action.payload.item.id === currentItem.id) { + // * This item is the same, and you should just add the quantity + return { + ...currentItem, + quantity: currentItem.quantity + action.payload.quantity + } + } else { + // * This currentItem is different, and you should return the currentItem + return currentItem + } + }) + + if ( + !updatedItems.find( + currentItem => currentItem.id === action.payload.item.id + ) + ) { + updatedItems.push({ + ...action.payload.item, + quantity: action.payload.quantity + }) + } + + return { + ...state, + cargo: { + ...state.cargo, + items: updatedItems, + volumeRemaining: state.cargo.volumeRemaining - action.payload.quantity + } + } default: return state } diff --git a/src/redux/reducers/world.js b/src/redux/reducers/world.js index baf8179..7561595 100644 --- a/src/redux/reducers/world.js +++ b/src/redux/reducers/world.js @@ -21,17 +21,43 @@ export default (state = worldDefaultState, action) => { })) } case 'REMOVE_ITEM': - const { item } = action.payload + const { item, quantity } = action.payload const updatedPlanets = state.planets.map(planet => { const { isHomePlanet, items, location, name } = planet if (planet.items.includes(item)) { - return { - isHomePlanet, - items: items.filter(currentItem => item !== currentItem), - location, - name + // * This plaanet has the item in question + // * Figure out if all of the quantity needs to be removed + + if (quantity === item.quantity) { + // * All of the item should be removed + return { + isHomePlanet, + items: items.filter(currentItem => item !== currentItem), + location, + name + } + } else { + // * Only some of the item quantity should be removed + const updatedItems = items.map(currentItem => { + if (item.id === currentItem.id) { + // * figure out quantity + return { + ...currentItem, + quantity: currentItem.quantity - quantity + } + } else { + return currentItem + } + }) + + return { + isHomePlanet, + items: updatedItems, + location, + name + } } } else { return planet diff --git a/src/setupTests.js b/src/setupTests.js index 7a1fee7..0235fb9 100644 --- a/src/setupTests.js +++ b/src/setupTests.js @@ -1,4 +1,5 @@ -import { configure } from 'enzyme'; -import Adapter from 'enzyme-adapter-react-16'; +// import { configure } from 'enzyme' +// import Adapter from 'enzyme-adapter-react-16' +// import 'jsdom-global/register' -configure({ adapter: new Adapter() }); \ No newline at end of file +// configure({ adapter: new Adapter() }) diff --git a/src/test-utils.js b/src/test-utils.js new file mode 100644 index 0000000..438d3e3 --- /dev/null +++ b/src/test-utils.js @@ -0,0 +1,21 @@ +import React from 'react'; +import configureStore from 'redux-mock-store' +import { Provider } from 'react-redux' +import { defaultState } from './fixtures' +import { render } from '@testing-library/react' + +const middlewares = [] + +export const mockStore = configureStore(middlewares) + +export const customRender = options => { + const { component, state } = options + + const Component = component + + return render( + <Provider store={mockStore(state ? state : defaultState)}> + <Component /> + </Provider> + ) +} diff --git a/src/util.js b/src/util.js index 03d4776..42b3ec8 100644 --- a/src/util.js +++ b/src/util.js @@ -1,5 +1,6 @@ import { itemList, planets } from './constants' import uuidv4 from 'uuid/v4' +import moment from 'moment' const getPlanetName = () => { const planet = planets[Math.floor(Math.random() * planets.length)] @@ -23,7 +24,8 @@ export const generateItems = possibleDestinations => { destination: { name: destinationPlanet.name, value: destinationPlanet.location - } + }, + quantity: 10 } ) items.push(item) @@ -71,3 +73,16 @@ export const saveState = state => { console.error(error) } } + +export const createDuration = () => { + const deadline = moment().minutes(60) + const now = moment() + + const minutesLeft = deadline + .clone() + .subtract(now.minutes(), 'minutes') + .minutes() + const secondsLeft = 60 - now.seconds() + + return moment.duration({ minutes: minutesLeft, seconds: secondsLeft }) +} \ No newline at end of file diff --git a/src/views/planets.js b/src/views/planets.js index 2da42d4..dfbf424 100644 --- a/src/views/planets.js +++ b/src/views/planets.js @@ -1,137 +1,24 @@ import React from 'react' import PropTypes from 'prop-types' import { connect } from 'react-redux' -import { Box, Text, Button, Heading } from 'grommet' -import { Add, Target } from 'grommet-icons' -import useLocalStorage from '../hooks/useLocalStorage' -import { - storeCargo, - setShipLocationName, - setShipLocationValue, - removeCargo -} from '../redux/actions/ship' -import { addCash } from '../redux/actions/user' -import { removeItem } from '../redux/actions/world' - -const PlanetsView = ({ - handleShipTravel, - handleStoreCargo, - planets, - shipCargo, - shipCargoLength, - shipLocationValue -}) => { - const [storagePlanets, setStoragePlanets] = useLocalStorage('planets', []) +import PlanetDisplay from '../components/PlanetDisplay' +const PlanetsView = ({ planets }) => { return ( <div> - {planets.map(({ items, isHomePlanet, location, name }) => ( - <div key={name}> - <Box direction="row" gap="medium"> - <Heading level="2"> - {isHomePlanet ? name + ' - Home Planet' : name} - </Heading> - {shipLocationValue !== location && ( - <Button - hoverIndicator - icon={<Target />} - onClick={() => - handleShipTravel({ name, value: location }, shipCargo) - } - plain - /> - )} - </Box> - <span>Items:</span> - {items.map(item => { - const { destination, id, name, space, value } = item - - return ( - <Box - align="center" - direction="row" - fill="horizontal" - justify="start" - key={id} - > - <Box pad="medium"> - <Text>Name: {name}</Text> - </Box> - <Box pad="medium"> - <Text>Space: {space}</Text> - </Box> - <Box pad="medium"> - <Text>Value: {value}</Text> - </Box> - <Box pad="medium"> - <Text>Destination: {destination.name}</Text> - </Box> - {shipLocationValue === location && ( - <Box pad="medium"> - <Button - disabled={shipCargoLength === 5} - hoverIndicator - icon={<Add />} - onClick={() => - handleStoreCargo( - item, - storagePlanets, - setStoragePlanets - ) - } - plain - /> - </Box> - )} - </Box> - ) - })} - </div> + {planets.map((planet, i) => ( + <PlanetDisplay key={i} planet={planet} /> ))} </div> ) } PlanetsView.propTypes = { - handleShipTravel: PropTypes.func.isRequired, - handleStoreCargo: PropTypes.func.isRequired, - planets: PropTypes.array.isRequired, - shipCargo: PropTypes.array.isRequired, - shipCargoLength: PropTypes.number.isRequired, - shipLocationValue: PropTypes.number.isRequired + planets: PropTypes.array.isRequired } -const mapStateToProps = ({ ship, world }) => ({ - planets: world.planets, - shipCargo: ship.cargo, - shipCargoLength: ship.cargo.length, - shipLocationValue: ship.location.value -}) - -const mapDispatchToProps = dispatch => ({ - handleStoreCargo: (item, currentPlanets, setStoragePlanets) => { - // * dispatch an action to store the item in ship cargo - dispatch(storeCargo(item)) - // * dispatch an action to remove the item from the list of stored items on this planet - dispatch(removeItem(item)) - }, - handleShipTravel: (destination, shipCargo) => { - const sellableItems = shipCargo.filter( - item => item.destination.value === destination.value - ) - - sellableItems.forEach(item => { - // * Add the value of this item to the user's cash - dispatch(addCash(item.value)) - // * Remove the item from the ship cargo - dispatch(removeCargo(item)) - }) - dispatch(setShipLocationName(destination.name)) - dispatch(setShipLocationValue(destination.value)) - } +const mapStateToProps = ({ world }) => ({ + planets: world.planets }) -export default connect( - mapStateToProps, - mapDispatchToProps -)(PlanetsView) +export default connect(mapStateToProps)(PlanetsView) diff --git a/src/views/ship.js b/src/views/ship.js index f004e11..5db0113 100644 --- a/src/views/ship.js +++ b/src/views/ship.js @@ -5,17 +5,24 @@ import { Box, Text, Button } from 'grommet' import { Subtract } from 'grommet-icons' import { removeCargo } from '../redux/actions/ship' -const ShipView = ({ cargo, handleRemoveCargo, location }) => { +const ShipView = ({ items, handleRemoveCargo, location, volumeRemaining }) => { return ( <div> <h2>Your Ship</h2> <h3>Cargo</h3> - {cargo.map(item => ( + <Box> + <Text weight="bold">Volume Remaining:</Text> + <Text>{volumeRemaining}</Text> + </Box> + {items.map(item => ( <Box direction="row" gap="medium" key={item.id}> <Text>{item.name}</Text> <Text weight="bold">Destination:</Text> <Text>{item.destination.name}</Text> + <Text weight="bold">Quantity:</Text> + <Text>{item.quantity}</Text> <Button + data-testid={`remove-button-${item.id}`} hoverIndicator icon={<Subtract />} onClick={() => handleRemoveCargo(item)} @@ -39,14 +46,16 @@ const ShipView = ({ cargo, handleRemoveCargo, location }) => { } ShipView.propTypes = { - cargo: PropTypes.array.isRequired, handleRemoveCargo: PropTypes.func.isRequired, - location: PropTypes.object.isRequired + items: PropTypes.array.isRequired, + location: PropTypes.object.isRequired, + volumeRemaining: PropTypes.number.isRequired } const mapStateToProps = ({ ship }) => ({ - cargo: ship.cargo, - location: ship.location + items: ship.cargo.items, + location: ship.location, + volumeRemaining: ship.cargo.volumeRemaining }) const mapDispatchToProps = dispatch => ({ diff --git a/yarn.lock b/yarn.lock index f909cb2..ce5fb81 100644 --- a/yarn.lock +++ b/yarn.lock @@ -756,7 +756,7 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-transform-typescript" "^7.3.2" -"@babel/runtime@7.5.5", "@babel/runtime@^7.0.0", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.5": +"@babel/runtime@7.5.5", "@babel/runtime@^7.0.0", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132" integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ== @@ -1076,6 +1076,11 @@ "@sentry/types" "5.6.1" tslib "^1.9.3" +"@sheerun/mutationobserver-shim@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz#8013f2af54a2b7d735f71560ff360d3a8176a87b" + integrity sha512-vTCdPp/T/Q3oSqwHmZ5Kpa9oI7iLtGl3RQaA/NyLHikvcrPxACkkKVr/XzkSPJWXHRhKGzVvb0urJsbMlRxi1Q== + "@svgr/babel-plugin-add-jsx-attribute@^4.2.0": version "4.2.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz#dadcb6218503532d6884b210e7f3c502caaa44b1" @@ -1179,6 +1184,27 @@ "@svgr/plugin-svgo" "^4.3.1" loader-utils "^1.2.3" +"@testing-library/dom@^6.0.0", "@testing-library/dom@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-6.1.0.tgz#8d5a954158e81ecd7c994907f4ec240296ed823b" + integrity sha512-qivqFvnbVIH3DyArFofEU/jlOhkGIioIemOy9A9M/NQTpPyDDQmtVkAfoB18RKN581f0s/RJMRBbq9WfMIhFTw== + dependencies: + "@babel/runtime" "^7.5.5" + "@sheerun/mutationobserver-shim" "^0.3.2" + "@types/testing-library__dom" "^6.0.0" + aria-query "3.0.0" + pretty-format "^24.8.0" + wait-for-expect "^1.3.0" + +"@testing-library/react@^9.1.3": + version "9.1.3" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-9.1.3.tgz#3fb495227322ea36cd817532441dabb552e0d6ce" + integrity sha512-qFVo6TsEbpEFpOmKjIxMHDujOKVdvVpcYFcUfJeWBqMO8eja5pN9SZnt6W6AzW3a1MRvRfw3X0Fhx3eXnBJxjA== + dependencies: + "@babel/runtime" "^7.5.5" + "@testing-library/dom" "^6.0.0" + "@types/testing-library__react" "^9.1.0" + "@types/babel__core@^7.1.0": version "7.1.2" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.2.tgz#608c74f55928033fce18b99b213c16be4b3d114f" @@ -1247,16 +1273,56 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.2.tgz#c4e63af5e8823ce9cc3f0b34f7b998c2171f0c44" integrity sha512-dyYO+f6ihZEtNPDcWNR1fkoTDf3zAK3lAABDze3mz6POyIercH0lEUawUFXlG8xaQZmm1yEBON/4TsYv/laDYg== +"@types/pretty-format@*": + version "20.0.1" + resolved "https://registry.yarnpkg.com/@types/pretty-format/-/pretty-format-20.0.1.tgz#7ce03b403887b087701a2b4534464f48ce7b2f48" + integrity sha512-Oh7wnvVUCtVIWnCHQWe9qDZKn0fGyk5AMq99jXml0x39K59P+z9qe31CNRtop9TceCpS7NmoK+J9eGeCnyFgnw== + +"@types/prop-types@*": + version "15.7.1" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.1.tgz#f1a11e7babb0c3cad68100be381d1e064c68f1f6" + integrity sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg== + "@types/q@^1.5.1": version "1.5.2" resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== +"@types/react-dom@*": + version "16.9.0" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.0.tgz#ba6ddb00bf5de700b0eb91daa452081ffccbfdea" + integrity sha512-OL2lk7LYGjxn4b0efW3Pvf2KBVP0y1v3wip1Bp7nA79NkOpElH98q3WdCEdDj93b2b0zaeBG9DvriuKjIK5xDA== + dependencies: + "@types/react" "*" + +"@types/react@*": + version "16.9.2" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.2.tgz#6d1765431a1ad1877979013906731aae373de268" + integrity sha512-jYP2LWwlh+FTqGd9v7ynUKZzjj98T8x7Yclz479QdRhHfuW9yQ+0jjnD31eXSXutmBpppj5PYNLYLRfnZJvcfg== + dependencies: + "@types/prop-types" "*" + csstype "^2.2.0" + "@types/stack-utils@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== +"@types/testing-library__dom@*", "@types/testing-library__dom@^6.0.0": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@types/testing-library__dom/-/testing-library__dom-6.0.1.tgz#e62c7799a210163ae72b8ac00cc4a841951f9f2d" + integrity sha512-j8wJmvER0VVrmDtab80SwA4MgF0vzFm6hSyk3j2bw9PXyJxgMjFDF8ihJDzo2gUOMP+HpEquHRZcUXOhLmYexA== + dependencies: + "@types/pretty-format" "*" + +"@types/testing-library__react@^9.1.0": + version "9.1.1" + resolved "https://registry.yarnpkg.com/@types/testing-library__react/-/testing-library__react-9.1.1.tgz#4bcb8bba54b07fbb6c084f2f00e7f9410e587c10" + integrity sha512-8/toTJaIlS3BC7JrK2ElTnbjH8tmFP7atdL2ZsIa1JDmH9RKSm/7Wp5oMDJzXoWr988Mv7ym/XZ8LRglyoGCGw== + dependencies: + "@types/react-dom" "*" + "@types/testing-library__dom" "*" + "@types/yargs-parser@*": version "13.0.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-13.0.0.tgz#453743c5bbf9f1bed61d959baab5b06be029b2d0" @@ -1481,7 +1547,7 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" -acorn-globals@^4.1.0, acorn-globals@^4.3.0: +acorn-globals@^4.1.0, acorn-globals@^4.3.0, acorn-globals@^4.3.2: version "4.3.3" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.3.tgz#a86f75b69680b8780d30edd21eee4e0ea170c05e" integrity sha512-vkR40VwS2SYO98AIeFvzWWh+xyc2qi9s7OoXSFEGIP/rOJKzjnhykaZJNnHdoq4BL2gGxI5EZOU16z896EYnOQ== @@ -1504,7 +1570,7 @@ acorn@^5.5.3: resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== -acorn@^6.0.1, acorn@^6.0.4, acorn@^6.2.1: +acorn@^6.0.1, acorn@^6.0.4, acorn@^6.1.1, acorn@^6.2.1: version "6.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e" integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA== @@ -1648,7 +1714,7 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" -aria-query@^3.0.0: +aria-query@3.0.0, aria-query@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc" integrity sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w= @@ -1819,7 +1885,7 @@ async-each@^1.0.1: resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== -async-limiter@~1.0.0: +async-limiter@^1.0.0, async-limiter@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== @@ -3110,18 +3176,23 @@ csso@^3.5.1: dependencies: css-tree "1.0.0-alpha.29" -cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0", cssom@^0.3.4: +cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0", cssom@^0.3.4, cssom@^0.3.6: version "0.3.8" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== -cssstyle@^1.0.0, cssstyle@^1.1.1: +cssstyle@^1.0.0, cssstyle@^1.1.1, cssstyle@^1.2.2: version "1.4.0" resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1" integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA== dependencies: cssom "0.3.x" +csstype@^2.2.0: + version "2.6.6" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.6.tgz#c34f8226a94bbb10c32cc0d714afdf942291fc41" + integrity sha512-RpFbQGUE74iyPgvr46U9t1xoQBM8T4BL8SxrN66Le2xYAPSaDJJKeztV3awugusb3g3G9iL8StmkBBXhcbbXhg== + cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" @@ -3691,7 +3762,7 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -escodegen@^1.11.0, escodegen@^1.9.1: +escodegen@^1.11.0, escodegen@^1.11.1, escodegen@^1.9.1: version "1.12.0" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.12.0.tgz#f763daf840af172bb3a2b6dd7219c0e17f7ff541" integrity sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg== @@ -5907,6 +5978,11 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= +jsdom-global@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/jsdom-global/-/jsdom-global-3.0.2.tgz#6bd299c13b0c4626b2da2c0393cd4385d606acb9" + integrity sha1-a9KZwTsMRiay2iwDk81DhdYGrLk= + jsdom@^11.5.1: version "11.12.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8" @@ -5971,6 +6047,38 @@ jsdom@^14.0.0: ws "^6.1.2" xml-name-validator "^3.0.0" +jsdom@^15.1.1: + version "15.1.1" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-15.1.1.tgz#21ed01f81d95ef4327f3e564662aef5e65881252" + integrity sha512-cQZRBB33arrDAeCrAEWn1U3SvrvC8XysBua9Oqg1yWrsY/gYcusloJC3RZJXuY5eehSCmws8f2YeliCqGSkrtQ== + dependencies: + abab "^2.0.0" + acorn "^6.1.1" + acorn-globals "^4.3.2" + array-equal "^1.0.0" + cssom "^0.3.6" + cssstyle "^1.2.2" + data-urls "^1.1.0" + domexception "^1.0.1" + escodegen "^1.11.1" + html-encoding-sniffer "^1.0.2" + nwsapi "^2.1.4" + parse5 "5.1.0" + pn "^1.1.0" + request "^2.88.0" + request-promise-native "^1.0.7" + saxes "^3.1.9" + symbol-tree "^3.2.2" + tough-cookie "^3.0.1" + w3c-hr-time "^1.0.1" + w3c-xmlserializer "^1.1.2" + webidl-conversions "^4.0.2" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^7.0.0" + ws "^7.0.0" + xml-name-validator "^3.0.0" + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -6229,6 +6337,11 @@ lodash.isequal@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -6893,7 +7006,7 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= -nwsapi@^2.0.7, nwsapi@^2.1.3: +nwsapi@^2.0.7, nwsapi@^2.1.3, nwsapi@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.1.4.tgz#e006a878db23636f8e8a67d33ca0e4edf61a842f" integrity sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw== @@ -8125,7 +8238,7 @@ pretty-error@^2.1.1: renderkid "^2.0.1" utila "~0.4" -pretty-format@^24.9.0: +pretty-format@^24.8.0, pretty-format@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== @@ -8616,6 +8729,13 @@ recursive-readdir@2.2.2: dependencies: minimatch "3.0.4" +redux-mock-store@^1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/redux-mock-store/-/redux-mock-store-1.5.3.tgz#1f10528949b7ce8056c2532624f7cafa98576c6d" + integrity sha512-ryhkkb/4D4CUGpAV2ln1GOY/uh51aczjcRz9k2L2bPx/Xja3c5pSGJJPyR25GNVRXtKIExScdAgFdiXp68GmJA== + dependencies: + lodash.isplainobject "^4.0.6" + redux-thunk@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" @@ -8748,7 +8868,7 @@ request-promise-core@1.1.2: dependencies: lodash "^4.17.11" -request-promise-native@^1.0.5: +request-promise-native@^1.0.5, request-promise-native@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.7.tgz#a49868a624bdea5069f1251d0a836e0d89aa2c59" integrity sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w== @@ -9847,6 +9967,15 @@ tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@^2.5.0: psl "^1.1.28" punycode "^2.1.1" +tough-cookie@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" + integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== + dependencies: + ip-regex "^2.1.0" + psl "^1.1.28" + punycode "^2.1.1" + tough-cookie@~2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" @@ -10184,6 +10313,11 @@ w3c-xmlserializer@^1.1.2: webidl-conversions "^4.0.2" xml-name-validator "^3.0.0" +wait-for-expect@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-1.3.0.tgz#65241ce355425f907f5d127bdb5e72c412ff830c" + integrity sha512-8fJU7jiA96HfGPt+P/UilelSAZfhMBJ52YhKzlmZQvKEZU2EcD1GQ0yqGB6liLdHjYtYAoGVigYwdxr5rktvzA== + walker@^1.0.7, walker@~1.0.5: version "1.0.7" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" @@ -10591,6 +10725,13 @@ ws@^6.1.2: dependencies: async-limiter "~1.0.0" +ws@^7.0.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.1.2.tgz#c672d1629de8bb27a9699eb599be47aeeedd8f73" + integrity sha512-gftXq3XI81cJCgkUiAVixA0raD9IVmXqsylCrjRygw4+UOOGzPoxnQ6r/CnVL9i+mDncJo94tSkyrtuuQVBmrg== + dependencies: + async-limiter "^1.0.0" + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"