@paypal/checkout-components 5.0.386 → 5.0.388

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.
@@ -0,0 +1,282 @@
1
+ /* @flow */
2
+
3
+ import { vi, describe, it, expect, beforeEach, afterEach } from "vitest";
4
+ import {
5
+ isWebView,
6
+ isIosWebview,
7
+ isAndroidWebview,
8
+ isFacebookWebView,
9
+ isOperaMini,
10
+ isFirefoxIOS,
11
+ isEdgeIOS,
12
+ isQQBrowser,
13
+ isElectron,
14
+ supportsPopups,
15
+ isTablet,
16
+ isIos,
17
+ isSafari,
18
+ isAndroid,
19
+ isChrome,
20
+ isFirefox,
21
+ } from "@krakenjs/belter/src";
22
+
23
+ import { supportsVenmoPopups, isSupportedNativeVenmoBrowser } from "./util";
24
+
25
+ // Mock all the browser detection functions from belter
26
+ vi.mock("@krakenjs/belter/src", () => ({
27
+ isWebView: vi.fn(),
28
+ isIosWebview: vi.fn(),
29
+ isAndroidWebview: vi.fn(),
30
+ isFacebookWebView: vi.fn(),
31
+ isOperaMini: vi.fn(),
32
+ isFirefoxIOS: vi.fn(),
33
+ isEdgeIOS: vi.fn(),
34
+ isQQBrowser: vi.fn(),
35
+ isElectron: vi.fn(),
36
+ supportsPopups: vi.fn(),
37
+ isTablet: vi.fn(),
38
+ isIos: vi.fn(),
39
+ isSafari: vi.fn(),
40
+ isAndroid: vi.fn(),
41
+ isChrome: vi.fn(),
42
+ isFirefox: vi.fn(),
43
+ }));
44
+
45
+ describe("funding/util", () => {
46
+ const defaultUserAgent =
47
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1";
48
+
49
+ beforeEach(() => {
50
+ // Reset all mocks before each test
51
+ vi.clearAllMocks();
52
+
53
+ // Reset window.popupBridge
54
+ delete window.popupBridge;
55
+
56
+ // Set default mock return values
57
+ vi.mocked(isWebView).mockReturnValue(false);
58
+ vi.mocked(isIosWebview).mockReturnValue(false);
59
+ vi.mocked(isAndroidWebview).mockReturnValue(false);
60
+ vi.mocked(isFacebookWebView).mockReturnValue(false);
61
+ vi.mocked(isOperaMini).mockReturnValue(false);
62
+ vi.mocked(isFirefoxIOS).mockReturnValue(false);
63
+ vi.mocked(isEdgeIOS).mockReturnValue(false);
64
+ vi.mocked(isQQBrowser).mockReturnValue(false);
65
+ vi.mocked(isElectron).mockReturnValue(false);
66
+ vi.mocked(supportsPopups).mockReturnValue(true);
67
+ vi.mocked(isTablet).mockReturnValue(false);
68
+ vi.mocked(isIos).mockReturnValue(false);
69
+ vi.mocked(isSafari).mockReturnValue(false);
70
+ vi.mocked(isAndroid).mockReturnValue(false);
71
+ vi.mocked(isChrome).mockReturnValue(false);
72
+ vi.mocked(isFirefox).mockReturnValue(false);
73
+ });
74
+
75
+ afterEach(() => {
76
+ vi.resetAllMocks();
77
+ });
78
+
79
+ describe("supportsVenmoPopups", () => {
80
+ describe("when in supported webview", () => {
81
+ beforeEach(() => {
82
+ vi.mocked(isWebView).mockReturnValue(true);
83
+ });
84
+
85
+ it("should return true when popupBridge is available", () => {
86
+ window.popupBridge = {};
87
+
88
+ expect(supportsVenmoPopups({}, true, defaultUserAgent)).toBe(true);
89
+ });
90
+
91
+ it("should return false when popupBridge is not available", () => {
92
+ expect(supportsVenmoPopups({}, true, defaultUserAgent)).toBe(false);
93
+ });
94
+ });
95
+
96
+ describe("when not in supported webview", () => {
97
+ it("should return true when experiment flag is enabled and user agent supports popups", () => {
98
+ vi.mocked(supportsPopups).mockReturnValue(true);
99
+ const experiment = { venmoEnableWebOnNonNativeBrowser: true };
100
+
101
+ expect(supportsVenmoPopups(experiment, true, defaultUserAgent)).toBe(
102
+ true
103
+ );
104
+ });
105
+
106
+ it("should return false when experiment flag is enabled but user agent doesn't support popups due to isOperaMini", () => {
107
+ vi.mocked(isOperaMini).mockReturnValue(true);
108
+ const experiment = { venmoEnableWebOnNonNativeBrowser: true };
109
+
110
+ expect(supportsVenmoPopups(experiment, false, defaultUserAgent)).toBe(
111
+ false
112
+ );
113
+ });
114
+
115
+ it("should return false when experiment flag is enabled but user agent doesn't support popups due to isFirefoxIOS", () => {
116
+ vi.mocked(isFirefoxIOS).mockReturnValue(true);
117
+ const experiment = { venmoEnableWebOnNonNativeBrowser: true };
118
+
119
+ expect(supportsVenmoPopups(experiment, false, defaultUserAgent)).toBe(
120
+ false
121
+ );
122
+ });
123
+
124
+ it("should return false when experiment flag is enabled but user agent doesn't support popups due to isEdgeIOS", () => {
125
+ vi.mocked(isEdgeIOS).mockReturnValue(true);
126
+ const experiment = { venmoEnableWebOnNonNativeBrowser: true };
127
+
128
+ expect(supportsVenmoPopups(experiment, false, defaultUserAgent)).toBe(
129
+ false
130
+ );
131
+ });
132
+
133
+ it("should return false when experiment flag is enabled but user agent doesn't support popups due to isQQBrowser", () => {
134
+ vi.mocked(isQQBrowser).mockReturnValue(true);
135
+ const experiment = { venmoEnableWebOnNonNativeBrowser: true };
136
+
137
+ expect(supportsVenmoPopups(experiment, false, defaultUserAgent)).toBe(
138
+ false
139
+ );
140
+ });
141
+
142
+ it("should return false when experiment flag is enabled but user agent doesn't support popups due to isElectron", () => {
143
+ vi.mocked(isElectron).mockReturnValue(true);
144
+ const experiment = { venmoEnableWebOnNonNativeBrowser: true };
145
+
146
+ expect(supportsVenmoPopups(experiment, false, defaultUserAgent)).toBe(
147
+ false
148
+ );
149
+ });
150
+
151
+ it("should fall back to supportsPopups when experiment flag is not enabled", () => {
152
+ vi.mocked(supportsPopups).mockReturnValue(true);
153
+ const experiment = {};
154
+
155
+ expect(supportsVenmoPopups(experiment, true, defaultUserAgent)).toBe(
156
+ true
157
+ );
158
+ });
159
+
160
+ it("should fall back to supportsPopups when experiment flag is false", () => {
161
+ vi.mocked(supportsPopups).mockReturnValue(false);
162
+ const experiment = { venmoEnableWebOnNonNativeBrowser: false };
163
+
164
+ expect(supportsVenmoPopups(experiment, false, defaultUserAgent)).toBe(
165
+ false
166
+ );
167
+ });
168
+
169
+ it("should handle undefined experiment", () => {
170
+ vi.mocked(supportsPopups).mockReturnValue(true);
171
+
172
+ expect(supportsVenmoPopups(undefined, true, defaultUserAgent)).toBe(
173
+ true
174
+ );
175
+ });
176
+ });
177
+ });
178
+
179
+ describe("isSupportedNativeVenmoBrowser", () => {
180
+ describe("when in supported webview", () => {
181
+ beforeEach(() => {
182
+ vi.mocked(isWebView).mockReturnValue(true);
183
+ });
184
+
185
+ it("should return true when popupBridge is available", () => {
186
+ window.popupBridge = {};
187
+
188
+ expect(isSupportedNativeVenmoBrowser({}, defaultUserAgent)).toBe(true);
189
+ });
190
+
191
+ it("should return false when popupBridge is not available", () => {
192
+ expect(isSupportedNativeVenmoBrowser({}, defaultUserAgent)).toBe(false);
193
+ });
194
+ });
195
+
196
+ describe("when not in supported webview", () => {
197
+ it("should return false when on tablet", () => {
198
+ vi.mocked(isTablet).mockReturnValue(true);
199
+
200
+ expect(isSupportedNativeVenmoBrowser({}, defaultUserAgent)).toBe(false);
201
+ });
202
+
203
+ it("should return true for iOS Safari by default", () => {
204
+ vi.mocked(isIos).mockReturnValue(true);
205
+ vi.mocked(isSafari).mockReturnValue(true);
206
+
207
+ expect(isSupportedNativeVenmoBrowser({}, defaultUserAgent)).toBe(true);
208
+ });
209
+
210
+ it("should return true for Android Chrome by default", () => {
211
+ vi.mocked(isAndroid).mockReturnValue(true);
212
+ vi.mocked(isChrome).mockReturnValue(true);
213
+
214
+ expect(isSupportedNativeVenmoBrowser({}, defaultUserAgent)).toBe(true);
215
+ });
216
+
217
+ it("should return false for iOS Chrome without experiment flag", () => {
218
+ vi.mocked(isIos).mockReturnValue(true);
219
+ vi.mocked(isChrome).mockReturnValue(true);
220
+
221
+ expect(isSupportedNativeVenmoBrowser({}, defaultUserAgent)).toBe(false);
222
+ });
223
+
224
+ it("should return true for iOS Chrome with experiment flag", () => {
225
+ vi.mocked(isIos).mockReturnValue(true);
226
+ vi.mocked(isChrome).mockReturnValue(true);
227
+ const experiment = { venmoEnableWebOnNonNativeBrowser: true };
228
+
229
+ expect(
230
+ isSupportedNativeVenmoBrowser(experiment, defaultUserAgent)
231
+ ).toBe(true);
232
+ });
233
+
234
+ it("should return false for Android Firefox without experiment flag", () => {
235
+ vi.mocked(isAndroid).mockReturnValue(true);
236
+ vi.mocked(isFirefox).mockReturnValue(true);
237
+
238
+ expect(isSupportedNativeVenmoBrowser({}, defaultUserAgent)).toBe(false);
239
+ });
240
+
241
+ it("should return true for Android Firefox with experiment flag", () => {
242
+ vi.mocked(isAndroid).mockReturnValue(true);
243
+ vi.mocked(isFirefox).mockReturnValue(true);
244
+ const experiment = { venmoEnableWebOnNonNativeBrowser: true };
245
+
246
+ expect(
247
+ isSupportedNativeVenmoBrowser(experiment, defaultUserAgent)
248
+ ).toBe(true);
249
+ });
250
+
251
+ it("should return false for unsupported browser combinations", () => {
252
+ vi.mocked(isIos).mockReturnValue(true);
253
+ vi.mocked(isFirefox).mockReturnValue(true); // iOS Firefox (not Chrome)
254
+
255
+ expect(isSupportedNativeVenmoBrowser({}, defaultUserAgent)).toBe(false);
256
+ });
257
+
258
+ it("should return false for desktop browsers", () => {
259
+ // No mobile flags set, simulating desktop
260
+
261
+ expect(isSupportedNativeVenmoBrowser({}, defaultUserAgent)).toBe(false);
262
+ });
263
+
264
+ it("should handle undefined experiment", () => {
265
+ vi.mocked(isIos).mockReturnValue(true);
266
+ vi.mocked(isSafari).mockReturnValue(true);
267
+
268
+ expect(isSupportedNativeVenmoBrowser(undefined, defaultUserAgent)).toBe(
269
+ true
270
+ );
271
+ });
272
+
273
+ it("should prioritize tablet check over browser checks", () => {
274
+ vi.mocked(isTablet).mockReturnValue(true);
275
+ vi.mocked(isIos).mockReturnValue(true);
276
+ vi.mocked(isSafari).mockReturnValue(true);
277
+
278
+ expect(isSupportedNativeVenmoBrowser({}, defaultUserAgent)).toBe(false);
279
+ });
280
+ });
281
+ });
282
+ });
@@ -48,19 +48,18 @@ export function getVenmoConfig(): FundingSourceConfig {
48
48
  return true;
49
49
  },
