diff --git a/package.json b/package.json index d44334b..42d120a 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,9 @@ "@storybook/preset-typescript": "^1.2.0", "@storybook/react": "^5.2.6", "@stripe/stripe-js": "^1.2.0", - "@types/enzyme": "^3.10.4", + "@testing-library/jest-dom": "^5.8.0", + "@testing-library/react": "^10.0.4", + "@testing-library/react-hooks": "^3.2.1", "@types/jest": "^25.1.1", "@types/react": "^16.9.19", "@types/react-dom": "^16.9.5", @@ -77,8 +79,6 @@ "babel-eslint": "^10.0.3", "babel-jest": "^24.9.0", "babel-loader": "^8.0.6", - "enzyme": "3.10", - "enzyme-adapter-react-16": "^1.14.0", "eslint": "6.6.0", "eslint-config-airbnb": "18.0.1", "eslint-config-prettier": "^6.10.0", @@ -94,6 +94,7 @@ "react": "~16.9.0", "react-docgen-typescript-loader": "^3.6.0", "react-dom": "~16.9.0", + "react-test-renderer": "16.9", "rimraf": "^2.6.2", "rollup": "^1.27.0", "rollup-plugin-babel": "^4.3.3", diff --git a/src/components/Elements.test.js b/src/components/Elements.test.js deleted file mode 100644 index e2b6037..0000000 --- a/src/components/Elements.test.js +++ /dev/null @@ -1,323 +0,0 @@ -import React from 'react'; -import {act} from 'react-dom/test-utils'; -import {mount} from 'enzyme'; -import {Elements, useElements, useStripe, ElementsConsumer} from './Elements'; -import {mockStripe} from '../../test/mocks'; - -const TestComponent = () =>
test
; -const InjectedTestComponent = () => { - const elements = useElements(); - return ; -}; - -const StripeInjectedTestComponent = () => { - const stripe = useStripe(); - return ; -}; - -describe('Elements', () => { - let stripe; - let stripePromise; - let mockElements; - - beforeEach(() => { - stripe = mockStripe(); - stripePromise = Promise.resolve(stripe); - mockElements = Symbol('MockElements'); - stripe.elements.mockReturnValue(mockElements); - }); - - it('injects elements with the useElements hook', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find(TestComponent).prop('elements')).toBe(mockElements); - }); - - it('only creates elements once', () => { - mount( - - - - ); - - expect(stripe.elements.mock.calls).toHaveLength(1); - }); - - it('injects stripe with the useStripe hook', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find(TestComponent).prop('stripe')).toBe(stripe); - }); - - it('provides elements and stripe with the ElementsConsumer component', () => { - const wrapper = mount( - - - {(ctx) => ( - - )} - - - ); - - expect(wrapper.find(TestComponent).prop('elements')).toBe(mockElements); - expect(wrapper.find(TestComponent).prop('stripe')).toBe(stripe); - }); - - it('allows a transition from null to a valid Stripe object', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find(TestComponent).prop('elements')).toBe(null); - wrapper.setProps({stripe}); - wrapper.update(); - expect(wrapper.find(TestComponent).prop('elements')).toBe(mockElements); - }); - - it('works with a Promise resolving to a valid Stripe object', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find(TestComponent).prop('elements')).toBe(null); - - return Promise.resolve(act(() => stripePromise)).then(() => { - wrapper.update(); - expect(wrapper.find(TestComponent).prop('elements')).toBe(mockElements); - }); - }); - - it('allows a transition from null to a valid Promise', () => { - const wrapper = mount( - - - - ); - - expect(wrapper.find(TestComponent).prop('elements')).toBe(null); - wrapper.setProps({stripe: stripePromise}); - wrapper.update(); - expect(wrapper.find(TestComponent).prop('elements')).toBe(null); - - return Promise.resolve(act(() => stripePromise)).then(() => { - wrapper.update(); - expect(wrapper.find(TestComponent).prop('elements')).toBe(mockElements); - }); - }); - - it('does not set context if Promise resolves after Elements is unmounted', () => { - jest.spyOn(console, 'error'); - console.error.mockImplementation(() => {}); - - const wrapper = mount( - - - - ); - - wrapper.unmount(); - - return Promise.resolve(act(() => stripePromise)).then(() => { - expect(console.error).not.toHaveBeenCalled(); - }); - }); - - it('does not allow updates to options after the Stripe Promise is set', () => { - jest.spyOn(console, 'warn'); - console.warn.mockImplementation(() => {}); - - const wrapper = mount( - - - - ); - - wrapper.setProps({options: {bar: 'bar'}}); - - return Promise.resolve(act(() => stripePromise)).then(() => { - expect(console.warn).toHaveBeenCalled(); - expect(stripe.elements).toHaveBeenCalledWith({foo: 'foo'}); - }); - }); - - it('works with a Promise resolving to null for SSR safety', () => { - const nullPromise = Promise.resolve(null); - - const wrapper = mount( - - - - ); - - expect(wrapper.find(TestComponent).prop('elements')).toBe(null); - - return Promise.resolve(act(() => nullPromise)).then(() => { - wrapper.update(); - expect(wrapper.find(TestComponent).prop('elements')).toBe(null); - }); - }); - - it('errors when props.stripe is `undefined`', () => { - // Prevent the console.errors to keep the test output clean - jest.spyOn(console, 'error'); - console.error.mockImplementation(() => {}); - - expect(() => - mount( - - - - ) - ).toThrow('Invalid prop `stripe` supplied to `Elements`.'); - - console.error.mockRestore(); - }); - - it('errors when props.stripe is `false`', () => { - // Prevent the console.errors to keep the test output clean - jest.spyOn(console, 'error'); - console.error.mockImplementation(() => {}); - - expect(() => - mount( - - - - ) - ).toThrow('Invalid prop `stripe` supplied to `Elements`.'); - - console.error.mockRestore(); - }); - - it('errors when props.stripe is a string', () => { - // Prevent the console.errors to keep the test output clean - jest.spyOn(console, 'error'); - console.error.mockImplementation(() => {}); - - expect(() => - mount( - - - - ) - ).toThrow('Invalid prop `stripe` supplied to `Elements`.'); - console.error.mockRestore(); - }); - - it('errors when props.stripe is a some other object', () => { - // Prevent the console.errors to keep the test output clean - jest.spyOn(console, 'error'); - console.error.mockImplementation(() => {}); - - expect(() => - mount( - - - - ) - ).toThrow('Invalid prop `stripe` supplied to `Elements`.'); - console.error.mockRestore(); - }); - - it('does not allow changes to a set Stripe object', () => { - const wrapper = mount(); - - jest.spyOn(console, 'warn'); - console.warn.mockImplementation(() => {}); - - const stripe2 = mockStripe(); - wrapper.setProps({stripe: stripe2}); - - expect(stripe.elements.mock.calls).toHaveLength(1); - expect(stripe2.elements.mock.calls).toHaveLength(0); - expect(console.warn).toHaveBeenCalledWith( - 'Unsupported prop change on Elements: You cannot change the `stripe` prop after setting it.' - ); - - console.warn.mockRestore(); - }); - - it('does not allow changes to options after setting the Stripe object', () => { - const wrapper = mount(); - - jest.spyOn(console, 'warn'); - console.warn.mockImplementation(() => {}); - - wrapper.setProps({options: {bar: 'bar'}}); - - expect(stripe.elements).toHaveBeenCalledWith({foo: 'foo'}); - expect(stripe.elements.mock.calls).toHaveLength(1); - - expect(console.warn).toHaveBeenCalledWith( - 'Unsupported prop change on Elements: You cannot change the `options` prop after setting the `stripe` prop.' - ); - - console.warn.mockRestore(); - }); - - it('allows options changes before setting the Stripe object', () => { - const wrapper = mount(); - - jest.spyOn(console, 'warn'); - console.warn.mockImplementation(() => {}); - - wrapper.setProps({options: {bar: 'bar'}}); - - expect(console.warn).not.toHaveBeenCalled(); - - wrapper.setProps({stripe}); - - expect(stripe.elements).toHaveBeenCalledWith({bar: 'bar'}); - - console.warn.mockRestore(); - }); - - it('throws when trying to call useElements outside of Elements context', () => { - // Prevent the console.errors to keep the test output clean - jest.spyOn(console, 'error'); - console.error.mockImplementation(() => {}); - expect(() => mount()).toThrow( - 'Could not find Elements context; You need to wrap the part of your app that calls useElements() in an provider.' - ); - console.error.mockRestore(); - }); - - it('throws when trying to call useStripe outside of Elements context', () => { - // Prevent the console.errors to keep the test output clean - jest.spyOn(console, 'error'); - console.error.mockImplementation(() => {}); - expect(() => mount()).toThrow( - 'Could not find Elements context; You need to wrap the part of your app that calls useStripe() in an provider.' - ); - console.error.mockRestore(); - }); - - it('throws when trying to mount an outside of Elements context', () => { - // Prevent the console.errors to keep the test output clean - jest.spyOn(console, 'error'); - console.error.mockImplementation(() => {}); - const WithAConsumer = () => ( - - {(elements) => } - - ); - - expect(() => mount()).toThrow( - 'Could not find Elements context; You need to wrap the part of your app that mounts in an provider.' - ); - console.error.mockRestore(); - }); -}); diff --git a/src/components/Elements.test.tsx b/src/components/Elements.test.tsx new file mode 100644 index 0000000..f8a6be2 --- /dev/null +++ b/src/components/Elements.test.tsx @@ -0,0 +1,387 @@ +import React from 'react'; +import {render, act} from '@testing-library/react'; +import {renderHook} from '@testing-library/react-hooks'; + +import {Elements, useElements, useStripe, ElementsConsumer} from './Elements'; +import * as mocks from '../../test/mocks'; + +describe('Elements', () => { + let mockStripe: any; + let mockStripePromise: any; + let mockElements: any; + let consoleError: any; + let consoleWarn: any; + + beforeEach(() => { + mockStripe = mocks.mockStripe(); + mockStripePromise = Promise.resolve(mockStripe); + mockElements = mocks.mockElements(); + mockStripe.elements.mockReturnValue(mockElements); + + jest.spyOn(console, 'error'); + jest.spyOn(console, 'warn'); + consoleError = console.error; + consoleWarn = console.warn; + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + test('injects elements with the useElements hook', () => { + const wrapper = ({children}: any) => ( + {children} + ); + + const {result} = renderHook(() => useElements(), {wrapper}); + + expect(result.current).toBe(mockElements); + }); + + test('only creates elements once', () => { + const TestComponent = () => { + const _ = useElements(); + return
; + }; + + render( + + + + ); + + expect(mockStripe.elements).toHaveBeenCalledTimes(1); + }); + + // TODO(christopher): support Strict Mode first + // eslint-disable-next-line jest/no-disabled-tests + test.skip('only creates elements once in Strict Mode', () => { + const TestComponent = () => { + const _ = useElements(); + return
; + }; + + render( + + + + + + ); + + expect(mockStripe.elements).toHaveBeenCalledTimes(1); + }); + + test('injects stripe with the useStripe hook', () => { + const wrapper = ({children}: any) => ( + {children} + ); + + const {result} = renderHook(() => useStripe(), {wrapper}); + + expect(result.current).toBe(mockStripe); + }); + + test('provides elements and stripe with the ElementsConsumer component', () => { + expect.assertions(2); + + render( + + + {(ctx) => { + expect(ctx.elements).toBe(mockElements); + expect(ctx.stripe).toBe(mockStripe); + + return null; + }} + + + ); + }); + + test('provides elements and stripe with the ElementsConsumer component in Strict Mode', () => { + expect.assertions(2); + + render( + + + + {(ctx) => { + expect(ctx.elements).toBe(mockElements); + expect(ctx.stripe).toBe(mockStripe); + + return null; + }} + + + + ); + }); + + test('provides given stripe instance on mount', () => { + const TestComponent = () => { + const stripe = useStripe(); + + if (!stripe) { + throw new Error('Stripe instance is null'); + } + + return null; + }; + + expect(() => { + render( + + + + ); + }).not.toThrow('Stripe instance is null'); + }); + + test('allows a transition from null to a valid Stripe object', () => { + let stripeProp: any = null; + const wrapper = ({children}: any) => ( + {children} + ); + + const {result, rerender} = renderHook(() => useElements(), {wrapper}); + expect(result.current).toBe(null); + + stripeProp = mockStripe; + rerender(); + expect(result.current).toBe(mockElements); + }); + + test('works with a Promise resolving to a valid Stripe object', async () => { + const wrapper = ({children}: any) => ( + {children} + ); + + const {result, waitForNextUpdate} = renderHook(() => useElements(), { + wrapper, + }); + + expect(result.current).toBe(null); + + await waitForNextUpdate(); + + expect(result.current).toBe(mockElements); + }); + + test('allows a transition from null to a valid Promise', async () => { + let stripeProp: any = null; + const wrapper = ({children}: any) => ( + {children} + ); + + const {result, rerender, waitForNextUpdate} = renderHook( + () => useElements(), + {wrapper} + ); + expect(result.current).toBe(null); + + stripeProp = mockStripePromise; + rerender(); + expect(result.current).toBe(null); + + await waitForNextUpdate(); + + expect(result.current).toBe(mockElements); + }); + + test('does not set context if Promise resolves after Elements is unmounted', async () => { + // Silence console output so test output is less noisy + consoleError.mockImplementation(() => {}); + + const {unmount} = render( + {null} + ); + + unmount(); + await act(() => mockStripePromise); + + expect(consoleError).not.toHaveBeenCalled(); + }); + + test('does not allow updates to options after the Stripe Promise is set', async () => { + // Silence console output so test output is less noisy + consoleWarn.mockImplementation(() => {}); + + const {rerender} = render( + + ); + + rerender( + + ); + + await act(() => mockStripePromise); + + expect(consoleWarn).toHaveBeenCalledTimes(1); + expect(consoleWarn.mock.calls[0][0]).toContain( + 'Unsupported prop change on Elements' + ); + expect(mockStripe.elements).toHaveBeenCalledTimes(1); + expect(mockStripe.elements).toHaveBeenCalledWith({foo: 'foo'}); + }); + + // TODO(christopher): support Strict Mode first + // eslint-disable-next-line jest/no-disabled-tests + test.skip('does not allow updates to options after the Stripe Promise is set in StrictMode', async () => { + // Silence console output so test output is less noisy + consoleWarn.mockImplementation(() => {}); + + const {rerender} = render( + + + + ); + + rerender( + + + + ); + + await act(() => mockStripePromise); + + expect(consoleWarn).toHaveBeenCalledTimes(1); + expect(consoleWarn.mock.calls[0][0]).toContain( + 'Unsupported prop change on Elements' + ); + expect(mockStripe.elements).toHaveBeenCalledTimes(1); + expect(mockStripe.elements).toHaveBeenCalledWith({foo: 'foo'}); + }); + + test('works with a Promise resolving to null for SSR safety', async () => { + const nullPromise = Promise.resolve(null); + const TestComponent = () => { + const elements = useElements(); + return elements ?
not empty
: null; + }; + + const {container} = render( + + + + ); + + expect(container).toBeEmpty(); + + await act(() => nullPromise.then(() => undefined)); + expect(container).toBeEmpty(); + }); + + test('errors when props.stripe is `undefined`', () => { + // Silence console output so test output is less noisy + consoleError.mockImplementation(() => {}); + + expect(() => render()).toThrow( + 'Invalid prop `stripe` supplied to `Elements`.' + ); + }); + + test('errors when props.stripe is `false`', () => { + // Silence console output so test output is less noisy + consoleError.mockImplementation(() => {}); + + expect(() => render()).toThrow( + 'Invalid prop `stripe` supplied to `Elements`.' + ); + }); + + test('errors when props.stripe is a string', () => { + // Silence console output so test output is less noisy + consoleError.mockImplementation(() => {}); + + expect(() => render()).toThrow( + 'Invalid prop `stripe` supplied to `Elements`.' + ); + }); + + test('errors when props.stripe is a some other object', () => { + // Silence console output so test output is less noisy + consoleError.mockImplementation(() => {}); + + expect(() => render()).toThrow( + 'Invalid prop `stripe` supplied to `Elements`.' + ); + }); + + test('does not allow changes to a set Stripe object', () => { + // Silence console output so test output is less noisy + consoleWarn.mockImplementation(() => {}); + + const {rerender} = render(); + + const mockStripe2: any = mocks.mockStripe(); + rerender(); + + expect(mockStripe.elements.mock.calls).toHaveLength(1); + expect(mockStripe2.elements.mock.calls).toHaveLength(0); + expect(consoleWarn).toHaveBeenCalledWith( + 'Unsupported prop change on Elements: You cannot change the `stripe` prop after setting it.' + ); + }); + + test('does not allow changes to options after setting the Stripe object', () => { + // Silence console output so test output is less noisy + consoleWarn.mockImplementation(() => {}); + + const {rerender} = render( + + ); + + rerender(); + + expect(mockStripe.elements).toHaveBeenCalledWith({foo: 'foo'}); + expect(mockStripe.elements).toHaveBeenCalledTimes(1); + + expect(consoleWarn).toHaveBeenCalledWith( + 'Unsupported prop change on Elements: You cannot change the `options` prop after setting the `stripe` prop.' + ); + }); + + test('allows options changes before setting the Stripe object', () => { + const {rerender} = render( + + ); + + rerender(); + + expect(console.warn).not.toHaveBeenCalled(); + + rerender(); + + expect(mockStripe.elements).toHaveBeenCalledWith({bar: 'bar'}); + }); + + test('throws when trying to call useElements outside of Elements context', () => { + const {result} = renderHook(() => useElements()); + + expect(result.error.message).toBe( + 'Could not find Elements context; You need to wrap the part of your app that calls useElements() in an provider.' + ); + }); + + test('throws when trying to call useStripe outside of Elements context', () => { + const {result} = renderHook(() => useStripe()); + + expect(result.error.message).toBe( + 'Could not find Elements context; You need to wrap the part of your app that calls useStripe() in an provider.' + ); + }); + + test('throws when trying to mount an outside of Elements context', () => { + // Silence console output so test output is less noisy + consoleError.mockImplementation(() => {}); + + const TestComponent = () => { + return {() => null}; + }; + + expect(() => render()).toThrow( + 'Could not find Elements context; You need to wrap the part of your app that mounts in an provider.' + ); + }); +}); diff --git a/src/components/createElementComponent.test.js b/src/components/createElementComponent.test.js deleted file mode 100644 index 7c98a24..0000000 --- a/src/components/createElementComponent.test.js +++ /dev/null @@ -1,429 +0,0 @@ -import React from 'react'; -import {mount} from 'enzyme'; -import {Elements} from './Elements'; -import createElementComponent from './createElementComponent'; -import {mockElements, mockElement, mockStripe} from '../../test/mocks'; - -describe('createElementComponent', () => { - let stripe; - let elements; - let element; - let simulateChange; - let simulateBlur; - let simulateFocus; - let simulateEscape; - let simulateReady; - let simulateClick; - - beforeEach(() => { - jest.restoreAllMocks(); - stripe = mockStripe(); - elements = mockElements(); - element = mockElement(); - stripe.elements.mockReturnValue(elements); - elements.create.mockReturnValue(element); - jest.spyOn(React, 'useLayoutEffect'); - element.on = jest.fn((event, fn) => { - switch (event) { - case 'change': - simulateChange = fn; - break; - case 'blur': - simulateBlur = fn; - break; - case 'focus': - simulateFocus = fn; - break; - case 'escape': - simulateEscape = fn; - break; - case 'ready': - simulateReady = fn; - break; - case 'click': - simulateClick = fn; - break; - default: - throw new Error('TestSetupError: Unexpected event registration.'); - } - }); - }); - - describe('on the server', () => { - const CardElement = createElementComponent('card', true); - - it('gives the element component a proper displayName', () => { - expect(CardElement.displayName).toBe('CardElement'); - }); - - it('stores the element component`s type as a static property', () => { - expect(CardElement.__elementType).toBe('card'); // eslint-disable-line no-underscore-dangle - }); - - it('passes id to the wrapping DOM element', () => { - const wrapper = mount( - - - - ); - expect(wrapper.find('div').prop('id')).toBe('foo'); - }); - - it('passes className to the wrapping DOM element', () => { - const wrapper = mount( - - - - ); - expect(wrapper.find('div').prop('className')).toBe('bar'); - }); - - it('throws when the Element is mounted outside of Elements context', () => { - // Prevent the console.errors to keep the test output clean - jest.spyOn(console, 'error'); - console.error.mockImplementation(() => {}); - expect(() => mount()).toThrow( - 'Could not find Elements context; You need to wrap the part of your app that mounts in an provider.' - ); - console.error.mockRestore(); - }); - - it('does not call useLayoutEffect', () => { - mount( - - - - ); - - expect(React.useLayoutEffect).not.toHaveBeenCalled(); - }); - }); - - describe('on the client', () => { - const CardElement = createElementComponent('card', false); - - it('gives the element component a proper displayName', () => { - expect(CardElement.displayName).toBe('CardElement'); - }); - - it('stores the element component`s type as a static property', () => { - expect(CardElement.__elementType).toBe('card'); // eslint-disable-line no-underscore-dangle - }); - - it('passes id to the wrapping DOM element', () => { - const wrapper = mount( - - - - ); - expect(wrapper.find('div').prop('id')).toBe('foo'); - }); - - it('passes className to the wrapping DOM element', () => { - const wrapper = mount( - - - - ); - expect(wrapper.find('div').prop('className')).toBe('bar'); - }); - - it('creates the element with options', () => { - const options = {foo: 'foo'}; - mount( - - - - ); - - expect(elements.create).toHaveBeenCalledWith('card', options); - }); - - it('mounts the element', () => { - const wrapper = mount( - - - - ); - - expect(element.mount).toHaveBeenCalledWith( - wrapper.find('div').getDOMNode() - ); - - expect(React.useLayoutEffect).toHaveBeenCalled(); - }); - - it('does not create and mount until Elements has been instantiated', () => { - const wrapper = mount( - - - - ); - - expect(element.mount).not.toHaveBeenCalled(); - expect(elements.create).not.toHaveBeenCalled(); - - wrapper.setProps({stripe}); - - expect(element.mount).toHaveBeenCalled(); - expect(elements.create).toHaveBeenCalled(); - }); - - it('throws when the Element is mounted outside of Elements context', () => { - // Prevent the console.errors to keep the test output clean - jest.spyOn(console, 'error'); - console.error.mockImplementation(() => {}); - expect(() => mount()).toThrow( - 'Could not find Elements context; You need to wrap the part of your app that mounts in an provider.' - ); - console.error.mockRestore(); - }); - - it('propagates the Element`s ready event to the current onReady prop', () => { - // We need to wrap so that we can update the CardElement's props later. - // Enzyme does not support calling setProps on child components. - const TestComponent = (props) => ( - - - - ); - - const onReady = jest.fn(); - const onReady2 = jest.fn(); - const wrapper = mount(); - - // when setting a new onReady prop (e.g. a lambda in the render), - // only the latest handler is called. - wrapper.setProps({ - onReady: onReady2, - }); - - simulateReady(); - expect(onReady2).toHaveBeenCalledWith(element); - expect(onReady).not.toHaveBeenCalled(); - }); - - it('propagates the Element`s change event to the current onChange prop', () => { - // We need to wrap so that we can update the CardElement's props later. - // Enzyme does not support calling setProps on child components. - const TestComponent = (props) => ( - - - - ); - - const onChange = jest.fn(); - const onChange2 = jest.fn(); - const wrapper = mount(); - - // when setting a new onChange prop (e.g. a lambda in the render), - // only the latest handler is called. - wrapper.setProps({ - onChange: onChange2, - }); - - const changeEventMock = Symbol('change'); - simulateChange(changeEventMock); - expect(onChange2).toHaveBeenCalledWith(changeEventMock); - expect(onChange).not.toHaveBeenCalled(); - }); - - it('propagates the Element`s blur event to the current onBlur prop', () => { - // We need to wrap so that we can update the CardElement's props later. - // Enzyme does not support calling setProps on child components. - const TestComponent = (props) => ( - - - - ); - - const onBlur = jest.fn(); - const onBlur2 = jest.fn(); - const wrapper = mount(); - - // when setting a new onBlur prop (e.g. a lambda in the render), - // only the latest handler is called. - wrapper.setProps({ - onBlur: onBlur2, - }); - - simulateBlur(); - expect(onBlur2).toHaveBeenCalled(); - expect(onBlur).not.toHaveBeenCalled(); - }); - - it('propagates the Element`s focus event to the current onFocus prop', () => { - // We need to wrap so that we can update the CardElement's props later. - // Enzyme does not support calling setProps on child components. - const TestComponent = (props) => ( - - - - ); - - const onFocus = jest.fn(); - const onFocus2 = jest.fn(); - const wrapper = mount(); - - // when setting a new onFocus prop (e.g. a lambda in the render), - // only the latest handler is called. - wrapper.setProps({ - onFocus: onFocus2, - }); - - simulateFocus(); - expect(onFocus2).toHaveBeenCalled(); - expect(onFocus).not.toHaveBeenCalled(); - }); - - it('propagates the Element`s escape event to the current onEscape prop', () => { - // We need to wrap so that we can update the CardElement's props later. - // Enzyme does not support calling setProps on child components. - const TestComponent = (props) => ( - - - - ); - - const onEscape = jest.fn(); - const onEscape2 = jest.fn(); - const wrapper = mount(); - - // when setting a new onEscape prop (e.g. a lambda in the render), - // only the latest handler is called. - wrapper.setProps({ - onEscape: onEscape2, - }); - - simulateEscape(); - expect(onEscape2).toHaveBeenCalled(); - expect(onEscape).not.toHaveBeenCalled(); - }); - - // Users can pass an an onClick prop on any Element component - // just as they could listen for the `click` event on any Element, - // but only the PaymentRequestButton will actually trigger the event. - it('propagates the Element`s click event to the current onClick prop', () => { - // We need to wrap so that we can update the CardElement's props later. - // Enzyme does not support calling setProps on child components. - const TestComponent = (props) => ( - - - - ); - - const onClick = jest.fn(); - const onClick2 = jest.fn(); - const wrapper = mount(); - - // when setting a new onClick prop (e.g. a lambda in the render), - // only the latest handler is called. - wrapper.setProps({ - onClick: onClick2, - }); - - const clickEventMock = Symbol('click'); - simulateClick(clickEventMock); - expect(onClick2).toHaveBeenCalledWith(clickEventMock); - expect(onClick).not.toHaveBeenCalled(); - }); - - it('updates the Element when options change', () => { - // We need to wrap so that we can update the CardElement's props later. - // Enzyme does not support calling setProps on child components. - const TestComponent = (props) => ( - - - - ); - - const wrapper = mount(); - - wrapper.setProps({ - options: {style: {foo: 'bar'}}, - }); - expect(element.update).toHaveBeenCalledWith({ - style: {foo: 'bar'}, - }); - }); - - it('does not trigger unnecessary updates', () => { - // We need to wrap so that we can update the CardElement's props later. - // Enzyme does not support calling setProps on child components. - const TestComponent = (props) => ( - - - - ); - - const wrapper = mount( - - ); - - wrapper.setProps({ - options: { - style: {foo: 'foo'}, - }, - }); - expect(element.update).not.toHaveBeenCalled(); - }); - - it('warns on changes to non-updatable options', () => { - // We need to wrap so that we can update the CardElement's props later. - // Enzyme does not support calling setProps on child components. - const TestComponent = (props) => ( - - - - ); - - const wrapper = mount( - - ); - - jest.spyOn(console, 'warn'); - console.warn.mockImplementation(() => {}); - wrapper.setProps({ - options: { - paymentRequest: Symbol('PaymentRequest'), - }, - }); - - expect(element.update).not.toHaveBeenCalled(); - expect(console.warn).toHaveBeenCalledWith( - 'Unsupported prop change: options.paymentRequest is not a customizable property.' - ); - - console.warn.mockRestore(); - }); - - it('destroys an existing Element when the component unmounts', () => { - // not called when Element has not been mounted (because stripe is still loading) - const wrapper = mount( - - - - ); - - wrapper.unmount(); - expect(element.destroy).not.toHaveBeenCalled(); - - const wrapper2 = mount( - - - - ); - - wrapper2.unmount(); - expect(element.destroy).toHaveBeenCalled(); - }); - }); -}); diff --git a/src/components/createElementComponent.test.tsx b/src/components/createElementComponent.test.tsx new file mode 100644 index 0000000..726e18c --- /dev/null +++ b/src/components/createElementComponent.test.tsx @@ -0,0 +1,409 @@ +import React from 'react'; +import {render} from '@testing-library/react'; + +import {Elements} from './Elements'; +import createElementComponent from './createElementComponent'; +import * as mocks from '../../test/mocks'; +import { + CardElementComponent, + PaymentRequestButtonElementComponent, +} from '../types'; + +describe('createElementComponent', () => { + let mockStripe: any; + let mockElements: any; + let mockElement: any; + let simulateChange: any; + let simulateBlur: any; + let simulateFocus: any; + let simulateEscape: any; + let simulateReady: any; + let simulateClick: any; + + beforeEach(() => { + mockStripe = mocks.mockStripe(); + mockElements = mocks.mockElements(); + mockElement = mocks.mockElement(); + mockStripe.elements.mockReturnValue(mockElements); + mockElements.create.mockReturnValue(mockElement); + jest.spyOn(React, 'useLayoutEffect'); + mockElement.on = jest.fn((event, fn) => { + switch (event) { + case 'change': + simulateChange = fn; + break; + case 'blur': + simulateBlur = fn; + break; + case 'focus': + simulateFocus = fn; + break; + case 'escape': + simulateEscape = fn; + break; + case 'ready': + simulateReady = fn; + break; + case 'click': + simulateClick = fn; + break; + default: + throw new Error('TestSetupError: Unexpected event registration.'); + } + }); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + describe('on the server', () => { + const CardElement = createElementComponent('card', true); + + it('gives the element component a proper displayName', () => { + expect(CardElement.displayName).toBe('CardElement'); + }); + + it('stores the element component`s type as a static property', () => { + expect((CardElement as any).__elementType).toBe('card'); + }); + + it('passes id to the wrapping DOM element', () => { + const {container} = render( + + + + ); + + const elementContainer = container.firstChild as Element; + + expect(elementContainer.id).toBe('foo'); + }); + + it('passes className to the wrapping DOM element', () => { + const {container} = render( + + + + ); + const elementContainer = container.firstChild as Element; + expect(elementContainer).toHaveClass('bar'); + }); + + it('throws when the Element is mounted outside of Elements context', () => { + // Prevent the console.errors to keep the test output clean + jest.spyOn(console, 'error'); + (console.error as any).mockImplementation(() => {}); + + expect(() => render()).toThrow( + 'Could not find Elements context; You need to wrap the part of your app that mounts in an provider.' + ); + }); + + it('does not call useLayoutEffect', () => { + render( + + + + ); + + expect(React.useLayoutEffect).not.toHaveBeenCalled(); + }); + }); + + describe('on the client', () => { + const CardElement: CardElementComponent = createElementComponent( + 'card', + false + ); + const PaymentRequestButtonElement: PaymentRequestButtonElementComponent = createElementComponent( + 'card', + false + ); + + it('gives the element component a proper displayName', () => { + expect(CardElement.displayName).toBe('CardElement'); + }); + + it('stores the element component`s type as a static property', () => { + expect((CardElement as any).__elementType).toBe('card'); + }); + + it('passes id to the wrapping DOM element', () => { + const {container} = render( + + + + ); + const elementContainer = container.firstChild as Element; + + expect(elementContainer.id).toBe('foo'); + }); + + it('passes className to the wrapping DOM element', () => { + const {container} = render( + + + + ); + const elementContainer = container.firstChild as Element; + + expect(elementContainer).toHaveClass('bar'); + }); + + it('creates the element with options', () => { + const options: any = {foo: 'foo'}; + render( + + + + ); + + expect(mockElements.create).toHaveBeenCalledWith('card', options); + }); + + it('mounts the element', () => { + const {container} = render( + + + + ); + + expect(mockElement.mount).toHaveBeenCalledWith(container.firstChild); + expect(React.useLayoutEffect).toHaveBeenCalled(); + }); + + it('does not create and mount until Elements has been instantiated', () => { + const {rerender} = render( + + + + ); + + expect(mockElement.mount).not.toHaveBeenCalled(); + expect(mockElements.create).not.toHaveBeenCalled(); + + rerender( + + + + ); + + expect(mockElement.mount).toHaveBeenCalled(); + expect(mockElements.create).toHaveBeenCalled(); + }); + + it('throws when the Element is mounted outside of Elements context', () => { + // Prevent the console.errors to keep the test output clean + jest.spyOn(console, 'error'); + (console.error as any).mockImplementation(() => {}); + + expect(() => render()).toThrow( + 'Could not find Elements context; You need to wrap the part of your app that mounts in an provider.' + ); + }); + + it('propagates the Element`s ready event to the current onReady prop', () => { + const mockHandler = jest.fn(); + const mockHandler2 = jest.fn(); + const {rerender} = render( + + + + ); + rerender( + + + + ); + + simulateReady(); + expect(mockHandler2).toHaveBeenCalledWith(mockElement); + expect(mockHandler).not.toHaveBeenCalled(); + }); + + it('propagates the Element`s change event to the current onChange prop', () => { + const mockHandler = jest.fn(); + const mockHandler2 = jest.fn(); + const {rerender} = render( + + + + ); + rerender( + + + + ); + + const changeEventMock = Symbol('change'); + simulateChange(changeEventMock); + expect(mockHandler2).toHaveBeenCalledWith(changeEventMock); + expect(mockHandler).not.toHaveBeenCalled(); + }); + + it('propagates the Element`s blur event to the current onBlur prop', () => { + const mockHandler = jest.fn(); + const mockHandler2 = jest.fn(); + const {rerender} = render( + + + + ); + rerender( + + + + ); + + simulateBlur(); + expect(mockHandler2).toHaveBeenCalledWith(); + expect(mockHandler).not.toHaveBeenCalled(); + }); + + it('propagates the Element`s focus event to the current onFocus prop', () => { + const mockHandler = jest.fn(); + const mockHandler2 = jest.fn(); + const {rerender} = render( + + + + ); + rerender( + + + + ); + + simulateFocus(); + expect(mockHandler2).toHaveBeenCalledWith(); + expect(mockHandler).not.toHaveBeenCalled(); + }); + + it('propagates the Element`s escape event to the current onEscape prop', () => { + const mockHandler = jest.fn(); + const mockHandler2 = jest.fn(); + const {rerender} = render( + + + + ); + rerender( + + + + ); + + simulateEscape(); + expect(mockHandler2).toHaveBeenCalledWith(); + expect(mockHandler).not.toHaveBeenCalled(); + }); + + it('propagates the Element`s click event to the current onClick prop', () => { + const mockHandler = jest.fn(); + const mockHandler2 = jest.fn(); + const {rerender} = render( + + + + ); + rerender( + + + + ); + + const clickEventMock = Symbol('click'); + simulateClick(clickEventMock); + expect(mockHandler2).toHaveBeenCalledWith(clickEventMock); + expect(mockHandler).not.toHaveBeenCalled(); + }); + + it('updates the Element when options change', () => { + const {rerender} = render( + + + + ); + + rerender( + + + + ); + + expect(mockElement.update).toHaveBeenCalledWith({ + style: {base: {fontSize: '30px'}}, + }); + }); + + it('does not trigger unnecessary updates', () => { + const {rerender} = render( + + + + ); + + rerender( + + + + ); + + expect(mockElement.update).not.toHaveBeenCalled(); + }); + + it('warns on changes to non-updatable options', () => { + jest.spyOn(console, 'warn'); + (console.warn as any).mockImplementation(() => {}); + + const {rerender} = render( + + + + ); + + rerender( + + + + ); + + expect(mockElement.update).not.toHaveBeenCalled(); + + expect(console.warn).toHaveBeenCalledWith( + 'Unsupported prop change: options.paymentRequest is not a customizable property.' + ); + }); + + it('destroys an existing Element when the component unmounts', () => { + const {unmount} = render( + + + + ); + + unmount(); + + // not called when Element has not been mounted (because stripe is still loading) + expect(mockElement.destroy).not.toHaveBeenCalled(); + + const {unmount: unmount2} = render( + + + + ); + + unmount2(); + expect(mockElement.destroy).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/utils/usePrevious.test.tsx b/src/utils/usePrevious.test.tsx index 2b7283f..0440b42 100644 --- a/src/utils/usePrevious.test.tsx +++ b/src/utils/usePrevious.test.tsx @@ -1,27 +1,30 @@ -import {FunctionComponent} from 'react'; -import React from 'react'; -import {mount} from 'enzyme'; -import {usePrevious} from './usePrevious'; +import {renderHook} from '@testing-library/react-hooks'; -const TestComponent: FunctionComponent<{foo: string}> = ({foo}) => { - const lastFoo = usePrevious(foo); - return
{lastFoo}
; -}; +import {usePrevious} from './usePrevious'; describe('usePrevious', () => { it('returns the initial value if it has not yet been changed', () => { - const wrapper = mount(); + const {result} = renderHook(() => usePrevious('foo')); - expect(wrapper.find('div').text()).toEqual('foo'); + expect(result.current).toEqual('foo'); }); it('returns the previous value after the it has been changed', () => { - const wrapper = mount(); - wrapper.setProps({foo: 'bar'}); - expect(wrapper.find('div').text()).toEqual('foo'); - wrapper.setProps({foo: 'baz'}); - expect(wrapper.find('div').text()).toEqual('bar'); - wrapper.setProps({foo: 'buz'}); - expect(wrapper.find('div').text()).toEqual('baz'); + let val = 'foo'; + const {result, rerender} = renderHook(() => usePrevious(val)); + + expect(result.current).toEqual('foo'); + + val = 'bar'; + rerender(); + expect(result.current).toEqual('foo'); + + val = 'baz'; + rerender(); + expect(result.current).toEqual('bar'); + + val = 'buz'; + rerender(); + expect(result.current).toEqual('baz'); }); }); diff --git a/test/setupJest.js b/test/setupJest.js index 96bab7d..7b0828b 100644 --- a/test/setupJest.js +++ b/test/setupJest.js @@ -1,4 +1 @@ -import {configure} from 'enzyme'; -import Adapter from 'enzyme-adapter-react-16'; - -configure({adapter: new Adapter()}); +import '@testing-library/jest-dom'; diff --git a/yarn.lock b/yarn.lock index e05f8a4..a1201f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -839,6 +839,20 @@ dependencies: regenerator-runtime "^0.13.2" +"@babel/runtime@^7.5.4": + version "7.10.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.0.tgz#2cdcd6d7a391c24f7154235134c830cfb58ac0b1" + integrity sha512-tgYb3zVApHbLHYOPWtVwg25sBqHhfBXRKeKoTIyoheIxln1nA7oBl7SfHfiTG2GhDPI8EUBkOD/0wJCP/3HN4Q== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/runtime@^7.9.2", "@babel/runtime@^7.9.6": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f" + integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.4.0", "@babel/template@^7.7.4", "@babel/template@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.3.tgz#e02ad04fe262a657809327f578056ca15fd4d1b8" @@ -1238,6 +1252,16 @@ "@types/yargs" "^15.0.0" chalk "^3.0.0" +"@jest/types@^25.5.0": + version "25.5.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.5.0.tgz#4d6a4793f7b9599fc3680877b856a97dbccf2a9d" + integrity sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^1.1.1" + "@types/yargs" "^15.0.0" + chalk "^3.0.0" + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -1743,6 +1767,48 @@ "@svgr/plugin-svgo" "^4.3.1" loader-utils "^1.2.3" +"@testing-library/dom@^7.2.2": + version "7.5.8" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.5.8.tgz#35eb20f1cfd90629a09728954fd916ea142e5de5" + integrity sha512-aEK4GDeIk3sHuuF8NNvZrmZg5xfF7llvdlVfjely/fPg/GE4yLa0cVZEBWpS6oVUBk2tEXjwTDPFnMOe/M0GTQ== + dependencies: + "@babel/runtime" "^7.9.6" + aria-query "^4.0.2" + dom-accessibility-api "^0.4.4" + pretty-format "^25.5.0" + +"@testing-library/jest-dom@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.8.0.tgz#815e830129c4dda6c8e9a725046397acec523669" + integrity sha512-9Y4FxYIxfwHpUyJVqI8EOfDP2LlEBqKwXE3F+V8ightji0M2rzQB+9kqZ5UJxNs+9oXJIgvYj7T3QaXLNHVDMw== + dependencies: + "@babel/runtime" "^7.9.2" + "@types/testing-library__jest-dom" "^5.0.2" + chalk "^3.0.0" + css "^2.2.4" + css.escape "^1.5.1" + jest-diff "^25.1.0" + jest-matcher-utils "^25.1.0" + lodash "^4.17.15" + redent "^3.0.0" + +"@testing-library/react-hooks@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-3.2.1.tgz#19b6caa048ef15faa69d439c469033873ea01294" + integrity sha512-1OB6Ksvlk6BCJA1xpj8/WWz0XVd1qRcgqdaFAq+xeC6l61Ucj0P6QpA5u+Db/x9gU4DCX8ziR5b66Mlfg0M2RA== + dependencies: + "@babel/runtime" "^7.5.4" + "@types/testing-library__react-hooks" "^3.0.0" + +"@testing-library/react@^10.0.4": + version "10.0.4" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-10.0.4.tgz#8e0e299cd91acc626d81ed8489fdc13df864c31d" + integrity sha512-2e1B5debfuiIGbvUuiSXybskuh7ZTVJDDvG/IxlzLOY9Co/mKFj9hIklAe2nGZYcOUxFaiqWrRZ9vCVGzJfRlQ== + dependencies: + "@babel/runtime" "^7.9.6" + "@testing-library/dom" "^7.2.2" + "@types/testing-library__react" "^10.0.1" + "@types/babel__core@^7.1.0", "@types/babel__core@^7.1.3": version "7.1.3" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.3.tgz#e441ea7df63cd080dfcd02ab199e6d16a735fc30" @@ -1776,26 +1842,11 @@ dependencies: "@babel/types" "^7.3.0" -"@types/cheerio@*": - version "0.22.16" - resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.16.tgz#c748a97b8a6f781b04bbda4a552e11b35bcc77e4" - integrity sha512-bSbnU/D4yzFdzLpp3+rcDj0aQQMIRUBNJU7azPxdqMpnexjUSvGJyDuOBQBHeOZh1mMKgsJm6Dy+LLh80Ew4tQ== - dependencies: - "@types/node" "*" - "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== -"@types/enzyme@^3.10.4": - version "3.10.4" - resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.10.4.tgz#dd4961042381a7c0f6637ce25fec3f773ce489dd" - integrity sha512-P5XpxcIt9KK8QUH4al4ttfJfIHg6xmN9ZjyUzRSzAsmDYwRXLI05ng/flZOPXrEXmp8ZYiN8/tEXYK5KSOQk3w== - dependencies: - "@types/cheerio" "*" - "@types/react" "*" - "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" @@ -1841,6 +1892,14 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" +"@types/jest@*": + version "25.2.3" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.2.3.tgz#33d27e4c4716caae4eced355097a47ad363fdcaf" + integrity sha512-JXc1nK/tXHiDhV55dvfzqtmP4S3sy3T3ouV2tkViZgxY/zeUkcpQcQPGRlgF4KmWzWW5oiWYSZwtCB+2RsE4Fw== + dependencies: + jest-diff "^25.2.1" + pretty-format "^25.2.1" + "@types/jest@^25.1.1": version "25.1.1" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.1.1.tgz#dcf65a8ee315b91ad39c0d358ae0ddc5602ab0e9" @@ -1892,6 +1951,13 @@ "@types/history" "*" "@types/react" "*" +"@types/react-dom@*": + version "16.9.8" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.8.tgz#fe4c1e11dfc67155733dfa6aa65108b4971cb423" + integrity sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA== + dependencies: + "@types/react" "*" + "@types/react-dom@^16.9.5": version "16.9.5" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.5.tgz#5de610b04a35d07ffd8f44edad93a71032d9aaa7" @@ -1906,6 +1972,13 @@ dependencies: "@types/react" "*" +"@types/react-test-renderer@*": + version "16.9.2" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-16.9.2.tgz#e1c408831e8183e5ad748fdece02214a7c2ab6c5" + integrity sha512-4eJr1JFLIAlWhzDkBCkhrOIWOvOxcCAfQh+jiKg7l/nNZcCIL2MHl2dZhogIFKyHzedVWHaVP1Yydq/Ruu4agw== + dependencies: + "@types/react" "*" + "@types/react-textarea-autosize@^4.3.3": version "4.3.5" resolved "https://registry.yarnpkg.com/@types/react-textarea-autosize/-/react-textarea-autosize-4.3.5.tgz#6c4d2753fa1864c98c0b2b517f67bb1f6e4c46de" @@ -1940,6 +2013,37 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== +"@types/testing-library__dom@*": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@types/testing-library__dom/-/testing-library__dom-7.0.2.tgz#2906f8a0dce58b0746c6ab606f786bd06fe6940e" + integrity sha512-8yu1gSwUEAwzg2OlPNbGq+ixhmSviGurBu1+ivxRKq1eRcwdjkmlwtPvr9VhuxTq2fNHBWN2po6Iem3Xt5A6rg== + dependencies: + pretty-format "^25.1.0" + +"@types/testing-library__jest-dom@^5.0.2": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.7.0.tgz#078790bf4dc89152a74428591a228ec5f9433251" + integrity sha512-LoZ3uonlnAbJUz4bg6UoeFl+frfndXngmkCItSjJ8DD5WlRfVqPC5/LgJASsY/dy7AHH2YJ7PcsdASOydcVeFA== + dependencies: + "@types/jest" "*" + +"@types/testing-library__react-hooks@^3.0.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@types/testing-library__react-hooks/-/testing-library__react-hooks-3.2.0.tgz#52f3a109bef06080e3b1e3ae7ea1c014ce859897" + integrity sha512-dE8iMTuR5lzB+MqnxlzORlXzXyCL0EKfzH0w/lau20OpkHD37EaWjZDz0iNG8b71iEtxT4XKGmSKAGVEqk46mw== + dependencies: + "@types/react" "*" + "@types/react-test-renderer" "*" + +"@types/testing-library__react@^10.0.1": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@types/testing-library__react/-/testing-library__react-10.0.1.tgz#92bb4a02394bf44428e35f1da2970ed77f803593" + integrity sha512-RbDwmActAckbujLZeVO/daSfdL1pnjVqas25UueOkAY5r7vriavWf0Zqg7ghXMHa8ycD/kLkv8QOj31LmSYwww== + dependencies: + "@types/react-dom" "*" + "@types/testing-library__dom" "*" + pretty-format "^25.1.0" + "@types/ua-parser-js@^0.7.33": version "0.7.33" resolved "https://registry.yarnpkg.com/@types/ua-parser-js/-/ua-parser-js-0.7.33.tgz#4a92089511574e12928a7cb6b99a01831acd1dd7" @@ -2331,22 +2435,6 @@ airbnb-js-shims@^2.2.1: string.prototype.padstart "^3.0.0" symbol.prototype.description "^1.0.0" -airbnb-prop-types@^2.15.0: - version "2.15.0" - resolved "https://registry.yarnpkg.com/airbnb-prop-types/-/airbnb-prop-types-2.15.0.tgz#5287820043af1eb469f5b0af0d6f70da6c52aaef" - integrity sha512-jUh2/hfKsRjNFC4XONQrxo/n/3GG4Tn6Hl0WlFQN5PY9OMC9loSCoAYKnZsWaP8wEfd5xcrPloK0Zg6iS1xwVA== - dependencies: - array.prototype.find "^2.1.0" - function.prototype.name "^1.1.1" - has "^1.0.3" - is-regex "^1.0.4" - object-is "^1.0.1" - object.assign "^4.1.0" - object.entries "^1.1.0" - prop-types "^15.7.2" - prop-types-exact "^1.2.0" - react-is "^16.9.0" - ajv-errors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" @@ -2492,6 +2580,14 @@ aria-query@^3.0.0: ast-types-flow "0.0.7" commander "^2.11.0" +aria-query@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.0.2.tgz#250687b4ccde1ab86d127da0432ae3552fc7b145" + integrity sha512-S1G1V790fTaigUSM/Gd0NngzEfiMy9uTUfMyHhKhVyy4cH5O/eTuR01ydhGL0z4Za1PXFTRGH3qL8VhUQuEO5w== + dependencies: + "@babel/runtime" "^7.7.4" + "@babel/runtime-corejs3" "^7.7.4" + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -2512,11 +2608,6 @@ array-equal@^1.0.0: resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= -array-filter@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" - integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= - array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -2548,14 +2639,6 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -array.prototype.find@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.1.0.tgz#630f2eaf70a39e608ac3573e45cf8ccd0ede9ad7" - integrity sha512-Wn41+K1yuO5p7wRZDl7890c3xvv5UBrfVXTVIe28rSQb6LS0fZMDrQB6PAcxQFRFy6vJTLDc3A2+3CjQdzVKRg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.13.0" - array.prototype.flat@^1.2.1: version "1.2.3" resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" @@ -3553,18 +3636,6 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -cheerio@^1.0.0-rc.2: - version "1.0.0-rc.3" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6" - integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA== - dependencies: - css-select "~1.2.0" - dom-serializer "~0.1.1" - entities "~1.1.1" - htmlparser2 "^3.9.1" - lodash "^4.15.0" - parse5 "^3.0.1" - chokidar@^2.0.2, chokidar@^2.0.4, chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -4050,7 +4121,7 @@ css-select-base-adapter@^0.1.1: resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== -css-select@^1.1.0, css-select@~1.2.0: +css-select@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= @@ -4088,6 +4159,21 @@ css-what@^3.2.1: resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.2.1.tgz#f4a8f12421064621b456755e34a03a2c22df5da1" integrity sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw== +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s= + +css@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" + integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw== + dependencies: + inherits "^2.0.3" + source-map "^0.6.1" + source-map-resolve "^0.5.2" + urix "^0.1.0" + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" @@ -4321,6 +4407,11 @@ diff-sequences@^25.1.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.1.0.tgz#fd29a46f1c913fd66c22645dc75bffbe43051f32" integrity sha512-nFIfVk5B/NStCsJ+zaPO4vYuLjlzQ6uFvPxzYyHlejNZ/UGa7G/n7peOXVrVNvRuyfstt+mZQYGpjxg9Z6N8Kw== +diff-sequences@^25.2.6: + version "25.2.6" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" + integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -4338,11 +4429,6 @@ dir-glob@2.0.0: arrify "^1.0.1" path-type "^3.0.0" -discontinuous-range@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" - integrity sha1-44Mx8IRLukm5qctxx3FYWqsbxlo= - doctrine@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" @@ -4365,6 +4451,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-accessibility-api@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.4.4.tgz#c2f9fb8b591bc19581e7ef3e6fe35baf1967c498" + integrity sha512-XBM62jdDc06IXSujkqw6BugEWiDkp6jphtzVJf1kgPQGvfzaU7/jRtRSF/mxc8DBCIm2LS3bN1dCa5Sfxx982A== + dom-converter@^0.2: version "0.2.0" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" @@ -4380,14 +4471,6 @@ dom-serializer@0: domelementtype "^2.0.1" entities "^2.0.0" -dom-serializer@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" - integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== - dependencies: - domelementtype "^1.3.0" - entities "^1.1.1" - dom-walk@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" @@ -4398,7 +4481,7 @@ domain-browser@^1.1.1: resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== -domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: +domelementtype@1, domelementtype@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== @@ -4570,7 +4653,7 @@ enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: memory-fs "^0.5.0" tapable "^1.0.0" -entities@^1.1.1, entities@^1.1.2, entities@~1.1.1: +entities@^1.1.1, entities@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== @@ -4588,68 +4671,6 @@ env-ci@^2.1.0: execa "^1.0.0" java-properties "^0.2.9" -enzyme-adapter-react-16@^1.14.0: - version "1.15.2" - resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.2.tgz#b16db2f0ea424d58a808f9df86ab6212895a4501" - integrity sha512-SkvDrb8xU3lSxID8Qic9rB8pvevDbLybxPK6D/vW7PrT0s2Cl/zJYuXvsd1EBTz0q4o3iqG3FJhpYz3nUNpM2Q== - dependencies: - enzyme-adapter-utils "^1.13.0" - enzyme-shallow-equal "^1.0.1" - has "^1.0.3" - object.assign "^4.1.0" - object.values "^1.1.1" - prop-types "^15.7.2" - react-is "^16.12.0" - react-test-renderer "^16.0.0-0" - semver "^5.7.0" - -enzyme-adapter-utils@^1.13.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.13.0.tgz#01c885dde2114b4690bf741f8dc94cee3060eb78" - integrity sha512-YuEtfQp76Lj5TG1NvtP2eGJnFKogk/zT70fyYHXK2j3v6CtuHqc8YmgH/vaiBfL8K1SgVVbQXtTcgQZFwzTVyQ== - dependencies: - airbnb-prop-types "^2.15.0" - function.prototype.name "^1.1.2" - object.assign "^4.1.0" - object.fromentries "^2.0.2" - prop-types "^15.7.2" - semver "^5.7.1" - -enzyme-shallow-equal@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.1.tgz#7afe03db3801c9b76de8440694096412a8d9d49e" - integrity sha512-hGA3i1so8OrYOZSM9whlkNmVHOicJpsjgTzC+wn2JMJXhq1oO4kA4bJ5MsfzSIcC71aLDKzJ6gZpIxrqt3QTAQ== - dependencies: - has "^1.0.3" - object-is "^1.0.2" - -enzyme@3.10: - version "3.10.0" - resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.10.0.tgz#7218e347c4a7746e133f8e964aada4a3523452f6" - integrity sha512-p2yy9Y7t/PFbPoTvrWde7JIYB2ZyGC+NgTNbVEGvZ5/EyoYSr9aG/2rSbVvyNvMHEhw9/dmGUJHWtfQIEiX9pg== - dependencies: - array.prototype.flat "^1.2.1" - cheerio "^1.0.0-rc.2" - function.prototype.name "^1.1.0" - has "^1.0.3" - html-element-map "^1.0.0" - is-boolean-object "^1.0.0" - is-callable "^1.1.4" - is-number-object "^1.0.3" - is-regex "^1.0.4" - is-string "^1.0.4" - is-subset "^0.1.1" - lodash.escape "^4.0.1" - lodash.isequal "^4.5.0" - object-inspect "^1.6.0" - object-is "^1.0.1" - object.assign "^4.1.0" - object.entries "^1.0.4" - object.values "^1.0.4" - raf "^3.4.0" - rst-selector-parser "^2.2.3" - string.prototype.trim "^1.1.2" - errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" @@ -4664,7 +4685,7 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.13.0, es-abstract@^1.17.0, es-abstract@^1.17.0-next.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4: +es-abstract@^1.17.0, es-abstract@^1.17.0-next.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4: version "1.17.4" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ== @@ -5585,7 +5606,7 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -function.prototype.name@^1.1.0, function.prototype.name@^1.1.1, function.prototype.name@^1.1.2: +function.prototype.name@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.2.tgz#5cdf79d7c05db401591dfde83e3b70c5123e9a45" integrity sha512-C8A+LlHBJjB2AdcRPorc5JvJ5VUoWlXdEHLOJdCI7kjHEtGTpHQUiqMvCIKUwIsGwZX2jZJy761AXsn356bJQg== @@ -5935,13 +5956,6 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== -html-element-map@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/html-element-map/-/html-element-map-1.2.0.tgz#dfbb09efe882806af63d990cf6db37993f099f22" - integrity sha512-0uXq8HsuG1v2TmQ8QkIhzbrqeskE4kn52Q18QJ9iAA/SnHoEKXWiUxHQtclRsCFWEUD2So34X+0+pZZu862nnw== - dependencies: - array-filter "^1.0.0" - html-encoding-sniffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" @@ -5984,7 +5998,7 @@ html-webpack-plugin@^4.0.0-beta.2: tapable "^1.1.3" util.promisify "1.0.0" -htmlparser2@^3.3.0, htmlparser2@^3.9.1: +htmlparser2@^3.3.0: version "3.10.1" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== @@ -6295,11 +6309,6 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" -is-boolean-object@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e" - integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ== - is-buffer@^1.0.2, is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -6467,11 +6476,6 @@ is-module@^1.0.0: resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= -is-number-object@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" - integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== - is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -6552,11 +6556,6 @@ is-string@^1.0.4, is-string@^1.0.5: resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== -is-subset@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" - integrity sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY= - is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" @@ -6772,6 +6771,16 @@ jest-diff@^25.1.0: jest-get-type "^25.1.0" pretty-format "^25.1.0" +jest-diff@^25.2.1: + version "25.5.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.5.0.tgz#1dd26ed64f96667c068cef026b677dfa01afcfa9" + integrity sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A== + dependencies: + chalk "^3.0.0" + diff-sequences "^25.2.6" + jest-get-type "^25.2.6" + pretty-format "^25.5.0" + jest-docblock@^25.1.0: version "25.1.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-25.1.0.tgz#0f44bea3d6ca6dfc38373d465b347c8818eccb64" @@ -6818,6 +6827,11 @@ jest-get-type@^25.1.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.1.0.tgz#1cfe5fc34f148dc3a8a3b7275f6b9ce9e2e8a876" integrity sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw== +jest-get-type@^25.2.6: + version "25.2.6" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" + integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig== + jest-haste-map@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d" @@ -7482,21 +7496,6 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= -lodash.escape@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" - integrity sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg= - -lodash.flattendeep@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" - integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= - -lodash.isequal@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" - integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= - lodash.memoize@4.x, lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -7517,7 +7516,7 @@ lodash.unescape@4.0.1: resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= -lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: +lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -7906,11 +7905,6 @@ moment@^2.18.1: resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== -moo@^0.5.0: - version "0.5.1" - resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4" - integrity sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w== - move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -7975,17 +7969,6 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -nearley@^2.7.10: - version "2.19.1" - resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.19.1.tgz#4af4006e16645ff800e9f993c3af039857d9dbdc" - integrity sha512-xq47GIUGXxU9vQg7g/y1o1xuKnkO7ev4nRWqftmQrLkfnE/FjRqDaGOUakM8XHPn/6pW3bGjU2wgoJyId90rqg== - dependencies: - commander "^2.19.0" - moo "^0.5.0" - railroad-diagrams "^1.0.0" - randexp "0.4.6" - semver "^5.4.1" - negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -8198,12 +8181,12 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.6.0, object-inspect@^1.7.0: +object-inspect@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== -object-is@^1.0.1, object-is@^1.0.2: +object-is@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4" integrity sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ== @@ -8235,7 +8218,7 @@ object.assign@^4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" -object.entries@^1.0.4, object.entries@^1.1.0, object.entries@^1.1.1: +object.entries@^1.1.0, object.entries@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.1.tgz#ee1cf04153de02bb093fec33683900f57ce5399b" integrity sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ== @@ -8270,7 +8253,7 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.0.4, object.values@^1.1.0, object.values@^1.1.1: +object.values@^1.1.0, object.values@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== @@ -8518,13 +8501,6 @@ parse5@5.1.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== -parse5@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" - integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA== - dependencies: - "@types/node" "*" - parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -8861,6 +8837,16 @@ pretty-format@^25.1.0: ansi-styles "^4.0.0" react-is "^16.12.0" +pretty-format@^25.2.1, pretty-format@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a" + integrity sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ== + dependencies: + "@jest/types" "^25.5.0" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^16.12.0" + pretty-hrtime@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" @@ -8941,15 +8927,6 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.3" -prop-types-exact@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/prop-types-exact/-/prop-types-exact-1.2.0.tgz#825d6be46094663848237e3925a98c6e944e9869" - integrity sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA== - dependencies: - has "^1.0.3" - object.assign "^4.1.0" - reflect.ownkeys "^0.2.0" - prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" @@ -9094,31 +9071,11 @@ quick-format-unescaped@^1.1.1: dependencies: fast-safe-stringify "^1.0.8" -raf@^3.4.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" - integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== - dependencies: - performance-now "^2.1.0" - -railroad-diagrams@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" - integrity sha1-635iZ1SN3t+4mcG5Dlc3RVnN234= - ramda@^0.21.0: version "0.21.0" resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.21.0.tgz#a001abedb3ff61077d4ff1d577d44de77e8d0a35" integrity sha1-oAGr7bP/YQd9T/HVd9RN536NCjU= -randexp@0.4.6: - version "0.4.6" - resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" - integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== - dependencies: - discontinuous-range "1.0.0" - ret "~0.1.10" - randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -9301,7 +9258,7 @@ react-inspector@^4.0.0: prop-types "^15.6.1" storybook-chromatic "^2.2.2" -react-is@^16.12.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6, react-is@^16.9.0: +react-is@^16.12.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.9.0: version "16.12.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q== @@ -9353,15 +9310,15 @@ react-syntax-highlighter@^11.0.2: prismjs "^1.8.4" refractor "^2.4.1" -react-test-renderer@^16.0.0-0: - version "16.12.0" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.12.0.tgz#11417ffda579306d4e841a794d32140f3da1b43f" - integrity sha512-Vj/teSqt2oayaWxkbhQ6gKis+t5JrknXfPVo+aIJ8QwYAqMPH77uptOdrlphyxl8eQI/rtkOYg86i/UWkpFu0w== +react-test-renderer@16.9: + version "16.9.0" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.9.0.tgz#7ed657a374af47af88f66f33a3ef99c9610c8ae9" + integrity sha512-R62stB73qZyhrJo7wmCW9jgl/07ai+YzvouvCXIJLBkRlRqLx4j9RqcLEAfNfU3OxTGucqR2Whmn3/Aad6L3hQ== dependencies: object-assign "^4.1.1" prop-types "^15.6.2" - react-is "^16.8.6" - scheduler "^0.18.0" + react-is "^16.9.0" + scheduler "^0.15.0" react-textarea-autosize@^7.1.0: version "7.1.2" @@ -9502,10 +9459,13 @@ recursive-readdir@2.2.2: dependencies: minimatch "3.0.4" -reflect.ownkeys@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460" - integrity sha1-dJrO7H8/34tj+SegSAnpDFwLNGA= +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" refractor@^2.4.1: version "2.10.1" @@ -9538,6 +9498,11 @@ regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.3: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== +regenerator-runtime@^0.13.4: + version "0.13.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" + integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== + regenerator-transform@^0.14.0: version "0.14.1" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb" @@ -9852,14 +9817,6 @@ rollup@^1.27.0: "@types/node" "*" acorn "^7.1.0" -rst-selector-parser@^2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91" - integrity sha1-gbIw6i/MYGbInjRy3nlChdmwPZE= - dependencies: - lodash.flattendeep "^4.4.0" - nearley "^2.7.10" - rsvp@^4.8.4: version "4.8.5" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" @@ -9978,7 +9935,7 @@ select@^1.1.2: resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0= -"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -10260,7 +10217,7 @@ source-list-map@^2.0.0: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-resolve@^0.5.0: +source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== @@ -10559,15 +10516,6 @@ string.prototype.padstart@^3.0.0: define-properties "^1.1.3" es-abstract "^1.17.0-next.1" -string.prototype.trim@^1.1.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz#141233dff32c82bfad80684d7e5f0869ee0fb782" - integrity sha512-MjGFEeqixw47dAMFMtgUro/I0+wNqZB5GKXGt1fFr24u3TzDXCPu7J9Buppzoe3r/LqkSDLDDJzE15RGWDGAVw== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - function-bind "^1.1.1" - string.prototype.trimleft@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74"