diff --git a/libs/blocks/marketo/marketo.js b/libs/blocks/marketo/marketo.js index a6cfb7a2e7..1602d0694a 100644 --- a/libs/blocks/marketo/marketo.js +++ b/libs/blocks/marketo/marketo.js @@ -19,14 +19,20 @@ const ROOT_MARGIN = 1000; const FORM_ID = 'form id'; const BASE_URL = 'marketo host'; const MUNCHKIN_ID = 'marketo munckin'; +const SUCCESS_TYPE = 'form.success.type'; +const SUCCESS_CONTENT = 'form.success.content'; +const SUCCESS_SECTION = 'form.success.section'; const FORM_MAP = { - 'destination-url': 'form.success.content', + 'success-type': SUCCESS_TYPE, + 'destination-type': SUCCESS_TYPE, + 'success-content': SUCCESS_CONTENT, + 'destination-url': SUCCESS_CONTENT, + 'success-section': SUCCESS_SECTION, 'co-partner-names': 'program.copartnernames', 'sfdc-campaign-id': 'program.campaignids.sfdc', }; -export const formValidate = (form) => { - const formEl = form.getFormElem().get(0); +export const formValidate = (formEl) => { formEl.classList.remove('hide-errors'); formEl.classList.add('show-warnings'); }; @@ -61,9 +67,24 @@ export const decorateURL = (destination, baseURL = window.location) => { return null; }; -export const formSuccess = (form) => { - const formEl = form.getFormElem().get(0); - const parentModal = formEl.closest('.dialog-modal'); +const setPreference = (key = '', value = '') => { + if (!value || !key.includes('.')) return; + const keyParts = key.split('.'); + const lastKey = keyParts.pop(); + const formDataObject = keyParts.reduce((obj, part) => { + obj[part] = obj[part] || {}; + return obj[part]; + }, window.mcz_marketoForm_pref); + formDataObject[lastKey] = value; +}; + +export const setPreferences = (formData) => { + window.mcz_marketoForm_pref = window.mcz_marketoForm_pref || {}; + Object.entries(formData).forEach(([key, value]) => setPreference(key, value)); +}; + +export const formSuccess = (formEl, formData) => { + const parentModal = formEl?.closest('.dialog-modal'); const mktoSubmit = new Event('mktoSubmit'); window.dispatchEvent(mktoSubmit); @@ -76,10 +97,23 @@ export const formSuccess = (form) => { return false; } - return true; + if (formData?.[SUCCESS_TYPE] !== 'section') return true; + + try { + const section = formData[SUCCESS_SECTION].toLowerCase().replaceAll(' ', '-'); + const success = document.querySelector(`.section.${section}`); + success.classList.remove('hide-block'); + success.scrollIntoView({ behavior: 'smooth' }); + setPreference(SUCCESS_TYPE, 'message'); + } catch (e) { + /* c8 ignore next 2 */ + window.lana?.log('Error showing Marketo success section', { tags: 'errorType=warn,module=marketo' }); + } + + return false; }; -const readyForm = (form) => { +const readyForm = (form, formData) => { const formEl = form.getFormElem().get(0); const isDesktop = matchMedia('(min-width: 900px)'); @@ -95,41 +129,27 @@ const readyForm = (form) => { const offsetPosition = targetPosition + window.pageYOffset - pageTop - window.innerHeight / 2; window.scrollTo(0, offsetPosition); }, true); - form.onValidate(() => formValidate(form)); - form.onSuccess(() => formSuccess(form)); -}; - -const setPreference = (key, value) => { - if (value && key?.includes('.')) { - const keyParts = key.split('.'); - const lastKey = keyParts.pop(); - const formDataObject = keyParts.reduce((obj, part) => { - obj[part] = obj[part] || {}; - return obj[part]; - }, window.mcz_marketoForm_pref); - formDataObject[lastKey] = value; - } -}; - -export const setPreferences = (formData) => { - window.mcz_marketoForm_pref = window.mcz_marketoForm_pref || {}; - Object.entries(formData).forEach(([key, value]) => setPreference(key, value)); + form.onValidate(() => formValidate(formEl)); + form.onSuccess(() => formSuccess(formEl, formData)); }; export const loadMarketo = (el, formData) => { const baseURL = formData[BASE_URL]; + const munchkinID = formData[MUNCHKIN_ID]; + const formID = formData[FORM_ID]; loadScript(`https://${baseURL}/js/forms2/js/forms2.min.js`) .then(() => { const { MktoForms2 } = window; if (!MktoForms2) throw new Error('Marketo forms not loaded'); - MktoForms2.loadForm(`//${baseURL}`, formData[MUNCHKIN_ID], formData[FORM_ID]); + MktoForms2.loadForm(`//${baseURL}`, munchkinID, formID); MktoForms2.whenReady((form) => { readyForm(form, formData); }); }) .catch(() => { - /* c8 ignore next */ + /* c8 ignore next 2 */ el.style.display = 'none'; + window.lana?.log(`Error loading Marketo form for ${munchkinID}_${formID}`, { tags: 'errorType=error,module=marketo' }); }); }; @@ -137,7 +157,6 @@ export default function init(el) { const children = Array.from(el.querySelectorAll(':scope > div')); const encodedConfigDiv = children.shift(); const link = encodedConfigDiv.querySelector('a'); - let formData = {}; if (!link?.href) { el.style.display = 'none'; @@ -145,8 +164,7 @@ export default function init(el) { } const encodedConfig = link.href.split('#')[1]; - - formData = parseEncodedConfig(encodedConfig); + const formData = parseEncodedConfig(encodedConfig); children.forEach((element) => { const key = element.children[0]?.textContent.trim().toLowerCase().replaceAll(' ', '-'); @@ -168,13 +186,12 @@ export default function init(el) { return; } - if (formData['form.success.content']) { - const destinationUrl = decorateURL(formData['form.success.content']); + formData[SUCCESS_TYPE] = formData[SUCCESS_TYPE] || 'redirect'; - if (destinationUrl) { - formData['form.success.type'] = 'redirect'; - formData['form.success.content'] = destinationUrl; - } + if (formData[SUCCESS_TYPE] === 'redirect') { + const destinationUrl = decorateURL(formData[SUCCESS_CONTENT]); + + if (destinationUrl) formData[SUCCESS_CONTENT] = destinationUrl; } setPreferences(formData); 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/libs/features/personalization/personalization.js b/libs/features/personalization/personalization.js index 66d5936c15..4a092127b4 100644 --- a/libs/features/personalization/personalization.js +++ b/libs/features/personalization/personalization.js @@ -169,21 +169,19 @@ const fetchData = async (url, type = DATA_TYPE.JSON) => { return null; }; -const getBlockProps = (fVal) => { +const getBlockProps = (fVal, miloLibs, origin) => { let val = fVal; if (val?.includes('\\')) val = val?.split('\\').join('/'); if (!val?.startsWith('/')) val = `/${val}`; const blockSelector = val?.split('/').pop(); - const { origin } = PAGE_URL; - if (origin.includes('.hlx.') || origin.includes('localhost')) { - if (val.startsWith('/libs/')) { - /* c8 ignore next 2 */ - const { miloLibs, codeRoot } = getConfig(); - val = `${miloLibs || codeRoot}${val.replace('/libs', '')}`; - } else { - val = `${origin}${val}`; - } + + if (val.startsWith('/libs/')) { + /* c8 ignore next 1 */ + val = `${miloLibs}${val.replace('/libs', '')}`; + } else { + val = `${origin}${val}`; } + return { blockSelector, blockTarget: val }; }; @@ -457,6 +455,7 @@ const getVariantInfo = (line, variantNames, variants, manifestPath, manifestOver if (pageFilter && !matchGlob(pageFilter, new URL(window.location).pathname)) return; if (!config.mep?.preview) manifestId = false; + const { origin } = PAGE_URL; variantNames.forEach((vn) => { const targetManifestId = vn.startsWith(TARGET_EXP_PREFIX) ? targetId : false; if (!line[vn] || line[vn].toLowerCase() === 'false') return; @@ -483,7 +482,7 @@ const getVariantInfo = (line, variantNames, variants, manifestPath, manifestOver variants[vn][action] = variants[vn][action] || []; if (action === 'useblockcode') { - const { blockSelector, blockTarget } = getBlockProps(line[vn]); + const { blockSelector, blockTarget } = getBlockProps(line[vn], config.miloLibs, origin); variants[vn][action].push({ selector: blockSelector, val: blockTarget, @@ -578,20 +577,26 @@ const checkForParamMatch = (paramStr) => { return false; }; +function trimNames(arr) { + return arr.map((v) => v.trim()).filter(Boolean); +} +export function buildVariantInfo(variantNames) { + return variantNames.reduce((acc, name) => { + let nameArr = [name]; + if (!name.startsWith(TARGET_EXP_PREFIX)) nameArr = name.split(','); + acc[name] = trimNames(nameArr); + acc.allNames = [...acc.allNames, ...trimNames(name.split(/[,&]|\bnot\b/))]; + return acc; + }, { allNames: [] }); +} + async function getPersonalizationVariant(manifestPath, variantNames = [], variantLabel = null) { const config = getConfig(); if (config.mep?.variantOverride?.[manifestPath]) { return config.mep.variantOverride[manifestPath]; } - const variantInfo = variantNames.reduce((acc, name) => { - let nameArr = [name]; - if (!name.startsWith(TARGET_EXP_PREFIX)) nameArr = name.split(','); - const vNames = nameArr.map((v) => v.trim()).filter(Boolean); - acc[name] = vNames; - acc.allNames = [...acc.allNames, ...vNames]; - return acc; - }, { allNames: [] }); + const variantInfo = buildVariantInfo(variantNames); const entitlementKeys = Object.values(await getEntitlementMap()); const hasEntitlementTag = entitlementKeys.some((tag) => variantInfo.allNames.includes(tag)); diff --git a/libs/features/spectrum-web-components/dist/icons-workflow.js b/libs/features/spectrum-web-components/dist/icons-workflow.js index a46140ed29..7006fc8758 100644 --- a/libs/features/spectrum-web-components/dist/icons-workflow.js +++ b/libs/features/spectrum-web-components/dist/icons-workflow.js @@ -1,7 +1,7 @@ /* eslint-disable */ /* Generated by Milo */ -import{html as C}from"/libs/features/spectrum-web-components/dist/base.js";import{IconBase as L}from"/libs/features/spectrum-web-components/dist/icon.js";var c,i=function(t,...e){return c?c(t,...e):e.reduce((r,o,$)=>r+o+t[$+1],t[0])},a=t=>{c=t};var f=({width:t=24,height:e=24,hidden:r=!1,title:o="Alert"}={})=>i`r+o+t[L+1],t[0])},i=t=>{g=t};var u=({width:t=24,height:e=24,hidden:r=!1,title:o="Alert"}={})=>a` - `;var s=class extends L{render(){return a(C),f({hidden:!this.label,title:this.label})}};import{defineElement as b}from"/libs/features/spectrum-web-components/dist/base.js";b("sp-icon-alert",s);import{html as I}from"/libs/features/spectrum-web-components/dist/base.js";import{IconBase as B}from"/libs/features/spectrum-web-components/dist/icon.js";var g=({width:t=24,height:e=24,hidden:r=!1,title:o="Help"}={})=>i``;var s=class extends Z{render(){return i(B),u({hidden:!this.label,title:this.label})}};import{defineElement as M}from"/libs/features/spectrum-web-components/dist/base.js";M("sp-icon-alert",s);import{html as H}from"/libs/features/spectrum-web-components/dist/base.js";import{IconBase as E}from"/libs/features/spectrum-web-components/dist/icon.js";var w=({width:t=24,height:e=24,hidden:r=!1,title:o="Help"}={})=>a` - `;var l=class extends B{render(){return a(I),g({hidden:!this.label,title:this.label})}};import{defineElement as Z}from"/libs/features/spectrum-web-components/dist/base.js";Z("sp-icon-help",l);import{html as M}from"/libs/features/spectrum-web-components/dist/base.js";import{IconBase as k}from"/libs/features/spectrum-web-components/dist/icon.js";var u=({width:t=24,height:e=24,hidden:r=!1,title:o="Link Out Light"}={})=>i``;var l=class extends E{render(){return i(H),w({hidden:!this.label,title:this.label})}};import{defineElement as k}from"/libs/features/spectrum-web-components/dist/base.js";k("sp-icon-help",l);import{html as A}from"/libs/features/spectrum-web-components/dist/base.js";import{IconBase as V}from"/libs/features/spectrum-web-components/dist/icon.js";var x=({width:t=24,height:e=24,hidden:r=!1,title:o="Link Out Light"}={})=>a` - `;var m=class extends k{render(){return a(M),u({hidden:!this.label,title:this.label})}};import{defineElement as A}from"/libs/features/spectrum-web-components/dist/base.js";A("sp-icon-link-out-light",m);import{html as H}from"/libs/features/spectrum-web-components/dist/base.js";import{IconBase as E}from"/libs/features/spectrum-web-components/dist/icon.js";var w=({width:t=24,height:e=24,hidden:r=!1,title:o="Magnify"}={})=>i``;var m=class extends V{render(){return i(A),x({hidden:!this.label,title:this.label})}};import{defineElement as O}from"/libs/features/spectrum-web-components/dist/base.js";O("sp-icon-link-out-light",m);import{html as U}from"/libs/features/spectrum-web-components/dist/base.js";import{IconBase as y}from"/libs/features/spectrum-web-components/dist/icon.js";var v=({width:t=24,height:e=24,hidden:r=!1,title:o="Magnify"}={})=>a` - `;var p=class extends E{render(){return a(H),w({hidden:!this.label,title:this.label})}};import{defineElement as U}from"/libs/features/spectrum-web-components/dist/base.js";U("sp-icon-magnify",p);import{html as y}from"/libs/features/spectrum-web-components/dist/base.js";import{IconBase as V}from"/libs/features/spectrum-web-components/dist/icon.js";var x=({width:t=24,height:e=24,hidden:r=!1,title:o="User"}={})=>i``;var n=class extends y{render(){return i(U),v({hidden:!this.label,title:this.label})}};import{defineElement as D}from"/libs/features/spectrum-web-components/dist/base.js";D("sp-icon-magnify",n);import{html as G}from"/libs/features/spectrum-web-components/dist/base.js";import{IconBase as S}from"/libs/features/spectrum-web-components/dist/icon.js";var T=({width:t=24,height:e=24,hidden:r=!1,title:o="User"}={})=>a` - `;var h=class extends V{render(){return a(y),x({hidden:!this.label,title:this.label})}};import{defineElement as G}from"/libs/features/spectrum-web-components/dist/base.js";G("sp-icon-user",h);import{html as O}from"/libs/features/spectrum-web-components/dist/base.js";import{IconBase as S}from"/libs/features/spectrum-web-components/dist/icon.js";var v=({width:t=24,height:e=24,hidden:r=!1,title:o="User Group"}={})=>i``;var p=class extends S{render(){return i(G),T({hidden:!this.label,title:this.label})}};import{defineElement as j}from"/libs/features/spectrum-web-components/dist/base.js";j("sp-icon-user",p);import{html as q}from"/libs/features/spectrum-web-components/dist/base.js";import{IconBase as z}from"/libs/features/spectrum-web-components/dist/icon.js";var $=({width:t=24,height:e=24,hidden:r=!1,title:o="User Group"}={})=>a` - `;var n=class extends S{render(){return a(O),v({hidden:!this.label,title:this.label})}};import{defineElement as j}from"/libs/features/spectrum-web-components/dist/base.js";j("sp-icon-user-group",n);import{html as q}from"/libs/features/spectrum-web-components/dist/base.js";import{IconBase as z}from"/libs/features/spectrum-web-components/dist/icon.js";var T=({width:t=24,height:e=24,hidden:r=!1,title:o="Book"}={})=>i``;var h=class extends z{render(){return i(q),$({hidden:!this.label,title:this.label})}};import{defineElement as F}from"/libs/features/spectrum-web-components/dist/base.js";F("sp-icon-user-group",h);import{html as J}from"/libs/features/spectrum-web-components/dist/base.js";import{IconBase as K}from"/libs/features/spectrum-web-components/dist/icon.js";var C=({width:t=24,height:e=24,hidden:r=!1,title:o="Book"}={})=>a` - `;var d=class extends z{render(){return a(q),T({hidden:!this.label,title:this.label})}};import{defineElement as D}from"/libs/features/spectrum-web-components/dist/base.js";D("sp-icon-book",d); + `;var d=class extends K{render(){return i(J),C({hidden:!this.label,title:this.label})}};import{defineElement as N}from"/libs/features/spectrum-web-components/dist/base.js";N("sp-icon-book",d);import{html as P}from"/libs/features/spectrum-web-components/dist/base.js";import{IconBase as Q}from"/libs/features/spectrum-web-components/dist/icon.js";var I=({width:t=24,height:e=24,hidden:r=!1,title:o="Download"}={})=>a` + + + `;var c=class extends Q{render(){return i(P),I({hidden:!this.label,title:this.label})}};import{defineElement as R}from"/libs/features/spectrum-web-components/dist/base.js";R("sp-icon-download",c);import{html as W}from"/libs/features/spectrum-web-components/dist/base.js";import{IconBase as X}from"/libs/features/spectrum-web-components/dist/icon.js";var b=({width:t=24,height:e=24,hidden:r=!1,title:o="Open In"}={})=>a` + + + `;var f=class extends X{render(){return i(W),b({hidden:!this.label,title:this.label})}};import{defineElement as Y}from"/libs/features/spectrum-web-components/dist/base.js";Y("sp-icon-open-in",f); diff --git a/libs/features/spectrum-web-components/src/icons-workflow.js b/libs/features/spectrum-web-components/src/icons-workflow.js index bec467c87a..4d00adeb2d 100644 --- a/libs/features/spectrum-web-components/src/icons-workflow.js +++ b/libs/features/spectrum-web-components/src/icons-workflow.js @@ -5,3 +5,5 @@ import '@spectrum-web-components/icons-workflow/icons/sp-icon-magnify.js'; import '@spectrum-web-components/icons-workflow/icons/sp-icon-user.js'; import '@spectrum-web-components/icons-workflow/icons/sp-icon-user-group.js'; import '@spectrum-web-components/icons-workflow/icons/sp-icon-book.js'; +import '@spectrum-web-components/icons-workflow/icons/sp-icon-download.js'; +import '@spectrum-web-components/icons-workflow/icons/sp-icon-open-in.js'; diff --git a/libs/utils/logWebVitals.js b/libs/utils/logWebVitals.js index 8c092fb5e4..a7e4bc39a5 100644 --- a/libs/utils/logWebVitals.js +++ b/libs/utils/logWebVitals.js @@ -87,9 +87,24 @@ function logMepExperiments(lanaData, mep) { }); } -export default function webVitals(mep, { delay = 1000 } = {}) { - const lanaData = {}; - logMepExperiments(lanaData, mep); - observeCLS(lanaData); - observeLCP(lanaData, delay); +export default function webVitals(mep, { delay = 1000, sampleRate = 50 } = {}) { + const isChrome = () => { + const nav = window.navigator; + return nav.userAgent.includes('Chrome') && nav.vendor.includes('Google'); + }; + if (!isChrome() || Math.random() * 100 > sampleRate) return; + const getConsent = () => window.adobePrivacy?.activeCookieGroups().indexOf('C0002') !== -1; + function handleEvent() { + if (!getConsent()) return; + const lanaData = {}; + logMepExperiments(lanaData, mep); + observeCLS(lanaData); + observeLCP(lanaData, delay); + } + if (getConsent()) { + handleEvent(); + return; + } + window.addEventListener('adobePrivacy:PrivacyConsent', handleEvent, { once: true }); + window.addEventListener('adobePrivacy:PrivacyCustom', handleEvent, { once: true }); } diff --git a/libs/utils/utils.js b/libs/utils/utils.js index 4402f394ae..b792862811 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -191,6 +191,8 @@ export function getMetadata(name, doc = document) { } const handleEntitlements = (() => { + const { martech } = Object.fromEntries(PAGE_URL.searchParams); + if (martech === 'off') return () => {}; let entResolve; const entPromise = new Promise((resolve) => { entResolve = resolve; @@ -850,6 +852,9 @@ export async function loadIms() { if (!window.adobeIMS?.isSignedInUser()) { getConfig().entitlements([]); } + }).catch((e) => { + getConfig().entitlements([]); + throw e; }); return imsLoaded; @@ -924,16 +929,21 @@ export const getMepEnablement = (mdKey, paramKey = false) => { }; async function checkForPageMods() { - const { mep: mepParam, mepHighlight, mepButton } = Object.fromEntries(PAGE_URL.searchParams); + const { + mep: mepParam, + mepHighlight, + mepButton, + martech, + } = Object.fromEntries(PAGE_URL.searchParams); if (mepParam === 'off') return; const pzn = getMepEnablement('personalization'); const promo = getMepEnablement('manifestnames', PROMO_PARAM); - const target = getMepEnablement('target'); + const target = martech === 'off' ? false : getMepEnablement('target'); if (!(pzn || target || promo || mepParam || mepHighlight || mepButton || mepParam === '')) return; if (target) { loadMartech(); - } else if (pzn) { + } else if (pzn && martech !== 'off') { loadIms() .then(() => { /* c8 ignore next */ @@ -999,18 +1009,6 @@ export function scrollToHashedElement(hash) { }); } -function logPagePerf() { - if (getMetadata('pageperf') !== 'on') return; - const isChrome = () => { - const nav = window.navigator; - return nav.userAgent.includes('Chrome') && nav.vendor.includes('Google'); - }; - const sampleRate = parseInt(getMetadata('pageperf-rate'), 10) || 50; - if (!isChrome() || Math.random() * 100 > sampleRate) return; - import('./logWebVitals.js') - .then((mod) => mod.default(getConfig().mep, getMetadata('pageperf-delay') || 1000)); -} - export async function loadDeferred(area, blocks, config) { const event = new Event(MILO_EVENTS.DEFERRED); area.dispatchEvent(event); @@ -1039,7 +1037,13 @@ export async function loadDeferred(area, blocks, config) { sampleRUM.observe(area.querySelectorAll('picture > img')); }); - logPagePerf(); + if (getMetadata('pageperf') === 'on') { + import('./logWebVitals.js') + .then((mod) => mod.default(getConfig().mep, { + delay: getMetadata('pageperf-delay'), + sampleRate: parseInt(getMetadata('pageperf-rate'), 10), + })); + } } function initSidekick() { diff --git a/test/blocks/marketo/marketo.test.html b/test/blocks/marketo/marketo.test.html index ef3f7c66c0..862ee6f6e1 100644 --- a/test/blocks/marketo/marketo.test.html +++ b/test/blocks/marketo/marketo.test.html @@ -9,6 +9,15 @@ +
+

Form Success

+
+
+
style
+
hide block, form success
+
+
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec cursus mi id tincidunt pretium. Praesent a porta ex. Etiam eu metus urna. Etiam vulputate nibh nisi, sed gravida diam dictum id. Cras et justo metus. Morbi consectetur @@ -89,15 +98,28 @@

Fill out the form to view the repo it('validates form', () => { expect(window.MktoForms2.onValidate.calledOnce).to.be.true; - formValidate(window.MktoForms2); const form = document.querySelector('form'); + formValidate(form); expect(form.classList.contains('show-warnings')).to.be.true; }); it('submits successfully', async () => { - formSuccess(window.MktoForms2); + const form = document.querySelector('form'); + formSuccess(form); expect(window.mktoSubmitted).to.be.true; }); + + it('shows success section', async () => { + const form = document.querySelector('form'); + const section = document.querySelector('.form-success'); + const formData = { + 'form.success.type': 'section', + 'form.success.section': 'form success', + }; + + formSuccess(form, formData); + expect(section.classList.contains('hide-block')).to.be.false; + }); }); }); 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', diff --git a/test/features/personalization/personalization.test.js b/test/features/personalization/personalization.test.js index d2554b7734..b396befa72 100644 --- a/test/features/personalization/personalization.test.js +++ b/test/features/personalization/personalization.test.js @@ -4,7 +4,7 @@ import { assert, stub } from 'sinon'; import { getConfig, setConfig } from '../../../libs/utils/utils.js'; import { handleFragmentCommand, applyPers, - init, matchGlob, createFrag, combineMepSources, + init, matchGlob, createFrag, combineMepSources, buildVariantInfo, } from '../../../libs/features/personalization/personalization.js'; import spoofParams from './spoofParams.js'; import mepSettings from './mepSettings.js'; @@ -175,6 +175,109 @@ describe('Functional Test', () => { expect(getConfig().mep?.martech).to.equal('|fireflies|manifest'); }); + it('should resolve variants correctly with entitlements and tags exist', async () => { + expect(buildVariantInfo(['cc-all-apps-any & desktop'])).to.deep.equal({ + allNames: [ + 'cc-all-apps-any', + 'desktop', + ], + 'cc-all-apps-any & desktop': [ + 'cc-all-apps-any & desktop', + ], + }); + expect(buildVariantInfo(['desktop & cc-all-apps-any'])).to.deep.equal({ + allNames: [ + 'desktop', + 'cc-all-apps-any', + ], + 'desktop & cc-all-apps-any': [ + 'desktop & cc-all-apps-any', + ], + }); + expect(buildVariantInfo(['cc-all-apps-any'])).to.deep.equal({ + allNames: [ + 'cc-all-apps-any', + ], + 'cc-all-apps-any': [ + 'cc-all-apps-any', + ], + }); + expect(buildVariantInfo(['phone, cc-all-apps-any'])).to.deep.equal({ + allNames: [ + 'phone', + 'cc-all-apps-any', + ], + 'phone, cc-all-apps-any': [ + 'phone', + 'cc-all-apps-any', + ], + }); + expect(buildVariantInfo(['cc-all-apps-any, not desktop'])).to.deep.equal({ + allNames: [ + 'cc-all-apps-any', + 'desktop', + ], + 'cc-all-apps-any, not desktop': [ + 'cc-all-apps-any', + 'not desktop', + ], + }); + expect(buildVariantInfo(['phone & not cc-all-apps-any'])).to.deep.equal({ + allNames: [ + 'phone', + 'cc-all-apps-any', + ], + 'phone & not cc-all-apps-any': [ + 'phone & not cc-all-apps-any', + ], + }); + expect(buildVariantInfo(['not phone & not cc-all-apps-any'])).to.deep.equal({ + allNames: [ + 'phone', + 'cc-all-apps-any', + ], + 'not phone & not cc-all-apps-any': [ + 'not phone & not cc-all-apps-any', + ], + }); + expect(buildVariantInfo(['not cc-free & not cc-all-apps-any'])).to.deep.equal({ + allNames: [ + 'cc-free', + 'cc-all-apps-any', + ], + 'not cc-free & not cc-all-apps-any': [ + 'not cc-free & not cc-all-apps-any', + ], + }); + expect(buildVariantInfo(['not cc-free, not cc-all-apps-any'])).to.deep.equal({ + allNames: [ + 'cc-free', + 'cc-all-apps-any', + ], + 'not cc-free, not cc-all-apps-any': [ + 'not cc-free', + 'not cc-all-apps-any', + ], + }); + expect(buildVariantInfo(['not cc-free, not cc-all-apps-any', 'desktop & cc-paid, ios'])).to.deep.equal({ + allNames: [ + 'cc-free', + 'cc-all-apps-any', + 'desktop', + 'cc-paid', + 'ios', + ], + 'not cc-free, not cc-all-apps-any': [ + 'not cc-free', + 'not cc-all-apps-any', + ], + 'desktop & cc-paid, ios': [ + 'desktop & cc-paid', + 'ios', + ], + }); + }); + it('invalid selector should output error to console', async () => { window.console.log = stub(); diff --git a/test/utils/logWebVitals.test.js b/test/utils/logWebVitals.test.js index 01414945b7..e04db9805a 100644 --- a/test/utils/logWebVitals.test.js +++ b/test/utils/logWebVitals.test.js @@ -6,6 +6,14 @@ import logWebVitals from '../../libs/utils/logWebVitals.js'; document.body.innerHTML = await readFile({ path: './mocks/body.html' }); describe('Log Web Vitals', () => { + before(() => { + window.adobePrivacy = { activeCookieGroups: () => ['C0002'] }; + }); + + after(() => { + delete window.adobePrivacy; + }); + it('Logs data to lana', (done) => { window.lana = { log: (logStr, logOpts) => { @@ -40,7 +48,8 @@ describe('Log Web Vitals', () => { done(); }, }; - logWebVitals(mepObject, { delay: 0 }); + logWebVitals(mepObject, { delay: 0, sampleRate: 100 }); + window.dispatchEvent(new Event('adobePrivacy:PrivacyCustom')); }); }); diff --git a/test/utils/logWebVitalsUtils.test.js b/test/utils/logWebVitalsUtils.test.js index 82afe1eb73..3028a4b89b 100644 --- a/test/utils/logWebVitalsUtils.test.js +++ b/test/utils/logWebVitalsUtils.test.js @@ -11,7 +11,20 @@ document.head.innerHTML = ` document.body.innerHTML = await readFile({ path: './mocks/body.html' }); -describe('Log Web Vitals', () => { +describe('Log Web Vitals Utils', () => { + let intervalId; + before(() => { + window.adobePrivacy = { activeCookieGroups: () => ['C0002'] }; + intervalId = setInterval(() => { + window.dispatchEvent(new Event('adobePrivacy:PrivacyCustom')); + }, 100); + }); + + after(() => { + delete window.adobePrivacy; + clearInterval(intervalId); + }); + it('Logs data to lana', (done) => { window.lana = { log: (logStr, logOpts) => {