Skip to content

Commit cc7c222

Browse files
authored
Always make call to analytics setup endpoint (#2838)
* Always make call to analytics setup endpoint * Restoring issuerList e2e tests * Allowing card configData to be more than 32 keys in length
1 parent 5b7116c commit cc7c222

File tree

11 files changed

+99
-86
lines changed

11 files changed

+99
-86
lines changed

.changeset/stale-toys-smash.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@adyen/adyen-web': minor
3+
---
4+
5+
Always make call to analytics setup endpoint

packages/e2e-playwright/tests/issuerList/issuer-list.spec.ts

+31-29
Original file line numberDiff line numberDiff line change
@@ -38,42 +38,44 @@ test.describe('Issuer List', () => {
3838
await expect(issuerList.submitButton).toHaveText('Continue to Nest Bank');
3939
});
4040

41-
// test('it should have the expected data in state, ready for the /payments call', async ({ issuerListPage }) => {
42-
// const { issuerList, page } = issuerListPage;
41+
test('it should have the expected data in state, ready for the /payments call', async ({ issuerListPage }) => {
42+
const { issuerList, page } = issuerListPage;
43+
44+
// Open the drop down and select an item
45+
await issuerList.clickOnSelector();
46+
await pressKeyboardToNextItem(page); // Arrow down
47+
await pressKeyboardToSelectItem(page); // Enter key
4348

44-
// // Open the drop down and select an item
45-
// await issuerList.clickOnSelector();
46-
// await pressKeyboardToNextItem(page); // Arrow down
47-
// await pressKeyboardToSelectItem(page); // Enter key
49+
let issuerListData = await page.evaluate('window.dotpay.data');
4850

49-
// let issuerListData = await page.evaluate('window.dotpay.data');
51+
// @ts-ignore
52+
const { checkoutAttemptId, ...rest } = issuerListData.paymentMethod; // strip checkoutAttemptId since we can't know its value
5053

51-
// // @ts-ignore
52-
// expect(issuerListData.paymentMethod).toEqual({
53-
// type: 'dotpay',
54-
// issuer: '73',
55-
// checkoutAttemptId: 'do-not-track'
56-
// });
57-
// });
54+
expect(rest).toEqual({
55+
type: 'dotpay',
56+
issuer: '73'
57+
});
58+
});
5859

59-
// test('should select highlighted issuer, update pay button label, and see the expected data in state', async ({ issuerListPage }) => {
60-
// const { issuerList, page } = issuerListPage;
60+
test('should select highlighted issuer, update pay button label, and see the expected data in state', async ({ issuerListPage }) => {
61+
const { issuerList, page } = issuerListPage;
6162

62-
// await issuerList.selectHighlightedIssuer('BLIK');
63-
// await expect(issuerList.submitButton).toHaveText('Continue to BLIK');
63+
await issuerList.selectHighlightedIssuer('BLIK');
64+
await expect(issuerList.submitButton).toHaveText('Continue to BLIK');
6465

65-
// await issuerList.selectHighlightedIssuer('Idea Cloud');
66-
// await expect(issuerList.submitButton).toHaveText('Continue to Idea Cloud');
66+
await issuerList.selectHighlightedIssuer('Idea Cloud');
67+
await expect(issuerList.submitButton).toHaveText('Continue to Idea Cloud');
6768

68-
// await expect(issuerList.highlightedIssuerButtonGroup.getByRole('button', { pressed: true })).toHaveText('Idea Cloud');
69+
await expect(issuerList.highlightedIssuerButtonGroup.getByRole('button', { pressed: true })).toHaveText('Idea Cloud');
6970

70-
// let issuerListData = await page.evaluate('window.dotpay.data');
71+
let issuerListData = await page.evaluate('window.dotpay.data');
7172

72-
// // @ts-ignore
73-
// expect(issuerListData.paymentMethod).toEqual({
74-
// type: 'dotpay',
75-
// issuer: '81',
76-
// checkoutAttemptId: 'do-not-track'
77-
// });
78-
// });
73+
// @ts-ignore
74+
const { checkoutAttemptId, ...rest } = issuerListData.paymentMethod; // strip checkoutAttemptId since we can't know its value
75+
76+
expect(rest).toEqual({
77+
type: 'dotpay',
78+
issuer: '81'
79+
});
80+
});
7981
});

