diff --git a/src/components/PayPalButtons.test.js b/src/components/PayPalButtons.test.js index c61043c..293cf12 100644 --- a/src/components/PayPalButtons.test.js +++ b/src/components/PayPalButtons.test.js @@ -1,5 +1,11 @@ import React, { useState } from "react"; -import { render, waitFor, screen, fireEvent } from "@testing-library/react"; +import { + render, + waitFor, + screen, + fireEvent, + act, +} from "@testing-library/react"; import { PayPalScriptProvider } from "../ScriptContext"; import PayPalButtons from "./PayPalButtons"; @@ -11,13 +17,11 @@ jest.mock("@paypal/paypal-js", () => ({ })); describe("", () => { - let consoleErrorSpy; beforeEach(() => { window.paypal = {}; loadScript.mockResolvedValue(window.paypal); - - consoleErrorSpy = jest.spyOn(console, "error"); - console.error.mockImplementation(() => {}); + const consoleErrorSpy = jest.spyOn(console, "error"); + consoleErrorSpy.mockImplementation(() => {}); }); afterEach(() => { jest.clearAllMocks(); @@ -98,14 +102,14 @@ describe("", () => { expect(window.paypal.Buttons).toHaveBeenCalled(); }); - const onInitCallback = window.paypal.Buttons.mock.calls[0][0].onInit; - const onInitActions = { enable: jest.fn(), disable: jest.fn(), }; - onInitCallback({}, onInitActions); + act(() => + window.paypal.Buttons.mock.calls[0][0].onInit({}, onInitActions) + ); await waitFor(() => { expect(onInitCallbackMock).toHaveBeenCalled(); @@ -183,8 +187,10 @@ describe("", () => { expect(screen.getByTestId("orderID").innerHTML).toBe("1"); - // call createOrder() to trigger a state change - window.paypal.Buttons.mock.calls[0][0].createOrder(); + act(() => + // call createOrder() to trigger a state change + window.paypal.Buttons.mock.calls[0][0].createOrder() + ); await waitFor(() => expect(screen.getByTestId("orderID").innerHTML).toBe("2") @@ -246,17 +252,21 @@ describe("", () => { }; }; + const onError = jest.fn(); + + const wrapper = ({ children }) => ( + {children} + ); + render( - + , + { wrapper } ); - await waitFor(() => - expect(consoleErrorSpy).toHaveBeenCalledWith( - expect.stringMatching(/Window closed/) - ) - ); + await waitFor(() => expect(onError).toHaveBeenCalled()); + expect(onError.mock.calls[0][0].message).toMatchSnapshot(); }); }); diff --git a/src/components/PayPalButtons.tsx b/src/components/PayPalButtons.tsx index b9af937..bcd27e3 100644 --- a/src/components/PayPalButtons.tsx +++ b/src/components/PayPalButtons.tsx @@ -88,9 +88,15 @@ export default function PayPalButtons({ } buttons.current.render(buttonsContainerRef.current).catch((err) => { - console.error( - `Failed to render component. ${err}` - ); + // component failed to render, possibly because it was closed or destroyed. + if (buttonsContainerRef.current === null) { + // ref is no longer in the DOM, we can safely ignore the error + return; + } + // ref is still in the DOM + setErrorState(() => { + throw new Error(`Failed to render component. ${err}`); + }); }); return closeButtonsComponent; diff --git a/src/components/__snapshots__/PayPalButtons.test.js.snap b/src/components/__snapshots__/PayPalButtons.test.js.snap index 5f90ae4..f49b607 100644 --- a/src/components/__snapshots__/PayPalButtons.test.js.snap +++ b/src/components/__snapshots__/PayPalButtons.test.js.snap @@ -1,5 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[` should catch and log zoid render errors 1`] = `"Failed to render component. Window closed"`; + exports[` should throw an error when no components are passed to the PayPalScriptProvider 1`] = `"Unable to render because window.paypal.Buttons is undefined."`; exports[` should throw an error when the 'buttons' component is missing from the components list passed to the PayPalScriptProvider 1`] = `