Skip to content

Commit

Permalink
test(UIElement): added tests for sending error event to the analytics
Browse files Browse the repository at this point in the history
  • Loading branch information
longyulongyu committed Dec 19, 2024
1 parent 24daf37 commit f83dd0f
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 8 deletions.
50 changes: 50 additions & 0 deletions packages/lib/src/components/ANCV/ANCV.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { render } from '@testing-library/preact';
import { mockDeep } from 'jest-mock-extended';
import { AnalyticsModule } from '../../types/global-types';
import AdyenCheckoutError from '../../core/Errors/AdyenCheckoutError';
import { ANALYTICS_ERROR_TYPE, ANALYTICS_EVENT } from '../../core/Analytics/constants';
import ANCV from './ANCV';

const flushPromises = () => new Promise(process.nextTick);

describe('ANCV', () => {
const resources = global.resources;
const i18n = global.i18n;

const baseProps = {
amount: { value: 1000, currency: 'EUR' },
i18n,
loadingContext: 'mock'
};

describe('createOrder', () => {
test('should send an error event to the analytics if the createOrder call fails for the session flow', async () => {
const code = 'mockErrorCode';
const analytics = mockDeep<AnalyticsModule>();
const mockedSendAnalytics = analytics.sendAnalytics as jest.Mock;

const ancv = new ANCV(global.core, {
...baseProps,
modules: {
resources,
analytics
},
onError: () => {},
// @ts-ignore test only
session: {
createOrder: () => {
return Promise.reject(new AdyenCheckoutError('NETWORK_ERROR', '', { code }));
}
}
});
render(ancv.render());
await ancv.createOrder();
await flushPromises();
expect(mockedSendAnalytics).toHaveBeenCalledWith(
'ancv',
{ code, errorType: ANALYTICS_ERROR_TYPE.apiError, type: ANALYTICS_EVENT.error },
undefined
);
});
});
});
8 changes: 7 additions & 1 deletion packages/lib/src/components/ANCV/ANCV.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,13 @@ export class ANCVElement extends UIElement<ANCVConfiguration> {
})
.catch(error => {
this.setStatus(error?.message || 'error');
if (this.props.onError) this.handleError(new AdyenCheckoutError('ERROR', error));
if (this.props.onError) {
if (error instanceof AdyenCheckoutError) {
this.handleError(error);
} else {
this.handleError(new AdyenCheckoutError('ERROR', error));
}
}
});
};

Expand Down
39 changes: 39 additions & 0 deletions packages/lib/src/components/Giftcard/Giftcard.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import Giftcard from './Giftcard';
import { render, screen } from '@testing-library/preact';
import userEvent from '@testing-library/user-event';
import AdyenCheckoutError from '../../core/Errors/AdyenCheckoutError';
import { AnalyticsModule } from '../../types/global-types';
import { mockDeep } from 'jest-mock-extended';
import { ANALYTICS_ERROR_TYPE, ANALYTICS_EVENT } from '../../core/Analytics/constants';

const flushPromises = () => new Promise(process.nextTick);

Expand Down Expand Up @@ -135,6 +139,41 @@ describe('Giftcard', () => {
expect(onOrderRequest).toHaveBeenCalled();
});

test('should send an error event to the analytics if the createOrder call fails for the session flow', async () => {
const code = 'mockErrorCode';
const analytics = mockDeep<AnalyticsModule>();
const mockedSendAnalytics = analytics.sendAnalytics as jest.Mock;
const onBalanceCheck = jest.fn(resolve =>
resolve({
balance: { value: 500, currency: 'EUR' }
})
);
const giftcard = new Giftcard(global.core, {
...baseProps,
modules: {
resources,
analytics
},
onError: () => {},
onBalanceCheck,
// @ts-ignore test only
session: {
createOrder: () => {
return Promise.reject(new AdyenCheckoutError('NETWORK_ERROR', '', { code }));
}
}
});
render(giftcard.render());
giftcard.setState({ isValid: true });
giftcard.balanceCheck();
await flushPromises();
expect(mockedSendAnalytics).toHaveBeenCalledWith(
'giftcard',
{ code, errorType: ANALYTICS_ERROR_TYPE.apiError, type: ANALYTICS_EVENT.error },
undefined
);
});