packages/lib/src/components/ApplePay/ApplePay.test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import ApplePay from './ApplePay';
22
import ApplePayService from './ApplePayService';
33
import { mock } from 'jest-mock-extended';
4+
import { NO_CHECKOUT_ATTEMPT_ID } from '../../core/Analytics/constants';
45

56
jest.mock('./ApplePayService');
67

@@ -366,7 +367,7 @@ describe('ApplePay', () => {
366367
},
367368
paymentMethod: {
368369
applePayToken: 'InBheW1lbnQtZGF0YSI=',
369-
checkoutAttemptId: 'do-not-track',
370+
checkoutAttemptId: NO_CHECKOUT_ATTEMPT_ID,
370371
type: 'applepay'
371372
}
372373
});

packages/lib/src/components/GooglePay/GooglePay.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import GooglePay from './GooglePay';
22
import GooglePayService from './GooglePayService';
33

44
import Analytics from '../../core/Analytics';
5-
import { ANALYTICS_EVENT_INFO, ANALYTICS_SELECTED_STR } from '../../core/Analytics/constants';
5+
import { ANALYTICS_EVENT_INFO, ANALYTICS_SELECTED_STR, NO_CHECKOUT_ATTEMPT_ID } from '../../core/Analytics/constants';
66

77
const analyticsModule = Analytics({ analytics: {}, loadingContext: '', locale: '', clientKey: '', bundleType: 'umd' });
88

