Skip to content

Commit

Permalink
feat: log loading errors for the JS SDK script (#212)
Browse files Browse the repository at this point in the history
  • Loading branch information
borodovisin committed Nov 29, 2021
1 parent fa5f2c9 commit 77973f0
Show file tree
Hide file tree
Showing 8 changed files with 31 additions and 43 deletions.
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
},
"homepage": "https://paypal.github.io/react-paypal-js/",
"dependencies": {
"@paypal/paypal-js": "^4.1.0",
"@paypal/paypal-js": "^4.2.1",
"@paypal/sdk-constants": "^1.0.110"
},
"devDependencies": {
Expand Down
8 changes: 8 additions & 0 deletions src/components/PayPalScriptProvider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ describe("<PayPalScriptProvider />", () => {
});

test('should set "isRejected" state to "true" after failing to load the script', async () => {
const spyConsoleError = jest
.spyOn(console, "error")
.mockImplementation();
(loadScript as jest.Mock).mockRejectedValue(new Error());
const { state, TestComponent } = setupTestComponent();
render(
Expand All @@ -80,9 +83,13 @@ describe("<PayPalScriptProvider />", () => {
await waitFor(() => expect(state.isRejected).toBeTruthy());
expect(state.isPending).toBeFalsy();
expect(state.isResolved).toBeFalsy();
spyConsoleError.mockRestore();
});

test("shouldn't set isRejected state to true after failing to load the script, because the component was unmount", async () => {
const spyConsoleError = jest
.spyOn(console, "error")
.mockImplementation();
(loadScript as jest.Mock).mockRejectedValue(new Error());
const { state, TestComponent } = setupTestComponent();
const { unmount } = render(
Expand All @@ -99,6 +106,7 @@ describe("<PayPalScriptProvider />", () => {
expect(state.isPending).toBeTruthy();
expect(state.isRejected).toBeFalsy();
expect(state.isResolved).toBeFalsy();
spyConsoleError.mockRestore();
});

test("should control script loading with the deferLoading prop", async () => {
Expand Down
4 changes: 3 additions & 1 deletion src/components/PayPalScriptProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
SCRIPT_ID,
DATA_SDK_INTEGRATION_SOURCE,
DATA_SDK_INTEGRATION_SOURCE_VALUE,
LOAD_SCRIPT_ERROR,
} from "../constants";
import type { ScriptProviderProps } from "../types";
import { SCRIPT_LOADING_STATE, DISPATCH_ACTION } from "../types";
Expand Down Expand Up @@ -60,7 +61,8 @@ export const PayPalScriptProvider: FC<ScriptProviderProps> = ({
});
}
})
.catch(() => {
.catch((err) => {
console.error(`${LOAD_SCRIPT_ERROR} ${err}`);
if (isSubscribed) {
dispatch({
type: DISPATCH_ACTION.LOADING_STATUS,
Expand Down
10 changes: 4 additions & 6 deletions src/components/braintree/BraintreePayPalButtons.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
EMPTY_PROVIDER_CONTEXT_CLIENT_TOKEN_ERROR_MESSAGE,
BRAINTREE_SOURCE,
BRAINTREE_PAYPAL_CHECKOUT_SOURCE,
LOAD_SCRIPT_ERROR,
} from "../../constants";

import { FUNDING } from "../../index";
Expand Down Expand Up @@ -208,8 +209,7 @@ describe("Braintree PayPal button fail in mount process", () => {
await waitFor(() => expect(onError).toBeCalled());
expect(onError.mock.calls[0][0]).toEqual(
expect.objectContaining({
message:
"An error occurred when loading the Braintree scripts: Error: Server Error",
message: `${LOAD_SCRIPT_ERROR} Error: Server Error`,
})
);
spyConsoleError.mockRestore();
Expand Down Expand Up @@ -239,8 +239,7 @@ describe("Braintree PayPal button fail in mount process", () => {
await waitFor(() => expect(onError).toBeCalled());
expect(onError.mock.calls[0][0]).toEqual(
expect.objectContaining({
message:
"An error occurred when loading the Braintree scripts: Error: Cannot create the Braintree client",
message: `${LOAD_SCRIPT_ERROR} Error: Cannot create the Braintree client`,
})
);
spyConsoleError.mockRestore();
Expand Down Expand Up @@ -268,8 +267,7 @@ describe("Braintree PayPal button fail in mount process", () => {
await waitFor(() => expect(onError).toBeCalled());
expect(onError.mock.calls[0][0]).toEqual(
expect.objectContaining({
message:
"An error occurred when loading the Braintree scripts: TypeError: Cannot read property 'client' of null",
message: `${LOAD_SCRIPT_ERROR} TypeError: Cannot read property 'client' of null`,
})
);
spyConsoleError.mockRestore();
Expand Down
29 changes: 2 additions & 27 deletions src/components/braintree/BraintreePayPalButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
DATA_CLIENT_TOKEN,
BRAINTREE_SOURCE,
BRAINTREE_PAYPAL_CHECKOUT_SOURCE,
LOAD_SCRIPT_ERROR,
} from "../../constants";
import { PayPalButtons } from "../PayPalButtons";
import { useScriptProviderContext } from "../../hooks/scriptProviderHooks";
Expand All @@ -19,30 +20,6 @@ import type {
/**
This `<BraintreePayPalButtons />` component renders the [Braintree PayPal Buttons](https://developer.paypal.com/braintree/docs/guides/paypal/overview) for Braintree Merchants.
It relies on the `<PayPalScriptProvider />` parent component for managing state related to loading the JS SDK script.
Use props for customizing your buttons. For example, here's how you would use the `style`, `createOrder`, and `onApprove` options:
```jsx
import { PayPalScriptProvider, BraintreePayPalButtons } from "@paypal/react-paypal-js";
<PayPalScriptProvider options={{ "client-id": "test" }}>
<BraintreePayPalButtons
style={{ layout: "horizontal" }}
createOrder={(data, actions) => {
// the paypalCheckoutInstance from the braintree sdk integration is added to `actions.braintree`
return actions.braintree.createPayment({
flow: "checkout",
amount: "10.0",
currency: "USD",
intent: "capture"
})
}}
onApprove={(data, actions) => {
return actions.braintree.tokenizePayment(data)
.then((payload) => {
// call server-side endpoint to finish the sale
})
}
/>
</PayPalScriptProvider>
```
*/
export const BraintreePayPalButtons: FC<BraintreePayPalButtonsComponentProps> =
({
Expand Down Expand Up @@ -84,9 +61,7 @@ export const BraintreePayPalButtons: FC<BraintreePayPalButtonsComponentProps> =
})
.catch((err) => {
setErrorState(() => {
throw new Error(
`An error occurred when loading the Braintree scripts: ${err}`
);
throw new Error(`${LOAD_SCRIPT_ERROR} ${err}`);
});
});
}, [providerContext.options, dispatch]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,10 @@ describe("PayPalHostedFieldsProvider", () => {
spyConsoleError.mockRestore();
});

test("should return inmediatly when script provider is rejected", async () => {
test("should return immediately when script provider is rejected", async () => {
const spyConsoleError = jest
.spyOn(console, "error")
.mockImplementation();
loadScript.mockRejectedValue(new Error("Unknown error"));

render(
Expand Down Expand Up @@ -174,6 +177,7 @@ describe("PayPalHostedFieldsProvider", () => {
await waitFor(() => {
expect(loadScript).toBeCalled();
});
spyConsoleError.mockRestore();
});

test("should remove hostedfields components when unilegible", async () => {
Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const DATA_CLIENT_TOKEN = "data-client-token";
export const DATA_SDK_INTEGRATION_SOURCE = "data-sdk-integration-source";
export const DATA_SDK_INTEGRATION_SOURCE_VALUE = "react-paypal-js";
export const DATA_NAMESPACE = "data-namespace";
export const LOAD_SCRIPT_ERROR = "Failed to load the PayPal JS SDK script.";

/****************************
* Braintree error messages *
Expand Down

0 comments on commit 77973f0

Please sign in to comment.