import React from 'react'; import { render, screen } from '@testing-library/react'; import { usePlaidLink, PlaidLinkOptions, PlaidLinkOptionsWithLinkToken } from './'; import useScript from './react-script-hook'; jest.mock('./react-script-hook'); const mockedUseScript = useScript as jest.Mock; const ScriptLoadingState = { LOADING: [true, null], LOADED: [false, null], ERROR: [false, 'SCRIPT_LOAD_ERROR'], }; const ReadyState = { READY: 'READY', NOT_READY: 'NOT_READY', ERROR: 'ERROR', NO_ERROR: 'NO_ERROR', }; const HookComponent: React.FC<{ config: PlaidLinkOptions }> = ({ config }) => { const { open, ready, error } = usePlaidLink(config); return (
{ready ? ReadyState.READY : ReadyState.NOT_READY}
{error ? ReadyState.ERROR : ReadyState.NO_ERROR}
); }; describe('usePlaidLink', () => { const config: PlaidLinkOptions = { token: 'test-token', onSuccess: jest.fn(), }; beforeEach(() => { mockedUseScript.mockImplementation(() => ScriptLoadingState.LOADED); window.Plaid = { create: ({ onLoad }) => { onLoad && onLoad(); return { create: jest.fn(), open: jest.fn(), submit: jest.fn(), exit: jest.fn(), destroy: jest.fn(), }; }, open: jest.fn(), submit: jest.fn(), exit: jest.fn(), destroy: jest.fn(), }; }); afterEach(() => { jest.restoreAllMocks(); }); it('should render with token', async () => { render(); expect(screen.getByRole('button')); expect(screen.getByText(ReadyState.READY)); expect(screen.getByText(ReadyState.NO_ERROR)); }); it('should render with publicKey', async () => { const configWithPubKey: PlaidLinkOptions = { publicKey: 'test-public-key', env: 'sandbox', product: ['auth'], clientName: 'TEST', onSuccess: jest.fn(), }; render(); expect(screen.getByRole('button')); expect(screen.getByText(ReadyState.READY)); expect(screen.getByText(ReadyState.NO_ERROR)); }); it('should render with receivedRedirectUri', async () => { const configWithRedUri: PlaidLinkOptionsWithLinkToken = { token: null, receivedRedirectUri: 'test-received-redirect-uri', onSuccess: jest.fn(), }; render(); expect(screen.getByRole('button')); expect(screen.getByText(ReadyState.READY)); expect(screen.getByText(ReadyState.NO_ERROR)); }); it('should not be ready when script is loading', async () => { mockedUseScript.mockImplementation(() => ScriptLoadingState.LOADING); render(); expect(screen.getByText(ReadyState.NOT_READY)); expect(screen.getByText(ReadyState.NO_ERROR)); }); it('should not be ready if token, publicKey, and receivedRedirectUri are all missing', async () => { render(); expect(screen.getByText(ReadyState.NOT_READY)); expect(screen.getByText(ReadyState.NO_ERROR)); }); it('should not be ready if script fails to load', async () => { const consoleSpy = jest .spyOn(console, 'error') .mockImplementationOnce(() => {}); mockedUseScript.mockImplementation(() => ScriptLoadingState.ERROR); render(); expect(screen.getByText(ReadyState.NOT_READY)); expect(screen.getByText(ReadyState.ERROR)); expect(consoleSpy).toHaveBeenCalledWith( 'Error loading Plaid', 'SCRIPT_LOAD_ERROR' ); }); it('should be ready if token is generated async', async () => { const { rerender } = render( ); expect(screen.getByText(ReadyState.NOT_READY)); expect(screen.getByText(ReadyState.NO_ERROR)); config.token = 'test-token'; rerender(); expect(screen.getByText(ReadyState.READY)); expect(screen.getByText(ReadyState.NO_ERROR)); }); it('should be ready if token is generated async and script loads after token', async () => { mockedUseScript.mockImplementation(() => ScriptLoadingState.LOADING); const c: PlaidLinkOptions = { token: null, onSuccess: jest.fn(), }; const { rerender } = render(); expect(screen.getByText(ReadyState.NOT_READY)); expect(screen.getByText(ReadyState.NO_ERROR)); c.token = 'test-token'; rerender(); expect(screen.getByText(ReadyState.NOT_READY)); expect(screen.getByText(ReadyState.NO_ERROR)); mockedUseScript.mockImplementation(() => ScriptLoadingState.LOADED); rerender(); expect(screen.getByText(ReadyState.READY)); expect(screen.getByText(ReadyState.NO_ERROR)); }); });