Skip to content

Commit 48ed5aa

Browse files
adds prop update listening to modal browser zoid polyfill
1 parent 713635e commit 48ed5aa

File tree

5 files changed

+194
-78
lines changed

5 files changed

+194
-78
lines changed

.eslintrc

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"extends": ["airbnb", "prettier"],
2+
"extends": ["airbnb", "prettier", "plugin:import/typescript"],
33
"parser": "@babel/eslint-parser",
44
"env": {
55
"browser": true,
@@ -39,7 +39,8 @@
3939
"server": "./server",
4040
"utils": "./utils"
4141
}
42-
}
42+
},
43+
"typescript": {}
4344
}
4445
}
4546
}

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"@paypal/sdk-client": "^4.0.166",
5353
"@paypal/sdk-constants": "^1.0.118",
5454
"@paypal/sdk-logos": "^2.0.0",
55+
"@paypalcorp/web-sdk-postmessenger": "^0.2.6",
5556
"core-js-pure": "3.31.1"
5657
},
5758
"devDependencies": {
@@ -81,6 +82,7 @@
8182
"eslint-config-prettier": "^8.5.0",
8283
"eslint-import-resolver-babel-module": "^5.3.1",
8384
"eslint-import-resolver-jest": "^3.0.2",
85+
"eslint-import-resolver-typescript": "^3.7.0",
8486
"eslint-plugin-import": "2.25.4",
8587
"eslint-plugin-jsx-a11y": "^6.5.1",
8688
"eslint-plugin-prettier": "^4.2.1",

src/components/modal/v2/lib/zoid-polyfill.js

+31-2
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,44 @@
11
/* global Android */
22
import { isAndroidWebview, isIosWebview, getPerformance } from '@krakenjs/belter/src';
3+
import PostMessenger from '@paypalcorp/web-sdk-postmessenger/src';
34
import { getOrCreateDeviceID, logger } from '../../../../utils';
45
import { isIframe } from './utils';
56

67
const IOS_INTERFACE_NAME = 'paypalMessageModalCallbackHandler';
78
const ANDROID_INTERFACE_NAME = 'paypalMessageModalCallbackHandler';
89

10+
const channelConfigObj = {
11+
// where to send or recieve the messages
12+
target: window, // TODO: determine window or window.top?
13+
// restricted list of destinations to send or recieve from
14+
allowedOrigins: [], // string[]
15+
// dump debug information
16+
debug: console.log.bind(console), // just a simple place to log info,
17+
// time to wait for message ack
18+
timeout: 2000,
19+
// number of times to retry failed messages
20+
retries: 3
21+
};
22+
923
const setupBrowser = props => {
24+
// setup communication layer with v6 modal wrapper
25+
const channel = new PostMessenger(channelConfigObj);
26+
27+
const propListeners = new Set();
28+
29+
channel.subscribe('PROPS_UPDATE', newProps => {
30+
if (newProps && typeof newProps === 'object') {
31+
Array.from(propListeners.values()).forEach(listener => {
32+
listener({ ...window.xprops, ...newProps });
33+
});
34+
35+
Object.assign(window.xprops, newProps);
36+
}
37+
console.log('Reciever got PROPS_UPDATE data =', newProps);
38+
});
39+
1040
window.xprops = {
11-
// We will never recieve new props via this integration style
12-
onProps: () => {},
41+
onProps: listener => propListeners.add(listener),
1342
// TODO: Verify these callbacks are instrumented correctly
1443
onReady: ({ products, meta }) => {
1544
const { clientId, payerId, merchantId, offer, partnerAttributionId } = props;

tests/unit/spec/src/components/modal/v2/lib/hooks/currency.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { currencyFormat } from 'src/components/modal/v2/lib/';
1+
import { currencyFormat } from 'src/components/modal/v2/lib/hooks/currency';
22

33
describe('currency format', () => {
44
test('array test', () => {

tests/unit/spec/src/components/modal/v2/lib/zoid-polyfill.test.js

+157-73
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { subscribeMockFn } from '@paypalcorp/web-sdk-postmessenger/src';
12
import zoidPolyfill from 'src/components/modal/v2/lib/zoid-polyfill';
23
import { logger } from 'src/utils';
34

@@ -25,6 +26,16 @@ jest.mock('@krakenjs/belter/src', () => {
2526
})
2627
};
2728
});
29+
jest.mock('@paypalcorp/web-sdk-postmessenger/src', () => {
30+
const subscribeMock = jest.fn();
31+
return {
32+
__esModule: true,
33+
subscribeMockFn: subscribeMock,
34+
default: jest.fn().mockImplementation(() => ({
35+
subscribe: subscribeMock
36+
}))
37+
};
38+
});
2839
jest.mock('src/components/modal/v2/lib/utils', () => ({
2940
isIframe: true
3041
}));
@@ -73,7 +84,7 @@ describe('zoidPollyfill', () => {
7384
describe('sets up xprops for browser', () => {
7485
beforeAll(() => {
7586
mockLoadUrl(
76-
'https://localhost.paypal.com:8080/credit-presentment/native/message?client_id=client_1&logo_type=inline&amount=500&devTouchpoint=true'
87+
'https://localhost.paypal.com:8080/credit-presentment/lander/modal?client_id=client_1&logo_type=inline&amount=500&devTouchpoint=true'
7788
);
7889

7990
zoidPolyfill();
@@ -177,7 +188,7 @@ describe('zoidPollyfill', () => {
177188

178189
test('sets up xprops for webview', () => {
179190
mockLoadUrl(
180-
'https://localhost.paypal.com:8080/credit-presentment/native/message?client_id=client_1&logo_type=inline&amount=500&dev_touchpoint=true',
191+
'https://localhost.paypal.com:8080/credit-presentment/native/modal?client_id=client_1&logo_type=inline&amount=500&dev_touchpoint=true',
181192
{
182193
platform: 'ios'
183194
}
@@ -316,94 +327,167 @@ describe('zoidPollyfill', () => {
316327
postMessage.mockClear();
317328
});
318329

319-
test('notifies when props update', () => {
320-
mockLoadUrl(
321-
'https://localhost.paypal.com:8080/credit-presentment/native/message?client_id=client_1&logo_type=inline&amount=500&devTouchpoint=true',
322-
{
323-
platform: 'android'
324-
}
325-
);
326-
const postMessage = global.Android.paypalMessageModalCallbackHandler;
330+
describe('notifies when props update', () => {
331+
afterEach(() => {
332+
subscribeMockFn.mockClear();
333+
});
334+
test('webview', () => {
335+
mockLoadUrl(
336+
'https://localhost.paypal.com:8080/credit-presentment/native/modal?client_id=client_1&logo_type=inline&amount=500&devTouchpoint=true',
337+
{
338+
platform: 'android'
339+
}
340+
);
341+
const postMessage = global.Android.paypalMessageModalCallbackHandler;
327342

328-
zoidPolyfill();
343+
zoidPolyfill();
329344

330-
expect(window.actions).toEqual(
331-
expect.objectContaining({
332-
updateProps: expect.any(Function)
333-
})
334-
);
335-
expect(window.xprops).toEqual(
336-
expect.objectContaining({
337-
onProps: expect.any(Function)
338-
})
339-
);
345+
expect(window.actions).toEqual(
346+
expect.objectContaining({
347+
updateProps: expect.any(Function)
348+
})
349+
);
350+
expect(window.xprops).toEqual(
351+
expect.objectContaining({
352+
onProps: expect.any(Function)
353+
})
354+
);
340355

341-
const onPropsCallback = jest.fn();
356+
const onPropsCallback = jest.fn();
342357

343-
window.xprops.onProps(onPropsCallback);
344-
window.actions.updateProps({ amount: 1000 });
358+
window.xprops.onProps(onPropsCallback);
359+
window.actions.updateProps({ amount: 1000 });
345360

346-
expect(onPropsCallback).toHaveBeenCalledTimes(1);
347-
expect(onPropsCallback).toHaveBeenCalledWith(
348-
expect.objectContaining({
349-
clientId: 'client_1',
350-
logoType: 'inline',
351-
amount: 1000
352-
})
353-
);
361+
expect(onPropsCallback).toHaveBeenCalledTimes(1);
362+
expect(onPropsCallback).toHaveBeenCalledWith(
363+
expect.objectContaining({
364+
clientId: 'client_1',
365+
logoType: 'inline',
366+
amount: 1000
367+
})
368+
);
354369

355-
window.actions.updateProps({ offer: 'TEST' });
370+
window.actions.updateProps({ offer: 'TEST' });
356371

357-
expect(onPropsCallback).toHaveBeenCalledTimes(2);
358-
expect(onPropsCallback).toHaveBeenCalledWith(
359-
expect.objectContaining({
360-
clientId: 'client_1',
361-
logoType: 'inline',
362-
amount: 1000,
363-
offer: 'TEST'
364-
})
365-
);
372+
expect(onPropsCallback).toHaveBeenCalledTimes(2);
373+
expect(onPropsCallback).toHaveBeenCalledWith(
374+
expect.objectContaining({
375+
clientId: 'client_1',
376+
logoType: 'inline',
377+
amount: 1000,
378+
offer: 'TEST'
379+
})
380+
);
366381

367-
window.xprops.onReady({
368-
products: ['PRODUCT_1', 'PRODUCT_2'],
369-
meta: {
370-
trackingDetails: {
371-
fdata: '123abc',
372-
credit_product_identifiers: ['PAY_LATER_LONG_TERM_US'],
373-
offer_country_code: 'US',
374-
extra_field: 'should not be present'
382+
window.xprops.onReady({
383+
products: ['PRODUCT_1', 'PRODUCT_2'],
384+
meta: {
385+
trackingDetails: {
386+
fdata: '123abc',
387+
credit_product_identifiers: ['PAY_LATER_LONG_TERM_US'],
388+
offer_country_code: 'US',
389+
extra_field: 'should not be present'
390+
}
375391
}
376-
}
377-
});
392+
});
378393

379-
expect(postMessage).toHaveBeenCalledTimes(1);
380-
expect(postMessage.mock.calls[0][0]).toEqual(expect.any(String));
381-
expect(JSON.parse(postMessage.mock.calls[0][0])).toMatchInlineSnapshot(`
382-
Object {
383-
"args": Array [
394+
expect(postMessage).toHaveBeenCalledTimes(1);
395+
expect(postMessage.mock.calls[0][0]).toEqual(expect.any(String));
396+
expect(JSON.parse(postMessage.mock.calls[0][0])).toMatchInlineSnapshot(`
384397
Object {
385-
"__shared__": Object {
386-
"credit_product_identifiers": Array [
387-
"PAY_LATER_LONG_TERM_US",
388-
],
389-
"fdata": "123abc",
390-
"offer_country_code": "US",
391-
},
392-
"event_type": "modal_rendered",
393-
"render_duration": "50",
394-
"request_duration": "100",
398+
"args": Array [
399+
Object {
400+
"__shared__": Object {
401+
"credit_product_identifiers": Array [
402+
"PAY_LATER_LONG_TERM_US",
403+
],
404+
"fdata": "123abc",
405+
"offer_country_code": "US",
406+
},
407+
"event_type": "modal_rendered",
408+
"render_duration": "50",
409+
"request_duration": "100",
410+
},
411+
],
412+
"name": "onReady",
413+
}
414+
`);
415+
postMessage.mockClear();
416+
});
417+
test('browser', () => {
418+
mockLoadUrl(
419+
'https://localhost.paypal.com:8080/credit-presentment/lander/modal?client_id=client_1&logo_type=inline&amount=500&devTouchpoint=true'
420+
);
421+
422+
zoidPolyfill();
423+
424+
expect(window.xprops).toEqual(
425+
expect.objectContaining({
426+
onProps: expect.any(Function)
427+
})
428+
);
429+
430+
const onPropsCallback = jest.fn();
431+
432+
window.xprops.onProps(onPropsCallback);
433+
434+
expect(subscribeMockFn).toHaveBeenCalledTimes(1);
435+
expect(subscribeMockFn).toHaveBeenCalledWith('PROPS_UPDATE', expect.any(Function));
436+
437+
subscribeMockFn(
438+
{
439+
amount: 1000
395440
},
396-
],
397-
"name": "onReady",
398-
}
399-
`);
400-
postMessage.mockClear();
441+
window.origin
442+
);
443+
444+
const subscribeCallback = subscribeMockFn.mock.calls[0][1];
445+
446+
subscribeCallback({
447+
amount: 1000
448+
});
449+
450+
expect(onPropsCallback).toHaveBeenCalledTimes(1);
451+
expect(onPropsCallback).toHaveBeenCalledWith(
452+
expect.objectContaining({
453+
clientId: 'client_1',
454+
logoType: 'inline',
455+
amount: 1000
456+
})
457+
);
458+
459+
subscribeCallback({
460+
offerTypes: ['TEST']
461+
});
462+
463+
expect(onPropsCallback).toHaveBeenCalledTimes(2);
464+
expect(onPropsCallback).toHaveBeenCalledWith(
465+
expect.objectContaining({
466+
clientId: 'client_1',
467+
logoType: 'inline',
468+
amount: 1000,
469+
offerTypes: ['TEST']
470+
})
471+
);
472+
473+
window.xprops.onReady({
474+
products: ['PRODUCT_1', 'PRODUCT_2'],
475+
meta: {
476+
trackingDetails: {
477+
fdata: '123abc',
478+
credit_product_identifiers: ['PAY_LATER_LONG_TERM_US'],
479+
offer_country_code: 'US',
480+
extra_field: 'should not be present'
481+
}
482+
}
483+
});
484+
});
401485
});
402486

403487
describe('communication with parent window on onClose ', () => {
404488
beforeAll(() => {
405489
mockLoadUrl(
406-
'https://localhost.paypal.com:8080/credit-presentment/native/message?client_id=client_1&logo_type=inline&amount=500&devTouchpoint=true'
490+
'https://localhost.paypal.com:8080/credit-presentment/native/modal?client_id=client_1&logo_type=inline&amount=500&devTouchpoint=true'
407491
);
408492
zoidPolyfill();
409493
const postMessage = jest.fn();

0 commit comments

Comments
 (0)