import React from 'react'; import {render, act} from '@testing-library/react'; import * as EmbeddedCheckoutProviderModule from './EmbeddedCheckoutProvider'; import {EmbeddedCheckout} from './EmbeddedCheckout'; import * as mocks from '../../test/mocks'; const {EmbeddedCheckoutProvider} = EmbeddedCheckoutProviderModule; describe('EmbeddedCheckout on the client', () => { let mockStripe: any; let mockStripePromise: any; let mockEmbeddedCheckout: any; let mockEmbeddedCheckoutPromise: any; const fakeClientSecret = 'cs_123_secret_abc'; const fetchClientSecret = () => Promise.resolve(fakeClientSecret); const fakeOptions = {fetchClientSecret}; beforeEach(() => { mockStripe = mocks.mockStripe(); mockStripePromise = Promise.resolve(mockStripe); mockEmbeddedCheckout = mocks.mockEmbeddedCheckout(); mockEmbeddedCheckoutPromise = Promise.resolve(mockEmbeddedCheckout); mockStripe.initEmbeddedCheckout.mockReturnValue( mockEmbeddedCheckoutPromise ); jest.spyOn(React, 'useLayoutEffect'); }); afterEach(() => { jest.restoreAllMocks(); }); it('passes id to the wrapping DOM element', async () => { const {container} = render( ); await act(async () => await mockStripePromise); const embeddedCheckoutDiv = container.firstChild as Element; expect(embeddedCheckoutDiv.id).toBe('foo'); }); it('passes className to the wrapping DOM element', async () => { const {container} = render( ); await act(async () => await mockStripePromise); const embeddedCheckoutDiv = container.firstChild as Element; expect(embeddedCheckoutDiv).toHaveClass('bar'); }); it('mounts Embedded Checkout', async () => { const {container} = render( ); await act(() => mockEmbeddedCheckoutPromise); expect(mockEmbeddedCheckout.mount).toBeCalledWith(container.firstChild); }); it('does not mount until Embedded Checkout has been initialized', async () => { // Render with no stripe instance and client secret const {container, rerender} = render( ); expect(mockEmbeddedCheckout.mount).not.toBeCalled(); // Set stripe prop rerender( ); expect(mockEmbeddedCheckout.mount).not.toBeCalled(); // Set fetchClientSecret rerender( ); expect(mockEmbeddedCheckout.mount).not.toBeCalled(); // Resolve initialization promise await act(() => mockEmbeddedCheckoutPromise); expect(mockEmbeddedCheckout.mount).toBeCalledWith(container.firstChild); }); it('unmounts Embedded Checkout when the component unmounts', async () => { const {container, rerender} = render( ); await act(() => mockEmbeddedCheckoutPromise); expect(mockEmbeddedCheckout.mount).toBeCalledWith(container.firstChild); rerender( ); expect(mockEmbeddedCheckout.unmount).toBeCalled(); }); it('does not throw when the Embedded Checkout instance is already destroyed when unmounting', async () => { const {container, rerender} = render( ); await act(() => mockEmbeddedCheckoutPromise); expect(mockEmbeddedCheckout.mount).toBeCalledWith(container.firstChild); mockEmbeddedCheckout.unmount.mockImplementation(() => { throw new Error('instance has been destroyed'); }); expect(() => { rerender( ); }).not.toThrow(); }); it('still works with clientSecret param (deprecated)', async () => { const {container} = render( ); await act(() => mockEmbeddedCheckoutPromise); expect(mockEmbeddedCheckout.mount).toBeCalledWith(container.firstChild); }); });