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));
});
});