diff --git a/libs/blocks/merch/merch.js b/libs/blocks/merch/merch.js index fc0ce27410..138fbbce71 100644 --- a/libs/blocks/merch/merch.js +++ b/libs/blocks/merch/merch.js @@ -192,28 +192,48 @@ export async function fetchCheckoutLinkConfigs(base = '') { ?? fetch(`${base}${CHECKOUT_LINK_CONFIG_PATH}`).catch((e) => { log?.error('Failed to fetch checkout link configs', e); }).then((mappings) => { - if (!mappings?.ok) return undefined; + if (!mappings?.ok) return { data: [] }; return mappings.json(); }); return fetchCheckoutLinkConfigs.promise; } -export async function getCheckoutLinkConfig(productFamily) { +export async function getCheckoutLinkConfig(productFamily, productCode, paCode) { let { base } = getConfig(); if (/\.page$/.test(document.location.origin)) { /* c8 ignore next 2 */ base = base.replace('.live', '.page'); } const checkoutLinkConfigs = await fetchCheckoutLinkConfigs(base); + if (!checkoutLinkConfigs.data.length) return undefined; const { locale: { region } } = getConfig(); - const productFamilyConfigs = checkoutLinkConfigs.data?.filter( - ({ [NAME_PRODUCT_FAMILY]: mappingProductFamily }) => mappingProductFamily === productFamily, - ); - if (productFamilyConfigs.length === 0) return undefined; - const checkoutLinkConfig = productFamilyConfigs.find( + + const { + paCodeConfigs, + productCodeConfigs, + productFamilyConfigs, + } = checkoutLinkConfigs.data.reduce((acc, config) => { + if (config[NAME_PRODUCT_FAMILY] === paCode) { + acc.paCodeConfigs.push(config); + } else if (config[NAME_PRODUCT_FAMILY] === productCode) { + acc.productCodeConfigs.push(config); + } else if (config[NAME_PRODUCT_FAMILY] === productFamily) { + acc.productFamilyConfigs.push(config); + } + return acc; + }, { paCodeConfigs: [], productCodeConfigs: [], productFamilyConfigs: [] }); + + // helps to fallback to product family config + // if no locale specific config is found below. + const productCheckoutLinkConfigs = [ + ...paCodeConfigs, ...productCodeConfigs, ...productFamilyConfigs, + ]; + + if (!productCheckoutLinkConfigs.length) return undefined; + const checkoutLinkConfig = productCheckoutLinkConfigs.find( ({ [NAME_LOCALE]: locale }) => locale === '', ); - const checkoutLinkConfigOverride = productFamilyConfigs.find( + const checkoutLinkConfigOverride = productCheckoutLinkConfigs.find( ({ [NAME_LOCALE]: locale }) => locale === region, ) ?? {}; const overrides = Object.fromEntries( @@ -231,14 +251,22 @@ export async function getCheckoutLinkConfig(productFamily) { export async function getDownloadAction( options, imsSignedInPromise, - [{ offerType, productArrangement: { productFamily: offerFamily } = {} }], + [{ + offerType, + productArrangementCode, + productArrangement: { productCode, productFamily: offerFamily } = {}, + }], ) { if (options.entitlement !== true) return undefined; const loggedIn = await imsSignedInPromise; if (!loggedIn) return undefined; const entitlements = await fetchEntitlements(); if (!entitlements?.length) return undefined; - const checkoutLinkConfig = await getCheckoutLinkConfig(offerFamily); + const checkoutLinkConfig = await getCheckoutLinkConfig( + offerFamily, + productCode, + productArrangementCode, + ); if (!checkoutLinkConfig?.DOWNLOAD_URL) return undefined; const offer = entitlements.find(( { offer: { product_arrangement: { family: subscriptionFamily } } }, @@ -355,9 +383,17 @@ export async function openModal(e, url, offerType) { } export async function getModalAction(offers, options) { - const [{ offerType, productArrangement: { productFamily: offerFamily } = {} }] = offers ?? [{}]; + const [{ + offerType, + productArrangementCode, + productArrangement: { productCode, productFamily: offerFamily } = {}, + }] = offers ?? [{}]; if (options.modal !== true) return undefined; - const checkoutLinkConfig = await getCheckoutLinkConfig(offerFamily); + const checkoutLinkConfig = await getCheckoutLinkConfig( + offerFamily, + productCode, + productArrangementCode, + ); if (!checkoutLinkConfig) return undefined; const columnName = (offerType === OFFER_TYPE_TRIAL) ? FREE_TRIAL_PATH : BUY_NOW_PATH; let url = checkoutLinkConfig[columnName]; diff --git a/test/blocks/merch/merch.test.js b/test/blocks/merch/merch.test.js index 12d75e8a5f..4a7641f9c2 100644 --- a/test/blocks/merch/merch.test.js +++ b/test/blocks/merch/merch.test.js @@ -64,6 +64,14 @@ const CHECKOUT_LINK_CONFIGS = { BUY_NOW_PATH: '', LOCALE: '', }, + { + PRODUCT_FAMILY: 'testPaCode', + DOWNLOAD_TEXT: 'paCode', + }, + { + PRODUCT_FAMILY: 'testProductCode', + DOWNLOAD_TEXT: 'productCode', + }, ], }; @@ -489,8 +497,7 @@ describe('Merch Block', () => { fetchCheckoutLinkConfigs.promise = undefined; setCheckoutLinkConfigs(null); const mappings = await fetchCheckoutLinkConfigs('http://localhost:2000/libs'); - expect(mappings).to.be.undefined; - setCheckoutLinkConfigs(CHECKOUT_LINK_CONFIGS); + expect(mappings.data).to.empty; fetchCheckoutLinkConfigs.promise = undefined; }); @@ -522,27 +529,6 @@ describe('Merch Block', () => { expect(url).to.equal('https://creativecloud.adobe.com/apps/download'); }); - it('getModalAction: returns undefined if checkout-link config is not found', async () => { - fetchCheckoutLinkConfigs.promise = undefined; - setCheckoutLinkConfigs(CHECKOUT_LINK_CONFIGS); - const action = await getModalAction([{ productArrangement: { productFamily: 'XZY' } }], { modal: true }); - expect(action).to.be.undefined; - }); - - it('getModalAction: returns undefined if modal path is cancelled', async () => { - setConfig({ - ...config, - pathname: '/fr/test.html', - locales: { fr: { ietf: 'fr-FR' } }, - prodDomains: PROD_DOMAINS, - placeholders: { download: 'Télécharger' }, - }); - fetchCheckoutLinkConfigs.promise = undefined; - setCheckoutLinkConfigs(CHECKOUT_LINK_CONFIGS); - const action = await getModalAction([{ productArrangement: { productFamily: 'PHOTOSHOP' } }], { modal: true }); - expect(action).to.be.undefined; - }); - it('getCheckoutAction: handles errors gracefully', async () => { const imsSignedInPromise = new Promise((resolve, reject) => { setTimeout(() => { @@ -590,6 +576,7 @@ describe('Merch Block', () => { expect(modal).to.exist; document.querySelector('.modal-curtain').click(); }); + it('renders Milo TWP modal', async () => { mockIms(); const el = document.querySelector('.merch.cta.milo.twp'); @@ -632,6 +619,7 @@ describe('Merch Block', () => { expect(modal).to.exist; document.querySelector('.modal-curtain').click(); }); + it('renders TWP modal with preselected plan', async () => { mockIms(); const meta = document.createElement('meta'); @@ -648,6 +636,41 @@ describe('Merch Block', () => { document.querySelector('meta[name="preselect-plan"]').remove(); }); + it('getCheckoutLinkConfig: finds using paCode', async () => { + let checkoutLinkConfig = await getCheckoutLinkConfig(undefined, undefined, 'testPaCode'); + expect(checkoutLinkConfig.DOWNLOAD_TEXT).to.equal('paCode'); + checkoutLinkConfig = await getCheckoutLinkConfig('', '', 'testPaCode'); + expect(checkoutLinkConfig.DOWNLOAD_TEXT).to.equal('paCode'); + }); + + it('getCheckoutLinkConfig: finds using productCode', async () => { + let checkoutLinkConfig = await getCheckoutLinkConfig(undefined, 'testProductCode', undefined); + expect(checkoutLinkConfig.DOWNLOAD_TEXT).to.equal('productCode'); + checkoutLinkConfig = await getCheckoutLinkConfig('', 'testProductCode', ''); + expect(checkoutLinkConfig.DOWNLOAD_TEXT).to.equal('productCode'); + }); + + it('getModalAction: returns undefined if modal path is cancelled', async () => { + setConfig({ + ...config, + pathname: '/fr/test.html', + locales: { fr: { ietf: 'fr-FR' } }, + prodDomains: PROD_DOMAINS, + placeholders: { download: 'Télécharger' }, + }); + fetchCheckoutLinkConfigs.promise = undefined; + setCheckoutLinkConfigs(CHECKOUT_LINK_CONFIGS); + const action = await getModalAction([{ productArrangement: { productFamily: 'PHOTOSHOP' } }], { modal: true }); + expect(action).to.be.undefined; + }); + + it('getModalAction: returns undefined if checkout-link config is not found', async () => { + fetchCheckoutLinkConfigs.promise = undefined; + setCheckoutLinkConfigs(CHECKOUT_LINK_CONFIGS); + const action = await getModalAction([{ productArrangement: { productFamily: 'XZY' } }], { modal: true }); + expect(action).to.be.undefined; + }); + const MODAL_URLS = [ { url: 'https://www.adobe.com/mini-plans/illustrator1.html?mid=ft&web=1',