diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 1fe46f17cf..cb6b41994b 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,6 +4,7 @@ "dbaeumer.vscode-eslint", "editorconfig.editorconfig", "ryanluker.vscode-coverage-gutters", - "eamodio.gitlens" + "eamodio.gitlens", + "github.vscode-pull-request-github" ] } diff --git a/libs/features/personalization/entitlements.js b/libs/features/personalization/entitlements.js new file mode 100644 index 0000000000..cab25b9d5d --- /dev/null +++ b/libs/features/personalization/entitlements.js @@ -0,0 +1,25 @@ +import { getConfig } from '../../utils/utils.js'; + +export const ENTITLEMENT_MAP = { + '9565ef55-faad-430b-b661-596ba7a036c4': 'all-apps', + 'c8d50cc2-491e-48df-a1b0-1509f0ca7323': 'photoshop', + 'fd30e9c7-9ae9-44db-8e70-5c652a5bb1d2': 'cc-all-apps', +}; + +const getEntitlements = (data) => { + const { entitlements = {} } = getConfig(); + const entitlementMap = { ...entitlements, ...ENTITLEMENT_MAP }; + + return data.flatMap((destination) => { + const ents = destination.segments?.flatMap((segment) => { + const entMatch = entitlementMap[segment.id]; + return entMatch ? [entMatch] : []; + }); + + return ents || []; + }); +}; + +export default function init(data) { + return getEntitlements(data); +} diff --git a/libs/features/personalization/personalization.js b/libs/features/personalization/personalization.js index b9b471053b..cc995dd2fb 100644 --- a/libs/features/personalization/personalization.js +++ b/libs/features/personalization/personalization.js @@ -1,21 +1,47 @@ /* eslint-disable no-console */ -import { - createTag, getConfig, loadIms, loadLink, loadScript, updateConfig, -} from '../../utils/utils.js'; +import { createTag, getConfig, loadLink, loadScript, updateConfig } from '../../utils/utils.js'; +import { ENTITLEMENT_MAP } from './entitlements.js'; + +/* c8 ignore start */ +export const PERSONALIZATION_TAGS = { + all: () => true, + chrome: () => navigator.userAgent.includes('Chrome') && !navigator.userAgent.includes('Mobile'), + firefox: () => navigator.userAgent.includes('Firefox') && !navigator.userAgent.includes('Mobile'), + android: () => navigator.userAgent.includes('Android'), + ios: () => /iPad|iPhone|iPod/.test(navigator.userAgent), + loggedout: () => !window.adobeIMS?.isSignedInUser(), + loggedin: () => window.adobeIMS?.isSignedInUser(), + darkmode: () => window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches, + lightmode: () => !PERSONALIZATION_TAGS.darkmode(), +}; +const PERSONALIZATION_KEYS = Object.keys(PERSONALIZATION_TAGS); +/* c8 ignore stop */ const CLASS_EL_DELETE = 'p13n-deleted'; const CLASS_EL_REPLACE = 'p13n-replaced'; -const LS_ENT_KEY = 'milo:entitlements'; -const LS_ENT_EXPIRE_KEY = 'milo:entitlements:expire'; const COLUMN_NOT_OPERATOR = 'not'; const TARGET_EXP_PREFIX = 'target-'; -const ENT_CACHE_EXPIRE = 1000 * 60 * 60 * 3; // 3 hours -const ENT_CACHE_REFRESH = 1000 * 60 * 3; // 3 minutes const PAGE_URL = new URL(window.location.href); export const NON_TRACKED_MANIFEST_TYPE = 'test or promo'; +// Replace any non-alpha chars except comma, space, ampersand and hyphen +const RE_KEY_REPLACE = /[^a-z0-9\- _,&=]/g; + +const MANIFEST_KEYS = [ + 'action', + 'selector', + 'pagefilter', + 'page filter', + 'page filter optional', +]; + +const DATA_TYPE = { + JSON: 'json', + TEXT: 'text', +}; + export const appendJsonExt = (path) => (path.endsWith('.json') ? path : `${path}.json`); export const normalizePath = (p) => { @@ -62,44 +88,6 @@ export const preloadManifests = ({ targetManifests = [], persManifests = [] }) = return manifests; }; -/* c8 ignore start */ -export const PERSONALIZATION_TAGS = { - all: () => true, - chrome: () => navigator.userAgent.includes('Chrome') && !navigator.userAgent.includes('Mobile'), - firefox: () => navigator.userAgent.includes('Firefox') && !navigator.userAgent.includes('Mobile'), - android: () => navigator.userAgent.includes('Android'), - ios: () => /iPad|iPhone|iPod/.test(navigator.userAgent), - loggedout: () => !window.adobeIMS?.isSignedInUser(), - loggedin: () => window.adobeIMS?.isSignedInUser(), - darkmode: () => window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches, - lightmode: () => !PERSONALIZATION_TAGS.darkmode(), -}; - -export const ENTITLEMENT_TAGS = { - photoshop: (ents) => ents.photoshop_cc, - lightroom: (ents) => ents.lightroom_cc, -}; -/* c8 ignore stop */ - -const personalizationKeys = Object.keys(PERSONALIZATION_TAGS); -const entitlementKeys = Object.keys(ENTITLEMENT_TAGS); - -// Replace any non-alpha chars except comma, space, ampersand and hyphen -const RE_KEY_REPLACE = /[^a-z0-9\- _,&=]/g; - -const MANIFEST_KEYS = [ - 'action', - 'selector', - 'pagefilter', - 'page filter', - 'page filter optional', -]; - -const DATA_TYPE = { - JSON: 'json', - TEXT: 'text', -}; - const createFrag = (el, url, manifestId) => { let href = url; try { @@ -316,78 +304,6 @@ function parsePlaceholders(placeholders, config, selectedVariantName = '') { } return config; } - -const fetchEntitlements = async () => { - const [{ default: getUserEntitlements }] = await Promise.all([ - import('../../blocks/global-navigation/utilities/getUserEntitlements.js'), - loadIms(), - ]); - return getUserEntitlements(); -}; - -const setEntLocalStorage = (ents) => { - localStorage.setItem(LS_ENT_KEY, JSON.stringify(ents)); - localStorage.setItem(LS_ENT_EXPIRE_KEY, Date.now()); -}; - -const loadEntsFromLocalStorage = () => { - const ents = localStorage.getItem(LS_ENT_KEY); - const expireDate = localStorage.getItem(LS_ENT_EXPIRE_KEY); - const now = Date.now(); - if (!ents || !expireDate || (now - expireDate) > ENT_CACHE_EXPIRE) return null; - if ((now - expireDate) > ENT_CACHE_REFRESH) { - // refresh entitlements in background - setTimeout(() => { - fetchEntitlements().then((newEnts) => { - setEntLocalStorage(newEnts); - }); - }, 5000); - } - return JSON.parse(ents); -}; - -const clearEntLocalStorage = () => { - localStorage.removeItem(LS_ENT_KEY); - localStorage.removeItem(LS_ENT_EXPIRE_KEY); -}; - -export const getEntitlements = (() => { - let ents; - let logoutEventSet; - return (async () => { - if (window.adobeIMS && !window.adobeIMS.isSignedInUser()) { - clearEntLocalStorage(); - return {}; - } - if (!ents) { - ents = loadEntsFromLocalStorage(); - } - if (!ents) { - ents = await fetchEntitlements(); - setEntLocalStorage(ents); - } - if (!logoutEventSet) { - window.addEventListener('feds:signOut', clearEntLocalStorage); - logoutEventSet = true; - } - return ents; - }); -})(); - -const getFlatEntitlements = async () => { - const ents = await getEntitlements(); - return { - ...ents.arrangement_codes, - ...ents.clouds, - ...ents.fulfilled_codes, - }; -}; - -const checkForEntitlementMatch = (name, entitlements) => { - const entName = name.split('ent-')[1]; - if (!entName) return false; - return entitlements[entName]; -}; /* c8 ignore stop */ const checkForParamMatch = (paramStr) => { @@ -427,23 +343,20 @@ async function getPersonalizationVariant(manifestPath, variantNames = [], varian return acc; }, { allNames: [] }); - const hasEntitlementPrefix = variantInfo.allNames.some((name) => name.startsWith('ent-')); + const entitlementKeys = Object.values(ENTITLEMENT_MAP); const hasEntitlementTag = entitlementKeys.some((tag) => variantInfo.allNames.includes(tag)); - let entitlements = {}; - if (hasEntitlementPrefix || hasEntitlementTag) { - entitlements = await getFlatEntitlements(); + let userEntitlements = []; + if (hasEntitlementTag) { + userEntitlements = await config.entitlements(); } const hasMatch = (name) => { if (name === '') return true; if (name === variantLabel?.toLowerCase()) return true; if (name.startsWith('param-')) return checkForParamMatch(name); - if (name.startsWith('ent-')) return checkForEntitlementMatch(name, entitlements); - if (entitlementKeys.includes(name)) { - return ENTITLEMENT_TAGS[name](entitlements); - } - return personalizationKeys.includes(name) && PERSONALIZATION_TAGS[name](); + if (userEntitlements?.includes(name)) return true; + return PERSONALIZATION_KEYS.includes(name) && PERSONALIZATION_TAGS[name](); }; const matchVariant = (name) => { @@ -483,8 +396,13 @@ export async function getPersConfig(info) { const config = parseConfig(persData); const infoTab = manifestInfo || data?.info?.data; - config.manifestType = infoTab?.find((element) => element.key?.toLowerCase() === 'manifest-type')?.value?.toLowerCase() || 'personalization'; - config.manifestOverrideName = infoTab?.find((element) => element.key?.toLowerCase() === 'manifest-override-name')?.value?.toLowerCase(); + config.manifestType = infoTab + ?.find((element) => element.key?.toLowerCase() === 'manifest-type')?.value?.toLowerCase() + || 'personalization'; + + config.manifestOverrideName = infoTab + ?.find((element) => element.key?.toLowerCase() === 'manifest-override-name') + ?.value?.toLowerCase(); if (!config) { /* c8 ignore next 3 */ @@ -606,7 +524,6 @@ export async function applyPers(manifests) { if (!manifests?.length) return; - getEntitlements(); const cleanedManifests = cleanManifestList(manifests); const override = config.mep?.override; diff --git a/libs/martech/martech.js b/libs/martech/martech.js index 94ae871c5b..f7e186e6cb 100644 --- a/libs/martech/martech.js +++ b/libs/martech/martech.js @@ -1,6 +1,8 @@ -import { getConfig, loadLink, loadScript } from '../utils/utils.js'; +import { getConfig, loadIms, loadLink, loadScript } from '../utils/utils.js'; +const ALLOY_SEND_EVENT = 'alloy_sendEvent'; const TARGET_TIMEOUT_MS = 2000; +const ENTITLEMENT_TIMEOUT = 3000; const setDeep = (obj, path, value) => { const pathArr = path.split('.'); @@ -16,9 +18,13 @@ const setDeep = (obj, path, value) => { currentObj[pathArr[pathArr.length - 1]] = value; }; -const waitForEventOrTimeout = (eventName, timeout) => new Promise((resolve, reject) => { +const waitForEventOrTimeout = (eventName, timeout, timeoutVal) => new Promise((resolve, reject) => { const timer = setTimeout(() => { - reject(new Error(`Timeout waiting for ${eventName} after ${timeout}ms`)); + if (timeoutVal !== undefined) { + resolve(timeoutVal); + } else { + reject(new Error(`Timeout waiting for ${eventName} after ${timeout}ms`)); + } }, timeout); window.addEventListener(eventName, (event) => { @@ -75,7 +81,7 @@ const getTargetPersonalization = async () => { let response; try { - response = await waitForEventOrTimeout('alloy_sendEvent', timeout); + response = await waitForEventOrTimeout(ALLOY_SEND_EVENT, timeout); } catch (e) { // eslint-disable-next-line no-console console.log(e); @@ -97,49 +103,94 @@ const getDtmLib = (env) => ({ : env.consumer?.marTechUrl || 'https://assets.adobedtm.com/d4d114c60e50/a0e989131fd5/launch-a27b33fc2dc0-development.min.js', }); -export default async function init({ persEnabled = false, persManifests }) { +const setupEntitlementCallback = () => { + const setEntitlements = async (destinations) => { + const { default: parseEntitlements } = await import('../features/personalization/entitlements.js'); + return parseEntitlements(destinations); + }; + + const getEntitlements = (resolve) => { + const handleEntitlements = (detail) => { + if (detail?.result?.destinations?.length) { + resolve(setEntitlements(detail.result.destinations)); + } else { + resolve([]); + } + }; + waitForEventOrTimeout(ALLOY_SEND_EVENT, ENTITLEMENT_TIMEOUT, []) + .then(handleEntitlements) + .catch(() => resolve([])); + }; + + const { miloLibs, codeRoot, entitlements: resolveEnt } = getConfig(); + getEntitlements(resolveEnt); + + loadLink( + `${miloLibs || codeRoot}/features/personalization/entitlements.js`, + { as: 'script', rel: 'modulepreload' }, + ); +}; + +let filesLoadedPromise = false; +const loadMartechFiles = async (config, url, edgeConfigId) => { + if (filesLoadedPromise) return filesLoadedPromise; + + filesLoadedPromise = async () => { + loadIms() + .then(() => { + if (window.adobeIMS.isSignedInUser()) setupEntitlementCallback(); + }) + .catch(() => {}); + + setDeep( + window, + 'alloy_all.data._adobe_corpnew.digitalData.page.pageInfo.language', + config.locale.ietf, + ); + setDeep(window, 'digitalData.diagnostic.franklin.implementation', 'milo'); + + window.marketingtech = { + adobe: { + launch: { url, controlPageLoad: true }, + alloy: { edgeConfigId }, + target: false, + }, + milo: true, + }; + window.edgeConfigId = edgeConfigId; + + await loadScript(`${config.miloLibs || config.codeRoot}/deps/martech.main.standard.min.js`); + // eslint-disable-next-line no-underscore-dangle + window._satellite.track('pageload'); + }; + + await filesLoadedPromise(); + return filesLoadedPromise; +}; + +export default async function init({ persEnabled = false, persManifests = [] }) { const config = getConfig(); const { url, edgeConfigId } = getDtmLib(config.env); loadLink(url, { as: 'script', rel: 'preload' }); + const martechPromise = loadMartechFiles(config, url, edgeConfigId); + if (persEnabled) { loadLink( `${config.miloLibs || config.codeRoot}/features/personalization/personalization.js`, { as: 'script', rel: 'modulepreload' }, ); - } - setDeep( - window, - 'alloy_all.data._adobe_corpnew.digitalData.page.pageInfo.language', - config.locale.ietf, - ); - setDeep(window, 'digitalData.diagnostic.franklin.implementation', 'milo'); - - window.marketingtech = { - adobe: { - launch: { url, controlPageLoad: true }, - alloy: { edgeConfigId }, - target: false, - }, - milo: true, - }; - window.edgeConfigId = edgeConfigId; - - await loadScript(`${config.miloLibs || config.codeRoot}/deps/martech.main.standard.min.js`); - // eslint-disable-next-line no-underscore-dangle - window._satellite.track('pageload'); - - if (persEnabled) { const targetManifests = await getTargetPersonalization(); if (targetManifests?.length || persManifests?.length) { - const { preloadManifests, applyPers, getEntitlements } = await import('../features/personalization/personalization.js'); - getEntitlements(); + const { preloadManifests, applyPers } = await import('../features/personalization/personalization.js'); const manifests = preloadManifests({ targetManifests, persManifests }); await applyPers(manifests); } else { document.body.dataset.mep = 'nopzn|nopzn'; } } + + return martechPromise; } diff --git a/libs/utils/utils.js b/libs/utils/utils.js index 2cbbbaf4f4..6ada41f80c 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -177,6 +177,27 @@ export function getMetadata(name, doc = document) { return meta && meta.content; } +const handleEntitlements = (() => { + let entResolve; + const entPromise = new Promise((resolve) => { + entResolve = resolve; + }); + + return (resolveVal) => { + if (resolveVal !== undefined) { + entResolve(resolveVal); + } + return entPromise; + }; +})(); + +function setupMiloObj(config) { + window.milo ||= {}; + window.milo.deferredPromise = new Promise((resolve) => { + config.resolveDeferred = resolve; + }); +} + export const [setConfig, updateConfig, getConfig] = (() => { let config = {}; return [ @@ -205,6 +226,8 @@ export const [setConfig, updateConfig, getConfig] = (() => { config.locale.contentRoot = `${origin}${config.locale.prefix}${config.contentRoot ?? ''}`; config.useDotHtml = !PAGE_URL.origin.includes('.hlx.') && (conf.useDotHtml ?? PAGE_URL.pathname.endsWith('.html')); + config.entitlements = handleEntitlements; + setupMiloObj(config); return config; }, (conf) => (config = conf), @@ -272,13 +295,14 @@ export function localizeLink( } } -export function loadLink(href, { as, callback, crossorigin, rel } = {}) { +export function loadLink(href, { as, callback, crossorigin, rel, fetchpriority } = {}) { let link = document.head.querySelector(`link[href="${href}"]`); if (!link) { link = document.createElement('link'); link.setAttribute('rel', rel); if (as) link.setAttribute('as', as); if (crossorigin) link.setAttribute('crossorigin', crossorigin); + if (fetchpriority) link.setAttribute('fetchpriority', fetchpriority); link.setAttribute('href', href); if (callback) { link.onload = (e) => callback(e.type); @@ -766,7 +790,12 @@ export async function loadIms() { onError: reject, }; loadScript('https://auth.services.adobe.com/imslib/imslib.min.js'); + }).then(() => { + if (!window.adobeIMS?.isSignedInUser()) { + getConfig().entitlements([]); + } }); + return imsLoaded; } @@ -837,7 +866,12 @@ async function checkForPageMods() { if (targetEnabled) { await loadMartech({ persEnabled: true, persManifests, targetMd }); } else if (persManifests.length) { - loadIms().catch(() => {}); + 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 }); @@ -853,7 +887,7 @@ async function checkForPageMods() { } async function loadPostLCP(config) { - loadMartech(config); + loadMartech(); const header = document.querySelector('header'); if (header) { header.classList.add('gnav-hide'); @@ -883,15 +917,7 @@ export function scrollToHashedElement(hash) { }); } -export function setupDeferredPromise() { - let resolveFn; - window.milo.deferredPromise = new Promise((resolve) => { - resolveFn = resolve; - }); - return resolveFn; -} - -export async function loadDeferred(area, blocks, config, resolveDeferred) { +export async function loadDeferred(area, blocks, config) { const event = new Event(MILO_EVENTS.DEFERRED); area.dispatchEvent(event); @@ -899,7 +925,7 @@ export async function loadDeferred(area, blocks, config, resolveDeferred) { return; } - resolveDeferred(true); + config.resolveDeferred?.(true); if (config.links === 'on') { const path = `${config.contentRoot || ''}${getMetadata('links-path') || '/seo/links.json'}`; @@ -1045,16 +1071,13 @@ async function processSection(section, config, isDoc) { export async function loadArea(area = document) { const isDoc = area === document; - let resolveDeferredFn; if (isDoc) { - window.milo = {}; await checkForPageMods(); appendHtmlToCanonicalUrl(); - resolveDeferredFn = setupDeferredPromise(); } - const config = getConfig(); + await decoratePlaceholders(area, config); if (isDoc) { @@ -1080,7 +1103,7 @@ export async function loadArea(area = document) { if (isDoc) await documentPostSectionLoading(config); - await loadDeferred(area, areaBlocks, config, resolveDeferredFn); + await loadDeferred(area, areaBlocks, config); } export function loadDelayed() { diff --git a/test/features/personalization/entitlements.test.js b/test/features/personalization/entitlements.test.js new file mode 100644 index 0000000000..af66e99bb7 --- /dev/null +++ b/test/features/personalization/entitlements.test.js @@ -0,0 +1,79 @@ +import { expect } from '@esm-bundle/chai'; +import { getConfig } from '../../../libs/utils/utils.js'; +import getEntitlements, { ENTITLEMENT_MAP } from '../../../libs/features/personalization/entitlements.js'; + +// Modify the entitlement map with custom keys so the test doesn't rely on real data +ENTITLEMENT_MAP['11111111-aaaa-bbbb-6666-cccccccccccc'] = 'my-special-app'; +ENTITLEMENT_MAP['22222222-xxxx-bbbb-7777-cccccccccccc'] = 'fireflies'; + +describe('entitlements', () => { + it('Should return any entitlements that match the id', () => { + const destinations = [ + { + segments: [ + { + id: '11111111-aaaa-bbbb-6666-cccccccccccc', + namespace: 'ups', + }, + { + id: '22222222-xxxx-bbbb-7777-cccccccccccc', + namespace: 'ups', + }, + { + id: '33333333-xxxx-bbbb-7777-cccccccccccc', + namespace: 'ups', + }, + ], + }, + ]; + + const expectedEntitlements = ['my-special-app', 'fireflies']; + const entitlements = getEntitlements(destinations); + expect(entitlements).to.deep.equal(expectedEntitlements); + }); + + it('Should not return any entitlements if there is no match', () => { + const destinations = [ + { + segments: [ + { + id: 'x1111111-aaaa-bbbb-6666-cccccccccccc', + namespace: 'ups', + }, + { + id: 'y2222222-xxxx-bbbb-7777-cccccccccccc', + namespace: 'ups', + }, + ], + }, + ]; + + const expectedEntitlements = []; + const entitlements = getEntitlements(destinations); + expect(entitlements).to.deep.equal(expectedEntitlements); + }); + + it('Should be able to use consumer defined entitlements in the config', () => { + const config = getConfig(); + config.entitlements = { 'consumer-defined-entitlement': 'consumer-defined' }; + + const destinations = [ + { + segments: [ + { + id: '11111111-aaaa-bbbb-6666-cccccccccccc', + namespace: 'ups', + }, + { + id: 'consumer-defined-entitlement', + namespace: 'ups', + }, + ], + }, + ]; + + const expectedEntitlements = ['my-special-app', 'consumer-defined']; + const entitlements = getEntitlements(destinations); + expect(entitlements).to.deep.equal(expectedEntitlements); + }); +}); diff --git a/test/features/personalization/mocks/manifestUseEntitlements.json b/test/features/personalization/mocks/manifestUseEntitlements.json new file mode 100644 index 0000000000..7a7dd59eca --- /dev/null +++ b/test/features/personalization/mocks/manifestUseEntitlements.json @@ -0,0 +1,40 @@ +{ + "info":{ + "total":2, + "offset":0, + "limit":2, + "data":[ + { + "key":"manifest-type", + "value":"Personalization" + } + ] + }, + "experiences":{ + "total":1, + "offset":0, + "limit":1, + "data":[ + { + "action":"replaceContent", + "selector":"#notthere", + "page filter (optional)":"", + "fireflies":"https://main--milo--adobecom.hlx.page/drafts/vgoodrich/fragments/139173-mep-and/android" + } + ] + }, + "placeholders":{ + "total":0, + "offset":0, + "limit":0, + "data":[] + }, + ":version":3, + ":names":[ + "info", + "experiences", + "placeholders" + ], + ":type":"multi-sheet" +} + diff --git a/test/features/personalization/personalization.test.js b/test/features/personalization/personalization.test.js index dd84c95073..05364ba089 100644 --- a/test/features/personalization/personalization.test.js +++ b/test/features/personalization/personalization.test.js @@ -1,8 +1,9 @@ import { expect } from '@esm-bundle/chai'; import { readFile } from '@web/test-runner-commands'; import { stub } from 'sinon'; -import { getConfig, loadBlock } from '../../../libs/utils/utils.js'; +import { getConfig, setConfig, loadBlock } from '../../../libs/utils/utils.js'; import initFragments from '../../../libs/blocks/fragment/fragment.js'; +import { ENTITLEMENT_MAP } from '../../../libs/features/personalization/entitlements.js'; import { applyPers } from '../../../libs/features/personalization/personalization.js'; document.head.innerHTML = await readFile({ path: './mocks/metadata.html' }); @@ -19,6 +20,10 @@ const setFetchResponse = (data, type = 'json') => { ); }; +// Modify the entitlement map with custom keys so tests doesn't rely on real data +ENTITLEMENT_MAP['11111111-aaaa-bbbb-6666-cccccccccccc'] = 'my-special-app'; +ENTITLEMENT_MAP['22222222-xxxx-bbbb-7777-cccccccccccc'] = 'fireflies'; + // Note that the manifestPath doesn't matter as we stub the fetch describe('Functional Test', () => { it('replaceContent should replace an element with a fragment', async () => { @@ -205,4 +210,16 @@ describe('Functional Test', () => { await applyPers([{ manifestPath: '/path/to/manifest.json' }]); expect(document.body.dataset.mep).to.equal('not firefox|not'); }); + + it('should read and use entitlement data', async () => { + setConfig(getConfig()); + const { entitlements } = getConfig(); + + entitlements(['some-app', 'fireflies']); + let manifestJson = await readFile({ path: './mocks/manifestUseEntitlements.json' }); + manifestJson = JSON.parse(manifestJson); + setFetchResponse(manifestJson); + await applyPers([{ manifestPath: '/path/to/manifest.json' }]); + expect(document.body.dataset.mep).to.equal('fireflies|manifest'); + }); }); diff --git a/test/utils/utils.test.js b/test/utils/utils.test.js index 30c8d136bc..fdd016ed19 100644 --- a/test/utils/utils.test.js +++ b/test/utils/utils.test.js @@ -235,8 +235,7 @@ describe('Utils', () => { }); it('Sets up milo.deferredPromise', async () => { - window.milo = {}; - const resolveDeferred = utils.setupDeferredPromise(); + const { resolveDeferred } = utils.getConfig(); expect(window.milo.deferredPromise).to.exist; utils.loadDeferred(document, [], {}, resolveDeferred); const result = await window.milo.deferredPromise;