Skip to content

Commit

Permalink
MWPW-153962: Introduce maslibs query parameter (adobecom#2544)
Browse files Browse the repository at this point in the history
* MWPW-153962: Introduce maslibs query parameter

Similar to milolibs query parameter, when present,
it will load commerce.js from the external M@S repository.

* update comment

* remove only

* improve cov + fix promise issue

* retore tests

* revert change in head.html

will raise a second PR only for this change

* update port to avoid conflicts

* fix test

* update tests

* restrict maslibs to lower envs + stage

* restrict maslibs to lower envs + stage

* update /lib to /libs

* update /lib to /libs

* update comments
  • Loading branch information
yesil authored Jul 10, 2024
1 parent 5b64cf1 commit f4e3b65
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 12 deletions.
58 changes: 48 additions & 10 deletions libs/blocks/merch/merch.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,32 @@ const LOADING_ENTITLEMENTS = 'loading-entitlements';
let log;
let upgradeOffer = null;

/**
* Given a url, calculates the hostname of MAS platform.
* Supports, www prod, stage, local and feature branches.
* if params are missing, it will return the latest calculated or default value.
* @param {string} hostname optional
* @param {string} maslibs optional
* @returns base url for mas platform
*/
export function getMasBase(hostname, maslibs) {
let { baseUrl } = getMasBase;
if (!baseUrl) {
if (maslibs === 'stage') {
baseUrl = 'https://www.stage.adobe.com/mas';
} else if (maslibs === 'local') {
baseUrl = 'http://localhost:9001';
} else if (maslibs) {
const extension = /.page$/.test(hostname) ? 'page' : 'live';
baseUrl = `https://${maslibs}.hlx.${extension}`;
} else {
baseUrl = 'https://www.adobe.com/mas';
}
getMasBase.baseUrl = baseUrl;
}
return baseUrl;
}

export async function polyfills() {
if (polyfills.promise) return polyfills.promise;
let isSupported = false;
Expand Down Expand Up @@ -326,15 +352,18 @@ export async function getModalAction(offers, options) {
}

export async function getCheckoutAction(offers, options, imsSignedInPromise) {
const [downloadAction, upgradeAction, modalAction] = await Promise.all([
getDownloadAction(options, imsSignedInPromise, offers),
getUpgradeAction(options, imsSignedInPromise, offers),
getModalAction(offers, options),
]).catch((e) => {
try {
await imsSignedInPromise;
const [downloadAction, upgradeAction, modalAction] = await Promise.all([
getDownloadAction(options, imsSignedInPromise, offers),
getUpgradeAction(options, imsSignedInPromise, offers),
getModalAction(offers, options),
]);
return downloadAction || upgradeAction || modalAction;
} catch (e) {
log?.error('Failed to resolve checkout action', e);
return [];
});
return downloadAction || upgradeAction || modalAction;
}
}

/**
Expand All @@ -349,7 +378,15 @@ export async function initService(force = false) {
const { env, commerce = {}, locale } = getConfig();
commerce.priceLiteralsPromise = fetchLiterals(PRICE_LITERALS_URL);
initService.promise = initService.promise ?? polyfills().then(async () => {
const commerceLib = await import('../../deps/commerce.js');
const { hostname, searchParams } = new URL(window.location.href);
let commerceLibPath = '../../deps/commerce.js';
if (/hlx\.(page|live)$|localhost$|www\.stage\.adobe\.com$/.test(hostname)) {
const maslibs = searchParams.get('maslibs');
if (maslibs) {
commerceLibPath = `${getMasBase(hostname, maslibs)}/libs/commerce.js`;
}
}
const commerceLib = await import(commerceLibPath);
const service = await commerceLib.init(() => ({
env,
commerce,
Expand All @@ -376,8 +413,9 @@ export async function getCommerceContext(el, params) {
}

/**
* Checkout parameter can be set Merch link, code config (scripts.js) or be a default from tacocat.
* To get the default, 'undefinded' should be passed, empty string will trigger an error!
* Checkout parameter can be set on the merch link,
* code config (scripts.js) or be a default from tacocat.
* To get the default, 'undefined' should be passed, empty string will trigger an error!
*
* clientId - code config -> default (adobe_com)
* workflow - merch link -> metadata -> default (UCv3)
Expand Down
4 changes: 4 additions & 0 deletions test/blocks/merch/mas/libs/commerce.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const mock = true;
const init = () => ({ imsSignedInPromise: Promise.resolve(), mock });
// eslint-disable-next-line import/prefer-default-export
export { init };
65 changes: 63 additions & 2 deletions test/blocks/merch/merch.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import merch, {
getCheckoutAction,
PRICE_LITERALS_URL,
PRICE_TEMPLATE_REGULAR,
getMasBase,
} from '../../../libs/blocks/merch/merch.js';

import { mockFetch, unmockFetch, readMockText } from './mocks/fetch.js';
Expand Down Expand Up @@ -72,6 +73,16 @@ const config = {
placeholders: { 'upgrade-now': 'Upgrade Now', download: 'Download' },
};

const updateSearch = ({ maslibs } = {}) => {
const url = new URL(window.location);
if (!maslibs) {
url.searchParams.delete('maslibs');
} else {
url.searchParams.set('maslibs', maslibs);
}
window.history.pushState({}, '', url);
};

/**
* utility function that tests Price spans against mock HTML
*
Expand Down Expand Up @@ -147,6 +158,7 @@ describe('Merch Block', () => {

afterEach(() => {
setSubscriptionsData();
updateSearch();
});

it('does not decorate merch with bad content', async () => {
Expand Down Expand Up @@ -531,12 +543,22 @@ describe('Merch Block', () => {
});

it('getCheckoutAction: handles errors gracefully', async () => {
const action = await getCheckoutAction([{ productArrangement: {} }], {}, Promise.reject(new Error('error')));
expect(action).to.be.undefined;
const imsSignedInPromise = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('error'));
}, 1);
});
const action = await getCheckoutAction([{ productArrangement: {} }], {}, imsSignedInPromise);
expect(action).to.be.empty;
});
});

describe('Upgrade Flow', () => {
beforeEach(() => {
getMasBase.baseUrl = undefined;
updateSearch({});
});

it('updates CTA text to Upgrade Now', async () => {
mockIms();
getUserEntitlements();
Expand Down Expand Up @@ -668,4 +690,43 @@ describe('Merch Block', () => {
});
});
});

describe('M@S consumption', () => {
describe('maslibs parameter', () => {
beforeEach(() => {
getMasBase.baseUrl = undefined;
updateSearch({});
});

it('should load commerce.js via maslibs', async () => {
initService.promise = undefined;
getMasBase.baseUrl = 'http://localhost:2000/test/blocks/merch/mas';
updateSearch({ maslibs: 'test' });
setConfig(config);
await mockIms();
const commerce = await initService(true);
expect(commerce.mock).to.be.true;
});

it('should return the default Adobe URL if no maslibs parameter is present', () => {
expect(getMasBase()).to.equal('https://www.adobe.com/mas');
});

it('should return the stage Adobe URL if maslibs=stage', () => {
expect(getMasBase('https://main--milo--adobecom.hlx.live', 'stage')).to.equal('https://www.stage.adobe.com/mas');
});

it('should return the local URL if maslibs=local', () => {
expect(getMasBase('https://main--milo--adobecom.hlx.live', 'local')).to.equal('http://localhost:9001');
});

it('should return the hlx live URL from the fork if maslibs contains double dashes', () => {
expect(getMasBase('https://main--milo--adobecom.hlx.live', 'test--mas--user')).to.equal('https://test--mas--user.hlx.live');
});

it('should return the hlx page URL from the fork if maslibs contains double dashes', () => {
expect(getMasBase('https://main--milo--adobecom.hlx.page', 'test--mas--user')).to.equal('https://test--mas--user.hlx.page');
});
});
});
});

0 comments on commit f4e3b65

Please sign in to comment.