From 9666b12ab397f9cd017031de16fb4a37cf830a75 Mon Sep 17 00:00:00 2001 From: Greg Jopa <534034+gregjopa@users.noreply.github.com> Date: Wed, 10 Mar 2021 21:08:04 -0600 Subject: [PATCH 1/2] Do not throw render errors when ref is invalid --- src/components/PayPalButtons.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) 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; From 685ef5e62888fbca5f2e7cfe688d332d3115dec7 Mon Sep 17 00:00:00 2001 From: Greg Jopa <534034+gregjopa@users.noreply.github.com> Date: Wed, 10 Mar 2021 22:03:53 -0600 Subject: [PATCH 2/2] Fix tests --- src/components/PayPalButtons.test.js | 42 ++++++++++++------- .../__snapshots__/PayPalButtons.test.js.snap | 2 + 2 files changed, 28 insertions(+), 16 deletions(-) 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/__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`] = `