Skip to content

Commit

Permalink
MWPW-168109, 166126 MEP Stage Batch (#3709)
Browse files Browse the repository at this point in the history
* MWPW-168109: do not modify absolute path to a script hosted in DAM when using insertScript action (#3708)

* MWPW-168109: do not modify absolute path to a script hosted in DAM when using insertScript action

* wording change

* Update libs/features/personalization/personalization.js

Co-authored-by: Vivian A Goodrich <[email protected]>

---------

Co-authored-by: Denys Fedotov <[email protected]>
Co-authored-by: Vivian A Goodrich <[email protected]>

* [MWPW-166126] Add new "updateAttribute" action to MEP (#3670)

* Get last string, check for match, save as attribute.

* Attribute update, working state.

* Refactor, good state.

* Parameter update refactor.

* Unit testing.

* Highlight updates.

* Unit test update.

* Code review updates.

* Error log update.

* Spacing fix.

* PR update.

* Update libs/features/personalization/personalization.js

* unit test update

---------

Co-authored-by: Vivian A Goodrich <[email protected]>
Co-authored-by: Vivian A Goodrich <[email protected]>

---------

Co-authored-by: Denys Fedotov <[email protected]>
Co-authored-by: Denys Fedotov <[email protected]>
Co-authored-by: Dave Linhart <[email protected]>
  • Loading branch information
4 people authored Feb 20, 2025
1 parent 5de9671 commit fd32858
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 20 deletions.
91 changes: 72 additions & 19 deletions libs/features/personalization/personalization.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ export const DATA_TYPE = {

const IN_BLOCK_SELECTOR_PREFIX = 'in-block:';

const isDamContent = (path) => path?.includes('/content/dam/');

export const normalizePath = (p, localize = true) => {
let path = p;

if (!path?.includes('/')) {
return path;
}
if (isDamContent(path) || !path?.includes('/')) return path;

const config = getConfig();
if (path.startsWith('https://www.adobe.com/federal/')) {
Expand Down Expand Up @@ -123,6 +123,7 @@ const CREATE_CMDS = {
const COMMANDS_KEYS = {
remove: 'remove',
replace: 'replace',
updateAttribute: 'updateattribute',
};

function addIds(el, manifestId, targetManifestId) {
Expand Down Expand Up @@ -222,6 +223,34 @@ const COMMANDS = {
createContent(el, cmd),
);
},
[COMMANDS_KEYS.updateAttribute]: (el, cmd) => {
const { manifestId, targetManifestId } = cmd;
if (!cmd.attribute || !cmd.content) return;
const [attribute, parameter] = cmd.attribute.split('_');

let value;

if (attribute === 'href' && parameter) {
const href = el.getAttribute('href');
try {
const url = new URL(href);
const parameters = new URLSearchParams(url.search);
parameters.set(parameter, cmd.content);
url.search = parameters.toString();
value = url.toString();
} catch (error) {
/* c8 ignore next 2 */
console.log(`Invalid updateAttribute URL: ${href}`, error.message || error);
}
} else {
value = cmd.content;
}

if (value) {
el.setAttribute(attribute, value);
addIds(el, manifestId, targetManifestId);
}
},
};

const log = (...msg) => {
Expand Down Expand Up @@ -433,22 +462,34 @@ function getModifiers(selector) {
}
return { sel, modifiers };
}
export function modifyNonFragmentSelector(selector) {
export function modifyNonFragmentSelector(selector, action) {
const { sel, modifiers } = getModifiers(selector);

let modifiedSelector = sel
.split('>').join(' > ')
.split(',').join(' , ')
.replaceAll(/main\s*>?\s*(section\d*)/gi, '$1')
.split(/\s+/)
.map(modifySelectorTerm)
.join(' ')
.trim();

let attribute;

if (action === COMMANDS_KEYS.updateAttribute) {
const string = modifiedSelector.split(' ').pop();
attribute = string.replace('.', '');
modifiedSelector = modifiedSelector.replace(string, '').trim();
}

return {
modifiedSelector: sel
.split('>').join(' > ')
.split(',').join(' , ')
.replaceAll(/main\s*>?\s*(section\d*)/gi, '$1')
.split(/\s+/)
.map(modifySelectorTerm)
.join(' ')
.trim(),
modifiedSelector,
modifiers,
attribute,
};
}

function getSelectedElements(sel, rootEl, forceRootEl) {
function getSelectedElements(sel, rootEl, forceRootEl, action) {
const root = forceRootEl ? rootEl : document;
const selector = sel.trim();
if (!selector) return {};
Expand All @@ -464,7 +505,12 @@ function getSelectedElements(sel, rootEl, forceRootEl) {
return { els: [], modifiers: [] };
}
}
const { modifiedSelector, modifiers } = modifyNonFragmentSelector(selector);
const {
modifiedSelector,
modifiers,
attribute,
} = modifyNonFragmentSelector(selector, action);

let els;
try {
els = root.querySelectorAll(modifiedSelector);
Expand All @@ -473,10 +519,11 @@ function getSelectedElements(sel, rootEl, forceRootEl) {
log('Invalid selector: ', selector);
return null;
}
if (modifiers.includes(FLAGS.all) || !els.length) return { els, modifiers };
if (modifiers.includes(FLAGS.all) || !els.length) return { els, modifiers, attribute };
els = [els[0]];
return { els, modifiers };
return { els, modifiers, attribute };
}

const addHash = (url, newHash) => {
if (!newHash) return url;
try {
Expand Down Expand Up @@ -523,8 +570,13 @@ export function handleCommands(
cmd.selectorType = IN_BLOCK_SELECTOR_PREFIX;
return;
}
const { els, modifiers } = getSelectedElements(selector, rootEl, forceRootEl);
cmd.modifiers = modifiers;
const {
els,
modifiers,
attribute,
} = getSelectedElements(selector, rootEl, forceRootEl, action);

Object.assign(cmd, { modifiers, attribute });

els?.forEach((el) => {
if (!el
Expand Down Expand Up @@ -1195,7 +1247,7 @@ function sendTargetResponseAnalytics(failure, responseStart, timeoutLocal, messa
},
},
data:
{ _adobe_corpnew: { digitalData: { primaryEvent: { eventInfo: { eventName: val } } } } },
{ _adobe_corpnew: { digitalData: { primaryEvent: { eventInfo: { eventName: val } } } } },
});
}, { once: true });
}
Expand Down Expand Up @@ -1290,6 +1342,7 @@ export async function init(enablements = {}) {
enablePersV2,
hybridPersEnabled,
};

manifests = manifests.concat(await combineMepSources(pzn, promo, mepParam));
manifests?.forEach((manifest) => {
if (manifest.disabled) return;
Expand Down
13 changes: 13 additions & 0 deletions test/features/personalization/actions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,19 @@ describe('replace action', () => {
});
});

describe('updateAttribute action', async () => {
it('updateAttribute should add or modify a html element attribute', async () => {
let manifestJson = await readFile({ path: './mocks/actions/manifestUpdateAttribute.json' });
manifestJson = JSON.parse(manifestJson);
setFetchResponse(manifestJson);
await init(mepSettings);
await handleCommands(manifestJson.data, undefined, true, true);
expect(document.querySelector('.marquee h2').getAttribute('class')).to.equal('added-class');
expect(document.querySelector('.marquee strong a').getAttribute('href')).to.equal('https://www.google.com/?osi=new-parameter#_inline');
expect(document.querySelector('.marquee em a').getAttribute('new-attribute')).to.equal('added-attribute');
});
});

describe('insertAfter action', async () => {
it('insertContentAfter should add fragment after target content and fragment', async () => {
let manifestJson = await readFile({ path: './mocks/actions/manifestInsertAfter.json' });
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"total": 5,
"offset": 0,
"limit": 5,
"data": [
{
"action": "updateattribute",
"selector": "marquee strong a href",
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "",
"content": "https://www.google.com/"
},
{
"action": "updateattribute",
"selector": "marquee strong a href_osi",
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "",
"content": "new-parameter"
},
{
"action": "updateattribute",
"selector": "marquee em a new-attribute",
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "",
"content": "added-attribute"
},
{
"action": "updateattribute",
"selector": "marquee h2 class",
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "",
"content": "added-class"
},
{
"action": "updateattribute",
"selector": ".second-marquee-title strong a href",
"page filter (optional)": "",
"param-newoffer=123": "",
"chrome": "",
"content": "thisshouldfail"
}
],
":type": "sheet"
}
21 changes: 21 additions & 0 deletions test/features/personalization/modifyNonFragmentSelector.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
import { expect } from '@esm-bundle/chai';
import { modifyNonFragmentSelector } from '../../../libs/features/personalization/personalization.js';

const attributeValue = [
{
c: 'href',
b: 'marquee primary-cta href',
a: '.marquee strong a',
},
{
c: 'href_osi',
b: 'marquee primary-cta href_osi',
a: '.marquee strong a',
},
];

const values = [
{
b: 'body > main > div',
Expand Down Expand Up @@ -160,4 +173,12 @@ describe('test different values', () => {
expect(modifiers).to.deep.equal(value.m || []);
});
});
attributeValue.forEach((value) => {
it(`should return the expected value for ${value.b} and ${value.c}`, () => {
const { modifiedSelector, modifiers, attribute } = modifyNonFragmentSelector(value.b, 'updateattribute');
expect(modifiedSelector).to.equal(value.a);
expect(modifiers).to.deep.equal(value.m || []);
expect(attribute).to.equal(value.c);
});
});
});
9 changes: 8 additions & 1 deletion test/features/personalization/personalization.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { readFile } from '@web/test-runner-commands';
import { assert, stub } from 'sinon';
import { getConfig, setConfig } from '../../../libs/utils/utils.js';
import {
handleFragmentCommand, applyPers, cleanAndSortManifestList,
handleFragmentCommand, applyPers, cleanAndSortManifestList, normalizePath,
init, matchGlob, createContent, combineMepSources, buildVariantInfo,
} from '../../../libs/features/personalization/personalization.js';
import mepSettings from './mepSettings.js';
Expand Down Expand Up @@ -90,6 +90,13 @@ describe('Functional Test', () => {
expect(document.querySelector('.custom-block-2')).to.be.null;
});

it('should not normalize absolute path to a script file, if the file is hosted in DAM', async () => {
const DAMpath = 'https://www.adobe.com/content/dam/cc/optimization/mwpw-168109/test.js';
const nonDAMpath = 'https://www.adobe.com/foo/test.js';
expect(normalizePath(DAMpath)).to.include('https://www.adobe.com');
expect(normalizePath(nonDAMpath)).to.not.include('https://www.adobe.com');
});

it('scheduled manifest should apply changes if active (bts)', async () => {
const config = getConfig();
config.mep = {
Expand Down

0 comments on commit fd32858

Please sign in to comment.