1 import { PAYMENT_TOKEN_STATUS } from './constants';
2 import { type EnsureTokenChargeableTranslations, ensureTokenChargeable } from './ensureTokenChargeable';
4 let tab: { closed: boolean; close: () => any };
6 const translations: EnsureTokenChargeableTranslations = {
7 processAbortedError: 'Process aborted',
8 paymentProcessCanceledError: 'Payment process canceled',
9 paymentProcessFailedError: 'Payment process failed',
10 paymentProcessConsumedError: 'Payment process consumed',
11 paymentProcessNotSupportedError: 'Payment process not supported',
12 unknownPaymentTokenStatusError: 'Unknown payment token status',
13 tabClosedError: 'Tab closed',
16 describe('process', () => {
17 let ApprovalURL = 'https://example.proton.me';
18 let ReturnHost = 'https://return.proton.me';
19 let Token = 'some-payment-token-222';
21 let signal: AbortSignal;
25 tab = { closed: false, close: jest.fn() };
26 jest.spyOn(window, 'open').mockReturnValue(tab as any);
27 jest.spyOn(window, 'removeEventListener');
28 jest.spyOn(window, 'addEventListener');
36 throwIfAborted: jest.fn(),
37 addEventListener: jest.fn(),
38 removeEventListener: jest.fn(),
39 dispatchEvent: jest.fn(),
43 it('should open the ApprovalURL', () => {
44 tab.closed = true; // prevents the test from hanging because of an unhandled promise
45 ensureTokenChargeable({ api, ApprovalURL, ReturnHost, signal, Token }, translations).catch(() => {});
47 expect(window.open).toHaveBeenCalledWith(ApprovalURL);
50 it('should add abort listener to the signal', async () => {
51 const promise = ensureTokenChargeable({ api, ApprovalURL, ReturnHost, signal, Token }, translations);
53 expect(signal.addEventListener).toHaveBeenCalledTimes(1);
54 const signalAddEventListenerMock = signal.addEventListener as jest.Mock;
55 expect(signalAddEventListenerMock.mock.lastCall[0]).toEqual('abort');
56 expect(typeof signalAddEventListenerMock.mock.lastCall[1]).toBe('function');
58 expect(window.addEventListener).toHaveBeenCalledTimes(1);
59 const windowAddEventListenerMock = window.addEventListener as jest.Mock;
60 expect(windowAddEventListenerMock.mock.lastCall[0]).toEqual('message');
61 expect(typeof windowAddEventListenerMock.mock.lastCall[1]).toEqual('function');
62 const onMessage = windowAddEventListenerMock.mock.lastCall[1];
64 const abort = signalAddEventListenerMock.mock.lastCall[1];
68 expect(window.removeEventListener).toHaveBeenCalledWith('message', onMessage, false);
69 expect(signal.removeEventListener).toHaveBeenCalledWith('abort', abort);
70 expect(tab.close).toHaveBeenCalled();
71 await expect(promise).rejects.toEqual(new Error('Process aborted'));
74 it('should resolve if Status is STATUS_CHARGEABLE', async () => {
76 api.mockResolvedValue({
77 Status: PAYMENT_TOKEN_STATUS.STATUS_CHARGEABLE,
80 const promise = ensureTokenChargeable({ api, ApprovalURL, ReturnHost, signal, Token }, translations);
82 await expect(promise).resolves.toEqual(undefined);
86 PAYMENT_TOKEN_STATUS.STATUS_PENDING,
87 PAYMENT_TOKEN_STATUS.STATUS_FAILED,
88 PAYMENT_TOKEN_STATUS.STATUS_CONSUMED,
89 PAYMENT_TOKEN_STATUS.STATUS_NOT_SUPPORTED,
90 ])('should reject if Status is %s', async (Status) => {
92 api.mockResolvedValue({
96 const promise = ensureTokenChargeable({ api, ApprovalURL, ReturnHost, signal, Token }, translations);
98 await expect(promise).rejects.toEqual({ tryAgain: true });
101 it('should re-try to confirm until the tab is closed', async () => {
102 api.mockResolvedValue({
103 Status: PAYMENT_TOKEN_STATUS.STATUS_CHARGEABLE,
106 const delayListening = 10;
107 const promise = ensureTokenChargeable(
108 { api, ApprovalURL, ReturnHost, signal, Token },
116 await expect(promise).resolves.toEqual(undefined);