From 6c5a816df13e44b336f3d5d23c9a1bc8883f3b8a Mon Sep 17 00:00:00 2001 From: Ruchika Sinha <69535463+Ruchika4@users.noreply.github.com> Date: Wed, 17 Apr 2024 22:39:11 -0700 Subject: [PATCH] MWPW-143904-New block: cc-mobile-app-banner (#271) * app banner in cc * app banner in CC * add review comments --- .../cc-mobile-app-banner.css | 4 ++ .../cc-mobile-app-banner.js | 66 ++++++++++++++++++ creativecloud/scripts/utils.js | 4 +- .../cc-mobile-app-banner.test.js | 67 +++++++++++++++++++ .../cc-mobile-app-banner/mocks/body.html | 10 +++ .../mocks/branch-io-key.json | 8 +++ test/helpers/waitForElement.js | 12 ++++ 7 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 creativecloud/blocks/cc-mobile-app-banner/cc-mobile-app-banner.css create mode 100644 creativecloud/blocks/cc-mobile-app-banner/cc-mobile-app-banner.js create mode 100644 test/blocks/cc-mobile-app-banner/cc-mobile-app-banner.test.js create mode 100644 test/blocks/cc-mobile-app-banner/mocks/body.html create mode 100644 test/blocks/cc-mobile-app-banner/mocks/branch-io-key.json diff --git a/creativecloud/blocks/cc-mobile-app-banner/cc-mobile-app-banner.css b/creativecloud/blocks/cc-mobile-app-banner/cc-mobile-app-banner.css new file mode 100644 index 000000000..8fdabf8bf --- /dev/null +++ b/creativecloud/blocks/cc-mobile-app-banner/cc-mobile-app-banner.css @@ -0,0 +1,4 @@ +.cc-mobile-app-banner { + display:none; +} + diff --git a/creativecloud/blocks/cc-mobile-app-banner/cc-mobile-app-banner.js b/creativecloud/blocks/cc-mobile-app-banner/cc-mobile-app-banner.js new file mode 100644 index 000000000..6093e08bd --- /dev/null +++ b/creativecloud/blocks/cc-mobile-app-banner/cc-mobile-app-banner.js @@ -0,0 +1,66 @@ +import { getConfig } from '../../scripts/utils.js'; + +async function getKey(product) { + const config = getConfig(); + let keyMatch = []; + const resp = await fetch(`${config.contentRoot ?? ''}/branch-io-key.json`); + if (resp.ok) { + const json = await resp.json(); + keyMatch = json.data.filter( + (p) => p.product === product, + ); + } + return keyMatch[0]?.key; +} + +/* eslint-disable */ +function branchInit(header, key) { + let initValue = false; + function initBranch() { + if (initValue) return; + initValue = true; + (function (b, r, a, n, c, h, _, s, d, k) { + if (!b[n] || !b[n]._q) { + for (; s < _.length;) c(h, _[s++]); + d = r.createElement(a); + d.async = 1; + d.src = 'https://cdn.branch.io/branch-latest.min.js'; + k = r.getElementsByTagName(a)[0]; + k.parentNode.insertBefore(d, k); + b[n] = h; + } + })( + window, + document, + 'script', + 'branch', + function (b, r) { + b[r] = function () { + b._q.push([r, arguments]); + }; + }, + { _q: [], _v: 1 }, + 'addListener applyCode autoAppIndex banner closeBanner closeJourney creditHistory credits data deepview deepviewCta first getCode init link logout redeem referrals removeListener sendSMS setBranchViewData setIdentity track validateCode trackCommerceEvent logEvent disableTracking'.split( + ' ' + ), + 0 + ); + const privacyConsent = window.adobePrivacy?.hasUserProvidedConsent(); + branch.init(key, { tracking_disabled: !privacyConsent }); + branch.addListener('didShowJourney', () => header.style.position = 'relative'); + branch.addListener('didCloseJourney', () => header.style.position = 'sticky'); + } + ['adobePrivacy:PrivacyConsent', 'adobePrivacy:PrivacyReject', 'adobePrivacy:PrivacyCustom'] + .forEach((event) => { + window.addEventListener(event, initBranch); + }); +} + +/* eslint-enable */ +export default async function init(el) { + const header = document.querySelector('.global-navigation'); + if (!header) return; + const product = [...el.classList].find((token) => token.startsWith('product-')).split('-')[1]; + const key = await getKey(product); + if (key) branchInit(header, key); +} diff --git a/creativecloud/scripts/utils.js b/creativecloud/scripts/utils.js index 3b753f87f..f4767f9ad 100644 --- a/creativecloud/scripts/utils.js +++ b/creativecloud/scripts/utils.js @@ -45,8 +45,8 @@ export const [setLibs, getLibs] = (() => { const miloLibs = setLibs('/libs'); -const { createTag, localizeLink, getConfig, loadStyle } = await import(`${miloLibs}/utils/utils.js`); -export { createTag, loadStyle, localizeLink }; +const { createTag, localizeLink, getConfig, loadStyle, setConfig } = await import(`${miloLibs}/utils/utils.js`); +export { createTag, loadStyle, localizeLink, setConfig, getConfig }; function getDecorateAreaFn() { let lcpImgSet = false; diff --git a/test/blocks/cc-mobile-app-banner/cc-mobile-app-banner.test.js b/test/blocks/cc-mobile-app-banner/cc-mobile-app-banner.test.js new file mode 100644 index 000000000..b6f4a8d0e --- /dev/null +++ b/test/blocks/cc-mobile-app-banner/cc-mobile-app-banner.test.js @@ -0,0 +1,67 @@ +import { readFile } from '@web/test-runner-commands'; +import { expect } from '@esm-bundle/chai'; +import sinon from 'sinon'; +import { delay } from '../../helpers/waitForElement.js'; + +const { setConfig } = await import('../../../creativecloud/scripts/utils.js'); + +const mockConfig = { contentRoot: '/test/blocks/cc-mobile-app-banner/mocks' }; +setConfig(mockConfig); + +describe('cc-mobile-app-banner', () => { + beforeEach(async () => { + document.body.innerHTML = await readFile({ path: './mocks/body.html' }); + }); + afterEach(() => { + sinon.restore(); + }); + + it('should not call branch init if no branch-io-key.json', async () => { + sinon.stub(window, 'fetch'); + const res = new window.Response('Not found', { status: 404 }); + window.fetch.returns(Promise.resolve(res)); + + const module = await import('../../../creativecloud/blocks/cc-mobile-app-banner/cc-mobile-app-banner.js'); + const banner = document.body.querySelector('.cc-mobile-app-banner.product-test'); + await module.default(banner); + window.dispatchEvent(new CustomEvent('adobePrivacy:PrivacyConsent')); + await delay(0); + + const scriptTags = document.querySelectorAll('head > script'); + const scriptSrcs = []; + scriptTags.forEach((scriptTag) => { + scriptSrcs.push(scriptTag.getAttribute('src')); + }); + expect(scriptSrcs).to.not.include('https://cdn.branch.io/branch-latest.min.js'); + }); + + it('should not call branch init if product not found in branch-io-key file', async () => { + const module = await import('../../../creativecloud/blocks/cc-mobile-app-banner/cc-mobile-app-banner.js'); + const banner = document.body.querySelector('.cc-mobile-app-banner.product-test1'); + await module.default(banner); + window.dispatchEvent(new CustomEvent('adobePrivacy:PrivacyConsent')); + await delay(0); + + const scriptTags = document.querySelectorAll('head > script'); + const scriptSrcs = []; + scriptTags.forEach((scriptTag) => { + if (scriptTag.getAttribute('src') !== null) scriptSrcs.push(scriptTag.getAttribute('src')); + }); + expect(scriptSrcs).to.not.include('https://cdn.branch.io/branch-latest.min.js'); + }); + + it('should init by adding branchio script', async () => { + window.adobePrivacy = { hasUserProvidedConsent: () => true }; + const module = await import('../../../creativecloud/blocks/cc-mobile-app-banner/cc-mobile-app-banner.js'); + const banner = document.body.querySelector('.cc-mobile-app-banner.product-test'); + await module.default(banner); + window.dispatchEvent(new CustomEvent('adobePrivacy:PrivacyConsent')); + await delay(0); + const scriptTags = document.querySelectorAll('head > script'); + const scriptSrcs = []; + scriptTags.forEach((scriptTag) => { + if (scriptTag.getAttribute('src') !== null) scriptSrcs.push(scriptTag.getAttribute('src')); + }); + expect(scriptSrcs).to.include('https://cdn.branch.io/branch-latest.min.js'); + }); +}); diff --git a/test/blocks/cc-mobile-app-banner/mocks/body.html b/test/blocks/cc-mobile-app-banner/mocks/body.html new file mode 100644 index 000000000..9b6a87704 --- /dev/null +++ b/test/blocks/cc-mobile-app-banner/mocks/body.html @@ -0,0 +1,10 @@ + +
Hello World
+
+
+
+
+
+
+
diff --git a/test/blocks/cc-mobile-app-banner/mocks/branch-io-key.json b/test/blocks/cc-mobile-app-banner/mocks/branch-io-key.json new file mode 100644 index 000000000..f3ec9ebe7 --- /dev/null +++ b/test/blocks/cc-mobile-app-banner/mocks/branch-io-key.json @@ -0,0 +1,8 @@ +{ + "data": [ + { + "product": "test", + "key": "key_test_eaNdoH8nTxeZXfOsgkELrjgpFrhm4q2m" + } + ] +} diff --git a/test/helpers/waitForElement.js b/test/helpers/waitForElement.js index b78a595a5..76b48a11e 100644 --- a/test/helpers/waitForElement.js +++ b/test/helpers/waitForElement.js @@ -47,4 +47,16 @@ const waitForElement = ( observer.observe(rootEl, options); }); +/** + * Promise based setTimeout that can be await'd + * @param {int} timeOut time out in milliseconds + * @param {*} cb Callback function to call when time elapses + * @returns + */ +export const delay = (timeOut, cb) => new Promise((resolve) => { + setTimeout(() => { + resolve((cb && cb()) || null); + }, timeOut); +}); + export default waitForElement;