diff --git a/libs/blocks/fragment/fragment.js b/libs/blocks/fragment/fragment.js
index 2bca02da83..84a922d6f5 100644
--- a/libs/blocks/fragment/fragment.js
+++ b/libs/blocks/fragment/fragment.js
@@ -32,12 +32,6 @@ const updateFragMap = (fragment, a, href) => {
}
};
-const setManifestIdOnChildren = (sections, manifestId) => {
- [...sections[0].children].forEach(
- (child) => (child.dataset.manifestId = manifestId),
- );
-};
-
const insertInlineFrag = (sections, a, relHref) => {
// Inline fragments only support one section, other sections are ignored
const fragChildren = [...sections[0].children];
@@ -79,7 +73,7 @@ export default async function init(a) {
}
const path = new URL(a.href).pathname;
- if (mep?.fragments?.[path] && mep) {
+ if (mep?.fragments?.[path]) {
relHref = mep.handleFragmentCommand(mep?.fragments[path], a);
if (!relHref) return;
}
@@ -122,15 +116,10 @@ export default async function init(a) {
}
updateFragMap(fragment, a, relHref);
-
- if (a.dataset.manifestId) {
- if (inline) {
- setManifestIdOnChildren(sections, a.dataset.manifestId);
- } else {
- fragment.dataset.manifestId = a.dataset.manifestId;
- }
+ if (a.dataset.manifestId || a.dataset.adobeTargetTestid) {
+ const { updateFragDataProps } = await import('../../features/personalization/personalization.js');
+ updateFragDataProps(a, inline, sections, fragment);
}
-
if (inline) {
insertInlineFrag(sections, a, relHref);
} else {
diff --git a/libs/blocks/global-navigation/global-navigation.js b/libs/blocks/global-navigation/global-navigation.js
index dbd6c14f49..0242698af9 100644
--- a/libs/blocks/global-navigation/global-navigation.js
+++ b/libs/blocks/global-navigation/global-navigation.js
@@ -33,7 +33,7 @@ import {
toFragment,
trigger,
yieldToMain,
- addMepHighlight,
+ addMepHighlightAndTargetId,
} from './utilities/utilities.js';
import { replaceKey, replaceKeyArray } from '../../features/placeholders.js';
@@ -919,14 +919,14 @@ class Gnav {
observer.observe(dropdownTrigger, { attributeFilter: ['aria-expanded'] });
delayDropdownDecoration({ template: triggerTemplate });
- return addMepHighlight(triggerTemplate, item);
+ return addMepHighlightAndTargetId(triggerTemplate, item);
}
case 'primaryCta':
case 'secondaryCta':
// Remove its 'em' or 'strong' wrapper
item.parentElement.replaceWith(item);
- return addMepHighlight(toFragment`
+ return addMepHighlightAndTargetId(toFragment`
${decorateCta({ elem: item, type: itemType, index: index + 1 })}
`, item);
case 'link': {
@@ -945,15 +945,15 @@ class Gnav {
${linkElem}
`;
- return addMepHighlight(linkTemplate, item);
+ return addMepHighlightAndTargetId(linkTemplate, item);
}
case 'text':
- return addMepHighlight(toFragment`
+ return addMepHighlightAndTargetId(toFragment`
${item.textContent}
`, item);
default:
/* c8 ignore next 3 */
- return addMepHighlight(toFragment`
+ return addMepHighlightAndTargetId(toFragment`
${item}
`, item);
}
diff --git a/libs/blocks/global-navigation/utilities/menu/menu.js b/libs/blocks/global-navigation/utilities/menu/menu.js
index d5f6d8fabb..aa2a21e704 100644
--- a/libs/blocks/global-navigation/utilities/menu/menu.js
+++ b/libs/blocks/global-navigation/utilities/menu/menu.js
@@ -11,7 +11,7 @@ import {
toFragment,
trigger,
yieldToMain,
- addMepHighlight,
+ addMepHighlightAndTargetId,
} from '../utilities.js';
const decorateHeadline = (elem, index) => {
@@ -317,7 +317,7 @@ const decorateMenu = (config) => logErrorFor(async () => {
${menuContent}
`;
- addMepHighlight(menuTemplate, content);
+ addMepHighlightAndTargetId(menuTemplate, content);
decorateCrossCloudMenu(menuTemplate);
diff --git a/libs/blocks/global-navigation/utilities/utilities.js b/libs/blocks/global-navigation/utilities/utilities.js
index fcc3c24f74..a4c73212f7 100644
--- a/libs/blocks/global-navigation/utilities/utilities.js
+++ b/libs/blocks/global-navigation/utilities/utilities.js
@@ -47,13 +47,12 @@ export const logErrorFor = async (fn, message, tags) => {
}
};
-export function addMepHighlight(el, source) {
- let { manifestId } = source.dataset;
- if (!manifestId) {
- const closestManifestId = source?.closest('[data-manifest-id]');
- if (closestManifestId) manifestId = closestManifestId.dataset.manifestId;
- }
+export function addMepHighlightAndTargetId(el, source) {
+ let { manifestId, targetManifestId } = source.dataset;
+ manifestId ??= source?.closest('[data-manifest-id]')?.dataset?.manifestId;
+ targetManifestId ??= source?.closest('[data-adobe-target-testid]')?.dataset?.adobeTargetTestid;
if (manifestId) el.dataset.manifestId = manifestId;
+ if (targetManifestId) el.dataset.adobeTargetTestid = targetManifestId;
return el;
}
@@ -310,6 +309,7 @@ export async function fetchAndProcessPlainHtml({ url, shouldDecorateLinks = true
const text = await res.text();
const { body } = new DOMParser().parseFromString(text, 'text/html');
if (mepFragment?.manifestId) body.dataset.manifestId = mepFragment.manifestId;
+ if (mepFragment?.targetManifestId) body.dataset.adobeTargetTestid = mepFragment.targetManifestId;
const commands = mepGnav?.commands;
if (commands?.length) {
const { handleCommands, deleteMarkedEls } = await import('../../../features/personalization/personalization.js');
diff --git a/libs/features/personalization/personalization.js b/libs/features/personalization/personalization.js
index cce6c71084..38f38c4195 100644
--- a/libs/features/personalization/personalization.js
+++ b/libs/features/personalization/personalization.js
@@ -1,3 +1,4 @@
+/* eslint-disable no-underscore-dangle */
/* eslint-disable no-console */
import {
@@ -54,8 +55,6 @@ const DATA_TYPE = {
const IN_BLOCK_SELECTOR_PREFIX = 'in-block:';
-export const appendJsonExt = (path) => (path.endsWith('.json') ? path : `${path}.json`);
-
export const normalizePath = (p, localize = true) => {
let path = p;
@@ -87,28 +86,6 @@ export const normalizePath = (p, localize = true) => {
return path;
};
-export const preloadManifests = ({ targetManifests = [], persManifests = [] }) => {
- let manifests = targetManifests;
-
- manifests = manifests.concat(
- persManifests.map((manifest) => ({
- ...manifest,
- manifestPath: normalizePath(appendJsonExt(manifest.manifestPath)),
- manifestUrl: manifest.manifestPath,
- })),
- );
-
- for (const manifest of manifests) {
- if (!manifest.manifestData && manifest.manifestPath && !manifest.disabled) {
- loadLink(
- manifest.manifestPath,
- { as: 'fetch', crossorigin: 'anonymous', rel: 'preload' },
- );
- }
- }
- return manifests;
-};
-
export const getFileName = (path) => path?.split('/').pop();
const isInLcpSection = (el) => {
@@ -116,7 +93,7 @@ const isInLcpSection = (el) => {
return lcpSection === el || lcpSection?.contains(el);
};
-export const createFrag = (el, url, manifestId) => {
+export const createFrag = (el, url, manifestId, targetManifestId) => {
let href = url;
try {
const { pathname, search, hash } = new URL(url);
@@ -126,6 +103,7 @@ export const createFrag = (el, url, manifestId) => {
}
const a = createTag('a', { href }, url);
if (manifestId) a.dataset.manifestId = manifestId;
+ if (targetManifestId) a.dataset.adobeTargetTestid = targetManifestId;
let frag = createTag('p', undefined, a);
const isDelayedModalAnchor = /#.*delay=/.test(href);
if (isDelayedModalAnchor) frag.classList.add('hide-block');
@@ -162,9 +140,9 @@ const COMMANDS = {
}
el.classList.add(CLASS_EL_DELETE);
},
- replace: (el, target, manifestId) => {
+ replace: (el, target, manifestId, targetManifestId) => {
if (!el || el.classList.contains(CLASS_EL_REPLACE)) return;
- el.insertAdjacentElement('beforebegin', createFrag(el, target, manifestId));
+ el.insertAdjacentElement('beforebegin', createFrag(el, target, manifestId, targetManifestId));
el.classList.add(CLASS_EL_DELETE, CLASS_EL_REPLACE);
},
};
@@ -175,7 +153,7 @@ function checkSelectorType(selector) {
const fetchData = async (url, type = DATA_TYPE.JSON) => {
try {
- const resp = await fetch(url);
+ const resp = await fetch(normalizePath(url));
if (!resp.ok) {
/* c8 ignore next 5 */
if (resp.status === 404) {
@@ -227,6 +205,7 @@ const consolidateObjects = (arr, prop, existing = {}) => arr.reduce((propMap, it
selector,
manifestPath: item.manifestPath,
manifestId: i.manifestId,
+ targetManifestId: i.targetManifestId,
};
// eslint-disable-next-line no-restricted-syntax
for (const key in propMap) {
@@ -310,9 +289,9 @@ function getSection(rootEl, idx) {
: rootEl.querySelector(`:scope > div:nth-child(${idx})`);
}
-function registerInBlockActions(cmd, manifestId) {
+function registerInBlockActions(cmd, manifestId, targetManifestId) {
const { action, target, selector } = cmd;
- const command = { action, target, manifestId };
+ const command = { action, target, manifestId, targetManifestId };
const blockAndSelector = selector.substring(IN_BLOCK_SELECTOR_PREFIX.length).trim().split(/\s+/);
const [blockName] = blockAndSelector;
@@ -424,24 +403,39 @@ const addHash = (url, newHash) => {
}
};
+const setDataIdOnChildren = (sections, id, value) => {
+ [...sections[0].children].forEach(
+ (child) => (child.dataset[id] = value),
+ );
+};
+
+export const updateFragDataProps = (a, inline, sections, fragment) => {
+ const { manifestId, adobeTargetTestid } = a.dataset;
+ if (inline) {
+ if (manifestId) setDataIdOnChildren(sections, 'manifestId', manifestId);
+ if (adobeTargetTestid) setDataIdOnChildren(sections, 'adobeTargetTestid', adobeTargetTestid);
+ } else {
+ if (manifestId) fragment.dataset.manifestId = manifestId;
+ if (adobeTargetTestid) fragment.dataset.adobeTargetTestid = adobeTargetTestid;
+ }
+};
export function handleCommands(commands, rootEl = document, forceInline = false) {
commands.forEach((cmd) => {
- const { manifestId } = cmd;
- const { action, selector, target: trgt } = cmd;
+ const { manifestId, targetManifestId, action, selector, target: trgt } = cmd;
const target = forceInline ? addHash(trgt, INLINE_HASH) : trgt;
if (selector.startsWith(IN_BLOCK_SELECTOR_PREFIX)) {
- registerInBlockActions(cmd, manifestId);
+ registerInBlockActions(cmd, manifestId, targetManifestId);
return;
}
if (action in COMMANDS) {
const el = getSelectedElement(selector, action, rootEl);
- COMMANDS[action](el, target, manifestId);
+ COMMANDS[action](el, target, manifestId, targetManifestId);
} else if (action in CREATE_CMDS) {
const el = getSelectedElement(selector, action, rootEl);
el?.insertAdjacentElement(
CREATE_CMDS[action],
- createFrag(el, target, manifestId),
+ createFrag(el, target, manifestId, targetManifestId),
);
} else {
/* c8 ignore next 2 */
@@ -450,14 +444,21 @@ export function handleCommands(commands, rootEl = document, forceInline = false)
});
}
-const getVariantInfo = (line, variantNames, variants, manifestId) => {
+const getVariantInfo = (line, variantNames, variants, manifestPath, manifestOverrideName) => {
+ const config = getConfig();
+ let manifestId = getFileName(manifestPath);
+ let targetId = manifestId.replace('.json', '');
+ if (manifestOverrideName) targetId = manifestOverrideName;
+ if (!config.mep?.preview) manifestId = false;
const action = line.action?.toLowerCase().replace('content', '').replace('fragment', '');
const { selector } = line;
const pageFilter = line['page filter'] || line['page filter optional'];
if (pageFilter && !matchGlob(pageFilter, new URL(window.location).pathname)) return;
+ if (!config.mep?.preview) manifestId = false;
variantNames.forEach((vn) => {
+ const targetManifestId = vn.startsWith(TARGET_EXP_PREFIX) ? targetId : false;
if (!line[vn] || line[vn].toLowerCase() === 'false') return;
const variantInfo = {
@@ -467,6 +468,7 @@ const getVariantInfo = (line, variantNames, variants, manifestId) => {
target: line[vn],
selectorType: checkSelectorType(selector),
manifestId,
+ targetManifestId,
};
if (action in COMMANDS && variantInfo.selectorType === 'fragment') {
@@ -475,6 +477,7 @@ const getVariantInfo = (line, variantNames, variants, manifestId) => {
val: normalizePath(line[vn]),
action,
manifestId,
+ targetManifestId,
});
} else if (GLOBAL_CMDS.includes(action)) {
variants[vn][action] = variants[vn][action] || [];
@@ -486,6 +489,7 @@ const getVariantInfo = (line, variantNames, variants, manifestId) => {
val: blockTarget,
pageFilter,
manifestId,
+ targetManifestId,
});
} else {
variants[vn][action].push({
@@ -493,6 +497,7 @@ const getVariantInfo = (line, variantNames, variants, manifestId) => {
val: normalizePath(line[vn]),
pageFilter,
manifestId,
+ targetManifestId,
});
}
} else if (action in COMMANDS || action in CREATE_CMDS) {
@@ -504,7 +509,7 @@ const getVariantInfo = (line, variantNames, variants, manifestId) => {
});
};
-export function parseManifestVariants(data, manifestId) {
+export function parseManifestVariants(data, manifestPath, manifestOverrideName) {
if (!data?.length) return null;
const manifestConfig = {};
@@ -519,10 +524,15 @@ export function parseManifestVariants(data, manifestId) {
variants[vn] = { commands: [], fragments: [] };
});
- experiences.forEach((line) => getVariantInfo(line, variantNames, variants, manifestId));
+ experiences.forEach((line) => {
+ getVariantInfo(line, variantNames, variants, manifestPath, manifestOverrideName);
+ });
manifestConfig.variants = variants;
manifestConfig.variantNames = variantNames;
+ const config = getConfig();
+ if (!config.mep?.preview) manifestConfig.manifestId = false;
+
return manifestConfig;
} catch (e) {
/* c8 ignore next 3 */
@@ -622,6 +632,23 @@ const createDefaultExperiment = (manifest) => ({
variants: {},
});
+export const addMepAnalytics = (config, header) => {
+ config.mep.experiments.forEach((experiment) => {
+ experiment?.selectedVariant?.useblockcode?.forEach(({ selector, targetManifestId }) => {
+ if (selector && targetManifestId) {
+ document.querySelectorAll(`.${selector}`)
+ .forEach((el) => (el.dataset.adobeTargetTestid = targetManifestId));
+ }
+ });
+ if (header) {
+ experiment?.selectedVariant?.updatemetadata?.forEach((updateMetaData) => {
+ if (updateMetaData?.selector === 'gnav-source' && updateMetaData.targetManifestId) {
+ header.dataset.adobeTargetTestid = updateMetaData.targetManifestId;
+ }
+ });
+ }
+ });
+};
export async function getManifestConfig(info, variantOverride = false) {
const {
name,
@@ -634,7 +661,7 @@ export async function getManifestConfig(info, variantOverride = false) {
disabled,
event,
} = info;
- if (disabled && !variantOverride) {
+ if (disabled && (!variantOverride || !Object.keys(variantOverride).length)) {
return createDefaultExperiment(info);
}
let data = manifestData;
@@ -645,33 +672,25 @@ export async function getManifestConfig(info, variantOverride = false) {
const persData = data?.experiences?.data || data?.data || data;
if (!persData) return null;
-
- let manifestId = getFileName(manifestPath);
- const config = getConfig();
- if (!config.mep?.preview) {
- manifestId = false;
- } else if (name) {
- manifestId = `${name}: ${manifestId}`;
- }
- const manifestConfig = parseManifestVariants(persData, manifestId);
+ const infoTab = manifestInfo || data?.info?.data;
+ const infoObj = infoTab?.reduce((acc, item) => {
+ acc[item.key] = item.value;
+ return acc;
+ }, {});
+ const manifestOverrideName = name || infoObj?.['manifest-override-name']?.toLowerCase();
+ const manifestConfig = parseManifestVariants(persData, manifestPath, manifestOverrideName);
if (!manifestConfig) {
/* c8 ignore next 3 */
console.log('Error loading personalization manifestConfig: ', name || manifestPath);
return null;
}
-
- const infoTab = manifestInfo || data?.info?.data;
const infoKeyMap = {
'manifest-type': ['Personalization', 'Promo', 'Test'],
'manifest-execution-order': ['First', 'Normal', 'Last'],
};
if (infoTab) {
- const infoObj = infoTab?.reduce((acc, item) => {
- acc[item.key] = item.value;
- return acc;
- }, {});
- manifestConfig.manifestOverrideName = infoObj?.['manifest-override-name']?.toLowerCase();
+ manifestConfig.manifestOverrideName = manifestOverrideName;
manifestConfig.manifestType = infoObj?.['manifest-type']?.toLowerCase();
const executionOrder = {
'manifest-type': 1,
@@ -705,7 +724,6 @@ export async function getManifestConfig(info, variantOverride = false) {
manifestConfig.selectedVariantName = 'default';
manifestConfig.selectedVariant = 'default';
}
-
const placeholders = manifestPlaceholders || data?.placeholders?.data;
if (placeholders) {
updateConfig(
@@ -726,22 +744,22 @@ export const deleteMarkedEls = (rootEl = document) => {
.forEach((el) => el.remove());
};
-const normalizeFragPaths = ({ selector, val, action }) => ({
+const normalizeFragPaths = ({ selector, val, action, manifestId, targetManifestId }) => ({
selector: normalizePath(selector),
val: normalizePath(val),
action,
+ manifestId,
+ targetManifestId,
});
-
-export async function categorizeActions(experiment) {
+export async function categorizeActions(experiment, config) {
if (!experiment) return null;
const { manifestPath, selectedVariant } = experiment;
if (!selectedVariant || selectedVariant === 'default') return { experiment };
- if (selectedVariant.replacepage) {
- // only one replacepage can be defined
- await replaceInner(selectedVariant.replacepage[0]?.val, document.querySelector('main'));
- document.querySelector('main').dataset.manifestId = manifestPath;
- }
+ // only one replacepage can be defined
+ const { replacepage } = selectedVariant;
+ // eslint-disable-next-line prefer-destructuring
+ if (selectedVariant.replacepage?.length) config.mep.replacepage = replacepage[0];
selectedVariant.insertscript?.map((script) => loadScript(script.val));
selectedVariant.updatemetadata?.map((metadata) => setMetadata(metadata));
@@ -805,14 +823,18 @@ export function cleanAndSortManifestList(manifests) {
window.lana?.log(`MEP Error parsing manifests: ${e.toString()}`);
}
});
+ Object.keys(manifestObj).forEach((key) => {
+ delete manifestObj[key].variants;
+ });
return Object.values(manifestObj).sort(compareExecutionOrder);
}
export function handleFragmentCommand(command, a) {
- const { action, fragment, manifestId } = command;
+ const { action, fragment, manifestId, targetManifestId } = command;
if (action === 'replace') {
a.href = fragment;
if (manifestId) a.dataset.manifestId = manifestId;
+ if (targetManifestId) a.dataset.adobeTargetTestid = targetManifestId;
return fragment;
}
if (action === 'remove') {
@@ -826,64 +848,125 @@ export function handleFragmentCommand(command, a) {
}
export async function applyPers(manifests, postLCP = false) {
- try {
- const config = getConfig();
- if (!postLCP) {
- const {
- mep: mepParam,
- mepHighlight,
- mepButton,
- } = Object.fromEntries(PAGE_URL.searchParams);
- config.mep = {
- handleFragmentCommand,
- preview: (mepButton !== 'off'
- && (config.env?.name !== 'prod' || mepParam || mepParam === '' || mepButton)),
- variantOverride: parseMepParam(mepParam),
- highlight: (mepHighlight !== undefined && mepHighlight !== 'false'),
- mepParam,
- targetEnabled: config.mep?.targetEnabled,
- };
- }
+ if (!manifests?.length) return;
+ let experiments = manifests;
+ const config = getConfig();
+ for (let i = 0; i < experiments.length; i += 1) {
+ experiments[i] = await getManifestConfig(experiments[i], config.mep?.variantOverride);
+ }
- if (!manifests?.length) return;
- let experiments = manifests;
- for (let i = 0; i < experiments.length; i += 1) {
- experiments[i] = await getManifestConfig(experiments[i], config.mep?.variantOverride);
- }
+ experiments = cleanAndSortManifestList(experiments);
- experiments = cleanAndSortManifestList(experiments);
+ let results = [];
- let results = [];
+ for (const experiment of experiments) {
+ const result = await categorizeActions(experiment, config);
+ if (result) results.push(result);
+ }
+ results = results.filter(Boolean);
- for (const experiment of experiments) {
- const result = await categorizeActions(experiment);
- if (result) {
- results.push(result);
- }
- }
- results = results.filter(Boolean);
+ config.mep.experiments = [...config.mep.experiments, ...experiments];
+ config.mep.blocks = consolidateObjects(results, 'blocks', config.mep.blocks);
+ config.mep.fragments = consolidateObjects(results, 'fragments', config.mep.fragments);
+ config.mep.commands = consolidateArray(results, 'commands', config.mep.commands);
- config.mep.experiments ??= [];
- config.mep.experiments = experiments;
- config.mep.blocks = consolidateObjects(results, 'blocks', config.mep.blocks);
- config.mep.fragments = consolidateObjects(results, 'fragments', config.mep.fragments);
- config.mep.commands = consolidateArray(results, 'commands', config.mep.commands);
+ const main = document.querySelector('main');
+ if (config.mep.replacepage && !postLCP && main) {
+ await replaceInner(config.mep.replacepage.val, main);
+ const { manifestId, targetManifestId } = config.mep.replacepage;
+ if (manifestId) main.dataset.manifestId = manifestId;
+ if (targetManifestId) main.dataset.adobeTargetTestid = targetManifestId;
+ }
- if (!postLCP) handleCommands(config.mep.commands);
- deleteMarkedEls();
+ if (!postLCP) handleCommands(config.mep.commands);
+ deleteMarkedEls();
- const pznList = results.filter((r) => (r.experiment?.manifestType === TRACKED_MANIFEST_TYPE));
- if (!pznList.length) return;
+ const pznList = results.filter((r) => (r.experiment?.manifestType === TRACKED_MANIFEST_TYPE));
+ if (!pznList.length) return;
- const pznVariants = pznList.map((r) => {
- const val = r.experiment.selectedVariantName.replace(TARGET_EXP_PREFIX, '').trim().slice(0, 15);
- return val === 'default' ? 'nopzn' : val;
+ const pznVariants = pznList.map((r) => {
+ const val = r.experiment.selectedVariantName.replace(TARGET_EXP_PREFIX, '').trim().slice(0, 15);
+ return val === 'default' ? 'nopzn' : val;
+ });
+ const pznManifests = pznList.map((r) => {
+ const val = r.experiment?.manifestOverrideName || r.experiment?.manifest;
+ return getFileName(val).replace('.json', '').trim().slice(0, 15);
+ });
+ config.mep.martech = `|${pznVariants.join('--')}|${pznManifests.join('--')}`;
+}
+
+export const combineMepSources = async (persEnabled, promoEnabled, mepParam) => {
+ let persManifests = [];
+
+ if (persEnabled) {
+ persManifests = persEnabled.toLowerCase()
+ .split(/,|(\s+)|(\\n)/g)
+ .filter((path) => path?.trim())
+ .map((manifestPath) => ({ manifestPath }));
+ }
+
+ if (promoEnabled) {
+ const { default: getPromoManifests } = await import('./promo-utils.js');
+ persManifests = persManifests.concat(getPromoManifests(promoEnabled, PAGE_URL.searchParams));
+ }
+
+ if (mepParam && mepParam !== 'off') {
+ const persManifestPaths = persManifests.map((manifest) => {
+ const { manifestPath } = manifest;
+ if (manifestPath.startsWith('/')) return manifestPath;
+ try {
+ const url = new URL(manifestPath);
+ return url.pathname;
+ } catch (e) {
+ return manifestPath;
+ }
});
- const pznManifests = pznList.map((r) => {
- const val = r.experiment?.manifestOverrideName || r.experiment?.manifest;
- return getFileName(val).replace('.json', '').trim().slice(0, 15);
+
+ mepParam.split('---').forEach((manifestPair) => {
+ const manifestPath = manifestPair.trim().toLowerCase().split('--')[0];
+ if (!persManifestPaths.includes(manifestPath)) {
+ persManifests.push({ manifestPath });
+ }
+ });
+ }
+ return persManifests;
+};
+
+export async function init(enablements = {}) {
+ let manifests = [];
+ const {
+ mepParam, mepHighlight, mepButton, pzn, promo, target, postLCP,
+ } = enablements;
+ const config = getConfig();
+ if (!postLCP) {
+ config.mep = {
+ handleFragmentCommand,
+ updateFragDataProps,
+ preview: (mepButton !== 'off'
+ && (config.env?.name !== 'prod' || mepParam || mepParam === '' || mepButton)),
+ variantOverride: parseMepParam(mepParam),
+ highlight: (mepHighlight !== undefined && mepHighlight !== 'false'),
+ targetEnabled: target,
+ experiments: [],
+ };
+ manifests = manifests.concat(await combineMepSources(pzn, promo, mepParam));
+ manifests?.forEach((manifest) => {
+ if (manifest.disabled) return;
+ const normalizedURL = normalizePath(manifest.manifestPath);
+ loadLink(normalizedURL, { as: 'fetch', crossorigin: 'anonymous', rel: 'preload' });
});
- config.mep.martech = `|${pznVariants.join('--')}|${pznManifests.join('--')}`;
+ }
+
+ if (target === true || (target === 'gnav' && postLCP)) {
+ const { getTargetPersonalization } = await import('../../martech/martech.js');
+ const { targetManifests, targetPropositions } = await getTargetPersonalization();
+ manifests = manifests.concat(targetManifests);
+ if (targetPropositions?.length && window._satellite) {
+ window._satellite.track('propositionDisplay', targetPropositions);
+ }
+ }
+ try {
+ await applyPers(manifests, postLCP);
} catch (e) {
console.warn(e);
window.lana?.log(`MEP Error: ${e.toString()}`);
diff --git a/libs/martech/martech.js b/libs/martech/martech.js
index 1d78f7c089..455539c1d3 100644
--- a/libs/martech/martech.js
+++ b/libs/martech/martech.js
@@ -1,4 +1,3 @@
-/* eslint-disable no-underscore-dangle */
import { getConfig, getMetadata, loadIms, loadLink, loadScript } from '../utils/utils.js';
const ALLOY_SEND_EVENT = 'alloy_sendEvent';
@@ -47,16 +46,6 @@ const waitForEventOrTimeout = (eventName, timeout, returnValIfTimeout) => new Pr
window.addEventListener(ALLOY_SEND_EVENT_ERROR, errorListener, { once: true });
});
-const getExpFromParam = (expParam) => {
- const lastSlash = expParam.lastIndexOf('/');
- return {
- experiments: [{
- experimentPath: expParam.substring(0, lastSlash),
- variantLabel: expParam.substring(lastSlash + 1),
- }],
- };
-};
-
const handleAlloyResponse = (response) => {
const items = (
(response.propositions?.length && response.propositions)
@@ -116,12 +105,9 @@ function sendTargetResponseAnalytics(failure, responseStart, timeout, message) {
});
}
-const getTargetPersonalization = async () => {
+export const getTargetPersonalization = async () => {
const params = new URL(window.location.href).searchParams;
- const experimentParam = params.get('experiment');
- if (experimentParam) return getExpFromParam(experimentParam);
-
const timeout = parseInt(params.get('target-timeout'), 10)
|| parseInt(getMetadata('target-timeout'), 10)
|| TARGET_TIMEOUT_MS;
@@ -249,7 +235,7 @@ const loadMartechFiles = async (config) => {
? '/marketingtech/main.standard.min.js'
: '/marketingtech/main.standard.qa.min.js'
));
-
+ // eslint-disable-next-line no-underscore-dangle
window._satellite.track('pageload');
};
@@ -257,30 +243,8 @@ const loadMartechFiles = async (config) => {
return filesLoadedPromise;
};
-export default async function init({
- persEnabled = false,
- persManifests = [],
- postLCP = false,
-}) {
+export default async function init() {
const config = getConfig();
const martechPromise = loadMartechFiles(config);
-
- if (persEnabled) {
- loadLink(
- `${config.miloLibs || config.codeRoot}/features/personalization/personalization.js`,
- { as: 'script', rel: 'modulepreload' },
- );
-
- const { targetManifests, targetPropositions } = await getTargetPersonalization();
- if (targetManifests?.length || persManifests?.length) {
- const { preloadManifests, applyPers } = await import('../features/personalization/personalization.js');
- const manifests = preloadManifests({ targetManifests, persManifests });
- await applyPers(manifests, postLCP);
- if (targetPropositions?.length && window._satellite) {
- window._satellite.track('propositionDisplay', targetPropositions);
- }
- }
- }
-
return martechPromise;
}
diff --git a/libs/mep/mwpw-142224/marquee/marquee.js b/libs/mep/mwpw-142224/marquee/marquee.js
deleted file mode 100644
index e7742cca13..0000000000
--- a/libs/mep/mwpw-142224/marquee/marquee.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function init(el) {
- el.innerHTML = 'Marquee was replaced MEP and the content were overwritten.';
-}
diff --git a/libs/mep/mwpw-142224/marquee/marquee.css b/libs/mep/sample-block-code/marquee/marquee.css
similarity index 100%
rename from libs/mep/mwpw-142224/marquee/marquee.css
rename to libs/mep/sample-block-code/marquee/marquee.css
diff --git a/libs/mep/sample-block-code/marquee/marquee.js b/libs/mep/sample-block-code/marquee/marquee.js
new file mode 100644
index 0000000000..f322c6e100
--- /dev/null
+++ b/libs/mep/sample-block-code/marquee/marquee.js
@@ -0,0 +1,3 @@
+export default function init(el) {
+ el.innerHTML = 'Marquee code was replaced MEP and the content was overwritten.';
+}
diff --git a/libs/utils/utils.js b/libs/utils/utils.js
index c4f4a6f64b..5feb6d18c7 100644
--- a/libs/utils/utils.js
+++ b/libs/utils/utils.js
@@ -922,81 +922,36 @@ export const getMepEnablement = (mdKey, paramKey = false) => {
return getMdValue(mdKey);
};
-export const combineMepSources = async (persEnabled, promoEnabled, mepParam) => {
- let persManifests = [];
-
- if (persEnabled) {
- persManifests = persEnabled.toLowerCase()
- .split(/,|(\s+)|(\\n)/g)
- .filter((path) => path?.trim())
- .map((manifestPath) => ({ manifestPath }));
- }
-
- if (promoEnabled) {
- const { default: getPromoManifests } = await import('../features/personalization/promo-utils.js');
- persManifests = persManifests.concat(getPromoManifests(promoEnabled, PAGE_URL.searchParams));
- }
-
- if (mepParam && mepParam !== 'off') {
- const persManifestPaths = persManifests.map((manifest) => {
- const { manifestPath } = manifest;
- if (manifestPath.startsWith('/')) return manifestPath;
- try {
- const url = new URL(manifestPath);
- return url.pathname;
- } catch (e) {
- return manifestPath;
- }
- });
-
- mepParam.split('---').forEach((manifestPair) => {
- const manifestPath = manifestPair.trim().toLowerCase().split('--')[0];
- if (!persManifestPaths.includes(manifestPath)) {
- persManifests.push({ manifestPath });
- }
- });
- }
- return persManifests;
-};
-
async function checkForPageMods() {
- const { mep: mepParam } = Object.fromEntries(PAGE_URL.searchParams);
+ const { mep: mepParam, mepHighlight, mepButton } = Object.fromEntries(PAGE_URL.searchParams);
if (mepParam === 'off') return;
- const persEnabled = getMepEnablement('personalization');
- const promoEnabled = getMepEnablement('manifestnames', PROMO_PARAM);
- const targetEnabled = getMepEnablement('target');
- const mepEnabled = persEnabled || targetEnabled || promoEnabled || mepParam;
- if (!mepEnabled) return;
-
- const config = getConfig();
- config.mep = { targetEnabled };
- loadLink(
- `${config.base}/features/personalization/personalization.js`,
- { as: 'script', rel: 'modulepreload' },
- );
-
- const persManifests = await combineMepSources(persEnabled, promoEnabled, mepParam);
- if (targetEnabled === true) {
- await loadMartech({ persEnabled: true, persManifests, targetEnabled });
- return;
+ const pzn = getMepEnablement('personalization');
+ const promo = getMepEnablement('manifestnames', PROMO_PARAM);
+ const target = getMepEnablement('target');
+ if (!(pzn || target || promo || mepParam
+ || mepHighlight || mepButton || mepParam === '')) return;
+ if (target) {
+ loadMartech();
+ } else if (pzn) {
+ loadIms()
+ .then(() => {
+ /* c8 ignore next */
+ if (window.adobeIMS?.isSignedInUser()) loadMartech();
+ })
+ .catch((e) => { console.log('Unable to load IMS:', e); });
}
- if (!persManifests.length) return;
- loadIms()
- .then(() => {
- if (window.adobeIMS.isSignedInUser()) loadMartech();
- })
- .catch((e) => { console.log('Unable to load IMS:', e); });
-
- const { preloadManifests, applyPers } = await import('../features/personalization/personalization.js');
- const manifests = preloadManifests({ persManifests }, { getConfig, loadLink });
-
- await applyPers(manifests);
+ const { init } = await import('../features/personalization/personalization.js');
+ await init({
+ mepParam, mepHighlight, mepButton, pzn, promo, target,
+ });
}
async function loadPostLCP(config) {
if (config.mep?.targetEnabled === 'gnav') {
- await loadMartech({ persEnabled: true, postLCP: true });
+ /* c8 ignore next 2 */
+ const { init } = await import('../features/personalization/personalization.js');
+ await init({ postLCP: true });
} else {
loadMartech();
}
@@ -1014,6 +969,11 @@ async function loadPostLCP(config) {
loadTemplate();
const { default: loadFonts } = await import('./fonts.js');
loadFonts(config.locale, loadStyle);
+
+ if (config?.mep) {
+ import('../features/personalization/personalization.js')
+ .then(({ addMepAnalytics }) => addMepAnalytics(config, header));
+ }
if (config.mep?.preview) {
import('../features/personalization/preview.js')
.then(({ default: decoratePreviewMode }) => decoratePreviewMode());
diff --git a/test/features/personalization/actions.test.js b/test/features/personalization/actions.test.js
index e9bce4b9c0..fe9e935202 100644
--- a/test/features/personalization/actions.test.js
+++ b/test/features/personalization/actions.test.js
@@ -3,8 +3,8 @@ import { readFile } from '@web/test-runner-commands';
import { stub } from 'sinon';
import { getConfig, loadBlock } from '../../../libs/utils/utils.js';
import initFragments from '../../../libs/blocks/fragment/fragment.js';
-import { applyPers, handleFragmentCommand } from '../../../libs/features/personalization/personalization.js';
-import spoofParams from './spoofParams.js';
+import { init, handleFragmentCommand } from '../../../libs/features/personalization/personalization.js';
+import mepSettings from './mepSettings.js';
document.head.innerHTML = await readFile({ path: './mocks/metadata.html' });
document.body.innerHTML = await readFile({ path: './mocks/personalization.html' });
@@ -35,10 +35,12 @@ describe('replace action', () => {
expect(document.querySelector('.how-to')).to.not.be.null;
const parentEl = document.querySelector('#features-of-milo-experimentation-platform')?.parentElement;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
expect(document.querySelector('#features-of-milo-experimentation-platform')).to.be.null;
- expect(parentEl.firstElementChild.firstElementChild.href)
+ const el = parentEl.firstElementChild.firstElementChild;
+ expect(el.href)
.to.equal('http://localhost:2000/test/features/personalization/mocks/fragments/milo-replace-content-chrome-howto-h2');
+ expect(getConfig().mep.commands[0].targetManifestId).to.equal(false);
// .how-to should not be changed as it is targeted to firefox
expect(document.querySelector('.how-to')).to.not.be.null;
});
@@ -52,7 +54,8 @@ describe('replace action', () => {
expect(document.querySelector('a[href="/fragments/replaceme"]')).to.exist;
expect(document.querySelector('a[href="/fragments/inline-replaceme#_inline"]')).to.exist;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
+ expect(getConfig().mep.commands[0].targetManifestId).to.equal(false);
const fragmentResp = await readFile({ path: './mocks/fragments/fragmentReplaced.plain.html' });
const inlineFragmentResp = await readFile({ path: './mocks/fragments/inlineFragReplaced.plain.html' });
@@ -83,7 +86,8 @@ describe('insertAfter action', async () => {
expect(document.querySelector('a[href="/fragments/insertafter"]')).to.be.null;
expect(document.querySelector('a[href="/fragments/insertafterfragment"]')).to.be.null;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
+ expect(getConfig().mep.commands[0].targetManifestId).to.equal(false);
let fragment = document.querySelector('a[href="/test/features/personalization/mocks/fragments/insertafter"]');
expect(fragment).to.not.be.null;
@@ -105,7 +109,8 @@ describe('insertBefore action', async () => {
setFetchResponse(manifestJson);
expect(document.querySelector('a[href="/fragments/insertbefore"]')).to.be.null;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
+ expect(getConfig().mep.commands[0].targetManifestId).to.equal(false);
let fragment = document.querySelector('a[href="/test/features/personalization/mocks/fragments/insertbefore"]');
expect(fragment).to.not.be.null;
@@ -127,7 +132,8 @@ describe('prependToSection action', async () => {
setFetchResponse(manifestJson);
expect(document.querySelector('a[href="/test/features/personalization/mocks/fragments/prependToSection"]')).to.be.null;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
+ expect(getConfig().mep.commands[0].targetManifestId).to.equal(false);
const fragment = document.querySelector('main > div:nth-child(2) > div:first-child a[href="/test/features/personalization/mocks/fragments/prependToSection"]');
expect(fragment).to.not.be.null;
@@ -143,7 +149,8 @@ describe('appendToSection action', async () => {
setFetchResponse(manifestJson);
expect(document.querySelector('a[href="/test/features/personalization/mocks/fragments/appendToSection"]')).to.be.null;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
+ expect(getConfig().mep.commands[0].targetManifestId).to.equal(false);
const fragment = document.querySelector('main > div:nth-child(2) > div:last-child a[href="/test/features/personalization/mocks/fragments/appendToSection"]');
expect(fragment).to.not.be.null;
@@ -155,7 +162,9 @@ describe('remove action', () => {
let manifestJson = await readFile({ path: './mocks/actions/manifestRemove.json' });
manifestJson = JSON.parse(manifestJson);
setFetchResponse(manifestJson);
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ mepSettings.mepButton = 'off';
+ await init(mepSettings);
+ expect(getConfig().mep.commands[0].targetManifestId).to.equal(false);
});
it('remove should remove content', async () => {
expect(document.querySelector('.z-pattern')).to.be.null;
@@ -168,7 +177,6 @@ describe('remove action', () => {
});
it('removeContent should tag but not remove content in preview', async () => {
- spoofParams({ mep: '' });
document.body.innerHTML = await readFile({ path: './mocks/personalization.html' });
let manifestJson = await readFile({ path: './mocks/actions/manifestRemove.json' });
@@ -177,7 +185,10 @@ describe('remove action', () => {
setTimeout(async () => {
expect(document.querySelector('.z-pattern')).to.not.be.null;
- await applyPers([{ manifestPath: '/mocks/manifestRemove.json' }]);
+ mepSettings.mepButton = false;
+ await init(mepSettings);
+ expect(getConfig().mep.commands[0].targetManifestId).to.equal(false);
+
expect(document.querySelector('.z-pattern')).to.not.be.null;
expect(document.querySelector('.z-pattern').dataset.removedManifestId).to.not.be.null;
@@ -195,7 +206,9 @@ describe('useBlockCode action', async () => {
manifestJson = JSON.parse(manifestJson);
setFetchResponse(manifestJson);
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
+ expect(getConfig().mep.experiments[0].selectedVariant.useblockcode[0]
+ .targetManifestId).to.equal(false);
expect(getConfig().mep.blocks).to.deep.equal({ promo: 'http://localhost:2000/test/features/personalization/mocks/promo' });
const promoBlock = document.querySelector('.promo');
@@ -209,7 +222,9 @@ describe('useBlockCode action', async () => {
manifestJson = JSON.parse(manifestJson);
setFetchResponse(manifestJson);
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
+ expect(getConfig().mep.experiments[0].selectedVariant.useblockcode[0]
+ .targetManifestId).to.equal(false);
expect(getConfig().mep.blocks).to.deep.equal({ myblock: 'http://localhost:2000/test/features/personalization/mocks/myblock' });
const myBlock = document.querySelector('.myblock');
@@ -224,7 +239,8 @@ describe('custom actions', async () => {
let manifestJson = await readFile({ path: './mocks/actions/manifestReplace.json' });
manifestJson = JSON.parse(manifestJson);
setFetchResponse(manifestJson);
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
+ expect(getConfig().mep.commands[0].targetManifestId).to.equal(false);
expect(getConfig().mep.custom).to.be.undefined;
});
@@ -233,31 +249,34 @@ describe('custom actions', async () => {
manifestJson = JSON.parse(manifestJson);
setFetchResponse(manifestJson);
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
- console.log(getConfig().mep.inBlock);
+ await init(mepSettings);
expect(getConfig().mep.inBlock).to.deep.equal({
'my-block': {
commands: [{
action: 'replace',
target: '/fragments/fragmentreplaced',
manifestId: false,
+ targetManifestId: false,
},
{
action: 'replace',
target: '/fragments/new-large-menu',
manifestId: false,
selector: '.large-menu',
+ targetManifestId: false,
}],
fragments: {
'/fragments/sub-menu': {
action: 'replace',
target: '/fragments/even-more-new-sub-menu',
manifestId: false,
+ targetManifestId: false,
},
'/fragments/new-sub-menu': {
action: 'replace',
target: '/fragments/even-more-new-sub-menu',
manifestId: false,
+ targetManifestId: false,
},
},
},
@@ -280,7 +299,7 @@ describe('custom actions', async () => {
expect(document.querySelector(lcpLink)).not.to.exist;
expect(document.querySelector(notLcpLink)).not.to.exist;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
expect(document.querySelector(lcpLink)).to.exist;
expect(document.querySelector(notLcpLink)).not.to.exist;
diff --git a/test/features/personalization/actionsTargetManifestId.test.js b/test/features/personalization/actionsTargetManifestId.test.js
new file mode 100644
index 0000000000..7345cabb8b
--- /dev/null
+++ b/test/features/personalization/actionsTargetManifestId.test.js
@@ -0,0 +1,164 @@
+import { expect } from '@esm-bundle/chai';
+import { readFile } from '@web/test-runner-commands';
+import { stub } from 'sinon';
+import { getConfig } from '../../../libs/utils/utils.js';
+import initFragments from '../../../libs/blocks/fragment/fragment.js';
+import { init, handleFragmentCommand, addMepAnalytics } from '../../../libs/features/personalization/personalization.js';
+import mepSettings from './mepTargetSettings.js';
+
+document.head.innerHTML = await readFile({ path: './mocks/metadata.html' });
+document.body.innerHTML = await readFile({ path: './mocks/personalization.html' });
+
+// Add custom keys so tests doesn't rely on real data
+const config = getConfig();
+config.env = { name: 'prod' };
+
+const getFetchPromise = (data, type = 'json') => new Promise((resolve) => {
+ resolve({
+ ok: true,
+ [type]: () => data,
+ });
+});
+
+const setFetchResponse = (data, type = 'json') => {
+ window.fetch = stub().returns(getFetchPromise(data, type));
+};
+
+// Note that the manifestPath doesn't matter as we stub the fetch
+describe('replace action', () => {
+ it('with a CSS Selector, it should replace an element with a fragment', async () => {
+ let manifestJson = await readFile({ path: './mocks/actions/manifestReplace.json' });
+ manifestJson = JSON.parse(manifestJson);
+ setFetchResponse(manifestJson);
+ await init(mepSettings);
+ expect(getConfig().mep.commands[0].targetManifestId).to.equal('manifest');
+ const el = document.querySelector('a[href="/test/features/personalization/mocks/fragments/milo-replace-content-chrome-howto-h2"]');
+ expect(el.dataset.adobeTargetTestid).to.equal('manifest');
+ });
+
+ it('with a fragment selector, it should replace a fragment in the document', async () => {
+ document.body.innerHTML = await readFile({ path: './mocks/personalization.html' });
+
+ let manifestJson = await readFile({ path: './mocks/actions/manifestReplace.json' });
+ manifestJson = JSON.parse(manifestJson);
+ setFetchResponse(manifestJson);
+
+ await init(mepSettings);
+ expect(getConfig().mep.fragments['/fragments/replaceme'].targetManifestId).to.equal('manifest');
+ const el = document.querySelector('a[href="/test/features/personalization/mocks/fragments/milo-replace-content-chrome-howto-h2"]');
+ expect(el.dataset.adobeTargetTestid).to.equal('manifest');
+ const fragmentResp = await readFile({ path: './mocks/fragments/fragmentReplaced.plain.html' });
+ const inlineFragmentResp = await readFile({ path: './mocks/fragments/inlineFragReplaced.plain.html' });
+ window.fetch = stub();
+ window.fetch.withArgs('http://localhost:2000/test/features/personalization/mocks/fragments/fragmentReplaced.plain.html')
+ .returns(getFetchPromise(fragmentResp, 'text'));
+ window.fetch.withArgs('http://localhost:2000/test/features/personalization/mocks/fragments/inlineFragReplaced.plain.html')
+ .returns(getFetchPromise(inlineFragmentResp, 'text'));
+ const replacemeFrag = document.querySelector('a[href="/fragments/replaceme"]');
+ await initFragments(replacemeFrag);
+ expect(document.querySelector('a[href="/fragments/replaceme"]')).to.be.null;
+ expect(document.querySelector('div[data-path="/test/features/personalization/mocks/fragments/fragmentReplaced"]')).to.exist;
+
+ const inlineReplacemeFrag = document.querySelector('a[href="/fragments/inline-replaceme#_inline"]');
+ await initFragments(inlineReplacemeFrag);
+ expect(document.querySelector('a[href="/fragments/inline-replaceme#_inline"]')).to.be.null;
+ expect(document.querySelector('.inlinefragmentreplaced')).to.exist;
+ });
+});
+
+describe('insertAfter action', async () => {
+ it('insertContentAfter should add fragment after target content and fragment', async () => {
+ let manifestJson = await readFile({ path: './mocks/actions/manifestInsertAfter.json' });
+ manifestJson = JSON.parse(manifestJson);
+ setFetchResponse(manifestJson);
+
+ await init(mepSettings);
+ expect(getConfig().mep.commands[0].targetManifestId).to.equal('manifest');
+ const el = document.querySelector('a[href="/test/features/personalization/mocks/fragments/insertafter"]');
+ expect(el.dataset.adobeTargetTestid).to.equal('manifest');
+ });
+});
+
+describe('insertBefore action', async () => {
+ it('insertContentBefore should add fragment before target content and fragment', async () => {
+ let manifestJson = await readFile({ path: './mocks/actions/manifestInsertBefore.json' });
+
+ manifestJson = JSON.parse(manifestJson);
+ setFetchResponse(manifestJson);
+
+ await init(mepSettings);
+ expect(getConfig().mep.commands[0].targetManifestId).to.equal('manifest');
+ const el = document.querySelector('a[href="/test/features/personalization/mocks/fragments/insertbefore"]');
+ expect(el.dataset.adobeTargetTestid).to.equal('manifest');
+ });
+});
+
+describe('prependToSection action', async () => {
+ it('appendToSection should add fragment to beginning of section', async () => {
+ let manifestJson = await readFile({ path: './mocks/actions/manifestPrependToSection.json' });
+
+ manifestJson = JSON.parse(manifestJson);
+ setFetchResponse(manifestJson);
+
+ expect(document.querySelector('a[href="/test/features/personalization/mocks/fragments/prependToSection"]')).to.be.null;
+ await init(mepSettings);
+ expect(getConfig().mep.commands[0].targetManifestId).to.equal('manifest');
+ const el = document.querySelector('a[href="/test/features/personalization/mocks/fragments/prependToSection"]');
+ expect(el.dataset.adobeTargetTestid).to.equal('manifest');
+ });
+});
+
+describe('appendToSection action', async () => {
+ it('appendToSection should add fragment to end of section', async () => {
+ config.mep = { handleFragmentCommand };
+ let manifestJson = await readFile({ path: './mocks/actions/manifestAppendToSection.json' });
+
+ manifestJson = JSON.parse(manifestJson);
+ setFetchResponse(manifestJson);
+
+ await init(mepSettings);
+ expect(getConfig().mep.commands[0].targetManifestId).to.equal('manifest');
+ const el = document.querySelector('a[href="/test/features/personalization/mocks/fragments/appendToSection"]');
+ expect(el.dataset.adobeTargetTestid).to.equal('manifest');
+ });
+});
+
+describe('useBlockCode action', async () => {
+ it('useBlockCode should override a current block with the custom block code provided', async () => {
+ let manifestJson = await readFile({ path: './mocks/actions/manifestUseBlockCode.json' });
+ manifestJson = JSON.parse(manifestJson);
+ setFetchResponse(manifestJson);
+
+ await init(mepSettings);
+ expect(getConfig().mep.experiments[0].selectedVariant.useblockcode[0].targetManifestId).to.equal('manifest');
+ await addMepAnalytics(config);
+ const el = document.querySelector('.promo');
+ expect(el.dataset.adobeTargetTestid).to.equal('manifest');
+ });
+
+ it('useBlockCode should be able to use a new type of block', async () => {
+ let manifestJson = await readFile({ path: './mocks/actions/manifestUseBlockCode2.json' });
+ manifestJson = JSON.parse(manifestJson);
+ setFetchResponse(manifestJson);
+
+ await init(mepSettings);
+ expect(getConfig().mep.experiments[0].selectedVariant.useblockcode[0].targetManifestId).to.equal('manifest');
+ await addMepAnalytics(config);
+ const el = document.querySelector('.myblock');
+ expect(el.dataset.adobeTargetTestid).to.equal('manifest');
+ });
+});
+
+describe('custom actions', async () => {
+ it('should add a custom action configuration', async () => {
+ let manifestJson = await readFile({ path: './mocks/actions/manifestCustomAction.json' });
+ manifestJson = JSON.parse(manifestJson);
+ setFetchResponse(manifestJson);
+
+ await init(mepSettings);
+ expect(getConfig().mep.inBlock['my-block'].commands[0].targetManifestId).to.equal('manifest');
+ expect(getConfig().mep.inBlock['my-block'].commands[1].targetManifestId).to.equal('manifest');
+ expect(getConfig().mep.inBlock['my-block'].fragments['/fragments/sub-menu'].targetManifestId).to.equal('manifest');
+ expect(getConfig().mep.inBlock['my-block'].fragments['/fragments/new-sub-menu'].targetManifestId).to.equal('manifest');
+ });
+});
diff --git a/test/features/personalization/deprecatedActions.test.js b/test/features/personalization/deprecatedActions.test.js
index b6041034ad..30ac2cb931 100644
--- a/test/features/personalization/deprecatedActions.test.js
+++ b/test/features/personalization/deprecatedActions.test.js
@@ -3,8 +3,9 @@ import { readFile } from '@web/test-runner-commands';
import { stub } from 'sinon';
import { getConfig } from '../../../libs/utils/utils.js';
import initFragments from '../../../libs/blocks/fragment/fragment.js';
-import { applyPers, handleFragmentCommand } from '../../../libs/features/personalization/personalization.js';
+import { init, handleFragmentCommand } from '../../../libs/features/personalization/personalization.js';
import spoofParams from './spoofParams.js';
+import mepSettings from './mepSettings.js';
document.head.innerHTML = await readFile({ path: './mocks/metadata.html' });
document.body.innerHTML = await readFile({ path: './mocks/personalization.html' });
@@ -38,7 +39,7 @@ describe('Functional Test', () => {
expect(document.querySelector('.how-to')).to.not.be.null;
const parentEl = document.querySelector('#features-of-milo-experimentation-platform')?.parentElement;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
expect(document.querySelector('#features-of-milo-experimentation-platform')).to.be.null;
expect(parentEl.firstElementChild.firstElementChild.href)
.to.equal('http://localhost:2000/test/features/personalization/mocks/fragments/milo-replace-content-chrome-howto-h2');
@@ -52,7 +53,7 @@ describe('Functional Test', () => {
setFetchResponse(manifestJson);
expect(document.querySelector('.z-pattern')).to.not.be.null;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
expect(document.querySelector('.z-pattern')).to.be.null;
});
@@ -62,7 +63,7 @@ describe('Functional Test', () => {
setFetchResponse(manifestJson);
expect(document.querySelector('a[href="/fragments/insertafter"]')).to.be.null;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
const fragment = document.querySelector('a[href="/test/features/personalization/mocks/fragments/insertafter"]');
expect(fragment).to.not.be.null;
@@ -77,7 +78,7 @@ describe('Functional Test', () => {
setFetchResponse(manifestJson);
expect(document.querySelector('a[href="/fragments/insertbefore"]')).to.be.null;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
const fragment = document.querySelector('a[href="/test/features/personalization/mocks/fragments/insertbefore"]');
expect(fragment).to.not.be.null;
@@ -94,7 +95,7 @@ describe('Functional Test', () => {
expect(document.querySelector('a[href="/fragments/replaceme"]')).to.exist;
expect(document.querySelector('a[href="/fragments/inline-replaceme#_inline"]')).to.exist;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
const fragmentResp = await readFile({ path: './mocks/fragments/fragmentReplaced.plain.html' });
const inlineFragmentResp = await readFile({ path: './mocks/fragments/inlineFragReplaced.plain.html' });
@@ -126,7 +127,7 @@ describe('Functional Test', () => {
setFetchResponse(manifestJson);
expect(document.querySelector('.z-pattern')).to.not.be.null;
- await applyPers([{ manifestPath: '/mocks/manifestRemove.json' }]);
+ await init(mepSettings);
expect(document.querySelector('.z-pattern')).to.not.be.null;
expect(document.querySelector('.z-pattern').dataset.removedManifestId).to.not.be.null;
diff --git a/test/features/personalization/mepSettings.js b/test/features/personalization/mepSettings.js
new file mode 100644
index 0000000000..d55161f0ae
--- /dev/null
+++ b/test/features/personalization/mepSettings.js
@@ -0,0 +1,10 @@
+const mepSettings = {
+ mepParam: false,
+ mepHighlight: false,
+ mepButton: false,
+ pzn: '/path/to/manifest.json',
+ promo: false,
+ target: false,
+};
+
+export default mepSettings;
diff --git a/test/features/personalization/mepTargetSettings.js b/test/features/personalization/mepTargetSettings.js
new file mode 100644
index 0000000000..a5313f2cde
--- /dev/null
+++ b/test/features/personalization/mepTargetSettings.js
@@ -0,0 +1,10 @@
+const mepSettings = {
+ mepParam: '/path/to/manifest.json--target-var1',
+ mepHighlight: false,
+ mepButton: false,
+ pzn: '/path/to/manifest.json',
+ promo: false,
+ target: false,
+};
+
+export default mepSettings;
diff --git a/test/features/personalization/mocks/actions/manifestAppendToSection.json b/test/features/personalization/mocks/actions/manifestAppendToSection.json
index 0269996cd6..9bd9bfdf47 100644
--- a/test/features/personalization/mocks/actions/manifestAppendToSection.json
+++ b/test/features/personalization/mocks/actions/manifestAppendToSection.json
@@ -8,6 +8,7 @@
"selector": "section2",
"page filter (optional)": "",
"param-newoffer=123": "",
+ "target-var1": "/test/features/personalization/mocks/fragments/appendToSection",
"chrome": "/test/features/personalization/mocks/fragments/appendToSection"
}
],
diff --git a/test/features/personalization/mocks/actions/manifestCustomAction.json b/test/features/personalization/mocks/actions/manifestCustomAction.json
index fafaccbe56..1de7092bbb 100644
--- a/test/features/personalization/mocks/actions/manifestCustomAction.json
+++ b/test/features/personalization/mocks/actions/manifestCustomAction.json
@@ -9,6 +9,7 @@
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "/fragments/fragmentreplaced",
+ "target-var1": "/fragments/fragmentreplaced",
"firefox": "",
"android": "",
"ios": ""
@@ -19,6 +20,7 @@
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "/fragments/new-large-menu",
+ "target-var1": "/fragments/new-large-menu",
"firefox": "",
"android": "",
"ios": ""
@@ -29,6 +31,7 @@
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "/fragments/new-sub-menu",
+ "target-var1": "/fragments/new-sub-menu",
"firefox": "",
"android": "",
"ios": ""
@@ -38,6 +41,7 @@
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "/fragments/even-more-new-sub-menu",
+ "target-var1": "/fragments/even-more-new-sub-menu",
"firefox": "",
"android": "",
"ios": ""
diff --git a/test/features/personalization/mocks/actions/manifestInsertAfter.json b/test/features/personalization/mocks/actions/manifestInsertAfter.json
index d694ec80cc..0e40395cf9 100644
--- a/test/features/personalization/mocks/actions/manifestInsertAfter.json
+++ b/test/features/personalization/mocks/actions/manifestInsertAfter.json
@@ -9,6 +9,7 @@
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "/test/features/personalization/mocks/fragments/insertafter",
+ "target-var1": "/test/features/personalization/mocks/fragments/insertafter",
"firefox": "",
"android": "",
"ios": ""
@@ -19,6 +20,7 @@
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "/test/features/personalization/mocks/fragments/insertafterfragment",
+ "target-var1": "/test/features/personalization/mocks/fragments/insertafterfragment",
"firefox": "",
"android": "",
"ios": ""
@@ -29,6 +31,7 @@
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "/test/features/personalization/mocks/fragments/insertafterfragment",
+ "target-var1": "/test/features/personalization/mocks/fragments/insertafterfragment",
"firefox": "",
"android": "",
"ios": ""
diff --git a/test/features/personalization/mocks/actions/manifestInsertBefore.json b/test/features/personalization/mocks/actions/manifestInsertBefore.json
index e58e39e05c..057cfaacf4 100644
--- a/test/features/personalization/mocks/actions/manifestInsertBefore.json
+++ b/test/features/personalization/mocks/actions/manifestInsertBefore.json
@@ -9,6 +9,7 @@
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "/test/features/personalization/mocks/fragments/insertbefore",
+ "target-var1": "/test/features/personalization/mocks/fragments/insertbefore",
"firefox": "",
"android": "",
"ios": ""
@@ -19,6 +20,7 @@
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "/test/features/personalization/mocks/fragments/insertbeforefragment",
+ "target-var1": "/test/features/personalization/mocks/fragments/insertbeforefragment",
"firefox": "",
"android": "",
"ios": ""
diff --git a/test/features/personalization/mocks/actions/manifestPrependToSection.json b/test/features/personalization/mocks/actions/manifestPrependToSection.json
index cd5f43432b..c2ccb7af8b 100644
--- a/test/features/personalization/mocks/actions/manifestPrependToSection.json
+++ b/test/features/personalization/mocks/actions/manifestPrependToSection.json
@@ -8,7 +8,8 @@
"selector": "section2",
"page filter (optional)": "",
"param-newoffer=123": "",
- "chrome": "/test/features/personalization/mocks/fragments/prependToSection"
+ "chrome": "/test/features/personalization/mocks/fragments/prependToSection",
+ "target-var1": "/test/features/personalization/mocks/fragments/prependToSection"
}
],
":type": "sheet"
diff --git a/test/features/personalization/mocks/actions/manifestRemove.json b/test/features/personalization/mocks/actions/manifestRemove.json
index 8ff94af803..030c9efb0a 100644
--- a/test/features/personalization/mocks/actions/manifestRemove.json
+++ b/test/features/personalization/mocks/actions/manifestRemove.json
@@ -9,6 +9,7 @@
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "yes",
+ "target-var1": "yes",
"firefox": "",
"android": "",
"ios": ""
@@ -19,6 +20,7 @@
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "yes",
+ "target-var1": "yes",
"firefox": "",
"android": "",
"ios": ""
diff --git a/test/features/personalization/mocks/actions/manifestReplace.json b/test/features/personalization/mocks/actions/manifestReplace.json
index f1feae60ca..c1d0d78d78 100644
--- a/test/features/personalization/mocks/actions/manifestReplace.json
+++ b/test/features/personalization/mocks/actions/manifestReplace.json
@@ -9,6 +9,7 @@
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "",
+ "target-var1": "",
"firefox": "/test/features/personalization/mocks/fragments/milo-replace-content-firefox-accordion",
"android": "",
"ios": ""
@@ -19,6 +20,7 @@
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "/test/features/personalization/mocks/fragments/milo-replace-content-chrome-howto-h2",
+ "target-var1": "/test/features/personalization/mocks/fragments/milo-replace-content-chrome-howto-h2",
"firefox": "",
"android": "",
"ios": ""
@@ -29,6 +31,7 @@
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "/test/features/personalization/mocks/fragments/fragmentReplaced",
+ "target-var1": "/test/features/personalization/mocks/fragments/fragmentReplaced",
"firefox": "",
"android": "",
"ios": ""
@@ -39,6 +42,7 @@
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "/test/features/personalization/mocks/fragments/inlineFragReplaced",
+ "target-var1": "/test/features/personalization/mocks/fragments/inlineFragReplaced",
"firefox": "",
"android": "",
"ios": ""
diff --git a/test/features/personalization/mocks/actions/manifestTargetReplace.json b/test/features/personalization/mocks/actions/manifestTargetReplace.json
new file mode 100644
index 0000000000..f1feae60ca
--- /dev/null
+++ b/test/features/personalization/mocks/actions/manifestTargetReplace.json
@@ -0,0 +1,48 @@
+{
+ "total": 5,
+ "offset": 0,
+ "limit": 5,
+ "data": [
+ {
+ "action": "replace",
+ "selector": ".how-to",
+ "page filter (optional)": "",
+ "param-newoffer=123": "",
+ "chrome": "",
+ "firefox": "/test/features/personalization/mocks/fragments/milo-replace-content-firefox-accordion",
+ "android": "",
+ "ios": ""
+ },
+ {
+ "action": "replace",
+ "selector": "#features-of-milo-experimentation-platform",
+ "page filter (optional)": "",
+ "param-newoffer=123": "",
+ "chrome": "/test/features/personalization/mocks/fragments/milo-replace-content-chrome-howto-h2",
+ "firefox": "",
+ "android": "",
+ "ios": ""
+ },
+ {
+ "action": "replace",
+ "selector": "/fragments/replaceme",
+ "page filter (optional)": "",
+ "param-newoffer=123": "",
+ "chrome": "/test/features/personalization/mocks/fragments/fragmentReplaced",
+ "firefox": "",
+ "android": "",
+ "ios": ""
+ },
+ {
+ "action": "replace",
+ "selector": "/fragments/inline-replaceme",
+ "page filter (optional)": "",
+ "param-newoffer=123": "",
+ "chrome": "/test/features/personalization/mocks/fragments/inlineFragReplaced",
+ "firefox": "",
+ "android": "",
+ "ios": ""
+ }
+ ],
+ ":type": "sheet"
+}
diff --git a/test/features/personalization/mocks/actions/manifestUseBlockCode.json b/test/features/personalization/mocks/actions/manifestUseBlockCode.json
index 1d1d47764c..e8837cb45f 100644
--- a/test/features/personalization/mocks/actions/manifestUseBlockCode.json
+++ b/test/features/personalization/mocks/actions/manifestUseBlockCode.json
@@ -9,6 +9,7 @@
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "test\\features\\personalization\\mocks\\promo",
+ "target-var1": "test\\features\\personalization\\mocks\\promo",
"firefox": "",
"android": "",
"ios": ""
diff --git a/test/features/personalization/mocks/actions/manifestUseBlockCode2.json b/test/features/personalization/mocks/actions/manifestUseBlockCode2.json
index f377c358d9..a2a48fdd0a 100644
--- a/test/features/personalization/mocks/actions/manifestUseBlockCode2.json
+++ b/test/features/personalization/mocks/actions/manifestUseBlockCode2.json
@@ -9,6 +9,7 @@
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "/test/features/personalization/mocks/myblock",
+ "target-var1": "/test/features/personalization/mocks/myblock",
"firefox": "",
"android": "",
"ios": ""
diff --git a/test/features/personalization/mocks/fragments/milo-replace-content-target-howto-h2.plain.html b/test/features/personalization/mocks/fragments/milo-replace-content-target-howto-h2.plain.html
new file mode 100644
index 0000000000..a9a5c882f2
--- /dev/null
+++ b/test/features/personalization/mocks/fragments/milo-replace-content-target-howto-h2.plain.html
@@ -0,0 +1 @@
+mock fragment
diff --git a/test/features/personalization/pageFilter.test.js b/test/features/personalization/pageFilter.test.js
index 263e11cbba..0a08e45481 100644
--- a/test/features/personalization/pageFilter.test.js
+++ b/test/features/personalization/pageFilter.test.js
@@ -2,7 +2,8 @@ import { expect } from '@esm-bundle/chai';
import { readFile } from '@web/test-runner-commands';
import { stub } from 'sinon';
import { getConfig } from '../../../libs/utils/utils.js';
-import { applyPers } from '../../../libs/features/personalization/personalization.js';
+import { init } from '../../../libs/features/personalization/personalization.js';
+import mepSettings from './mepSettings.js';
document.body.innerHTML = await readFile({ path: './mocks/personalization.html' });
@@ -35,7 +36,7 @@ it('pageFilter should exclude page if it is not a match', async () => {
expect(document.querySelector('.marquee')).to.not.be.null;
expect(document.querySelector('.newpage')).to.be.null;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
// Nothing should be changed since the pageFilter excludes this page
expect(document.querySelector('.marquee')).to.not.be.null;
@@ -71,7 +72,7 @@ it('pageFilter should include page if it is a match', async () => {
expect(document.querySelector('.marquee')).to.not.be.null;
expect(document.querySelector('.newpage')).to.be.null;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
expect(document.querySelector('.marquee')).to.be.null;
expect(document.querySelector('.newpage')).to.not.be.null;
diff --git a/test/features/personalization/personalization.test.js b/test/features/personalization/personalization.test.js
index 38211b358c..d2554b7734 100644
--- a/test/features/personalization/personalization.test.js
+++ b/test/features/personalization/personalization.test.js
@@ -2,8 +2,12 @@ import { expect } from '@esm-bundle/chai';
import { readFile } from '@web/test-runner-commands';
import { assert, stub } from 'sinon';
import { getConfig, setConfig } from '../../../libs/utils/utils.js';
-import { applyPers, matchGlob, createFrag } from '../../../libs/features/personalization/personalization.js';
+import {
+ handleFragmentCommand, applyPers,
+ init, matchGlob, createFrag, combineMepSources,
+} from '../../../libs/features/personalization/personalization.js';
import spoofParams from './spoofParams.js';
+import mepSettings from './mepSettings.js';
document.head.innerHTML = await readFile({ path: './mocks/metadata.html' });
document.body.innerHTML = await readFile({ path: './mocks/personalization.html' });
@@ -42,7 +46,7 @@ describe('Functional Test', () => {
expect(document.querySelector('.marquee')).to.not.be.null;
expect(document.querySelector('a[href="/test/features/personalization/mocks/fragments/insertafter2"]')).to.be.null;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
const fragment = document.querySelector('a[href="/test/features/personalization/mocks/fragments/insertafter2"]');
expect(fragment).to.not.be.null;
expect(fragment.parentElement.previousElementSibling.className).to.equal('marquee');
@@ -58,7 +62,7 @@ describe('Functional Test', () => {
const secondMarquee = document.getElementsByClassName('marquee')[1];
expect(secondMarquee).to.not.be.null;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
const fragment = document.querySelector('a[href="/fragments/replace/marquee/r2c1"]');
expect(fragment).to.not.be.null;
@@ -76,7 +80,7 @@ describe('Functional Test', () => {
expect(document.querySelector('.custom-block-2')).to.not.be.null;
expect(document.querySelector('.custom-block-3')).to.not.be.null;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
expect(document.querySelector('.special-block')).to.be.null;
expect(document.querySelector('.custom-block-2')).to.be.null;
@@ -84,12 +88,27 @@ describe('Functional Test', () => {
});
it('scheduled manifest should apply changes if active (bts)', async () => {
+ const config = getConfig();
+ config.mep = {
+ handleFragmentCommand,
+ preview: false,
+ variantOverride: {},
+ highlight: false,
+ targetEnabled: false,
+ experiments: [],
+ };
+ const promoMepSettings = [
+ {
+ manifestPath: '/promos/bts/manifest.json',
+ disabled: false,
+ event: { name: 'bts', start: new Date('2023-11-24T13:00:00+00:00'), end: new Date('2222-11-24T13:00:00+00:00') },
+ },
+ ];
let manifestJson = await readFile({ path: './mocks/manifestScheduledActive.json' });
manifestJson = JSON.parse(manifestJson);
setFetchResponse(manifestJson);
expect(document.querySelector('a[href="/test/features/personalization/mocks/fragments/insertafter3"]')).to.be.null;
- const event = { name: 'bts', start: new Date('2023-11-24T13:00:00+00:00'), end: new Date('2222-11-24T13:00:00+00:00') };
- await applyPers([{ manifestPath: '/promos/bts/manifest.json', disabled: false, event }]);
+ await applyPers(promoMepSettings);
const fragment = document.querySelector('a[href="/test/features/personalization/mocks/fragments/insertafter3"]');
expect(fragment).to.not.be.null;
@@ -98,10 +117,25 @@ describe('Functional Test', () => {
});
it('scheduled manifest should not apply changes if not active (blackfriday)', async () => {
+ const config = getConfig();
+ config.mep = {
+ handleFragmentCommand,
+ preview: false,
+ variantOverride: {},
+ highlight: false,
+ targetEnabled: false,
+ experiments: [],
+ };
+ const promoMepSettings = [
+ {
+ manifestPath: '/promos/blackfriday/manifest.json',
+ disabled: true,
+ event: { name: 'blackfriday', start: new Date('2022-11-24T13:00:00+00:00'), end: new Date('2022-11-24T13:00:00+00:00') },
+ },
+ ];
await loadManifestAndSetResponse('./mocks/manifestScheduledInactive.json');
expect(document.querySelector('a[href="/fragments/insertafter4"]')).to.be.null;
- const event = { name: 'blackfriday', start: new Date('2022-11-24T13:00:00+00:00'), end: new Date('2022-11-24T13:00:00+00:00') };
- await applyPers([{ manifestPath: '/promos/blackfriday/manifest.json', disabled: true, event }]);
+ await applyPers(promoMepSettings);
const fragment = document.querySelector('a[href="/fragments/insertafter4"]');
expect(fragment).to.be.null;
@@ -112,20 +146,20 @@ describe('Functional Test', () => {
config.mep = {};
await loadManifestAndSetResponse('./mocks/manifestTestOrPromo.json');
config = getConfig();
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
expect(config.mep?.martech).to.be.undefined;
});
it('should choose chrome & logged out', async () => {
await loadManifestAndSetResponse('./mocks/manifestWithAmpersand.json');
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
const config = getConfig();
expect(config.mep?.martech).to.equal('|chrome & logged|ampersand');
});
it('should choose not firefox', async () => {
await loadManifestAndSetResponse('./mocks/manifestWithNot.json');
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
const config = getConfig();
expect(config.mep?.martech).to.equal('|not firefox|not');
});
@@ -137,7 +171,7 @@ describe('Functional Test', () => {
config.entitlements = () => Promise.resolve(['indesign-any', 'fireflies', 'after-effects-any']);
await loadManifestAndSetResponse('./mocks/manifestUseEntitlements.json');
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
expect(getConfig().mep?.martech).to.equal('|fireflies|manifest');
});
@@ -146,7 +180,7 @@ describe('Functional Test', () => {
await loadManifestAndSetResponse('./mocks/manifestInvalidSelector.json');
- await applyPers([{ manifestPath: '/mocks/manifestRemove.json' }]);
+ await init(mepSettings);
assert.calledWith(window.console.log, 'Invalid selector: ', '.bad...selector');
window.console.log.reset();
@@ -164,7 +198,7 @@ describe('Functional Test', () => {
expect(document.querySelector('meta[property="og:title"]').content).to.equal('milo');
expect(document.querySelector('meta[property="og:image"]')).to.be.null;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
expect(geoMetadata.content).to.equal('on');
expect(document.querySelector('meta[name="mynewmetadata"]').content).to.equal('woot');
@@ -177,7 +211,7 @@ describe('Functional Test', () => {
const config = getConfig();
await loadManifestAndSetResponse('./mocks/actions/manifestAppendToSection.json');
setTimeout(async () => {
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
expect(config.mep.experiments[0].selectedVariantName).to.equal('param-newoffer=123');
}, 100);
});
@@ -235,3 +269,36 @@ describe('matchGlob function', () => {
expect(wrapper.classList.contains('hide-block')).to.be.true;
});
});
+
+describe('MEP Utils', () => {
+ describe('combineMepSources', async () => {
+ it('yields an empty list when everything is undefined', async () => {
+ const manifests = await combineMepSources(undefined, undefined, undefined);
+ expect(manifests.length).to.equal(0);
+ });
+ it('combines promos and personalization', async () => {
+ document.head.innerHTML = await readFile({ path: '../../utils/mocks/mep/head-promo.html' });
+ const promos = { manifestnames: 'pre-black-friday-global,black-friday-global' };
+ const manifests = await combineMepSources('/pers/manifest.json', promos, undefined);
+ expect(manifests.length).to.equal(3);
+ expect(manifests[0].manifestPath).to.equal('/pers/manifest.json');
+ expect(manifests[1].manifestPath).to.equal('/pre-black-friday.json');
+ expect(manifests[2].manifestPath).to.equal('/black-friday.json');
+ });
+ it('combines promos and personalization and mep param', async () => {
+ document.head.innerHTML = await readFile({ path: '../../utils/mocks/mep/head-promo.html' });
+ const promos = { manifestnames: 'pre-black-friday-global,black-friday-global' };
+ const manifests = await combineMepSources(
+ '/pers/manifest.json',
+ promos,
+ '/pers/manifest.json--var1---/mep-param/manifest1.json--all---/mep-param/manifest2.json--all',
+ );
+ expect(manifests.length).to.equal(5);
+ expect(manifests[0].manifestPath).to.equal('/pers/manifest.json');
+ expect(manifests[1].manifestPath).to.equal('/pre-black-friday.json');
+ expect(manifests[2].manifestPath).to.equal('/black-friday.json');
+ expect(manifests[3].manifestPath).to.equal('/mep-param/manifest1.json');
+ expect(manifests[4].manifestPath).to.equal('/mep-param/manifest2.json');
+ });
+ });
+});
diff --git a/test/features/personalization/replacePage.test.js b/test/features/personalization/replacePage.test.js
index 3b3fed3038..3ea70387f5 100644
--- a/test/features/personalization/replacePage.test.js
+++ b/test/features/personalization/replacePage.test.js
@@ -2,7 +2,8 @@ import { expect } from '@esm-bundle/chai';
import { readFile } from '@web/test-runner-commands';
import { stub } from 'sinon';
import { getConfig } from '../../../libs/utils/utils.js';
-import { applyPers } from '../../../libs/features/personalization/personalization.js';
+import { init } from '../../../libs/features/personalization/personalization.js';
+import mepSettings from './mepSettings.js';
document.body.innerHTML = await readFile({ path: './mocks/personalization.html' });
@@ -35,7 +36,7 @@ it('replacePage should replace all of the main block', async () => {
expect(document.querySelector('.marquee')).to.not.be.null;
expect(document.querySelector('.newpage')).to.be.null;
- await applyPers([{ manifestPath: '/path/to/manifest.json' }]);
+ await init(mepSettings);
expect(document.querySelector('.marquee')).to.be.null;
expect(document.querySelector('.newpage')).to.not.be.null;
diff --git a/test/utils/utils-mep.test.js b/test/utils/utils-mep.test.js
index 53af9d1d12..fdbdd7a2e7 100644
--- a/test/utils/utils-mep.test.js
+++ b/test/utils/utils-mep.test.js
@@ -1,6 +1,7 @@
import { readFile } from '@web/test-runner-commands';
import { expect } from '@esm-bundle/chai';
-import { combineMepSources, getMepEnablement } from '../../libs/utils/utils.js';
+import { getMepEnablement } from '../../libs/utils/utils.js';
+import { combineMepSources } from '../../libs/features/personalization/personalization.js';
describe('MEP Utils', () => {
describe('combineMepSources', async () => {
diff --git a/test/utils/utils.test.js b/test/utils/utils.test.js
index 586183ec6a..fbdfd5e184 100644
--- a/test/utils/utils.test.js
+++ b/test/utils/utils.test.js
@@ -634,7 +634,7 @@ describe('Utils', () => {
const resultExperiment = resultConfig.mep.experiments[0];
expect(resultConfig.mep.preview).to.be.true;
expect(resultConfig.mep.experiments.length).to.equal(3);
- expect(resultExperiment.manifest).to.equal('/products/special-offers-manifest.json');
+ expect(resultExperiment.manifest).to.equal('https://main--milo--adobecom.hlx.page/products/special-offers-manifest.json');
});
});