@paypal/checkout-components 5.0.437-alpha-ca07432.0 → 5.0.437
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/button.js +1 -1
- package/dist/test/button.js +1 -1
- package/package.json +1 -1
- package/src/types.js +0 -1
- package/src/zoid/buttons/component.jsx +24 -23
- package/src/zoid/buttons/component.test.js +23 -15
- package/src/zoid/checkout/component.jsx +13 -6
- package/src/zoid/checkout/component.test.js +84 -0
package/package.json
CHANGED
package/src/types.js
CHANGED
|
@@ -116,7 +116,7 @@ export type ButtonsComponent = ZoidComponent<
|
|
|
116
116
|
ButtonProps,
|
|
117
117
|
void,
|
|
118
118
|
void,
|
|
119
|
-
ButtonExtensions
|
|
119
|
+
ButtonExtensions
|
|
120
120
|
>;
|
|
121
121
|
|
|
122
122
|
// $FlowIssue
|
|
@@ -195,7 +195,7 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
195
195
|
try {
|
|
196
196
|
const cplPhases = prepareInstrumentationPayload(
|
|
197
197
|
buttonSessionID,
|
|
198
|
-
"buttons"
|
|
198
|
+
"buttons"
|
|
199
199
|
);
|
|
200
200
|
const cplLatencyMetrics = {
|
|
201
201
|
[FPTI_KEY.STATE]: "CPL_LATENCY_METRICS",
|
|
@@ -236,17 +236,18 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
236
236
|
fundingSource = `-${props.fundingSource}`;
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
-
const baseIframeAttributes = {
|
|
240
|
-
allowpaymentrequest: "allowpaymentrequest",
|
|
241
|
-
scrolling: "no",
|
|
242
|
-
title: `${FUNDING_BRAND_LABEL.PAYPAL}${fundingSource}`,
|
|
243
|
-
role: "presentation",
|
|
244
|
-
};
|
|
245
|
-
|
|
246
239
|
return {
|
|
247
|
-
iframe:
|
|
248
|
-
|
|
249
|
-
:
|
|
240
|
+
iframe: {
|
|
241
|
+
allowpaymentrequest: "allowpaymentrequest",
|
|
242
|
+
// Named origin, not bare "payment": zoid never sets this iframe's `src`
|
|
243
|
+
// attribute (it navigates the frame via window.location/form-POST after
|
|
244
|
+
// creation instead), so the 'src' keyword that bare `allow="payment"`
|
|
245
|
+
// resolves to can never match the actual checkout origin.
|
|
246
|
+
allow: `payment ${getPayPalDomain()}`,
|
|
247
|
+
scrolling: "no",
|
|
248
|
+
title: `${FUNDING_BRAND_LABEL.PAYPAL}${fundingSource}`,
|
|
249
|
+
role: "presentation",
|
|
250
|
+
},
|
|
250
251
|
};
|
|
251
252
|
},
|
|
252
253
|
|
|
@@ -266,11 +267,11 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
266
267
|
supportsVenmoPopups = supportsVenmoPopupsUtil(
|
|
267
268
|
buttonExperiments,
|
|
268
269
|
userAgentSupportsPopups(),
|
|
269
|
-
getUserAgent()
|
|
270
|
+
getUserAgent()
|
|
270
271
|
),
|
|
271
272
|
supportedNativeVenmoBrowser = isSupportedNativeVenmoBrowser(
|
|
272
273
|
buttonExperiments,
|
|
273
|
-
getUserAgent()
|
|
274
|
+
getUserAgent()
|
|
274
275
|
),
|
|
275
276
|
experiment = buttonExperiments,
|
|
276
277
|
createBillingAgreement,
|
|
@@ -384,7 +385,7 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
384
385
|
({ props: { buttonSessionID } }) =>
|
|
385
386
|
({ close }) => {
|
|
386
387
|
const overlay = document.getElementsByName(
|
|
387
|
-
`paypal-overlay-${buttonSessionID}
|
|
388
|
+
`paypal-overlay-${buttonSessionID}`
|
|
388
389
|
)?.[0];
|
|
389
390
|
|
|
390
391
|
if (overlay) {
|
|
@@ -460,7 +461,7 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
460
461
|
() => {
|
|
461
462
|
window.addEventListener(
|
|
462
463
|
"visibilitychange",
|
|
463
|
-
props.visibilityChangeHandler
|
|
464
|
+
props.visibilityChangeHandler
|
|
464
465
|
);
|
|
465
466
|
},
|
|
466
467
|
},
|
|
@@ -473,7 +474,7 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
473
474
|
() => {
|
|
474
475
|
window.removeEventListener(
|
|
475
476
|
"visibilitychange",
|
|
476
|
-
props.visibilityChangeHandler
|
|
477
|
+
props.visibilityChangeHandler
|
|
477
478
|
);
|
|
478
479
|
},
|
|
479
480
|
},
|
|
@@ -960,7 +961,7 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
960
961
|
// $FlowFixMe
|
|
961
962
|
value,
|
|
962
963
|
layout,
|
|
963
|
-
fundingSources
|
|
964
|
+
fundingSources
|
|
964
965
|
);
|
|
965
966
|
},
|
|
966
967
|
},
|
|
@@ -1045,7 +1046,7 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
1045
1046
|
const modalInstance = await getModal(
|
|
1046
1047
|
clientID,
|
|
1047
1048
|
merchantID,
|
|
1048
|
-
buttonSessionID
|
|
1049
|
+
buttonSessionID
|
|
1049
1050
|
);
|
|
1050
1051
|
return modalInstance?.show({
|
|
1051
1052
|
amount,
|
|
@@ -1141,8 +1142,8 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
1141
1142
|
value: ({ props }) => {
|
|
1142
1143
|
return Boolean(
|
|
1143
1144
|
props.onShippingChange ||
|
|
1144
|
-
|
|
1145
|
-
|
|
1145
|
+
props.onShippingAddressChange ||
|
|
1146
|
+
props.onShippingOptionsChange
|
|
1146
1147
|
);
|
|
1147
1148
|
},
|
|
1148
1149
|
},
|
|
@@ -1376,7 +1377,7 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
1376
1377
|
value: ({ props }) => {
|
|
1377
1378
|
return isSupportedNativeVenmoBrowser(
|
|
1378
1379
|
props.experiment,
|
|
1379
|
-
props.userAgent
|
|
1380
|
+
props.userAgent
|
|
1380
1381
|
);
|
|
1381
1382
|
},
|
|
1382
1383
|
queryParam: true,
|
|
@@ -1397,7 +1398,7 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
|
|
|
1397
1398
|
return supportsVenmoPopupsUtil(
|
|
1398
1399
|
props.experiment,
|
|
1399
1400
|
userAgentSupportsPopups(),
|
|
1400
|
-
props.userAgent
|
|
1401
|
+
props.userAgent
|
|
1401
1402
|
);
|
|
1402
1403
|
},
|
|
1403
1404
|
queryParam: true,
|
|
@@ -29,6 +29,7 @@ vi.mock("@paypal/sdk-client/src", async (importOriginal) => ({
|
|
|
29
29
|
error: vi.fn(),
|
|
30
30
|
})),
|
|
31
31
|
getPayPalDomainRegex: vi.fn(() => /paypal\.com/),
|
|
32
|
+
getPayPalDomain: vi.fn(() => "https://www.paypal.com"),
|
|
32
33
|
getEnv: vi.fn(() => "test"),
|
|
33
34
|
}));
|
|
34
35
|
|
|
@@ -43,7 +44,7 @@ describe("getButtonsComponent iframe title", () => {
|
|
|
43
44
|
const { attributes } = createMock.mock.calls[0][0];
|
|
44
45
|
|
|
45
46
|
expect(attributes({ props: {} }).iframe.title).toBe(
|
|
46
|
-
FUNDING_BRAND_LABEL.PAYPAL
|
|
47
|
+
FUNDING_BRAND_LABEL.PAYPAL
|
|
47
48
|
);
|
|
48
49
|
});
|
|
49
50
|
|
|
@@ -52,36 +53,43 @@ describe("getButtonsComponent iframe title", () => {
|
|
|
52
53
|
const { attributes } = createMock.mock.calls[0][0];
|
|
53
54
|
|
|
54
55
|
expect(attributes({ props: { fundingSource: "venmo" } }).iframe.title).toBe(
|
|
55
|
-
`${FUNDING_BRAND_LABEL.PAYPAL}-venmo
|
|
56
|
+
`${FUNDING_BRAND_LABEL.PAYPAL}-venmo`
|
|
56
57
|
);
|
|
57
58
|
});
|
|
58
59
|
});
|
|
59
60
|
|
|
60
|
-
describe("getButtonsComponent iframe
|
|
61
|
-
it("
|
|
61
|
+
describe("getButtonsComponent iframe payment permissions policy", () => {
|
|
62
|
+
it("delegates the payment permissions policy to the buttons iframe's actual origin", () => {
|
|
62
63
|
getButtonsComponent();
|
|
63
64
|
const { attributes } = createMock.mock.calls[0][0];
|
|
64
65
|
|
|
65
|
-
expect(attributes({ props: {} }).iframe.allow).
|
|
66
|
+
expect(attributes({ props: {} }).iframe.allow).toBe(
|
|
67
|
+
"payment https://www.paypal.com"
|
|
68
|
+
);
|
|
66
69
|
});
|
|
67
70
|
|
|
68
|
-
it("
|
|
71
|
+
it("keeps the legacy allowpaymentrequest attribute alongside the modern allow attribute", () => {
|
|
69
72
|
getButtonsComponent();
|
|
70
73
|
const { attributes } = createMock.mock.calls[0][0];
|
|
71
74
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
).toBeUndefined();
|
|
75
|
+
const iframeAttrs = attributes({ props: {} }).iframe;
|
|
76
|
+
expect(iframeAttrs.allowpaymentrequest).toBe("allowpaymentrequest");
|
|
77
|
+
expect(iframeAttrs.allow).toBe("payment https://www.paypal.com");
|
|
76
78
|
});
|
|
77
79
|
|
|
78
|
-
it("
|
|
80
|
+
it("names the origin explicitly rather than relying on the 'src' keyword", () => {
|
|
81
|
+
// zoid's openFrame() never sets this iframe's `src` attribute — it creates
|
|
82
|
+
// the iframe bare, then navigates it later via proxyWin.setLocation(), which
|
|
83
|
+
// uses window.location/form-POST, not the `src` attribute. Permissions
|
|
84
|
+
// Policy's 'src' keyword resolves by reading that attribute, so a bare
|
|
85
|
+
// `allow="payment"` (== `payment 'src'`) can never grant the feature here.
|
|
86
|
+
// Regression test for the retest failure after the original 'payment'-only
|
|
87
|
+
// fix (DTINAPPXO-5124).
|
|
79
88
|
getButtonsComponent();
|
|
80
89
|
const { attributes } = createMock.mock.calls[0][0];
|
|
81
90
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
).toBe("tools");
|
|
91
|
+
const iframeAttrs = attributes({ props: {} }).iframe;
|
|
92
|
+
expect(iframeAttrs.allow).not.toBe("payment");
|
|
93
|
+
expect(iframeAttrs.allow).toContain("https://www.paypal.com");
|
|
86
94
|
});
|
|
87
95
|
});
|
|
@@ -68,13 +68,13 @@ function spyOnWindowNameAssignment(): void {
|
|
|
68
68
|
new Error(
|
|
69
69
|
`window.name was overwritten: ${String(value).slice(
|
|
70
70
|
0,
|
|
71
|
-
WINDOW_NAME_LOG_TRUNCATION_LENGTH
|
|
72
|
-
)}
|
|
73
|
-
)
|
|
71
|
+
WINDOW_NAME_LOG_TRUNCATION_LENGTH
|
|
72
|
+
)}`
|
|
73
|
+
)
|
|
74
74
|
),
|
|
75
75
|
originalValue: String(currentWindowName).slice(
|
|
76
76
|
0,
|
|
77
|
-
WINDOW_NAME_LOG_TRUNCATION_LENGTH
|
|
77
|
+
WINDOW_NAME_LOG_TRUNCATION_LENGTH
|
|
78
78
|
),
|
|
79
79
|
});
|
|
80
80
|
currentWindowName = value;
|
|
@@ -97,6 +97,13 @@ export function getCheckoutComponent(): CheckoutComponent {
|
|
|
97
97
|
attributes: {
|
|
98
98
|
iframe: {
|
|
99
99
|
scrolling: "yes",
|
|
100
|
+
// Delegates the Permissions Policy 'payment' feature so PaymentRequest
|
|
101
|
+
// works when this iframe falls back to CONTEXT.IFRAME (supportsPopups() false).
|
|
102
|
+
// zoid never sets this iframe's `src` attribute (it navigates the frame
|
|
103
|
+
// via window.location/form-POST after creation instead), so the 'src'
|
|
104
|
+
// keyword that bare `allow="payment"` resolves to can never match the
|
|
105
|
+
// actual checkout origin. Name the origin explicitly instead.
|
|
106
|
+
allow: `payment ${getPayPalDomain()}`,
|
|
100
107
|
},
|
|
101
108
|
},
|
|
102
109
|
|
|
@@ -343,8 +350,8 @@ export function getCheckoutComponent(): CheckoutComponent {
|
|
|
343
350
|
value: ({ props }) => {
|
|
344
351
|
return Boolean(
|
|
345
352
|
props.onShippingChange ||
|
|
346
|
-
|
|
347
|
-
|
|
353
|
+
props.onShippingAddressChange ||
|
|
354
|
+
props.onShippingOptionsChange
|
|
348
355
|
);
|
|
349
356
|
},
|
|
350
357
|
},
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/* @flow */
|
|
2
|
+
|
|
3
|
+
import { describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { supportsPopups } from "@krakenjs/belter/src";
|
|
5
|
+
|
|
6
|
+
import { getCheckoutComponent } from "./component";
|
|
7
|
+
|
|
8
|
+
// Capture the config object component.jsx passes to zoid's create() so we can
|
|
9
|
+
// assert on the real (un-exported) attributes.iframe config, mirroring the
|
|
10
|
+
// pattern used in ../buttons/component.test.js.
|
|
11
|
+
const { createMock } = vi.hoisted(() => ({
|
|
12
|
+
createMock: vi.fn((options) => ({ ...options, isChild: () => false })),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
vi.mock("@krakenjs/zoid/src", () => ({
|
|
16
|
+
create: createMock,
|
|
17
|
+
CONTEXT: { POPUP: "popup", IFRAME: "iframe" },
|
|
18
|
+
EVENT: { FOCUS: "focus" },
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
vi.mock("@krakenjs/belter/src", async (importOriginal) => ({
|
|
22
|
+
...(await importOriginal()),
|
|
23
|
+
supportsPopups: vi.fn(() => true),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
vi.mock("@paypal/sdk-client/src", async (importOriginal) => ({
|
|
27
|
+
...(await importOriginal()),
|
|
28
|
+
getLogger: vi.fn(() => ({
|
|
29
|
+
metricCounter: vi.fn(),
|
|
30
|
+
track: vi.fn(),
|
|
31
|
+
info: vi.fn(),
|
|
32
|
+
error: vi.fn(),
|
|
33
|
+
})),
|
|
34
|
+
getPayPalDomainRegex: vi.fn(() => /paypal\.com/),
|
|
35
|
+
getPayPalDomain: vi.fn(() => "https://www.paypal.com"),
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
describe("getCheckoutComponent iframe payment permissions policy", () => {
|
|
39
|
+
it("delegates the payment permissions policy to the checkout iframe's actual origin", () => {
|
|
40
|
+
getCheckoutComponent();
|
|
41
|
+
const { attributes } = createMock.mock.calls[0][0];
|
|
42
|
+
|
|
43
|
+
expect(attributes.iframe.allow).toBe("payment https://www.paypal.com");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("keeps the existing scrolling attribute alongside the new allow attribute", () => {
|
|
47
|
+
getCheckoutComponent();
|
|
48
|
+
const { attributes } = createMock.mock.calls[0][0];
|
|
49
|
+
|
|
50
|
+
expect(attributes.iframe.scrolling).toBe("yes");
|
|
51
|
+
expect(attributes.iframe.allow).toBe("payment https://www.paypal.com");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("still delegates the payment permissions policy when the component falls back to CONTEXT.IFRAME", async () => {
|
|
55
|
+
// getCheckoutComponent() is inline-memoized, so the earlier tests' calls
|
|
56
|
+
// never re-invoke create(). Reset the module registry to get a fresh,
|
|
57
|
+
// un-memoized component that actually re-evaluates supportsPopups().
|
|
58
|
+
vi.mocked(supportsPopups).mockReturnValueOnce(false);
|
|
59
|
+
vi.resetModules();
|
|
60
|
+
const { getCheckoutComponent: getFreshCheckoutComponent } = await import(
|
|
61
|
+
"./component"
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
getFreshCheckoutComponent();
|
|
65
|
+
|
|
66
|
+
const { attributes } = createMock.mock.calls.at(-1)[0];
|
|
67
|
+
expect(attributes.iframe.allow).toBe("payment https://www.paypal.com");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("names the origin explicitly rather than relying on the 'src' keyword", () => {
|
|
71
|
+
// zoid's openFrame() never sets this iframe's `src` attribute — it creates
|
|
72
|
+
// the iframe bare, then navigates it later via proxyWin.setLocation(), which
|
|
73
|
+
// uses window.location/form-POST, not the `src` attribute. Permissions
|
|
74
|
+
// Policy's 'src' keyword resolves by reading that attribute, so a bare
|
|
75
|
+
// `allow="payment"` (== `payment 'src'`) can never grant the feature here.
|
|
76
|
+
// Regression test for the retest failure after the original 'payment'-only
|
|
77
|
+
// fix (DTINAPPXO-5124).
|
|
78
|
+
getCheckoutComponent();
|
|
79
|
+
const { attributes } = createMock.mock.calls[0][0];
|
|
80
|
+
|
|
81
|
+
expect(attributes.iframe.allow).not.toBe("payment");
|
|
82
|
+
expect(attributes.iframe.allow).toContain("https://www.paypal.com");
|
|
83
|
+
});
|
|
84
|
+
});
|