@@ -95,7 +95,7 @@ describe('GooglePay', () => {
9595

9696
expect(state.data.origin).toBe('http://localhost');
9797
expect(state.data.paymentMethod).toStrictEqual({
98-
checkoutAttemptId: 'do-not-track',
98+
checkoutAttemptId: NO_CHECKOUT_ATTEMPT_ID,
9999
googlePayCardNetwork: 'VISA',
100100
googlePayToken: 'google-pay-token',
101101
type: 'googlepay'
@@ -161,7 +161,7 @@ describe('GooglePay', () => {
161161

162162
expect(state.data.origin).toBe('http://localhost');
163163
expect(state.data.paymentMethod).toStrictEqual({
164-
checkoutAttemptId: 'do-not-track',
164+
checkoutAttemptId: NO_CHECKOUT_ATTEMPT_ID,
165165
googlePayCardNetwork: 'VISA',
166166
googlePayToken: 'google-pay-token',
167167
type: 'googlepay'

packages/lib/src/components/PayPal/Paypal.test.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,37 @@
11
import Paypal from './Paypal';
22
import { render, screen } from '@testing-library/preact';
3+
import { NO_CHECKOUT_ATTEMPT_ID } from '../../core/Analytics/constants';
34

45
describe('Paypal', () => {
56
test('Returns a data object', () => {
67
const paypal = new Paypal(global.core);
78
expect(paypal.data).toEqual({
89
clientStateDataIndicator: true,
9-
paymentMethod: { subtype: 'sdk', type: 'paypal', userAction: 'pay', checkoutAttemptId: 'do-not-track' }
10+
paymentMethod: { subtype: 'sdk', type: 'paypal', userAction: 'pay', checkoutAttemptId: NO_CHECKOUT_ATTEMPT_ID }
1011
});
1112
});
1213

1314
test('should return subtype express if isExpress flag is set', () => {
1415
const paypal = new Paypal(global.core, { isExpress: true });
1516
expect(paypal.data).toEqual({
1617
clientStateDataIndicator: true,
17-
paymentMethod: { subtype: 'express', type: 'paypal', userAction: 'pay', checkoutAttemptId: 'do-not-track' }
18+
paymentMethod: { subtype: 'express', type: 'paypal', userAction: 'pay', checkoutAttemptId: NO_CHECKOUT_ATTEMPT_ID }
1819
});
1920
});
2021

2122
test('should return userAction=pay as default', () => {
2223
const paypal = new Paypal(global.core);
2324
expect(paypal.data).toEqual({
2425
clientStateDataIndicator: true,
25-
paymentMethod: { subtype: 'sdk', type: 'paypal', userAction: 'pay', checkoutAttemptId: 'do-not-track' }
26+
paymentMethod: { subtype: 'sdk', type: 'paypal', userAction: 'pay', checkoutAttemptId: NO_CHECKOUT_ATTEMPT_ID }
2627
});
2728
});
2829

2930
test('should return userAction=continue if set', () => {
3031
const paypal = new Paypal(global.core, { isExpress: true, userAction: 'continue' });
3132
expect(paypal.data).toEqual({
3233
clientStateDataIndicator: true,
33-
paymentMethod: { subtype: 'express', type: 'paypal', userAction: 'continue', checkoutAttemptId: 'do-not-track' }
34+
paymentMethod: { subtype: 'express', type: 'paypal', userAction: 'continue', checkoutAttemptId: NO_CHECKOUT_ATTEMPT_ID }
3435
});
3536
});
3637

packages/lib/src/components/Pix/Pix.test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import Pix from './Pix';
22
import { render, screen, waitFor } from '@testing-library/preact';
33
import userEvent from '@testing-library/user-event';
4+
import { NO_CHECKOUT_ATTEMPT_ID } from '../../core/Analytics/constants';
45

56
test('should return only payment type if personalDetails is not required', () => {
67
const pixElement = new Pix(global.core);
7-
expect(pixElement.data).toEqual({ clientStateDataIndicator: true, paymentMethod: { type: 'pix', checkoutAttemptId: 'do-not-track' } });
8+
expect(pixElement.data).toEqual({ clientStateDataIndicator: true, paymentMethod: { type: 'pix', checkoutAttemptId: NO_CHECKOUT_ATTEMPT_ID } });
89
});
910

1011
test('should show personal details form if enabled', async () => {

packages/lib/src/components/internal/BaseElement/BaseElement.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { ComponentChild, h, render } from 'preact';
22
import getProp from '../../../utils/getProp';
33
import uuid from '../../../utils/uuid';
44
import AdyenCheckoutError from '../../../core/Errors/AdyenCheckoutError';
5-
import { ANALYTICS_RENDERED_STR } from '../../../core/Analytics/constants';
5+
import { ANALYTICS_RENDERED_STR, NO_CHECKOUT_ATTEMPT_ID } from '../../../core/Analytics/constants';
66

77
import type { ICore } from '../../../core/types';
88
import type { BaseElementProps, IBaseElement } from './types';
@@ -94,8 +94,7 @@ abstract class BaseElement<P extends BaseElementProps> implements IBaseElement {
9494
*/
9595
public get data(): PaymentData {
9696
const clientData = getProp(this.props, 'modules.risk.data');
97-
const useAnalytics = !!getProp(this.props, 'modules.analytics.getEnabled')?.();
98-
const checkoutAttemptId = useAnalytics ? getProp(this.props, 'modules.analytics.getCheckoutAttemptId')?.() : 'do-not-track';
97+
const checkoutAttemptId = getProp(this.props, 'modules.analytics.getCheckoutAttemptId')?.() ?? NO_CHECKOUT_ATTEMPT_ID; // NOTE: we never expect to see this "failed" value, but, just in case...
9998
const order = this.state.order || this.props.order;
10099
const componentData = this.formatData();
101100

@@ -156,7 +155,7 @@ abstract class BaseElement<P extends BaseElementProps> implements IBaseElement {
156155
if (this.props.modules && this.props.modules.analytics) {
157156
this.setUpAnalytics({
158157
containerWidth: node && node.offsetWidth,
159-
component: !this.props.isDropin ? this.constructor['analyticsType'] ?? this.constructor['type'] : 'dropin',
158+
component: !this.props.isDropin ? (this.constructor['analyticsType'] ?? this.constructor['type']) : 'dropin',
160159
flavor: !this.props.isDropin ? 'components' : 'dropin'
161160
}).then(() => {
162161
// Once the initial analytics set up call has been made...

packages/lib/src/core/Analytics/Analytics.test.ts

+41-29
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@ const analyticsEventObj = {
2727
target: 'PAN input'
2828
} as CreateAnalyticsObject;
2929

30+
const applicationInfo = {
31+
merchantApplication: {
32+
name: 'merchant_application_name',
33+
version: 'version'
34+
},
35+
externalPlatform: {
36+
name: 'external_platform_name',
37+
version: 'external_platform_version',
38+
integrator: 'getSystemIntegratorName'
39+
}
40+
};
41+
3042
let analytics;
3143

3244
describe('Analytics initialisation and event queue', () => {
@@ -37,7 +49,14 @@ describe('Analytics initialisation and event queue', () => {
3749
mockedCollectId.mockImplementation(() => collectIdPromiseMock);
3850
collectIdPromiseMock.mockClear();
3951

40-
analytics = Analytics({ analytics: {}, loadingContext: '', locale: '', clientKey: '', amount, bundleType: '' });
52+
analytics = Analytics({
53+
analytics: { payload: { payloadData: 'test' } },
54+
loadingContext: '',
55+
locale: '',
56+
clientKey: '',
57+
amount,
58+
bundleType: ''
59+
});
4160
});
4261

4362
test('Creates an Analytics module with defaultProps', () => {
@@ -50,33 +69,15 @@ describe('Analytics initialisation and event queue', () => {
5069
expect(collectIdPromiseMock).toHaveLength(0);
5170
});
5271

53-
test('Should not fire any calls if analytics is disabled', () => {
54-
const analytics = Analytics({ analytics: { enabled: false }, loadingContext: '', locale: '', clientKey: '', amount, bundleType: '' });
55-
56-
void analytics.setUp(setUpEvent);
57-
expect(collectIdPromiseMock).not.toHaveBeenCalled();
58-
});
59-
60-
test('Calls the collectId endpoint by default, adding expected fields, including sanitising the passed analyticsData object', async () => {
61-
const applicationInfo = {
62-
merchantApplication: {
63-
name: 'merchant_application_name',
64-
version: 'version'
65-
},
66-
externalPlatform: {
67-
name: 'external_platform_name',
68-
version: 'external_platform_version',
69-
integrator: 'getSystemIntegratorName'
70-
}
71-
};
72-
72+
test('Should still make the setup call even when analytics is disabled, adding expected fields, including sanitising the passed analyticsData object', async () => {
7373
const checkoutAttemptId = 'my.attempt.id';
7474

7575
analytics = Analytics({
7676
analytics: {
77+
enabled: false,
7778
analyticsData: {
7879
applicationInfo,
79-
checkoutAttemptId,
80+
checkoutAttemptId, // checking that we can also pass in a checkoutAttemptId
8081
// @ts-ignore - this is one of the things we're testing (that this object gets stripped out)
8182
foo: {
8283
bar: 'val'
@@ -86,6 +87,7 @@ describe('Analytics initialisation and event queue', () => {
8687
loadingContext: '',
8788
locale: '',
8889
clientKey: '',
90+
bundleType: '',
8991
amount
9092
});
9193

@@ -101,18 +103,28 @@ describe('Analytics initialisation and event queue', () => {
101103
expect(analytics.getCheckoutAttemptId()).toEqual(mockCheckoutAttemptId);
102104
});
103105

104-
test('A second attempt to call "send" should fail (since we already have a checkoutAttemptId)', () => {
105-
const payload = {
106-
payloadData: 'test'
107-
};
108-
const analytics = Analytics({ analytics: { payload }, loadingContext: '', locale: '', clientKey: '', amount, bundleType: '' });
109-
106+
test('A second attempt to call "send" should fail (since we have retrieved a checkoutAttemptId)', () => {
110107
void analytics.setUp(setUpEvent);
111108

112109
expect(collectIdPromiseMock).toHaveLength(0);
113110
});
114111

115-
test('Create info event and see that it is held in a queue', () => {
112+
test('Try to create info event but see that it fails because analytics.enabled is false', () => {
113+
const analytics = Analytics({
114+
analytics: { enabled: false },
115+
loadingContext: '',
116+
locale: '',
117+
clientKey: '',
118+
amount,
119+
bundleType: ''
120+
});
121+
122+
const aObj: AnalyticsObject = analytics.createAnalyticsEvent({ event: 'info', data: analyticsEventObj });
123+
124+
expect(aObj).toBe(undefined);
125+
});
126+
127+
test('With Analytics being enabled by default, create info event and see that it is held in a queue', () => {
116128
const aObj: AnalyticsObject = analytics.createAnalyticsEvent({ event: 'info', data: analyticsEventObj });
117129

118130
expect(aObj.timestamp).not.toBe(undefined);

packages/lib/src/core/Analytics/Analytics.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@ const Analytics = ({ locale, clientKey, analytics, amount, analyticsContext, bun
6262
* @param initialEvent -
6363
*/
6464
setUp: async (initialEvent: AnalyticsInitialEvent) => {
65-
const { enabled, payload } = props; // TODO what is payload, is it ever used?
65+
const { payload } = props; // TODO what is payload, is it ever used?
6666

6767
const analyticsData = processAnalyticsData(props.analyticsData);
6868

69-
if (enabled === true && !capturedCheckoutAttemptId) {
69+
if (!capturedCheckoutAttemptId) {
7070
try {
7171
const checkoutAttemptId = await collectId({
7272
...initialEvent,
@@ -86,6 +86,8 @@ const Analytics = ({ locale, clientKey, analytics, amount, analyticsContext, bun
8686
getEventsQueue: () => eventsQueue,
8787

8888
createAnalyticsEvent: ({ event, data }: CreateAnalyticsEventObject): AnalyticsObject => {
89+
if (!props.enabled) return;
90+
8991
const aObj: AnalyticsObject = createAnalyticsObject({
9092
event,
9193
...data
@@ -102,7 +104,7 @@ const Analytics = ({ locale, clientKey, analytics, amount, analyticsContext, bun
102104
sendAnalytics: null
103105
};
104106

105-
anlModule.sendAnalytics = analyticsPreProcessor(anlModule);
107+
anlModule.sendAnalytics = props.enabled === true ? analyticsPreProcessor(anlModule) : () => {};
106108

107109
return anlModule;
108110
};

packages/lib/src/core/Analytics/constants.ts

+2
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,5 @@ export const errorCodeMapping: Record<string, string> = {
9999
export const ANALYTICS_EXPRESS_PAGES_ARRAY = ['cart', 'minicart', 'pdp', 'checkout'];
100100

101101
export const ALLOWED_ANALYTICS_DATA = ['applicationInfo', 'checkoutAttemptId'];
102+
103+
export const NO_CHECKOUT_ATTEMPT_ID = 'fetch-checkoutAttemptId-failed';

packages/lib/src/core/Analytics/utils.ts

-12
Original file line numberDiff line numberDiff line change
@@ -194,17 +194,5 @@ export const getCardConfigData = (cardProps: CardConfiguration): CardConfigData
194194
hasOnLoad: onLoad !== CardInputDefaultProps.onLoad
195195
};
196196

197-
// TODO - keep until endpoint can accept more entries in the configData object (current limit: 32);
198-
if (Object.keys(configData).length > 32) {
199-
const strippedConfigData = Object.entries(configData).reduce((acc, [key, value], index) => {
200-
if (index < 32) {
201-
acc[key] = value;
202-
}
203-
return acc;
204-
}, {});
205-
206-
return strippedConfigData as CardConfigData;
207-
}
208-
209197
return configData;
210198
};

0 commit comments

Comments
 (0)