Skip to content

Commit

Permalink
refactor: removes onReady callback from PlaidLink
Browse files Browse the repository at this point in the history
  • Loading branch information
thedanchez committed Nov 26, 2024
1 parent 73e0119 commit 016ca63
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 33 deletions.
8 changes: 1 addition & 7 deletions src/components/PlaidLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type { CreatePlaidLinkConfig } from "../types";

type PlaidLinkProps = CreatePlaidLinkConfig &
JSX.ButtonHTMLAttributes<HTMLButtonElement> & {
readonly onReady?: () => void;
readonly onLoadError?: (error: Error) => void;
};

Expand Down Expand Up @@ -48,11 +47,6 @@ const PlaidLink = (props: ParentProps<PlaidLinkProps>) => {
const [localProps, otherBtnProps] = splitProps(btnProps, ["disabled", "onClick"]);
const { ready, error, plaidLink } = createPlaidLink(() => plaidProps);

createEffect(() => {
if (!ready()) return;
props.onReady?.();
});

createEffect(() => {
if (!error()) return;
props.onLoadError?.(error() as Error);
Expand All @@ -61,7 +55,7 @@ const PlaidLink = (props: ParentProps<PlaidLinkProps>) => {
return (
<button
{...otherBtnProps}
disabled={!ready() || Boolean(error()) || Boolean(localProps.disabled)}
disabled={!ready() || Boolean(localProps.disabled)}
onClick={(e) => {
if (typeof localProps.onClick === "function") {
localProps.onClick?.(e);
Expand Down
126 changes: 100 additions & 26 deletions src/components/__tests__/PlaidLink.test.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { render } from "@solidjs/testing-library";
import { render, waitFor } from "@solidjs/testing-library";
import { userEvent } from "@testing-library/user-event";
import { createScript } from "solid-create-script";
import { createRoot, createSignal } from "solid-js";
import { beforeEach, describe, expect, it, type MockedFunction, vi } from "vitest";
import { beforeEach, describe, expect, it, type MockedFunction, type MockedObject, vi } from "vitest";

import { createFakeResource } from "../../testUtils";
import type { PlaidHandler } from "../../types";
Expand All @@ -12,16 +11,9 @@ vi.mock("solid-create-script");
const mockCreateScript = createScript as MockedFunction<typeof createScript>;

describe("COMPONENT: <PlaidLink />", () => {
let fakePlaidLink: PlaidHandler;
let fakePlaidLink: MockedObject<PlaidHandler>;

beforeEach(() => {
mockCreateScript.mockImplementationOnce(() =>
createFakeResource({
loading: false,
state: "ready",
}),
);

fakePlaidLink = {
submit: vi.fn(),
exit: vi.fn(),
Expand All @@ -31,15 +23,70 @@ describe("COMPONENT: <PlaidLink />", () => {

window.Plaid = {
createEmbedded: vi.fn(),
create: () => fakePlaidLink,
create: ({ onLoad }) => {
onLoad?.();
return fakePlaidLink;
},
};
});

it("renders correctly on screen", async () => {
it("is disabled when Plaid Link is not ready", async () => {
mockCreateScript.mockImplementationOnce(() =>
createFakeResource({
loading: true,
state: "pending",
}),
);

const { getByText } = render(() => (
<PlaidLink
fetchToken={() => Promise.resolve({ link_token: "test-token", expiration: "2024-11-25T14:30:00Z" })}
onSuccess={vi.fn()}
>
Open
</PlaidLink>
));

expect(getByText("Open")).toBeDisabled();
});

it("is disabled when user-side disabled is true despite Plaid Link being ready", async () => {
mockCreateScript.mockImplementationOnce(() =>
createFakeResource({
loading: false,
state: "ready",
}),
);

const { getByText } = render(() => (
<PlaidLink
fetchToken={vi.fn(async () => ({ link_token: "test-token", expiration: "2024-11-25T14:30:00Z" }))}
fetchToken={() => Promise.resolve({ link_token: "test-token", expiration: "2024-11-25T14:30:00Z" })}
onSuccess={vi.fn()}
disabled
>
Open
</PlaidLink>
));

expect(getByText("Open")).toBeDisabled();
});

it("triggers onLoadError when error returns", async () => {
mockCreateScript.mockImplementationOnce(() =>
createFakeResource({
loading: false,
state: "errored",
error: new Error("SCRIPT_LOAD"),
}),
);

const onLoadErrorSpy = vi.fn();

const { getByText } = render(() => (
<PlaidLink
fetchToken={() => Promise.resolve({ link_token: "test-token", expiration: "2024-11-25T14:30:00Z" })}
onSuccess={vi.fn()}
onLoadError={onLoadErrorSpy}
>
Open
</PlaidLink>
Expand All @@ -49,32 +96,59 @@ describe("COMPONENT: <PlaidLink />", () => {

await userEvent.click(getByText("Open"));

expect(fakePlaidLink.open).toHaveBeenCalled();
waitFor(() => expect(onLoadErrorSpy).toHaveBeenCalledWith(new Error("SCRIPT_LOAD")));
});

it("is enabled when Plaid Link is ready", async () => {
mockCreateScript.mockImplementationOnce(() =>
createFakeResource({
loading: false,
state: "ready",
}),
);

const { getByText } = render(() => (
<PlaidLink
fetchToken={() => Promise.resolve({ link_token: "test-token", expiration: "2024-11-25T14:30:00Z" })}
onSuccess={vi.fn()}
>
Open
</PlaidLink>
));

waitFor(() => expect(getByText("Open")).toBeEnabled());

await userEvent.click(getByText("Open"));

waitFor(() => expect(fakePlaidLink.open).toHaveBeenCalled());
});

it("runs additional onClick logic when supplied as a prop", async () => {
const [didClick, setDidClick] = createSignal(false);
mockCreateScript.mockImplementationOnce(() =>
createFakeResource({
loading: false,
state: "ready",
}),
);

const onClickSpy = vi.fn();

const { getByText } = render(() => (
<PlaidLink
fetchToken={vi.fn(async () => ({ link_token: "test-token", expiration: "2024-11-25T14:30:00Z" }))}
fetchToken={() => Promise.resolve({ link_token: "test-token", expiration: "2024-11-25T14:30:00Z" })}
onSuccess={vi.fn()}
onClick={() => setDidClick(true)}
onClick={onClickSpy}
>
Open
</PlaidLink>
));

await createRoot(async (dispose) => {
expect(getByText("Open")).toBeInTheDocument();
expect(didClick()).toBe(false);
expect(getByText("Open")).toBeInTheDocument();

await userEvent.click(getByText("Open"));
await userEvent.click(getByText("Open"));

expect(fakePlaidLink.open).toHaveBeenCalled();
expect(didClick()).toBe(true);
waitFor(() => expect(fakePlaidLink.open).toHaveBeenCalled());

dispose();
});
waitFor(() => expect(onClickSpy).toHaveBeenCalled());
});
});

0 comments on commit 016ca63

Please sign in to comment.