50
50
 
51
- requires: ({ experiment, platform }) => {
52
- const isNonNativeSupported =
53
- experiment?.venmoEnableWebOnNonNativeBrowser === true ||
54
- (__WEB__ && window.popupBridge);
55
-
51
+ requires: ({ platform }) => {
56
52
  if (platform === PLATFORM.MOBILE) {
57
53
  return {
58
- native: isNonNativeSupported ? false : true,
59
- popup: isNonNativeSupported ? false : true,
54
+ native: true,
55
+ popup: true,
60
56
  };
61
57
  }
62
58
 
63
- return {};
59
+ return {
60
+ native: false,
61
+ popup: false,
62
+ };
64
63
  },
65
64
 
66
65
  Logo: ({ logoColor, optional, shouldApplyRebrandedStyles }) => {
@@ -8,6 +8,9 @@ import { BUTTON_FLOW } from "../../constants";
8
8
  import { getVenmoConfig } from "./config";
9
9
 
10
10
  describe("Venmo eligibility", () => {
11
+ window.navigator.mockUserAgent =
12
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1";
13
+
11
14
  const baseEligibilityProps = {
12
15
  fundingSource: undefined,
13
16
  components: ["buttons"],
@@ -102,58 +105,35 @@ describe("Venmo eligibility", () => {
102
105
  });
103
106
 
104
107
  describe("requires", () => {
105
- test("should not check for native or popup eligibility if platform is mobile and window.popupBridge is defined", () => {
106
- window.popupBridge = {};
107
-
108
- const isVenmoEligible = venmoConfig.requires?.({
109
- experiment: {
110
- venmoEnableWebOnNonNativeBrowser: true,
111
- },
108
+ test("should require native and popup support when platform is mobile", () => {
109
+ const venmoRequires = venmoConfig.requires?.({
112
110
  platform: PLATFORM.MOBILE,
113
111
  });
114
112
 
115
- expect(isVenmoEligible).toEqual({
116
- native: false,
117
- popup: false,
113
+ expect(venmoRequires).toEqual({
114
+ native: true,
115
+ popup: true,
118
116
  });
119
-
120
- window.popupBridge = undefined;
121
117
  });
122
118
 
123
- test("should not check for native or popup eligibility if platform is mobile and venmoEnableWebOnNonNativeBrowser is true", () => {
124
- const isVenmoEligible = venmoConfig.requires?.({
125
- experiment: {
126
- venmoEnableWebOnNonNativeBrowser: true,
127
- },
128
- platform: PLATFORM.MOBILE,
119
+ test("should not require native or popup support when platform is desktop", () => {
120
+ const venmoRequires = venmoConfig.requires?.({
121
+ platform: PLATFORM.DESKTOP,
129
122
  });
130
123
 
131
- expect(isVenmoEligible).toEqual({
124
+ expect(venmoRequires).toEqual({
132
125
  native: false,
133
126
  popup: false,
134
127
  });
135
128
  });
136
129
 
137
- test("should check for native and popup eligibility if platform is mobile and venmoEnableWebOnNonNativeBrowser is false and window.popupBridge is not defined", () => {
138
- const isVenmoEligible = venmoConfig.requires?.({
139
- experiment: {
140
- venmoEnableWebOnNonNativeBrowser: false,
141
- },
142
- platform: PLATFORM.MOBILE,
143
- });
130
+ test("should not require native or popup support when platform is not specified", () => {
131
+ const venmoRequires = venmoConfig.requires?.({});
144
132
 
145
- expect(isVenmoEligible).toEqual({
146
- native: true,
147
- popup: true,
148
- });
149
- });
150
-
151
- test("should not check for native and popup eligibility if platform is not mobile", () => {
152
- const isVenmoEligible = venmoConfig.requires?.({
153
- platform: PLATFORM.DESKTOP,
133
+ expect(venmoRequires).toEqual({
134
+ native: false,
135
+ popup: false,
154
136
  });
155
-
156
- expect(isVenmoEligible).toEqual({});
157
137
  });
158
138
  });
159
139
  });
@@ -77,6 +77,7 @@ export type HostedButtonStyles = {|
77
77
  label: string,
78
78
  height: ?number,
79
79
  tagline: boolean,
80
+ shouldApplyRebrandedStyles?: boolean,
80
81
  |};
81
82
 
82
83
  export type HostedButtonOptions = {|
@@ -6,6 +6,7 @@ import {
6
6
  buildDPoPHeaders,
7
7
  getSDKHost,
8
8
  getClientID,
9
+ getFirstRenderExperiments,
9
10
  getLocale,
10
11
  getMerchantID as getSDKMerchantID,
11
12
  } from "@paypal/sdk-client/src";
@@ -14,6 +15,7 @@ import { SUPPORTED_FUNDING_SOURCES } from "@paypal/funding-components/src";
14
15
  import { ZalgoPromise } from "@krakenjs/zalgo-promise/src";
15
16
 
16
17
  import { getButtonsComponent, type ButtonsComponent } from "../zoid/buttons";
18
+ import type { Experiment as EligibilityExperiment } from "../types";
17
19
 
18
20
  import type {
19
21
  ButtonVariables,
@@ -155,6 +157,12 @@ export const getButtonPreferences = ({
155
157
  };
156
158
  };
157
159
 
160
+ function getButtonExperiments(): EligibilityExperiment {
161
+ return {
162
+ ...getFirstRenderExperiments(),
163
+ };
164
+ }
165
+
158
166
  const getButtonVariable = (variables: ButtonVariables, key: string): string =>
159
167
  variables?.find((variable) => variable.name === key)?.value ?? "";
160
168
 
@@ -185,6 +193,8 @@ export const getHostedButtonDetails: HostedButtonDetailsParams = async ({
185
193
  getButtonVariable(variables, "tax_rate_preference") ===
186
194
  "tax_rate_from_profile";
187
195
 
196
+ const { isPaypalRebrandEnabled } = getButtonExperiments();
197
+
188
198
  return {
189
199
  style: {
190
200
  layout: getButtonVariable(variables, "layout"),
@@ -193,6 +203,7 @@ export const getHostedButtonDetails: HostedButtonDetailsParams = async ({
193
203
  label: getButtonVariable(variables, "button_text"),
194
204
  tagline: getButtonVariable(variables, "tagline") === "true",
195
205
  height: parseInt(getButtonVariable(variables, "height"), 10) || undefined,
206
+ shouldApplyRebrandedStyles: isPaypalRebrandEnabled,
196
207
  },
197
208
  enableDPoP: getButtonVariable(variables, "enable_dpop") === "true",
198
209
  shouldIncludeShippingCallbacks: shippingFromProfile || taxRateFromProfile,
@@ -445,7 +456,8 @@ export const getFlexDirection = ({
445
456
 
446
457
  export const getButtonColor = (
447
458
  color: Color,
448
- fundingSource: FundingSources
459
+ fundingSource: FundingSources,
460
+ shouldApplyRebrandedStyles?: boolean
449
461
  ): Color => {
450
462
  const colorMap = {
451
463
  gold: {
@@ -455,7 +467,7 @@ export const getButtonColor = (
455
467
  },
456
468
  blue: {
457
469
  paypal: "blue",
458
- venmo: "silver",
470
+ venmo: shouldApplyRebrandedStyles ? "blue" : "silver",
459
471
  paylater: "blue",
460
472
  },
461
473
  black: {
@@ -470,7 +482,7 @@ export const getButtonColor = (
470
482
  },
471
483
  silver: {
472
484
  paypal: "silver",
473
- venmo: "blue",
485
+ venmo: shouldApplyRebrandedStyles ? "silver" : "blue",
474
486
  paylater: "silver",
475
487
  },
476
488
  };
@@ -525,7 +537,11 @@ export const getButtons = ({
525
537
  style: {
526
538
  ...style,
527
539
  // $FlowFixMe
528
- color: getButtonColor(style.color, fundingSource),
540
+ color: getButtonColor(
541
+ style.color,
542
+ fundingSource,
543
+ style.shouldApplyRebrandedStyles
544
+ ),
529
545
  },
530
546
  });
531
547
  };
@@ -2,7 +2,7 @@
2
2
  /* eslint-disable no-restricted-globals, promise/no-native, max-lines */
3
3
  import { test, expect, vi } from "vitest";
4
4
  import { request } from "@krakenjs/belter/src";
5
- import { getLogger } from "@paypal/sdk-client/src";
5
+ import { getFirstRenderExperiments, getLogger } from "@paypal/sdk-client/src";
6
6
 
7
7
  import { getButtonsComponent } from "../zoid/buttons";
8
8
 
@@ -38,6 +38,9 @@ vi.mock("@paypal/sdk-client/src", async () => {
38
38
  getClientID: () => "client_id_123",
39
39
  getMerchantID: () => ["merchant_id_123"],
40
40
  getLocale: () => ({ lang: "en", country: "US" }),
41
+ getFirstRenderExperiments: vi.fn(() => ({
42
+ isPaypalRebrandEnabled: false,
43
+ })),
41
44
  getLogger: vi.fn(() => ({
42
45
  error: vi.fn(),
43
46
  track: vi.fn().mockImplementation(() => ({
@@ -149,6 +152,7 @@ describe("getHostedButtonDetails", () => {
149
152
  color: "gold",
150
153
  label: "paypal",
151
154
  tagline: true,
155
+ shouldApplyRebrandedStyles: false,
152
156
  });
153
157
  });
154
158
  expect.assertions(1);
@@ -176,6 +180,42 @@ describe("getHostedButtonDetails", () => {
176
180
  expect.assertions(5);
177
181
  });
178
182
 
183
+ describe("Returns appropriate rebrand styles flag", () => {
184
+ test("should handle when rebrand experiment returns false", async () => {
185
+ // $FlowIssue
186
+ request.mockImplementationOnce(() =>
187
+ // eslint-disable-next-line compat/compat
188
+ Promise.resolve(getHostedButtonDetailsResponse.v2)
189
+ );
190
+ await getHostedButtonDetails({
191
+ hostedButtonId,
192
+ fundingSources: [],
193
+ }).then(({ style }) => {
194
+ expect(style.shouldApplyRebrandedStyles).toBe(false);
195
+ });
196
+ expect.assertions(1);
197
+ });
198
+
199
+ test("should handle when rebrand experiment returns true", async () => {
200
+ vi.mocked(getFirstRenderExperiments).mockReturnValueOnce({
201
+ isPaypalRebrandEnabled: true,
202
+ });
203
+
204
+ // $FlowIssue
205
+ request.mockImplementationOnce(() =>
206
+ // eslint-disable-next-line compat/compat
207
+ Promise.resolve(getHostedButtonDetailsResponse.v2)
208
+ );
209
+ await getHostedButtonDetails({
210
+ hostedButtonId,
211
+ fundingSources: [],
212
+ }).then(({ style }) => {
213
+ expect(style.shouldApplyRebrandedStyles).toBe(true);
214
+ });
215
+ expect.assertions(1);
216
+ });
217
+ });
218
+
179
219
  test("handles false tagline values", async () => {
180
220
  // $FlowIssue
181
221
  request.mockImplementationOnce(() =>
@@ -778,10 +818,10 @@ test("getFlexDirection", () => {
778
818
  });
779
819
  });
780
820
 
781
- test("getButtonColor", () => {
821
+ describe("getButtonColor", () => {
782
822
  const colors = ["gold", "blue", "silver", "white", "black"];
783
823
  const fundingSources = ["paypal", "venmo", "paylater"];
784
- const colorMap = {
824
+ const colorMapLegacy = {
785
825
  gold: {
786
826
  paypal: "gold",
787
827
  venmo: "blue",
@@ -809,11 +849,51 @@ test("getButtonColor", () => {
809
849
  },
810
850
  };
811
851
 
812
- colors.forEach((color) => {
813
- fundingSources.forEach((fundingSource) => {
814
- expect(getButtonColor(color, fundingSource)).toBe(
815
- colorMap[color][fundingSource]
816
- );
852
+ const colorMapRebrand = {
853
+ gold: {
854
+ paypal: "gold",
855
+ venmo: "blue",
856
+ paylater: "gold",
857
+ },
858
+ blue: {
859
+ paypal: "blue",
860
+ venmo: "blue",
861
+ paylater: "blue",
862
+ },
863
+ black: {
864
+ paypal: "black",
865
+ venmo: "black",
866
+ paylater: "black",
867
+ },
868
+ white: {
869
+ paypal: "white",
870
+ venmo: "white",
871
+ paylater: "white",
872
+ },
873
+ silver: {
874
+ paypal: "silver",
875
+ venmo: "silver",
876
+ paylater: "silver",
877
+ },
878
+ };
879
+
880
+ test("legacy button colors", () => {
881
+ colors.forEach((color) => {
882
+ fundingSources.forEach((fundingSource) => {
883
+ expect(getButtonColor(color, fundingSource)).toBe(
884
+ colorMapLegacy[color][fundingSource]
885
+ );
886
+ });
887
+ });
888
+ });
889
+
890
+ test("rebrand button colors", () => {
891
+ colors.forEach((color) => {
892
+ fundingSources.forEach((fundingSource) => {
893
+ expect(getButtonColor(color, fundingSource, true)).toBe(
894
+ colorMapRebrand[color][fundingSource]
895
+ );
896
+ });
817
897
  });
818
898
  });
819
899
  });
@@ -9,6 +9,7 @@ import {
9
9
  memoize,
10
10
  isApplePaySupported,
11
11
  supportsPopups as userAgentSupportsPopups,
12
+ getUserAgent,
12
13
  } from "@krakenjs/belter/src";
13
14
  import {
14
15
  PLATFORM,
@@ -51,6 +52,7 @@ type MarksProps = {|
51
52
  onShippingAddressChange?: OnShippingAddressChange,
52
53
  onShippingOptionsChange?: OnShippingOptionsChange,
53
54
  displayOnly?: $ReadOnlyArray<$Values<typeof DISPLAY_ONLY_VALUES>>,
55
+ userAgent: string,
54
56
  |};
55
57
 
56
58
  export type MarksComponent = (MarksProps) => MarksInstance;
@@ -62,6 +64,7 @@ export const getMarksComponent: () => MarksComponent = memoize(() => {
62
64
  onShippingAddressChange,
63
65
  onShippingOptionsChange,
64
66
  displayOnly,
67
+ userAgent = getUserAgent(),
65
68
  }: MarksProps = {}): MarksInstance {
66
69
  const height = DEFAULT_HEIGHT;
67
70
  const fundingEligibility = getFundingEligibility();
@@ -99,6 +102,7 @@ export const getMarksComponent: () => MarksComponent = memoize(() => {
99
102
  supportedNativeBrowser,
100
103
  experiment,
101
104
  displayOnly,
105
+ userAgent,
102
106
  });
103
107
  const env = getEnv();
104
108
 
@@ -123,6 +127,7 @@ export const getMarksComponent: () => MarksComponent = memoize(() => {
123
127
  supportedNativeBrowser,
124
128
  experiment,
125
129
  displayOnly,
130
+ userAgent,
126
131
  });
127
132
  };
128
133
 
@@ -168,6 +168,7 @@ export function Buttons(props: ButtonsProps): ElementNode {
168
168
  userIDToken,
169
169
  vault,
170
170
  wallet,
171
+ userAgent,
171
172
  } = normalizeButtonProps(props);
172
173
  const { layout, shape, tagline } = style;
173
174
 
@@ -190,6 +191,7 @@ export function Buttons(props: ButtonsProps): ElementNode {
190
191
  supportedNativeBrowser,
191
192
  experiment,
192
193
  displayOnly,
194
+ userAgent,
193
195
  });
194
196
  const multiple = fundingSources.length > 1;
195
197