test('if there is enough balance for checkout we should require confirmation', async () => {
const onBalanceCheck = jest.fn(resolve =>
resolve({
Expand Down
1 change: 0 additions & 1 deletion packages/lib/src/components/Giftcard/Giftcard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ export class GiftcardElement extends UIElement<GiftCardConfiguration> {
return new Promise((resolve, reject) => {
void this.props.onOrderRequest(resolve, reject, data);
});

if (this.props.session) {
return this.props.session.createOrder();
}
Expand Down
65 changes: 62 additions & 3 deletions packages/lib/src/components/internal/UIElement/UIElement.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { any, mock, mockDeep } from 'jest-mock-extended';
import { AdyenCheckout, ThreeDS2Challenge, ThreeDS2DeviceFingerprint } from '../../../index';
import { UIElementProps } from './types';
import { Resources } from '../../../core/Context/Resources';
import { PaymentActionsType } from '../../../types/global-types';
import { AnalyticsModule, PaymentActionsType } from '../../../types/global-types';
import AdyenCheckoutError from '../../../core/Errors/AdyenCheckoutError';
import { ANALYTICS_ERROR_TYPE, ANALYTICS_EVENT } from '../../../core/Analytics/constants';

jest.mock('../../../core/Services/get-translations');

Expand All @@ -23,6 +25,9 @@ class MyElement extends UIElement<MyElementProps> {
public callOnChange() {
super.onChange();
}
public handleAdditionalDetails(data) {
super.handleAdditionalDetails(data);
}
render() {
return '';
}
Expand All @@ -32,8 +37,15 @@ const submitMock = jest.fn();
(global as any).HTMLFormElement.prototype.submit = () => submitMock;

let core;
let analytics;
beforeEach(() => {
core = mockDeep<ICore>();
analytics = mockDeep<AnalyticsModule>();
});

afterEach(() => {
jest.clearAllMocks();
jest.resetAllMocks();
});

describe('UIElement', () => {
Expand Down Expand Up @@ -231,7 +243,7 @@ describe('UIElement', () => {
test('should trigger showValidation() and not call makePaymentsCall() if component is not valid', () => {
const showValidation = jest.fn();

const element = new MyElement(core);
const element = new MyElement(core, { modules: { analytics } });

// @ts-ignore Checking that internal method is not reached
const makePaymentsCallSpy = jest.spyOn(element, 'makePaymentsCall');
Expand Down Expand Up @@ -263,6 +275,7 @@ describe('UIElement', () => {
});

const element = new MyElement(core, {
modules: { analytics },
onSubmit: onSubmitMock,
onPaymentCompleted: onPaymentCompletedMock
});
Expand All @@ -275,7 +288,7 @@ describe('UIElement', () => {
expect(onPaymentCompletedMock).toHaveBeenCalledWith({ resultCode: 'Authorized' }, element);
});

test('should make successfull payment using sessions flow', async () => {
test('should make successful payment using sessions flow', async () => {
const onPaymentCompletedMock = jest.fn();

core.session.submitPayment.calledWith(any()).mockResolvedValue({
Expand All @@ -293,6 +306,7 @@ describe('UIElement', () => {
});

const element = new MyElement(core, {
modules: { analytics },
onPaymentCompleted: onPaymentCompletedMock
});

Expand Down Expand Up @@ -327,6 +341,7 @@ describe('UIElement', () => {
});

const element = new MyElement(core, {
modules: { analytics },
onSubmit: onSubmitMock,
onPaymentFailed: onPaymentFailedMock
});
Expand All @@ -347,6 +362,7 @@ describe('UIElement', () => {
jest.spyOn(MyElement.prototype, 'isValid', 'get').mockReturnValue(true);

const element = new MyElement(core, {
modules: { analytics },
onSubmit: onSubmitMock,
onPaymentFailed: onPaymentFailedMock
});
Expand Down Expand Up @@ -374,6 +390,7 @@ describe('UIElement', () => {
jest.spyOn(MyElement.prototype, 'isValid', 'get').mockReturnValue(true);

const element = new MyElement(core, {
modules: { analytics },
onSubmit: onSubmitMock
});

Expand Down Expand Up @@ -420,6 +437,7 @@ describe('UIElement', () => {
});

const element = new MyElement(core, {
modules: { analytics },
onOrderUpdated: onOrderUpdatedMock
});

Expand Down Expand Up @@ -469,6 +487,7 @@ describe('UIElement', () => {
core.session = null;

const element = new MyElement(core, {
modules: { analytics },
onSubmit: onSubmitMock,
onPaymentMethodsRequest: onPaymentMethodsRequestMock,
onOrderUpdated: onOrderUpdatedMock
Expand Down Expand Up @@ -531,6 +550,7 @@ describe('UIElement', () => {
core.session = null;

const element = new MyElement(core, {
modules: { analytics },
onSubmit: onSubmitMock,
onOrderUpdated: onOrderUpdatedMock,
onError: onErrorMock
Expand All @@ -551,6 +571,26 @@ describe('UIElement', () => {
expect(onOrderUpdatedMock).toHaveBeenCalledTimes(1);
expect(onOrderUpdatedMock).toHaveBeenCalledWith({ order });
});

test('should send an error event to analytic module with correct errorType and error code, if makePayment call fails', async () => {
const errorCode = 'mockedErrorCode';
const txVariant = 'scheme';

core.session.submitPayment.mockImplementation(() => Promise.reject(new AdyenCheckoutError('NETWORK_ERROR', '', { code: errorCode })));
const analytics = mock<AnalyticsModule>();
const mockedSendAnalytics = analytics.sendAnalytics as jest.Mock;
jest.spyOn(MyElement.prototype, 'isValid', 'get').mockReturnValue(true);

const element = new MyElement(core, { type: txVariant, modules: { analytics } });
element.submit();
await new Promise(process.nextTick);

expect(mockedSendAnalytics).toHaveBeenCalledWith(
txVariant,
{ code: errorCode, errorType: ANALYTICS_ERROR_TYPE.apiError, type: ANALYTICS_EVENT.error },
undefined
);
});
});

describe('[Internal] handleAdditionalDetails()', () => {
Expand Down Expand Up @@ -684,5 +724,24 @@ describe('UIElement', () => {
expect(onPaymentFailedMock).toHaveBeenCalledTimes(1);
expect(onPaymentFailedMock).toHaveBeenCalledWith(undefined, element);
});

test('should send an error event to analytic module with correct errorType and error code, if payment/details call fails', async () => {
const errorCode = 'mockedErrorCode';
const txVariant = 'scheme';

core.session.submitDetails.mockImplementation(() => Promise.reject(new AdyenCheckoutError('NETWORK_ERROR', '', { code: errorCode })));

const mockedSendAnalytics = analytics.sendAnalytics as jest.Mock;

const element = new MyElement(core, { type: txVariant, modules: { analytics } });
element.handleAdditionalDetails({});
await new Promise(process.nextTick);

expect(mockedSendAnalytics).toHaveBeenCalledWith(
txVariant,
{ code: errorCode, errorType: ANALYTICS_ERROR_TYPE.apiError, type: ANALYTICS_EVENT.error },
undefined
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export abstract class UIElement<P extends UIElementProps = UIElementProps> exten
if (this.constructor['type'] === 'scheme' || this.constructor['type'] === 'bcmc') {
return this.constructor['type'];
}
return this.props.type;
return this.type;
}

public submit(): void {
Expand Down
3 changes: 1 addition & 2 deletions packages/lib/src/styles/variable-generator.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,13 @@
@return $adyen-output-map;
}


@function token($token, $generate-css-var: true) {
$adyen-tokens-map: ();

@if $generate-css-var {
$adyen-tokens-map: adyen-sdk-generate-css-variables($color, $text, $focus-ring, $border, $spacer, $shadow);
} @else {
$adyen-tokens-map: map-merge($color, $text, $focus-ring, $border, $spacer, $shadow)
$adyen-tokens-map: map-merge($color, $text, $focus-ring, $border, $spacer, $shadow);
}

@return map-get($adyen-tokens-map, '#{$token}');
Expand Down

0 comments on commit f83dd0f

Please sign in to comment.