diff --git a/.eslintrc.js b/.eslintrc.js index 5e06277005..f24adfa387 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -45,6 +45,17 @@ module.exports = { files: ['test/**/*.js'], rules: { 'no-console': 0 }, }, + { + // Override for nala test + files: ['nala/**/*.js', 'nala/**/*.test.js'], + rules: { + 'no-console': 0, + 'import/no-extraneous-dependencies': 0, + 'max-len': 0, + 'chai-friendly/no-unused-expressions': 0, + 'no-plusplus': 0, + }, + }, ], ignorePatterns: [ '/libs/deps/*', diff --git a/.github/workflows/helpers.js b/.github/workflows/helpers.js index 7b36bc38b1..056ac563f4 100644 --- a/.github/workflows/helpers.js +++ b/.github/workflows/helpers.js @@ -26,8 +26,8 @@ const RCPDates = [ end: new Date('2024-09-12T14:00:00-07:00'), }, { - start: new Date('2024-10-14T00:00:00-07:00'), - end: new Date('2024-11-18T17:00:00-08:00'), + start: new Date('2024-10-07T00:00:00-07:00'), + end: new Date('2024-10-18T17:00:00-07:00'), }, { start: new Date('2024-11-17T00:00:00-08:00'), diff --git a/.github/workflows/run-mas-tests.yaml b/.github/workflows/run-mas-tests.yaml new file mode 100644 index 0000000000..c5ccba75b7 --- /dev/null +++ b/.github/workflows/run-mas-tests.yaml @@ -0,0 +1,46 @@ +name: MAS Unit Tests +on: + push: + branches: + - main + pull_request: + types: [opened, synchronize, reopened] +jobs: + run-tests: + name: Running tests + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [20.x] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Set up Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Install npm dependencies + run: npm install + working-directory: libs/features/mas + + - name: Run npm test + run: npm test + working-directory: libs/features/mas + + - name: Upload commerce coverage to Codecov + uses: codecov/codecov-action@v4 + with: + name: mas-commerce + token: ${{ secrets.CODECOV_TOKEN }} + files: libs/features/mas/commerce/coverage/lcov.info + + - name: Upload web-components coverage to Codecov + uses: codecov/codecov-action@v4 + with: + name: mas-web-components + token: ${{ secrets.CODECOV_TOKEN }} + files: libs/features/mas/web-components/coverage/lcov.info diff --git a/.github/workflows/run-nala-default.yml b/.github/workflows/run-nala-default.yml new file mode 100644 index 0000000000..4031be7c8d --- /dev/null +++ b/.github/workflows/run-nala-default.yml @@ -0,0 +1,48 @@ +name: Run Nala Tests + +on: + push: + branches: + - stage + - main + pull_request: + branches: + - stage + - main + types: [opened, synchronize, reopened] + +jobs: + run-nala-tests: + name: Running Nala E2E UI Tests + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [20.x] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Set up Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Set execute permission for nalarun.sh + run: chmod +x ./nala/utils/pr.run.sh + + - name: Run Nala Tests via pr.run.sh + run: ./nala/utils/pr.run.sh + env: + labels: ${{ join(github.event.pull_request.labels.*.name, ' ') }} + branch: ${{ github.event.pull_request.head.ref }} + repoName: ${{ github.repository }} + prUrl: ${{ github.event.pull_request.head.repo.html_url }} + prOrg: ${{ github.event.pull_request.head.repo.owner.login }} + prRepo: ${{ github.event.pull_request.head.repo.name }} + prBranch: ${{ github.event.pull_request.head.ref }} + prBaseBranch: ${{ github.event.pull_request.base.ref }} + GITHUB_ACTION_PATH: ${{ github.workspace }} + IMS_EMAIL: ${{ secrets.IMS_EMAIL }} + IMS_PASS: ${{ secrets.IMS_PASS }} diff --git a/.github/workflows/run-nala.yml b/.github/workflows/run-nala.yml index 7b701eb8e5..286b7df3c9 100644 --- a/.github/workflows/run-nala.yml +++ b/.github/workflows/run-nala.yml @@ -1,4 +1,4 @@ -name: Nala Tests +name: Nala Tests (Consuming Apps) on: pull_request: @@ -7,13 +7,13 @@ on: jobs: action: name: Running E2E & IT - if: contains(github.event.pull_request.labels.*.name, 'run-nala') runs-on: ubuntu-latest + if: contains(join(github.event.pull_request.labels.*.name, ' '), 'run-nala-on-') steps: - name: Check out repository uses: actions/checkout@v4 - - name: Run Nala + - name: Run Nala Tests (Consuming Apps) uses: adobecom/nala@main # Change if doing dev work env: labels: ${{ join(github.event.pull_request.labels.*.name, ' ') }} diff --git a/.hlxignore b/.hlxignore index e394ce0b3b..0fdf90bee2 100644 --- a/.hlxignore +++ b/.hlxignore @@ -3,6 +3,7 @@ .* *.md test/* +nala build/* LICENSE web-test-runner.config.mjs diff --git a/libs/blocks/article-feed/article-helpers.js b/libs/blocks/article-feed/article-helpers.js index 779099e8f1..e9cfe1d59b 100644 --- a/libs/blocks/article-feed/article-helpers.js +++ b/libs/blocks/article-feed/article-helpers.js @@ -129,7 +129,7 @@ export async function loadTaxonomy() { a.href = tax.link; } else { // eslint-disable-next-line no-console - window.lana.log(`Trying to get a link for an unknown topic: ${topic} (current page)`, { tags: 'errorType=warn,module=article-feed' }); + window.lana.log(`Trying to get a link for an unknown topic: ${topic} (current page)`, { tags: 'article-feed' }); a.href = '#'; } delete a.dataset.topicLink; diff --git a/libs/blocks/article-header/article-header.js b/libs/blocks/article-header/article-header.js index fb4ea8235f..8f94e0b405 100644 --- a/libs/blocks/article-header/article-header.js +++ b/libs/blocks/article-header/article-header.js @@ -32,14 +32,15 @@ function openPopup(e) { } async function buildAuthorInfo(authorEl, bylineContainer) { - const { href, textContent } = authorEl; + const { textContent } = authorEl; + const link = authorEl.href || authorEl.dataset.authorPage; const config = getConfig(); const base = config.miloLibs || config.codeRoot; const authorImg = createTag('div', { class: 'article-author-image' }); authorImg.style.backgroundImage = `url(${base}/blocks/article-header/adobe-logo.svg)`; bylineContainer.prepend(authorImg); - const doc = await validateAuthorUrl(href); + const doc = await validateAuthorUrl(link); if (!doc) { const p = createTag('p', null, textContent); authorEl.replaceWith(p); @@ -48,7 +49,7 @@ async function buildAuthorInfo(authorEl, bylineContainer) { const img = doc.querySelector('img'); if (img) { - img.setAttribute('alt', authorEl.textContent); + img.setAttribute('alt', textContent); authorImg.append(img); if (!img.complete) { img.addEventListener('load', () => { @@ -197,7 +198,7 @@ export default async function init(blockEl) { bylineContainer.firstElementChild.classList.add('article-byline-info'); const authorContainer = bylineContainer.firstElementChild.firstElementChild; - const authorEl = authorContainer.querySelector('a'); + const authorEl = authorContainer.firstElementChild; authorContainer.classList.add('article-author'); buildAuthorInfo(authorEl, bylineContainer); diff --git a/libs/blocks/aside/aside.js b/libs/blocks/aside/aside.js index 59975b58f6..fc488d4ada 100644 --- a/libs/blocks/aside/aside.js +++ b/libs/blocks/aside/aside.js @@ -96,6 +96,7 @@ function addCloseButton(el) { el.querySelector('.foreground').appendChild(closeBtn); closeBtn.addEventListener('click', (e) => { e.target.closest('.section').classList.add('close-sticky-section'); + document.dispatchEvent(new CustomEvent('milo:sticky:closed')); }); } diff --git a/libs/blocks/caas-config/caas-config.js b/libs/blocks/caas-config/caas-config.js index db1859b792..48fc34b052 100644 --- a/libs/blocks/caas-config/caas-config.js +++ b/libs/blocks/caas-config/caas-config.js @@ -660,7 +660,7 @@ const PaginationPanel = () => { options=${defaultOptions.paginationType} /> <${Select} - label="Animation Style" + label="Carousel Animation Style" prop="paginationAnimationStyle" options=${defaultOptions.paginationAnimationStyle} /> diff --git a/libs/blocks/caas/utils.js b/libs/blocks/caas/utils.js index e83d9b3e36..e87a4f5c33 100644 --- a/libs/blocks/caas/utils.js +++ b/libs/blocks/caas/utils.js @@ -738,8 +738,10 @@ export const getConfig = async (originalState, strs = {}) => { lastViewedSession: state.lastViewedSession || '', }, customCard: ['card', `return \`${state.customCard}\``], + linkTransformer: pageConfig.caasLinkTransformer || {}, headers: caasRequestHeaders, }; + return config; }; diff --git a/libs/blocks/editorial-card/editorial-card.css b/libs/blocks/editorial-card/editorial-card.css index 8191393f80..a244581810 100644 --- a/libs/blocks/editorial-card/editorial-card.css +++ b/libs/blocks/editorial-card/editorial-card.css @@ -92,6 +92,11 @@ margin-top: auto; } +.editorial-card .vp-media .tablet-only, +.editorial-card .vp-media .desktop-only { + display: none; +} + .editorial-card .card-footer > div { display: flex; flex-direction: column; @@ -142,11 +147,22 @@ height: 100%; } +.editorial-card .background .milo-video, +.editorial-card .background .milo-iframe { + height: 100%; + padding-bottom: 0; +} + .editorial-card .lockup-area, .editorial-card .device { margin: 0; } +.editorial-card .device { + font-size: var(--type-body-xxs-size); + line-height: var(--type-body-xxs-lh); +} + .editorial-card .lockup-area { row-gap: var(--spacing-xxs); } @@ -173,7 +189,6 @@ justify-content: center; } - .editorial-card hr { background-color: currentcolor; border: none; @@ -248,3 +263,24 @@ .editorial-card.footer-align-left .action-area { justify-content: start; } .editorial-card.footer-align-center .action-area { justify-content: center; } + + +/* Tablet up */ +@media screen and (min-width: 600px) { + .editorial-card .vp-media .mobile-only, + .editorial-card .vp-media .desktop-only { + display: none; + } + + .editorial-card .vp-media .tablet-only { display: block; } +} + +/* desktop up */ +@media screen and (min-width: 1200px) { + .editorial-card .vp-media .mobile-only, + .editorial-card .vp-media .tablet-only { + display: none; + } + + .editorial-card .vp-media .desktop-only { display: block; } +} diff --git a/libs/blocks/editorial-card/editorial-card.js b/libs/blocks/editorial-card/editorial-card.js index 9cd9a31d2b..931b17e8e8 100644 --- a/libs/blocks/editorial-card/editorial-card.js +++ b/libs/blocks/editorial-card/editorial-card.js @@ -25,9 +25,10 @@ async function decorateLockupFromContent(el) { const extendDeviceContent = (el) => { const detail = el.querySelector('[class^="detail-"]'); const prevElem = detail?.previousElementSibling; - if (!prevElem || ![...prevElem.classList].some((c) => c.startsWith('body-'))) return; - prevElem.classList.remove('body-m'); - prevElem.classList.add('body-xxs', 'device'); + if (!prevElem) return; + const elBodyClass = [...prevElem.classList].find((c) => c.startsWith('body-')); + prevElem.classList.remove(elBodyClass); + prevElem.classList.add('device'); }; const decorateMedia = (el, media) => { @@ -37,14 +38,14 @@ const decorateMedia = (el, media) => { if (mediaVideo) { applyHoverPlay(mediaVideo); } - if (media.children.length > 1) decorateBlockBg(el, media); + if (media.children.length > 1) decorateBlockBg(el, media, { className: 'vp-media' }); }; const decorateForeground = async (el, rows) => { - rows.forEach(async (row, i) => { + rows.forEach((row, i) => { if (i === 0) { row.classList.add('foreground'); - await decorateLockupFromContent(row); + decorateLockupFromContent(row); } else if (i === (rows.length - 1)) { row.classList.add('card-footer'); if (!row.textContent.trim()) row.classList.add('empty'); @@ -56,8 +57,10 @@ const decorateForeground = async (el, rows) => { }); }; -const decorateBgRow = (el, background) => { - if (background.textContent.trim() === '') { +const decorateBgRow = (el, background, remove = false) => { + const rows = background.querySelectorAll(':scope > div'); + const bgRowsEmpty = [...rows].every((row) => row.innerHTML.trim() === ''); + if (bgRowsEmpty || remove) { el.classList.add('no-bg'); background.remove(); return; @@ -78,38 +81,50 @@ function handleClickableCard(el) { const init = async (el) => { el.classList.add('con-block'); - if (el.className.includes('open')) { - el.classList.add('no-border', 'l-rounded-corners-image', 'static-links-copy'); - } - if (el.className.includes('rounded-corners')) { - loadStyle(`${base}/styles/rounded-corners.css`); - } + const hasOpenClass = el.className.includes('open'); + if (hasOpenClass) el.classList.add('no-border', 'l-rounded-corners-image', 'static-links-copy'); + if (el.className.includes('rounded-corners')) loadStyle(`${base}/styles/rounded-corners.css`); if (![...el.classList].some((c) => c.endsWith('-lockup'))) el.classList.add('m-lockup'); let rows = el.querySelectorAll(':scope > div'); const [head, middle, ...tail] = rows; if (rows.length === 4) el.classList.add('equal-height'); if (rows.length >= 1) { - const count = rows.length >= 3 ? 'three-plus' : rows.length; + const count = rows.length >= 4 ? 'four-plus' : rows.length; switch (count) { - case 'three-plus': - // 3+ rows (0:bg, 1:media, 2:copy, ...3:static, last:card-footer) - decorateBgRow(el, head); + case 'four-plus': + // 4+ rows (0:bg, 1:media, 2:copy, ...3:static, last:card-footer) + // 4+ rows.open (0:bg[removed], 1:media, 2:copy, ...3:static, last:card-footer) + decorateBgRow(el, head, hasOpenClass); rows = tail; - await decorateForeground(el, rows); decorateMedia(el, middle); + await decorateForeground(el, rows); + break; + case 3: + // 3 rows (0:bg, 1:media, last:copy) + // 3 rows.open (0:media, 1:copy, last:card-footer) + if (hasOpenClass) { + el.classList.add('no-bg'); + rows = [middle, tail[0]]; + decorateMedia(el, head); + } else { + rows = tail; + decorateBgRow(el, head); + decorateMedia(el, middle); + } + await decorateForeground(el, rows); break; case 2: // 2 rows (0:media, 1:copy) - rows = middle; - await decorateForeground(el, [rows]); + rows = [middle]; decorateMedia(el, head); el.classList.add('no-bg'); + await decorateForeground(el, rows); break; case 1: // 1 row (0:copy) - rows = head; - await decorateForeground(el, [rows]); + rows = [head]; el.classList.add('no-bg', 'no-media'); + await decorateForeground(el, rows); break; default: } diff --git a/libs/blocks/event-rich-results/event-rich-results.js b/libs/blocks/event-rich-results/event-rich-results.js index e0df0551d6..78f69ff15b 100644 --- a/libs/blocks/event-rich-results/event-rich-results.js +++ b/libs/blocks/event-rich-results/event-rich-results.js @@ -16,7 +16,7 @@ function logNullValues(obj) { Object.keys(obj).forEach((key) => { const value = obj[key]; if (!value || value === '') { - window.lana.log(`Event property ${key} is not defined`, { tags: 'errorType=warn,module=event-rich-results' }); + window.lana.log(`Event property ${key} is not defined`, { tags: 'event-rich-results' }); } logNullValues(value); }); diff --git a/libs/blocks/faas/utils.js b/libs/blocks/faas/utils.js index 70c17f093d..9c3249d7e4 100644 --- a/libs/blocks/faas/utils.js +++ b/libs/blocks/faas/utils.js @@ -248,7 +248,7 @@ const beforeSubmitCallback = () => { }), }) .catch((error) => { - window.lana.log(`AA Sandbox Error: ${error.reason || error.error || error.message || error}`, { tags: 'errorType=info,module=faas' }); + window.lana.log(`AA Sandbox Error: ${error.reason || error.error || error.message || error}`, { tags: 'faas', errorType: 'i' }); }); } }; diff --git a/libs/blocks/global-navigation/global-navigation.css b/libs/blocks/global-navigation/global-navigation.css index 60f84165b3..cf32fe32ec 100644 --- a/libs/blocks/global-navigation/global-navigation.css +++ b/libs/blocks/global-navigation/global-navigation.css @@ -461,6 +461,10 @@ header.global-navigation { background-color: var(--feds-background-link--hover); } +.feds-utilities .unav-comp-profile .secondary-button{ + line-height: inherit; +} + #feds-googleLogin { position: absolute; top: 100%; diff --git a/libs/blocks/global-navigation/global-navigation.js b/libs/blocks/global-navigation/global-navigation.js index 1440b0b94c..bd21c226ae 100644 --- a/libs/blocks/global-navigation/global-navigation.js +++ b/libs/blocks/global-navigation/global-navigation.js @@ -322,7 +322,7 @@ class Gnav { isDesktop.addEventListener('change', closeAllDropdowns); }, 'Error in global navigation init', 'errorType=error,module=gnav'); - ims = async () => loadIms() + ims = async () => (window.adobeIMS?.initialized ? this.imsReady() : loadIms() .then(() => this.imsReady()) .catch((e) => { if (e?.message === 'IMS timeout') { @@ -330,7 +330,7 @@ class Gnav { return; } lanaLog({ message: 'GNAV: Error with IMS', e, tags: 'errorType=info,module=gnav' }); - }); + })); decorateTopNav = () => { this.elements.mobileToggle = this.decorateToggle(); @@ -521,7 +521,7 @@ class Gnav { return 'linux'; }; - const unavVersion = new URLSearchParams(window.location.search).get('unavVersion') || '1.1'; + const unavVersion = new URLSearchParams(window.location.search).get('unavVersion') || '1.3'; await Promise.all([ loadScript(`https://${environment}.adobeccstatic.com/unav/${unavVersion}/UniversalNav.js`), loadStyles(`https://${environment}.adobeccstatic.com/unav/${unavVersion}/UniversalNav.css`), @@ -608,9 +608,6 @@ class Gnav { locale, imsClientId: window.adobeid?.client_id, theme: isDarkMode() ? 'dark' : 'light', - onReady: () => { - this.decorateAppPrompt({ getAnchorState: () => window.UniversalNav.getComponent?.('app-switcher') }); - }, analyticsContext: { consumer: { name: 'adobecom', @@ -627,8 +624,8 @@ class Gnav { // Exposing UNAV config for consumers CONFIG.universalNav.universalNavConfig = getConfiguration(); - window.UniversalNav(CONFIG.universalNav.universalNavConfig); - + await window.UniversalNav(CONFIG.universalNav.universalNavConfig); + this.decorateAppPrompt({ getAnchorState: () => window.UniversalNav.getComponent?.('app-switcher') }); isDesktop.addEventListener('change', () => { window.UniversalNav.reload(CONFIG.universalNav.universalNavConfig); }); @@ -822,6 +819,7 @@ class Gnav { ${isDesktop.matches ? '' : this.decorateSearch()} ${this.elements.mainNav} ${isDesktop.matches ? this.decorateSearch() : ''} + ${getConfig().searchEnabled === 'on' ? toFragment`` : ''} `; diff --git a/libs/blocks/hero-marquee/hero-marquee.js b/libs/blocks/hero-marquee/hero-marquee.js index 550c5aa2c9..91a0499e68 100644 --- a/libs/blocks/hero-marquee/hero-marquee.js +++ b/libs/blocks/hero-marquee/hero-marquee.js @@ -132,14 +132,14 @@ function parseKeyString(str) { return result; } -function loadContentType(el, key, classes) { +async function loadContentType(el, key, classes) { if (classes !== undefined && classes.length) el.classList.add(...classes); switch (key) { case 'bgcolor': decorateBg(el); break; case 'lockup': - decorateLockupRow(el, classes); + await decorateLockupRow(el, classes); break; case 'qrcode': decorateQr(el); @@ -206,7 +206,7 @@ export default async function init(el) { : null; if (assetUnknown) assetUnknown.classList.add('asset-unknown'); - decorateBlockText(copy, textDefault); + decorateBlockText(copy, textDefault, 'hasDetailHeading'); await decorateLockupFromContent(copy); extendButtonsClass(copy); @@ -237,6 +237,7 @@ export default async function init(el) { } }); + const promiseArr = []; [...rows].forEach(async (row) => { const cols = row.querySelectorAll(':scope > div'); const firstCol = cols[0]; @@ -248,7 +249,9 @@ export default async function init(el) { firstCol.parentElement.classList.add(`row-${parsed.key}`, 'con-block'); firstCol.remove(); cols[1].classList.add('row-wrapper'); - if (contentTypes.includes(parsed.key)) loadContentType(row, parsed.key, parsed.classes); + if (contentTypes.includes(parsed.key)) { + promiseArr.push(loadContentType(row, parsed.key, parsed.classes)); + } } else { row.classList.add('norm'); decorateBlockHrs(row); @@ -256,4 +259,5 @@ export default async function init(el) { } }); decorateTextOverrides(el, ['-heading', '-body', '-detail'], mainCopy); + await Promise.all(promiseArr); } diff --git a/libs/blocks/library-config/lists/templates.js b/libs/blocks/library-config/lists/templates.js index 2c1f780078..736f79c35c 100644 --- a/libs/blocks/library-config/lists/templates.js +++ b/libs/blocks/library-config/lists/templates.js @@ -43,7 +43,7 @@ function formatDom(aemDom, path) { async function formatTemplate(path) { const resp = await fetch(path); - if (!resp.ok) window.lana.log('Could not fetch template path', { tags: 'errorType=info,module=sidekick-templates' }); + if (!resp.ok) window.lana.log('Could not fetch template path', { tags: 'sidekick-templates', errorType: 'i' }); const html = await resp.text(); const dom = new DOMParser().parseFromString(html, 'text/html'); diff --git a/libs/blocks/merch-card-collection/merch-card-collection.js b/libs/blocks/merch-card-collection/merch-card-collection.js index 695f9da51e..dad54cb600 100644 --- a/libs/blocks/merch-card-collection/merch-card-collection.js +++ b/libs/blocks/merch-card-collection/merch-card-collection.js @@ -1,3 +1,4 @@ +import { overrideUrlOrigin } from '../../utils/helpers.js'; import { createTag, decorateLinks, getConfig, loadBlock, loadStyle, localizeLink, } from '../../utils/utils.js'; @@ -57,7 +58,7 @@ async function getCardsRoot(config, html) { } const fetchOverrideCard = (action, config) => new Promise((resolve, reject) => { - fetch(`${localizeLink(action?.target, config)}.plain.html`).then((res) => { + fetch(`${localizeLink(overrideUrlOrigin(action?.target))}.plain.html`).then((res) => { if (res.ok) { res.text().then((cardContent) => { const response = { path: action.target, cardContent: /^
(.*)<\/div>$/.exec(cardContent.replaceAll('\n', ''))[1] }; diff --git a/libs/blocks/merch/merch.js b/libs/blocks/merch/merch.js index 9332d3a331..1a21ac5cac 100644 --- a/libs/blocks/merch/merch.js +++ b/libs/blocks/merch/merch.js @@ -4,7 +4,6 @@ import { import { replaceKey } from '../../features/placeholders.js'; import '../../deps/mas/commerce.js'; -export const PRICE_LITERALS_URL = 'https://www.adobe.com/federal/commerce/price-literals.json'; export const CHECKOUT_LINK_CONFIG_PATH = '/commerce/checkout-link.json'; // relative to libs. export const PRICE_TEMPLATE_DISCOUNT = 'discount'; @@ -180,14 +179,6 @@ export async function fetchEntitlements() { return fetchEntitlements.promise; } -export async function fetchLiterals(url) { - fetchLiterals.promise = fetchLiterals.promise ?? new Promise((resolve) => { - fetch(url) - .then((response) => response.json().then(({ data }) => resolve(data))); - }); - return fetchLiterals.promise; -} - export async function fetchCheckoutLinkConfigs(base = '') { fetchCheckoutLinkConfigs.promise = fetchCheckoutLinkConfigs.promise ?? fetch(`${base}${CHECKOUT_LINK_CONFIG_PATH}`).catch((e) => { @@ -365,6 +356,8 @@ async function openExternalModal(url, getModal) { }); } +const isInternalModal = (url) => /\/fragments\//.test(url); + export async function openModal(e, url, offerType) { e.preventDefault(); e.stopImmediatePropagation(); @@ -372,7 +365,7 @@ export async function openModal(e, url, offerType) { await import('../modal/modal.merch.js'); const offerTypeClass = offerType === OFFER_TYPE_TRIAL ? 'twp' : 'crm'; let modal; - if (/\/fragments\//.test(url)) { + if (isInternalModal(url)) { const fragmentPath = url.split(/hlx.(page|live)/).pop(); modal = await openFragmentModal(fragmentPath, getModal); } else { @@ -399,7 +392,8 @@ export async function getModalAction(offers, options) { const columnName = (offerType === OFFER_TYPE_TRIAL) ? FREE_TRIAL_PATH : BUY_NOW_PATH; let url = checkoutLinkConfig[columnName]; if (!url) return undefined; - url = localizeLink(checkoutLinkConfig[columnName]); + url = isInternalModal(url) + ? localizeLink(checkoutLinkConfig[columnName]) : checkoutLinkConfig[columnName]; return { url, handler: (e) => openModal(e, url, offerType) }; } @@ -428,7 +422,6 @@ export async function initService(force = false) { fetchCheckoutLinkConfigs.promise = undefined; } const { env, commerce = {}, locale } = getConfig(); - commerce.priceLiteralsPromise = fetchLiterals(PRICE_LITERALS_URL); initService.promise = initService.promise ?? polyfills().then(async () => { const commerceLibPath = '../../deps/mas/commerce.js'; const commerceLib = await import(commerceLibPath); diff --git a/libs/blocks/mobile-app-banner/mobile-app-banner.js b/libs/blocks/mobile-app-banner/mobile-app-banner.js index 9c5988ee44..62f68b7086 100644 --- a/libs/blocks/mobile-app-banner/mobile-app-banner.js +++ b/libs/blocks/mobile-app-banner/mobile-app-banner.js @@ -18,7 +18,7 @@ async function getECID() { if (window.alloy) { await window.alloy('getIdentity').then((data) => { ecid = data?.identity?.ECID; - }).catch((err) => window.lana.log(`Error fetching ECID: ${err}`, { tags: 'errorType=error,module=mobile-app-banner' })); + }).catch((err) => window.lana.log(`Error fetching ECID: ${err}`, { tags: 'mobile-app-banner' })); } return ecid; } diff --git a/libs/blocks/notification/notification.css b/libs/blocks/notification/notification.css index 182ca31ced..7131cc89eb 100644 --- a/libs/blocks/notification/notification.css +++ b/libs/blocks/notification/notification.css @@ -16,7 +16,7 @@ --icon-size: 56px; --icon-size-xl: 64px; --pill-radius: 16px; - --pill-shadow: 0 3px 6px #707070; + --pill-shadow: 0 3px 6px #00000029; display: flex; inline-size: 100%; @@ -310,11 +310,20 @@ justify-content: flex-end; } +.notification.flexible { + background: unset; +} + .notification .flexible-inner { inline-size: 100%; box-shadow: var(--pill-shadow); } +.notification.pill.no-shadow, +.notification.pill.no-shadow.flexible .flexible-inner { + box-shadow: unset; +} + .notification .foreground > :is(.tablet-up, .desktop-up) { display: none; } @@ -328,6 +337,12 @@ margin-block-end: var(--spacing-xxs); } +.notification.pill.max-width-6 { max-width: 600px; } +.notification.pill.max-width-8 { max-width: 800px; } +.notification.pill.max-width-10 { max-width: 1000px; } +.notification.pill.max-width-12 { max-width: 1200px; } +.notification.pill.max-width-auto { max-width: unset; } + @media screen and (min-width: 600px) { .notification { --max-inline-size-image: 188px; @@ -468,6 +483,12 @@ padding-inline-end: unset; inline-size: unset; } + + .notification.pill.max-width-6-tablet { max-width: 600px; } + .notification.pill.max-width-8-tablet { max-width: 800px; } + .notification.pill.max-width-10-tablet { max-width: 1000px; } + .notification.pill.max-width-12-tablet { max-width: 1200px; } + .notification.pill.max-width-auto-tablet { max-width: unset; } } @media screen and (min-width: 1200px) { @@ -632,4 +653,10 @@ .notification.pill .copy-wrap p:first-child:not(:only-child) { margin-block-end: 0; } + + .notification.pill.max-width-6-desktop { max-width: 600px; } + .notification.pill.max-width-8-desktop { max-width: 800px; } + .notification.pill.max-width-10-desktop { max-width: 1000px; } + .notification.pill.max-width-12-desktop { max-width: 1200px; } + .notification.pill.max-width-auto-desktop { max-width: unset; } } diff --git a/libs/blocks/notification/notification.js b/libs/blocks/notification/notification.js index 4a2cab725d..5ee6398d6c 100644 --- a/libs/blocks/notification/notification.js +++ b/libs/blocks/notification/notification.js @@ -88,7 +88,12 @@ function wrapCopy(foreground) { function decorateClose(el) { const btn = createTag('button', { 'aria-label': 'close', class: 'close' }, closeSvg); - btn.addEventListener('click', () => (el.style.display = 'none')); + btn.addEventListener('click', () => { + el.style.display = 'none'; + el.closest('.section')?.classList.add('close-sticky-section'); + document.dispatchEvent(new CustomEvent('milo:sticky:closed')); + }); + el.appendChild(btn); } diff --git a/libs/blocks/path-finder/path-finder.js b/libs/blocks/path-finder/path-finder.js index 8cad4c1c9d..3ba93e71df 100644 --- a/libs/blocks/path-finder/path-finder.js +++ b/libs/blocks/path-finder/path-finder.js @@ -63,7 +63,7 @@ function buildUi(el, path) { async function setup(el) { await login({ scopes: SCOPES, telemetry: TELEMETRY }); if (!account.value.username) { - window.lana.log('Could not login to MS Graph', { tags: 'errorType=info,module=path-finder' }); + window.lana.log('Could not login to MS Graph', { tags: 'path-finder', errorType: 'i' }); return; } el.innerHTML = ''; diff --git a/libs/blocks/preflight/panels/seo.js b/libs/blocks/preflight/panels/seo.js index e69c561a7d..443b35d7c8 100644 --- a/libs/blocks/preflight/panels/seo.js +++ b/libs/blocks/preflight/panels/seo.js @@ -160,7 +160,7 @@ async function spidyCheck(url) { connectionError(); } catch (e) { connectionError(); - window.lana.log(`There was a problem connecting to the link check API ${url}. ${e}`, { tags: 'errorType=info,module=preflight' }); + window.lana.log(`There was a problem connecting to the link check API ${url}. ${e}`, { tags: 'preflight', errorType: 'i' }); } return false; } @@ -182,7 +182,7 @@ async function getSpidyResults(url, opts) { return acc; }, []); } catch (e) { - window.lana.log(`There was a problem connecting to the link check API ${url}/api/url-http-status. ${e}`, { tags: 'errorType=info,module=preflight' }); + window.lana.log(`There was a problem connecting to the link check API ${url}/api/url-http-status. ${e}`, { tags: 'preflight', errorType: 'i' }); return []; } } diff --git a/libs/blocks/quiz-entry/mlField.js b/libs/blocks/quiz-entry/mlField.js index a04545b80c..17b9f72fc9 100644 --- a/libs/blocks/quiz-entry/mlField.js +++ b/libs/blocks/quiz-entry/mlField.js @@ -26,7 +26,7 @@ export const getMLResults = async (endpoint, apiKey, threshold, input, count, va body: JSON.stringify(params), }) .then((response) => response.json()) - .catch((error) => window.lana.log(`ERROR: Fetching fi codes ${error}`, { tags: 'errorType=info,module=quiz-entry' })); + .catch((error) => window.lana.log(`ERROR: Fetching fi codes ${error}`, { tags: 'quiz-entry', errorType: 'i' })); let value; let highestProb = null; diff --git a/libs/blocks/quiz-entry/quiz-entry.css b/libs/blocks/quiz-entry/quiz-entry.css index 049f39a0b9..915e94a7a8 100644 --- a/libs/blocks/quiz-entry/quiz-entry.css +++ b/libs/blocks/quiz-entry/quiz-entry.css @@ -1,6 +1,6 @@ .quiz-entry { --quiz-button-disabled-bg: #757575; - --quiz-button-disabled-text: #FFF6F6; + --quiz-button-disabled-text: #FFF; } .quiz-container { @@ -25,12 +25,13 @@ font-size: var(--type-heading-xl-size); line-height: var(--type-heading-xl-lh); font-weight: var(--type-heading-all-weight); - margin-bottom: 8px; + margin: 0 0 8px; } .quiz-subtitle { font-size: var(--type-heading-l-size); line-height: var(--type-heading-l-lh); + margin: 0; } .quiz-question-container { diff --git a/libs/blocks/quiz-entry/quiz-entry.js b/libs/blocks/quiz-entry/quiz-entry.js index 6c9c9394ba..294a7cc18f 100644 --- a/libs/blocks/quiz-entry/quiz-entry.js +++ b/libs/blocks/quiz-entry/quiz-entry.js @@ -212,7 +212,7 @@ const App = ({ } if (fiResults.errors) error = fiResults.errors[0].title; if (fiResults.error_code) error = fiResults.message; - window.lana.log(`ML results error - ${error}`, { tags: 'errorType=info,module=quiz-entry' }); + window.lana.log(`ML results error - ${error}`, { tags: 'quiz-entry', errorType: 'i' }); sendMLFieldAnalytics(fallback, false); } @@ -345,8 +345,8 @@ const App = ({ return html`
-
${quizLists.strings[selectedQuestion.questions].heading}
-
${quizLists.strings[selectedQuestion.questions]['sub-head']}
+

${quizLists.strings[selectedQuestion.questions].heading}

+

${quizLists.strings[selectedQuestion.questions]['sub-head']}

diff --git a/libs/blocks/quiz-entry/quizPopover.js b/libs/blocks/quiz-entry/quizPopover.js index 1c444d0d3d..ed0edbe212 100644 --- a/libs/blocks/quiz-entry/quizPopover.js +++ b/libs/blocks/quiz-entry/quizPopover.js @@ -12,7 +12,7 @@ export const getSuggestions = async (endpoint, clientId, input, scope) => { }); if (!response.ok) { - window.lana.log('Failed to fetch suggestions', { tags: 'errorType=info,module=quiz-entry' }); + window.lana.log('Failed to fetch suggestions', { tags: 'quiz-entry', errorType: 'i' }); return ''; } diff --git a/libs/blocks/quiz-entry/quizoption.js b/libs/blocks/quiz-entry/quizoption.js index bb3f88bf15..83a3e710f0 100644 --- a/libs/blocks/quiz-entry/quizoption.js +++ b/libs/blocks/quiz-entry/quizoption.js @@ -1,5 +1,6 @@ import { html, useState, useEffect } from '../../deps/htm-preact.js'; import { getSwipeDistance, getSwipeDirection } from '../carousel/carousel.js'; +import { createTag } from '../../utils/utils.js'; export const OptionCard = ({ text, title, image, icon, iconTablet, iconDesktop, options, @@ -51,15 +52,30 @@ export const GetQuizOption = ({ const isRTL = document.documentElement.getAttribute('dir') === 'rtl'; + /** + * Resets focus to the top of the quiz-entry options for accessibility. + * To ensure that the next keyboard tab after carousel navigation + * will focus the first avaiable quiz option. + */ + const resetFocus = () => { + const quiz = document.querySelector('.quiz-options-container'); + const focuser = createTag('button', { tabindex: 0 }); + quiz.prepend(focuser); + focuser.focus(); + quiz.removeChild(focuser); + }; + const next = async () => { if (index + visibleCount < options.data.length) { setIndex(index + 1); + resetFocus(); } }; const prev = () => { if (index > 0) { setIndex(index - 1); + resetFocus(); } }; @@ -144,24 +160,24 @@ export const GetQuizOption = ({ return html`
- ${index > 0 && html``} - - ${(index + visibleCount < options.data.length) && html``} + ${index > 0 && html``} + + ${(index + visibleCount < options.data.length) && html``}
`; }; diff --git a/libs/blocks/quiz-results/quiz-results.js b/libs/blocks/quiz-results/quiz-results.js index 7465782fc7..7c9622366a 100644 --- a/libs/blocks/quiz-results/quiz-results.js +++ b/libs/blocks/quiz-results/quiz-results.js @@ -20,7 +20,7 @@ async function loadFragments(el, experiences) { function redirectPage(quizUrl, debug, message) { const url = quizUrl ? getLocalizedURL(quizUrl.text) : 'https://adobe.com'; - window.lana.log(message, { tags: 'errorType=error,module=quiz-results' }); + window.lana.log(message, { tags: 'quiz-results' }); if (debug === 'quiz-results') { // eslint-disable-next-line no-console @@ -97,7 +97,7 @@ export default async function init(el, debug = null, localStoreKey = null) { loadFragments(el, basic); } else { - window.lana.log(`${LOADING_ERROR} The quiz-results block is misconfigured`, { tags: 'errorType=error,module=quiz-results' }); + window.lana.log(`${LOADING_ERROR} The quiz-results block is misconfigured`, { tags: 'quiz-results' }); return; } diff --git a/libs/blocks/region-nav/region-nav.js b/libs/blocks/region-nav/region-nav.js index 7c626978ef..109051ffcd 100644 --- a/libs/blocks/region-nav/region-nav.js +++ b/libs/blocks/region-nav/region-nav.js @@ -3,7 +3,8 @@ import { getConfig } from '../../utils/utils.js'; /* c8 ignore next 11 */ function handleEvent(prefix, link) { const domain = window.location.host.endsWith('.adobe.com') ? 'domain=adobe.com' : ''; - document.cookie = `international=${prefix};path=/;${domain}`; + const maxAge = 365 * 24 * 60 * 60; // max-age in seconds for 365 days + document.cookie = `international=${prefix};max-age=${maxAge};path=/;${domain}`; sessionStorage.setItem('international', prefix); fetch(link.href, { method: 'HEAD' }).then((resp) => { if (!resp.ok) throw new Error('request failed'); diff --git a/libs/blocks/section-metadata/section-metadata.css b/libs/blocks/section-metadata/section-metadata.css index c027f794d5..8b17e07611 100644 --- a/libs/blocks/section-metadata/section-metadata.css +++ b/libs/blocks/section-metadata/section-metadata.css @@ -205,7 +205,7 @@ .section.sticky-bottom.promo-sticky-section { background: none; - z-index: 4; + z-index: 40000; } .section.sticky-bottom.popup { diff --git a/libs/blocks/table/table.css b/libs/blocks/table/table.css index 5087a46867..fd95c0714b 100644 --- a/libs/blocks/table/table.css +++ b/libs/blocks/table/table.css @@ -264,6 +264,7 @@ .table .section-row .col { padding: 16px 24px; + column-gap: 0.5ch; } .table .section-row .col:has(> p:nth-child(2)) { @@ -614,6 +615,14 @@ header.global-navigation { right: initial; } + .table .row-heading .col-heading .pricing { + overflow-wrap: anywhere; + } + + .table .row-heading .col-heading span[is='inline-price'] { + display: inline; + } + .table .row-heading .col:nth-child(n+1) { padding: 20px; } @@ -705,4 +714,12 @@ header.global-navigation { flex-direction: column; align-items: initial; } + + .table :is(.heading-button,.action-area) { + max-width: 100%; + } + + .table .action-area .con-button.button-l { + overflow: hidden; + } } diff --git a/libs/blocks/table/table.js b/libs/blocks/table/table.js index 6f2213c57c..9d41802ff6 100644 --- a/libs/blocks/table/table.js +++ b/libs/blocks/table/table.js @@ -6,6 +6,7 @@ const DESKTOP_SIZE = 900; const MOBILE_SIZE = 768; const tableHighlightLoadedEvent = new Event('milo:table:highlight:loaded'); let tableIndex = 0; +const isMobileLandscape = () => (window.matchMedia('(orientation: landscape)').matches && window.innerHeight <= MOBILE_SIZE); function defineDeviceByScreenSize() { const screenWidth = window.innerWidth; if (screenWidth >= DESKTOP_SIZE) { @@ -17,6 +18,12 @@ function defineDeviceByScreenSize() { return 'TABLET'; } +function isStickyHeader(el) { + return el.classList.contains('sticky') + || (el.classList.contains('sticky-desktop-up') && defineDeviceByScreenSize() === 'DESKTOP') + || (el.classList.contains('sticky-tablet-up') && defineDeviceByScreenSize() !== 'MOBILE' && !isMobileLandscape()); +} + function handleHeading(table, headingCols) { const isPriceBottom = table.classList.contains('pricing-bottom'); headingCols.forEach((col, i) => { @@ -382,7 +389,7 @@ function applyStylesBasedOnScreenSize(table, originTable) { } if ((!isMerch && !table.querySelector('.col-3')) - || (isMerch && !table.querySelector('.col-2'))) return; + || (isMerch && !table.querySelector('.col-2'))) return; const filterChangeEvent = () => { table.innerHTML = originTable.innerHTML; @@ -501,8 +508,6 @@ export default function init(el) { expandSection = handleSection(sectionParams); }); - const isStickyHeader = el.classList.contains('sticky'); - handleHighlight(el); if (isMerch) formatMerchTable(el); @@ -523,7 +528,7 @@ export default function init(el) { const handleResize = () => { applyStylesBasedOnScreenSize(el, originTable); - if (isStickyHeader) handleScrollEffect(el); + if (isStickyHeader(el)) handleScrollEffect(el); }; handleResize(); diff --git a/libs/blocks/tag-selector/tag-selector.js b/libs/blocks/tag-selector/tag-selector.js index 8607fed08c..ccfc43f9c9 100644 --- a/libs/blocks/tag-selector/tag-selector.js +++ b/libs/blocks/tag-selector/tag-selector.js @@ -86,7 +86,7 @@ const TagSelector = ({ consumerUrls = [] }) => { const fetchCasS = async () => { const { tags, errorMsg } = await loadCaasTags(caasTagUrl); - if (errorMsg) window.lana.log(`Tag Selector. Error fetching caas tags: ${errorMsg}`, { tags: 'errorType=info,module=tag-selector' }); + if (errorMsg) window.lana.log(`Tag Selector. Error fetching caas tags: ${errorMsg}`, { tags: 'tag-selector', errorType: 'i' }); setTagSelectorTags((prevConsumerTags) => ({ CaaS: tags, ...prevConsumerTags })); }; diff --git a/libs/blocks/text/text.css b/libs/blocks/text/text.css index 0104689af2..6242f50132 100644 --- a/libs/blocks/text/text.css +++ b/libs/blocks/text/text.css @@ -1,4 +1,6 @@ .text-block { + --accent-height: 10px; + position: relative; } @@ -16,6 +18,8 @@ .text-block [class^="detail"] { margin: 0 0 var(--spacing-xxs) 0; } +.text-block [class^="detail"] strong { font-weight: unset; } /* bugfix for FF */ + .text-block .cta-container, .text-block p.action-area { margin-top: var(--spacing-s); } @@ -31,7 +35,6 @@ position: absolute; right: 0; top: 0; - z-index: -1; overflow: hidden; } @@ -73,6 +76,25 @@ margin: var(--spacing-m) 0; } +.text-block .title-l { + font-size: var(--type-body-m-size); + line-height: var(--type-body-m-lh); + font-weight: 700; + text-transform: none; + margin-bottom: var(--spacing-xs); +} + +.text-block :is(.image, .lockup-area) { + margin-block-end: var(--spacing-m); +} + +.text-block .image em { + display: block; + font-size: var(--type-body-s-size); + line-height: var(--type-body-s-lh); + margin-block-start: var(--spacing-xs); +} + .text-block .icon-list-item { list-style: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'/%3E"); /* Must not be `none` for accessibility */ position: relative; @@ -83,17 +105,68 @@ inset: 0 100% auto auto; } +.text-block.accent-bar { + inline-size: 100%; + block-size: var(--accent-height); +} + +.text-block .lockup-area img { + width: auto; +} + /* Alignment */ .text-block.center { text-align: center; align-items: center; } +.text-block .icon-area { + display: flex; + column-gap: var(--spacing-xs); +} + +.text-block p.icon-area { /* NOT tags with icons in them */ + margin-block-end: var(--spacing-m); +} + +.text-block.xxs-icon .icon-area { + height: var(--icon-size-xxs); +} + +.text-block.xs-icon .icon-area { + height: var(--icon-size-xs); +} + +.text-block.s-icon .icon-area { + height: var(--icon-size-s); +} + +.text-block.m-icon .icon-area { + height: var(--icon-size-m); +} + +.text-block.l-icon .icon-area { + height: var(--icon-size-l); +} + +.text-block.xl-icon .icon-area { + height: var(--icon-size-xl); +} + +.text-block.xxl-icon .icon-area { + height: var(--icon-size-xxl); +} + +.text-block .icon-area img { + max-height: 100%; + width: auto; +} + .text-block.center .action-area, .text-block.center .icon-area { justify-content: center; } .text-block.right { - text-align: right; + text-align: end; align-items: end; } @@ -145,17 +218,25 @@ max-width: unset; } -.text-block .icon-area { - display: flex; - column-gap: var(--spacing-xxs); -} - .text-block .icon-area.con-button { column-gap: unset; } .text-block .icon-area picture { line-height: 0em; + height: inherit; /* Safari + FF bug fix */ +} + +.text-block .image picture { + display: flex; +} + +.text-block.center .image picture { + justify-content: center; +} + +.text-block.right .image picture { + justify-content: flex-end; } /* icon inline */ diff --git a/libs/blocks/text/text.js b/libs/blocks/text/text.js index 5886a8f23b..947922b312 100644 --- a/libs/blocks/text/text.js +++ b/libs/blocks/text/text.js @@ -24,9 +24,8 @@ const blockTypeSizes = { }, }; -function decorateMultiViewport(el) { +function decorateMultiViewport(foreground) { const viewports = ['mobile-up', 'tablet-up', 'desktop-up']; - const foreground = el.querySelector('.foreground'); if (foreground.childElementCount === 2 || foreground.childElementCount === 3) { [...foreground.children].forEach((child, index) => { child.className = viewports[index]; @@ -36,8 +35,13 @@ function decorateMultiViewport(el) { return foreground; } -function decorateBlockIconArea(el) { - const headings = el.querySelectorAll('h1, h2, h3, h4, h5, h6'); +function decorateBlockIconArea(content, el) { + const first = content.children[0]; + if (first?.querySelector('img')) { + const areaClass = el.className.match(/-(lockup|icon)/); + first.classList.add(areaClass ? `${areaClass[1]}-area` : 'image'); + } + const headings = content.querySelectorAll('h1, h2, h3, h4, h5, h6'); if (!headings) return; headings.forEach((h) => { const hPrevElem = h.previousElementSibling; @@ -76,14 +80,20 @@ function decorateLinkFarms(el) { }); } +function addStyle(filename) { + const { miloLibs, codeRoot } = getConfig(); + const base = miloLibs || codeRoot; + loadStyle(`${base}/styles/${filename}.css`); +} + export default async function init(el) { el.classList.add('text-block', 'con-block'); let rows = el.querySelectorAll(':scope > div'); - if (rows.length > 1) { + if (rows.length > 1 || el.matches('.accent-bar')) { if (rows[0].textContent !== '') el.classList.add('has-bg'); const [head, ...tail] = rows; decorateBlockBg(el, head); - rows = tail; + rows = tail || rows; } const helperClasses = []; let blockType = 'text'; @@ -97,8 +107,11 @@ export default async function init(el) { const hasLinkFarm = el.classList.contains('link-farm'); rows.forEach((row) => { row.classList.add('foreground'); - if (!hasLinkFarm) decorateBlockText(row, blockTypeSizes[blockType][size]); - decorateBlockIconArea(row); + if (!hasLinkFarm) { + [...row.children].forEach((c) => decorateBlockText(c, blockTypeSizes[blockType][size])); + decorateMultiViewport(row); + } + [...row.children].forEach((c) => decorateBlockIconArea(c, el)); }); if (el.classList.contains('full-width')) helperClasses.push('max-width-8-desktop', 'center', 'xxl-spacing'); if (el.classList.contains('intro')) helperClasses.push('max-width-8-desktop', 'xxl-spacing-top', 'xl-spacing-bottom'); @@ -109,7 +122,6 @@ export default async function init(el) { if (hasLinkFarm) decorateLinkFarms(el); el.classList.add(...helperClasses); decorateTextOverrides(el); - if (!hasLinkFarm) decorateMultiViewport(el); const lastActionArea = el.querySelector('.action-area:last-of-type'); if (lastActionArea) { @@ -124,4 +136,10 @@ export default async function init(el) { mnemonicList.querySelectorAll('p').forEach((product) => product.removeAttribute('class')); await loadBlock(mnemonicList); } + if (el.matches('[class*="rounded-corners"]')) addStyle('rounded-corners'); + if (el.matches('[class*="-lockup"]')) addStyle('iconography'); + // Override Detail with Title L style if class exists - Temporary solution until Spectrum 2 + if (el.classList.contains('l-title')) { + el.querySelectorAll('[class*="detail-"]')?.forEach((detail) => detail.classList.add('title-l')); + } } diff --git a/libs/blocks/video-metadata/video-metadata.js b/libs/blocks/video-metadata/video-metadata.js index 388cb62034..d94b9410b4 100644 --- a/libs/blocks/video-metadata/video-metadata.js +++ b/libs/blocks/video-metadata/video-metadata.js @@ -25,7 +25,7 @@ function addBroadcastEventField(videoObj, blockKey, blockValue) { videoObj.publication[i][camelize(key)] = blockValue; break; default: - window.lana.log(`VideoMetadata -- Unknown BroadcastEvent property: ${blockKey}`, { tags: 'errorType=warn,module=video-metadata' }); + window.lana.log(`VideoMetadata -- Unknown BroadcastEvent property: ${blockKey}`, { tags: 'video-metadata' }); break; } } @@ -45,7 +45,7 @@ function addClipField(videoObj, blockKey, blockValue) { videoObj.hasPart[i][camelize(key)] = blockValue; break; default: - window.lana.log(`VideoMetadata -- Unhandled Clip property: ${blockKey}`, { tags: 'errorType=warn,module=video-metadata' }); + window.lana.log(`VideoMetadata -- Unhandled Clip property: ${blockKey}`, { tags: 'video-metadata' }); break; } } @@ -61,7 +61,7 @@ function addSeekToActionField(videoObj, blockKey, blockValue) { videoObj.potentialAction['startOffset-input'] = blockValue; break; default: - window.lana.log(`VideoMetadata -- Unhandled SeekToAction property: ${blockKey}`, { tags: 'errorType=warn,module=video-metadata' }); + window.lana.log(`VideoMetadata -- Unhandled SeekToAction property: ${blockKey}`, { tags: 'video-metadata' }); break; } } @@ -96,7 +96,7 @@ export function createVideoObject(record) { addSeekToActionField(video, blockKey, blockVal); break; default: - window.lana.log(`VideoMetadata -- Unhandled VideoObject property: ${blockKey}`, { tags: 'errorType=warn,module=video-metadata' }); + window.lana.log(`VideoMetadata -- Unhandled VideoObject property: ${blockKey}`, { tags: 'video-metadata' }); break; } }); diff --git a/libs/blocks/vimeo/vimeo.css b/libs/blocks/vimeo/vimeo.css index 2724a958eb..90bdd59030 100644 --- a/libs/blocks/vimeo/vimeo.css +++ b/libs/blocks/vimeo/vimeo.css @@ -5,3 +5,59 @@ position: relative; padding-bottom: 56.25%; } + +lite-vimeo { + aspect-ratio: 16 / 9; + background-color: #000; + position: relative; + display: block; + contain: content; + background-position: center center; + background-size: cover; + cursor: pointer; +} + +lite-vimeo > .ltv-playbtn { + font-size: 10px; + padding: 0; + width: 6.5em; + height: 4em; + background: rgb(23, 35, 34, 75%); + z-index: 1; + opacity: .8; + border-radius: .5em; + transition: opacity .2s ease-out, background .2s ease-out; + outline: 0; + border: 0; + cursor: pointer; +} + +lite-vimeo:hover > .ltv-playbtn { + background-color: rgb(0, 173, 239); + opacity: 1; +} + +lite-vimeo > .ltv-playbtn::before { + content: ''; + border-style: solid; + border-width: 10px 0 10px 20px; + border-color: transparent transparent transparent #fff; +} + +lite-vimeo > .ltv-playbtn, +lite-vimeo > .ltv-playbtn::before { + position: absolute; + top: 50%; + left: 50%; + transform: translate3d(-50%, -50%, 0); +} + +lite-vimeo.ltv-activated { + cursor: unset; +} + +lite-vimeo.ltv-activated::before, +lite-vimeo.ltv-activated > .ltv-playbtn { + opacity: 0; + pointer-events: none; +} diff --git a/libs/blocks/vimeo/vimeo.js b/libs/blocks/vimeo/vimeo.js index 8019259af4..10c55b599d 100644 --- a/libs/blocks/vimeo/vimeo.js +++ b/libs/blocks/vimeo/vimeo.js @@ -1,25 +1,80 @@ -import { createIntersectionObserver, createTag, isInTextNode } from '../../utils/utils.js'; +// part of the code is an optimized version of lite-vimeo-embed -> https://github.com/luwes/lite-vimeo-embed +import { replaceKey } from '../../features/placeholders.js'; +import { createIntersectionObserver, createTag, getConfig, isInTextNode, loadLink } from '../../utils/utils.js'; -export default function init(a) { - if (isInTextNode(a)) return; - const embedVimeo = () => { - const url = new URL(a.href); - let src = url.href; - if (url.hostname !== 'player.vimeo.com') { - const video = url.pathname.split('/')[1]; - src = `https://player.vimeo.com/video/${video}?app_id=122963`; - } - const iframe = createTag('iframe', { - src, - style: 'border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute;', +class LiteVimeo extends HTMLElement { + static preconnected = false; + + connectedCallback() { + this.isMobile = navigator.userAgent.includes('Mobi'); + this.videoId = this.getAttribute('videoid'); + this.setupThumbnail(); + this.setupPlayButton(); + this.addEventListener('pointerover', LiteVimeo.warmConnections, { once: true }); + this.addEventListener('click', this.addIframe); + } + + static warmConnections() { + if (LiteVimeo.preconnected) return; + LiteVimeo.preconnected = true; + ['player.vimeo.com', + 'i.vimeocdn.com', + 'f.vimeocdn.com', + 'fresnel.vimeocdn.com', + ].forEach((url) => loadLink(`https://${url}`, { rel: 'preconnect' })); + } + + setupThumbnail() { + const { width, height } = this.getBoundingClientRect(); + const roundedWidth = Math.min(Math.ceil(width / 100) * 100, 1920); + const roundedHeight = Math.round((roundedWidth / width) * height); + + fetch(`https://vimeo.com/api/v2/video/${this.videoId}.json`) + .then((response) => response.json()) + .then((data) => { + const thumbnailUrl = data[0]?.thumbnail_large?.replace(/-d_[\dx]+$/i, `-d_${roundedWidth}x${roundedHeight}`); + this.style.backgroundImage = `url("${thumbnailUrl}")`; + }) + .catch((e) => { + window.lana.log(`Error fetching Vimeo thumbnail: ${e}`, { tags: 'vimeo', errorType: 'i' }); + }); + } + + async setupPlayButton() { + const playBtnEl = createTag('button', { + type: 'button', + 'aria-label': `${await replaceKey('play-video', getConfig())}`, + class: 'ltv-playbtn', + }); + this.append(playBtnEl); + } + + addIframe() { + if (this.classList.contains('ltv-activated')) return; + this.classList.add('ltv-activated'); + const iframeEl = createTag('iframe', { + style: 'border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute; background-color: #000;', frameborder: '0', - allow: 'autoplay; fullscreen; picture-in-picture', - allowfullscreen: 'true', title: 'Content from Vimeo', - loading: 'lazy', + allow: 'accelerometer; fullscreen; autoplay; encrypted-media; gyroscope; picture-in-picture', + allowFullscreen: true, + src: `https://player.vimeo.com/video/${encodeURIComponent(this.videoId)}?autoplay=1&muted=${this.isMobile ? 1 : 0}`, }); - const wrapper = createTag('div', { class: 'embed-vimeo' }, iframe); + this.insertAdjacentElement('afterend', iframeEl); + iframeEl.addEventListener('load', () => iframeEl.focus(), { once: true }); + this.remove(); + } +} +export default async function init(a) { + if (isInTextNode(a)) return; + if (!customElements.get('lite-vimeo')) customElements.define('lite-vimeo', LiteVimeo); + + const embedVimeo = () => { + const url = new URL(a.href); + const videoid = url.pathname.split('/')[url.hostname === 'player.vimeo.com' ? 2 : 1]; + const liteVimeo = createTag('lite-vimeo', { videoid }); + const wrapper = createTag('div', { class: 'embed-vimeo' }, liteVimeo); a.parentElement.replaceChild(wrapper, a); }; diff --git a/libs/blocks/youtube/youtube.css b/libs/blocks/youtube/youtube.css index 35e7775617..d682a05679 100644 --- a/libs/blocks/youtube/youtube.css +++ b/libs/blocks/youtube/youtube.css @@ -1 +1,56 @@ @import url('../../styles/iframe.css'); + +lite-youtube { + top: 0; + left: 0; + width: 100%; + height: 100%; + position: absolute; + cursor: pointer; +} + +lite-youtube > .lty-playbtn { + display: block; + width: 68px; + height: 48px; + position: absolute; + cursor: pointer; + transform: translate3d(-50%, -50%, 0); + top: 50%; + left: 50%; + z-index: 1; + background-color: transparent; + background-image: url('data:image/svg+xml;utf8,'); + filter: grayscale(100%); + transition: filter .1s cubic-bezier(0, 0, 0.2, 1); + border: none; +} + +lite-youtube:hover > .lty-playbtn, +lite-youtube .lty-playbtn:focus { + filter: none; +} + +lite-youtube.lyt-activated { + cursor: unset; +} + +lite-youtube.lyt-activated::before, +lite-youtube.lyt-activated > .lty-playbtn { + opacity: 0; + pointer-events: none; +} + +.lyt-visually-hidden { + clip: rect(0 0 0 0); + clip-path: inset(50%); + height: 1px; + overflow: hidden; + position: absolute; + white-space: nowrap; + width: 1px; +} + +.dark-background { + background-color: #000; +} diff --git a/libs/blocks/youtube/youtube.js b/libs/blocks/youtube/youtube.js index d8589e96e9..1dd4063975 100644 --- a/libs/blocks/youtube/youtube.js +++ b/libs/blocks/youtube/youtube.js @@ -1,36 +1,109 @@ -import { createIntersectionObserver, isInTextNode } from '../../utils/utils.js'; +// part of the code is an optimized version of lite-youtube-embed -> https://github.com/paulirish/lite-youtube-embed +import { createIntersectionObserver, createTag, isInTextNode, loadLink } from '../../utils/utils.js'; + +class LiteYTEmbed extends HTMLElement { + connectedCallback() { + this.isMobile = navigator.userAgent.includes('Mobi'); + this.videoId = this.getAttribute('videoid'); + const playBtnEl = createTag('button', { type: 'button', class: 'lty-playbtn' }); + this.append(playBtnEl); + this.playLabel = this.getAttribute('playlabel') || 'Play'; + this.style.backgroundImage = `url("https://i.ytimg.com/vi/${this.videoId}/hqdefault.jpg")`; + this.style.backgroundSize = 'cover'; + this.style.backgroundPosition = 'center'; + const playBtnLabelEl = createTag('span', { class: 'lyt-visually-hidden' }); + playBtnLabelEl.textContent = this.playLabel; + playBtnEl.append(playBtnLabelEl); + this.addEventListener('pointerover', LiteYTEmbed.warmConnections, { once: true }); + this.addEventListener('click', this.addIframe); + this.needsYTApiForAutoplay = navigator.vendor.includes('Apple') || this.isMobile; + } + + static warmConnections() { + if (LiteYTEmbed.preconnected) return; + LiteYTEmbed.preconnected = true; + ['www.youtube-nocookie.com', + 'www.google.com', + 'googleads.g.doubleclick.net', + 'static.doubleclick.net', + ].forEach((url) => loadLink(`https://${url}`, { rel: 'preconnect' })); + } + + static loadYouTubeAPI() { + return new Promise((resolve) => { + if (window.YT?.Player) { + resolve(); + return; + } + const tag = createTag('script', { src: 'https://www.youtube.com/iframe_api' }); + window.onYouTubeIframeAPIReady = resolve; + document.head.appendChild(tag); + }); + } + + async addIframe() { + if (this.classList.contains('lyt-activated')) return; + + this.classList.add('lyt-activated'); + const params = new URLSearchParams(this.getAttribute('params') || []); + params.append('autoplay', '1'); + params.append('playsinline', '1'); + if (this.isMobile) params.append('mute', '1'); + + if (this.needsYTApiForAutoplay) { + await LiteYTEmbed.loadYouTubeAPI(); + await new Promise((resolve) => { window.YT.ready(resolve); }); + // eslint-disable-next-line + new window.YT.Player(this, { + videoId: this.videoId, + playerVars: Object.fromEntries(params), + allow: 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture', + allowfullscreen: true, + title: this.playLabel, + }); + } else { + const iframeEl = createTag('iframe', { + src: `https://www.youtube-nocookie.com/embed/${encodeURIComponent(this.videoId)}?${params.toString()}`, + allowFullscreen: true, + allow: 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture', + title: this.playLabel, + }); + this.insertAdjacentElement('afterend', iframeEl); + iframeEl.focus(); + this.remove(); + } + } +} + +export default async function init(a) { + if (!customElements.get('lite-youtube')) customElements.define('lite-youtube', LiteYTEmbed); -export default function init(a) { const embedVideo = () => { if (isInTextNode(a) || !a.origin?.includes('youtu')) return; const title = !a.textContent.includes('http') ? a.textContent : 'Youtube Video'; const searchParams = new URLSearchParams(a.search); const id = searchParams.get('v') || a.pathname.split('/').pop(); searchParams.delete('v'); - const src = `https://www.youtube.com/embed/${id}?${searchParams.toString()}`; - const embedHTML = ` -
- -
`; - a.insertAdjacentHTML('afterend', embedHTML); + const liteYTElement = createTag('lite-youtube', { videoid: id, playlabel: title }); + + if (searchParams.toString()) liteYTElement.setAttribute('params', searchParams.toString()); + + const ytContainer = createTag('div', { class: 'milo-video dark-background' }, liteYTElement); + a.insertAdjacentElement('afterend', ytContainer); a.remove(); + if (document.readyState === 'complete') { - /* eslint-disable-next-line no-underscore-dangle */ + // eslint-disable-next-line no-underscore-dangle window._satellite?.track('trackYoutube'); } else { document.addEventListener('readystatechange', () => { if (document.readyState === 'complete') { - /* eslint-disable-next-line no-underscore-dangle */ + // eslint-disable-next-line no-underscore-dangle window._satellite?.track('trackYoutube'); } }); } }; + createIntersectionObserver({ el: a, callback: embedVideo }); } diff --git a/libs/deps/mas/commerce.js b/libs/deps/mas/commerce.js index cf25cfbf96..e1afe935b7 100644 --- a/libs/deps/mas/commerce.js +++ b/libs/deps/mas/commerce.js @@ -1,4 +1,3 @@ -var Rr=Object.defineProperty;var Jn=(e,t,n)=>t in e?Rr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var Qn=(e,t)=>{for(var n in t)Rr(e,n,{get:t[n],enumerable:!0})};var K=(e,t,n)=>(Jn(e,typeof t!="symbol"?t+"":t,n),n),Ir=(e,t,n)=>{if(!t.has(e))throw TypeError("Cannot "+n)};var At=(e,t,n)=>(Ir(e,t,"read from private field"),n?n.call(e):t.get(e)),Mr=(e,t,n)=>{if(t.has(e))throw TypeError("Cannot add the same private member more than once");t instanceof WeakSet?t.add(e):t.set(e,n)},wt=(e,t,n,r)=>(Ir(e,t,"write to private field"),r?r.call(e,n):t.set(e,n),n);var Le;(function(e){e.STAGE="STAGE",e.PRODUCTION="PRODUCTION",e.LOCAL="LOCAL"})(Le||(Le={}));var Lt;(function(e){e.STAGE="STAGE",e.PRODUCTION="PROD",e.LOCAL="LOCAL"})(Lt||(Lt={}));var Oe;(function(e){e.DRAFT="DRAFT",e.PUBLISHED="PUBLISHED"})(Oe||(Oe={}));var ae;(function(e){e.V2="UCv2",e.V3="UCv3"})(ae||(ae={}));var V;(function(e){e.CHECKOUT="checkout",e.CHECKOUT_EMAIL="checkout/email",e.SEGMENTATION="segmentation",e.BUNDLE="bundle",e.COMMITMENT="commitment",e.RECOMMENDATION="recommendation",e.EMAIL="email",e.PAYMENT="payment",e.CHANGE_PLAN_TEAM_PLANS="change-plan/team-upgrade/plans",e.CHANGE_PLAN_TEAM_PAYMENT="change-plan/team-upgrade/payment"})(V||(V={}));var Ot=function(e){var t;return(t=Kn.get(e))!==null&&t!==void 0?t:e},Kn=new Map([["countrySpecific","cs"],["quantity","q"],["authCode","code"],["checkoutPromoCode","apc"],["rurl","rUrl"],["curl","cUrl"],["ctxrturl","ctxRtUrl"],["country","co"],["language","lang"],["clientId","cli"],["context","ctx"],["productArrangementCode","pa"],["offerType","ot"],["marketSegment","ms"]]);var Dr=function(e){var t=typeof Symbol=="function"&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&typeof e.length=="number")return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},Ur=function(e,t){var n=typeof Symbol=="function"&&e[Symbol.iterator];if(!n)return e;var r=n.call(e),i,o=[],s;try{for(;(t===void 0||t-- >0)&&!(i=r.next()).done;)o.push(i.value)}catch(a){s={error:a}}finally{try{i&&!i.done&&(n=r.return)&&n.call(r)}finally{if(s)throw s.error}}return o};function xe(e,t,n){var r,i;try{for(var o=Dr(Object.entries(e)),s=o.next();!s.done;s=o.next()){var a=Ur(s.value,2),l=a[0],u=a[1],c=Ot(l);u!=null&&n.has(c)&&t.set(c,u)}}catch(p){r={error:p}}finally{try{s&&!s.done&&(i=o.return)&&i.call(o)}finally{if(r)throw r.error}}}function He(e){switch(e){case Le.PRODUCTION:return"https://commerce.adobe.com";default:return"https://commerce-stg.adobe.com"}}function We(e,t){var n,r;for(var i in e){var o=e[i];try{for(var s=(n=void 0,Dr(Object.entries(o))),a=s.next();!a.done;a=s.next()){var l=Ur(a.value,2),u=l[0],c=l[1];if(c!=null){var p=Ot(u);t.set("items["+i+"]["+p+"]",c)}}}catch(f){n={error:f}}finally{try{a&&!a.done&&(r=s.return)&&r.call(s)}finally{if(n)throw n.error}}}}var ei=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var i=0,r=Object.getOwnPropertySymbols(e);i=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};function kr(e){ii(e);var t=e.env,n=e.items,r=e.workflowStep,i=ei(e,["env","items","workflowStep"]),o=new URL(He(t));return o.pathname=r+"/",We(n,o.searchParams),xe(i,o.searchParams,ri),o.toString()}var ri=new Set(["cli","co","lang","ctx","cUrl","mv","nglwfdata","otac","promoid","rUrl","sdid","spint","trackingid","code","campaignid","appctxid"]),ni=["env","workflowStep","clientId","country","items"];function ii(e){var t,n;try{for(var r=ti(ni),i=r.next();!i.done;i=r.next()){var o=i.value;if(!e[o])throw new Error('Argument "checkoutData" is not valid, missing: '+o)}}catch(s){t={error:s}}finally{try{i&&!i.done&&(n=r.return)&&n.call(r)}finally{if(t)throw t.error}}return!0}var oi=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var i=0,r=Object.getOwnPropertySymbols(e);i=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},ai="p_draft_landscape",ci="/store/";function Ct(e){ui(e);var t=e.env,n=e.items,r=e.workflowStep,i=e.ms,o=e.marketSegment,s=e.ot,a=e.offerType,l=e.pa,u=e.productArrangementCode,c=e.landscape,p=oi(e,["env","items","workflowStep","ms","marketSegment","ot","offerType","pa","productArrangementCode","landscape"]),f={marketSegment:o??i,offerType:a??s,productArrangementCode:u??l},h=new URL(He(t));return h.pathname=""+ci+r,r!==V.SEGMENTATION&&r!==V.CHANGE_PLAN_TEAM_PLANS&&We(n,h.searchParams),r===V.SEGMENTATION&&xe(f,h.searchParams,Nt),xe(p,h.searchParams,Nt),c===Oe.DRAFT&&xe({af:ai},h.searchParams,Nt),h.toString()}var Nt=new Set(["af","ai","apc","appctxid","cli","co","csm","ctx","ctxRtUrl","DCWATC","dp","fr","gsp","ijt","lang","lo","mal","ms","mv","mv2","nglwfdata","ot","otac","pa","pcid","promoid","q","rf","sc","scl","sdid","sid","spint","svar","th","thm","trackingid","usid","workflowid","context.guid","so.ca","so.su","so.tr","so.va"]),li=["env","workflowStep","clientId","country"];function ui(e){var t,n;try{for(var r=si(li),i=r.next();!i.done;i=r.next()){var o=i.value;if(!e[o])throw new Error('Argument "checkoutData" is not valid, missing: '+o)}}catch(s){t={error:s}}finally{try{i&&!i.done&&(n=r.return)&&n.call(r)}finally{if(t)throw t.error}}if(e.workflowStep!==V.SEGMENTATION&&e.workflowStep!==V.CHANGE_PLAN_TEAM_PLANS&&!e.items)throw new Error('Argument "checkoutData" is not valid, missing: items');return!0}function Rt(e,t){switch(e){case ae.V2:return kr(t);case ae.V3:return Ct(t);default:return console.warn("Unsupported CheckoutType, will use UCv3 as default. Given type: "+e),Ct(t)}}var It;(function(e){e.BASE="BASE",e.TRIAL="TRIAL",e.PROMOTION="PROMOTION"})(It||(It={}));var R;(function(e){e.MONTH="MONTH",e.YEAR="YEAR",e.TWO_YEARS="TWO_YEARS",e.THREE_YEARS="THREE_YEARS",e.PERPETUAL="PERPETUAL",e.TERM_LICENSE="TERM_LICENSE",e.ACCESS_PASS="ACCESS_PASS",e.THREE_MONTHS="THREE_MONTHS",e.SIX_MONTHS="SIX_MONTHS"})(R||(R={}));var O;(function(e){e.ANNUAL="ANNUAL",e.MONTHLY="MONTHLY",e.TWO_YEARS="TWO_YEARS",e.THREE_YEARS="THREE_YEARS",e.P1D="P1D",e.P1Y="P1Y",e.P3Y="P3Y",e.P10Y="P10Y",e.P15Y="P15Y",e.P3D="P3D",e.P7D="P7D",e.P30D="P30D",e.HALF_YEARLY="HALF_YEARLY",e.QUARTERLY="QUARTERLY"})(O||(O={}));var Mt;(function(e){e.INDIVIDUAL="INDIVIDUAL",e.TEAM="TEAM",e.ENTERPRISE="ENTERPRISE"})(Mt||(Mt={}));var Dt;(function(e){e.COM="COM",e.EDU="EDU",e.GOV="GOV"})(Dt||(Dt={}));var Ut;(function(e){e.DIRECT="DIRECT",e.INDIRECT="INDIRECT"})(Ut||(Ut={}));var kt;(function(e){e.ENTERPRISE_PRODUCT="ENTERPRISE_PRODUCT",e.ETLA="ETLA",e.RETAIL="RETAIL",e.VIP="VIP",e.VIPMP="VIPMP",e.FREE="FREE"})(kt||(kt={}));var Gr="tacocat.js";var Xe=(e,t)=>String(e??"").toLowerCase()==String(t??"").toLowerCase(),Fr=e=>`${e??""}`.replace(/[&<>'"]/g,t=>({"&":"&","<":"<",">":">","'":"'",'"':"""})[t]??t)??"";function N(e,t={},{metadata:n=!0,search:r=!0,storage:i=!0}={}){let o;if(r&&o==null){let s=new URLSearchParams(window.location.search),a=ye(r)?r:e;o=s.get(a)}if(i&&o==null){let s=ye(i)?i:e;o=window.sessionStorage.getItem(s)??window.localStorage.getItem(s)}if(n&&o==null){let s=fi(ye(n)?n:e);o=document.documentElement.querySelector(`meta[name="${s}"]`)?.content}return o??t[e]}var _e=()=>{};var Vr=e=>typeof e=="boolean",re=e=>typeof e=="function",Ye=e=>typeof e=="number",jr=e=>e!=null&&typeof e=="object";var ye=e=>typeof e=="string",Gt=e=>ye(e)&&e,Se=e=>Ye(e)&&Number.isFinite(e)&&e>0;function ve(e,t=n=>n==null||n===""){return e!=null&&Object.entries(e).forEach(([n,r])=>{t(r)&&delete e[n]}),e}function v(e,t){if(Vr(e))return e;let n=String(e);return n==="1"||n==="true"?!0:n==="0"||n==="false"?!1:t}function ne(e,t,n){let r=Object.values(t);return r.find(i=>Xe(i,e))??n??r[0]}function fi(e=""){return String(e).replace(/(\p{Lowercase_Letter})(\p{Uppercase_Letter})/gu,(t,n,r)=>`${n}-${r}`).replace(/\W+/gu,"-").toLowerCase()}function Te(e,t=1){return Ye(e)||(e=Number.parseInt(e,10)),!Number.isNaN(e)&&e>0&&Number.isFinite(e)?e:t}var pi=Date.now(),Ft=()=>`(+${Date.now()-pi}ms)`,Be=new Set,mi=v(N("tacocat.debug",{},{metadata:!1}),typeof process<"u"&&process.env?.DEBUG);function Hr(e){let t=`[${Gr}/${e}]`,n=(s,a,...l)=>s?!0:(i(a,...l),!1),r=mi?(s,...a)=>{console.debug(`${t} ${s}`,...a,Ft())}:()=>{},i=(s,...a)=>{let l=`${t} ${s}`;Be.forEach(([u])=>u(l,...a))};return{assert:n,debug:r,error:i,warn:(s,...a)=>{let l=`${t} ${s}`;Be.forEach(([,u])=>u(l,...a))}}}function hi(e,t){let n=[e,t];return Be.add(n),()=>{Be.delete(n)}}hi((e,...t)=>{console.error(e,...t,Ft())},(e,...t)=>{console.warn(e,...t,Ft())});var di="no promo",Wr="promo-tag",Ei="yellow",gi="neutral",xi=(e,t,n)=>{let r=o=>o||di,i=n?` (was "${r(t)}")`:"";return`${r(e)}${i}`},yi="cancel-context",Ne=(e,t)=>{let n=e===yi,r=!n&&e?.length>0,i=(r||n)&&(t&&t!=e||!t&&!n),o=i&&r||!i&&!!t,s=o?e||t:void 0;return{effectivePromoCode:s,overridenPromoCode:e,className:o?Wr:`${Wr} no-promo`,text:xi(s,t,i),variant:o?Ei:gi,isOverriden:i}};var Vt="ABM",jt="PUF",Ht="M2M",Wt="PERPETUAL",Xt="P3Y",_i="TAX_INCLUSIVE_DETAILS",Si="TAX_EXCLUSIVE",Xr={ABM:Vt,PUF:jt,M2M:Ht,PERPETUAL:Wt,P3Y:Xt},bs={[Vt]:{commitment:R.YEAR,term:O.MONTHLY},[jt]:{commitment:R.YEAR,term:O.ANNUAL},[Ht]:{commitment:R.MONTH,term:O.MONTHLY},[Wt]:{commitment:R.PERPETUAL,term:void 0},[Xt]:{commitment:R.THREE_MONTHS,term:O.P3Y}},Yr="Value is not an offer",$e=e=>{if(typeof e!="object")return Yr;let{commitment:t,term:n}=e,r=vi(t,n);return{...e,planType:r}};var vi=(e,t)=>{switch(e){case void 0:return Yr;case"":return"";case R.YEAR:return t===O.MONTHLY?Vt:t===O.ANNUAL?jt:"";case R.MONTH:return t===O.MONTHLY?Ht:"";case R.PERPETUAL:return Wt;case R.TERM_LICENSE:return t===O.P3Y?Xt:"";default:return""}};function Yt(e){let{priceDetails:t}=e,{price:n,priceWithoutDiscount:r,priceWithoutTax:i,priceWithoutDiscountAndTax:o,taxDisplay:s}=t;if(s!==_i)return e;let a={...e,priceDetails:{...t,price:i??n,priceWithoutDiscount:o??r,taxDisplay:Si}};return a.offerType==="TRIAL"&&a.priceDetails.price===0&&(a.priceDetails.price=a.priceDetails.priceWithoutDiscount),a}var Bt=function(e,t){return Bt=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(n,r){n.__proto__=r}||function(n,r){for(var i in r)Object.prototype.hasOwnProperty.call(r,i)&&(n[i]=r[i])},Bt(e,t)};function Ce(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");Bt(e,t);function n(){this.constructor=e}e.prototype=t===null?Object.create(t):(n.prototype=t.prototype,new n)}var y=function(){return y=Object.assign||function(t){for(var n,r=1,i=arguments.length;r0}),n=[],r=0,i=t;r1)throw new RangeError("integer-width stems only accept a single optional option");i.options[0].replace(bi,function(a,l,u,c,p,f){if(l)t.minimumIntegerDigits=u.length;else{if(c&&p)throw new Error("We currently do not support maximum integer digits");if(f)throw new Error("We currently do not support exact integer digits")}return""});continue}if(tn.test(i.stem)){t.minimumIntegerDigits=i.stem.length;continue}if(Zr.test(i.stem)){if(i.options.length>1)throw new RangeError("Fraction-precision stems only accept a single optional option");i.stem.replace(Zr,function(a,l,u,c,p,f){return u==="*"?t.minimumFractionDigits=l.length:c&&c[0]==="#"?t.maximumFractionDigits=c.length:p&&f?(t.minimumFractionDigits=p.length,t.maximumFractionDigits=p.length+f.length):(t.minimumFractionDigits=l.length,t.maximumFractionDigits=l.length),""}),i.options.length&&(t=y(y({},t),Jr(i.options[0])));continue}if(en.test(i.stem)){t=y(y({},t),Jr(i.stem));continue}var o=rn(i.stem);o&&(t=y(y({},t),o));var s=Ai(i.stem);s&&(t=y(y({},t),s))}return t}var zt,wi=new RegExp("^"+qt.source+"*"),Li=new RegExp(qt.source+"*$");function g(e,t){return{start:e,end:t}}var Oi=!!String.prototype.startsWith,Ni=!!String.fromCodePoint,Ci=!!Object.fromEntries,Ri=!!String.prototype.codePointAt,Ii=!!String.prototype.trimStart,Mi=!!String.prototype.trimEnd,Di=!!Number.isSafeInteger,Ui=Di?Number.isSafeInteger:function(e){return typeof e=="number"&&isFinite(e)&&Math.floor(e)===e&&Math.abs(e)<=9007199254740991},Jt=!0;try{on=ln("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu"),Jt=((zt=on.exec("a"))===null||zt===void 0?void 0:zt[0])==="a"}catch{Jt=!1}var on,sn=Oi?function(t,n,r){return t.startsWith(n,r)}:function(t,n,r){return t.slice(r,r+n.length)===n},Qt=Ni?String.fromCodePoint:function(){for(var t=[],n=0;no;){if(s=t[o++],s>1114111)throw RangeError(s+" is not a valid code point");r+=s<65536?String.fromCharCode(s):String.fromCharCode(((s-=65536)>>10)+55296,s%1024+56320)}return r},an=Ci?Object.fromEntries:function(t){for(var n={},r=0,i=t;r=r)){var i=t.charCodeAt(n),o;return i<55296||i>56319||n+1===r||(o=t.charCodeAt(n+1))<56320||o>57343?i:(i-55296<<10)+(o-56320)+65536}},ki=Ii?function(t){return t.trimStart()}:function(t){return t.replace(wi,"")},Gi=Mi?function(t){return t.trimEnd()}:function(t){return t.replace(Li,"")};function ln(e,t){return new RegExp(e,t)}var Kt;Jt?(Zt=ln("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu"),Kt=function(t,n){var r;Zt.lastIndex=n;var i=Zt.exec(t);return(r=i[1])!==null&&r!==void 0?r:""}):Kt=function(t,n){for(var r=[];;){var i=cn(t,n);if(i===void 0||fn(i)||ji(i))break;r.push(i),n+=i>=65536?2:1}return Qt.apply(void 0,r)};var Zt,un=function(){function e(t,n){n===void 0&&(n={}),this.message=t,this.position={offset:0,line:1,column:1},this.ignoreTag=!!n.ignoreTag,this.requiresOtherClause=!!n.requiresOtherClause,this.shouldParseSkeletons=!!n.shouldParseSkeletons}return e.prototype.parse=function(){if(this.offset()!==0)throw Error("parser can only be used once");return this.parseMessage(0,"",!1)},e.prototype.parseMessage=function(t,n,r){for(var i=[];!this.isEOF();){var o=this.char();if(o===123){var s=this.parseArgument(t,r);if(s.err)return s;i.push(s.val)}else{if(o===125&&t>0)break;if(o===35&&(n==="plural"||n==="selectordinal")){var a=this.clonePosition();this.bump(),i.push({type:b.pound,location:g(a,this.clonePosition())})}else if(o===60&&!this.ignoreTag&&this.peek()===47){if(r)break;return this.error(E.UNMATCHED_CLOSING_TAG,g(this.clonePosition(),this.clonePosition()))}else if(o===60&&!this.ignoreTag&&er(this.peek()||0)){var s=this.parseTag(t,n);if(s.err)return s;i.push(s.val)}else{var s=this.parseLiteral(t,n);if(s.err)return s;i.push(s.val)}}}return{val:i,err:null}},e.prototype.parseTag=function(t,n){var r=this.clonePosition();this.bump();var i=this.parseTagName();if(this.bumpSpace(),this.bumpIf("/>"))return{val:{type:b.literal,value:"<"+i+"/>",location:g(r,this.clonePosition())},err:null};if(this.bumpIf(">")){var o=this.parseMessage(t+1,n,!0);if(o.err)return o;var s=o.val,a=this.clonePosition();if(this.bumpIf("")?{val:{type:b.tag,value:i,children:s,location:g(r,this.clonePosition())},err:null}:this.error(E.INVALID_TAG,g(a,this.clonePosition())))}else return this.error(E.UNCLOSED_TAG,g(r,this.clonePosition()))}else return this.error(E.INVALID_TAG,g(r,this.clonePosition()))},e.prototype.parseTagName=function(){var t=this.offset();for(this.bump();!this.isEOF()&&Vi(this.char());)this.bump();return this.message.slice(t,this.offset())},e.prototype.parseLiteral=function(t,n){for(var r=this.clonePosition(),i="";;){var o=this.tryParseQuote(n);if(o){i+=o;continue}var s=this.tryParseUnquoted(t,n);if(s){i+=s;continue}var a=this.tryParseLeftAngleBracket();if(a){i+=a;continue}break}var l=g(r,this.clonePosition());return{val:{type:b.literal,value:i,location:l},err:null}},e.prototype.tryParseLeftAngleBracket=function(){return!this.isEOF()&&this.char()===60&&(this.ignoreTag||!Fi(this.peek()||0))?(this.bump(),"<"):null},e.prototype.tryParseQuote=function(t){if(this.isEOF()||this.char()!==39)return null;switch(this.peek()){case 39:return this.bump(),this.bump(),"'";case 123:case 60:case 62:case 125:break;case 35:if(t==="plural"||t==="selectordinal")break;return null;default:return null}this.bump();var n=[this.char()];for(this.bump();!this.isEOF();){var r=this.char();if(r===39)if(this.peek()===39)n.push(39),this.bump();else{this.bump();break}else n.push(r);this.bump()}return Qt.apply(void 0,n)},e.prototype.tryParseUnquoted=function(t,n){if(this.isEOF())return null;var r=this.char();return r===60||r===123||r===35&&(n==="plural"||n==="selectordinal")||r===125&&t>0?null:(this.bump(),Qt(r))},e.prototype.parseArgument=function(t,n){var r=this.clonePosition();if(this.bump(),this.bumpSpace(),this.isEOF())return this.error(E.EXPECT_ARGUMENT_CLOSING_BRACE,g(r,this.clonePosition()));if(this.char()===125)return this.bump(),this.error(E.EMPTY_ARGUMENT,g(r,this.clonePosition()));var i=this.parseIdentifierIfPossible().value;if(!i)return this.error(E.MALFORMED_ARGUMENT,g(r,this.clonePosition()));if(this.bumpSpace(),this.isEOF())return this.error(E.EXPECT_ARGUMENT_CLOSING_BRACE,g(r,this.clonePosition()));switch(this.char()){case 125:return this.bump(),{val:{type:b.argument,value:i,location:g(r,this.clonePosition())},err:null};case 44:return this.bump(),this.bumpSpace(),this.isEOF()?this.error(E.EXPECT_ARGUMENT_CLOSING_BRACE,g(r,this.clonePosition())):this.parseArgumentOptions(t,n,i,r);default:return this.error(E.MALFORMED_ARGUMENT,g(r,this.clonePosition()))}},e.prototype.parseIdentifierIfPossible=function(){var t=this.clonePosition(),n=this.offset(),r=Kt(this.message,n),i=n+r.length;this.bumpTo(i);var o=this.clonePosition(),s=g(t,o);return{value:r,location:s}},e.prototype.parseArgumentOptions=function(t,n,r,i){var o,s=this.clonePosition(),a=this.parseIdentifierIfPossible().value,l=this.clonePosition();switch(a){case"":return this.error(E.EXPECT_ARGUMENT_TYPE,g(s,l));case"number":case"date":case"time":{this.bumpSpace();var u=null;if(this.bumpIf(",")){this.bumpSpace();var c=this.clonePosition(),p=this.parseSimpleArgStyleIfPossible();if(p.err)return p;var f=Gi(p.val);if(f.length===0)return this.error(E.EXPECT_ARGUMENT_STYLE,g(this.clonePosition(),this.clonePosition()));var h=g(c,this.clonePosition());u={style:f,styleLocation:h}}var d=this.tryParseArgumentClose(i);if(d.err)return d;var _=g(i,this.clonePosition());if(u&&sn(u?.style,"::",0)){var S=ki(u.style.slice(2));if(a==="number"){var p=this.parseNumberSkeletonFromString(S,u.styleLocation);return p.err?p:{val:{type:b.number,value:r,location:_,style:p.val},err:null}}else{if(S.length===0)return this.error(E.EXPECT_DATE_TIME_SKELETON,_);var f={type:ce.dateTime,pattern:S,location:u.styleLocation,parsedOptions:this.shouldParseSkeletons?qr(S):{}},A=a==="date"?b.date:b.time;return{val:{type:A,value:r,location:_,style:f},err:null}}}return{val:{type:a==="number"?b.number:a==="date"?b.date:b.time,value:r,location:_,style:(o=u?.style)!==null&&o!==void 0?o:null},err:null}}case"plural":case"selectordinal":case"select":{var w=this.clonePosition();if(this.bumpSpace(),!this.bumpIf(","))return this.error(E.EXPECT_SELECT_ARGUMENT_OPTIONS,g(w,y({},w)));this.bumpSpace();var T=this.parseIdentifierIfPossible(),C=0;if(a!=="select"&&T.value==="offset"){if(!this.bumpIf(":"))return this.error(E.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE,g(this.clonePosition(),this.clonePosition()));this.bumpSpace();var p=this.tryParseDecimalInteger(E.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE,E.INVALID_PLURAL_ARGUMENT_OFFSET_VALUE);if(p.err)return p;this.bumpSpace(),T=this.parseIdentifierIfPossible(),C=p.val}var P=this.tryParsePluralOrSelectOptions(t,a,n,T);if(P.err)return P;var d=this.tryParseArgumentClose(i);if(d.err)return d;var L=g(i,this.clonePosition());return a==="select"?{val:{type:b.select,value:r,options:an(P.val),location:L},err:null}:{val:{type:b.plural,value:r,options:an(P.val),offset:C,pluralType:a==="plural"?"cardinal":"ordinal",location:L},err:null}}default:return this.error(E.INVALID_ARGUMENT_TYPE,g(s,l))}},e.prototype.tryParseArgumentClose=function(t){return this.isEOF()||this.char()!==125?this.error(E.EXPECT_ARGUMENT_CLOSING_BRACE,g(t,this.clonePosition())):(this.bump(),{val:!0,err:null})},e.prototype.parseSimpleArgStyleIfPossible=function(){for(var t=0,n=this.clonePosition();!this.isEOF();){var r=this.char();switch(r){case 39:{this.bump();var i=this.clonePosition();if(!this.bumpUntil("'"))return this.error(E.UNCLOSED_QUOTE_IN_ARGUMENT_STYLE,g(i,this.clonePosition()));this.bump();break}case 123:{t+=1,this.bump();break}case 125:{if(t>0)t-=1;else return{val:this.message.slice(n.offset,this.offset()),err:null};break}default:this.bump();break}}return{val:this.message.slice(n.offset,this.offset()),err:null}},e.prototype.parseNumberSkeletonFromString=function(t,n){var r=[];try{r=Kr(t)}catch{return this.error(E.INVALID_NUMBER_SKELETON,n)}return{val:{type:ce.number,tokens:r,location:n,parsedOptions:this.shouldParseSkeletons?nn(r):{}},err:null}},e.prototype.tryParsePluralOrSelectOptions=function(t,n,r,i){for(var o,s=!1,a=[],l=new Set,u=i.value,c=i.location;;){if(u.length===0){var p=this.clonePosition();if(n!=="select"&&this.bumpIf("=")){var f=this.tryParseDecimalInteger(E.EXPECT_PLURAL_ARGUMENT_SELECTOR,E.INVALID_PLURAL_ARGUMENT_SELECTOR);if(f.err)return f;c=g(p,this.clonePosition()),u=this.message.slice(p.offset,this.offset())}else break}if(l.has(u))return this.error(n==="select"?E.DUPLICATE_SELECT_ARGUMENT_SELECTOR:E.DUPLICATE_PLURAL_ARGUMENT_SELECTOR,c);u==="other"&&(s=!0),this.bumpSpace();var h=this.clonePosition();if(!this.bumpIf("{"))return this.error(n==="select"?E.EXPECT_SELECT_ARGUMENT_SELECTOR_FRAGMENT:E.EXPECT_PLURAL_ARGUMENT_SELECTOR_FRAGMENT,g(this.clonePosition(),this.clonePosition()));var d=this.parseMessage(t+1,n,r);if(d.err)return d;var _=this.tryParseArgumentClose(h);if(_.err)return _;a.push([u,{value:d.val,location:g(h,this.clonePosition())}]),l.add(u),this.bumpSpace(),o=this.parseIdentifierIfPossible(),u=o.value,c=o.location}return a.length===0?this.error(n==="select"?E.EXPECT_SELECT_ARGUMENT_SELECTOR:E.EXPECT_PLURAL_ARGUMENT_SELECTOR,g(this.clonePosition(),this.clonePosition())):this.requiresOtherClause&&!s?this.error(E.MISSING_OTHER_CLAUSE,g(this.clonePosition(),this.clonePosition())):{val:a,err:null}},e.prototype.tryParseDecimalInteger=function(t,n){var r=1,i=this.clonePosition();this.bumpIf("+")||this.bumpIf("-")&&(r=-1);for(var o=!1,s=0;!this.isEOF();){var a=this.char();if(a>=48&&a<=57)o=!0,s=s*10+(a-48),this.bump();else break}var l=g(i,this.clonePosition());return o?(s*=r,Ui(s)?{val:s,err:null}:this.error(n,l)):this.error(t,l)},e.prototype.offset=function(){return this.position.offset},e.prototype.isEOF=function(){return this.offset()===this.message.length},e.prototype.clonePosition=function(){return{offset:this.position.offset,line:this.position.line,column:this.position.column}},e.prototype.char=function(){var t=this.position.offset;if(t>=this.message.length)throw Error("out of bound");var n=cn(this.message,t);if(n===void 0)throw Error("Offset "+t+" is at invalid UTF-16 code unit boundary");return n},e.prototype.error=function(t,n){return{val:null,err:{kind:t,message:this.message,location:n}}},e.prototype.bump=function(){if(!this.isEOF()){var t=this.char();t===10?(this.position.line+=1,this.position.column=1,this.position.offset+=1):(this.position.column+=1,this.position.offset+=t<65536?1:2)}},e.prototype.bumpIf=function(t){if(sn(this.message,t,this.offset())){for(var n=0;n=0?(this.bumpTo(r),!0):(this.bumpTo(this.message.length),!1)},e.prototype.bumpTo=function(t){if(this.offset()>t)throw Error("targetOffset "+t+" must be greater than or equal to the current offset "+this.offset());for(t=Math.min(t,this.message.length);;){var n=this.offset();if(n===t)break;if(n>t)throw Error("targetOffset "+t+" is at invalid UTF-16 code unit boundary");if(this.bump(),this.isEOF())break}},e.prototype.bumpSpace=function(){for(;!this.isEOF()&&fn(this.char());)this.bump()},e.prototype.peek=function(){if(this.isEOF())return null;var t=this.char(),n=this.offset(),r=this.message.charCodeAt(n+(t>=65536?2:1));return r??null},e}();function er(e){return e>=97&&e<=122||e>=65&&e<=90}function Fi(e){return er(e)||e===47}function Vi(e){return e===45||e===46||e>=48&&e<=57||e===95||e>=97&&e<=122||e>=65&&e<=90||e==183||e>=192&&e<=214||e>=216&&e<=246||e>=248&&e<=893||e>=895&&e<=8191||e>=8204&&e<=8205||e>=8255&&e<=8256||e>=8304&&e<=8591||e>=11264&&e<=12271||e>=12289&&e<=55295||e>=63744&&e<=64975||e>=65008&&e<=65533||e>=65536&&e<=983039}function fn(e){return e>=9&&e<=13||e===32||e===133||e>=8206&&e<=8207||e===8232||e===8233}function ji(e){return e>=33&&e<=35||e===36||e>=37&&e<=39||e===40||e===41||e===42||e===43||e===44||e===45||e>=46&&e<=47||e>=58&&e<=59||e>=60&&e<=62||e>=63&&e<=64||e===91||e===92||e===93||e===94||e===96||e===123||e===124||e===125||e===126||e===161||e>=162&&e<=165||e===166||e===167||e===169||e===171||e===172||e===174||e===176||e===177||e===182||e===187||e===191||e===215||e===247||e>=8208&&e<=8213||e>=8214&&e<=8215||e===8216||e===8217||e===8218||e>=8219&&e<=8220||e===8221||e===8222||e===8223||e>=8224&&e<=8231||e>=8240&&e<=8248||e===8249||e===8250||e>=8251&&e<=8254||e>=8257&&e<=8259||e===8260||e===8261||e===8262||e>=8263&&e<=8273||e===8274||e===8275||e>=8277&&e<=8286||e>=8592&&e<=8596||e>=8597&&e<=8601||e>=8602&&e<=8603||e>=8604&&e<=8607||e===8608||e>=8609&&e<=8610||e===8611||e>=8612&&e<=8613||e===8614||e>=8615&&e<=8621||e===8622||e>=8623&&e<=8653||e>=8654&&e<=8655||e>=8656&&e<=8657||e===8658||e===8659||e===8660||e>=8661&&e<=8691||e>=8692&&e<=8959||e>=8960&&e<=8967||e===8968||e===8969||e===8970||e===8971||e>=8972&&e<=8991||e>=8992&&e<=8993||e>=8994&&e<=9e3||e===9001||e===9002||e>=9003&&e<=9083||e===9084||e>=9085&&e<=9114||e>=9115&&e<=9139||e>=9140&&e<=9179||e>=9180&&e<=9185||e>=9186&&e<=9254||e>=9255&&e<=9279||e>=9280&&e<=9290||e>=9291&&e<=9311||e>=9472&&e<=9654||e===9655||e>=9656&&e<=9664||e===9665||e>=9666&&e<=9719||e>=9720&&e<=9727||e>=9728&&e<=9838||e===9839||e>=9840&&e<=10087||e===10088||e===10089||e===10090||e===10091||e===10092||e===10093||e===10094||e===10095||e===10096||e===10097||e===10098||e===10099||e===10100||e===10101||e>=10132&&e<=10175||e>=10176&&e<=10180||e===10181||e===10182||e>=10183&&e<=10213||e===10214||e===10215||e===10216||e===10217||e===10218||e===10219||e===10220||e===10221||e===10222||e===10223||e>=10224&&e<=10239||e>=10240&&e<=10495||e>=10496&&e<=10626||e===10627||e===10628||e===10629||e===10630||e===10631||e===10632||e===10633||e===10634||e===10635||e===10636||e===10637||e===10638||e===10639||e===10640||e===10641||e===10642||e===10643||e===10644||e===10645||e===10646||e===10647||e===10648||e>=10649&&e<=10711||e===10712||e===10713||e===10714||e===10715||e>=10716&&e<=10747||e===10748||e===10749||e>=10750&&e<=11007||e>=11008&&e<=11055||e>=11056&&e<=11076||e>=11077&&e<=11078||e>=11079&&e<=11084||e>=11085&&e<=11123||e>=11124&&e<=11125||e>=11126&&e<=11157||e===11158||e>=11159&&e<=11263||e>=11776&&e<=11777||e===11778||e===11779||e===11780||e===11781||e>=11782&&e<=11784||e===11785||e===11786||e===11787||e===11788||e===11789||e>=11790&&e<=11798||e===11799||e>=11800&&e<=11801||e===11802||e===11803||e===11804||e===11805||e>=11806&&e<=11807||e===11808||e===11809||e===11810||e===11811||e===11812||e===11813||e===11814||e===11815||e===11816||e===11817||e>=11818&&e<=11822||e===11823||e>=11824&&e<=11833||e>=11834&&e<=11835||e>=11836&&e<=11839||e===11840||e===11841||e===11842||e>=11843&&e<=11855||e>=11856&&e<=11857||e===11858||e>=11859&&e<=11903||e>=12289&&e<=12291||e===12296||e===12297||e===12298||e===12299||e===12300||e===12301||e===12302||e===12303||e===12304||e===12305||e>=12306&&e<=12307||e===12308||e===12309||e===12310||e===12311||e===12312||e===12313||e===12314||e===12315||e===12316||e===12317||e>=12318&&e<=12319||e===12320||e===12336||e===64830||e===64831||e>=65093&&e<=65094}function tr(e){e.forEach(function(t){if(delete t.location,Qe(t)||Ke(t))for(var n in t.options)delete t.options[n].location,tr(t.options[n].value);else ze(t)&&tt(t.style)||(Ze(t)||Je(t))&&Re(t.style)?delete t.style.location:et(t)&&tr(t.children)})}function pn(e,t){t===void 0&&(t={}),t=y({shouldParseSkeletons:!0,requiresOtherClause:!0},t);var n=new un(e,t).parse();if(n.err){var r=SyntaxError(E[n.err.kind]);throw r.location=n.err.location,r.originalMessage=n.err.message,r}return t?.captureLocation||tr(n.val),n.val}function Ie(e,t){var n=t&&t.cache?t.cache:$i,r=t&&t.serializer?t.serializer:Bi,i=t&&t.strategy?t.strategy:Wi;return i(e,{cache:n,serializer:r})}function Hi(e){return e==null||typeof e=="number"||typeof e=="boolean"}function mn(e,t,n,r){var i=Hi(r)?r:n(r),o=t.get(i);return typeof o>"u"&&(o=e.call(this,r),t.set(i,o)),o}function hn(e,t,n){var r=Array.prototype.slice.call(arguments,3),i=n(r),o=t.get(i);return typeof o>"u"&&(o=e.apply(this,r),t.set(i,o)),o}function rr(e,t,n,r,i){return n.bind(t,e,r,i)}function Wi(e,t){var n=e.length===1?mn:hn;return rr(e,this,n,t.cache.create(),t.serializer)}function Xi(e,t){return rr(e,this,hn,t.cache.create(),t.serializer)}function Yi(e,t){return rr(e,this,mn,t.cache.create(),t.serializer)}var Bi=function(){return JSON.stringify(arguments)};function nr(){this.cache=Object.create(null)}nr.prototype.get=function(e){return this.cache[e]};nr.prototype.set=function(e,t){this.cache[e]=t};var $i={create:function(){return new nr}},rt={variadic:Xi,monadic:Yi};var le;(function(e){e.MISSING_VALUE="MISSING_VALUE",e.INVALID_VALUE="INVALID_VALUE",e.MISSING_INTL_API="MISSING_INTL_API"})(le||(le={}));var Me=function(e){Ce(t,e);function t(n,r,i){var o=e.call(this,n)||this;return o.code=r,o.originalMessage=i,o}return t.prototype.toString=function(){return"[formatjs Error: "+this.code+"] "+this.message},t}(Error);var ir=function(e){Ce(t,e);function t(n,r,i,o){return e.call(this,'Invalid values for "'+n+'": "'+r+'". Options are "'+Object.keys(i).join('", "')+'"',le.INVALID_VALUE,o)||this}return t}(Me);var dn=function(e){Ce(t,e);function t(n,r,i){return e.call(this,'Value for "'+n+'" must be of type '+r,le.INVALID_VALUE,i)||this}return t}(Me);var En=function(e){Ce(t,e);function t(n,r){return e.call(this,'The intl string context variable "'+n+'" was not provided to the string "'+r+'"',le.MISSING_VALUE,r)||this}return t}(Me);var I;(function(e){e[e.literal=0]="literal",e[e.object=1]="object"})(I||(I={}));function qi(e){return e.length<2?e:e.reduce(function(t,n){var r=t[t.length-1];return!r||r.type!==I.literal||n.type!==I.literal?t.push(n):r.value+=n.value,t},[])}function zi(e){return typeof e=="function"}function De(e,t,n,r,i,o,s){if(e.length===1&&$t(e[0]))return[{type:I.literal,value:e[0].value}];for(var a=[],l=0,u=e;lt in e?Xe(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var ii=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),ai=(e,t)=>{for(var n in t)Xe(e,n,{get:t[n],enumerable:!0})},oi=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of ei(t))!ri.call(e,i)&&i!==n&&Xe(e,i,{get:()=>t[i],enumerable:!(r=Qn(t,i))||r.enumerable});return e};var si=(e,t,n)=>(n=e!=null?Jn(ti(e)):{},oi(t||!e||!e.__esModule?Xe(n,"default",{value:e,enumerable:!0}):n,e));var Q=(e,t,n)=>(ni(e,typeof t!="symbol"?t+"":t,n),n),wr=(e,t,n)=>{if(!t.has(e))throw TypeError("Cannot "+n)};var _t=(e,t,n)=>(wr(e,t,"read from private field"),n?n.call(e):t.get(e)),kr=(e,t,n)=>{if(t.has(e))throw TypeError("Cannot add the same private member more than once");t instanceof WeakSet?t.add(e):t.set(e,n)},It=(e,t,n,r)=>(wr(e,t,"write to private field"),r?r.call(e,n):t.set(e,n),n);var zn=ii((Il,ro)=>{ro.exports={total:38,offset:0,limit:38,data:[{lang:"ar",recurrenceLabel:"{recurrenceTerm, select, MONTH {/\u0627\u0644\u0634\u0647\u0631} YEAR {/\u0627\u0644\u0639\u0627\u0645} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {\u0643\u0644 \u0634\u0647\u0631} YEAR {\u0643\u0644 \u0639\u0627\u0645} other {}}",perUnitLabel:"{perUnit, select, LICENSE {\u0644\u0643\u0644 \u062A\u0631\u062E\u064A\u0635} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {\u0644\u0643\u0644 \u062A\u0631\u062E\u064A\u0635} other {}}",freeLabel:"\u0645\u062C\u0627\u0646\u064B\u0627",freeAriaLabel:"\u0645\u062C\u0627\u0646\u064B\u0627",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"\u0623\u0648 \u0628\u062F\u0644\u0627\u064B \u0645\u0646 \u0630\u0644\u0643 \u0628\u0642\u064A\u0645\u0629 {alternativePrice}",strikethroughAriaLabel:"\u0628\u0634\u0643\u0644 \u0645\u0646\u062A\u0638\u0645 \u0628\u0642\u064A\u0645\u0629 {strikethroughPrice}"},{lang:"bg",recurrenceLabel:"{recurrenceTerm, select, MONTH {/\u043C\u0435\u0441.} YEAR {/\u0433\u043E\u0434.} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {\u043D\u0430 \u043C\u0435\u0441\u0435\u0446} YEAR {\u043D\u0430 \u0433\u043E\u0434\u0438\u043D\u0430} other {}}",perUnitLabel:"{perUnit, select, LICENSE {\u043D\u0430 \u043B\u0438\u0446\u0435\u043D\u0437} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {\u043D\u0430 \u043B\u0438\u0446\u0435\u043D\u0437} other {}}",freeLabel:"\u0411\u0435\u0437\u043F\u043B\u0430\u0442\u043D\u043E",freeAriaLabel:"\u0411\u0435\u0437\u043F\u043B\u0430\u0442\u043D\u043E",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"\u0410\u043B\u0442\u0435\u0440\u043D\u0430\u0442\u0438\u0432\u043D\u043E \u043D\u0430 {alternativePrice}",strikethroughAriaLabel:"\u0420\u0435\u0434\u043E\u0432\u043D\u043E \u043D\u0430 {strikethroughPrice}"},{lang:"cs",recurrenceLabel:"{recurrenceTerm, select, MONTH {/m\u011Bs\xEDc} YEAR {/rok} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {za m\u011Bs\xEDc} YEAR {za rok} other {}}",perUnitLabel:"{perUnit, select, LICENSE {za licenci} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {za licenci} other {}}",freeLabel:"Zdarma",freeAriaLabel:"Zdarma",taxExclusiveLabel:"{taxTerm, select, GST {bez dan\u011B ze zbo\u017E\xED a slu\u017Eeb} VAT {bez DPH} TAX {bez dan\u011B} IVA {bez IVA} SST {bez SST} KDV {bez KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {v\u010Detn\u011B dan\u011B ze zbo\u017E\xED a slu\u017Eeb} VAT {v\u010Detn\u011B DPH} TAX {v\u010Detn\u011B dan\u011B} IVA {v\u010Detn\u011B IVA} SST {v\u010Detn\u011B SST} KDV {v\u010Detn\u011B KDV} other {}}",alternativePriceAriaLabel:"P\u0159\xEDpadn\u011B za {alternativePrice}",strikethroughAriaLabel:"Pravideln\u011B za {strikethroughPrice}"},{lang:"da",recurrenceLabel:"{recurrenceTerm, select, MONTH {/md} YEAR {/\xE5r} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {pr. m\xE5ned} YEAR {pr. \xE5r} other {}}",perUnitLabel:"{perUnit, select, LICENSE {pr. licens} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {pr. licens} other {}}",freeLabel:"Gratis",freeAriaLabel:"Gratis",taxExclusiveLabel:"{taxTerm, select, GST {ekskl. GST} VAT {ekskl. moms} TAX {ekskl. skat} IVA {ekskl. IVA} SST {ekskl. SST} KDV {ekskl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {inkl. GST} VAT {inkl. moms} TAX {inkl. skat} IVA {inkl. IVA} SST {inkl. SST} KDV {inkl. KDV} other {}}",alternativePriceAriaLabel:"Alternativt til {alternativePrice}",strikethroughAriaLabel:"Normalpris {strikethroughPrice}"},{lang:"de",recurrenceLabel:"{recurrenceTerm, select, MONTH {/Monat} YEAR {/Jahr} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {pro Monat} YEAR {pro Jahr} other {}}",perUnitLabel:"{perUnit, select, LICENSE {pro Lizenz} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {pro Lizenz} other {}}",freeLabel:"Kostenlos",freeAriaLabel:"Kostenlos",taxExclusiveLabel:"{taxTerm, select, GST {zzgl. GST} VAT {zzgl. MwSt.} TAX {zzgl. Steuern} IVA {zzgl. IVA} SST {zzgl. SST} KDV {zzgl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {inkl. GST} VAT {inkl. MwSt.} TAX {inkl. Steuern} IVA {inkl. IVA} SST {inkl. SST} KDV {inkl. KDV} other {}}",alternativePriceAriaLabel:"Alternativ: {alternativePrice}",strikethroughAriaLabel:"Regul\xE4r: {strikethroughPrice}"},{lang:"en",recurrenceLabel:"{recurrenceTerm, select, MONTH {/mo} YEAR {/yr} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {per month} YEAR {per year} other {}}",perUnitLabel:"{perUnit, select, LICENSE {per license} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {per license} other {}}",freeLabel:"Free",freeAriaLabel:"Free",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"Alternatively at {alternativePrice}",strikethroughAriaLabel:"Regularly at {strikethroughPrice}"},{lang:"et",recurrenceLabel:"{recurrenceTerm, select, MONTH {kuus} YEAR {aastas} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {kuus} YEAR {aastas} other {}}",perUnitLabel:"{perUnit, select, LICENSE {litsentsi kohta} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {litsentsi kohta} other {}}",freeLabel:"Tasuta",freeAriaLabel:"Tasuta",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"Teise v\xF5imalusena hinnaga {alternativePrice}",strikethroughAriaLabel:"Tavahind {strikethroughPrice}"},{lang:"fi",recurrenceLabel:"{recurrenceTerm, select, MONTH {/kk} YEAR {/v} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {kuukausittain} YEAR {vuosittain} other {}}",perUnitLabel:"{perUnit, select, LICENSE {k\xE4ytt\xF6oikeutta kohti} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {k\xE4ytt\xF6oikeutta kohti} other {}}",freeLabel:"Maksuton",freeAriaLabel:"Maksuton",taxExclusiveLabel:"{taxTerm, select, GST {ilman GST:t\xE4} VAT {ilman ALV:t\xE4} TAX {ilman veroja} IVA {ilman IVA:ta} SST {ilman SST:t\xE4} KDV {ilman KDV:t\xE4} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {sis. GST:n} VAT {sis. ALV:n} TAX {sis. verot} IVA {sis. IVA:n} SST {sis. SST:n} KDV {sis. KDV:n} other {}}",alternativePriceAriaLabel:"Vaihtoehtoisesti hintaan {alternativePrice}",strikethroughAriaLabel:"S\xE4\xE4nn\xF6llisesti hintaan {strikethroughPrice}"},{lang:"fr",recurrenceLabel:"{recurrenceTerm, select, MONTH {/mois} YEAR {/an} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {par mois} YEAR {par an} other {}}",perUnitLabel:"{perUnit, select, LICENSE {par licence} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {par licence} other {}}",freeLabel:"Gratuit",freeAriaLabel:"Gratuit",taxExclusiveLabel:"{taxTerm, select, GST {hors TPS} VAT {hors TVA} TAX {hors taxes} IVA {hors IVA} SST {hors SST} KDV {hors KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {TPS comprise} VAT {TVA comprise} TAX {taxes comprises} IVA {IVA comprise} SST {SST comprise} KDV {KDV comprise} other {}}",alternativePriceAriaLabel:"Autre prix {alternativePrice}",strikethroughAriaLabel:"Prix habituel {strikethroughPrice}"},{lang:"he",recurrenceLabel:"{recurrenceTerm, select, MONTH {/\u05D7\u05D5\u05D3\u05E9} YEAR {/\u05E9\u05E0\u05D4} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {\u05DC\u05D7\u05D5\u05D3\u05E9} YEAR {\u05DC\u05E9\u05E0\u05D4} other {}}",perUnitLabel:"{perUnit, select, LICENSE {\u05DC\u05E8\u05D9\u05E9\u05D9\u05D5\u05DF} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {\u05DC\u05E8\u05D9\u05E9\u05D9\u05D5\u05DF} other {}}",freeLabel:"\u05D7\u05D9\u05E0\u05DD",freeAriaLabel:"\u05D7\u05D9\u05E0\u05DD",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"\u05DC\u05D7\u05DC\u05D5\u05E4\u05D9\u05DF \u05D1-{alternativePrice}",strikethroughAriaLabel:"\u05D1\u05D0\u05D5\u05E4\u05DF \u05E7\u05D1\u05D5\u05E2 \u05D1-{strikethroughPrice}"},{lang:"hu",recurrenceLabel:"{recurrenceTerm, select, MONTH {/h\xF3} YEAR {/\xE9v} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {havonta} YEAR {\xE9vente} other {}}",perUnitLabel:"{perUnit, select, LICENSE {licencenk\xE9nt} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {licencenk\xE9nt} other {}}",freeLabel:"Ingyenes",freeAriaLabel:"Ingyenes",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"M\xE1sik lehet\u0151s\xE9g: {alternativePrice}",strikethroughAriaLabel:"\xC1ltal\xE1ban {strikethroughPrice} \xE1ron"},{lang:"it",recurrenceLabel:"{recurrenceTerm, select, MONTH {/mese} YEAR {/anno} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {al mese} YEAR {all'anno} other {}}",perUnitLabel:"{perUnit, select, LICENSE {per licenza} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {per licenza} other {}}",freeLabel:"Gratuito",freeAriaLabel:"Gratuito",taxExclusiveLabel:"{taxTerm, select, GST {escl. GST} VAT {escl. IVA.} TAX {escl. imposte} IVA {escl. IVA} SST {escl. SST} KDV {escl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. IVA} TAX {incl. imposte} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"In alternativa a {alternativePrice}",strikethroughAriaLabel:"Regolarmente a {strikethroughPrice}"},{lang:"ja",recurrenceLabel:"{recurrenceTerm, select, MONTH {/\u6708} YEAR {/\u5E74} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {\u6BCE\u6708} YEAR {\u6BCE\u5E74} other {}}",perUnitLabel:"{perUnit, select, LICENSE {\u30E9\u30A4\u30BB\u30F3\u30B9\u3054\u3068} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {\u30E9\u30A4\u30BB\u30F3\u30B9\u3054\u3068} other {}}",freeLabel:"\u7121\u6599",freeAriaLabel:"\u7121\u6599",taxExclusiveLabel:"{taxTerm, select, GST {GST \u5225} VAT {VAT \u5225} TAX {\u7A0E\u5225} IVA {IVA \u5225} SST {SST \u5225} KDV {KDV \u5225} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {GST \u8FBC} VAT {VAT \u8FBC} TAX {\u7A0E\u8FBC} IVA {IVA \u8FBC} SST {SST \u8FBC} KDV {KDV \u8FBC} other {}}",alternativePriceAriaLabel:"\u7279\u5225\u4FA1\u683C : {alternativePrice}",strikethroughAriaLabel:"\u901A\u5E38\u4FA1\u683C : {strikethroughPrice}"},{lang:"ko",recurrenceLabel:"{recurrenceTerm, select, MONTH {/\uC6D4} YEAR {/\uB144} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {\uC6D4\uAC04} YEAR {\uC5F0\uAC04} other {}}",perUnitLabel:"{perUnit, select, LICENSE {\uB77C\uC774\uC120\uC2A4\uB2F9} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {\uB77C\uC774\uC120\uC2A4\uB2F9} other {}}",freeLabel:"\uBB34\uB8CC",freeAriaLabel:"\uBB34\uB8CC",taxExclusiveLabel:"{taxTerm, select, GST {GST \uC81C\uC678} VAT {VAT \uC81C\uC678} TAX {\uC138\uAE08 \uC81C\uC678} IVA {IVA \uC81C\uC678} SST {SST \uC81C\uC678} KDV {KDV \uC81C\uC678} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {GST \uD3EC\uD568} VAT {VAT \uD3EC\uD568} TAX {\uC138\uAE08 \uD3EC\uD568} IVA {IVA \uD3EC\uD568} SST {SST \uD3EC\uD568} KDV {KDV \uD3EC\uD568} other {}}",alternativePriceAriaLabel:"\uB610\uB294 {alternativePrice}\uC5D0",strikethroughAriaLabel:"\uB610\uB294 {alternativePrice}\uC5D0"},{lang:"lt",recurrenceLabel:"{recurrenceTerm, select, MONTH { per m\u0117n.} YEAR { per metus} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {per m\u0117n.} YEAR {per metus} other {}}",perUnitLabel:"{perUnit, select, LICENSE {u\u017E licencij\u0105} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {u\u017E licencij\u0105} other {}}",freeLabel:"Nemokamai",freeAriaLabel:"Nemokamai",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"Arba u\u017E {alternativePrice}",strikethroughAriaLabel:"Normaliai u\u017E {strikethroughPrice}"},{lang:"lv",recurrenceLabel:"{recurrenceTerm, select, MONTH {m\u0113nes\u012B} YEAR {gad\u0101} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {m\u0113nes\u012B} YEAR {gad\u0101} other {}}",perUnitLabel:"{perUnit, select, LICENSE {vienai licencei} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {vienai licencei} other {}}",freeLabel:"Bezmaksas",freeAriaLabel:"Bezmaksas",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"Alternat\u012Bvi par {alternativePrice}",strikethroughAriaLabel:"Regul\u0101ri par {strikethroughPrice}"},{lang:"nb",recurrenceLabel:"{recurrenceTerm, select, MONTH {/mnd.} YEAR {/\xE5r} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {per m\xE5ned} YEAR {per \xE5r} other {}}",perUnitLabel:"{perUnit, select, LICENSE {per lisens} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {per lisens} other {}}",freeLabel:"Fri",freeAriaLabel:"Fri",taxExclusiveLabel:"{taxTerm, select, GST {ekskl. GST} VAT {ekskl. moms} TAX {ekskl. avgift} IVA {ekskl. IVA} SST {ekskl. SST} KDV {ekskl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {inkl. GST} VAT {inkl. moms} TAX {inkl. avgift} IVA {inkl. IVA} SST {inkl. SST} KDV {inkl. KDV} other {}}",alternativePriceAriaLabel:"Alternativt til {alternativePrice}",strikethroughAriaLabel:"Regelmessig til {strikethroughPrice}"},{lang:"nl",recurrenceLabel:"{recurrenceTerm, select, MONTH {/mnd} YEAR {/jr} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {per maand} YEAR {per jaar} other {}}",perUnitLabel:"{perUnit, select, LICENSE {per licentie} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {per licentie} other {}}",freeLabel:"Gratis",freeAriaLabel:"Gratis",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. btw} TAX {excl. belasting} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. btw} TAX {incl. belasting} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"Nu {alternativePrice}",strikethroughAriaLabel:"Normaal {strikethroughPrice}"},{lang:"pl",recurrenceLabel:"{recurrenceTerm, select, MONTH { / mies.} YEAR { / rok} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH { / miesi\u0105c} YEAR { / rok} other {}}",perUnitLabel:"{perUnit, select, LICENSE {za licencj\u0119} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {za licencj\u0119} other {}}",freeLabel:"Bezp\u0142atne",freeAriaLabel:"Bezp\u0142atne",taxExclusiveLabel:"{taxTerm, select, GST {bez GST} VAT {bez VAT} TAX {netto} IVA {bez IVA} SST {bez SST} KDV {bez KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {z GST} VAT {z VAT} TAX {brutto} IVA {z IVA} SST {z SST} KDV {z KDV} other {}}",alternativePriceAriaLabel:"Lub za {alternativePrice}",strikethroughAriaLabel:"Cena zwyk\u0142a: {strikethroughPrice}"},{lang:"pt",recurrenceLabel:"{recurrenceTerm, select, MONTH {/m\xEAs} YEAR {/ano} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {por m\xEAs} YEAR {por ano} other {}}",perUnitLabel:"{perUnit, select, LICENSE {por licen\xE7a} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {por licen\xE7a} other {}}",freeLabel:"Gratuito",freeAriaLabel:"Gratuito",taxExclusiveLabel:"{taxTerm, select, GST {ICMS n\xE3o incluso} VAT {IVA n\xE3o incluso} TAX {impostos n\xE3o inclusos} IVA {IVA n\xE3o incluso} SST { SST n\xE3o incluso} KDV {KDV n\xE3o incluso} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {ICMS incluso} VAT {IVA incluso} TAX {impostos inclusos} IVA {IVA incluso} SST {SST incluso} KDV {KDV incluso} other {}}",alternativePriceAriaLabel:"Ou a {alternativePrice}",strikethroughAriaLabel:"Pre\xE7o normal: {strikethroughPrice}"},{lang:"ro",recurrenceLabel:"{recurrenceTerm, select, MONTH {/lun\u0103} YEAR {/an} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {pe lun\u0103} YEAR {pe an} other {}}",perUnitLabel:"{perUnit, select, LICENSE {pe licen\u021B\u0103} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {pe licen\u021B\u0103} other {}}",freeLabel:"Gratuit",freeAriaLabel:"Gratuit",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"Alternativ, la {alternativePrice}",strikethroughAriaLabel:"\xCEn mod normal, la {strikethroughPrice}"},{lang:"ru",recurrenceLabel:"{recurrenceTerm, select, MONTH {/\u043C\u0435\u0441.} YEAR {/\u0433.} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {\u0432 \u043C\u0435\u0441\u044F\u0446} YEAR {\u0432 \u0433\u043E\u0434} other {}}",perUnitLabel:"{perUnit, select, LICENSE {\u0437\u0430 \u043B\u0438\u0446\u0435\u043D\u0437\u0438\u044E} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {\u0437\u0430 \u043B\u0438\u0446\u0435\u043D\u0437\u0438\u044E} other {}}",freeLabel:"\u0411\u0435\u0441\u043F\u043B\u0430\u0442\u043D\u043E",freeAriaLabel:"\u0411\u0435\u0441\u043F\u043B\u0430\u0442\u043D\u043E",taxExclusiveLabel:"{taxTerm, select, GST {\u0438\u0441\u043A\u043B. \u043D\u0430\u043B\u043E\u0433 \u043D\u0430 \u0442\u043E\u0432\u0430\u0440\u044B \u0438 \u0443\u0441\u043B\u0443\u0433\u0438} VAT {\u0438\u0441\u043A\u043B. \u041D\u0414\u0421} TAX {\u0438\u0441\u043A\u043B. \u043D\u0430\u043B\u043E\u0433} IVA {\u0438\u0441\u043A\u043B. \u0418\u0412\u0410} SST {\u0438\u0441\u043A\u043B. SST} KDV {\u0438\u0441\u043A\u043B. \u041A\u0414\u0412} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {\u0432\u043A\u043B. \u043D\u0430\u043B\u043E\u0433 \u043D\u0430 \u0442\u043E\u0432\u0430\u0440\u044B \u0438 \u0443\u0441\u043B\u0443\u0433\u0438} VAT {\u0432\u043A\u043B. \u041D\u0414\u0421} TAX {\u0432\u043A\u043B. \u043D\u0430\u043B\u043E\u0433} IVA {\u0432\u043A\u043B. \u0418\u0412\u0410} SST {\u0432\u043A\u043B. SST} KDV {\u0432\u043A\u043B. \u041A\u0414\u0412} other {}}",alternativePriceAriaLabel:"\u0410\u043B\u044C\u0442\u0435\u0440\u043D\u0430\u0442\u0438\u0432\u043D\u044B\u0439 \u0432\u0430\u0440\u0438\u0430\u043D\u0442 \u0437\u0430 {alternativePrice}",strikethroughAriaLabel:"\u0420\u0435\u0433\u0443\u043B\u044F\u0440\u043D\u043E \u043F\u043E \u0446\u0435\u043D\u0435 {strikethroughPrice}"},{lang:"sk",recurrenceLabel:"{recurrenceTerm, select, MONTH {/mesiac} YEAR {/rok} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {za mesiac} YEAR {za rok} other {}}",perUnitLabel:"{perUnit, select, LICENSE {za licenciu} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {za licenciu} other {}}",freeLabel:"Zadarmo",freeAriaLabel:"Zadarmo",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"Pr\xEDpadne za {alternativePrice}",strikethroughAriaLabel:"Pravidelne za {strikethroughPrice}"},{lang:"sl",recurrenceLabel:"{recurrenceTerm, select, MONTH {/mesec} YEAR {/leto} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {na mesec} YEAR {na leto} other {}}",perUnitLabel:"{perUnit, select, LICENSE {na licenco} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {na licenco} other {}}",freeLabel:"Brezpla\u010Dno",freeAriaLabel:"Brezpla\u010Dno",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"Druga mo\u017Enost je: {alternativePrice}",strikethroughAriaLabel:"Redno po {strikethroughPrice}"},{lang:"sv",recurrenceLabel:"{recurrenceTerm, select, MONTH {/m\xE5n} YEAR {/\xE5r} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {per m\xE5nad} YEAR {per \xE5r} other {}}",perUnitLabel:"{perUnit, select, LICENSE {per licens} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {per licens} other {}}",freeLabel:"Kostnadsfritt",freeAriaLabel:"Kostnadsfritt",taxExclusiveLabel:"{taxTerm, select, GST {exkl. GST} VAT {exkl. moms} TAX {exkl. skatt} IVA {exkl. IVA} SST {exkl. SST} KDV {exkl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {inkl. GST} VAT {inkl. moms} TAX {inkl. skatt} IVA {inkl. IVA} SST {inkl. SST} KDV {inkl. KDV} other {}}",alternativePriceAriaLabel:"Alternativt f\xF6r {alternativePrice}",strikethroughAriaLabel:"Normalpris {strikethroughPrice}"},{lang:"tr",recurrenceLabel:"{recurrenceTerm, select, MONTH {/ay} YEAR {/y\u0131l} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {(ayl\u0131k)} YEAR {(y\u0131ll\u0131k)} other {}}",perUnitLabel:"{perUnit, select, LICENSE {(lisans ba\u015F\u0131na)} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {(lisans ba\u015F\u0131na)} other {}}",freeLabel:"\xDCcretsiz",freeAriaLabel:"\xDCcretsiz",taxExclusiveLabel:"{taxTerm, select, GST {GST hari\xE7} VAT {KDV hari\xE7} TAX {vergi hari\xE7} IVA {IVA hari\xE7} SST {SST hari\xE7} KDV {KDV hari\xE7} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {GST dahil} VAT {KDV dahil} TAX {vergi dahil} IVA {IVA dahil} SST {SST dahil} KDV {KDV dahil} other {}}",alternativePriceAriaLabel:"Ya da {alternativePrice}",strikethroughAriaLabel:"Standart fiyat: {strikethroughPrice}"},{lang:"uk",recurrenceLabel:"{recurrenceTerm, select, MONTH {/\u043C\u0456\u0441.} YEAR {/\u0440\u0456\u043A} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {\u043D\u0430 \u043C\u0456\u0441\u044F\u0446\u044C} YEAR {\u043D\u0430 \u0440\u0456\u043A} other {}}",perUnitLabel:"{perUnit, select, LICENSE {\u0437\u0430 \u043B\u0456\u0446\u0435\u043D\u0437\u0456\u044E} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {\u0437\u0430 \u043B\u0456\u0446\u0435\u043D\u0437\u0456\u044E} other {}}",freeLabel:"\u0411\u0435\u0437\u043A\u043E\u0448\u0442\u043E\u0432\u043D\u043E",freeAriaLabel:"\u0411\u0435\u0437\u043A\u043E\u0448\u0442\u043E\u0432\u043D\u043E",taxExclusiveLabel:"{taxTerm, select, GST {\u0431\u0435\u0437 GST} VAT {\u0431\u0435\u0437 \u041F\u0414\u0412} TAX {\u0431\u0435\u0437 \u043F\u043E\u0434\u0430\u0442\u043A\u0443} IVA {\u0431\u0435\u0437 IVA} SST {\u0431\u0435\u0437 SST} KDV {\u0431\u0435\u0437 KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {\u0440\u0430\u0437\u043E\u043C \u0456\u0437 GST} VAT {\u0440\u0430\u0437\u043E\u043C \u0456\u0437 \u041F\u0414\u0412} TAX {\u0440\u0430\u0437\u043E\u043C \u0456\u0437 \u043F\u043E\u0434\u0430\u0442\u043A\u043E\u043C} IVA {\u0440\u0430\u0437\u043E\u043C \u0437 IVA} SST {\u0440\u0430\u0437\u043E\u043C \u0456\u0437 SST} KDV {\u0440\u0430\u0437\u043E\u043C \u0456\u0437 KDV} other {}}",alternativePriceAriaLabel:"\u0410\u0431\u043E \u0437\u0430 {alternativePrice}",strikethroughAriaLabel:"\u0417\u0432\u0438\u0447\u0430\u0439\u043D\u0430 \u0446\u0456\u043D\u0430 {strikethroughPrice}"},{lang:"zh-hans",recurrenceLabel:"{recurrenceTerm, select, MONTH {/\u6708} YEAR {/\u5E74} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {\u6BCF\u6708} YEAR {\u6BCF\u5E74} other {}}",perUnitLabel:"{perUnit, select, LICENSE {\u6BCF\u4E2A\u8BB8\u53EF\u8BC1} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {\u6BCF\u4E2A\u8BB8\u53EF\u8BC1} other {}}",freeLabel:"\u514D\u8D39",freeAriaLabel:"\u514D\u8D39",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"\u6216\u5B9A\u4EF7 {alternativePrice}",strikethroughAriaLabel:"\u6B63\u5E38\u4EF7 {strikethroughPrice}"},{lang:"zh-hant",recurrenceLabel:"{recurrenceTerm, select, MONTH {/\u6708} YEAR {/\u5E74} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {\u6BCF\u6708} YEAR {\u6BCF\u5E74} other {}}",perUnitLabel:"{perUnit, select, LICENSE {\u6BCF\u500B\u6388\u6B0A} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {\u6BCF\u500B\u6388\u6B0A} other {}}",freeLabel:"\u514D\u8CBB",freeAriaLabel:"\u514D\u8CBB",taxExclusiveLabel:"{taxTerm, select, GST {\u4E0D\u542B GST} VAT {\u4E0D\u542B VAT} TAX {\u4E0D\u542B\u7A05} IVA {\u4E0D\u542B IVA} SST {\u4E0D\u542B SST} KDV {\u4E0D\u542B KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {\u542B GST} VAT {\u542B VAT} TAX {\u542B\u7A05} IVA {\u542B IVA} SST {\u542B SST} KDV {\u542B KDV} other {}}",alternativePriceAriaLabel:"\u6216\u8005\u5728 {alternativePrice}",strikethroughAriaLabel:"\u6A19\u6E96\u50F9\u683C\u70BA {strikethroughPrice}"},{lang:"es",recurrenceLabel:"{recurrenceTerm, select, MONTH {/mes} YEAR {/a\xF1o} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {al mes} YEAR {al a\xF1o} other {}}",perUnitLabel:"{perUnit, select, LICENSE {por licencia} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {por licencia} other {}}",freeLabel:"Gratuito",freeAriaLabel:"Gratuito",taxExclusiveLabel:"{taxTerm, select, GST {GST no incluido} VAT {IVA no incluido} TAX {Impuestos no incluidos} IVA {IVA no incluido} SST {SST no incluido} KDV {KDV no incluido} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {GST incluido} VAT {IVA incluido} TAX {Impuestos incluidos} IVA {IVA incluido} SST {SST incluido} KDV {KDV incluido} other {}}",alternativePriceAriaLabel:"Alternativamente por {alternativePrice}",strikethroughAriaLabel:"Normalmente a {strikethroughPrice}"},{lang:"in",recurrenceLabel:"{recurrenceTerm, select, MONTH {/bulan} YEAR {/tahun} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {per bulan} YEAR {per tahun} other {}}",perUnitLabel:"{perUnit, select, LICENSE {per lisensi} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {per lisensi} other {}}",freeLabel:"Gratis",freeAriaLabel:"Gratis",taxExclusiveLabel:"{taxTerm, select, GST {tidak termasuk PBJ} VAT {tidak termasuk PPN} TAX {tidak termasuk pajak} IVA {tidak termasuk IVA} SST {tidak termasuk SST} KDV {tidak termasuk KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {termasuk PBJ} VAT {termasuk PPN} TAX {termasuk pajak} IVA {termasuk IVA} SST {termasuk SST} KDV {termasuk KDV} other {}}",alternativePriceAriaLabel:"Atau seharga {alternativePrice}",strikethroughAriaLabel:"Normalnya seharga {strikethroughPrice}"},{lang:"vi",recurrenceLabel:"{recurrenceTerm, select, MONTH {/th\xE1ng} YEAR {/n\u0103m} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {m\u1ED7i th\xE1ng} YEAR {m\u1ED7i n\u0103m} other {}}",perUnitLabel:"{perUnit, select, LICENSE {m\u1ED7i gi\u1EA5y ph\xE9p} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {m\u1ED7i gi\u1EA5y ph\xE9p} other {}}",freeLabel:"Mi\u1EC5n ph\xED",freeAriaLabel:"Mi\u1EC5n ph\xED",taxExclusiveLabel:"{taxTerm, select, GST {ch\u01B0a bao g\u1ED3m thu\u1EBF h\xE0ng h\xF3a v\xE0 d\u1ECBch v\u1EE5} VAT {ch\u01B0a bao g\u1ED3m thu\u1EBF GTGT} TAX {ch\u01B0a bao g\u1ED3m thu\u1EBF} IVA {ch\u01B0a bao g\u1ED3m IVA} SST {ch\u01B0a bao g\u1ED3m SST} KDV {ch\u01B0a bao g\u1ED3m KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {(\u0111\xE3 bao g\u1ED3m thu\u1EBF h\xE0ng h\xF3a v\xE0 d\u1ECBch v\u1EE5)} VAT {(\u0111\xE3 bao g\u1ED3m thu\u1EBF GTGT)} TAX {(\u0111\xE3 bao g\u1ED3m thu\u1EBF)} IVA {(\u0111\xE3 bao g\u1ED3m IVA)} SST {(\u0111\xE3 bao g\u1ED3m SST)} KDV {(\u0111\xE3 bao g\u1ED3m KDV)} other {}}",alternativePriceAriaLabel:"Gi\xE1 \u01B0u \u0111\xE3i {alternativePrice}",strikethroughAriaLabel:"Gi\xE1 th\xF4ng th\u01B0\u1EDDng {strikethroughPrice}"},{lang:"th",recurrenceLabel:"{recurrenceTerm, select, MONTH {/\u0E40\u0E14\u0E37\u0E2D\u0E19} YEAR {/\u0E1B\u0E35} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {\u0E15\u0E48\u0E2D\u0E40\u0E14\u0E37\u0E2D\u0E19} YEAR {\u0E15\u0E48\u0E2D\u0E1B\u0E35} other {}}",perUnitLabel:"{perUnit, select, LICENSE {\u0E15\u0E48\u0E2D\u0E2A\u0E34\u0E17\u0E18\u0E34\u0E4C\u0E01\u0E32\u0E23\u0E43\u0E0A\u0E49\u0E07\u0E32\u0E19} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {\u0E15\u0E48\u0E2D\u0E2A\u0E34\u0E17\u0E18\u0E34\u0E4C\u0E01\u0E32\u0E23\u0E43\u0E0A\u0E49\u0E07\u0E32\u0E19} other {}}",freeLabel:"\u0E1F\u0E23\u0E35",freeAriaLabel:"\u0E1F\u0E23\u0E35",taxExclusiveLabel:"{taxTerm, select, GST {\u0E44\u0E21\u0E48\u0E23\u0E27\u0E21\u0E20\u0E32\u0E29\u0E35 GST} VAT {\u0E44\u0E21\u0E48\u0E23\u0E27\u0E21 VAT} TAX {\u0E44\u0E21\u0E48\u0E23\u0E27\u0E21\u0E20\u0E32\u0E29\u0E35} IVA {\u0E44\u0E21\u0E48\u0E23\u0E27\u0E21 IVA} SST {\u0E44\u0E21\u0E48\u0E23\u0E27\u0E21 SST} KDV {\u0E44\u0E21\u0E48\u0E23\u0E27\u0E21 KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {\u0E23\u0E27\u0E21\u0E20\u0E32\u0E29\u0E35 GST} VAT {\u0E23\u0E27\u0E21 VAT} TAX {\u0E23\u0E27\u0E21\u0E20\u0E32\u0E29\u0E35} IVA {\u0E23\u0E27\u0E21 IVA} SST {\u0E23\u0E27\u0E21 SST} KDV {\u0E23\u0E27\u0E21 KDV} other {}}",alternativePriceAriaLabel:"\u0E23\u0E32\u0E04\u0E32\u0E1E\u0E34\u0E40\u0E28\u0E29 {alternativePrice}",strikethroughAriaLabel:"\u0E23\u0E32\u0E04\u0E32\u0E1B\u0E01\u0E15\u0E34 {strikethroughPrice}"},{lang:"el",recurrenceLabel:"{recurrenceTerm, select, MONTH {/\u03BC\u03AE\u03BD\u03B1} YEAR {/\u03AD\u03C4\u03BF\u03C2} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {\u03BA\u03AC\u03B8\u03B5 \u03BC\u03AE\u03BD\u03B1} YEAR {\u03B1\u03BD\u03AC \u03AD\u03C4\u03BF\u03C2} other {}}",perUnitLabel:"{perUnit, select, LICENSE {\u03B1\u03BD\u03AC \u03AC\u03B4\u03B5\u03B9\u03B1 \u03C7\u03C1\u03AE\u03C3\u03B7\u03C2} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {\u03B1\u03BD\u03AC \u03AC\u03B4\u03B5\u03B9\u03B1 \u03C7\u03C1\u03AE\u03C3\u03B7\u03C2} other {}}",freeLabel:"\u0394\u03C9\u03C1\u03B5\u03AC\u03BD",freeAriaLabel:"\u0394\u03C9\u03C1\u03B5\u03AC\u03BD",taxExclusiveLabel:"{taxTerm, select, GST {(\u03BC\u03B7 \u03C3\u03C5\u03BC\u03C0\u03B5\u03C1\u03B9\u03BB\u03B1\u03BC\u03B2\u03B1\u03BD\u03BF\u03BC\u03AD\u03BD\u03BF\u03C5 GST)} VAT {(\u03BC\u03B7 \u03C3\u03C5\u03BC\u03C0\u03B5\u03C1\u03B9\u03BB\u03B1\u03BC\u03B2\u03B1\u03BD\u03BF\u03BC\u03AD\u03BD\u03BF\u03C5 \u03A6\u03A0\u0391)} TAX {(\u03BC\u03B7 \u03C3\u03C5\u03BC\u03C0\u03B5\u03C1\u03B9\u03BB\u03B1\u03BC\u03B2\u03B1\u03BD\u03BF\u03BC\u03AD\u03BD\u03BF\u03C5 \u03C6\u03CC\u03C1\u03BF)} IVA {(\u03BC\u03B7 \u03C3\u03C5\u03BC\u03C0\u03B5\u03C1\u03B9\u03BB\u03B1\u03BC\u03B2\u03B1\u03BD\u03BF\u03BC\u03AD\u03BD\u03BF\u03C5 IVA)} SST {(\u03BC\u03B7 \u03C3\u03C5\u03BC\u03C0\u03B5\u03C1\u03B9\u03BB\u03B1\u03BC\u03B2\u03B1\u03BD\u03BF\u03BC\u03AD\u03BD\u03BF\u03C5 SST)} KDV {(\u03BC\u03B7 \u03C3\u03C5\u03BC\u03C0\u03B5\u03C1\u03B9\u03BB\u03B1\u03BC\u03B2\u03B1\u03BD\u03BF\u03BC\u03AD\u03BD\u03BF\u03C5 KDV)} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {(\u03C3\u03C5\u03BC\u03C0\u03B5\u03C1\u03B9\u03BB\u03B1\u03BC\u03B2\u03B1\u03BD\u03BF\u03BC\u03AD\u03BD\u03BF\u03C5 \u03C4\u03BF\u03C5 GST)} VAT {(\u03C3\u03C5\u03BC\u03C0\u03B5\u03C1\u03B9\u03BB\u03B1\u03BC\u03B2\u03B1\u03BD\u03BF\u03BC\u03AD\u03BD\u03BF\u03C5 \u03A6\u03A0\u0391)} TAX {(\u03C3\u03C5\u03BC\u03C0\u03B5\u03C1\u03B9\u03BB\u03B1\u03BC\u03B2\u03B1\u03BD\u03BF\u03BC\u03AD\u03BD\u03BF\u03C5 \u03C4\u03BF\u03C5 \u03C6\u03CC\u03C1\u03BF\u03C5)} IVA {(\u03C3\u03C5\u03BC\u03C0\u03B5\u03C1\u03B9\u03BB\u03B1\u03BC\u03B2\u03B1\u03BD\u03BF\u03BC\u03AD\u03BD\u03BF\u03C5 \u03C4\u03BF\u03C5 IVA)} SST {(\u03C3\u03C5\u03BC\u03C0\u03B5\u03C1\u03B9\u03BB\u03B1\u03BC\u03B2\u03B1\u03BD\u03BF\u03BC\u03AD\u03BD\u03BF\u03C5 \u03C4\u03BF\u03C5 SST)} KDV {(\u03C3\u03C5\u03BC\u03C0\u03B5\u03C1\u03B9\u03BB\u03B1\u03BC\u03B2\u03B1\u03BD\u03BF\u03BC\u03AD\u03BD\u03BF\u03C5 \u03C4\u03BF\u03C5 KDV)} other {}}",alternativePriceAriaLabel:"\u0394\u03B9\u03B1\u03C6\u03BF\u03C1\u03B5\u03C4\u03B9\u03BA\u03AC, {alternativePrice}",strikethroughAriaLabel:"\u039A\u03B1\u03BD\u03BF\u03BD\u03B9\u03BA\u03AE \u03C4\u03B9\u03BC\u03AE {strikethroughPrice}"},{lang:"fil",recurrenceLabel:"{recurrenceTerm, select, MONTH {/buwan} YEAR {/taon} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {per buwan} YEAR {per taon} other {}}",perUnitLabel:"{perUnit, select, LICENSE {kada lisensya} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {kada lisensya} other {}}",freeLabel:"Libre",freeAriaLabel:"Libre",taxExclusiveLabel:"{taxTerm, select, GST {hindi kasama ang GST} VAT {hindi kasama ang VAT} TAX {hindi kasama ang Buwis} IVA {hindi kasama ang IVA} SST {hindi kasama ang SST} KDV {hindi kasama ang KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {kasama ang GST} VAT {kasama ang VAT} TAX {kasama ang Buwis} IVA {kasama ang IVA} SST {kasama ang SST} KDV {kasama ang KDV} other {}}",alternativePriceAriaLabel:"Alternatibong nasa halagang {alternativePrice}",strikethroughAriaLabel:"Regular na nasa halagang {strikethroughPrice}"},{lang:"ms",recurrenceLabel:"{recurrenceTerm, select, MONTH {/bulan} YEAR {/tahun} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {per bulan} YEAR {per tahun} other {}}",perUnitLabel:"{perUnit, select, LICENSE {setiap lesen} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {setiap lesen} other {}}",freeLabel:"Percuma",freeAriaLabel:"Percuma",taxExclusiveLabel:"{taxTerm, select, GST {kecuali GST} VAT {kecuali VAT} TAX {kecuali Cukai} IVA {kecuali IVA} SST {kecuali SST} KDV {kecuali KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {termasuk GST} VAT {termasuk VAT} TAX {termasuk Cukai} IVA {termasuk IVA} SST {termasuk SST} KDV {termasuk KDV} other {}}",alternativePriceAriaLabel:"Secara alternatif pada {alternativePrice}",strikethroughAriaLabel:"Biasanya pada {strikethroughPrice}"},{lang:"hi",recurrenceLabel:"{recurrenceTerm, select, MONTH {/\u092E\u093E\u0939} YEAR {/\u0935\u0930\u094D\u0937} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {per \u092E\u093E\u0939} YEAR {per \u0935\u0930\u094D\u0937} other {}}",perUnitLabel:"{perUnit, select, LICENSE {\u092A\u094D\u0930\u0924\u093F \u0932\u093E\u0907\u0938\u0947\u0902\u0938} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {\u092A\u094D\u0930\u0924\u093F \u0932\u093E\u0907\u0938\u0947\u0902\u0938} other {}}",freeLabel:"\u092B\u093C\u094D\u0930\u0940",freeAriaLabel:"\u092B\u093C\u094D\u0930\u0940",taxExclusiveLabel:"{taxTerm, select, GST {GST \u0905\u0924\u093F\u0930\u093F\u0915\u094D\u0924} VAT {VAT \u0905\u0924\u093F\u0930\u093F\u0915\u094D\u0924} TAX {\u0915\u0930 \u0905\u0924\u093F\u0930\u093F\u0915\u094D\u0924} IVA {IVA \u0905\u0924\u093F\u0930\u093F\u0915\u094D\u0924} SST {SST \u0905\u0924\u093F\u0930\u093F\u0915\u094D\u0924} KDV {KDV \u0905\u0924\u093F\u0930\u093F\u0915\u094D\u0924} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {GST \u0938\u0939\u093F\u0924} VAT {VAT \u0938\u0939\u093F\u0924} TAX {\u0915\u0930 \u0938\u0939\u093F\u0924} IVA {IVA \u0938\u0939\u093F\u0924} SST {SST \u0938\u0939\u093F\u0924} KDV {KDV \u0938\u0939\u093F\u0924} other {}}",alternativePriceAriaLabel:"\u0935\u0948\u0915\u0932\u094D\u092A\u093F\u0915 \u0930\u0942\u092A \u0938\u0947 \u0907\u0938 \u092A\u0930 {alternativePrice}",strikethroughAriaLabel:"\u0928\u093F\u092F\u092E\u093F\u0924 \u0930\u0942\u092A \u0938\u0947 \u0907\u0938 \u092A\u0930 {strikethroughPrice}"},{lang:"iw",recurrenceLabel:"{recurrenceTerm, select, MONTH {/\u05D7\u05D5\u05D3\u05E9} YEAR {/\u05E9\u05E0\u05D4} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {\u05DC\u05D7\u05D5\u05D3\u05E9} YEAR {\u05DC\u05E9\u05E0\u05D4} other {}}",perUnitLabel:"{perUnit, select, LICENSE {\u05DC\u05E8\u05D9\u05E9\u05D9\u05D5\u05DF} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {\u05DC\u05E8\u05D9\u05E9\u05D9\u05D5\u05DF} other {}}",freeLabel:"\u05D7\u05D9\u05E0\u05DD",freeAriaLabel:"\u05D7\u05D9\u05E0\u05DD",taxExclusiveLabel:'{taxTerm, select, GST {\u05DC\u05DC\u05D0 GST} VAT {\u05DC\u05DC\u05D0 \u05DE\u05E2"\u05DE} TAX {\u05DC\u05DC\u05D0 \u05DE\u05E1} IVA {\u05DC\u05DC\u05D0 IVA} SST {\u05DC\u05DC\u05D0 SST} KDV {\u05DC\u05DC\u05D0 KDV} other {}}',taxInclusiveLabel:'{taxTerm, select, GST {\u05DB\u05D5\u05DC\u05DC GST} VAT {\u05DB\u05D5\u05DC\u05DC \u05DE\u05E2"\u05DE} TAX {\u05DB\u05D5\u05DC\u05DC \u05DE\u05E1} IVA {\u05DB\u05D5\u05DC\u05DC IVA} SST {\u05DB\u05D5\u05DC\u05DC SST} KDV {\u05DB\u05D5\u05DC\u05DC KDV} other {}}',alternativePriceAriaLabel:"\u05DC\u05D7\u05DC\u05D5\u05E4\u05D9\u05DF \u05D1-{alternativePrice}",strikethroughAriaLabel:"\u05D1\u05D0\u05D5\u05E4\u05DF \u05E7\u05D1\u05D5\u05E2 \u05D1-{strikethroughPrice}"}],":type":"sheet"}});var Ie;(function(e){e.STAGE="STAGE",e.PRODUCTION="PRODUCTION",e.LOCAL="LOCAL"})(Ie||(Ie={}));var Nt;(function(e){e.STAGE="STAGE",e.PRODUCTION="PROD",e.LOCAL="LOCAL"})(Nt||(Nt={}));var Ne;(function(e){e.DRAFT="DRAFT",e.PUBLISHED="PUBLISHED"})(Ne||(Ne={}));var se;(function(e){e.V2="UCv2",e.V3="UCv3"})(se||(se={}));var H;(function(e){e.CHECKOUT="checkout",e.CHECKOUT_EMAIL="checkout/email",e.SEGMENTATION="segmentation",e.BUNDLE="bundle",e.COMMITMENT="commitment",e.RECOMMENDATION="recommendation",e.EMAIL="email",e.PAYMENT="payment",e.CHANGE_PLAN_TEAM_PLANS="change-plan/team-upgrade/plans",e.CHANGE_PLAN_TEAM_PAYMENT="change-plan/team-upgrade/payment"})(H||(H={}));var Vt=function(e){var t;return(t=li.get(e))!==null&&t!==void 0?t:e},li=new Map([["countrySpecific","cs"],["quantity","q"],["authCode","code"],["checkoutPromoCode","apc"],["rurl","rUrl"],["curl","cUrl"],["ctxrturl","ctxRtUrl"],["country","co"],["language","lang"],["clientId","cli"],["context","ctx"],["productArrangementCode","pa"],["offerType","ot"],["marketSegment","ms"]]);var Rr=function(e){var t=typeof Symbol=="function"&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&typeof e.length=="number")return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},Dr=function(e,t){var n=typeof Symbol=="function"&&e[Symbol.iterator];if(!n)return e;var r=n.call(e),i,a=[],o;try{for(;(t===void 0||t-- >0)&&!(i=r.next()).done;)a.push(i.value)}catch(s){o={error:s}}finally{try{i&&!i.done&&(n=r.return)&&n.call(r)}finally{if(o)throw o.error}}return a};function de(e,t,n){var r,i;try{for(var a=Rr(Object.entries(e)),o=a.next();!o.done;o=a.next()){var s=Dr(o.value,2),c=s[0],u=s[1],l=Vt(c);u!=null&&n.has(l)&&t.set(l,u)}}catch(p){r={error:p}}finally{try{o&&!o.done&&(i=a.return)&&i.call(a)}finally{if(r)throw r.error}}}function Ye(e){switch(e){case Ie.PRODUCTION:return"https://commerce.adobe.com";default:return"https://commerce-stg.adobe.com"}}function Ke(e,t){var n,r;for(var i in e){var a=e[i];try{for(var o=(n=void 0,Rr(Object.entries(a))),s=o.next();!s.done;s=o.next()){var c=Dr(s.value,2),u=c[0],l=c[1];if(l!=null){var p=Vt(u);t.set("items["+i+"]["+p+"]",l)}}}catch(f){n={error:f}}finally{try{s&&!s.done&&(r=o.return)&&r.call(o)}finally{if(n)throw n.error}}}}var ci=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var i=0,r=Object.getOwnPropertySymbols(e);i=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};function Ur(e){hi(e);var t=e.env,n=e.items,r=e.workflowStep,i=ci(e,["env","items","workflowStep"]),a=new URL(Ye(t));return a.pathname=r+"/",Ke(n,a.searchParams),de(i,a.searchParams,fi),a.toString()}var fi=new Set(["cli","co","lang","ctx","cUrl","mv","nglwfdata","otac","promoid","rUrl","sdid","spint","trackingid","code","campaignid","appctxid"]),pi=["env","workflowStep","clientId","country","items"];function hi(e){var t,n;try{for(var r=ui(pi),i=r.next();!i.done;i=r.next()){var a=i.value;if(!e[a])throw new Error('Argument "checkoutData" is not valid, missing: '+a)}}catch(o){t={error:o}}finally{try{i&&!i.done&&(n=r.return)&&n.call(r)}finally{if(t)throw t.error}}return!0}var mi=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var i=0,r=Object.getOwnPropertySymbols(e);i=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},Ai="p_draft_landscape",di="/store/";function Ct(e){Ei(e);var t=e.env,n=e.items,r=e.workflowStep,i=e.ms,a=e.marketSegment,o=e.ot,s=e.offerType,c=e.pa,u=e.productArrangementCode,l=e.landscape,p=mi(e,["env","items","workflowStep","ms","marketSegment","ot","offerType","pa","productArrangementCode","landscape"]),f={marketSegment:a??i,offerType:s??o,productArrangementCode:u??c},m=new URL(Ye(t));return m.pathname=""+di+r,r!==H.SEGMENTATION&&r!==H.CHANGE_PLAN_TEAM_PLANS&&Ke(n,m.searchParams),r===H.SEGMENTATION&&de(f,m.searchParams,Ot),de(p,m.searchParams,Ot),l===Ne.DRAFT&&de({af:Ai},m.searchParams,Ot),m.toString()}var Ot=new Set(["af","ai","apc","appctxid","cli","co","csm","ctx","ctxRtUrl","DCWATC","dp","fr","gsp","ijt","lang","lo","mal","ms","mv","mv2","nglwfdata","ot","otac","pa","pcid","promoid","q","rf","sc","scl","sdid","sid","spint","svar","th","thm","trackingid","usid","workflowid","context.guid","so.ca","so.su","so.tr","so.va"]),Si=["env","workflowStep","clientId","country"];function Ei(e){var t,n;try{for(var r=Ti(Si),i=r.next();!i.done;i=r.next()){var a=i.value;if(!e[a])throw new Error('Argument "checkoutData" is not valid, missing: '+a)}}catch(o){t={error:o}}finally{try{i&&!i.done&&(n=r.return)&&n.call(r)}finally{if(t)throw t.error}}if(e.workflowStep!==H.SEGMENTATION&&e.workflowStep!==H.CHANGE_PLAN_TEAM_PLANS&&!e.items)throw new Error('Argument "checkoutData" is not valid, missing: items');return!0}function wt(e,t){switch(e){case se.V2:return Ur(t);case se.V3:return Ct(t);default:return console.warn("Unsupported CheckoutType, will use UCv3 as default. Given type: "+e),Ct(t)}}var kt;(function(e){e.BASE="BASE",e.TRIAL="TRIAL",e.PROMOTION="PROMOTION"})(kt||(kt={}));var C;(function(e){e.MONTH="MONTH",e.YEAR="YEAR",e.TWO_YEARS="TWO_YEARS",e.THREE_YEARS="THREE_YEARS",e.PERPETUAL="PERPETUAL",e.TERM_LICENSE="TERM_LICENSE",e.ACCESS_PASS="ACCESS_PASS",e.THREE_MONTHS="THREE_MONTHS",e.SIX_MONTHS="SIX_MONTHS"})(C||(C={}));var N;(function(e){e.ANNUAL="ANNUAL",e.MONTHLY="MONTHLY",e.TWO_YEARS="TWO_YEARS",e.THREE_YEARS="THREE_YEARS",e.P1D="P1D",e.P1Y="P1Y",e.P3Y="P3Y",e.P10Y="P10Y",e.P15Y="P15Y",e.P3D="P3D",e.P7D="P7D",e.P30D="P30D",e.HALF_YEARLY="HALF_YEARLY",e.QUARTERLY="QUARTERLY"})(N||(N={}));var Rt;(function(e){e.INDIVIDUAL="INDIVIDUAL",e.TEAM="TEAM",e.ENTERPRISE="ENTERPRISE"})(Rt||(Rt={}));var Dt;(function(e){e.COM="COM",e.EDU="EDU",e.GOV="GOV"})(Dt||(Dt={}));var Ut;(function(e){e.DIRECT="DIRECT",e.INDIRECT="INDIRECT"})(Ut||(Ut={}));var Mt;(function(e){e.ENTERPRISE_PRODUCT="ENTERPRISE_PRODUCT",e.ETLA="ETLA",e.RETAIL="RETAIL",e.VIP="VIP",e.VIPMP="VIPMP",e.FREE="FREE"})(Mt||(Mt={}));var Mr="tacocat.js";var ze=(e,t)=>String(e??"").toLowerCase()==String(t??"").toLowerCase(),Gr=e=>`${e??""}`.replace(/[&<>'"]/g,t=>({"&":"&","<":"<",">":">","'":"'",'"':"""})[t]??t)??"";function V(e,t={},{metadata:n=!0,search:r=!0,storage:i=!0}={}){let a;if(r&&a==null){let o=new URLSearchParams(window.location.search),s=Se(r)?r:e;a=o.get(s)}if(i&&a==null){let o=Se(i)?i:e;a=window.sessionStorage.getItem(o)??window.localStorage.getItem(o)}if(n&&a==null){let o=xi(Se(n)?n:e);a=document.documentElement.querySelector(`meta[name="${o}"]`)?.content}return a??t[e]}var Ee=()=>{};var Hr=e=>typeof e=="boolean",re=e=>typeof e=="function",je=e=>typeof e=="number",Fr=e=>e!=null&&typeof e=="object";var Se=e=>typeof e=="string",Gt=e=>Se(e)&&e,xe=e=>je(e)&&Number.isFinite(e)&&e>0;function be(e,t=n=>n==null||n===""){return e!=null&&Object.entries(e).forEach(([n,r])=>{t(r)&&delete e[n]}),e}function g(e,t){if(Hr(e))return e;let n=String(e);return n==="1"||n==="true"?!0:n==="0"||n==="false"?!1:t}function ne(e,t,n){let r=Object.values(t);return r.find(i=>ze(i,e))??n??r[0]}function xi(e=""){return String(e).replace(/(\p{Lowercase_Letter})(\p{Uppercase_Letter})/gu,(t,n,r)=>`${n}-${r}`).replace(/\W+/gu,"-").toLowerCase()}function ge(e,t=1){return je(e)||(e=Number.parseInt(e,10)),!Number.isNaN(e)&&e>0&&Number.isFinite(e)?e:t}var bi=Date.now(),Ht=()=>`(+${Date.now()-bi}ms)`,We=new Set,gi=g(V("tacocat.debug",{},{metadata:!1}),typeof process<"u"&&process.env?.DEBUG);function Xr(e){let t=`[${Mr}/${e}]`,n=(o,s,...c)=>o?!0:(i(s,...c),!1),r=gi?(o,...s)=>{console.debug(`${t} ${o}`,...s,Ht())}:()=>{},i=(o,...s)=>{let c=`${t} ${o}`;We.forEach(([u])=>u(c,...s))};return{assert:n,debug:r,error:i,warn:(o,...s)=>{let c=`${t} ${o}`;We.forEach(([,u])=>u(c,...s))}}}function Li(e,t){let n=[e,t];return We.add(n),()=>{We.delete(n)}}Li((e,...t)=>{console.error(e,...t,Ht())},(e,...t)=>{console.warn(e,...t,Ht())});var vi="no promo",Yr="promo-tag",yi="yellow",Pi="neutral",_i=(e,t,n)=>{let r=a=>a||vi,i=n?` (was "${r(t)}")`:"";return`${r(e)}${i}`},Ii="cancel-context",Ve=(e,t)=>{let n=e===Ii,r=!n&&e?.length>0,i=(r||n)&&(t&&t!=e||!t&&!n),a=i&&r||!i&&!!t,o=a?e||t:void 0;return{effectivePromoCode:o,overridenPromoCode:e,className:a?Yr:`${Yr} no-promo`,text:_i(o,t,i),variant:a?yi:Pi,isOverriden:i}};var Ft="ABM",Xt="PUF",Yt="M2M",Kt="PERPETUAL",zt="P3Y",Ni="TAX_INCLUSIVE_DETAILS",Vi="TAX_EXCLUSIVE",Kr={ABM:Ft,PUF:Xt,M2M:Yt,PERPETUAL:Kt,P3Y:zt},ko={[Ft]:{commitment:C.YEAR,term:N.MONTHLY},[Xt]:{commitment:C.YEAR,term:N.ANNUAL},[Yt]:{commitment:C.MONTH,term:N.MONTHLY},[Kt]:{commitment:C.PERPETUAL,term:void 0},[zt]:{commitment:C.THREE_MONTHS,term:N.P3Y}},zr="Value is not an offer",Be=e=>{if(typeof e!="object")return zr;let{commitment:t,term:n}=e,r=Oi(t,n);return{...e,planType:r}};var Oi=(e,t)=>{switch(e){case void 0:return zr;case"":return"";case C.YEAR:return t===N.MONTHLY?Ft:t===N.ANNUAL?Xt:"";case C.MONTH:return t===N.MONTHLY?Yt:"";case C.PERPETUAL:return Kt;case C.TERM_LICENSE:return t===N.P3Y?zt:"";default:return""}};function jt(e){let{priceDetails:t}=e,{price:n,priceWithoutDiscount:r,priceWithoutTax:i,priceWithoutDiscountAndTax:a,taxDisplay:o}=t;if(o!==Ni)return e;let s={...e,priceDetails:{...t,price:i??n,priceWithoutDiscount:a??r,taxDisplay:Vi}};return s.offerType==="TRIAL"&&s.priceDetails.price===0&&(s.priceDetails.price=s.priceDetails.priceWithoutDiscount),s}var Wt=function(e,t){return Wt=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(n,r){n.__proto__=r}||function(n,r){for(var i in r)Object.prototype.hasOwnProperty.call(r,i)&&(n[i]=r[i])},Wt(e,t)};function Oe(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");Wt(e,t);function n(){this.constructor=e}e.prototype=t===null?Object.create(t):(n.prototype=t.prototype,new n)}var E=function(){return E=Object.assign||function(t){for(var n,r=1,i=arguments.length;r0}),n=[],r=0,i=t;r1)throw new RangeError("integer-width stems only accept a single optional option");i.options[0].replace(ki,function(s,c,u,l,p,f){if(c)t.minimumIntegerDigits=u.length;else{if(l&&p)throw new Error("We currently do not support maximum integer digits");if(f)throw new Error("We currently do not support exact integer digits")}return""});continue}if(tn.test(i.stem)){t.minimumIntegerDigits=i.stem.length;continue}if(qr.test(i.stem)){if(i.options.length>1)throw new RangeError("Fraction-precision stems only accept a single optional option");i.stem.replace(qr,function(s,c,u,l,p,f){return u==="*"?t.minimumFractionDigits=c.length:l&&l[0]==="#"?t.maximumFractionDigits=l.length:p&&f?(t.minimumFractionDigits=p.length,t.maximumFractionDigits=p.length+f.length):(t.minimumFractionDigits=c.length,t.maximumFractionDigits=c.length),""}),i.options.length&&(t=E(E({},t),Zr(i.options[0])));continue}if(en.test(i.stem)){t=E(E({},t),Zr(i.stem));continue}var a=rn(i.stem);a&&(t=E(E({},t),a));var o=Ri(i.stem);o&&(t=E(E({},t),o))}return t}var qt,Di=new RegExp("^"+$t.source+"*"),Ui=new RegExp($t.source+"*$");function d(e,t){return{start:e,end:t}}var Mi=!!String.prototype.startsWith,Gi=!!String.fromCodePoint,Hi=!!Object.fromEntries,Fi=!!String.prototype.codePointAt,Xi=!!String.prototype.trimStart,Yi=!!String.prototype.trimEnd,Ki=!!Number.isSafeInteger,zi=Ki?Number.isSafeInteger:function(e){return typeof e=="number"&&isFinite(e)&&Math.floor(e)===e&&Math.abs(e)<=9007199254740991},Jt=!0;try{an=cn("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu"),Jt=((qt=an.exec("a"))===null||qt===void 0?void 0:qt[0])==="a"}catch{Jt=!1}var an,on=Mi?function(t,n,r){return t.startsWith(n,r)}:function(t,n,r){return t.slice(r,r+n.length)===n},Qt=Gi?String.fromCodePoint:function(){for(var t=[],n=0;na;){if(o=t[a++],o>1114111)throw RangeError(o+" is not a valid code point");r+=o<65536?String.fromCharCode(o):String.fromCharCode(((o-=65536)>>10)+55296,o%1024+56320)}return r},sn=Hi?Object.fromEntries:function(t){for(var n={},r=0,i=t;r=r)){var i=t.charCodeAt(n),a;return i<55296||i>56319||n+1===r||(a=t.charCodeAt(n+1))<56320||a>57343?i:(i-55296<<10)+(a-56320)+65536}},ji=Xi?function(t){return t.trimStart()}:function(t){return t.replace(Di,"")},Wi=Yi?function(t){return t.trimEnd()}:function(t){return t.replace(Ui,"")};function cn(e,t){return new RegExp(e,t)}var er;Jt?(Zt=cn("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu"),er=function(t,n){var r;Zt.lastIndex=n;var i=Zt.exec(t);return(r=i[1])!==null&&r!==void 0?r:""}):er=function(t,n){for(var r=[];;){var i=ln(t,n);if(i===void 0||fn(i)||qi(i))break;r.push(i),n+=i>=65536?2:1}return Qt.apply(void 0,r)};var Zt,un=function(){function e(t,n){n===void 0&&(n={}),this.message=t,this.position={offset:0,line:1,column:1},this.ignoreTag=!!n.ignoreTag,this.requiresOtherClause=!!n.requiresOtherClause,this.shouldParseSkeletons=!!n.shouldParseSkeletons}return e.prototype.parse=function(){if(this.offset()!==0)throw Error("parser can only be used once");return this.parseMessage(0,"",!1)},e.prototype.parseMessage=function(t,n,r){for(var i=[];!this.isEOF();){var a=this.char();if(a===123){var o=this.parseArgument(t,r);if(o.err)return o;i.push(o.val)}else{if(a===125&&t>0)break;if(a===35&&(n==="plural"||n==="selectordinal")){var s=this.clonePosition();this.bump(),i.push({type:y.pound,location:d(s,this.clonePosition())})}else if(a===60&&!this.ignoreTag&&this.peek()===47){if(r)break;return this.error(A.UNMATCHED_CLOSING_TAG,d(this.clonePosition(),this.clonePosition()))}else if(a===60&&!this.ignoreTag&&tr(this.peek()||0)){var o=this.parseTag(t,n);if(o.err)return o;i.push(o.val)}else{var o=this.parseLiteral(t,n);if(o.err)return o;i.push(o.val)}}}return{val:i,err:null}},e.prototype.parseTag=function(t,n){var r=this.clonePosition();this.bump();var i=this.parseTagName();if(this.bumpSpace(),this.bumpIf("/>"))return{val:{type:y.literal,value:"<"+i+"/>",location:d(r,this.clonePosition())},err:null};if(this.bumpIf(">")){var a=this.parseMessage(t+1,n,!0);if(a.err)return a;var o=a.val,s=this.clonePosition();if(this.bumpIf("")?{val:{type:y.tag,value:i,children:o,location:d(r,this.clonePosition())},err:null}:this.error(A.INVALID_TAG,d(s,this.clonePosition())))}else return this.error(A.UNCLOSED_TAG,d(r,this.clonePosition()))}else return this.error(A.INVALID_TAG,d(r,this.clonePosition()))},e.prototype.parseTagName=function(){var t=this.offset();for(this.bump();!this.isEOF()&&$i(this.char());)this.bump();return this.message.slice(t,this.offset())},e.prototype.parseLiteral=function(t,n){for(var r=this.clonePosition(),i="";;){var a=this.tryParseQuote(n);if(a){i+=a;continue}var o=this.tryParseUnquoted(t,n);if(o){i+=o;continue}var s=this.tryParseLeftAngleBracket();if(s){i+=s;continue}break}var c=d(r,this.clonePosition());return{val:{type:y.literal,value:i,location:c},err:null}},e.prototype.tryParseLeftAngleBracket=function(){return!this.isEOF()&&this.char()===60&&(this.ignoreTag||!Bi(this.peek()||0))?(this.bump(),"<"):null},e.prototype.tryParseQuote=function(t){if(this.isEOF()||this.char()!==39)return null;switch(this.peek()){case 39:return this.bump(),this.bump(),"'";case 123:case 60:case 62:case 125:break;case 35:if(t==="plural"||t==="selectordinal")break;return null;default:return null}this.bump();var n=[this.char()];for(this.bump();!this.isEOF();){var r=this.char();if(r===39)if(this.peek()===39)n.push(39),this.bump();else{this.bump();break}else n.push(r);this.bump()}return Qt.apply(void 0,n)},e.prototype.tryParseUnquoted=function(t,n){if(this.isEOF())return null;var r=this.char();return r===60||r===123||r===35&&(n==="plural"||n==="selectordinal")||r===125&&t>0?null:(this.bump(),Qt(r))},e.prototype.parseArgument=function(t,n){var r=this.clonePosition();if(this.bump(),this.bumpSpace(),this.isEOF())return this.error(A.EXPECT_ARGUMENT_CLOSING_BRACE,d(r,this.clonePosition()));if(this.char()===125)return this.bump(),this.error(A.EMPTY_ARGUMENT,d(r,this.clonePosition()));var i=this.parseIdentifierIfPossible().value;if(!i)return this.error(A.MALFORMED_ARGUMENT,d(r,this.clonePosition()));if(this.bumpSpace(),this.isEOF())return this.error(A.EXPECT_ARGUMENT_CLOSING_BRACE,d(r,this.clonePosition()));switch(this.char()){case 125:return this.bump(),{val:{type:y.argument,value:i,location:d(r,this.clonePosition())},err:null};case 44:return this.bump(),this.bumpSpace(),this.isEOF()?this.error(A.EXPECT_ARGUMENT_CLOSING_BRACE,d(r,this.clonePosition())):this.parseArgumentOptions(t,n,i,r);default:return this.error(A.MALFORMED_ARGUMENT,d(r,this.clonePosition()))}},e.prototype.parseIdentifierIfPossible=function(){var t=this.clonePosition(),n=this.offset(),r=er(this.message,n),i=n+r.length;this.bumpTo(i);var a=this.clonePosition(),o=d(t,a);return{value:r,location:o}},e.prototype.parseArgumentOptions=function(t,n,r,i){var a,o=this.clonePosition(),s=this.parseIdentifierIfPossible().value,c=this.clonePosition();switch(s){case"":return this.error(A.EXPECT_ARGUMENT_TYPE,d(o,c));case"number":case"date":case"time":{this.bumpSpace();var u=null;if(this.bumpIf(",")){this.bumpSpace();var l=this.clonePosition(),p=this.parseSimpleArgStyleIfPossible();if(p.err)return p;var f=Wi(p.val);if(f.length===0)return this.error(A.EXPECT_ARGUMENT_STYLE,d(this.clonePosition(),this.clonePosition()));var m=d(l,this.clonePosition());u={style:f,styleLocation:m}}var T=this.tryParseArgumentClose(i);if(T.err)return T;var x=d(i,this.clonePosition());if(u&&on(u?.style,"::",0)){var b=ji(u.style.slice(2));if(s==="number"){var p=this.parseNumberSkeletonFromString(b,u.styleLocation);return p.err?p:{val:{type:y.number,value:r,location:x,style:p.val},err:null}}else{if(b.length===0)return this.error(A.EXPECT_DATE_TIME_SKELETON,x);var f={type:le.dateTime,pattern:b,location:u.styleLocation,parsedOptions:this.shouldParseSkeletons?Br(b):{}},P=s==="date"?y.date:y.time;return{val:{type:P,value:r,location:x,style:f},err:null}}}return{val:{type:s==="number"?y.number:s==="date"?y.date:y.time,value:r,location:x,style:(a=u?.style)!==null&&a!==void 0?a:null},err:null}}case"plural":case"selectordinal":case"select":{var _=this.clonePosition();if(this.bumpSpace(),!this.bumpIf(","))return this.error(A.EXPECT_SELECT_ARGUMENT_OPTIONS,d(_,E({},_)));this.bumpSpace();var L=this.parseIdentifierIfPossible(),O=0;if(s!=="select"&&L.value==="offset"){if(!this.bumpIf(":"))return this.error(A.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE,d(this.clonePosition(),this.clonePosition()));this.bumpSpace();var p=this.tryParseDecimalInteger(A.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE,A.INVALID_PLURAL_ARGUMENT_OFFSET_VALUE);if(p.err)return p;this.bumpSpace(),L=this.parseIdentifierIfPossible(),O=p.val}var v=this.tryParsePluralOrSelectOptions(t,s,n,L);if(v.err)return v;var T=this.tryParseArgumentClose(i);if(T.err)return T;var I=d(i,this.clonePosition());return s==="select"?{val:{type:y.select,value:r,options:sn(v.val),location:I},err:null}:{val:{type:y.plural,value:r,options:sn(v.val),offset:O,pluralType:s==="plural"?"cardinal":"ordinal",location:I},err:null}}default:return this.error(A.INVALID_ARGUMENT_TYPE,d(o,c))}},e.prototype.tryParseArgumentClose=function(t){return this.isEOF()||this.char()!==125?this.error(A.EXPECT_ARGUMENT_CLOSING_BRACE,d(t,this.clonePosition())):(this.bump(),{val:!0,err:null})},e.prototype.parseSimpleArgStyleIfPossible=function(){for(var t=0,n=this.clonePosition();!this.isEOF();){var r=this.char();switch(r){case 39:{this.bump();var i=this.clonePosition();if(!this.bumpUntil("'"))return this.error(A.UNCLOSED_QUOTE_IN_ARGUMENT_STYLE,d(i,this.clonePosition()));this.bump();break}case 123:{t+=1,this.bump();break}case 125:{if(t>0)t-=1;else return{val:this.message.slice(n.offset,this.offset()),err:null};break}default:this.bump();break}}return{val:this.message.slice(n.offset,this.offset()),err:null}},e.prototype.parseNumberSkeletonFromString=function(t,n){var r=[];try{r=Qr(t)}catch{return this.error(A.INVALID_NUMBER_SKELETON,n)}return{val:{type:le.number,tokens:r,location:n,parsedOptions:this.shouldParseSkeletons?nn(r):{}},err:null}},e.prototype.tryParsePluralOrSelectOptions=function(t,n,r,i){for(var a,o=!1,s=[],c=new Set,u=i.value,l=i.location;;){if(u.length===0){var p=this.clonePosition();if(n!=="select"&&this.bumpIf("=")){var f=this.tryParseDecimalInteger(A.EXPECT_PLURAL_ARGUMENT_SELECTOR,A.INVALID_PLURAL_ARGUMENT_SELECTOR);if(f.err)return f;l=d(p,this.clonePosition()),u=this.message.slice(p.offset,this.offset())}else break}if(c.has(u))return this.error(n==="select"?A.DUPLICATE_SELECT_ARGUMENT_SELECTOR:A.DUPLICATE_PLURAL_ARGUMENT_SELECTOR,l);u==="other"&&(o=!0),this.bumpSpace();var m=this.clonePosition();if(!this.bumpIf("{"))return this.error(n==="select"?A.EXPECT_SELECT_ARGUMENT_SELECTOR_FRAGMENT:A.EXPECT_PLURAL_ARGUMENT_SELECTOR_FRAGMENT,d(this.clonePosition(),this.clonePosition()));var T=this.parseMessage(t+1,n,r);if(T.err)return T;var x=this.tryParseArgumentClose(m);if(x.err)return x;s.push([u,{value:T.val,location:d(m,this.clonePosition())}]),c.add(u),this.bumpSpace(),a=this.parseIdentifierIfPossible(),u=a.value,l=a.location}return s.length===0?this.error(n==="select"?A.EXPECT_SELECT_ARGUMENT_SELECTOR:A.EXPECT_PLURAL_ARGUMENT_SELECTOR,d(this.clonePosition(),this.clonePosition())):this.requiresOtherClause&&!o?this.error(A.MISSING_OTHER_CLAUSE,d(this.clonePosition(),this.clonePosition())):{val:s,err:null}},e.prototype.tryParseDecimalInteger=function(t,n){var r=1,i=this.clonePosition();this.bumpIf("+")||this.bumpIf("-")&&(r=-1);for(var a=!1,o=0;!this.isEOF();){var s=this.char();if(s>=48&&s<=57)a=!0,o=o*10+(s-48),this.bump();else break}var c=d(i,this.clonePosition());return a?(o*=r,zi(o)?{val:o,err:null}:this.error(n,c)):this.error(t,c)},e.prototype.offset=function(){return this.position.offset},e.prototype.isEOF=function(){return this.offset()===this.message.length},e.prototype.clonePosition=function(){return{offset:this.position.offset,line:this.position.line,column:this.position.column}},e.prototype.char=function(){var t=this.position.offset;if(t>=this.message.length)throw Error("out of bound");var n=ln(this.message,t);if(n===void 0)throw Error("Offset "+t+" is at invalid UTF-16 code unit boundary");return n},e.prototype.error=function(t,n){return{val:null,err:{kind:t,message:this.message,location:n}}},e.prototype.bump=function(){if(!this.isEOF()){var t=this.char();t===10?(this.position.line+=1,this.position.column=1,this.position.offset+=1):(this.position.column+=1,this.position.offset+=t<65536?1:2)}},e.prototype.bumpIf=function(t){if(on(this.message,t,this.offset())){for(var n=0;n=0?(this.bumpTo(r),!0):(this.bumpTo(this.message.length),!1)},e.prototype.bumpTo=function(t){if(this.offset()>t)throw Error("targetOffset "+t+" must be greater than or equal to the current offset "+this.offset());for(t=Math.min(t,this.message.length);;){var n=this.offset();if(n===t)break;if(n>t)throw Error("targetOffset "+t+" is at invalid UTF-16 code unit boundary");if(this.bump(),this.isEOF())break}},e.prototype.bumpSpace=function(){for(;!this.isEOF()&&fn(this.char());)this.bump()},e.prototype.peek=function(){if(this.isEOF())return null;var t=this.char(),n=this.offset(),r=this.message.charCodeAt(n+(t>=65536?2:1));return r??null},e}();function tr(e){return e>=97&&e<=122||e>=65&&e<=90}function Bi(e){return tr(e)||e===47}function $i(e){return e===45||e===46||e>=48&&e<=57||e===95||e>=97&&e<=122||e>=65&&e<=90||e==183||e>=192&&e<=214||e>=216&&e<=246||e>=248&&e<=893||e>=895&&e<=8191||e>=8204&&e<=8205||e>=8255&&e<=8256||e>=8304&&e<=8591||e>=11264&&e<=12271||e>=12289&&e<=55295||e>=63744&&e<=64975||e>=65008&&e<=65533||e>=65536&&e<=983039}function fn(e){return e>=9&&e<=13||e===32||e===133||e>=8206&&e<=8207||e===8232||e===8233}function qi(e){return e>=33&&e<=35||e===36||e>=37&&e<=39||e===40||e===41||e===42||e===43||e===44||e===45||e>=46&&e<=47||e>=58&&e<=59||e>=60&&e<=62||e>=63&&e<=64||e===91||e===92||e===93||e===94||e===96||e===123||e===124||e===125||e===126||e===161||e>=162&&e<=165||e===166||e===167||e===169||e===171||e===172||e===174||e===176||e===177||e===182||e===187||e===191||e===215||e===247||e>=8208&&e<=8213||e>=8214&&e<=8215||e===8216||e===8217||e===8218||e>=8219&&e<=8220||e===8221||e===8222||e===8223||e>=8224&&e<=8231||e>=8240&&e<=8248||e===8249||e===8250||e>=8251&&e<=8254||e>=8257&&e<=8259||e===8260||e===8261||e===8262||e>=8263&&e<=8273||e===8274||e===8275||e>=8277&&e<=8286||e>=8592&&e<=8596||e>=8597&&e<=8601||e>=8602&&e<=8603||e>=8604&&e<=8607||e===8608||e>=8609&&e<=8610||e===8611||e>=8612&&e<=8613||e===8614||e>=8615&&e<=8621||e===8622||e>=8623&&e<=8653||e>=8654&&e<=8655||e>=8656&&e<=8657||e===8658||e===8659||e===8660||e>=8661&&e<=8691||e>=8692&&e<=8959||e>=8960&&e<=8967||e===8968||e===8969||e===8970||e===8971||e>=8972&&e<=8991||e>=8992&&e<=8993||e>=8994&&e<=9e3||e===9001||e===9002||e>=9003&&e<=9083||e===9084||e>=9085&&e<=9114||e>=9115&&e<=9139||e>=9140&&e<=9179||e>=9180&&e<=9185||e>=9186&&e<=9254||e>=9255&&e<=9279||e>=9280&&e<=9290||e>=9291&&e<=9311||e>=9472&&e<=9654||e===9655||e>=9656&&e<=9664||e===9665||e>=9666&&e<=9719||e>=9720&&e<=9727||e>=9728&&e<=9838||e===9839||e>=9840&&e<=10087||e===10088||e===10089||e===10090||e===10091||e===10092||e===10093||e===10094||e===10095||e===10096||e===10097||e===10098||e===10099||e===10100||e===10101||e>=10132&&e<=10175||e>=10176&&e<=10180||e===10181||e===10182||e>=10183&&e<=10213||e===10214||e===10215||e===10216||e===10217||e===10218||e===10219||e===10220||e===10221||e===10222||e===10223||e>=10224&&e<=10239||e>=10240&&e<=10495||e>=10496&&e<=10626||e===10627||e===10628||e===10629||e===10630||e===10631||e===10632||e===10633||e===10634||e===10635||e===10636||e===10637||e===10638||e===10639||e===10640||e===10641||e===10642||e===10643||e===10644||e===10645||e===10646||e===10647||e===10648||e>=10649&&e<=10711||e===10712||e===10713||e===10714||e===10715||e>=10716&&e<=10747||e===10748||e===10749||e>=10750&&e<=11007||e>=11008&&e<=11055||e>=11056&&e<=11076||e>=11077&&e<=11078||e>=11079&&e<=11084||e>=11085&&e<=11123||e>=11124&&e<=11125||e>=11126&&e<=11157||e===11158||e>=11159&&e<=11263||e>=11776&&e<=11777||e===11778||e===11779||e===11780||e===11781||e>=11782&&e<=11784||e===11785||e===11786||e===11787||e===11788||e===11789||e>=11790&&e<=11798||e===11799||e>=11800&&e<=11801||e===11802||e===11803||e===11804||e===11805||e>=11806&&e<=11807||e===11808||e===11809||e===11810||e===11811||e===11812||e===11813||e===11814||e===11815||e===11816||e===11817||e>=11818&&e<=11822||e===11823||e>=11824&&e<=11833||e>=11834&&e<=11835||e>=11836&&e<=11839||e===11840||e===11841||e===11842||e>=11843&&e<=11855||e>=11856&&e<=11857||e===11858||e>=11859&&e<=11903||e>=12289&&e<=12291||e===12296||e===12297||e===12298||e===12299||e===12300||e===12301||e===12302||e===12303||e===12304||e===12305||e>=12306&&e<=12307||e===12308||e===12309||e===12310||e===12311||e===12312||e===12313||e===12314||e===12315||e===12316||e===12317||e>=12318&&e<=12319||e===12320||e===12336||e===64830||e===64831||e>=65093&&e<=65094}function rr(e){e.forEach(function(t){if(delete t.location,Qe(t)||et(t))for(var n in t.options)delete t.options[n].location,rr(t.options[n].value);else qe(t)&&rt(t.style)||(Ze(t)||Je(t))&&Ce(t.style)?delete t.style.location:tt(t)&&rr(t.children)})}function pn(e,t){t===void 0&&(t={}),t=E({shouldParseSkeletons:!0,requiresOtherClause:!0},t);var n=new un(e,t).parse();if(n.err){var r=SyntaxError(A[n.err.kind]);throw r.location=n.err.location,r.originalMessage=n.err.message,r}return t?.captureLocation||rr(n.val),n.val}function we(e,t){var n=t&&t.cache?t.cache:ra,r=t&&t.serializer?t.serializer:ta,i=t&&t.strategy?t.strategy:Ji;return i(e,{cache:n,serializer:r})}function Zi(e){return e==null||typeof e=="number"||typeof e=="boolean"}function hn(e,t,n,r){var i=Zi(r)?r:n(r),a=t.get(i);return typeof a>"u"&&(a=e.call(this,r),t.set(i,a)),a}function mn(e,t,n){var r=Array.prototype.slice.call(arguments,3),i=n(r),a=t.get(i);return typeof a>"u"&&(a=e.apply(this,r),t.set(i,a)),a}function nr(e,t,n,r,i){return n.bind(t,e,r,i)}function Ji(e,t){var n=e.length===1?hn:mn;return nr(e,this,n,t.cache.create(),t.serializer)}function Qi(e,t){return nr(e,this,mn,t.cache.create(),t.serializer)}function ea(e,t){return nr(e,this,hn,t.cache.create(),t.serializer)}var ta=function(){return JSON.stringify(arguments)};function ir(){this.cache=Object.create(null)}ir.prototype.get=function(e){return this.cache[e]};ir.prototype.set=function(e,t){this.cache[e]=t};var ra={create:function(){return new ir}},nt={variadic:Qi,monadic:ea};var ce;(function(e){e.MISSING_VALUE="MISSING_VALUE",e.INVALID_VALUE="INVALID_VALUE",e.MISSING_INTL_API="MISSING_INTL_API"})(ce||(ce={}));var ke=function(e){Oe(t,e);function t(n,r,i){var a=e.call(this,n)||this;return a.code=r,a.originalMessage=i,a}return t.prototype.toString=function(){return"[formatjs Error: "+this.code+"] "+this.message},t}(Error);var ar=function(e){Oe(t,e);function t(n,r,i,a){return e.call(this,'Invalid values for "'+n+'": "'+r+'". Options are "'+Object.keys(i).join('", "')+'"',ce.INVALID_VALUE,a)||this}return t}(ke);var Tn=function(e){Oe(t,e);function t(n,r,i){return e.call(this,'Value for "'+n+'" must be of type '+r,ce.INVALID_VALUE,i)||this}return t}(ke);var An=function(e){Oe(t,e);function t(n,r){return e.call(this,'The intl string context variable "'+n+'" was not provided to the string "'+r+'"',ce.MISSING_VALUE,r)||this}return t}(ke);var w;(function(e){e[e.literal=0]="literal",e[e.object=1]="object"})(w||(w={}));function na(e){return e.length<2?e:e.reduce(function(t,n){var r=t[t.length-1];return!r||r.type!==w.literal||n.type!==w.literal?t.push(n):r.value+=n.value,t},[])}function ia(e){return typeof e=="function"}function Re(e,t,n,r,i,a,o){if(e.length===1&&Bt(e[0]))return[{type:w.literal,value:e[0].value}];for(var s=[],c=0,u=e;c0?e.substring(0,r):"";let i=yn(e.split("").reverse().join("")),o=n-i,s=e.substring(o,o+1),a=o+(s==="."||s===","?1:0);t.suffix=i>0?e.substring(a,n):"",t.mask=e.substring(r,a),t.maskHasNegativeSign=t.mask.charAt(0)==="-",t.maskHasPositiveSign=t.mask.charAt(0)==="+";let l=t.mask.match(eo);return t.decimal=l&&l[l.length-1]||".",t.separator=l&&l[1]&&l[0]||",",l=t.mask.split(t.decimal),t.integer=l[0],t.fraction=l[1],t}function ro(e,t,n){let r=!1,i={value:e};e<0&&(r=!0,i.value=-i.value),i.sign=r?"-":"",i.value=Number(i.value).toFixed(t.fraction&&t.fraction.length),i.value=Number(i.value).toString();let o=t.fraction&&t.fraction.lastIndexOf("0"),[s="0",a=""]=i.value.split(".");return(!a||a&&a.length<=o)&&(a=o<0?"":(+("0."+a)).toFixed(o+1).replace("0.","")),i.integer=s,i.fraction=a,no(i,t),(i.result==="0"||i.result==="")&&(r=!1,i.sign=""),!r&&t.maskHasPositiveSign?i.sign="+":r&&t.maskHasPositiveSign?i.sign="-":r&&(i.sign=n&&n.enforceMaskSign&&!t.maskHasNegativeSign?"":"-"),i}function no(e,t){e.result="";let n=t.integer.split(t.separator),r=n.join(""),i=r&&r.indexOf("0");if(i>-1)for(;e.integer.lengthMath.round(e*20)/20},sr=(e,t)=>({accept:e,round:t}),co=[sr(({divisor:e,price:t})=>t%e==0,({divisor:e,price:t})=>t/e),sr(({usePrecision:e})=>e,({divisor:e,price:t})=>Math.ceil(Math.floor(t*1e4/e)/100)/100),sr(()=>!0,({divisor:e,price:t})=>Math.ceil(Math.floor(t*100/e)/100))],ar={[R.YEAR]:{[O.MONTHLY]:Ue.MONTH,[O.ANNUAL]:Ue.YEAR},[R.MONTH]:{[O.MONTHLY]:Ue.MONTH}},lo=(e,t)=>e.indexOf(`'${t}'`)===0,uo=(e,t=!0)=>{let n=e.replace(/'.*?'/,"").trim(),r=An(n);return!!r?t||(n=n.replace(/[,\.]0+/,r)):n=n.replace(/\s?(#.*0)(?!\s)?/,"$&"+po(e)),n},fo=e=>{let t=mo(e),n=lo(e,t),r=e.replace(/'.*?'/,""),i=Tn.test(r)||Pn.test(r);return{currencySymbol:t,isCurrencyFirst:n,hasCurrencySpace:i}},bn=e=>e.replace(Tn,vn).replace(Pn,vn),po=e=>e.match(/#(.?)#/)?.[1]===Sn?oo:Sn,mo=e=>e.match(/'(.*?)'/)?.[1]??"",An=e=>e.match(/0(.?)0/)?.[1]??"";function nt({formatString:e,price:t,usePrecision:n,isIndianPrice:r=!1},i,o=s=>s){let{currencySymbol:s,isCurrencyFirst:a,hasCurrencySpace:l}=fo(e),u=n?An(e):"",c=uo(e,n),p=n?2:0,f=o(t,{currencySymbol:s}),h=r?f.toLocaleString("hi-IN",{minimumFractionDigits:p,maximumFractionDigits:p}):_n(c,f),d=n?h.lastIndexOf(u):h.length,_=h.substring(0,d),S=h.substring(d+1);return{accessiblePrice:e.replace(/'.*?'/,"SYMBOL").replace(/#.*0/,h).replace(/SYMBOL/,s),currencySymbol:s,decimals:S,decimalsDelimiter:u,hasCurrencySpace:l,integer:_,isCurrencyFirst:a,recurrenceTerm:i}}var wn=e=>{let{commitment:t,term:n,usePrecision:r}=e,i=so[n]??1;return nt(e,i>1?Ue.MONTH:ar[t]?.[n],(o,{currencySymbol:s})=>{let a={divisor:i,price:o,usePrecision:r},{round:l}=co.find(({accept:c})=>c(a));if(!l)throw new Error(`Missing rounding rule for: ${JSON.stringify(a)}`);return(ao[s]??(c=>c))(l(a))})},Ln=({commitment:e,term:t,...n})=>nt(n,ar[e]?.[t]),On=e=>{let{commitment:t,term:n}=e;return t===R.YEAR&&n===O.MONTHLY?nt(e,Ue.YEAR,r=>r*12):nt(e,ar[t]?.[n])};var ho={recurrenceLabel:"{recurrenceTerm, select, MONTH {/mo} YEAR {/yr} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {per month} YEAR {per year} other {}}",perUnitLabel:"{perUnit, select, LICENSE {per license} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {per license} other {}}",freeLabel:"Free",freeAriaLabel:"Free",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"Alternatively at {alternativePrice}",strikethroughAriaLabel:"Regularly at {strikethroughPrice}"},Eo=Hr("ConsonantTemplates/price"),go=/<.+?>/g,F={container:"price",containerOptical:"price-optical",containerStrikethrough:"price-strikethrough",containerAnnual:"price-annual",disabled:"disabled",currencySpace:"price-currency-space",currencySymbol:"price-currency-symbol",decimals:"price-decimals",decimalsDelimiter:"price-decimals-delimiter",integer:"price-integer",recurrence:"price-recurrence",taxInclusivity:"price-tax-inclusivity",unitType:"price-unit-type"},ue={perUnitLabel:"perUnitLabel",perUnitAriaLabel:"perUnitAriaLabel",recurrenceLabel:"recurrenceLabel",recurrenceAriaLabel:"recurrenceAriaLabel",taxExclusiveLabel:"taxExclusiveLabel",taxInclusiveLabel:"taxInclusiveLabel",strikethroughAriaLabel:"strikethroughAriaLabel"},xo="TAX_EXCLUSIVE",yo=e=>jr(e)?Object.entries(e).filter(([,t])=>ye(t)||Ye(t)||t===!0).reduce((t,[n,r])=>t+` ${n}${r===!0?"":'="'+Fr(r)+'"'}`,""):"",X=(e,t,n,r=!1)=>`${r?bn(t):t??""}`;function _o(e,{accessibleLabel:t,currencySymbol:n,decimals:r,decimalsDelimiter:i,hasCurrencySpace:o,integer:s,isCurrencyFirst:a,recurrenceLabel:l,perUnitLabel:u,taxInclusivityLabel:c},p={}){let f=X(F.currencySymbol,n),h=X(F.currencySpace,o?" ":""),d="";return a&&(d+=f+h),d+=X(F.integer,s),d+=X(F.decimalsDelimiter,i),d+=X(F.decimals,r),a||(d+=h+f),d+=X(F.recurrence,l,null,!0),d+=X(F.unitType,u,null,!0),d+=X(F.taxInclusivity,c,!0),X(e,d,{...p,"aria-label":t})}var fe=({displayOptical:e=!1,displayStrikethrough:t=!1,displayAnnual:n=!1}={})=>({country:r,displayFormatted:i=!0,displayRecurrence:o=!0,displayPerUnit:s=!1,displayTax:a=!1,language:l,literals:u={}}={},{commitment:c,formatString:p,price:f,priceWithoutDiscount:h,taxDisplay:d,taxTerm:_,term:S,usePrecision:A}={},w={})=>{Object.entries({country:r,formatString:p,language:l,price:f}).forEach(([Q,Pt])=>{if(Pt==null)throw new Error(`Argument "${Q}" is missing`)});let T={...ho,...u},C=`${l.toLowerCase()}-${r.toUpperCase()}`;function P(Q,Pt){let bt=T[Q];if(bt==null)return"";try{return new xn(bt.replace(go,""),C).format(Pt)}catch{return Eo.error("Failed to format literal:",bt),""}}let L=t&&h?h:f,U=e?wn:Ln;n&&(U=On);let{accessiblePrice:j,recurrenceTerm:Z,...te}=U({commitment:c,formatString:p,term:S,price:e?f:L,usePrecision:A,isIndianPrice:r==="IN"}),H=j,oe="";if(v(o)&&Z){let Q=P(ue.recurrenceAriaLabel,{recurrenceTerm:Z});Q&&(H+=" "+Q),oe=P(ue.recurrenceLabel,{recurrenceTerm:Z})}let se="";if(v(s)){se=P(ue.perUnitLabel,{perUnit:"LICENSE"});let Q=P(ue.perUnitAriaLabel,{perUnit:"LICENSE"});Q&&(H+=" "+Q)}let J="";v(a)&&_&&(J=P(d===xo?ue.taxExclusiveLabel:ue.taxInclusiveLabel,{taxTerm:_}),J&&(H+=" "+J)),t&&(H=P(ue.strikethroughAriaLabel,{strikethroughPrice:H}));let W=F.container;if(e&&(W+=" "+F.containerOptical),t&&(W+=" "+F.containerStrikethrough),n&&(W+=" "+F.containerAnnual),v(i))return _o(W,{...te,accessibleLabel:H,recurrenceLabel:oe,perUnitLabel:se,taxInclusivityLabel:J},w);let{currencySymbol:Ee,decimals:Ve,decimalsDelimiter:je,hasCurrencySpace:we,integer:Tt,isCurrencyFirst:zn}=te,ge=[Tt,je,Ve];zn?(ge.unshift(we?"\xA0":""),ge.unshift(Ee)):(ge.push(we?"\xA0":""),ge.push(Ee)),ge.push(oe,se,J);let Zn=ge.join("");return X(W,Zn,w)},Nn=()=>(e,t,n)=>{let i=(e.displayOldPrice===void 0||v(e.displayOldPrice))&&t.priceWithoutDiscount&&t.priceWithoutDiscount!=t.price;return`${fe()(e,t,n)}${i?" "+fe({displayStrikethrough:!0})(e,t,n):""}`};var cr=fe(),lr=Nn(),ur=fe({displayOptical:!0}),fr=fe({displayStrikethrough:!0}),pr=fe({displayAnnual:!0});var So=(e,t)=>{if(!(!Se(e)||!Se(t)))return Math.floor((t-e)/t*100)},Cn=()=>(e,t,n)=>{let{price:r,priceWithoutDiscount:i}=t,o=So(r,i);return o===void 0?'':`${o}%`};var mr=Cn();var{freeze:ke}=Object,Y=ke({...ae}),B=ke({...V}),pe={STAGE:"STAGE",PRODUCTION:"PRODUCTION",LOCAL:"LOCAL"},hr=ke({...R}),dr=ke({...Xr}),Er=ke({...O});var Pr={};Qn(Pr,{CLASS_NAME_FAILED:()=>it,CLASS_NAME_PENDING:()=>ot,CLASS_NAME_RESOLVED:()=>st,ERROR_MESSAGE_BAD_REQUEST:()=>at,ERROR_MESSAGE_MISSING_LITERALS_URL:()=>xr,ERROR_MESSAGE_OFFER_NOT_FOUND:()=>gr,EVENT_TYPE_ERROR:()=>vo,EVENT_TYPE_FAILED:()=>ct,EVENT_TYPE_PENDING:()=>lt,EVENT_TYPE_READY:()=>me,EVENT_TYPE_RESOLVED:()=>ut,LOG_NAMESPACE:()=>yr,Landscape:()=>he,PARAM_AOS_API_KEY:()=>To,PARAM_ENV:()=>_r,PARAM_LANDSCAPE:()=>Sr,PARAM_WCS_API_KEY:()=>Po,STATE_FAILED:()=>$,STATE_PENDING:()=>q,STATE_RESOLVED:()=>z,TAG_NAME_SERVICE:()=>ee,WCS_PROD_URL:()=>vr,WCS_STAGE_URL:()=>Tr});var it="placeholder-failed",ot="placeholder-pending",st="placeholder-resolved",at="Bad WCS request",gr="Commerce offer not found",xr="Literals URL not provided",vo="wcms:commerce:error",ct="wcms:placeholder:failed",lt="wcms:placeholder:pending",me="wcms:commerce:ready",ut="wcms:placeholder:resolved",yr="wcms/commerce",_r="commerce.env",Sr="commerce.landscape",To="commerce.aosKey",Po="commerce.wcsKey",vr="https://www.adobe.com/web_commerce_artifact",Tr="https://www.stage.adobe.com/web_commerce_artifact_stage",$="failed",q="pending",z="resolved",ee="wcms-commerce",he={DRAFT:"DRAFT",PUBLISHED:"PUBLISHED"};var br={clientId:"merch-at-scale",delimiter:"\xB6",ignoredProperties:["analytics","literals"],serializableTypes:["Array","Object"],sampleRate:30,tags:"consumer=milo/commerce"},Rn=new Set,bo=e=>e instanceof Error||typeof e.originatingRequest=="string";function In(e){if(e==null)return;let t=typeof e;if(t==="function"){let{name:n}=e;return n?`${t} ${n}`:t}if(t==="object"){if(e instanceof Error)return e.message;if(typeof e.originatingRequest=="string"){let{message:r,originatingRequest:i,status:o}=e;return[r,o,i].filter(s=>s).join(" ")}let n=e[Symbol.toStringTag]??Object.getPrototypeOf(e).constructor.name;if(!br.serializableTypes.includes(n))return n}return e}function Ao(e,t){if(!br.ignoredProperties.includes(e))return In(t)}var Ar={append(e){let{delimiter:t,sampleRate:n,tags:r,clientId:i}=br,{message:o,params:s}=e,a=[],l=o,u=[];s.forEach(f=>{f!=null&&(bo(f)?a:u).push(f)}),a.length&&(l+=" ",l+=a.map(In).join(" "));let{pathname:c,search:p}=window.location;l+=`${t}page=`,l+=c+p,u.length&&(l+=`${t}facts=`,l+=JSON.stringify(u,Ao)),Rn.has(l)||(Rn.add(l),window.lana?.log(l,{sampleRate:n,tags:r,clientId:i}))}};var x=Object.freeze({checkoutClientId:"adobe_com",checkoutWorkflow:Y.V3,checkoutWorkflowStep:B.EMAIL,country:"US",displayOldPrice:!0,displayPerUnit:!1,displayRecurrence:!0,displayTax:!1,env:pe.PRODUCTION,forceTaxExclusive:!1,language:"en",entitlement:!1,extraOptions:{},modal:!1,promotionCode:"",quantity:1,wcsApiKey:"wcms-commerce-ims-ro-user-milo",wcsBufferDelay:1,wcsURL:"https://www.adobe.com/web_commerce_artifact",landscape:he.PUBLISHED,wcsBufferLimit:1});function Mn(e,{once:t=!1}={}){let n=null;function r(){let i=document.querySelector(ee);i!==n&&(n=i,i&&e(i))}return document.addEventListener(me,r,{once:t}),ie(r),()=>document.removeEventListener(me,r)}function Ge(e,{country:t,forceTaxExclusive:n,perpetual:r}){let i;if(e.length<2)i=e;else{let o=t==="GB"||r?"EN":"MULT",[s,a]=e;i=[s.language===o?s:a]}return n&&(i=i.map(Yt)),i}var ie=e=>window.setTimeout(e);function Pe(e,t=1){if(e==null)return[t];let n=(Array.isArray(e)?e:String(e).split(",")).map(Te).filter(Se);return n.length||(n=[t]),n}function ft(e){return e==null?[]:(Array.isArray(e)?e:String(e).split(",")).filter(Gt)}function k(){return window.customElements.get(ee)?.instance}var wo="en_US",m={ar:"AR_es",be_en:"BE_en",be_fr:"BE_fr",be_nl:"BE_nl",br:"BR_pt",ca:"CA_en",ch_de:"CH_de",ch_fr:"CH_fr",ch_it:"CH_it",cl:"CL_es",co:"CO_es",la:"DO_es",mx:"MX_es",pe:"PE_es",africa:"MU_en",dk:"DK_da",de:"DE_de",ee:"EE_et",eg_ar:"EG_ar",eg_en:"EG_en",es:"ES_es",fr:"FR_fr",gr_el:"GR_el",gr_en:"GR_en",ie:"IE_en",il_he:"IL_iw",it:"IT_it",lv:"LV_lv",lt:"LT_lt",lu_de:"LU_de",lu_en:"LU_en",lu_fr:"LU_fr",my_en:"MY_en",my_ms:"MY_ms",hu:"HU_hu",mt:"MT_en",mena_en:"DZ_en",mena_ar:"DZ_ar",nl:"NL_nl",no:"NO_nb",pl:"PL_pl",pt:"PT_pt",ro:"RO_ro",si:"SI_sl",sk:"SK_sk",fi:"FI_fi",se:"SE_sv",tr:"TR_tr",uk:"GB_en",at:"AT_de",cz:"CZ_cs",bg:"BG_bg",ru:"RU_ru",ua:"UA_uk",au:"AU_en",in_en:"IN_en",in_hi:"IN_hi",id_en:"ID_en",id_id:"ID_in",nz:"NZ_en",sa_ar:"SA_ar",sa_en:"SA_en",sg:"SG_en",cn:"CN_zh-Hans",tw:"TW_zh-Hant",hk_zh:"HK_zh-hant",jp:"JP_ja",kr:"KR_ko",za:"ZA_en",ng:"NG_en",cr:"CR_es",ec:"EC_es",pr:"US_es",gt:"GT_es",cis_en:"AZ_en",cis_ru:"AZ_ru",sea:"SG_en",th_en:"TH_en",th_th:"TH_th"},pt=Object.freeze({LOCAL:"local",PROD:"prod",STAGE:"stage"});function Dn({locale:e={}}={}){if(!e.prefix)return{country:x.country,language:x.language,locale:wo};let t=e.prefix.replace("/","")??"",[n=x.country,r=x.language]=(m[t]??t).split("_",2);return n=n.toUpperCase(),r=r.toLowerCase(),{country:n,language:r,locale:`${r}_${n}`}}function wr(e={}){let{commerce:t={},locale:n=void 0}=e,r=pe.PRODUCTION,i=vr,o=["local","stage"].includes(e.env?.name),s=N(_r,t,{metadata:!1})?.toLowerCase()==="stage";o&&s&&(r=pe.STAGE,i=Tr);let a=N("checkoutClientId",t)??x.checkoutClientId,l=ne(N("checkoutWorkflow",t),Y,x.checkoutWorkflow),u=B.CHECKOUT;l===Y.V3&&(u=ne(N("checkoutWorkflowStep",t),B,x.checkoutWorkflowStep));let c=v(N("displayOldPrice",t),x.displayOldPrice),p=v(N("displayPerUnit",t),x.displayPerUnit),f=v(N("displayRecurrence",t),x.displayRecurrence),h=v(N("displayTax",t),x.displayTax),d=v(N("entitlement",t),x.entitlement),_=v(N("modal",t),x.modal),S=v(N("forceTaxExclusive",t),x.forceTaxExclusive),A=N("promotionCode",t)??x.promotionCode,w=Pe(N("quantity",t)),T=N("wcsApiKey",t)??x.wcsApiKey,C=e.env?.name===pt.PROD?he.PUBLISHED:ne(N(Sr,t),he,x.landscape),P=Te(N("wcsBufferDelay",t),x.wcsBufferDelay),L=Te(N("wcsBufferLimit",t),x.wcsBufferLimit);return{...Dn({locale:n}),displayOldPrice:c,checkoutClientId:a,checkoutWorkflow:l,checkoutWorkflowStep:u,displayPerUnit:p,displayRecurrence:f,displayTax:h,entitlement:d,extraOptions:x.extraOptions,modal:_,env:r,forceTaxExclusive:S,priceLiteralsURL:t.priceLiteralsURL,priceLiteralsPromise:t.priceLiteralsPromise,promotionCode:A,quantity:w,wcsApiKey:T,wcsBufferDelay:P,wcsBufferLimit:L,wcsURL:i,landscape:C}}var kn="debug",Lo="error",Oo="info",No="warn",Co=Date.now(),Lr=new Set,Or=new Set,Un=new Map,Fe=Object.freeze({DEBUG:kn,ERROR:Lo,INFO:Oo,WARN:No}),Gn={append({level:e,message:t,params:n,timestamp:r,source:i}){console[e](`${r}ms [${i}] %c${t}`,"font-weight: bold;",...n)}},Fn={filter:({level:e})=>e!==kn},Ro={filter:()=>!1};function Io(e,t,n,r,i){return{level:e,message:t,namespace:n,get params(){if(r.length===1){let[o]=r;re(o)&&(r=o(),Array.isArray(r)||(r=[r]))}return r},source:i,timestamp:Date.now()-Co}}function Mo(e){[...Or].every(t=>t(e))&&Lr.forEach(t=>t(e))}function Vn(e){let t=(Un.get(e)??0)+1;Un.set(e,t);let n=`${e} #${t}`,r=o=>(s,...a)=>Mo(Io(o,s,e,a,n)),i=Object.seal({id:n,namespace:e,module(o){return Vn(`${i.namespace}/${o}`)},debug:r(Fe.DEBUG),error:r(Fe.ERROR),info:r(Fe.INFO),warn:r(Fe.WARN)});return i}function mt(...e){e.forEach(t=>{let{append:n,filter:r}=t;re(r)?Or.add(r):re(n)&&Lr.add(n)})}function Do(e={}){let{name:t}=e,n=v(N("commerce.debug",{search:!0,storage:!0}),t===pt.LOCAL);return mt(n?Gn:Fn),t===pt.PROD&&mt(Ar),M}function Uo(){Lr.clear(),Or.clear()}var M={...Vn(yr),Level:Fe,Plugins:{consoleAppender:Gn,debugFilter:Fn,quietFilter:Ro,lanaAppender:Ar},init:Do,reset:Uo,use:mt};var ko={CLASS_NAME_FAILED:it,CLASS_NAME_PENDING:ot,CLASS_NAME_RESOLVED:st,EVENT_TYPE_FAILED:ct,EVENT_TYPE_PENDING:lt,EVENT_TYPE_RESOLVED:ut,STATE_FAILED:$,STATE_PENDING:q,STATE_RESOLVED:z},Go={[$]:it,[q]:ot,[z]:st},Fo={[$]:ct,[q]:lt,[z]:ut},Et=new WeakMap;function G(e){if(!Et.has(e)){let t=M.module(e.constructor.is);Et.set(e,{changes:new Map,connected:!1,dispose:_e,error:void 0,log:t,options:void 0,promises:[],state:q,timer:null,value:void 0,version:0})}return Et.get(e)}function ht(e){let t=G(e),{error:n,promises:r,state:i}=t;(i===z||i===$)&&(t.promises=[],i===z?r.forEach(({resolve:o})=>o(e)):i===$&&r.forEach(({reject:o})=>o(n))),e.dispatchEvent(new CustomEvent(Fo[i],{bubbles:!0}))}function dt(e){let t=Et.get(e);[$,q,z].forEach(n=>{e.classList.toggle(Go[n],n===t.state)})}var Vo={get error(){return G(this).error},get log(){return G(this).log},get options(){return G(this).options},get state(){return G(this).state},get value(){return G(this).value},attributeChangedCallback(e,t,n){G(this).changes.set(e,n),this.requestUpdate()},connectedCallback(){G(this).dispose=Mn(()=>this.requestUpdate(!0))},disconnectedCallback(){let e=G(this);e.connected&&(e.connected=!1,e.log.debug("Disconnected:",{element:this})),e.dispose(),e.dispose=_e},onceSettled(){let{error:e,promises:t,state:n}=G(this);return z===n?Promise.resolve(this):$===n?Promise.reject(e):new Promise((r,i)=>{t.push({resolve:r,reject:i})})},toggleResolved(e,t,n){let r=G(this);return e!==r.version?!1:(n!==void 0&&(r.options=n),r.state=z,r.value=t,dt(this),this.log.debug("Resolved:",{element:this,value:t}),ie(()=>ht(this)),!0)},toggleFailed(e,t,n){let r=G(this);return e!==r.version?!1:(n!==void 0&&(r.options=n),r.error=t,r.state=$,dt(this),r.log.error("Failed:",{element:this,error:t}),ie(()=>ht(this)),!0)},togglePending(e){let t=G(this);return t.version++,e&&(t.options=e),t.state=q,dt(this),ie(()=>ht(this)),t.version},requestUpdate(e=!1){if(!this.isConnected||!k())return;let t=G(this);if(t.timer)return;let{error:n,options:r,state:i,value:o,version:s}=t;t.state=q,t.timer=ie(async()=>{t.timer=null;let a=null;if(t.changes.size&&(a=Object.fromEntries(t.changes.entries()),t.changes.clear()),t.connected?t.log.debug("Updated:",{element:this,changes:a}):(t.connected=!0,t.log.debug("Connected:",{element:this,changes:a})),a||e)try{await this.render?.()===!1&&t.state===q&&t.version===s&&(t.state=i,t.error=n,t.value=o,dt(this),ht(this))}catch(l){this.toggleFailed(t.version,l,r)}})}};function jn(e={}){return Object.entries(e).forEach(([t,n])=>{(n==null||n===""||n?.length===0)&&delete e[t]}),e}function gt(e,t={}){let{tag:n,is:r}=e,i=document.createElement(n,{is:r});return i.setAttribute("is",r),Object.assign(i.dataset,jn(t)),i}function xt(e){let{tag:t,is:n,prototype:r}=e,i=window.customElements.get(n);return i||(Object.defineProperties(r,Object.getOwnPropertyDescriptors(Vo)),i=Object.defineProperties(e,Object.getOwnPropertyDescriptors(ko)),window.customElements.define(n,i,{extends:t})),i}function yt(e,t=document.body){return Array.from(t?.querySelectorAll(`${e.tag}[is="${e.is}"]`)??[])}function _t(e,t={}){return e instanceof HTMLElement?(Object.assign(e.dataset,jn(t)),e):null}var jo="download",Ho="upgrade",de,be=class be extends HTMLAnchorElement{constructor(){super();Mr(this,de,void 0);this.addEventListener("click",this.clickHandler)}static get observedAttributes(){return["data-checkout-workflow","data-checkout-workflow-step","data-extra-options","data-ims-country","data-perpetual","data-promotion-code","data-quantity","data-template","data-wcs-osi","data-entitlement","data-upgrade","data-modal"]}static createCheckoutLink(n={},r=""){let i=k();if(!i)return null;let{checkoutMarketSegment:o,checkoutWorkflow:s,checkoutWorkflowStep:a,entitlement:l,upgrade:u,modal:c,perpetual:p,promotionCode:f,quantity:h,wcsOsi:d,extraOptions:_}=i.collectCheckoutOptions(n),S=gt(be,{checkoutMarketSegment:o,checkoutWorkflow:s,checkoutWorkflowStep:a,entitlement:l,upgrade:u,modal:c,perpetual:p,promotionCode:f,quantity:h,wcsOsi:d,extraOptions:_});return r&&(S.innerHTML=`${r}`),S}static getCheckoutLinks(n){return yt(be,n)}get isCheckoutLink(){return!0}get placeholder(){return this}clickHandler(n){var r;(r=At(this,de))==null||r.call(this,n)}async render(n={}){if(!this.isConnected)return!1;let r=k();if(!r)return!1;this.dataset.imsCountry||r.imsCountryPromise.then(c=>{c&&(this.dataset.imsCountry=c)},_e);let i=r.collectCheckoutOptions(n,this.placeholder);if(!i.wcsOsi.length)return!1;let o;try{o=JSON.parse(i.extraOptions??"{}")}catch(c){this.placeholder.log.error("cannot parse exta checkout options",c)}let s=this.placeholder.togglePending(i);this.href="";let a=r.resolveOfferSelectors(i),l=await Promise.all(a);l=l.map(c=>Ge(c,i));let u=await r.buildCheckoutAction(l.flat(),{...o,...i});return this.renderOffers(l.flat(),i,{},u,s)}renderOffers(n,r,i={},o=void 0,s=void 0){if(!this.isConnected)return!1;let a=k();if(!a)return!1;if(r={...JSON.parse(this.placeholder.dataset.extraOptions??"null"),...r,...i},s??(s=this.placeholder.togglePending(r)),At(this,de)&&wt(this,de,void 0),o){this.classList.remove(jo,Ho),this.placeholder.toggleResolved(s,n,r);let{url:u,text:c,className:p,handler:f}=o;return u&&(this.href=u),c&&(this.firstElementChild.innerHTML=c),p&&this.classList.add(...p.split(" ")),f&&(this.setAttribute("href","#"),wt(this,de,f.bind(this))),!0}else if(n.length){if(this.placeholder.toggleResolved(s,n,r)){let u=a.buildCheckoutURL(n,r);return this.setAttribute("href",u),!0}}else{let u=new Error(`Not provided: ${r?.wcsOsi??"-"}`);if(this.placeholder.toggleFailed(s,u,r))return this.setAttribute("href","#"),!0}return!1}updateOptions(n={}){let r=k();if(!r)return!1;let{checkoutMarketSegment:i,checkoutWorkflow:o,checkoutWorkflowStep:s,entitlement:a,upgrade:l,modal:u,perpetual:c,promotionCode:p,quantity:f,wcsOsi:h}=r.collectCheckoutOptions(n);return _t(this,{checkoutMarketSegment:i,checkoutWorkflow:o,checkoutWorkflowStep:s,entitlement:a,upgrade:l,modal:u,perpetual:c,promotionCode:p,quantity:f,wcsOsi:h}),!0}};de=new WeakMap,K(be,"is","checkout-link"),K(be,"tag","a");var Nr=be,St=xt(Nr);var Hn=[m.uk,m.au,m.fr,m.at,m.be_en,m.be_fr,m.be_nl,m.bg,m.ch_de,m.ch_fr,m.ch_it,m.cz,m.de,m.dk,m.ee,m.eg_ar,m.eg_en,m.es,m.fi,m.fr,m.gr_el,m.gr_en,m.hu,m.ie,m.it,m.lu_de,m.lu_en,m.lu_fr,m.nl,m.no,m.pl,m.pt,m.ro,m.se,m.si,m.sk,m.tr,m.ua,m.id_en,m.id_id,m.in_en,m.in_hi,m.jp,m.my_en,m.my_ms,m.nz,m.th_en,m.th_th],Wo={INDIVIDUAL_COM:[m.za,m.lt,m.lv,m.ng,m.sa_ar,m.sa_en,m.za,m.sg,m.kr],TEAM_COM:[m.za,m.lt,m.lv,m.ng,m.za,m.co,m.kr],INDIVIDUAL_EDU:[m.lt,m.lv,m.sa_en,m.sea],TEAM_EDU:[m.sea,m.kr]},Ae=class Ae extends HTMLSpanElement{static get observedAttributes(){return["data-display-old-price","data-display-per-unit","data-display-recurrence","data-display-tax","data-perpetual","data-promotion-code","data-tax-exclusive","data-template","data-wcs-osi"]}static createInlinePrice(t){let n=k();if(!n)return null;let{displayOldPrice:r,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:l,promotionCode:u,quantity:c,template:p,wcsOsi:f}=n.collectPriceOptions(t);return gt(Ae,{displayOldPrice:r,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:l,promotionCode:u,quantity:c,template:p,wcsOsi:f})}static getInlinePrices(t){return yt(Ae,t)}get isInlinePrice(){return!0}get placeholder(){return this}resolveDisplayTaxForGeoAndSegment(t,n,r,i){let o=`${t}_${n}`;if(Hn.includes(t)||Hn.includes(o))return!0;let s=Wo[`${r}_${i}`];return s?!!(s.includes(t)||s.includes(o)):!1}async resolveDisplayTax(t,n){let[r]=await t.resolveOfferSelectors(n),i=Ge(await r,n);if(i?.length){let{country:o,language:s}=n,a=i[0],[l=""]=a.marketSegments;return this.resolveDisplayTaxForGeoAndSegment(o,s,a.customerSegment,l)}}async render(t={}){if(!this.isConnected)return!1;let n=k();if(!n)return!1;let r=n.collectPriceOptions(t,this.placeholder);if(!r.wcsOsi.length)return!1;let i=this.placeholder.togglePending(r);this.innerHTML="";let[o]=n.resolveOfferSelectors(r);return this.renderOffers(Ge(await o,r),r,i)}renderOffers(t,n={},r=void 0){if(!this.isConnected)return;let i=k();if(!i)return!1;let o=i.collectPriceOptions({...this.dataset,...n});if(r??(r=this.placeholder.togglePending(o)),t.length){if(this.placeholder.toggleResolved(r,t,o))return this.innerHTML=i.buildPriceHTML(t,o),!0}else{let s=new Error(`Not provided: ${o?.wcsOsi??"-"}`);if(this.placeholder.toggleFailed(r,s,o))return this.innerHTML="",!0}return!1}updateOptions(t){let n=k();if(!n)return!1;let{displayOldPrice:r,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:l,promotionCode:u,quantity:c,template:p,wcsOsi:f}=n.collectPriceOptions(t);return _t(this,{displayOldPrice:r,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:l,promotionCode:u,quantity:c,template:p,wcsOsi:f}),!0}};K(Ae,"is","inline-price"),K(Ae,"tag","span");var Cr=Ae,vt=xt(Cr);function Wn({providers:e,settings:t},n){let r=M.module("checkout");function i(u,c){let{checkoutClientId:p,checkoutWorkflow:f,checkoutWorkflowStep:h,country:d,language:_,promotionCode:S,quantity:A}=t,{checkoutMarketSegment:w,checkoutWorkflow:T=f,checkoutWorkflowStep:C=h,imsCountry:P,country:L=P??d,language:U=_,quantity:j=A,entitlement:Z,upgrade:te,modal:H,perpetual:oe,promotionCode:se=S,wcsOsi:J,extraOptions:W,...Ee}=Object.assign({},c?.dataset??{},u??{}),Ve=ne(T,Y,x.checkoutWorkflow),je=B.CHECKOUT;Ve===Y.V3&&(je=ne(C,B,x.checkoutWorkflowStep));let we=ve({...Ee,extraOptions:W,checkoutClientId:p,checkoutMarketSegment:w,country:L,quantity:Pe(j,x.quantity),checkoutWorkflow:Ve,checkoutWorkflowStep:je,language:U,entitlement:v(Z),upgrade:v(te),modal:v(H),perpetual:v(oe),promotionCode:Ne(se).effectivePromoCode,wcsOsi:ft(J)});if(c)for(let Tt of e.checkout)Tt(c,we);return we}async function o(u,c){let p=k(),f=await n.getCheckoutAction?.(u,c,p.imsSignedInPromise);return f||null}function s(u,c){if(!Array.isArray(u)||!u.length||!c)return"";let{env:p,landscape:f}=t,{checkoutClientId:h,checkoutMarketSegment:d,checkoutWorkflow:_,checkoutWorkflowStep:S,country:A,promotionCode:w,quantity:T,...C}=i(c),P=window.frameElement?"if":"fp",L={checkoutPromoCode:w,clientId:h,context:P,country:A,env:p,items:[],marketSegment:d,workflowStep:S,landscape:f,...C};if(u.length===1){let[{offerId:U,offerType:j,productArrangementCode:Z}]=u,{marketSegments:[te]}=u[0];Object.assign(L,{marketSegment:te,offerType:j,productArrangementCode:Z}),L.items.push(T[0]===1?{id:U}:{id:U,quantity:T[0]})}else L.items.push(...u.map(({offerId:U},j)=>({id:U,quantity:T[j]??x.quantity})));return Rt(_,L)}let{createCheckoutLink:a,getCheckoutLinks:l}=St;return{CheckoutLink:St,CheckoutWorkflow:Y,CheckoutWorkflowStep:B,buildCheckoutAction:o,buildCheckoutURL:s,collectCheckoutOptions:i,createCheckoutLink:a,getCheckoutLinks:l}}function Xo({interval:e=200,maxAttempts:t=25}={}){let n=M.module("ims");return new Promise(r=>{n.debug("Waing for IMS to be ready");let i=0;function o(){window.adobeIMS?.initialized?r():++i>t?(n.debug("Timeout"),r()):setTimeout(o,e)}o()})}function Yo(e){return e.then(()=>window.adobeIMS?.isSignedInUser()??!1)}function Bo(e){let t=M.module("ims");return e.then(n=>n?window.adobeIMS.getProfile().then(({countryCode:r})=>(t.debug("Got user country:",r),r),r=>{t.error("Unable to get user country:",r)}):null)}function Xn({}){let e=Xo(),t=Yo(e),n=Bo(t);return{imsReadyPromise:e,imsSignedInPromise:t,imsCountryPromise:n}}function $o(e){if(!e.priceLiteralsURL)throw new Error(xr);return new Promise(t=>{window.fetch(e.priceLiteralsURL).then(n=>{n.json().then(({data:r})=>{t(r)})})})}async function Yn(e){let n=await(e.priceLiteralsPromise||$o(e));if(Array.isArray(n)){let r=o=>n.find(s=>Xe(s.lang,o)),i=r(e.language)??r(x.language);if(i)return Object.freeze(i)}return{}}function Bn({literals:e,providers:t,settings:n}){function r(a,l){let{country:u,displayOldPrice:c,displayPerUnit:p,displayRecurrence:f,displayTax:h,forceTaxExclusive:d,language:_,promotionCode:S,quantity:A}=n,{displayOldPrice:w=c,displayPerUnit:T=p,displayRecurrence:C=f,displayTax:P=h,forceTaxExclusive:L=d,country:U=u,language:j=_,perpetual:Z,promotionCode:te=S,quantity:H=A,template:oe,wcsOsi:se,...J}=Object.assign({},l?.dataset??{},a??{}),W=ve({...J,country:U,displayOldPrice:v(w),displayPerUnit:v(T),displayRecurrence:v(C),displayTax:v(P),forceTaxExclusive:v(L),language:j,perpetual:v(Z),promotionCode:Ne(te).effectivePromoCode,quantity:Pe(H,x.quantity),template:oe,wcsOsi:ft(se)});if(l)for(let Ee of t.price)Ee(l,W);return W}function i(a,l){if(!Array.isArray(a)||!a.length||!l)return"";let{template:u}=l,c;switch(u){case"discount":c=mr;break;case"strikethrough":c=fr;break;case"optical":c=ur;break;case"annual":c=pr;break;default:c=l.promotionCode?lr:cr}let p=r(l);p.literals=Object.assign({},e.price,ve(l.literals??{}));let[f]=a;return f={...f,...f.priceDetails},c(p,f)}let{createInlinePrice:o,getInlinePrices:s}=vt;return{InlinePrice:vt,buildPriceHTML:i,collectPriceOptions:r,createInlinePrice:o,getInlinePrices:s}}function $n({settings:e}){let t=M.module("wcs"),{env:n,wcsApiKey:r}=e,i=new Map,o=new Map,s;async function a(c,p,f=!0){let h=gr;t.debug("Fetching:",c);try{c.offerSelectorIds=c.offerSelectorIds.sort();let d=new URL(e.wcsURL);d.searchParams.set("offer_selector_ids",c.offerSelectorIds.join(",")),d.searchParams.set("country",c.country),d.searchParams.set("locale",c.locale),d.searchParams.set("landscape",n===pe.STAGE?"ALL":e.landscape),d.searchParams.set("api_key",r),c.language&&d.searchParams.set("language",c.language),c.promotionCode&&d.searchParams.set("promotion_code",c.promotionCode),c.currency&&d.searchParams.set("currency",c.currency);let _=await fetch(d.toString(),{credentials:"omit"});if(_.ok){let S=await _.json();t.debug("Fetched:",c,S);let A=S.resolvedOffers??[];A=A.map($e),p.forEach(({resolve:w},T)=>{let C=A.filter(({offerSelectorIds:P})=>P.includes(T)).flat();C.length&&(p.delete(T),w(C))})}else _.status===404&&c.offerSelectorIds.length>1?(t.debug("Multi-osi 404, fallback to fetch-by-one strategy"),await Promise.allSettled(c.offerSelectorIds.map(S=>a({...c,offerSelectorIds:[S]},p,!1)))):(h=at,t.error(h,c))}catch(d){h=at,t.error(h,c,d)}f&&p.size&&(t.debug("Missing:",{offerSelectorIds:[...p.keys()]}),p.forEach(d=>{d.reject(new Error(h))}))}function l(){clearTimeout(s);let c=[...o.values()];o.clear(),c.forEach(({options:p,promises:f})=>a(p,f))}function u({country:c,language:p,perpetual:f=!1,promotionCode:h="",wcsOsi:d=[]}){let _=`${p}_${c}`;c!=="GB"&&(p=f?"EN":"MULT");let S=[c,p,h].filter(A=>A).join("-").toLowerCase();return d.map(A=>{let w=`${A}-${S}`;if(!i.has(w)){let T=new Promise((C,P)=>{let L=o.get(S);if(!L){let U={country:c,locale:_,offerSelectorIds:[]};c!=="GB"&&(U.language=p),L={options:U,promises:new Map},o.set(S,L)}h&&(L.options.promotionCode=h),L.options.offerSelectorIds.push(A),L.promises.set(A,{resolve:C,reject:P}),L.options.offerSelectorIds.length>=e.wcsBufferLimit?l():(t.debug("Queued:",L.options),s||(s=setTimeout(l,e.wcsBufferDelay)))});i.set(w,T)}return i.get(w)})}return{WcsCommitment:hr,WcsPlanType:dr,WcsTerm:Er,resolveOfferSelectors:u}}var D=class extends HTMLElement{get isWcmsCommerce(){return!0}};K(D,"instance"),K(D,"promise",null);window.customElements.define(ee,D);async function qo(e,t){let n=M.init(e.env).module("service");n.debug("Activating:",e);let r={price:{}},i=Object.freeze(wr(e));try{r.price=await Yn(i)}catch(l){n.warn("Price literals were not fetched:",l)}let o={checkout:new Set,price:new Set},s=document.createElement(ee),a={literals:r,providers:o,settings:i};return D.instance=Object.defineProperties(s,Object.getOwnPropertyDescriptors({...Wn(a,t),...Xn(a),...Bn(a),...$n(a),...Pr,Log:M,get defaults(){return x},get literals(){return r},get log(){return M},get providers(){return{checkout(l){return o.checkout.add(l),()=>o.checkout.delete(l)},price(l){return o.price.add(l),()=>o.price.delete(l)}}},get settings(){return i}})),n.debug("Activated:",{literals:r,settings:i,element:s}),document.head.append(s),ie(()=>{let l=new CustomEvent(me,{bubbles:!0,cancelable:!1,detail:D.instance});D.instance.dispatchEvent(l)}),D.instance}function qn(){document.head.querySelector(ee)?.remove(),D.promise=null,M.reset()}function zo(e,t){if(re(e)){let n=re(t)?t():{};return n.force&&qn(),D.promise??(D.promise=qo(e(),n))}return D.promise?D.promise:new Promise(n=>{let r=i=>{n(i.detail)};document.head.addEventListener(me,r,{once:!0})})}export{St as CheckoutLink,Y as CheckoutWorkflow,B as CheckoutWorkflowStep,x as Defaults,vt as InlinePrice,he as Landscape,M as Log,ee as TAG_NAME_SERVICE,hr as WcsCommitment,dr as WcsPlanType,Er as WcsTerm,$e as applyPlanType,Dn as getLocaleSettings,wr as getSettings,zo as init,qn as reset}; -//# sourceMappingURL=commerce.js.map +`,ce.MISSING_INTL_API,o);var O=n.getPluralRules(t,{type:l.pluralType}).select(f-(l.offset||0));L=l.options[O]||l.options.other}if(!L)throw new ar(l.value,f,Object.keys(l.options),o);s.push.apply(s,Re(L.value,t,n,r,i,f-(l.offset||0)));continue}}return na(s)}function aa(e,t){return t?E(E(E({},e||{}),t||{}),Object.keys(e).reduce(function(n,r){return n[r]=E(E({},e[r]),t[r]||{}),n},{})):e}function oa(e,t){return t?Object.keys(e).reduce(function(n,r){return n[r]=aa(e[r],t[r]),n},E({},e)):e}function or(e){return{create:function(){return{get:function(t){return e[t]},set:function(t,n){e[t]=n}}}}}function sa(e){return e===void 0&&(e={number:{},dateTime:{},pluralRules:{}}),{getNumberFormat:we(function(){for(var t,n=[],r=0;r0?e.substring(0,r):"";let i=En(e.split("").reverse().join("")),a=n-i,o=e.substring(a,a+1),s=a+(o==="."||o===","?1:0);t.suffix=i>0?e.substring(s,n):"",t.mask=e.substring(r,s),t.maskHasNegativeSign=t.mask.charAt(0)==="-",t.maskHasPositiveSign=t.mask.charAt(0)==="+";let c=t.mask.match(ca);return t.decimal=c&&c[c.length-1]||".",t.separator=c&&c[1]&&c[0]||",",c=t.mask.split(t.decimal),t.integer=c[0],t.fraction=c[1],t}function fa(e,t,n){let r=!1,i={value:e};e<0&&(r=!0,i.value=-i.value),i.sign=r?"-":"",i.value=Number(i.value).toFixed(t.fraction&&t.fraction.length),i.value=Number(i.value).toString();let a=t.fraction&&t.fraction.lastIndexOf("0"),[o="0",s=""]=i.value.split(".");return(!s||s&&s.length<=a)&&(s=a<0?"":(+("0."+s)).toFixed(a+1).replace("0.","")),i.integer=o,i.fraction=s,pa(i,t),(i.result==="0"||i.result==="")&&(r=!1,i.sign=""),!r&&t.maskHasPositiveSign?i.sign="+":r&&t.maskHasPositiveSign?i.sign="-":r&&(i.sign=n&&n.enforceMaskSign&&!t.maskHasNegativeSign?"":"-"),i}function pa(e,t){e.result="";let n=t.integer.split(t.separator),r=n.join(""),i=r&&r.indexOf("0");if(i>-1)for(;e.integer.lengthMath.round(e*20)/20},sr=(e,t)=>({accept:e,round:t}),da=[sr(({divisor:e,price:t})=>t%e==0,({divisor:e,price:t})=>t/e),sr(({usePrecision:e})=>e,({divisor:e,price:t})=>Math.ceil(Math.floor(t*1e4/e)/100)/100),sr(()=>!0,({divisor:e,price:t})=>Math.ceil(Math.floor(t*100/e)/100))],lr={[C.YEAR]:{[N.MONTHLY]:De.MONTH,[N.ANNUAL]:De.YEAR},[C.MONTH]:{[N.MONTHLY]:De.MONTH}},Sa=(e,t)=>e.indexOf(`'${t}'`)===0,Ea=(e,t=!0)=>{let n=e.replace(/'.*?'/,"").trim(),r=Pn(n);return!!r?t||(n=n.replace(/[,\.]0+/,r)):n=n.replace(/\s?(#.*0)(?!\s)?/,"$&"+ba(e)),n},xa=e=>{let t=ga(e),n=Sa(e,t),r=e.replace(/'.*?'/,""),i=Ln.test(r)||vn.test(r);return{currencySymbol:t,isCurrencyFirst:n,hasCurrencySpace:i}},yn=e=>e.replace(Ln,gn).replace(vn,gn),ba=e=>e.match(/#(.?)#/)?.[1]===bn?ma:bn,ga=e=>e.match(/'(.*?)'/)?.[1]??"",Pn=e=>e.match(/0(.?)0/)?.[1]??"";function it({formatString:e,price:t,usePrecision:n,isIndianPrice:r=!1},i,a=o=>o){let{currencySymbol:o,isCurrencyFirst:s,hasCurrencySpace:c}=xa(e),u=n?Pn(e):"",l=Ea(e,n),p=n?2:0,f=a(t,{currencySymbol:o}),m=r?f.toLocaleString("hi-IN",{minimumFractionDigits:p,maximumFractionDigits:p}):xn(l,f),T=n?m.lastIndexOf(u):m.length,x=m.substring(0,T),b=m.substring(T+1);return{accessiblePrice:e.replace(/'.*?'/,"SYMBOL").replace(/#.*0/,m).replace(/SYMBOL/,o),currencySymbol:o,decimals:b,decimalsDelimiter:u,hasCurrencySpace:c,integer:x,isCurrencyFirst:s,recurrenceTerm:i}}var _n=e=>{let{commitment:t,term:n,usePrecision:r}=e,i=Ta[n]??1;return it(e,i>1?De.MONTH:lr[t]?.[n],(a,{currencySymbol:o})=>{let s={divisor:i,price:a,usePrecision:r},{round:c}=da.find(({accept:l})=>l(s));if(!c)throw new Error(`Missing rounding rule for: ${JSON.stringify(s)}`);return(Aa[o]??(l=>l))(c(s))})},In=({commitment:e,term:t,...n})=>it(n,lr[e]?.[t]),Nn=e=>{let{commitment:t,term:n}=e;return t===C.YEAR&&n===N.MONTHLY?it(e,De.YEAR,r=>r*12):it(e,lr[t]?.[n])};var La={recurrenceLabel:"{recurrenceTerm, select, MONTH {/mo} YEAR {/yr} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {per month} YEAR {per year} other {}}",perUnitLabel:"{perUnit, select, LICENSE {per license} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {per license} other {}}",freeLabel:"Free",freeAriaLabel:"Free",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"Alternatively at {alternativePrice}",strikethroughAriaLabel:"Regularly at {strikethroughPrice}"},va=Xr("ConsonantTemplates/price"),ya=/<.+?>/g,G={container:"price",containerOptical:"price-optical",containerStrikethrough:"price-strikethrough",containerAnnual:"price-annual",disabled:"disabled",currencySpace:"price-currency-space",currencySymbol:"price-currency-symbol",decimals:"price-decimals",decimalsDelimiter:"price-decimals-delimiter",integer:"price-integer",recurrence:"price-recurrence",taxInclusivity:"price-tax-inclusivity",unitType:"price-unit-type"},ue={perUnitLabel:"perUnitLabel",perUnitAriaLabel:"perUnitAriaLabel",recurrenceLabel:"recurrenceLabel",recurrenceAriaLabel:"recurrenceAriaLabel",taxExclusiveLabel:"taxExclusiveLabel",taxInclusiveLabel:"taxInclusiveLabel",strikethroughAriaLabel:"strikethroughAriaLabel"},Pa="TAX_EXCLUSIVE",_a=e=>Fr(e)?Object.entries(e).filter(([,t])=>Se(t)||je(t)||t===!0).reduce((t,[n,r])=>t+` ${n}${r===!0?"":'="'+Gr(r)+'"'}`,""):"",K=(e,t,n,r=!1)=>`${r?yn(t):t??""}`;function Ia(e,{accessibleLabel:t,currencySymbol:n,decimals:r,decimalsDelimiter:i,hasCurrencySpace:a,integer:o,isCurrencyFirst:s,recurrenceLabel:c,perUnitLabel:u,taxInclusivityLabel:l},p={}){let f=K(G.currencySymbol,n),m=K(G.currencySpace,a?" ":""),T="";return s&&(T+=f+m),T+=K(G.integer,o),T+=K(G.decimalsDelimiter,i),T+=K(G.decimals,r),s||(T+=m+f),T+=K(G.recurrence,c,null,!0),T+=K(G.unitType,u,null,!0),T+=K(G.taxInclusivity,l,!0),K(e,T,{...p,"aria-label":t})}var fe=({displayOptical:e=!1,displayStrikethrough:t=!1,displayAnnual:n=!1}={})=>({country:r,displayFormatted:i=!0,displayRecurrence:a=!0,displayPerUnit:o=!1,displayTax:s=!1,language:c,literals:u={}}={},{commitment:l,formatString:p,price:f,priceWithoutDiscount:m,taxDisplay:T,taxTerm:x,term:b,usePrecision:P}={},_={})=>{Object.entries({country:r,formatString:p,language:c,price:f}).forEach(([J,yt])=>{if(yt==null)throw new Error(`Argument "${J}" is missing`)});let L={...La,...u},O=`${c.toLowerCase()}-${r.toUpperCase()}`;function v(J,yt){let Pt=L[J];if(Pt==null)return"";try{return new Sn(Pt.replace(ya,""),O).format(yt)}catch{return va.error("Failed to format literal:",Pt),""}}let I=t&&m?m:f,R=e?_n:In;n&&(R=Nn);let{accessiblePrice:F,recurrenceTerm:q,...te}=R({commitment:l,formatString:p,term:b,price:e?f:I,usePrecision:P,isIndianPrice:r==="IN"}),X=F,ae="";if(g(a)&&q){let J=v(ue.recurrenceAriaLabel,{recurrenceTerm:q});J&&(X+=" "+J),ae=v(ue.recurrenceLabel,{recurrenceTerm:q})}let oe="";if(g(o)){oe=v(ue.perUnitLabel,{perUnit:"LICENSE"});let J=v(ue.perUnitAriaLabel,{perUnit:"LICENSE"});J&&(X+=" "+J)}let Z="";g(s)&&x&&(Z=v(T===Pa?ue.taxExclusiveLabel:ue.taxInclusiveLabel,{taxTerm:x}),Z&&(X+=" "+Z)),t&&(X=v(ue.strikethroughAriaLabel,{strikethroughPrice:X}));let Y=G.container;if(e&&(Y+=" "+G.containerOptical),t&&(Y+=" "+G.containerStrikethrough),n&&(Y+=" "+G.containerAnnual),g(i))return Ia(Y,{...te,accessibleLabel:X,recurrenceLabel:ae,perUnitLabel:oe,taxInclusivityLabel:Z},_);let{currencySymbol:Te,decimals:He,decimalsDelimiter:Fe,hasCurrencySpace:_e,integer:vt,isCurrencyFirst:qn}=te,Ae=[vt,Fe,He];qn?(Ae.unshift(_e?"\xA0":""),Ae.unshift(Te)):(Ae.push(_e?"\xA0":""),Ae.push(Te)),Ae.push(ae,oe,Z);let Zn=Ae.join("");return K(Y,Zn,_)},Vn=()=>(e,t,n)=>{let i=(e.displayOldPrice===void 0||g(e.displayOldPrice))&&t.priceWithoutDiscount&&t.priceWithoutDiscount!=t.price;return`${fe()(e,t,n)}${i?" "+fe({displayStrikethrough:!0})(e,t,n):""}`};var cr=fe(),ur=Vn(),fr=fe({displayOptical:!0}),pr=fe({displayStrikethrough:!0}),hr=fe({displayAnnual:!0});var Na=(e,t)=>{if(!(!xe(e)||!xe(t)))return Math.floor((t-e)/t*100)},On=()=>(e,t,n)=>{let{price:r,priceWithoutDiscount:i}=t,a=Na(r,i);return a===void 0?'':`${a}%`};var mr=On();var{freeze:Ue}=Object,z=Ue({...se}),j=Ue({...H}),pe={STAGE:"STAGE",PRODUCTION:"PRODUCTION",LOCAL:"LOCAL"},Tr=Ue({...C}),Ar=Ue({...Kr}),dr=Ue({...N});var vr={};ai(vr,{CLASS_NAME_FAILED:()=>at,CLASS_NAME_PENDING:()=>ot,CLASS_NAME_RESOLVED:()=>st,ERROR_MESSAGE_BAD_REQUEST:()=>lt,ERROR_MESSAGE_MISSING_LITERALS_URL:()=>Va,ERROR_MESSAGE_OFFER_NOT_FOUND:()=>Sr,EVENT_TYPE_ERROR:()=>Oa,EVENT_TYPE_FAILED:()=>ct,EVENT_TYPE_PENDING:()=>ut,EVENT_TYPE_READY:()=>Le,EVENT_TYPE_RESOLVED:()=>ft,LOG_NAMESPACE:()=>Er,Landscape:()=>he,PARAM_AOS_API_KEY:()=>Ca,PARAM_ENV:()=>xr,PARAM_LANDSCAPE:()=>br,PARAM_WCS_API_KEY:()=>wa,STATE_FAILED:()=>W,STATE_PENDING:()=>B,STATE_RESOLVED:()=>$,TAG_NAME_SERVICE:()=>ee,WCS_PROD_URL:()=>gr,WCS_STAGE_URL:()=>Lr});var at="placeholder-failed",ot="placeholder-pending",st="placeholder-resolved",lt="Bad WCS request",Sr="Commerce offer not found",Va="Literals URL not provided",Oa="wcms:commerce:error",ct="wcms:placeholder:failed",ut="wcms:placeholder:pending",Le="wcms:commerce:ready",ft="wcms:placeholder:resolved",Er="wcms/commerce",xr="commerce.env",br="commerce.landscape",Ca="commerce.aosKey",wa="commerce.wcsKey",gr="https://www.adobe.com/web_commerce_artifact",Lr="https://www.stage.adobe.com/web_commerce_artifact_stage",W="failed",B="pending",$="resolved",ee="wcms-commerce",he={DRAFT:"DRAFT",PUBLISHED:"PUBLISHED"};var yr={clientId:"merch-at-scale",delimiter:"\xB6",ignoredProperties:["analytics","literals"],serializableTypes:["Array","Object"],sampleRate:30,tags:"consumer=milo/commerce"},Cn=new Set,ka=e=>e instanceof Error||typeof e.originatingRequest=="string";function wn(e){if(e==null)return;let t=typeof e;if(t==="function"){let{name:n}=e;return n?`${t} ${n}`:t}if(t==="object"){if(e instanceof Error)return e.message;if(typeof e.originatingRequest=="string"){let{message:r,originatingRequest:i,status:a}=e;return[r,a,i].filter(o=>o).join(" ")}let n=e[Symbol.toStringTag]??Object.getPrototypeOf(e).constructor.name;if(!yr.serializableTypes.includes(n))return n}return e}function Ra(e,t){if(!yr.ignoredProperties.includes(e))return wn(t)}var Pr={append(e){let{delimiter:t,sampleRate:n,tags:r,clientId:i}=yr,{message:a,params:o}=e,s=[],c=a,u=[];o.forEach(f=>{f!=null&&(ka(f)?s:u).push(f)}),s.length&&(c+=" ",c+=s.map(wn).join(" "));let{pathname:l,search:p}=window.location;c+=`${t}page=`,c+=l+p,u.length&&(c+=`${t}facts=`,c+=JSON.stringify(u,Ra)),Cn.has(c)||(Cn.add(c),window.lana?.log(c,{sampleRate:n,tags:r,clientId:i}))}};var S=Object.freeze({checkoutClientId:"adobe_com",checkoutWorkflow:z.V3,checkoutWorkflowStep:j.EMAIL,country:"US",displayOldPrice:!0,displayPerUnit:!1,displayRecurrence:!0,displayTax:!1,env:pe.PRODUCTION,forceTaxExclusive:!1,language:"en",entitlement:!1,extraOptions:{},modal:!1,promotionCode:"",quantity:1,wcsApiKey:"wcms-commerce-ims-ro-user-milo",wcsBufferDelay:1,wcsURL:"https://www.adobe.com/web_commerce_artifact",landscape:he.PUBLISHED,wcsBufferLimit:1});function kn(e,{once:t=!1}={}){let n=null;function r(){let i=document.querySelector(ee);i!==n&&(n=i,i&&e(i))}return document.addEventListener(Le,r,{once:t}),ie(r),()=>document.removeEventListener(Le,r)}function Me(e,{country:t,forceTaxExclusive:n,perpetual:r}){let i;if(e.length<2)i=e;else{let a=t==="GB"||r?"EN":"MULT",[o,s]=e;i=[o.language===a?o:s]}return n&&(i=i.map(jt)),i}var ie=e=>window.setTimeout(e);function ve(e,t=1){if(e==null)return[t];let n=(Array.isArray(e)?e:String(e).split(",")).map(ge).filter(xe);return n.length||(n=[t]),n}function pt(e){return e==null?[]:(Array.isArray(e)?e:String(e).split(",")).filter(Gt)}function D(){return window.customElements.get(ee)?.instance}var Da="en_US",h={ar:"AR_es",be_en:"BE_en",be_fr:"BE_fr",be_nl:"BE_nl",br:"BR_pt",ca:"CA_en",ch_de:"CH_de",ch_fr:"CH_fr",ch_it:"CH_it",cl:"CL_es",co:"CO_es",la:"DO_es",mx:"MX_es",pe:"PE_es",africa:"MU_en",dk:"DK_da",de:"DE_de",ee:"EE_et",eg_ar:"EG_ar",eg_en:"EG_en",es:"ES_es",fr:"FR_fr",gr_el:"GR_el",gr_en:"GR_en",ie:"IE_en",il_he:"IL_iw",it:"IT_it",lv:"LV_lv",lt:"LT_lt",lu_de:"LU_de",lu_en:"LU_en",lu_fr:"LU_fr",my_en:"MY_en",my_ms:"MY_ms",hu:"HU_hu",mt:"MT_en",mena_en:"DZ_en",mena_ar:"DZ_ar",nl:"NL_nl",no:"NO_nb",pl:"PL_pl",pt:"PT_pt",ro:"RO_ro",si:"SI_sl",sk:"SK_sk",fi:"FI_fi",se:"SE_sv",tr:"TR_tr",uk:"GB_en",at:"AT_de",cz:"CZ_cs",bg:"BG_bg",ru:"RU_ru",ua:"UA_uk",au:"AU_en",in_en:"IN_en",in_hi:"IN_hi",id_en:"ID_en",id_id:"ID_in",nz:"NZ_en",sa_ar:"SA_ar",sa_en:"SA_en",sg:"SG_en",cn:"CN_zh-Hans",tw:"TW_zh-Hant",hk_zh:"HK_zh-hant",jp:"JP_ja",kr:"KR_ko",za:"ZA_en",ng:"NG_en",cr:"CR_es",ec:"EC_es",pr:"US_es",gt:"GT_es",cis_en:"AZ_en",cis_ru:"AZ_ru",sea:"SG_en",th_en:"TH_en",th_th:"TH_th"},ht=Object.freeze({LOCAL:"local",PROD:"prod",STAGE:"stage"});function Rn({locale:e={}}={}){if(!e.prefix)return{country:S.country,language:S.language,locale:Da};let t=e.prefix.replace("/","")??"",[n=S.country,r=S.language]=(h[t]??t).split("_",2);return n=n.toUpperCase(),r=r.toLowerCase(),{country:n,language:r,locale:`${r}_${n}`}}function _r(e={}){let{commerce:t={},locale:n=void 0}=e,r=pe.PRODUCTION,i=gr,a=["local","stage"].includes(e.env?.name),o=V(xr,t,{metadata:!1})?.toLowerCase()==="stage";a&&o&&(r=pe.STAGE,i=Lr);let s=V("checkoutClientId",t)??S.checkoutClientId,c=ne(V("checkoutWorkflow",t),z,S.checkoutWorkflow),u=j.CHECKOUT;c===z.V3&&(u=ne(V("checkoutWorkflowStep",t),j,S.checkoutWorkflowStep));let l=g(V("displayOldPrice",t),S.displayOldPrice),p=g(V("displayPerUnit",t),S.displayPerUnit),f=g(V("displayRecurrence",t),S.displayRecurrence),m=g(V("displayTax",t),S.displayTax),T=g(V("entitlement",t),S.entitlement),x=g(V("modal",t),S.modal),b=g(V("forceTaxExclusive",t),S.forceTaxExclusive),P=V("promotionCode",t)??S.promotionCode,_=ve(V("quantity",t)),L=V("wcsApiKey",t)??S.wcsApiKey,O=e.env?.name===ht.PROD?he.PUBLISHED:ne(V(br,t),he,S.landscape),v=ge(V("wcsBufferDelay",t),S.wcsBufferDelay),I=ge(V("wcsBufferLimit",t),S.wcsBufferLimit);return{...Rn({locale:n}),displayOldPrice:l,checkoutClientId:s,checkoutWorkflow:c,checkoutWorkflowStep:u,displayPerUnit:p,displayRecurrence:f,displayTax:m,entitlement:T,extraOptions:S.extraOptions,modal:x,env:r,forceTaxExclusive:b,promotionCode:P,quantity:_,wcsApiKey:L,wcsBufferDelay:v,wcsBufferLimit:I,wcsURL:i,landscape:O}}var Un="debug",Ua="error",Ma="info",Ga="warn",Ha=Date.now(),Ir=new Set,Nr=new Set,Dn=new Map,Ge=Object.freeze({DEBUG:Un,ERROR:Ua,INFO:Ma,WARN:Ga}),Mn={append({level:e,message:t,params:n,timestamp:r,source:i}){console[e](`${r}ms [${i}] %c${t}`,"font-weight: bold;",...n)}},Gn={filter:({level:e})=>e!==Un},Fa={filter:()=>!1};function Xa(e,t,n,r,i){return{level:e,message:t,namespace:n,get params(){if(r.length===1){let[a]=r;re(a)&&(r=a(),Array.isArray(r)||(r=[r]))}return r},source:i,timestamp:Date.now()-Ha}}function Ya(e){[...Nr].every(t=>t(e))&&Ir.forEach(t=>t(e))}function Hn(e){let t=(Dn.get(e)??0)+1;Dn.set(e,t);let n=`${e} #${t}`,r=a=>(o,...s)=>Ya(Xa(a,o,e,s,n)),i=Object.seal({id:n,namespace:e,module(a){return Hn(`${i.namespace}/${a}`)},debug:r(Ge.DEBUG),error:r(Ge.ERROR),info:r(Ge.INFO),warn:r(Ge.WARN)});return i}function mt(...e){e.forEach(t=>{let{append:n,filter:r}=t;re(r)?Nr.add(r):re(n)&&Ir.add(n)})}function Ka(e={}){let{name:t}=e,n=g(V("commerce.debug",{search:!0,storage:!0}),t===ht.LOCAL);return mt(n?Mn:Gn),t===ht.PROD&&mt(Pr),k}function za(){Ir.clear(),Nr.clear()}var k={...Hn(Er),Level:Ge,Plugins:{consoleAppender:Mn,debugFilter:Gn,quietFilter:Fa,lanaAppender:Pr},init:Ka,reset:za,use:mt};var ja={CLASS_NAME_FAILED:at,CLASS_NAME_PENDING:ot,CLASS_NAME_RESOLVED:st,EVENT_TYPE_FAILED:ct,EVENT_TYPE_PENDING:ut,EVENT_TYPE_RESOLVED:ft,STATE_FAILED:W,STATE_PENDING:B,STATE_RESOLVED:$},Wa={[W]:at,[B]:ot,[$]:st},Ba={[W]:ct,[B]:ut,[$]:ft},dt=new WeakMap;function U(e){if(!dt.has(e)){let t=k.module(e.constructor.is);dt.set(e,{changes:new Map,connected:!1,dispose:Ee,error:void 0,log:t,options:void 0,promises:[],state:B,timer:null,value:void 0,version:0})}return dt.get(e)}function Tt(e){let t=U(e),{error:n,promises:r,state:i}=t;(i===$||i===W)&&(t.promises=[],i===$?r.forEach(({resolve:a})=>a(e)):i===W&&r.forEach(({reject:a})=>a(n))),e.dispatchEvent(new CustomEvent(Ba[i],{bubbles:!0}))}function At(e){let t=dt.get(e);[W,B,$].forEach(n=>{e.classList.toggle(Wa[n],n===t.state)})}var $a={get error(){return U(this).error},get log(){return U(this).log},get options(){return U(this).options},get state(){return U(this).state},get value(){return U(this).value},attributeChangedCallback(e,t,n){U(this).changes.set(e,n),this.requestUpdate()},connectedCallback(){U(this).dispose=kn(()=>this.requestUpdate(!0))},disconnectedCallback(){let e=U(this);e.connected&&(e.connected=!1,e.log.debug("Disconnected:",{element:this})),e.dispose(),e.dispose=Ee},onceSettled(){let{error:e,promises:t,state:n}=U(this);return $===n?Promise.resolve(this):W===n?Promise.reject(e):new Promise((r,i)=>{t.push({resolve:r,reject:i})})},toggleResolved(e,t,n){let r=U(this);return e!==r.version?!1:(n!==void 0&&(r.options=n),r.state=$,r.value=t,At(this),this.log.debug("Resolved:",{element:this,value:t}),ie(()=>Tt(this)),!0)},toggleFailed(e,t,n){let r=U(this);return e!==r.version?!1:(n!==void 0&&(r.options=n),r.error=t,r.state=W,At(this),r.log.error("Failed:",{element:this,error:t}),ie(()=>Tt(this)),!0)},togglePending(e){let t=U(this);return t.version++,e&&(t.options=e),t.state=B,At(this),ie(()=>Tt(this)),t.version},requestUpdate(e=!1){if(!this.isConnected||!D())return;let t=U(this);if(t.timer)return;let{error:n,options:r,state:i,value:a,version:o}=t;t.state=B,t.timer=ie(async()=>{t.timer=null;let s=null;if(t.changes.size&&(s=Object.fromEntries(t.changes.entries()),t.changes.clear()),t.connected?t.log.debug("Updated:",{element:this,changes:s}):(t.connected=!0,t.log.debug("Connected:",{element:this,changes:s})),s||e)try{await this.render?.()===!1&&t.state===B&&t.version===o&&(t.state=i,t.error=n,t.value=a,At(this),Tt(this))}catch(c){this.toggleFailed(t.version,c,r)}})}};function Fn(e={}){return Object.entries(e).forEach(([t,n])=>{(n==null||n===""||n?.length===0)&&delete e[t]}),e}function St(e,t={}){let{tag:n,is:r}=e,i=document.createElement(n,{is:r});return i.setAttribute("is",r),Object.assign(i.dataset,Fn(t)),i}function Et(e){let{tag:t,is:n,prototype:r}=e,i=window.customElements.get(n);return i||(Object.defineProperties(r,Object.getOwnPropertyDescriptors($a)),i=Object.defineProperties(e,Object.getOwnPropertyDescriptors(ja)),window.customElements.define(n,i,{extends:t})),i}function xt(e,t=document.body){return Array.from(t?.querySelectorAll(`${e.tag}[is="${e.is}"]`)??[])}function bt(e,t={}){return e instanceof HTMLElement?(Object.assign(e.dataset,Fn(t)),e):null}var qa="download",Za="upgrade",me,ye=class ye extends HTMLAnchorElement{constructor(){super();kr(this,me,void 0);this.addEventListener("click",this.clickHandler)}static get observedAttributes(){return["data-checkout-workflow","data-checkout-workflow-step","data-extra-options","data-ims-country","data-perpetual","data-promotion-code","data-quantity","data-template","data-wcs-osi","data-entitlement","data-upgrade","data-modal"]}static createCheckoutLink(n={},r=""){let i=D();if(!i)return null;let{checkoutMarketSegment:a,checkoutWorkflow:o,checkoutWorkflowStep:s,entitlement:c,upgrade:u,modal:l,perpetual:p,promotionCode:f,quantity:m,wcsOsi:T,extraOptions:x}=i.collectCheckoutOptions(n),b=St(ye,{checkoutMarketSegment:a,checkoutWorkflow:o,checkoutWorkflowStep:s,entitlement:c,upgrade:u,modal:l,perpetual:p,promotionCode:f,quantity:m,wcsOsi:T,extraOptions:x});return r&&(b.innerHTML=`${r}`),b}static getCheckoutLinks(n){return xt(ye,n)}get isCheckoutLink(){return!0}get placeholder(){return this}clickHandler(n){var r;(r=_t(this,me))==null||r.call(this,n)}async render(n={}){if(!this.isConnected)return!1;let r=D();if(!r)return!1;this.dataset.imsCountry||r.imsCountryPromise.then(l=>{l&&(this.dataset.imsCountry=l)},Ee);let i=r.collectCheckoutOptions(n,this.placeholder);if(!i.wcsOsi.length)return!1;let a;try{a=JSON.parse(i.extraOptions??"{}")}catch(l){this.placeholder.log.error("cannot parse exta checkout options",l)}let o=this.placeholder.togglePending(i);this.href="";let s=r.resolveOfferSelectors(i),c=await Promise.all(s);c=c.map(l=>Me(l,i));let u=await r.buildCheckoutAction(c.flat(),{...a,...i});return this.renderOffers(c.flat(),i,{},u,o)}renderOffers(n,r,i={},a=void 0,o=void 0){if(!this.isConnected)return!1;let s=D();if(!s)return!1;if(r={...JSON.parse(this.placeholder.dataset.extraOptions??"null"),...r,...i},o??(o=this.placeholder.togglePending(r)),_t(this,me)&&It(this,me,void 0),a){this.classList.remove(qa,Za),this.placeholder.toggleResolved(o,n,r);let{url:u,text:l,className:p,handler:f}=a;return u&&(this.href=u),l&&(this.firstElementChild.innerHTML=l),p&&this.classList.add(...p.split(" ")),f&&(this.setAttribute("href","#"),It(this,me,f.bind(this))),!0}else if(n.length){if(this.placeholder.toggleResolved(o,n,r)){let u=s.buildCheckoutURL(n,r);return this.setAttribute("href",u),!0}}else{let u=new Error(`Not provided: ${r?.wcsOsi??"-"}`);if(this.placeholder.toggleFailed(o,u,r))return this.setAttribute("href","#"),!0}return!1}updateOptions(n={}){let r=D();if(!r)return!1;let{checkoutMarketSegment:i,checkoutWorkflow:a,checkoutWorkflowStep:o,entitlement:s,upgrade:c,modal:u,perpetual:l,promotionCode:p,quantity:f,wcsOsi:m}=r.collectCheckoutOptions(n);return bt(this,{checkoutMarketSegment:i,checkoutWorkflow:a,checkoutWorkflowStep:o,entitlement:s,upgrade:c,modal:u,perpetual:l,promotionCode:p,quantity:f,wcsOsi:m}),!0}};me=new WeakMap,Q(ye,"is","checkout-link"),Q(ye,"tag","a");var Vr=ye,gt=Et(Vr);var Xn=[h.uk,h.au,h.fr,h.at,h.be_en,h.be_fr,h.be_nl,h.bg,h.ch_de,h.ch_fr,h.ch_it,h.cz,h.de,h.dk,h.ee,h.eg_ar,h.eg_en,h.es,h.fi,h.fr,h.gr_el,h.gr_en,h.hu,h.ie,h.it,h.lu_de,h.lu_en,h.lu_fr,h.nl,h.no,h.pl,h.pt,h.ro,h.se,h.si,h.sk,h.tr,h.ua,h.id_en,h.id_id,h.in_en,h.in_hi,h.jp,h.my_en,h.my_ms,h.nz,h.th_en,h.th_th],Ja={INDIVIDUAL_COM:[h.za,h.lt,h.lv,h.ng,h.sa_ar,h.sa_en,h.za,h.sg,h.kr],TEAM_COM:[h.za,h.lt,h.lv,h.ng,h.za,h.co,h.kr],INDIVIDUAL_EDU:[h.lt,h.lv,h.sa_en,h.sea],TEAM_EDU:[h.sea,h.kr]},Pe=class Pe extends HTMLSpanElement{static get observedAttributes(){return["data-display-old-price","data-display-per-unit","data-display-recurrence","data-display-tax","data-perpetual","data-promotion-code","data-tax-exclusive","data-template","data-wcs-osi"]}static createInlinePrice(t){let n=D();if(!n)return null;let{displayOldPrice:r,displayPerUnit:i,displayRecurrence:a,displayTax:o,forceTaxExclusive:s,perpetual:c,promotionCode:u,quantity:l,template:p,wcsOsi:f}=n.collectPriceOptions(t);return St(Pe,{displayOldPrice:r,displayPerUnit:i,displayRecurrence:a,displayTax:o,forceTaxExclusive:s,perpetual:c,promotionCode:u,quantity:l,template:p,wcsOsi:f})}static getInlinePrices(t){return xt(Pe,t)}get isInlinePrice(){return!0}get placeholder(){return this}resolveDisplayTaxForGeoAndSegment(t,n,r,i){let a=`${t}_${n}`;if(Xn.includes(t)||Xn.includes(a))return!0;let o=Ja[`${r}_${i}`];return o?!!(o.includes(t)||o.includes(a)):!1}async resolveDisplayTax(t,n){let[r]=await t.resolveOfferSelectors(n),i=Me(await r,n);if(i?.length){let{country:a,language:o}=n,s=i[0],[c=""]=s.marketSegments;return this.resolveDisplayTaxForGeoAndSegment(a,o,s.customerSegment,c)}}async render(t={}){if(!this.isConnected)return!1;let n=D();if(!n)return!1;let r=n.collectPriceOptions(t,this.placeholder);if(!r.wcsOsi.length)return!1;let i=this.placeholder.togglePending(r);this.innerHTML="";let[a]=n.resolveOfferSelectors(r);return this.renderOffers(Me(await a,r),r,i)}renderOffers(t,n={},r=void 0){if(!this.isConnected)return;let i=D();if(!i)return!1;let a=i.collectPriceOptions({...this.dataset,...n});if(r??(r=this.placeholder.togglePending(a)),t.length){if(this.placeholder.toggleResolved(r,t,a))return this.innerHTML=i.buildPriceHTML(t,a),!0}else{let o=new Error(`Not provided: ${a?.wcsOsi??"-"}`);if(this.placeholder.toggleFailed(r,o,a))return this.innerHTML="",!0}return!1}updateOptions(t){let n=D();if(!n)return!1;let{displayOldPrice:r,displayPerUnit:i,displayRecurrence:a,displayTax:o,forceTaxExclusive:s,perpetual:c,promotionCode:u,quantity:l,template:p,wcsOsi:f}=n.collectPriceOptions(t);return bt(this,{displayOldPrice:r,displayPerUnit:i,displayRecurrence:a,displayTax:o,forceTaxExclusive:s,perpetual:c,promotionCode:u,quantity:l,template:p,wcsOsi:f}),!0}};Q(Pe,"is","inline-price"),Q(Pe,"tag","span");var Or=Pe,Lt=Et(Or);function Yn({providers:e,settings:t},n){let r=k.module("checkout");function i(u,l){let{checkoutClientId:p,checkoutWorkflow:f,checkoutWorkflowStep:m,country:T,language:x,promotionCode:b,quantity:P}=t,{checkoutMarketSegment:_,checkoutWorkflow:L=f,checkoutWorkflowStep:O=m,imsCountry:v,country:I=v??T,language:R=x,quantity:F=P,entitlement:q,upgrade:te,modal:X,perpetual:ae,promotionCode:oe=b,wcsOsi:Z,extraOptions:Y,...Te}=Object.assign({},l?.dataset??{},u??{}),He=ne(L,z,S.checkoutWorkflow),Fe=j.CHECKOUT;He===z.V3&&(Fe=ne(O,j,S.checkoutWorkflowStep));let _e=be({...Te,extraOptions:Y,checkoutClientId:p,checkoutMarketSegment:_,country:I,quantity:ve(F,S.quantity),checkoutWorkflow:He,checkoutWorkflowStep:Fe,language:R,entitlement:g(q),upgrade:g(te),modal:g(X),perpetual:g(ae),promotionCode:Ve(oe).effectivePromoCode,wcsOsi:pt(Z)});if(l)for(let vt of e.checkout)vt(l,_e);return _e}async function a(u,l){let p=D(),f=await n.getCheckoutAction?.(u,l,p.imsSignedInPromise);return f||null}function o(u,l){if(!Array.isArray(u)||!u.length||!l)return"";let{env:p,landscape:f}=t,{checkoutClientId:m,checkoutMarketSegment:T,checkoutWorkflow:x,checkoutWorkflowStep:b,country:P,promotionCode:_,quantity:L,...O}=i(l),v=window.frameElement?"if":"fp",I={checkoutPromoCode:_,clientId:m,context:v,country:P,env:p,items:[],marketSegment:T,workflowStep:b,landscape:f,...O};if(u.length===1){let[{offerId:R,offerType:F,productArrangementCode:q}]=u,{marketSegments:[te]}=u[0];Object.assign(I,{marketSegment:te,offerType:F,productArrangementCode:q}),I.items.push(L[0]===1?{id:R}:{id:R,quantity:L[0]})}else I.items.push(...u.map(({offerId:R},F)=>({id:R,quantity:L[F]??S.quantity})));return wt(x,I)}let{createCheckoutLink:s,getCheckoutLinks:c}=gt;return{CheckoutLink:gt,CheckoutWorkflow:z,CheckoutWorkflowStep:j,buildCheckoutAction:a,buildCheckoutURL:o,collectCheckoutOptions:i,createCheckoutLink:s,getCheckoutLinks:c}}function Qa({interval:e=200,maxAttempts:t=25}={}){let n=k.module("ims");return new Promise(r=>{n.debug("Waing for IMS to be ready");let i=0;function a(){window.adobeIMS?.initialized?r():++i>t?(n.debug("Timeout"),r()):setTimeout(a,e)}a()})}function eo(e){return e.then(()=>window.adobeIMS?.isSignedInUser()??!1)}function to(e){let t=k.module("ims");return e.then(n=>n?window.adobeIMS.getProfile().then(({countryCode:r})=>(t.debug("Got user country:",r),r),r=>{t.error("Unable to get user country:",r)}):null)}function Kn({}){let e=Qa(),t=eo(e),n=to(t);return{imsReadyPromise:e,imsSignedInPromise:t,imsCountryPromise:n}}async function jn(e,t){let{data:n}=t||await Promise.resolve().then(()=>si(zn(),1));if(Array.isArray(n)){let r=a=>n.find(o=>ze(o.lang,a)),i=r(e.language)??r(S.language);if(i)return Object.freeze(i)}return{}}function Wn({literals:e,providers:t,settings:n}){function r(s,c){let{country:u,displayOldPrice:l,displayPerUnit:p,displayRecurrence:f,displayTax:m,forceTaxExclusive:T,language:x,promotionCode:b,quantity:P}=n,{displayOldPrice:_=l,displayPerUnit:L=p,displayRecurrence:O=f,displayTax:v=m,forceTaxExclusive:I=T,country:R=u,language:F=x,perpetual:q,promotionCode:te=b,quantity:X=P,template:ae,wcsOsi:oe,...Z}=Object.assign({},c?.dataset??{},s??{}),Y=be({...Z,country:R,displayOldPrice:g(_),displayPerUnit:g(L),displayRecurrence:g(O),displayTax:g(v),forceTaxExclusive:g(I),language:F,perpetual:g(q),promotionCode:Ve(te).effectivePromoCode,quantity:ve(X,S.quantity),template:ae,wcsOsi:pt(oe)});if(c)for(let Te of t.price)Te(c,Y);return Y}function i(s,c){if(!Array.isArray(s)||!s.length||!c)return"";let{template:u}=c,l;switch(u){case"discount":l=mr;break;case"strikethrough":l=pr;break;case"optical":l=fr;break;case"annual":l=hr;break;default:l=c.promotionCode?ur:cr}let p=r(c);p.literals=Object.assign({},e.price,be(c.literals??{}));let[f]=s;return f={...f,...f.priceDetails},l(p,f)}let{createInlinePrice:a,getInlinePrices:o}=Lt;return{InlinePrice:Lt,buildPriceHTML:i,collectPriceOptions:r,createInlinePrice:a,getInlinePrices:o}}function Bn({settings:e}){let t=k.module("wcs"),{env:n,wcsApiKey:r}=e,i=new Map,a=new Map,o;async function s(l,p,f=!0){let m=Sr;t.debug("Fetching:",l);try{l.offerSelectorIds=l.offerSelectorIds.sort();let T=new URL(e.wcsURL);T.searchParams.set("offer_selector_ids",l.offerSelectorIds.join(",")),T.searchParams.set("country",l.country),T.searchParams.set("locale",l.locale),T.searchParams.set("landscape",n===pe.STAGE?"ALL":e.landscape),T.searchParams.set("api_key",r),l.language&&T.searchParams.set("language",l.language),l.promotionCode&&T.searchParams.set("promotion_code",l.promotionCode),l.currency&&T.searchParams.set("currency",l.currency);let x=await fetch(T.toString(),{credentials:"omit"});if(x.ok){let b=await x.json();t.debug("Fetched:",l,b);let P=b.resolvedOffers??[];P=P.map(Be),p.forEach(({resolve:_},L)=>{let O=P.filter(({offerSelectorIds:v})=>v.includes(L)).flat();O.length&&(p.delete(L),_(O))})}else x.status===404&&l.offerSelectorIds.length>1?(t.debug("Multi-osi 404, fallback to fetch-by-one strategy"),await Promise.allSettled(l.offerSelectorIds.map(b=>s({...l,offerSelectorIds:[b]},p,!1)))):(m=lt,t.error(m,l))}catch(T){m=lt,t.error(m,l,T)}f&&p.size&&(t.debug("Missing:",{offerSelectorIds:[...p.keys()]}),p.forEach(T=>{T.reject(new Error(m))}))}function c(){clearTimeout(o);let l=[...a.values()];a.clear(),l.forEach(({options:p,promises:f})=>s(p,f))}function u({country:l,language:p,perpetual:f=!1,promotionCode:m="",wcsOsi:T=[]}){let x=`${p}_${l}`;l!=="GB"&&(p=f?"EN":"MULT");let b=[l,p,m].filter(P=>P).join("-").toLowerCase();return T.map(P=>{let _=`${P}-${b}`;if(!i.has(_)){let L=new Promise((O,v)=>{let I=a.get(b);if(!I){let R={country:l,locale:x,offerSelectorIds:[]};l!=="GB"&&(R.language=p),I={options:R,promises:new Map},a.set(b,I)}m&&(I.options.promotionCode=m),I.options.offerSelectorIds.push(P),I.promises.set(P,{resolve:O,reject:v}),I.options.offerSelectorIds.length>=e.wcsBufferLimit?c():(t.debug("Queued:",I.options),o||(o=setTimeout(c,e.wcsBufferDelay)))});i.set(_,L)}return i.get(_)})}return{WcsCommitment:Tr,WcsPlanType:Ar,WcsTerm:dr,resolveOfferSelectors:u}}var M=class extends HTMLElement{get isWcmsCommerce(){return!0}};Q(M,"instance"),Q(M,"promise",null);window.customElements.define(ee,M);async function no(e,t){let n=k.init(e.env).module("service");n.debug("Activating:",e);let r={price:{}},i=Object.freeze(_r(e));try{r.price=await jn(i,e.commerce.priceLiterals)}catch(c){n.warn("Price literals were not fetched:",c)}let a={checkout:new Set,price:new Set},o=document.createElement(ee),s={literals:r,providers:a,settings:i};return M.instance=Object.defineProperties(o,Object.getOwnPropertyDescriptors({...Yn(s,t),...Kn(s),...Wn(s),...Bn(s),...vr,Log:k,get defaults(){return S},get literals(){return r},get log(){return k},get providers(){return{checkout(c){return a.checkout.add(c),()=>a.checkout.delete(c)},price(c){return a.price.add(c),()=>a.price.delete(c)}}},get settings(){return i}})),n.debug("Activated:",{literals:r,settings:i,element:o}),document.head.append(o),ie(()=>{let c=new CustomEvent(Le,{bubbles:!0,cancelable:!1,detail:M.instance});M.instance.dispatchEvent(c)}),M.instance}function $n(){document.head.querySelector(ee)?.remove(),M.promise=null,k.reset()}function Cr(e,t){let n=re(e)?e():null,r=re(t)?t():{};return n&&(r.force&&$n(),no(n,r).then(i=>{Cr.resolve(i)})),M.promise??(M.promise=new Promise(i=>{Cr.resolve=i})),M.promise}export{gt as CheckoutLink,z as CheckoutWorkflow,j as CheckoutWorkflowStep,S as Defaults,Lt as InlinePrice,he as Landscape,k as Log,ee as TAG_NAME_SERVICE,Tr as WcsCommitment,Ar as WcsPlanType,dr as WcsTerm,Be as applyPlanType,Rn as getLocaleSettings,_r as getSettings,Cr as init,$n as reset}; diff --git a/libs/deps/mas/mas.js b/libs/deps/mas/mas.js index 6e761a8a42..4e5c80f3a9 100644 --- a/libs/deps/mas/mas.js +++ b/libs/deps/mas/mas.js @@ -1,4 +1,1725 @@ -var Nr=Object.defineProperty;var Cr=e=>{throw TypeError(e)};var ei=(e,t,n)=>t in e?Nr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var ti=(e,t)=>{for(var n in t)Nr(e,n,{get:t[n],enumerable:!0})};var J=(e,t,n)=>ei(e,typeof t!="symbol"?t+"":t,n),Rr=(e,t,n)=>t.has(e)||Cr("Cannot "+n);var Tt=(e,t,n)=>(Rr(e,t,"read from private field"),n?n.call(e):t.get(e)),Ir=(e,t,n)=>t.has(e)?Cr("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,n),bt=(e,t,n,r)=>(Rr(e,t,"write to private field"),r?r.call(e,n):t.set(e,n),n);var Le;(function(e){e.STAGE="STAGE",e.PRODUCTION="PRODUCTION",e.LOCAL="LOCAL"})(Le||(Le={}));var At;(function(e){e.STAGE="STAGE",e.PRODUCTION="PROD",e.LOCAL="LOCAL"})(At||(At={}));var Oe;(function(e){e.DRAFT="DRAFT",e.PUBLISHED="PUBLISHED"})(Oe||(Oe={}));var ae;(function(e){e.V2="UCv2",e.V3="UCv3"})(ae||(ae={}));var V;(function(e){e.CHECKOUT="checkout",e.CHECKOUT_EMAIL="checkout/email",e.SEGMENTATION="segmentation",e.BUNDLE="bundle",e.COMMITMENT="commitment",e.RECOMMENDATION="recommendation",e.EMAIL="email",e.PAYMENT="payment",e.CHANGE_PLAN_TEAM_PLANS="change-plan/team-upgrade/plans",e.CHANGE_PLAN_TEAM_PAYMENT="change-plan/team-upgrade/payment"})(V||(V={}));var wt=function(e){var t;return(t=ri.get(e))!==null&&t!==void 0?t:e},ri=new Map([["countrySpecific","cs"],["quantity","q"],["authCode","code"],["checkoutPromoCode","apc"],["rurl","rUrl"],["curl","cUrl"],["ctxrturl","ctxRtUrl"],["country","co"],["language","lang"],["clientId","cli"],["context","ctx"],["productArrangementCode","pa"],["offerType","ot"],["marketSegment","ms"]]);var Mr=function(e){var t=typeof Symbol=="function"&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&typeof e.length=="number")return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},Dr=function(e,t){var n=typeof Symbol=="function"&&e[Symbol.iterator];if(!n)return e;var r=n.call(e),i,o=[],s;try{for(;(t===void 0||t-- >0)&&!(i=r.next()).done;)o.push(i.value)}catch(a){s={error:a}}finally{try{i&&!i.done&&(n=r.return)&&n.call(r)}finally{if(s)throw s.error}}return o};function ge(e,t,n){var r,i;try{for(var o=Mr(Object.entries(e)),s=o.next();!s.done;s=o.next()){var a=Dr(s.value,2),l=a[0],u=a[1],c=wt(l);u!=null&&n.has(c)&&t.set(c,u)}}catch(p){r={error:p}}finally{try{s&&!s.done&&(i=o.return)&&i.call(o)}finally{if(r)throw r.error}}}function He(e){switch(e){case Le.PRODUCTION:return"https://commerce.adobe.com";default:return"https://commerce-stg.adobe.com"}}function We(e,t){var n,r;for(var i in e){var o=e[i];try{for(var s=(n=void 0,Mr(Object.entries(o))),a=s.next();!a.done;a=s.next()){var l=Dr(a.value,2),u=l[0],c=l[1];if(c!=null){var p=wt(u);t.set("items["+i+"]["+p+"]",c)}}}catch(f){n={error:f}}finally{try{a&&!a.done&&(r=s.return)&&r.call(s)}finally{if(n)throw n.error}}}}var ni=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var i=0,r=Object.getOwnPropertySymbols(e);i=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};function Ur(e){ai(e);var t=e.env,n=e.items,r=e.workflowStep,i=ni(e,["env","items","workflowStep"]),o=new URL(He(t));return o.pathname=r+"/",We(n,o.searchParams),ge(i,o.searchParams,oi),o.toString()}var oi=new Set(["cli","co","lang","ctx","cUrl","mv","nglwfdata","otac","promoid","rUrl","sdid","spint","trackingid","code","campaignid","appctxid"]),si=["env","workflowStep","clientId","country","items"];function ai(e){var t,n;try{for(var r=ii(si),i=r.next();!i.done;i=r.next()){var o=i.value;if(!e[o])throw new Error('Argument "checkoutData" is not valid, missing: '+o)}}catch(s){t={error:s}}finally{try{i&&!i.done&&(n=r.return)&&n.call(r)}finally{if(t)throw t.error}}return!0}var ci=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var i=0,r=Object.getOwnPropertySymbols(e);i=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},ui="p_draft_landscape",fi="/store/";function Ot(e){mi(e);var t=e.env,n=e.items,r=e.workflowStep,i=e.ms,o=e.marketSegment,s=e.ot,a=e.offerType,l=e.pa,u=e.productArrangementCode,c=e.landscape,p=ci(e,["env","items","workflowStep","ms","marketSegment","ot","offerType","pa","productArrangementCode","landscape"]),f={marketSegment:o??i,offerType:a??s,productArrangementCode:u??l},h=new URL(He(t));return h.pathname=""+fi+r,r!==V.SEGMENTATION&&r!==V.CHANGE_PLAN_TEAM_PLANS&&We(n,h.searchParams),r===V.SEGMENTATION&&ge(f,h.searchParams,Lt),ge(p,h.searchParams,Lt),c===Oe.DRAFT&&ge({af:ui},h.searchParams,Lt),h.toString()}var Lt=new Set(["af","ai","apc","appctxid","cli","co","csm","ctx","ctxRtUrl","DCWATC","dp","fr","gsp","ijt","lang","lo","mal","ms","mv","mv2","nglwfdata","ot","otac","pa","pcid","promoid","q","rf","sc","scl","sdid","sid","spint","svar","th","thm","trackingid","usid","workflowid","context.guid","so.ca","so.su","so.tr","so.va"]),pi=["env","workflowStep","clientId","country"];function mi(e){var t,n;try{for(var r=li(pi),i=r.next();!i.done;i=r.next()){var o=i.value;if(!e[o])throw new Error('Argument "checkoutData" is not valid, missing: '+o)}}catch(s){t={error:s}}finally{try{i&&!i.done&&(n=r.return)&&n.call(r)}finally{if(t)throw t.error}}if(e.workflowStep!==V.SEGMENTATION&&e.workflowStep!==V.CHANGE_PLAN_TEAM_PLANS&&!e.items)throw new Error('Argument "checkoutData" is not valid, missing: items');return!0}function Nt(e,t){switch(e){case ae.V2:return Ur(t);case ae.V3:return Ot(t);default:return console.warn("Unsupported CheckoutType, will use UCv3 as default. Given type: "+e),Ot(t)}}var Ct;(function(e){e.BASE="BASE",e.TRIAL="TRIAL",e.PROMOTION="PROMOTION"})(Ct||(Ct={}));var R;(function(e){e.MONTH="MONTH",e.YEAR="YEAR",e.TWO_YEARS="TWO_YEARS",e.THREE_YEARS="THREE_YEARS",e.PERPETUAL="PERPETUAL",e.TERM_LICENSE="TERM_LICENSE",e.ACCESS_PASS="ACCESS_PASS",e.THREE_MONTHS="THREE_MONTHS",e.SIX_MONTHS="SIX_MONTHS"})(R||(R={}));var O;(function(e){e.ANNUAL="ANNUAL",e.MONTHLY="MONTHLY",e.TWO_YEARS="TWO_YEARS",e.THREE_YEARS="THREE_YEARS",e.P1D="P1D",e.P1Y="P1Y",e.P3Y="P3Y",e.P10Y="P10Y",e.P15Y="P15Y",e.P3D="P3D",e.P7D="P7D",e.P30D="P30D",e.HALF_YEARLY="HALF_YEARLY",e.QUARTERLY="QUARTERLY"})(O||(O={}));var Rt;(function(e){e.INDIVIDUAL="INDIVIDUAL",e.TEAM="TEAM",e.ENTERPRISE="ENTERPRISE"})(Rt||(Rt={}));var It;(function(e){e.COM="COM",e.EDU="EDU",e.GOV="GOV"})(It||(It={}));var Mt;(function(e){e.DIRECT="DIRECT",e.INDIRECT="INDIRECT"})(Mt||(Mt={}));var Dt;(function(e){e.ENTERPRISE_PRODUCT="ENTERPRISE_PRODUCT",e.ETLA="ETLA",e.RETAIL="RETAIL",e.VIP="VIP",e.VIPMP="VIPMP",e.FREE="FREE"})(Dt||(Dt={}));var kr="tacocat.js";var Xe=(e,t)=>String(e??"").toLowerCase()==String(t??"").toLowerCase(),Gr=e=>`${e??""}`.replace(/[&<>'"]/g,t=>({"&":"&","<":"<",">":">","'":"'",'"':"""})[t]??t)??"";function N(e,t={},{metadata:n=!0,search:r=!0,storage:i=!0}={}){let o;if(r&&o==null){let s=new URLSearchParams(window.location.search),a=xe(r)?r:e;o=s.get(a)}if(i&&o==null){let s=xe(i)?i:e;o=window.sessionStorage.getItem(s)??window.localStorage.getItem(s)}if(n&&o==null){let s=hi(xe(n)?n:e);o=document.documentElement.querySelector(`meta[name="${s}"]`)?.content}return o??t[e]}var ye=()=>{};var Fr=e=>typeof e=="boolean",te=e=>typeof e=="function",Ye=e=>typeof e=="number",Vr=e=>e!=null&&typeof e=="object";var xe=e=>typeof e=="string",Ut=e=>xe(e)&&e,_e=e=>Ye(e)&&Number.isFinite(e)&&e>0;function Se(e,t=n=>n==null||n===""){return e!=null&&Object.entries(e).forEach(([n,r])=>{t(r)&&delete e[n]}),e}function v(e,t){if(Fr(e))return e;let n=String(e);return n==="1"||n==="true"?!0:n==="0"||n==="false"?!1:t}function re(e,t,n){let r=Object.values(t);return r.find(i=>Xe(i,e))??n??r[0]}function hi(e=""){return String(e).replace(/(\p{Lowercase_Letter})(\p{Uppercase_Letter})/gu,(t,n,r)=>`${n}-${r}`).replace(/\W+/gu,"-").toLowerCase()}function ve(e,t=1){return Ye(e)||(e=Number.parseInt(e,10)),!Number.isNaN(e)&&e>0&&Number.isFinite(e)?e:t}var di=Date.now(),kt=()=>`(+${Date.now()-di}ms)`,Be=new Set,Ei=v(N("tacocat.debug",{},{metadata:!1}),typeof process<"u"&&process.env?.DEBUG);function jr(e){let t=`[${kr}/${e}]`,n=(s,a,...l)=>s?!0:(i(a,...l),!1),r=Ei?(s,...a)=>{console.debug(`${t} ${s}`,...a,kt())}:()=>{},i=(s,...a)=>{let l=`${t} ${s}`;Be.forEach(([u])=>u(l,...a))};return{assert:n,debug:r,error:i,warn:(s,...a)=>{let l=`${t} ${s}`;Be.forEach(([,u])=>u(l,...a))}}}function gi(e,t){let n=[e,t];return Be.add(n),()=>{Be.delete(n)}}gi((e,...t)=>{console.error(e,...t,kt())},(e,...t)=>{console.warn(e,...t,kt())});var xi="no promo",Hr="promo-tag",yi="yellow",_i="neutral",Si=(e,t,n)=>{let r=o=>o||xi,i=n?` (was "${r(t)}")`:"";return`${r(e)}${i}`},vi="cancel-context",Ne=(e,t)=>{let n=e===vi,r=!n&&e?.length>0,i=(r||n)&&(t&&t!=e||!t&&!n),o=i&&r||!i&&!!t,s=o?e||t:void 0;return{effectivePromoCode:s,overridenPromoCode:e,className:o?Hr:`${Hr} no-promo`,text:Si(s,t,i),variant:o?yi:_i,isOverriden:i}};var Gt="ABM",Ft="PUF",Vt="M2M",jt="PERPETUAL",Ht="P3Y",Pi="TAX_INCLUSIVE_DETAILS",Ti="TAX_EXCLUSIVE",Wr={ABM:Gt,PUF:Ft,M2M:Vt,PERPETUAL:jt,P3Y:Ht},Ms={[Gt]:{commitment:R.YEAR,term:O.MONTHLY},[Ft]:{commitment:R.YEAR,term:O.ANNUAL},[Vt]:{commitment:R.MONTH,term:O.MONTHLY},[jt]:{commitment:R.PERPETUAL,term:void 0},[Ht]:{commitment:R.THREE_MONTHS,term:O.P3Y}},Xr="Value is not an offer",Wt=e=>{if(typeof e!="object")return Xr;let{commitment:t,term:n}=e,r=bi(t,n);return{...e,planType:r}};var bi=(e,t)=>{switch(e){case void 0:return Xr;case"":return"";case R.YEAR:return t===O.MONTHLY?Gt:t===O.ANNUAL?Ft:"";case R.MONTH:return t===O.MONTHLY?Vt:"";case R.PERPETUAL:return jt;case R.TERM_LICENSE:return t===O.P3Y?Ht:"";default:return""}};function Xt(e){let{priceDetails:t}=e,{price:n,priceWithoutDiscount:r,priceWithoutTax:i,priceWithoutDiscountAndTax:o,taxDisplay:s}=t;if(s!==Pi)return e;let a={...e,priceDetails:{...t,price:i??n,priceWithoutDiscount:o??r,taxDisplay:Ti}};return a.offerType==="TRIAL"&&a.priceDetails.price===0&&(a.priceDetails.price=a.priceDetails.priceWithoutDiscount),a}var Yt=function(e,t){return Yt=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(n,r){n.__proto__=r}||function(n,r){for(var i in r)Object.prototype.hasOwnProperty.call(r,i)&&(n[i]=r[i])},Yt(e,t)};function Ce(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");Yt(e,t);function n(){this.constructor=e}e.prototype=t===null?Object.create(t):(n.prototype=t.prototype,new n)}var x=function(){return x=Object.assign||function(t){for(var n,r=1,i=arguments.length;r0}),n=[],r=0,i=t;r1)throw new RangeError("integer-width stems only accept a single optional option");i.options[0].replace(Li,function(a,l,u,c,p,f){if(l)t.minimumIntegerDigits=u.length;else{if(c&&p)throw new Error("We currently do not support maximum integer digits");if(f)throw new Error("We currently do not support exact integer digits")}return""});continue}if(en.test(i.stem)){t.minimumIntegerDigits=i.stem.length;continue}if(zr.test(i.stem)){if(i.options.length>1)throw new RangeError("Fraction-precision stems only accept a single optional option");i.stem.replace(zr,function(a,l,u,c,p,f){return u==="*"?t.minimumFractionDigits=l.length:c&&c[0]==="#"?t.maximumFractionDigits=c.length:p&&f?(t.minimumFractionDigits=p.length,t.maximumFractionDigits=p.length+f.length):(t.minimumFractionDigits=l.length,t.maximumFractionDigits=l.length),""}),i.options.length&&(t=x(x({},t),Zr(i.options[0])));continue}if(Kr.test(i.stem)){t=x(x({},t),Zr(i.stem));continue}var o=tn(i.stem);o&&(t=x(x({},t),o));var s=Oi(i.stem);s&&(t=x(x({},t),s))}return t}var qt,Ni=new RegExp("^"+$t.source+"*"),Ci=new RegExp($t.source+"*$");function g(e,t){return{start:e,end:t}}var Ri=!!String.prototype.startsWith,Ii=!!String.fromCodePoint,Mi=!!Object.fromEntries,Di=!!String.prototype.codePointAt,Ui=!!String.prototype.trimStart,ki=!!String.prototype.trimEnd,Gi=!!Number.isSafeInteger,Fi=Gi?Number.isSafeInteger:function(e){return typeof e=="number"&&isFinite(e)&&Math.floor(e)===e&&Math.abs(e)<=9007199254740991},Zt=!0;try{nn=cn("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu"),Zt=((qt=nn.exec("a"))===null||qt===void 0?void 0:qt[0])==="a"}catch{Zt=!1}var nn,on=Ri?function(t,n,r){return t.startsWith(n,r)}:function(t,n,r){return t.slice(r,r+n.length)===n},Jt=Ii?String.fromCodePoint:function(){for(var t=[],n=0;no;){if(s=t[o++],s>1114111)throw RangeError(s+" is not a valid code point");r+=s<65536?String.fromCharCode(s):String.fromCharCode(((s-=65536)>>10)+55296,s%1024+56320)}return r},sn=Mi?Object.fromEntries:function(t){for(var n={},r=0,i=t;r=r)){var i=t.charCodeAt(n),o;return i<55296||i>56319||n+1===r||(o=t.charCodeAt(n+1))<56320||o>57343?i:(i-55296<<10)+(o-56320)+65536}},Vi=Ui?function(t){return t.trimStart()}:function(t){return t.replace(Ni,"")},ji=ki?function(t){return t.trimEnd()}:function(t){return t.replace(Ci,"")};function cn(e,t){return new RegExp(e,t)}var Qt;Zt?(zt=cn("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu"),Qt=function(t,n){var r;zt.lastIndex=n;var i=zt.exec(t);return(r=i[1])!==null&&r!==void 0?r:""}):Qt=function(t,n){for(var r=[];;){var i=an(t,n);if(i===void 0||un(i)||Xi(i))break;r.push(i),n+=i>=65536?2:1}return Jt.apply(void 0,r)};var zt,ln=function(){function e(t,n){n===void 0&&(n={}),this.message=t,this.position={offset:0,line:1,column:1},this.ignoreTag=!!n.ignoreTag,this.requiresOtherClause=!!n.requiresOtherClause,this.shouldParseSkeletons=!!n.shouldParseSkeletons}return e.prototype.parse=function(){if(this.offset()!==0)throw Error("parser can only be used once");return this.parseMessage(0,"",!1)},e.prototype.parseMessage=function(t,n,r){for(var i=[];!this.isEOF();){var o=this.char();if(o===123){var s=this.parseArgument(t,r);if(s.err)return s;i.push(s.val)}else{if(o===125&&t>0)break;if(o===35&&(n==="plural"||n==="selectordinal")){var a=this.clonePosition();this.bump(),i.push({type:b.pound,location:g(a,this.clonePosition())})}else if(o===60&&!this.ignoreTag&&this.peek()===47){if(r)break;return this.error(E.UNMATCHED_CLOSING_TAG,g(this.clonePosition(),this.clonePosition()))}else if(o===60&&!this.ignoreTag&&Kt(this.peek()||0)){var s=this.parseTag(t,n);if(s.err)return s;i.push(s.val)}else{var s=this.parseLiteral(t,n);if(s.err)return s;i.push(s.val)}}}return{val:i,err:null}},e.prototype.parseTag=function(t,n){var r=this.clonePosition();this.bump();var i=this.parseTagName();if(this.bumpSpace(),this.bumpIf("/>"))return{val:{type:b.literal,value:"<"+i+"/>",location:g(r,this.clonePosition())},err:null};if(this.bumpIf(">")){var o=this.parseMessage(t+1,n,!0);if(o.err)return o;var s=o.val,a=this.clonePosition();if(this.bumpIf("")?{val:{type:b.tag,value:i,children:s,location:g(r,this.clonePosition())},err:null}:this.error(E.INVALID_TAG,g(a,this.clonePosition())))}else return this.error(E.UNCLOSED_TAG,g(r,this.clonePosition()))}else return this.error(E.INVALID_TAG,g(r,this.clonePosition()))},e.prototype.parseTagName=function(){var t=this.offset();for(this.bump();!this.isEOF()&&Wi(this.char());)this.bump();return this.message.slice(t,this.offset())},e.prototype.parseLiteral=function(t,n){for(var r=this.clonePosition(),i="";;){var o=this.tryParseQuote(n);if(o){i+=o;continue}var s=this.tryParseUnquoted(t,n);if(s){i+=s;continue}var a=this.tryParseLeftAngleBracket();if(a){i+=a;continue}break}var l=g(r,this.clonePosition());return{val:{type:b.literal,value:i,location:l},err:null}},e.prototype.tryParseLeftAngleBracket=function(){return!this.isEOF()&&this.char()===60&&(this.ignoreTag||!Hi(this.peek()||0))?(this.bump(),"<"):null},e.prototype.tryParseQuote=function(t){if(this.isEOF()||this.char()!==39)return null;switch(this.peek()){case 39:return this.bump(),this.bump(),"'";case 123:case 60:case 62:case 125:break;case 35:if(t==="plural"||t==="selectordinal")break;return null;default:return null}this.bump();var n=[this.char()];for(this.bump();!this.isEOF();){var r=this.char();if(r===39)if(this.peek()===39)n.push(39),this.bump();else{this.bump();break}else n.push(r);this.bump()}return Jt.apply(void 0,n)},e.prototype.tryParseUnquoted=function(t,n){if(this.isEOF())return null;var r=this.char();return r===60||r===123||r===35&&(n==="plural"||n==="selectordinal")||r===125&&t>0?null:(this.bump(),Jt(r))},e.prototype.parseArgument=function(t,n){var r=this.clonePosition();if(this.bump(),this.bumpSpace(),this.isEOF())return this.error(E.EXPECT_ARGUMENT_CLOSING_BRACE,g(r,this.clonePosition()));if(this.char()===125)return this.bump(),this.error(E.EMPTY_ARGUMENT,g(r,this.clonePosition()));var i=this.parseIdentifierIfPossible().value;if(!i)return this.error(E.MALFORMED_ARGUMENT,g(r,this.clonePosition()));if(this.bumpSpace(),this.isEOF())return this.error(E.EXPECT_ARGUMENT_CLOSING_BRACE,g(r,this.clonePosition()));switch(this.char()){case 125:return this.bump(),{val:{type:b.argument,value:i,location:g(r,this.clonePosition())},err:null};case 44:return this.bump(),this.bumpSpace(),this.isEOF()?this.error(E.EXPECT_ARGUMENT_CLOSING_BRACE,g(r,this.clonePosition())):this.parseArgumentOptions(t,n,i,r);default:return this.error(E.MALFORMED_ARGUMENT,g(r,this.clonePosition()))}},e.prototype.parseIdentifierIfPossible=function(){var t=this.clonePosition(),n=this.offset(),r=Qt(this.message,n),i=n+r.length;this.bumpTo(i);var o=this.clonePosition(),s=g(t,o);return{value:r,location:s}},e.prototype.parseArgumentOptions=function(t,n,r,i){var o,s=this.clonePosition(),a=this.parseIdentifierIfPossible().value,l=this.clonePosition();switch(a){case"":return this.error(E.EXPECT_ARGUMENT_TYPE,g(s,l));case"number":case"date":case"time":{this.bumpSpace();var u=null;if(this.bumpIf(",")){this.bumpSpace();var c=this.clonePosition(),p=this.parseSimpleArgStyleIfPossible();if(p.err)return p;var f=ji(p.val);if(f.length===0)return this.error(E.EXPECT_ARGUMENT_STYLE,g(this.clonePosition(),this.clonePosition()));var h=g(c,this.clonePosition());u={style:f,styleLocation:h}}var d=this.tryParseArgumentClose(i);if(d.err)return d;var _=g(i,this.clonePosition());if(u&&on(u?.style,"::",0)){var S=Vi(u.style.slice(2));if(a==="number"){var p=this.parseNumberSkeletonFromString(S,u.styleLocation);return p.err?p:{val:{type:b.number,value:r,location:_,style:p.val},err:null}}else{if(S.length===0)return this.error(E.EXPECT_DATE_TIME_SKELETON,_);var f={type:ce.dateTime,pattern:S,location:u.styleLocation,parsedOptions:this.shouldParseSkeletons?$r(S):{}},A=a==="date"?b.date:b.time;return{val:{type:A,value:r,location:_,style:f},err:null}}}return{val:{type:a==="number"?b.number:a==="date"?b.date:b.time,value:r,location:_,style:(o=u?.style)!==null&&o!==void 0?o:null},err:null}}case"plural":case"selectordinal":case"select":{var w=this.clonePosition();if(this.bumpSpace(),!this.bumpIf(","))return this.error(E.EXPECT_SELECT_ARGUMENT_OPTIONS,g(w,x({},w)));this.bumpSpace();var P=this.parseIdentifierIfPossible(),C=0;if(a!=="select"&&P.value==="offset"){if(!this.bumpIf(":"))return this.error(E.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE,g(this.clonePosition(),this.clonePosition()));this.bumpSpace();var p=this.tryParseDecimalInteger(E.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE,E.INVALID_PLURAL_ARGUMENT_OFFSET_VALUE);if(p.err)return p;this.bumpSpace(),P=this.parseIdentifierIfPossible(),C=p.val}var T=this.tryParsePluralOrSelectOptions(t,a,n,P);if(T.err)return T;var d=this.tryParseArgumentClose(i);if(d.err)return d;var L=g(i,this.clonePosition());return a==="select"?{val:{type:b.select,value:r,options:sn(T.val),location:L},err:null}:{val:{type:b.plural,value:r,options:sn(T.val),offset:C,pluralType:a==="plural"?"cardinal":"ordinal",location:L},err:null}}default:return this.error(E.INVALID_ARGUMENT_TYPE,g(s,l))}},e.prototype.tryParseArgumentClose=function(t){return this.isEOF()||this.char()!==125?this.error(E.EXPECT_ARGUMENT_CLOSING_BRACE,g(t,this.clonePosition())):(this.bump(),{val:!0,err:null})},e.prototype.parseSimpleArgStyleIfPossible=function(){for(var t=0,n=this.clonePosition();!this.isEOF();){var r=this.char();switch(r){case 39:{this.bump();var i=this.clonePosition();if(!this.bumpUntil("'"))return this.error(E.UNCLOSED_QUOTE_IN_ARGUMENT_STYLE,g(i,this.clonePosition()));this.bump();break}case 123:{t+=1,this.bump();break}case 125:{if(t>0)t-=1;else return{val:this.message.slice(n.offset,this.offset()),err:null};break}default:this.bump();break}}return{val:this.message.slice(n.offset,this.offset()),err:null}},e.prototype.parseNumberSkeletonFromString=function(t,n){var r=[];try{r=Qr(t)}catch{return this.error(E.INVALID_NUMBER_SKELETON,n)}return{val:{type:ce.number,tokens:r,location:n,parsedOptions:this.shouldParseSkeletons?rn(r):{}},err:null}},e.prototype.tryParsePluralOrSelectOptions=function(t,n,r,i){for(var o,s=!1,a=[],l=new Set,u=i.value,c=i.location;;){if(u.length===0){var p=this.clonePosition();if(n!=="select"&&this.bumpIf("=")){var f=this.tryParseDecimalInteger(E.EXPECT_PLURAL_ARGUMENT_SELECTOR,E.INVALID_PLURAL_ARGUMENT_SELECTOR);if(f.err)return f;c=g(p,this.clonePosition()),u=this.message.slice(p.offset,this.offset())}else break}if(l.has(u))return this.error(n==="select"?E.DUPLICATE_SELECT_ARGUMENT_SELECTOR:E.DUPLICATE_PLURAL_ARGUMENT_SELECTOR,c);u==="other"&&(s=!0),this.bumpSpace();var h=this.clonePosition();if(!this.bumpIf("{"))return this.error(n==="select"?E.EXPECT_SELECT_ARGUMENT_SELECTOR_FRAGMENT:E.EXPECT_PLURAL_ARGUMENT_SELECTOR_FRAGMENT,g(this.clonePosition(),this.clonePosition()));var d=this.parseMessage(t+1,n,r);if(d.err)return d;var _=this.tryParseArgumentClose(h);if(_.err)return _;a.push([u,{value:d.val,location:g(h,this.clonePosition())}]),l.add(u),this.bumpSpace(),o=this.parseIdentifierIfPossible(),u=o.value,c=o.location}return a.length===0?this.error(n==="select"?E.EXPECT_SELECT_ARGUMENT_SELECTOR:E.EXPECT_PLURAL_ARGUMENT_SELECTOR,g(this.clonePosition(),this.clonePosition())):this.requiresOtherClause&&!s?this.error(E.MISSING_OTHER_CLAUSE,g(this.clonePosition(),this.clonePosition())):{val:a,err:null}},e.prototype.tryParseDecimalInteger=function(t,n){var r=1,i=this.clonePosition();this.bumpIf("+")||this.bumpIf("-")&&(r=-1);for(var o=!1,s=0;!this.isEOF();){var a=this.char();if(a>=48&&a<=57)o=!0,s=s*10+(a-48),this.bump();else break}var l=g(i,this.clonePosition());return o?(s*=r,Fi(s)?{val:s,err:null}:this.error(n,l)):this.error(t,l)},e.prototype.offset=function(){return this.position.offset},e.prototype.isEOF=function(){return this.offset()===this.message.length},e.prototype.clonePosition=function(){return{offset:this.position.offset,line:this.position.line,column:this.position.column}},e.prototype.char=function(){var t=this.position.offset;if(t>=this.message.length)throw Error("out of bound");var n=an(this.message,t);if(n===void 0)throw Error("Offset "+t+" is at invalid UTF-16 code unit boundary");return n},e.prototype.error=function(t,n){return{val:null,err:{kind:t,message:this.message,location:n}}},e.prototype.bump=function(){if(!this.isEOF()){var t=this.char();t===10?(this.position.line+=1,this.position.column=1,this.position.offset+=1):(this.position.column+=1,this.position.offset+=t<65536?1:2)}},e.prototype.bumpIf=function(t){if(on(this.message,t,this.offset())){for(var n=0;n=0?(this.bumpTo(r),!0):(this.bumpTo(this.message.length),!1)},e.prototype.bumpTo=function(t){if(this.offset()>t)throw Error("targetOffset "+t+" must be greater than or equal to the current offset "+this.offset());for(t=Math.min(t,this.message.length);;){var n=this.offset();if(n===t)break;if(n>t)throw Error("targetOffset "+t+" is at invalid UTF-16 code unit boundary");if(this.bump(),this.isEOF())break}},e.prototype.bumpSpace=function(){for(;!this.isEOF()&&un(this.char());)this.bump()},e.prototype.peek=function(){if(this.isEOF())return null;var t=this.char(),n=this.offset(),r=this.message.charCodeAt(n+(t>=65536?2:1));return r??null},e}();function Kt(e){return e>=97&&e<=122||e>=65&&e<=90}function Hi(e){return Kt(e)||e===47}function Wi(e){return e===45||e===46||e>=48&&e<=57||e===95||e>=97&&e<=122||e>=65&&e<=90||e==183||e>=192&&e<=214||e>=216&&e<=246||e>=248&&e<=893||e>=895&&e<=8191||e>=8204&&e<=8205||e>=8255&&e<=8256||e>=8304&&e<=8591||e>=11264&&e<=12271||e>=12289&&e<=55295||e>=63744&&e<=64975||e>=65008&&e<=65533||e>=65536&&e<=983039}function un(e){return e>=9&&e<=13||e===32||e===133||e>=8206&&e<=8207||e===8232||e===8233}function Xi(e){return e>=33&&e<=35||e===36||e>=37&&e<=39||e===40||e===41||e===42||e===43||e===44||e===45||e>=46&&e<=47||e>=58&&e<=59||e>=60&&e<=62||e>=63&&e<=64||e===91||e===92||e===93||e===94||e===96||e===123||e===124||e===125||e===126||e===161||e>=162&&e<=165||e===166||e===167||e===169||e===171||e===172||e===174||e===176||e===177||e===182||e===187||e===191||e===215||e===247||e>=8208&&e<=8213||e>=8214&&e<=8215||e===8216||e===8217||e===8218||e>=8219&&e<=8220||e===8221||e===8222||e===8223||e>=8224&&e<=8231||e>=8240&&e<=8248||e===8249||e===8250||e>=8251&&e<=8254||e>=8257&&e<=8259||e===8260||e===8261||e===8262||e>=8263&&e<=8273||e===8274||e===8275||e>=8277&&e<=8286||e>=8592&&e<=8596||e>=8597&&e<=8601||e>=8602&&e<=8603||e>=8604&&e<=8607||e===8608||e>=8609&&e<=8610||e===8611||e>=8612&&e<=8613||e===8614||e>=8615&&e<=8621||e===8622||e>=8623&&e<=8653||e>=8654&&e<=8655||e>=8656&&e<=8657||e===8658||e===8659||e===8660||e>=8661&&e<=8691||e>=8692&&e<=8959||e>=8960&&e<=8967||e===8968||e===8969||e===8970||e===8971||e>=8972&&e<=8991||e>=8992&&e<=8993||e>=8994&&e<=9e3||e===9001||e===9002||e>=9003&&e<=9083||e===9084||e>=9085&&e<=9114||e>=9115&&e<=9139||e>=9140&&e<=9179||e>=9180&&e<=9185||e>=9186&&e<=9254||e>=9255&&e<=9279||e>=9280&&e<=9290||e>=9291&&e<=9311||e>=9472&&e<=9654||e===9655||e>=9656&&e<=9664||e===9665||e>=9666&&e<=9719||e>=9720&&e<=9727||e>=9728&&e<=9838||e===9839||e>=9840&&e<=10087||e===10088||e===10089||e===10090||e===10091||e===10092||e===10093||e===10094||e===10095||e===10096||e===10097||e===10098||e===10099||e===10100||e===10101||e>=10132&&e<=10175||e>=10176&&e<=10180||e===10181||e===10182||e>=10183&&e<=10213||e===10214||e===10215||e===10216||e===10217||e===10218||e===10219||e===10220||e===10221||e===10222||e===10223||e>=10224&&e<=10239||e>=10240&&e<=10495||e>=10496&&e<=10626||e===10627||e===10628||e===10629||e===10630||e===10631||e===10632||e===10633||e===10634||e===10635||e===10636||e===10637||e===10638||e===10639||e===10640||e===10641||e===10642||e===10643||e===10644||e===10645||e===10646||e===10647||e===10648||e>=10649&&e<=10711||e===10712||e===10713||e===10714||e===10715||e>=10716&&e<=10747||e===10748||e===10749||e>=10750&&e<=11007||e>=11008&&e<=11055||e>=11056&&e<=11076||e>=11077&&e<=11078||e>=11079&&e<=11084||e>=11085&&e<=11123||e>=11124&&e<=11125||e>=11126&&e<=11157||e===11158||e>=11159&&e<=11263||e>=11776&&e<=11777||e===11778||e===11779||e===11780||e===11781||e>=11782&&e<=11784||e===11785||e===11786||e===11787||e===11788||e===11789||e>=11790&&e<=11798||e===11799||e>=11800&&e<=11801||e===11802||e===11803||e===11804||e===11805||e>=11806&&e<=11807||e===11808||e===11809||e===11810||e===11811||e===11812||e===11813||e===11814||e===11815||e===11816||e===11817||e>=11818&&e<=11822||e===11823||e>=11824&&e<=11833||e>=11834&&e<=11835||e>=11836&&e<=11839||e===11840||e===11841||e===11842||e>=11843&&e<=11855||e>=11856&&e<=11857||e===11858||e>=11859&&e<=11903||e>=12289&&e<=12291||e===12296||e===12297||e===12298||e===12299||e===12300||e===12301||e===12302||e===12303||e===12304||e===12305||e>=12306&&e<=12307||e===12308||e===12309||e===12310||e===12311||e===12312||e===12313||e===12314||e===12315||e===12316||e===12317||e>=12318&&e<=12319||e===12320||e===12336||e===64830||e===64831||e>=65093&&e<=65094}function er(e){e.forEach(function(t){if(delete t.location,Je(t)||Qe(t))for(var n in t.options)delete t.options[n].location,er(t.options[n].value);else qe(t)&&et(t.style)||(ze(t)||Ze(t))&&Re(t.style)?delete t.style.location:Ke(t)&&er(t.children)})}function fn(e,t){t===void 0&&(t={}),t=x({shouldParseSkeletons:!0,requiresOtherClause:!0},t);var n=new ln(e,t).parse();if(n.err){var r=SyntaxError(E[n.err.kind]);throw r.location=n.err.location,r.originalMessage=n.err.message,r}return t?.captureLocation||er(n.val),n.val}function Ie(e,t){var n=t&&t.cache?t.cache:Zi,r=t&&t.serializer?t.serializer:zi,i=t&&t.strategy?t.strategy:Bi;return i(e,{cache:n,serializer:r})}function Yi(e){return e==null||typeof e=="number"||typeof e=="boolean"}function pn(e,t,n,r){var i=Yi(r)?r:n(r),o=t.get(i);return typeof o>"u"&&(o=e.call(this,r),t.set(i,o)),o}function mn(e,t,n){var r=Array.prototype.slice.call(arguments,3),i=n(r),o=t.get(i);return typeof o>"u"&&(o=e.apply(this,r),t.set(i,o)),o}function tr(e,t,n,r,i){return n.bind(t,e,r,i)}function Bi(e,t){var n=e.length===1?pn:mn;return tr(e,this,n,t.cache.create(),t.serializer)}function $i(e,t){return tr(e,this,mn,t.cache.create(),t.serializer)}function qi(e,t){return tr(e,this,pn,t.cache.create(),t.serializer)}var zi=function(){return JSON.stringify(arguments)};function rr(){this.cache=Object.create(null)}rr.prototype.get=function(e){return this.cache[e]};rr.prototype.set=function(e,t){this.cache[e]=t};var Zi={create:function(){return new rr}},tt={variadic:$i,monadic:qi};var le;(function(e){e.MISSING_VALUE="MISSING_VALUE",e.INVALID_VALUE="INVALID_VALUE",e.MISSING_INTL_API="MISSING_INTL_API"})(le||(le={}));var Me=function(e){Ce(t,e);function t(n,r,i){var o=e.call(this,n)||this;return o.code=r,o.originalMessage=i,o}return t.prototype.toString=function(){return"[formatjs Error: "+this.code+"] "+this.message},t}(Error);var nr=function(e){Ce(t,e);function t(n,r,i,o){return e.call(this,'Invalid values for "'+n+'": "'+r+'". Options are "'+Object.keys(i).join('", "')+'"',le.INVALID_VALUE,o)||this}return t}(Me);var hn=function(e){Ce(t,e);function t(n,r,i){return e.call(this,'Value for "'+n+'" must be of type '+r,le.INVALID_VALUE,i)||this}return t}(Me);var dn=function(e){Ce(t,e);function t(n,r){return e.call(this,'The intl string context variable "'+n+'" was not provided to the string "'+r+'"',le.MISSING_VALUE,r)||this}return t}(Me);var I;(function(e){e[e.literal=0]="literal",e[e.object=1]="object"})(I||(I={}));function Ji(e){return e.length<2?e:e.reduce(function(t,n){var r=t[t.length-1];return!r||r.type!==I.literal||n.type!==I.literal?t.push(n):r.value+=n.value,t},[])}function Qi(e){return typeof e=="function"}function De(e,t,n,r,i,o,s){if(e.length===1&&Bt(e[0]))return[{type:I.literal,value:e[0].value}];for(var a=[],l=0,u=e;l{throw TypeError(e)};var Ms=(e,t,r)=>t in e?bo(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var Us=(e,t)=>{for(var r in t)bo(e,r,{get:t[r],enumerable:!0})};var O=(e,t,r)=>Ms(e,typeof t!="symbol"?t+"":t,r),wo=(e,t,r)=>t.has(e)||_o("Cannot "+r);var R=(e,t,r)=>(wo(e,t,"read from private field"),r?r.call(e):t.get(e)),Q=(e,t,r)=>t.has(e)?_o("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,r),K=(e,t,r,n)=>(wo(e,t,"write to private field"),n?n.call(e,r):t.set(e,r),r);var lt;(function(e){e.STAGE="STAGE",e.PRODUCTION="PRODUCTION",e.LOCAL="LOCAL"})(lt||(lt={}));var Mr;(function(e){e.STAGE="STAGE",e.PRODUCTION="PROD",e.LOCAL="LOCAL"})(Mr||(Mr={}));var ht;(function(e){e.DRAFT="DRAFT",e.PUBLISHED="PUBLISHED"})(ht||(ht={}));var Ae;(function(e){e.V2="UCv2",e.V3="UCv3"})(Ae||(Ae={}));var Y;(function(e){e.CHECKOUT="checkout",e.CHECKOUT_EMAIL="checkout/email",e.SEGMENTATION="segmentation",e.BUNDLE="bundle",e.COMMITMENT="commitment",e.RECOMMENDATION="recommendation",e.EMAIL="email",e.PAYMENT="payment",e.CHANGE_PLAN_TEAM_PLANS="change-plan/team-upgrade/plans",e.CHANGE_PLAN_TEAM_PAYMENT="change-plan/team-upgrade/payment"})(Y||(Y={}));var Ur=function(e){var t;return(t=Ds.get(e))!==null&&t!==void 0?t:e},Ds=new Map([["countrySpecific","cs"],["quantity","q"],["authCode","code"],["checkoutPromoCode","apc"],["rurl","rUrl"],["curl","cUrl"],["ctxrturl","ctxRtUrl"],["country","co"],["language","lang"],["clientId","cli"],["context","ctx"],["productArrangementCode","pa"],["offerType","ot"],["marketSegment","ms"]]);var Ao=function(e){var t=typeof Symbol=="function"&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&typeof e.length=="number")return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},So=function(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),o,i=[],s;try{for(;(t===void 0||t-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(c){s={error:c}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return i};function je(e,t,r){var n,o;try{for(var i=Ao(Object.entries(e)),s=i.next();!s.done;s=i.next()){var c=So(s.value,2),a=c[0],h=c[1],l=Ur(a);h!=null&&r.has(l)&&t.set(l,h)}}catch(d){n={error:d}}finally{try{s&&!s.done&&(o=i.return)&&o.call(i)}finally{if(n)throw n.error}}}function Ft(e){switch(e){case lt.PRODUCTION:return"https://commerce.adobe.com";default:return"https://commerce-stg.adobe.com"}}function Ht(e,t){var r,n;for(var o in e){var i=e[o];try{for(var s=(r=void 0,Ao(Object.entries(i))),c=s.next();!c.done;c=s.next()){var a=So(c.value,2),h=a[0],l=a[1];if(l!=null){var d=Ur(h);t.set("items["+o+"]["+d+"]",l)}}}catch(u){r={error:u}}finally{try{c&&!c.done&&(n=s.return)&&n.call(s)}finally{if(r)throw r.error}}}}var Fs=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var o=0,n=Object.getOwnPropertySymbols(e);o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};function To(e){Vs(e);var t=e.env,r=e.items,n=e.workflowStep,o=Fs(e,["env","items","workflowStep"]),i=new URL(Ft(t));return i.pathname=n+"/",Ht(r,i.searchParams),je(o,i.searchParams,zs),i.toString()}var zs=new Set(["cli","co","lang","ctx","cUrl","mv","nglwfdata","otac","promoid","rUrl","sdid","spint","trackingid","code","campaignid","appctxid"]),Gs=["env","workflowStep","clientId","country","items"];function Vs(e){var t,r;try{for(var n=Hs(Gs),o=n.next();!o.done;o=n.next()){var i=o.value;if(!e[i])throw new Error('Argument "checkoutData" is not valid, missing: '+i)}}catch(s){t={error:s}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}return!0}var js=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var o=0,n=Object.getOwnPropertySymbols(e);o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},Ws="p_draft_landscape",qs="/store/";function Fr(e){Xs(e);var t=e.env,r=e.items,n=e.workflowStep,o=e.ms,i=e.marketSegment,s=e.ot,c=e.offerType,a=e.pa,h=e.productArrangementCode,l=e.landscape,d=js(e,["env","items","workflowStep","ms","marketSegment","ot","offerType","pa","productArrangementCode","landscape"]),u={marketSegment:i??o,offerType:c??s,productArrangementCode:h??a},m=new URL(Ft(t));return m.pathname=""+qs+n,n!==Y.SEGMENTATION&&n!==Y.CHANGE_PLAN_TEAM_PLANS&&Ht(r,m.searchParams),n===Y.SEGMENTATION&&je(u,m.searchParams,Dr),je(d,m.searchParams,Dr),l===ht.DRAFT&&je({af:Ws},m.searchParams,Dr),m.toString()}var Dr=new Set(["af","ai","apc","appctxid","cli","co","csm","ctx","ctxRtUrl","DCWATC","dp","fr","gsp","ijt","lang","lo","mal","ms","mv","mv2","nglwfdata","ot","otac","pa","pcid","promoid","q","rf","sc","scl","sdid","sid","spint","svar","th","thm","trackingid","usid","workflowid","context.guid","so.ca","so.su","so.tr","so.va"]),Ys=["env","workflowStep","clientId","country"];function Xs(e){var t,r;try{for(var n=Bs(Ys),o=n.next();!o.done;o=n.next()){var i=o.value;if(!e[i])throw new Error('Argument "checkoutData" is not valid, missing: '+i)}}catch(s){t={error:s}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}if(e.workflowStep!==Y.SEGMENTATION&&e.workflowStep!==Y.CHANGE_PLAN_TEAM_PLANS&&!e.items)throw new Error('Argument "checkoutData" is not valid, missing: items');return!0}function Hr(e,t){switch(e){case Ae.V2:return To(t);case Ae.V3:return Fr(t);default:return console.warn("Unsupported CheckoutType, will use UCv3 as default. Given type: "+e),Fr(t)}}var zr;(function(e){e.BASE="BASE",e.TRIAL="TRIAL",e.PROMOTION="PROMOTION"})(zr||(zr={}));var k;(function(e){e.MONTH="MONTH",e.YEAR="YEAR",e.TWO_YEARS="TWO_YEARS",e.THREE_YEARS="THREE_YEARS",e.PERPETUAL="PERPETUAL",e.TERM_LICENSE="TERM_LICENSE",e.ACCESS_PASS="ACCESS_PASS",e.THREE_MONTHS="THREE_MONTHS",e.SIX_MONTHS="SIX_MONTHS"})(k||(k={}));var L;(function(e){e.ANNUAL="ANNUAL",e.MONTHLY="MONTHLY",e.TWO_YEARS="TWO_YEARS",e.THREE_YEARS="THREE_YEARS",e.P1D="P1D",e.P1Y="P1Y",e.P3Y="P3Y",e.P10Y="P10Y",e.P15Y="P15Y",e.P3D="P3D",e.P7D="P7D",e.P30D="P30D",e.HALF_YEARLY="HALF_YEARLY",e.QUARTERLY="QUARTERLY"})(L||(L={}));var Gr;(function(e){e.INDIVIDUAL="INDIVIDUAL",e.TEAM="TEAM",e.ENTERPRISE="ENTERPRISE"})(Gr||(Gr={}));var Vr;(function(e){e.COM="COM",e.EDU="EDU",e.GOV="GOV"})(Vr||(Vr={}));var jr;(function(e){e.DIRECT="DIRECT",e.INDIRECT="INDIRECT"})(jr||(jr={}));var Br;(function(e){e.ENTERPRISE_PRODUCT="ENTERPRISE_PRODUCT",e.ETLA="ETLA",e.RETAIL="RETAIL",e.VIP="VIP",e.VIPMP="VIPMP",e.FREE="FREE"})(Br||(Br={}));var Po="tacocat.js";var zt=(e,t)=>String(e??"").toLowerCase()==String(t??"").toLowerCase(),Co=e=>`${e??""}`.replace(/[&<>'"]/g,t=>({"&":"&","<":"<",">":">","'":"'",'"':"""})[t]??t)??"";function N(e,t={},{metadata:r=!0,search:n=!0,storage:o=!0}={}){let i;if(n&&i==null){let s=new URLSearchParams(window.location.search),c=Be(n)?n:e;i=s.get(c)}if(o&&i==null){let s=Be(o)?o:e;i=window.sessionStorage.getItem(s)??window.localStorage.getItem(s)}if(r&&i==null){let s=Zs(Be(r)?r:e);i=document.documentElement.querySelector(`meta[name="${s}"]`)?.content}return i??t[e]}var We=()=>{};var $o=e=>typeof e=="boolean",me=e=>typeof e=="function",Gt=e=>typeof e=="number",Oo=e=>e!=null&&typeof e=="object";var Be=e=>typeof e=="string",Wr=e=>Be(e)&&e,qe=e=>Gt(e)&&Number.isFinite(e)&&e>0;function Ye(e,t=r=>r==null||r===""){return e!=null&&Object.entries(e).forEach(([r,n])=>{t(n)&&delete e[r]}),e}function w(e,t){if($o(e))return e;let r=String(e);return r==="1"||r==="true"?!0:r==="0"||r==="false"?!1:t}function pe(e,t,r){let n=Object.values(t);return n.find(o=>zt(o,e))??r??n[0]}function Zs(e=""){return String(e).replace(/(\p{Lowercase_Letter})(\p{Uppercase_Letter})/gu,(t,r,n)=>`${r}-${n}`).replace(/\W+/gu,"-").toLowerCase()}function Xe(e,t=1){return Gt(e)||(e=Number.parseInt(e,10)),!Number.isNaN(e)&&e>0&&Number.isFinite(e)?e:t}var Js=Date.now(),qr=()=>`(+${Date.now()-Js}ms)`,Vt=new Set,Qs=w(N("tacocat.debug",{},{metadata:!1}),typeof process<"u"&&process.env?.DEBUG);function Lo(e){let t=`[${Po}/${e}]`,r=(s,c,...a)=>s?!0:(o(c,...a),!1),n=Qs?(s,...c)=>{console.debug(`${t} ${s}`,...c,qr())}:()=>{},o=(s,...c)=>{let a=`${t} ${s}`;Vt.forEach(([h])=>h(a,...c))};return{assert:r,debug:n,error:o,warn:(s,...c)=>{let a=`${t} ${s}`;Vt.forEach(([,h])=>h(a,...c))}}}function Ks(e,t){let r=[e,t];return Vt.add(r),()=>{Vt.delete(r)}}Ks((e,...t)=>{console.error(e,...t,qr())},(e,...t)=>{console.warn(e,...t,qr())});var ea="no promo",No="promo-tag",ta="yellow",ra="neutral",na=(e,t,r)=>{let n=i=>i||ea,o=r?` (was "${n(t)}")`:"";return`${n(e)}${o}`},oa="cancel-context",dt=(e,t)=>{let r=e===oa,n=!r&&e?.length>0,o=(n||r)&&(t&&t!=e||!t&&!r),i=o&&n||!o&&!!t,s=i?e||t:void 0;return{effectivePromoCode:s,overridenPromoCode:e,className:i?No:`${No} no-promo`,text:na(s,t,o),variant:i?ta:ra,isOverriden:o}};var Yr="ABM",Xr="PUF",Zr="M2M",Jr="PERPETUAL",Qr="P3Y",ia="TAX_INCLUSIVE_DETAILS",sa="TAX_EXCLUSIVE",Ro={ABM:Yr,PUF:Xr,M2M:Zr,PERPETUAL:Jr,P3Y:Qr},Ol={[Yr]:{commitment:k.YEAR,term:L.MONTHLY},[Xr]:{commitment:k.YEAR,term:L.ANNUAL},[Zr]:{commitment:k.MONTH,term:L.MONTHLY},[Jr]:{commitment:k.PERPETUAL,term:void 0},[Qr]:{commitment:k.THREE_MONTHS,term:L.P3Y}},Io="Value is not an offer",Kr=e=>{if(typeof e!="object")return Io;let{commitment:t,term:r}=e,n=aa(t,r);return{...e,planType:n}};var aa=(e,t)=>{switch(e){case void 0:return Io;case"":return"";case k.YEAR:return t===L.MONTHLY?Yr:t===L.ANNUAL?Xr:"";case k.MONTH:return t===L.MONTHLY?Zr:"";case k.PERPETUAL:return Jr;case k.TERM_LICENSE:return t===L.P3Y?Qr:"";default:return""}};function en(e){let{priceDetails:t}=e,{price:r,priceWithoutDiscount:n,priceWithoutTax:o,priceWithoutDiscountAndTax:i,taxDisplay:s}=t;if(s!==ia)return e;let c={...e,priceDetails:{...t,price:o??r,priceWithoutDiscount:i??n,taxDisplay:sa}};return c.offerType==="TRIAL"&&c.priceDetails.price===0&&(c.priceDetails.price=c.priceDetails.priceWithoutDiscount),c}var tn=function(e,t){return tn=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,n){r.__proto__=n}||function(r,n){for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(r[o]=n[o])},tn(e,t)};function ut(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");tn(e,t);function r(){this.constructor=e}e.prototype=t===null?Object.create(t):(r.prototype=t.prototype,new r)}var E=function(){return E=Object.assign||function(t){for(var r,n=1,o=arguments.length;n0}),r=[],n=0,o=t;n1)throw new RangeError("integer-width stems only accept a single optional option");o.options[0].replace(ha,function(c,a,h,l,d,u){if(a)t.minimumIntegerDigits=h.length;else{if(l&&d)throw new Error("We currently do not support maximum integer digits");if(u)throw new Error("We currently do not support exact integer digits")}return""});continue}if(jo.test(o.stem)){t.minimumIntegerDigits=o.stem.length;continue}if(Fo.test(o.stem)){if(o.options.length>1)throw new RangeError("Fraction-precision stems only accept a single optional option");o.stem.replace(Fo,function(c,a,h,l,d,u){return h==="*"?t.minimumFractionDigits=a.length:l&&l[0]==="#"?t.maximumFractionDigits=l.length:d&&u?(t.minimumFractionDigits=d.length,t.maximumFractionDigits=d.length+u.length):(t.minimumFractionDigits=a.length,t.maximumFractionDigits=a.length),""}),o.options.length&&(t=E(E({},t),Ho(o.options[0])));continue}if(Vo.test(o.stem)){t=E(E({},t),Ho(o.stem));continue}var i=Bo(o.stem);i&&(t=E(E({},t),i));var s=da(o.stem);s&&(t=E(E({},t),s))}return t}var on,ua=new RegExp("^"+nn.source+"*"),ma=new RegExp(nn.source+"*$");function y(e,t){return{start:e,end:t}}var pa=!!String.prototype.startsWith,fa=!!String.fromCodePoint,ga=!!Object.fromEntries,xa=!!String.prototype.codePointAt,va=!!String.prototype.trimStart,ya=!!String.prototype.trimEnd,Ea=!!Number.isSafeInteger,ba=Ea?Number.isSafeInteger:function(e){return typeof e=="number"&&isFinite(e)&&Math.floor(e)===e&&Math.abs(e)<=9007199254740991},an=!0;try{qo=Jo("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu"),an=((on=qo.exec("a"))===null||on===void 0?void 0:on[0])==="a"}catch{an=!1}var qo,Yo=pa?function(t,r,n){return t.startsWith(r,n)}:function(t,r,n){return t.slice(n,n+r.length)===r},cn=fa?String.fromCodePoint:function(){for(var t=[],r=0;ri;){if(s=t[i++],s>1114111)throw RangeError(s+" is not a valid code point");n+=s<65536?String.fromCharCode(s):String.fromCharCode(((s-=65536)>>10)+55296,s%1024+56320)}return n},Xo=ga?Object.fromEntries:function(t){for(var r={},n=0,o=t;n=n)){var o=t.charCodeAt(r),i;return o<55296||o>56319||r+1===n||(i=t.charCodeAt(r+1))<56320||i>57343?o:(o-55296<<10)+(i-56320)+65536}},_a=va?function(t){return t.trimStart()}:function(t){return t.replace(ua,"")},wa=ya?function(t){return t.trimEnd()}:function(t){return t.replace(ma,"")};function Jo(e,t){return new RegExp(e,t)}var ln;an?(sn=Jo("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu"),ln=function(t,r){var n;sn.lastIndex=r;var o=sn.exec(t);return(n=o[1])!==null&&n!==void 0?n:""}):ln=function(t,r){for(var n=[];;){var o=Zo(t,r);if(o===void 0||Ko(o)||Ta(o))break;n.push(o),r+=o>=65536?2:1}return cn.apply(void 0,n)};var sn,Qo=function(){function e(t,r){r===void 0&&(r={}),this.message=t,this.position={offset:0,line:1,column:1},this.ignoreTag=!!r.ignoreTag,this.requiresOtherClause=!!r.requiresOtherClause,this.shouldParseSkeletons=!!r.shouldParseSkeletons}return e.prototype.parse=function(){if(this.offset()!==0)throw Error("parser can only be used once");return this.parseMessage(0,"",!1)},e.prototype.parseMessage=function(t,r,n){for(var o=[];!this.isEOF();){var i=this.char();if(i===123){var s=this.parseArgument(t,n);if(s.err)return s;o.push(s.val)}else{if(i===125&&t>0)break;if(i===35&&(r==="plural"||r==="selectordinal")){var c=this.clonePosition();this.bump(),o.push({type:T.pound,location:y(c,this.clonePosition())})}else if(i===60&&!this.ignoreTag&&this.peek()===47){if(n)break;return this.error(v.UNMATCHED_CLOSING_TAG,y(this.clonePosition(),this.clonePosition()))}else if(i===60&&!this.ignoreTag&&hn(this.peek()||0)){var s=this.parseTag(t,r);if(s.err)return s;o.push(s.val)}else{var s=this.parseLiteral(t,r);if(s.err)return s;o.push(s.val)}}}return{val:o,err:null}},e.prototype.parseTag=function(t,r){var n=this.clonePosition();this.bump();var o=this.parseTagName();if(this.bumpSpace(),this.bumpIf("/>"))return{val:{type:T.literal,value:"<"+o+"/>",location:y(n,this.clonePosition())},err:null};if(this.bumpIf(">")){var i=this.parseMessage(t+1,r,!0);if(i.err)return i;var s=i.val,c=this.clonePosition();if(this.bumpIf("")?{val:{type:T.tag,value:o,children:s,location:y(n,this.clonePosition())},err:null}:this.error(v.INVALID_TAG,y(c,this.clonePosition())))}else return this.error(v.UNCLOSED_TAG,y(n,this.clonePosition()))}else return this.error(v.INVALID_TAG,y(n,this.clonePosition()))},e.prototype.parseTagName=function(){var t=this.offset();for(this.bump();!this.isEOF()&&Sa(this.char());)this.bump();return this.message.slice(t,this.offset())},e.prototype.parseLiteral=function(t,r){for(var n=this.clonePosition(),o="";;){var i=this.tryParseQuote(r);if(i){o+=i;continue}var s=this.tryParseUnquoted(t,r);if(s){o+=s;continue}var c=this.tryParseLeftAngleBracket();if(c){o+=c;continue}break}var a=y(n,this.clonePosition());return{val:{type:T.literal,value:o,location:a},err:null}},e.prototype.tryParseLeftAngleBracket=function(){return!this.isEOF()&&this.char()===60&&(this.ignoreTag||!Aa(this.peek()||0))?(this.bump(),"<"):null},e.prototype.tryParseQuote=function(t){if(this.isEOF()||this.char()!==39)return null;switch(this.peek()){case 39:return this.bump(),this.bump(),"'";case 123:case 60:case 62:case 125:break;case 35:if(t==="plural"||t==="selectordinal")break;return null;default:return null}this.bump();var r=[this.char()];for(this.bump();!this.isEOF();){var n=this.char();if(n===39)if(this.peek()===39)r.push(39),this.bump();else{this.bump();break}else r.push(n);this.bump()}return cn.apply(void 0,r)},e.prototype.tryParseUnquoted=function(t,r){if(this.isEOF())return null;var n=this.char();return n===60||n===123||n===35&&(r==="plural"||r==="selectordinal")||n===125&&t>0?null:(this.bump(),cn(n))},e.prototype.parseArgument=function(t,r){var n=this.clonePosition();if(this.bump(),this.bumpSpace(),this.isEOF())return this.error(v.EXPECT_ARGUMENT_CLOSING_BRACE,y(n,this.clonePosition()));if(this.char()===125)return this.bump(),this.error(v.EMPTY_ARGUMENT,y(n,this.clonePosition()));var o=this.parseIdentifierIfPossible().value;if(!o)return this.error(v.MALFORMED_ARGUMENT,y(n,this.clonePosition()));if(this.bumpSpace(),this.isEOF())return this.error(v.EXPECT_ARGUMENT_CLOSING_BRACE,y(n,this.clonePosition()));switch(this.char()){case 125:return this.bump(),{val:{type:T.argument,value:o,location:y(n,this.clonePosition())},err:null};case 44:return this.bump(),this.bumpSpace(),this.isEOF()?this.error(v.EXPECT_ARGUMENT_CLOSING_BRACE,y(n,this.clonePosition())):this.parseArgumentOptions(t,r,o,n);default:return this.error(v.MALFORMED_ARGUMENT,y(n,this.clonePosition()))}},e.prototype.parseIdentifierIfPossible=function(){var t=this.clonePosition(),r=this.offset(),n=ln(this.message,r),o=r+n.length;this.bumpTo(o);var i=this.clonePosition(),s=y(t,i);return{value:n,location:s}},e.prototype.parseArgumentOptions=function(t,r,n,o){var i,s=this.clonePosition(),c=this.parseIdentifierIfPossible().value,a=this.clonePosition();switch(c){case"":return this.error(v.EXPECT_ARGUMENT_TYPE,y(s,a));case"number":case"date":case"time":{this.bumpSpace();var h=null;if(this.bumpIf(",")){this.bumpSpace();var l=this.clonePosition(),d=this.parseSimpleArgStyleIfPossible();if(d.err)return d;var u=wa(d.val);if(u.length===0)return this.error(v.EXPECT_ARGUMENT_STYLE,y(this.clonePosition(),this.clonePosition()));var m=y(l,this.clonePosition());h={style:u,styleLocation:m}}var f=this.tryParseArgumentClose(o);if(f.err)return f;var g=y(o,this.clonePosition());if(h&&Yo(h?.style,"::",0)){var _=_a(h.style.slice(2));if(c==="number"){var d=this.parseNumberSkeletonFromString(_,h.styleLocation);return d.err?d:{val:{type:T.number,value:n,location:g,style:d.val},err:null}}else{if(_.length===0)return this.error(v.EXPECT_DATE_TIME_SKELETON,g);var u={type:Se.dateTime,pattern:_,location:h.styleLocation,parsedOptions:this.shouldParseSkeletons?Uo(_):{}},P=c==="date"?T.date:T.time;return{val:{type:P,value:n,location:g,style:u},err:null}}}return{val:{type:c==="number"?T.number:c==="date"?T.date:T.time,value:n,location:g,style:(i=h?.style)!==null&&i!==void 0?i:null},err:null}}case"plural":case"selectordinal":case"select":{var C=this.clonePosition();if(this.bumpSpace(),!this.bumpIf(","))return this.error(v.EXPECT_SELECT_ARGUMENT_OPTIONS,y(C,E({},C)));this.bumpSpace();var A=this.parseIdentifierIfPossible(),I=0;if(c!=="select"&&A.value==="offset"){if(!this.bumpIf(":"))return this.error(v.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE,y(this.clonePosition(),this.clonePosition()));this.bumpSpace();var d=this.tryParseDecimalInteger(v.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE,v.INVALID_PLURAL_ARGUMENT_OFFSET_VALUE);if(d.err)return d;this.bumpSpace(),A=this.parseIdentifierIfPossible(),I=d.val}var S=this.tryParsePluralOrSelectOptions(t,c,r,A);if(S.err)return S;var f=this.tryParseArgumentClose(o);if(f.err)return f;var $=y(o,this.clonePosition());return c==="select"?{val:{type:T.select,value:n,options:Xo(S.val),location:$},err:null}:{val:{type:T.plural,value:n,options:Xo(S.val),offset:I,pluralType:c==="plural"?"cardinal":"ordinal",location:$},err:null}}default:return this.error(v.INVALID_ARGUMENT_TYPE,y(s,a))}},e.prototype.tryParseArgumentClose=function(t){return this.isEOF()||this.char()!==125?this.error(v.EXPECT_ARGUMENT_CLOSING_BRACE,y(t,this.clonePosition())):(this.bump(),{val:!0,err:null})},e.prototype.parseSimpleArgStyleIfPossible=function(){for(var t=0,r=this.clonePosition();!this.isEOF();){var n=this.char();switch(n){case 39:{this.bump();var o=this.clonePosition();if(!this.bumpUntil("'"))return this.error(v.UNCLOSED_QUOTE_IN_ARGUMENT_STYLE,y(o,this.clonePosition()));this.bump();break}case 123:{t+=1,this.bump();break}case 125:{if(t>0)t-=1;else return{val:this.message.slice(r.offset,this.offset()),err:null};break}default:this.bump();break}}return{val:this.message.slice(r.offset,this.offset()),err:null}},e.prototype.parseNumberSkeletonFromString=function(t,r){var n=[];try{n=Go(t)}catch{return this.error(v.INVALID_NUMBER_SKELETON,r)}return{val:{type:Se.number,tokens:n,location:r,parsedOptions:this.shouldParseSkeletons?Wo(n):{}},err:null}},e.prototype.tryParsePluralOrSelectOptions=function(t,r,n,o){for(var i,s=!1,c=[],a=new Set,h=o.value,l=o.location;;){if(h.length===0){var d=this.clonePosition();if(r!=="select"&&this.bumpIf("=")){var u=this.tryParseDecimalInteger(v.EXPECT_PLURAL_ARGUMENT_SELECTOR,v.INVALID_PLURAL_ARGUMENT_SELECTOR);if(u.err)return u;l=y(d,this.clonePosition()),h=this.message.slice(d.offset,this.offset())}else break}if(a.has(h))return this.error(r==="select"?v.DUPLICATE_SELECT_ARGUMENT_SELECTOR:v.DUPLICATE_PLURAL_ARGUMENT_SELECTOR,l);h==="other"&&(s=!0),this.bumpSpace();var m=this.clonePosition();if(!this.bumpIf("{"))return this.error(r==="select"?v.EXPECT_SELECT_ARGUMENT_SELECTOR_FRAGMENT:v.EXPECT_PLURAL_ARGUMENT_SELECTOR_FRAGMENT,y(this.clonePosition(),this.clonePosition()));var f=this.parseMessage(t+1,r,n);if(f.err)return f;var g=this.tryParseArgumentClose(m);if(g.err)return g;c.push([h,{value:f.val,location:y(m,this.clonePosition())}]),a.add(h),this.bumpSpace(),i=this.parseIdentifierIfPossible(),h=i.value,l=i.location}return c.length===0?this.error(r==="select"?v.EXPECT_SELECT_ARGUMENT_SELECTOR:v.EXPECT_PLURAL_ARGUMENT_SELECTOR,y(this.clonePosition(),this.clonePosition())):this.requiresOtherClause&&!s?this.error(v.MISSING_OTHER_CLAUSE,y(this.clonePosition(),this.clonePosition())):{val:c,err:null}},e.prototype.tryParseDecimalInteger=function(t,r){var n=1,o=this.clonePosition();this.bumpIf("+")||this.bumpIf("-")&&(n=-1);for(var i=!1,s=0;!this.isEOF();){var c=this.char();if(c>=48&&c<=57)i=!0,s=s*10+(c-48),this.bump();else break}var a=y(o,this.clonePosition());return i?(s*=n,ba(s)?{val:s,err:null}:this.error(r,a)):this.error(t,a)},e.prototype.offset=function(){return this.position.offset},e.prototype.isEOF=function(){return this.offset()===this.message.length},e.prototype.clonePosition=function(){return{offset:this.position.offset,line:this.position.line,column:this.position.column}},e.prototype.char=function(){var t=this.position.offset;if(t>=this.message.length)throw Error("out of bound");var r=Zo(this.message,t);if(r===void 0)throw Error("Offset "+t+" is at invalid UTF-16 code unit boundary");return r},e.prototype.error=function(t,r){return{val:null,err:{kind:t,message:this.message,location:r}}},e.prototype.bump=function(){if(!this.isEOF()){var t=this.char();t===10?(this.position.line+=1,this.position.column=1,this.position.offset+=1):(this.position.column+=1,this.position.offset+=t<65536?1:2)}},e.prototype.bumpIf=function(t){if(Yo(this.message,t,this.offset())){for(var r=0;r=0?(this.bumpTo(n),!0):(this.bumpTo(this.message.length),!1)},e.prototype.bumpTo=function(t){if(this.offset()>t)throw Error("targetOffset "+t+" must be greater than or equal to the current offset "+this.offset());for(t=Math.min(t,this.message.length);;){var r=this.offset();if(r===t)break;if(r>t)throw Error("targetOffset "+t+" is at invalid UTF-16 code unit boundary");if(this.bump(),this.isEOF())break}},e.prototype.bumpSpace=function(){for(;!this.isEOF()&&Ko(this.char());)this.bump()},e.prototype.peek=function(){if(this.isEOF())return null;var t=this.char(),r=this.offset(),n=this.message.charCodeAt(r+(t>=65536?2:1));return n??null},e}();function hn(e){return e>=97&&e<=122||e>=65&&e<=90}function Aa(e){return hn(e)||e===47}function Sa(e){return e===45||e===46||e>=48&&e<=57||e===95||e>=97&&e<=122||e>=65&&e<=90||e==183||e>=192&&e<=214||e>=216&&e<=246||e>=248&&e<=893||e>=895&&e<=8191||e>=8204&&e<=8205||e>=8255&&e<=8256||e>=8304&&e<=8591||e>=11264&&e<=12271||e>=12289&&e<=55295||e>=63744&&e<=64975||e>=65008&&e<=65533||e>=65536&&e<=983039}function Ko(e){return e>=9&&e<=13||e===32||e===133||e>=8206&&e<=8207||e===8232||e===8233}function Ta(e){return e>=33&&e<=35||e===36||e>=37&&e<=39||e===40||e===41||e===42||e===43||e===44||e===45||e>=46&&e<=47||e>=58&&e<=59||e>=60&&e<=62||e>=63&&e<=64||e===91||e===92||e===93||e===94||e===96||e===123||e===124||e===125||e===126||e===161||e>=162&&e<=165||e===166||e===167||e===169||e===171||e===172||e===174||e===176||e===177||e===182||e===187||e===191||e===215||e===247||e>=8208&&e<=8213||e>=8214&&e<=8215||e===8216||e===8217||e===8218||e>=8219&&e<=8220||e===8221||e===8222||e===8223||e>=8224&&e<=8231||e>=8240&&e<=8248||e===8249||e===8250||e>=8251&&e<=8254||e>=8257&&e<=8259||e===8260||e===8261||e===8262||e>=8263&&e<=8273||e===8274||e===8275||e>=8277&&e<=8286||e>=8592&&e<=8596||e>=8597&&e<=8601||e>=8602&&e<=8603||e>=8604&&e<=8607||e===8608||e>=8609&&e<=8610||e===8611||e>=8612&&e<=8613||e===8614||e>=8615&&e<=8621||e===8622||e>=8623&&e<=8653||e>=8654&&e<=8655||e>=8656&&e<=8657||e===8658||e===8659||e===8660||e>=8661&&e<=8691||e>=8692&&e<=8959||e>=8960&&e<=8967||e===8968||e===8969||e===8970||e===8971||e>=8972&&e<=8991||e>=8992&&e<=8993||e>=8994&&e<=9e3||e===9001||e===9002||e>=9003&&e<=9083||e===9084||e>=9085&&e<=9114||e>=9115&&e<=9139||e>=9140&&e<=9179||e>=9180&&e<=9185||e>=9186&&e<=9254||e>=9255&&e<=9279||e>=9280&&e<=9290||e>=9291&&e<=9311||e>=9472&&e<=9654||e===9655||e>=9656&&e<=9664||e===9665||e>=9666&&e<=9719||e>=9720&&e<=9727||e>=9728&&e<=9838||e===9839||e>=9840&&e<=10087||e===10088||e===10089||e===10090||e===10091||e===10092||e===10093||e===10094||e===10095||e===10096||e===10097||e===10098||e===10099||e===10100||e===10101||e>=10132&&e<=10175||e>=10176&&e<=10180||e===10181||e===10182||e>=10183&&e<=10213||e===10214||e===10215||e===10216||e===10217||e===10218||e===10219||e===10220||e===10221||e===10222||e===10223||e>=10224&&e<=10239||e>=10240&&e<=10495||e>=10496&&e<=10626||e===10627||e===10628||e===10629||e===10630||e===10631||e===10632||e===10633||e===10634||e===10635||e===10636||e===10637||e===10638||e===10639||e===10640||e===10641||e===10642||e===10643||e===10644||e===10645||e===10646||e===10647||e===10648||e>=10649&&e<=10711||e===10712||e===10713||e===10714||e===10715||e>=10716&&e<=10747||e===10748||e===10749||e>=10750&&e<=11007||e>=11008&&e<=11055||e>=11056&&e<=11076||e>=11077&&e<=11078||e>=11079&&e<=11084||e>=11085&&e<=11123||e>=11124&&e<=11125||e>=11126&&e<=11157||e===11158||e>=11159&&e<=11263||e>=11776&&e<=11777||e===11778||e===11779||e===11780||e===11781||e>=11782&&e<=11784||e===11785||e===11786||e===11787||e===11788||e===11789||e>=11790&&e<=11798||e===11799||e>=11800&&e<=11801||e===11802||e===11803||e===11804||e===11805||e>=11806&&e<=11807||e===11808||e===11809||e===11810||e===11811||e===11812||e===11813||e===11814||e===11815||e===11816||e===11817||e>=11818&&e<=11822||e===11823||e>=11824&&e<=11833||e>=11834&&e<=11835||e>=11836&&e<=11839||e===11840||e===11841||e===11842||e>=11843&&e<=11855||e>=11856&&e<=11857||e===11858||e>=11859&&e<=11903||e>=12289&&e<=12291||e===12296||e===12297||e===12298||e===12299||e===12300||e===12301||e===12302||e===12303||e===12304||e===12305||e>=12306&&e<=12307||e===12308||e===12309||e===12310||e===12311||e===12312||e===12313||e===12314||e===12315||e===12316||e===12317||e>=12318&&e<=12319||e===12320||e===12336||e===64830||e===64831||e>=65093&&e<=65094}function dn(e){e.forEach(function(t){if(delete t.location,Yt(t)||Xt(t))for(var r in t.options)delete t.options[r].location,dn(t.options[r].value);else Bt(t)&&Jt(t.style)||(Wt(t)||qt(t))&&mt(t.style)?delete t.style.location:Zt(t)&&dn(t.children)})}function ei(e,t){t===void 0&&(t={}),t=E({shouldParseSkeletons:!0,requiresOtherClause:!0},t);var r=new Qo(e,t).parse();if(r.err){var n=SyntaxError(v[r.err.kind]);throw n.location=r.err.location,n.originalMessage=r.err.message,n}return t?.captureLocation||dn(r.val),r.val}function pt(e,t){var r=t&&t.cache?t.cache:Na,n=t&&t.serializer?t.serializer:La,o=t&&t.strategy?t.strategy:Ca;return o(e,{cache:r,serializer:n})}function Pa(e){return e==null||typeof e=="number"||typeof e=="boolean"}function ti(e,t,r,n){var o=Pa(n)?n:r(n),i=t.get(o);return typeof i>"u"&&(i=e.call(this,n),t.set(o,i)),i}function ri(e,t,r){var n=Array.prototype.slice.call(arguments,3),o=r(n),i=t.get(o);return typeof i>"u"&&(i=e.apply(this,n),t.set(o,i)),i}function un(e,t,r,n,o){return r.bind(t,e,n,o)}function Ca(e,t){var r=e.length===1?ti:ri;return un(e,this,r,t.cache.create(),t.serializer)}function $a(e,t){return un(e,this,ri,t.cache.create(),t.serializer)}function Oa(e,t){return un(e,this,ti,t.cache.create(),t.serializer)}var La=function(){return JSON.stringify(arguments)};function mn(){this.cache=Object.create(null)}mn.prototype.get=function(e){return this.cache[e]};mn.prototype.set=function(e,t){this.cache[e]=t};var Na={create:function(){return new mn}},Qt={variadic:$a,monadic:Oa};var Te;(function(e){e.MISSING_VALUE="MISSING_VALUE",e.INVALID_VALUE="INVALID_VALUE",e.MISSING_INTL_API="MISSING_INTL_API"})(Te||(Te={}));var ft=function(e){ut(t,e);function t(r,n,o){var i=e.call(this,r)||this;return i.code=n,i.originalMessage=o,i}return t.prototype.toString=function(){return"[formatjs Error: "+this.code+"] "+this.message},t}(Error);var pn=function(e){ut(t,e);function t(r,n,o,i){return e.call(this,'Invalid values for "'+r+'": "'+n+'". Options are "'+Object.keys(o).join('", "')+'"',Te.INVALID_VALUE,i)||this}return t}(ft);var ni=function(e){ut(t,e);function t(r,n,o){return e.call(this,'Value for "'+r+'" must be of type '+n,Te.INVALID_VALUE,o)||this}return t}(ft);var oi=function(e){ut(t,e);function t(r,n){return e.call(this,'The intl string context variable "'+r+'" was not provided to the string "'+n+'"',Te.MISSING_VALUE,n)||this}return t}(ft);var D;(function(e){e[e.literal=0]="literal",e[e.object=1]="object"})(D||(D={}));function Ra(e){return e.length<2?e:e.reduce(function(t,r){var n=t[t.length-1];return!n||n.type!==D.literal||r.type!==D.literal?t.push(r):n.value+=r.value,t},[])}function Ia(e){return typeof e=="function"}function gt(e,t,r,n,o,i,s){if(e.length===1&&rn(e[0]))return[{type:D.literal,value:e[0].value}];for(var c=[],a=0,h=e;a0?e.substring(0,r):"";let i=xn(e.split("").reverse().join("")),o=n-i,s=e.substring(o,o+1),a=o+(s==="."||s===","?1:0);t.suffix=i>0?e.substring(a,n):"",t.mask=e.substring(r,a),t.maskHasNegativeSign=t.mask.charAt(0)==="-",t.maskHasPositiveSign=t.mask.charAt(0)==="+";let l=t.mask.match(no);return t.decimal=l&&l[l.length-1]||".",t.separator=l&&l[1]&&l[0]||",",l=t.mask.split(t.decimal),t.integer=l[0],t.fraction=l[1],t}function oo(e,t,n){let r=!1,i={value:e};e<0&&(r=!0,i.value=-i.value),i.sign=r?"-":"",i.value=Number(i.value).toFixed(t.fraction&&t.fraction.length),i.value=Number(i.value).toString();let o=t.fraction&&t.fraction.lastIndexOf("0"),[s="0",a=""]=i.value.split(".");return(!a||a&&a.length<=o)&&(a=o<0?"":(+("0."+a)).toFixed(o+1).replace("0.","")),i.integer=s,i.fraction=a,so(i,t),(i.result==="0"||i.result==="")&&(r=!1,i.sign=""),!r&&t.maskHasPositiveSign?i.sign="+":r&&t.maskHasPositiveSign?i.sign="-":r&&(i.sign=n&&n.enforceMaskSign&&!t.maskHasNegativeSign?"":"-"),i}function so(e,t){e.result="";let n=t.integer.split(t.separator),r=n.join(""),i=r&&r.indexOf("0");if(i>-1)for(;e.integer.lengthMath.round(e*20)/20},or=(e,t)=>({accept:e,round:t}),fo=[or(({divisor:e,price:t})=>t%e==0,({divisor:e,price:t})=>t/e),or(({usePrecision:e})=>e,({divisor:e,price:t})=>Math.ceil(Math.floor(t*1e4/e)/100)/100),or(()=>!0,({divisor:e,price:t})=>Math.ceil(Math.floor(t*100/e)/100))],sr={[R.YEAR]:{[O.MONTHLY]:Ue.MONTH,[O.ANNUAL]:Ue.YEAR},[R.MONTH]:{[O.MONTHLY]:Ue.MONTH}},po=(e,t)=>e.indexOf(`'${t}'`)===0,mo=(e,t=!0)=>{let n=e.replace(/'.*?'/,"").trim(),r=bn(n);return!!r?t||(n=n.replace(/[,\.]0+/,r)):n=n.replace(/\s?(#.*0)(?!\s)?/,"$&"+Eo(e)),n},ho=e=>{let t=go(e),n=po(e,t),r=e.replace(/'.*?'/,""),i=vn.test(r)||Pn.test(r);return{currencySymbol:t,isCurrencyFirst:n,hasCurrencySpace:i}},Tn=e=>e.replace(vn,Sn).replace(Pn,Sn),Eo=e=>e.match(/#(.?)#/)?.[1]===_n?co:_n,go=e=>e.match(/'(.*?)'/)?.[1]??"",bn=e=>e.match(/0(.?)0/)?.[1]??"";function rt({formatString:e,price:t,usePrecision:n,isIndianPrice:r=!1},i,o=s=>s){let{currencySymbol:s,isCurrencyFirst:a,hasCurrencySpace:l}=ho(e),u=n?bn(e):"",c=mo(e,n),p=n?2:0,f=o(t,{currencySymbol:s}),h=r?f.toLocaleString("hi-IN",{minimumFractionDigits:p,maximumFractionDigits:p}):yn(c,f),d=n?h.lastIndexOf(u):h.length,_=h.substring(0,d),S=h.substring(d+1);return{accessiblePrice:e.replace(/'.*?'/,"SYMBOL").replace(/#.*0/,h).replace(/SYMBOL/,s),currencySymbol:s,decimals:S,decimalsDelimiter:u,hasCurrencySpace:l,integer:_,isCurrencyFirst:a,recurrenceTerm:i}}var An=e=>{let{commitment:t,term:n,usePrecision:r}=e,i=lo[n]??1;return rt(e,i>1?Ue.MONTH:sr[t]?.[n],(o,{currencySymbol:s})=>{let a={divisor:i,price:o,usePrecision:r},{round:l}=fo.find(({accept:c})=>c(a));if(!l)throw new Error(`Missing rounding rule for: ${JSON.stringify(a)}`);return(uo[s]??(c=>c))(l(a))})},wn=({commitment:e,term:t,...n})=>rt(n,sr[e]?.[t]),Ln=e=>{let{commitment:t,term:n}=e;return t===R.YEAR&&n===O.MONTHLY?rt(e,Ue.YEAR,r=>r*12):rt(e,sr[t]?.[n])};var xo={recurrenceLabel:"{recurrenceTerm, select, MONTH {/mo} YEAR {/yr} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {per month} YEAR {per year} other {}}",perUnitLabel:"{perUnit, select, LICENSE {per license} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {per license} other {}}",freeLabel:"Free",freeAriaLabel:"Free",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"Alternatively at {alternativePrice}",strikethroughAriaLabel:"Regularly at {strikethroughPrice}"},yo=jr("ConsonantTemplates/price"),_o=/<.+?>/g,F={container:"price",containerOptical:"price-optical",containerStrikethrough:"price-strikethrough",containerAnnual:"price-annual",disabled:"disabled",currencySpace:"price-currency-space",currencySymbol:"price-currency-symbol",decimals:"price-decimals",decimalsDelimiter:"price-decimals-delimiter",integer:"price-integer",recurrence:"price-recurrence",taxInclusivity:"price-tax-inclusivity",unitType:"price-unit-type"},ue={perUnitLabel:"perUnitLabel",perUnitAriaLabel:"perUnitAriaLabel",recurrenceLabel:"recurrenceLabel",recurrenceAriaLabel:"recurrenceAriaLabel",taxExclusiveLabel:"taxExclusiveLabel",taxInclusiveLabel:"taxInclusiveLabel",strikethroughAriaLabel:"strikethroughAriaLabel"},So="TAX_EXCLUSIVE",vo=e=>Vr(e)?Object.entries(e).filter(([,t])=>xe(t)||Ye(t)||t===!0).reduce((t,[n,r])=>t+` ${n}${r===!0?"":'="'+Gr(r)+'"'}`,""):"",X=(e,t,n,r=!1)=>`${r?Tn(t):t??""}`;function Po(e,{accessibleLabel:t,currencySymbol:n,decimals:r,decimalsDelimiter:i,hasCurrencySpace:o,integer:s,isCurrencyFirst:a,recurrenceLabel:l,perUnitLabel:u,taxInclusivityLabel:c},p={}){let f=X(F.currencySymbol,n),h=X(F.currencySpace,o?" ":""),d="";return a&&(d+=f+h),d+=X(F.integer,s),d+=X(F.decimalsDelimiter,i),d+=X(F.decimals,r),a||(d+=h+f),d+=X(F.recurrence,l,null,!0),d+=X(F.unitType,u,null,!0),d+=X(F.taxInclusivity,c,!0),X(e,d,{...p,"aria-label":t})}var fe=({displayOptical:e=!1,displayStrikethrough:t=!1,displayAnnual:n=!1}={})=>({country:r,displayFormatted:i=!0,displayRecurrence:o=!0,displayPerUnit:s=!1,displayTax:a=!1,language:l,literals:u={}}={},{commitment:c,formatString:p,price:f,priceWithoutDiscount:h,taxDisplay:d,taxTerm:_,term:S,usePrecision:A}={},w={})=>{Object.entries({country:r,formatString:p,language:l,price:f}).forEach(([Z,vt])=>{if(vt==null)throw new Error(`Argument "${Z}" is missing`)});let P={...xo,...u},C=`${l.toLowerCase()}-${r.toUpperCase()}`;function T(Z,vt){let Pt=P[Z];if(Pt==null)return"";try{return new gn(Pt.replace(_o,""),C).format(vt)}catch{return yo.error("Failed to format literal:",Pt),""}}let L=t&&h?h:f,U=e?An:wn;n&&(U=Ln);let{accessiblePrice:j,recurrenceTerm:q,...ee}=U({commitment:c,formatString:p,term:S,price:e?f:L,usePrecision:A,isIndianPrice:r==="IN"}),H=j,oe="";if(v(o)&&q){let Z=T(ue.recurrenceAriaLabel,{recurrenceTerm:q});Z&&(H+=" "+Z),oe=T(ue.recurrenceLabel,{recurrenceTerm:q})}let se="";if(v(s)){se=T(ue.perUnitLabel,{perUnit:"LICENSE"});let Z=T(ue.perUnitAriaLabel,{perUnit:"LICENSE"});Z&&(H+=" "+Z)}let z="";v(a)&&_&&(z=T(d===So?ue.taxExclusiveLabel:ue.taxInclusiveLabel,{taxTerm:_}),z&&(H+=" "+z)),t&&(H=T(ue.strikethroughAriaLabel,{strikethroughPrice:H}));let W=F.container;if(e&&(W+=" "+F.containerOptical),t&&(W+=" "+F.containerStrikethrough),n&&(W+=" "+F.containerAnnual),v(i))return Po(W,{...ee,accessibleLabel:H,recurrenceLabel:oe,perUnitLabel:se,taxInclusivityLabel:z},w);let{currencySymbol:de,decimals:Ve,decimalsDelimiter:je,hasCurrencySpace:we,integer:St,isCurrencyFirst:Qn}=ee,Ee=[St,je,Ve];Qn?(Ee.unshift(we?"\xA0":""),Ee.unshift(de)):(Ee.push(we?"\xA0":""),Ee.push(de)),Ee.push(oe,se,z);let Kn=Ee.join("");return X(W,Kn,w)},On=()=>(e,t,n)=>{let i=(e.displayOldPrice===void 0||v(e.displayOldPrice))&&t.priceWithoutDiscount&&t.priceWithoutDiscount!=t.price;return`${fe()(e,t,n)}${i?" "+fe({displayStrikethrough:!0})(e,t,n):""}`};var ar=fe(),cr=On(),lr=fe({displayOptical:!0}),ur=fe({displayStrikethrough:!0}),fr=fe({displayAnnual:!0});var To=(e,t)=>{if(!(!_e(e)||!_e(t)))return Math.floor((t-e)/t*100)},Nn=()=>(e,t,n)=>{let{price:r,priceWithoutDiscount:i}=t,o=To(r,i);return o===void 0?'':`${o}%`};var pr=Nn();var{freeze:ke}=Object,Q=ke({...ae}),K=ke({...V}),pe={STAGE:"STAGE",PRODUCTION:"PRODUCTION",LOCAL:"LOCAL"},Cn=ke({...R}),Rn=ke({...Wr}),In=ke({...O});var _r={};ti(_r,{CLASS_NAME_FAILED:()=>nt,CLASS_NAME_PENDING:()=>it,CLASS_NAME_RESOLVED:()=>ot,ERROR_MESSAGE_BAD_REQUEST:()=>st,ERROR_MESSAGE_MISSING_LITERALS_URL:()=>hr,ERROR_MESSAGE_OFFER_NOT_FOUND:()=>mr,EVENT_TYPE_ERROR:()=>bo,EVENT_TYPE_FAILED:()=>at,EVENT_TYPE_PENDING:()=>ct,EVENT_TYPE_READY:()=>me,EVENT_TYPE_RESOLVED:()=>lt,LOG_NAMESPACE:()=>dr,Landscape:()=>Pe,PARAM_AOS_API_KEY:()=>Ao,PARAM_ENV:()=>Er,PARAM_LANDSCAPE:()=>gr,PARAM_WCS_API_KEY:()=>wo,STATE_FAILED:()=>Y,STATE_PENDING:()=>B,STATE_RESOLVED:()=>$,TAG_NAME_SERVICE:()=>ne,WCS_PROD_URL:()=>xr,WCS_STAGE_URL:()=>yr});var nt="placeholder-failed",it="placeholder-pending",ot="placeholder-resolved",st="Bad WCS request",mr="Commerce offer not found",hr="Literals URL not provided",bo="wcms:commerce:error",at="wcms:placeholder:failed",ct="wcms:placeholder:pending",me="wcms:commerce:ready",lt="wcms:placeholder:resolved",dr="wcms/commerce",Er="commerce.env",gr="commerce.landscape",Ao="commerce.aosKey",wo="commerce.wcsKey",xr="https://www.adobe.com/web_commerce_artifact",yr="https://www.stage.adobe.com/web_commerce_artifact_stage",Y="failed",B="pending",$="resolved",ne="wcms-commerce",Pe={DRAFT:"DRAFT",PUBLISHED:"PUBLISHED"};var Sr={clientId:"merch-at-scale",delimiter:"\xB6",ignoredProperties:["analytics","literals"],serializableTypes:["Array","Object"],sampleRate:30,tags:"consumer=milo/commerce"},Mn=new Set,Lo=e=>e instanceof Error||typeof e.originatingRequest=="string";function Dn(e){if(e==null)return;let t=typeof e;if(t==="function"){let{name:n}=e;return n?`${t} ${n}`:t}if(t==="object"){if(e instanceof Error)return e.message;if(typeof e.originatingRequest=="string"){let{message:r,originatingRequest:i,status:o}=e;return[r,o,i].filter(s=>s).join(" ")}let n=e[Symbol.toStringTag]??Object.getPrototypeOf(e).constructor.name;if(!Sr.serializableTypes.includes(n))return n}return e}function Oo(e,t){if(!Sr.ignoredProperties.includes(e))return Dn(t)}var vr={append(e){let{delimiter:t,sampleRate:n,tags:r,clientId:i}=Sr,{message:o,params:s}=e,a=[],l=o,u=[];s.forEach(f=>{f!=null&&(Lo(f)?a:u).push(f)}),a.length&&(l+=" ",l+=a.map(Dn).join(" "));let{pathname:c,search:p}=window.location;l+=`${t}page=`,l+=c+p,u.length&&(l+=`${t}facts=`,l+=JSON.stringify(u,Oo)),Mn.has(l)||(Mn.add(l),window.lana?.log(l,{sampleRate:n,tags:r,clientId:i}))}};var y=Object.freeze({checkoutClientId:"adobe_com",checkoutWorkflow:Q.V3,checkoutWorkflowStep:K.EMAIL,country:"US",displayOldPrice:!0,displayPerUnit:!1,displayRecurrence:!0,displayTax:!1,env:pe.PRODUCTION,forceTaxExclusive:!1,language:"en",entitlement:!1,extraOptions:{},modal:!1,promotionCode:"",quantity:1,wcsApiKey:"wcms-commerce-ims-ro-user-milo",wcsBufferDelay:1,wcsURL:"https://www.adobe.com/web_commerce_artifact",landscape:Pe.PUBLISHED,wcsBufferLimit:1});function Un(e,{once:t=!1}={}){let n=null;function r(){let i=document.querySelector(ne);i!==n&&(n=i,i&&e(i))}return document.addEventListener(me,r,{once:t}),ie(r),()=>document.removeEventListener(me,r)}function Ge(e,{country:t,forceTaxExclusive:n,perpetual:r}){let i;if(e.length<2)i=e;else{let o=t==="GB"||r?"EN":"MULT",[s,a]=e;i=[s.language===o?s:a]}return n&&(i=i.map(Xt)),i}var ie=e=>window.setTimeout(e);function Te(e,t=1){if(e==null)return[t];let n=(Array.isArray(e)?e:String(e).split(",")).map(ve).filter(_e);return n.length||(n=[t]),n}function ut(e){return e==null?[]:(Array.isArray(e)?e:String(e).split(",")).filter(Ut)}function k(){return window.customElements.get(ne)?.instance}var No="en_US",m={ar:"AR_es",be_en:"BE_en",be_fr:"BE_fr",be_nl:"BE_nl",br:"BR_pt",ca:"CA_en",ch_de:"CH_de",ch_fr:"CH_fr",ch_it:"CH_it",cl:"CL_es",co:"CO_es",la:"DO_es",mx:"MX_es",pe:"PE_es",africa:"MU_en",dk:"DK_da",de:"DE_de",ee:"EE_et",eg_ar:"EG_ar",eg_en:"EG_en",es:"ES_es",fr:"FR_fr",gr_el:"GR_el",gr_en:"GR_en",ie:"IE_en",il_he:"IL_iw",it:"IT_it",lv:"LV_lv",lt:"LT_lt",lu_de:"LU_de",lu_en:"LU_en",lu_fr:"LU_fr",my_en:"MY_en",my_ms:"MY_ms",hu:"HU_hu",mt:"MT_en",mena_en:"DZ_en",mena_ar:"DZ_ar",nl:"NL_nl",no:"NO_nb",pl:"PL_pl",pt:"PT_pt",ro:"RO_ro",si:"SI_sl",sk:"SK_sk",fi:"FI_fi",se:"SE_sv",tr:"TR_tr",uk:"GB_en",at:"AT_de",cz:"CZ_cs",bg:"BG_bg",ru:"RU_ru",ua:"UA_uk",au:"AU_en",in_en:"IN_en",in_hi:"IN_hi",id_en:"ID_en",id_id:"ID_in",nz:"NZ_en",sa_ar:"SA_ar",sa_en:"SA_en",sg:"SG_en",cn:"CN_zh-Hans",tw:"TW_zh-Hant",hk_zh:"HK_zh-hant",jp:"JP_ja",kr:"KR_ko",za:"ZA_en",ng:"NG_en",cr:"CR_es",ec:"EC_es",pr:"US_es",gt:"GT_es",cis_en:"AZ_en",cis_ru:"AZ_ru",sea:"SG_en",th_en:"TH_en",th_th:"TH_th"},ft=Object.freeze({LOCAL:"local",PROD:"prod",STAGE:"stage"});function Co({locale:e={}}={}){if(!e.prefix)return{country:y.country,language:y.language,locale:No};let t=e.prefix.replace("/","")??"",[n=y.country,r=y.language]=(m[t]??t).split("_",2);return n=n.toUpperCase(),r=r.toLowerCase(),{country:n,language:r,locale:`${r}_${n}`}}function kn(e={}){let{commerce:t={},locale:n=void 0}=e,r=pe.PRODUCTION,i=xr,o=["local","stage"].includes(e.env?.name),s=N(Er,t,{metadata:!1})==="stage";o&&s&&(r=pe.STAGE,i=yr);let a=N("checkoutClientId",t)??y.checkoutClientId,l=re(N("checkoutWorkflow",t),Q,y.checkoutWorkflow),u=K.CHECKOUT;l===Q.V3&&(u=re(N("checkoutWorkflowStep",t),K,y.checkoutWorkflowStep));let c=v(N("displayOldPrice",t),y.displayOldPrice),p=v(N("displayPerUnit",t),y.displayPerUnit),f=v(N("displayRecurrence",t),y.displayRecurrence),h=v(N("displayTax",t),y.displayTax),d=v(N("entitlement",t),y.entitlement),_=v(N("modal",t),y.modal),S=v(N("forceTaxExclusive",t),y.forceTaxExclusive),A=N("promotionCode",t)??y.promotionCode,w=Te(N("quantity",t)),P=N("wcsApiKey",t)??y.wcsApiKey,C=e.env?.name===ft.PROD?Pe.PUBLISHED:re(N(gr,t),Pe,y.landscape),T=ve(N("wcsBufferDelay",t),y.wcsBufferDelay),L=ve(N("wcsBufferLimit",t),y.wcsBufferLimit);return{...Co({locale:n}),displayOldPrice:c,checkoutClientId:a,checkoutWorkflow:l,checkoutWorkflowStep:u,displayPerUnit:p,displayRecurrence:f,displayTax:h,entitlement:d,extraOptions:y.extraOptions,modal:_,env:r,forceTaxExclusive:S,priceLiteralsURL:t.priceLiteralsURL,priceLiteralsPromise:t.priceLiteralsPromise,promotionCode:A,quantity:w,wcsApiKey:P,wcsBufferDelay:T,wcsBufferLimit:L,wcsURL:i,landscape:C}}var Fn="debug",Ro="error",Io="info",Mo="warn",Do=Date.now(),Pr=new Set,Tr=new Set,Gn=new Map,Fe=Object.freeze({DEBUG:Fn,ERROR:Ro,INFO:Io,WARN:Mo}),Vn={append({level:e,message:t,params:n,timestamp:r,source:i}){console[e](`${r}ms [${i}] %c${t}`,"font-weight: bold;",...n)}},jn={filter:({level:e})=>e!==Fn},Uo={filter:()=>!1};function ko(e,t,n,r,i){return{level:e,message:t,namespace:n,get params(){if(r.length===1){let[o]=r;te(o)&&(r=o(),Array.isArray(r)||(r=[r]))}return r},source:i,timestamp:Date.now()-Do}}function Go(e){[...Tr].every(t=>t(e))&&Pr.forEach(t=>t(e))}function Hn(e){let t=(Gn.get(e)??0)+1;Gn.set(e,t);let n=`${e} #${t}`,r=o=>(s,...a)=>Go(ko(o,s,e,a,n)),i=Object.seal({id:n,namespace:e,module(o){return Hn(`${i.namespace}/${o}`)},debug:r(Fe.DEBUG),error:r(Fe.ERROR),info:r(Fe.INFO),warn:r(Fe.WARN)});return i}function pt(...e){e.forEach(t=>{let{append:n,filter:r}=t;te(r)?Tr.add(r):te(n)&&Pr.add(n)})}function Fo(e={}){let{name:t}=e,n=v(N("commerce.debug",{search:!0,storage:!0}),t===ft.LOCAL);return pt(n?Vn:jn),t===ft.PROD&&pt(vr),M}function Vo(){Pr.clear(),Tr.clear()}var M={...Hn(dr),Level:Fe,Plugins:{consoleAppender:Vn,debugFilter:jn,quietFilter:Uo,lanaAppender:vr},init:Fo,reset:Vo,use:pt};var jo={CLASS_NAME_FAILED:nt,CLASS_NAME_PENDING:it,CLASS_NAME_RESOLVED:ot,EVENT_TYPE_FAILED:at,EVENT_TYPE_PENDING:ct,EVENT_TYPE_RESOLVED:lt,STATE_FAILED:Y,STATE_PENDING:B,STATE_RESOLVED:$},Ho={[Y]:nt,[B]:it,[$]:ot},Wo={[Y]:at,[B]:ct,[$]:lt},dt=new WeakMap;function G(e){if(!dt.has(e)){let t=M.module(e.constructor.is);dt.set(e,{changes:new Map,connected:!1,dispose:ye,error:void 0,log:t,options:void 0,promises:[],state:B,timer:null,value:void 0,version:0})}return dt.get(e)}function mt(e){let t=G(e),{error:n,promises:r,state:i}=t;(i===$||i===Y)&&(t.promises=[],i===$?r.forEach(({resolve:o})=>o(e)):i===Y&&r.forEach(({reject:o})=>o(n))),e.dispatchEvent(new CustomEvent(Wo[i],{bubbles:!0}))}function ht(e){let t=dt.get(e);[Y,B,$].forEach(n=>{e.classList.toggle(Ho[n],n===t.state)})}var Xo={get error(){return G(this).error},get log(){return G(this).log},get options(){return G(this).options},get state(){return G(this).state},get value(){return G(this).value},attributeChangedCallback(e,t,n){G(this).changes.set(e,n),this.requestUpdate()},connectedCallback(){G(this).dispose=Un(()=>this.requestUpdate(!0))},disconnectedCallback(){let e=G(this);e.connected&&(e.connected=!1,e.log.debug("Disconnected:",{element:this})),e.dispose(),e.dispose=ye},onceSettled(){let{error:e,promises:t,state:n}=G(this);return $===n?Promise.resolve(this):Y===n?Promise.reject(e):new Promise((r,i)=>{t.push({resolve:r,reject:i})})},toggleResolved(e,t,n){let r=G(this);return e!==r.version?!1:(n!==void 0&&(r.options=n),r.state=$,r.value=t,ht(this),this.log.debug("Resolved:",{element:this,value:t}),ie(()=>mt(this)),!0)},toggleFailed(e,t,n){let r=G(this);return e!==r.version?!1:(n!==void 0&&(r.options=n),r.error=t,r.state=Y,ht(this),r.log.error("Failed:",{element:this,error:t}),ie(()=>mt(this)),!0)},togglePending(e){let t=G(this);return t.version++,e&&(t.options=e),t.state=B,ht(this),ie(()=>mt(this)),t.version},requestUpdate(e=!1){if(!this.isConnected||!k())return;let t=G(this);if(t.timer)return;let{error:n,options:r,state:i,value:o,version:s}=t;t.state=B,t.timer=ie(async()=>{t.timer=null;let a=null;if(t.changes.size&&(a=Object.fromEntries(t.changes.entries()),t.changes.clear()),t.connected?t.log.debug("Updated:",{element:this,changes:a}):(t.connected=!0,t.log.debug("Connected:",{element:this,changes:a})),a||e)try{await this.render?.()===!1&&t.state===B&&t.version===s&&(t.state=i,t.error=n,t.value=o,ht(this),mt(this))}catch(l){this.toggleFailed(t.version,l,r)}})}};function Wn(e={}){return Object.entries(e).forEach(([t,n])=>{(n==null||n===""||n?.length===0)&&delete e[t]}),e}function Et(e,t={}){let{tag:n,is:r}=e,i=document.createElement(n,{is:r});return i.setAttribute("is",r),Object.assign(i.dataset,Wn(t)),i}function gt(e){let{tag:t,is:n,prototype:r}=e,i=window.customElements.get(n);return i||(Object.defineProperties(r,Object.getOwnPropertyDescriptors(Xo)),i=Object.defineProperties(e,Object.getOwnPropertyDescriptors(jo)),window.customElements.define(n,i,{extends:t})),i}function xt(e,t=document.body){return Array.from(t?.querySelectorAll(`${e.tag}[is="${e.is}"]`)??[])}function yt(e,t={}){return e instanceof HTMLElement?(Object.assign(e.dataset,Wn(t)),e):null}var Yo="download",Bo="upgrade",he,be=class be extends HTMLAnchorElement{constructor(){super();Ir(this,he);this.addEventListener("click",this.clickHandler)}static get observedAttributes(){return["data-checkout-workflow","data-checkout-workflow-step","data-extra-options","data-ims-country","data-perpetual","data-promotion-code","data-quantity","data-template","data-wcs-osi","data-entitlement","data-upgrade","data-modal"]}static createCheckoutLink(n={},r=""){let i=k();if(!i)return null;let{checkoutMarketSegment:o,checkoutWorkflow:s,checkoutWorkflowStep:a,entitlement:l,upgrade:u,modal:c,perpetual:p,promotionCode:f,quantity:h,wcsOsi:d,extraOptions:_}=i.collectCheckoutOptions(n),S=Et(be,{checkoutMarketSegment:o,checkoutWorkflow:s,checkoutWorkflowStep:a,entitlement:l,upgrade:u,modal:c,perpetual:p,promotionCode:f,quantity:h,wcsOsi:d,extraOptions:_});return r&&(S.innerHTML=`${r}`),S}static getCheckoutLinks(n){return xt(be,n)}get isCheckoutLink(){return!0}get placeholder(){return this}clickHandler(n){var r;(r=Tt(this,he))==null||r.call(this,n)}async render(n={}){if(!this.isConnected)return!1;let r=k();if(!r)return!1;this.dataset.imsCountry||r.imsCountryPromise.then(c=>{c&&(this.dataset.imsCountry=c)},ye);let i=r.collectCheckoutOptions(n,this.placeholder);if(!i.wcsOsi.length)return!1;let o;try{o=JSON.parse(i.extraOptions??"{}")}catch(c){this.placeholder.log.error("cannot parse exta checkout options",c)}let s=this.placeholder.togglePending(i);this.href="";let a=r.resolveOfferSelectors(i),l=await Promise.all(a);l=l.map(c=>Ge(c,i));let u=await r.buildCheckoutAction(l.flat(),{...o,...i});return this.renderOffers(l.flat(),i,{},u,s)}renderOffers(n,r,i={},o=void 0,s=void 0){if(!this.isConnected)return!1;let a=k();if(!a)return!1;if(r={...JSON.parse(this.placeholder.dataset.extraOptions??"null"),...r,...i},s??(s=this.placeholder.togglePending(r)),Tt(this,he)&&bt(this,he,void 0),o){this.classList.remove(Yo,Bo),this.placeholder.toggleResolved(s,n,r);let{url:u,text:c,className:p,handler:f}=o;return u&&(this.href=u),c&&(this.firstElementChild.innerHTML=c),p&&this.classList.add(...p.split(" ")),f&&(this.setAttribute("href","#"),bt(this,he,f.bind(this))),!0}else if(n.length){if(this.placeholder.toggleResolved(s,n,r)){let u=a.buildCheckoutURL(n,r);return this.setAttribute("href",u),!0}}else{let u=new Error(`Not provided: ${r?.wcsOsi??"-"}`);if(this.placeholder.toggleFailed(s,u,r))return this.setAttribute("href","#"),!0}return!1}updateOptions(n={}){let r=k();if(!r)return!1;let{checkoutMarketSegment:i,checkoutWorkflow:o,checkoutWorkflowStep:s,entitlement:a,upgrade:l,modal:u,perpetual:c,promotionCode:p,quantity:f,wcsOsi:h}=r.collectCheckoutOptions(n);return yt(this,{checkoutMarketSegment:i,checkoutWorkflow:o,checkoutWorkflowStep:s,entitlement:a,upgrade:l,modal:u,perpetual:c,promotionCode:p,quantity:f,wcsOsi:h}),!0}};he=new WeakMap,J(be,"is","checkout-link"),J(be,"tag","a");var br=be,Ar=gt(br);var Xn=[m.uk,m.au,m.fr,m.at,m.be_en,m.be_fr,m.be_nl,m.bg,m.ch_de,m.ch_fr,m.ch_it,m.cz,m.de,m.dk,m.ee,m.eg_ar,m.eg_en,m.es,m.fi,m.fr,m.gr_el,m.gr_en,m.hu,m.ie,m.it,m.lu_de,m.lu_en,m.lu_fr,m.nl,m.no,m.pl,m.pt,m.ro,m.se,m.si,m.sk,m.tr,m.ua,m.id_en,m.id_id,m.in_en,m.in_hi,m.jp,m.my_en,m.my_ms,m.nz,m.th_en,m.th_th],$o={INDIVIDUAL_COM:[m.za,m.lt,m.lv,m.ng,m.sa_ar,m.sa_en,m.za,m.sg,m.kr],TEAM_COM:[m.za,m.lt,m.lv,m.ng,m.za,m.co,m.kr],INDIVIDUAL_EDU:[m.lt,m.lv,m.sa_en,m.sea],TEAM_EDU:[m.sea,m.kr]},Ae=class Ae extends HTMLSpanElement{static get observedAttributes(){return["data-display-old-price","data-display-per-unit","data-display-recurrence","data-display-tax","data-perpetual","data-promotion-code","data-tax-exclusive","data-template","data-wcs-osi"]}static createInlinePrice(t){let n=k();if(!n)return null;let{displayOldPrice:r,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:l,promotionCode:u,quantity:c,template:p,wcsOsi:f}=n.collectPriceOptions(t);return Et(Ae,{displayOldPrice:r,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:l,promotionCode:u,quantity:c,template:p,wcsOsi:f})}static getInlinePrices(t){return xt(Ae,t)}get isInlinePrice(){return!0}get placeholder(){return this}resolveDisplayTaxForGeoAndSegment(t,n,r,i){let o=`${t}_${n}`;if(Xn.includes(t)||Xn.includes(o))return!0;let s=$o[`${r}_${i}`];return s?!!(s.includes(t)||s.includes(o)):!1}async resolveDisplayTax(t,n){let[r]=await t.resolveOfferSelectors(n),i=Ge(await r,n);if(i?.length){let{country:o,language:s}=n,a=i[0],[l=""]=a.marketSegments;return this.resolveDisplayTaxForGeoAndSegment(o,s,a.customerSegment,l)}}async render(t={}){if(!this.isConnected)return!1;let n=k();if(!n)return!1;let r=n.collectPriceOptions(t,this.placeholder);if(!r.wcsOsi.length)return!1;let i=this.placeholder.togglePending(r);this.innerHTML="";let[o]=n.resolveOfferSelectors(r);return this.renderOffers(Ge(await o,r),r,i)}renderOffers(t,n={},r=void 0){if(!this.isConnected)return;let i=k();if(!i)return!1;let o=i.collectPriceOptions({...this.dataset,...n});if(r??(r=this.placeholder.togglePending(o)),t.length){if(this.placeholder.toggleResolved(r,t,o))return this.innerHTML=i.buildPriceHTML(t,o),!0}else{let s=new Error(`Not provided: ${o?.wcsOsi??"-"}`);if(this.placeholder.toggleFailed(r,s,o))return this.innerHTML="",!0}return!1}updateOptions(t){let n=k();if(!n)return!1;let{displayOldPrice:r,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:l,promotionCode:u,quantity:c,template:p,wcsOsi:f}=n.collectPriceOptions(t);return yt(this,{displayOldPrice:r,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:l,promotionCode:u,quantity:c,template:p,wcsOsi:f}),!0}};J(Ae,"is","inline-price"),J(Ae,"tag","span");var wr=Ae,Lr=gt(wr);function Yn({providers:e,settings:t},n){let r=M.module("checkout");function i(u,c){let{checkoutClientId:p,checkoutWorkflow:f,checkoutWorkflowStep:h,country:d,language:_,promotionCode:S,quantity:A}=t,{checkoutMarketSegment:w,checkoutWorkflow:P=f,checkoutWorkflowStep:C=h,imsCountry:T,country:L=T??d,language:U=_,quantity:j=A,entitlement:q,upgrade:ee,modal:H,perpetual:oe,promotionCode:se=S,wcsOsi:z,extraOptions:W,...de}=Object.assign({},c?.dataset??{},u??{}),Ve=re(P,Q,y.checkoutWorkflow),je=K.CHECKOUT;Ve===Q.V3&&(je=re(C,K,y.checkoutWorkflowStep));let we=Se({...de,extraOptions:W,checkoutClientId:p,checkoutMarketSegment:w,country:L,quantity:Te(j,y.quantity),checkoutWorkflow:Ve,checkoutWorkflowStep:je,language:U,entitlement:v(q),upgrade:v(ee),modal:v(H),perpetual:v(oe),promotionCode:Ne(se).effectivePromoCode,wcsOsi:ut(z)});if(c)for(let St of e.checkout)St(c,we);return we}async function o(u,c){let p=k(),f=await n.getCheckoutAction?.(u,c,p.imsSignedInPromise);return f||null}function s(u,c){if(!Array.isArray(u)||!u.length||!c)return"";let{env:p,landscape:f}=t,{checkoutClientId:h,checkoutMarketSegment:d,checkoutWorkflow:_,checkoutWorkflowStep:S,country:A,promotionCode:w,quantity:P,...C}=i(c),T=window.frameElement?"if":"fp",L={checkoutPromoCode:w,clientId:h,context:T,country:A,env:p,items:[],marketSegment:d,workflowStep:S,landscape:f,...C};if(u.length===1){let[{offerId:U,offerType:j,productArrangementCode:q}]=u,{marketSegments:[ee]}=u[0];Object.assign(L,{marketSegment:ee,offerType:j,productArrangementCode:q}),L.items.push(P[0]===1?{id:U}:{id:U,quantity:P[0]})}else L.items.push(...u.map(({offerId:U},j)=>({id:U,quantity:P[j]??y.quantity})));return Nt(_,L)}let{createCheckoutLink:a,getCheckoutLinks:l}=Ar;return{CheckoutLink:Ar,CheckoutWorkflow:Q,CheckoutWorkflowStep:K,buildCheckoutAction:o,buildCheckoutURL:s,collectCheckoutOptions:i,createCheckoutLink:a,getCheckoutLinks:l}}function qo({interval:e=200,maxAttempts:t=25}={}){let n=M.module("ims");return new Promise(r=>{n.debug("Waing for IMS to be ready");let i=0;function o(){window.adobeIMS?.initialized?r():++i>t?(n.debug("Timeout"),r()):setTimeout(o,e)}o()})}function zo(e){return e.then(()=>window.adobeIMS?.isSignedInUser()??!1)}function Zo(e){let t=M.module("ims");return e.then(n=>n?window.adobeIMS.getProfile().then(({countryCode:r})=>(t.debug("Got user country:",r),r),r=>{t.error("Unable to get user country:",r)}):null)}function Bn({}){let e=qo(),t=zo(e),n=Zo(t);return{imsReadyPromise:e,imsSignedInPromise:t,imsCountryPromise:n}}function Jo(e){if(!e.priceLiteralsURL)throw new Error(hr);return new Promise(t=>{window.fetch(e.priceLiteralsURL).then(n=>{n.json().then(({data:r})=>{t(r)})})})}async function $n(e){let n=await(e.priceLiteralsPromise||Jo(e));if(Array.isArray(n)){let r=o=>n.find(s=>Xe(s.lang,o)),i=r(e.language)??r(y.language);if(i)return Object.freeze(i)}return{}}function qn({literals:e,providers:t,settings:n}){function r(a,l){let{country:u,displayOldPrice:c,displayPerUnit:p,displayRecurrence:f,displayTax:h,forceTaxExclusive:d,language:_,promotionCode:S,quantity:A}=n,{displayOldPrice:w=c,displayPerUnit:P=p,displayRecurrence:C=f,displayTax:T=h,forceTaxExclusive:L=d,country:U=u,language:j=_,perpetual:q,promotionCode:ee=S,quantity:H=A,template:oe,wcsOsi:se,...z}=Object.assign({},l?.dataset??{},a??{}),W=Se({...z,country:U,displayOldPrice:v(w),displayPerUnit:v(P),displayRecurrence:v(C),displayTax:v(T),forceTaxExclusive:v(L),language:j,perpetual:v(q),promotionCode:Ne(ee).effectivePromoCode,quantity:Te(H,y.quantity),template:oe,wcsOsi:ut(se)});if(l)for(let de of t.price)de(l,W);return W}function i(a,l){if(!Array.isArray(a)||!a.length||!l)return"";let{template:u}=l,c;switch(u){case"discount":c=pr;break;case"strikethrough":c=ur;break;case"optical":c=lr;break;case"annual":c=fr;break;default:c=l.promotionCode?cr:ar}let p=r(l);p.literals=Object.assign({},e.price,Se(l.literals??{}));let[f]=a;return f={...f,...f.priceDetails},c(p,f)}let{createInlinePrice:o,getInlinePrices:s}=Lr;return{InlinePrice:Lr,buildPriceHTML:i,collectPriceOptions:r,createInlinePrice:o,getInlinePrices:s}}function zn({settings:e}){let t=M.module("wcs"),{env:n,wcsApiKey:r}=e,i=new Map,o=new Map,s;async function a(c,p,f=!0){let h=mr;t.debug("Fetching:",c);try{c.offerSelectorIds=c.offerSelectorIds.sort();let d=new URL(e.wcsURL);d.searchParams.set("offer_selector_ids",c.offerSelectorIds.join(",")),d.searchParams.set("country",c.country),d.searchParams.set("language",c.language),d.searchParams.set("locale",c.locale),d.searchParams.set("landscape",n===pe.STAGE?"ALL":e.landscape),d.searchParams.set("api_key",r),c.promotionCode&&d.searchParams.set("promotion_code",c.promotionCode),c.currency&&d.searchParams.set("currency",c.currency);let _=await fetch(d.toString());if(_.ok){let S=await _.json();t.debug("Fetched:",c,S);let A=S.resolvedOffers??[];A=A.map(Wt),p.forEach(({resolve:w},P)=>{let C=A.filter(({offerSelectorIds:T})=>T.includes(P)).flat();C.length&&(p.delete(P),w(C))})}else _.status===404&&c.offerSelectorIds.length>1?(t.debug("Multi-osi 404, fallback to fetch-by-one strategy"),await Promise.allSettled(c.offerSelectorIds.map(S=>a({...c,offerSelectorIds:[S]},p,!1)))):(h=st,t.error(h,c))}catch(d){h=st,t.error(h,c,d)}f&&p.size&&(t.debug("Missing:",{offerSelectorIds:[...p.keys()]}),p.forEach(d=>{d.reject(new Error(h))}))}function l(){clearTimeout(s);let c=[...o.values()];o.clear(),c.forEach(({options:p,promises:f})=>a(p,f))}function u({country:c,language:p,perpetual:f=!1,promotionCode:h="",wcsOsi:d=[]}){let _=`${p}_${c}`;c!=="GB"&&(p=f?"EN":"MULT");let S=[c,p,h].filter(A=>A).join("-").toLowerCase();return d.map(A=>{let w=`${A}-${S}`;if(!i.has(w)){let P=new Promise((C,T)=>{let L=o.get(S);if(!L){let U={country:c,locale:_,offerSelectorIds:[]};c!=="GB"&&(U.language=p),L={options:U,promises:new Map},o.set(S,L)}h&&(L.options.promotionCode=h),L.options.offerSelectorIds.push(A),L.promises.set(A,{resolve:C,reject:T}),L.options.offerSelectorIds.length>=e.wcsBufferLimit?l():(t.debug("Queued:",L.options),s||(s=setTimeout(l,e.wcsBufferDelay)))});i.set(w,P)}return i.get(w)})}return{WcsCommitment:Cn,WcsPlanType:Rn,WcsTerm:In,resolveOfferSelectors:u}}var D=class extends HTMLElement{get isWcmsCommerce(){return!0}};J(D,"instance"),J(D,"promise",null);window.customElements.define(ne,D);async function Qo(e,t){let n=M.init(e.env).module("service");n.debug("Activating:",e);let r={price:{}},i=Object.freeze(kn(e));try{r.price=await $n(i)}catch(l){n.warn("Price literals were not fetched:",l)}let o={checkout:new Set,price:new Set},s=document.createElement(ne),a={literals:r,providers:o,settings:i};return D.instance=Object.defineProperties(s,Object.getOwnPropertyDescriptors({...Yn(a,t),...Bn(a),...qn(a),...zn(a),..._r,Log:M,get defaults(){return y},get literals(){return r},get log(){return M},get providers(){return{checkout(l){return o.checkout.add(l),()=>o.checkout.delete(l)},price(l){return o.price.add(l),()=>o.price.delete(l)}}},get settings(){return i}})),n.debug("Activated:",{literals:r,settings:i,element:s}),document.head.append(s),ie(()=>{let l=new CustomEvent(me,{bubbles:!0,cancelable:!1,detail:D.instance});D.instance.dispatchEvent(l)}),D.instance}function Zn(){document.head.querySelector(ne)?.remove(),D.promise=null,M.reset()}function Or(e,t){if(te(e)){let n=te(t)?t():{};return n.force&&Zn(),D.promise??(D.promise=Qo(e(),n))}return D.promise?D.promise:new Promise(n=>{let r=i=>{n(i.detail)};document.head.addEventListener(me,r,{once:!0})})}var{origin:Ko,searchParams:_t}=new URL(import.meta.url),es=_t.get("locale")??"US_en",nl=_t.get("lang")??"en",Jn=_t.get("env")==="stage",ts=_t.get("features"),rs=Jn?"stage":"prod",ns=Jn?"STAGE":"PROD",is=()=>({env:{name:rs},commerce:{"commerce.env":ns},locale:{prefix:es}});Or(is);ts.includes("merch-card")&&import(`${Ko}/libs/deps/merch-card-all.js`); -//# sourceMappingURL=mas.js.map +`,Te.MISSING_INTL_API,s);var I=r.getPluralRules(t,{type:l.pluralType}).select(u-(l.offset||0));A=l.options[I]||l.options.other}if(!A)throw new pn(l.value,u,Object.keys(l.options),s);c.push.apply(c,gt(A.value,t,r,n,o,u-(l.offset||0)));continue}}return Ra(c)}function ka(e,t){return t?E(E(E({},e||{}),t||{}),Object.keys(e).reduce(function(r,n){return r[n]=E(E({},e[n]),t[n]||{}),r},{})):e}function Ma(e,t){return t?Object.keys(e).reduce(function(r,n){return r[n]=ka(e[n],t[n]),r},E({},e)):e}function fn(e){return{create:function(){return{get:function(t){return e[t]},set:function(t,r){e[t]=r}}}}}function Ua(e){return e===void 0&&(e={number:{},dateTime:{},pluralRules:{}}),{getNumberFormat:pt(function(){for(var t,r=[],n=0;n0?e.substring(0,n):"";let o=ai(e.split("").reverse().join("")),i=r-o,s=e.substring(i,i+1),c=i+(s==="."||s===","?1:0);t.suffix=o>0?e.substring(c,r):"",t.mask=e.substring(n,c),t.maskHasNegativeSign=t.mask.charAt(0)==="-",t.maskHasPositiveSign=t.mask.charAt(0)==="+";let a=t.mask.match(Fa);return t.decimal=a&&a[a.length-1]||".",t.separator=a&&a[1]&&a[0]||",",a=t.mask.split(t.decimal),t.integer=a[0],t.fraction=a[1],t}function za(e,t,r){let n=!1,o={value:e};e<0&&(n=!0,o.value=-o.value),o.sign=n?"-":"",o.value=Number(o.value).toFixed(t.fraction&&t.fraction.length),o.value=Number(o.value).toString();let i=t.fraction&&t.fraction.lastIndexOf("0"),[s="0",c=""]=o.value.split(".");return(!c||c&&c.length<=i)&&(c=i<0?"":(+("0."+c)).toFixed(i+1).replace("0.","")),o.integer=s,o.fraction=c,Ga(o,t),(o.result==="0"||o.result==="")&&(n=!1,o.sign=""),!n&&t.maskHasPositiveSign?o.sign="+":n&&t.maskHasPositiveSign?o.sign="-":n&&(o.sign=r&&r.enforceMaskSign&&!t.maskHasNegativeSign?"":"-"),o}function Ga(e,t){e.result="";let r=t.integer.split(t.separator),n=r.join(""),o=n&&n.indexOf("0");if(o>-1)for(;e.integer.lengthMath.round(e*20)/20},gn=(e,t)=>({accept:e,round:t}),qa=[gn(({divisor:e,price:t})=>t%e==0,({divisor:e,price:t})=>t/e),gn(({usePrecision:e})=>e,({divisor:e,price:t})=>Math.ceil(Math.floor(t*1e4/e)/100)/100),gn(()=>!0,({divisor:e,price:t})=>Math.ceil(Math.floor(t*100/e)/100))],xn={[k.YEAR]:{[L.MONTHLY]:xt.MONTH,[L.ANNUAL]:xt.YEAR},[k.MONTH]:{[L.MONTHLY]:xt.MONTH}},Ya=(e,t)=>e.indexOf(`'${t}'`)===0,Xa=(e,t=!0)=>{let r=e.replace(/'.*?'/,"").trim(),n=pi(r);return!!n?t||(r=r.replace(/[,\.]0+/,n)):r=r.replace(/\s?(#.*0)(?!\s)?/,"$&"+Ja(e)),r},Za=e=>{let t=Qa(e),r=Ya(e,t),n=e.replace(/'.*?'/,""),o=di.test(n)||ui.test(n);return{currencySymbol:t,isCurrencyFirst:r,hasCurrencySpace:o}},mi=e=>e.replace(di,hi).replace(ui,hi),Ja=e=>e.match(/#(.?)#/)?.[1]===li?ja:li,Qa=e=>e.match(/'(.*?)'/)?.[1]??"",pi=e=>e.match(/0(.?)0/)?.[1]??"";function Kt({formatString:e,price:t,usePrecision:r,isIndianPrice:n=!1},o,i=s=>s){let{currencySymbol:s,isCurrencyFirst:c,hasCurrencySpace:a}=Za(e),h=r?pi(e):"",l=Xa(e,r),d=r?2:0,u=i(t,{currencySymbol:s}),m=n?u.toLocaleString("hi-IN",{minimumFractionDigits:d,maximumFractionDigits:d}):ci(l,u),f=r?m.lastIndexOf(h):m.length,g=m.substring(0,f),_=m.substring(f+1);return{accessiblePrice:e.replace(/'.*?'/,"SYMBOL").replace(/#.*0/,m).replace(/SYMBOL/,s),currencySymbol:s,decimals:_,decimalsDelimiter:h,hasCurrencySpace:a,integer:g,isCurrencyFirst:c,recurrenceTerm:o}}var fi=e=>{let{commitment:t,term:r,usePrecision:n}=e,o=Ba[r]??1;return Kt(e,o>1?xt.MONTH:xn[t]?.[r],(i,{currencySymbol:s})=>{let c={divisor:o,price:i,usePrecision:n},{round:a}=qa.find(({accept:l})=>l(c));if(!a)throw new Error(`Missing rounding rule for: ${JSON.stringify(c)}`);return(Wa[s]??(l=>l))(a(c))})},gi=({commitment:e,term:t,...r})=>Kt(r,xn[e]?.[t]),xi=e=>{let{commitment:t,term:r}=e;return t===k.YEAR&&r===L.MONTHLY?Kt(e,xt.YEAR,n=>n*12):Kt(e,xn[t]?.[r])};var Ka={recurrenceLabel:"{recurrenceTerm, select, MONTH {/mo} YEAR {/yr} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {per month} YEAR {per year} other {}}",perUnitLabel:"{perUnit, select, LICENSE {per license} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {per license} other {}}",freeLabel:"Free",freeAriaLabel:"Free",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"Alternatively at {alternativePrice}",strikethroughAriaLabel:"Regularly at {strikethroughPrice}"},ec=Lo("ConsonantTemplates/price"),tc=/<.+?>/g,W={container:"price",containerOptical:"price-optical",containerStrikethrough:"price-strikethrough",containerAnnual:"price-annual",disabled:"disabled",currencySpace:"price-currency-space",currencySymbol:"price-currency-symbol",decimals:"price-decimals",decimalsDelimiter:"price-decimals-delimiter",integer:"price-integer",recurrence:"price-recurrence",taxInclusivity:"price-tax-inclusivity",unitType:"price-unit-type"},Pe={perUnitLabel:"perUnitLabel",perUnitAriaLabel:"perUnitAriaLabel",recurrenceLabel:"recurrenceLabel",recurrenceAriaLabel:"recurrenceAriaLabel",taxExclusiveLabel:"taxExclusiveLabel",taxInclusiveLabel:"taxInclusiveLabel",strikethroughAriaLabel:"strikethroughAriaLabel"},rc="TAX_EXCLUSIVE",nc=e=>Oo(e)?Object.entries(e).filter(([,t])=>Be(t)||Gt(t)||t===!0).reduce((t,[r,n])=>t+` ${r}${n===!0?"":'="'+Co(n)+'"'}`,""):"",ee=(e,t,r,n=!1)=>`${n?mi(t):t??""}`;function oc(e,{accessibleLabel:t,currencySymbol:r,decimals:n,decimalsDelimiter:o,hasCurrencySpace:i,integer:s,isCurrencyFirst:c,recurrenceLabel:a,perUnitLabel:h,taxInclusivityLabel:l},d={}){let u=ee(W.currencySymbol,r),m=ee(W.currencySpace,i?" ":""),f="";return c&&(f+=u+m),f+=ee(W.integer,s),f+=ee(W.decimalsDelimiter,o),f+=ee(W.decimals,n),c||(f+=m+u),f+=ee(W.recurrence,a,null,!0),f+=ee(W.unitType,h,null,!0),f+=ee(W.taxInclusivity,l,!0),ee(e,f,{...d,"aria-label":t})}var Ce=({displayOptical:e=!1,displayStrikethrough:t=!1,displayAnnual:r=!1}={})=>({country:n,displayFormatted:o=!0,displayRecurrence:i=!0,displayPerUnit:s=!1,displayTax:c=!1,language:a,literals:h={}}={},{commitment:l,formatString:d,price:u,priceWithoutDiscount:m,taxDisplay:f,taxTerm:g,term:_,usePrecision:P}={},C={})=>{Object.entries({country:n,formatString:d,language:a,price:u}).forEach(([ae,Ir])=>{if(Ir==null)throw new Error(`Argument "${ae}" is missing`)});let A={...Ka,...h},I=`${a.toLowerCase()}-${n.toUpperCase()}`;function S(ae,Ir){let kr=A[ae];if(kr==null)return"";try{return new si(kr.replace(tc,""),I).format(Ir)}catch{return ec.error("Failed to format literal:",kr),""}}let $=t&&m?m:u,z=e?fi:gi;r&&(z=xi);let{accessiblePrice:X,recurrenceTerm:ie,...ue}=z({commitment:l,formatString:d,term:_,price:e?u:$,usePrecision:P,isIndianPrice:n==="IN"}),Z=X,_e="";if(w(i)&&ie){let ae=S(Pe.recurrenceAriaLabel,{recurrenceTerm:ie});ae&&(Z+=" "+ae),_e=S(Pe.recurrenceLabel,{recurrenceTerm:ie})}let we="";if(w(s)){we=S(Pe.perUnitLabel,{perUnit:"LICENSE"});let ae=S(Pe.perUnitAriaLabel,{perUnit:"LICENSE"});ae&&(Z+=" "+ae)}let se="";w(c)&&g&&(se=S(f===rc?Pe.taxExclusiveLabel:Pe.taxInclusiveLabel,{taxTerm:g}),se&&(Z+=" "+se)),t&&(Z=S(Pe.strikethroughAriaLabel,{strikethroughPrice:Z}));let J=W.container;if(e&&(J+=" "+W.containerOptical),t&&(J+=" "+W.containerStrikethrough),r&&(J+=" "+W.containerAnnual),w(o))return oc(J,{...ue,accessibleLabel:Z,recurrenceLabel:_e,perUnitLabel:we,taxInclusivityLabel:se},C);let{currencySymbol:Ge,decimals:Ut,decimalsDelimiter:Dt,hasCurrencySpace:ct,integer:Rr,isCurrencyFirst:Is}=ue,Ve=[Rr,Dt,Ut];Is?(Ve.unshift(ct?"\xA0":""),Ve.unshift(Ge)):(Ve.push(ct?"\xA0":""),Ve.push(Ge)),Ve.push(_e,we,se);let ks=Ve.join("");return ee(J,ks,C)},vi=()=>(e,t,r)=>{let o=(e.displayOldPrice===void 0||w(e.displayOldPrice))&&t.priceWithoutDiscount&&t.priceWithoutDiscount!=t.price;return`${Ce()(e,t,r)}${o?" "+Ce({displayStrikethrough:!0})(e,t,r):""}`};var vn=Ce(),yn=vi(),En=Ce({displayOptical:!0}),bn=Ce({displayStrikethrough:!0}),_n=Ce({displayAnnual:!0});var ic=(e,t)=>{if(!(!qe(e)||!qe(t)))return Math.floor((t-e)/t*100)},yi=()=>(e,t,r)=>{let{price:n,priceWithoutDiscount:o}=t,i=ic(n,o);return i===void 0?'':`${i}%`};var wn=yi();var{freeze:vt}=Object,ce=vt({...Ae}),le=vt({...Y}),$e={STAGE:"STAGE",PRODUCTION:"PRODUCTION",LOCAL:"LOCAL"},Ei=vt({...k}),bi=vt({...Ro}),_i=vt({...L});var Ln={};Us(Ln,{CLASS_NAME_FAILED:()=>er,CLASS_NAME_PENDING:()=>tr,CLASS_NAME_RESOLVED:()=>rr,ERROR_MESSAGE_BAD_REQUEST:()=>nr,ERROR_MESSAGE_MISSING_LITERALS_URL:()=>Sn,ERROR_MESSAGE_OFFER_NOT_FOUND:()=>An,EVENT_TYPE_ERROR:()=>sc,EVENT_TYPE_FAILED:()=>or,EVENT_TYPE_PENDING:()=>ir,EVENT_TYPE_READY:()=>Ze,EVENT_TYPE_RESOLVED:()=>sr,LOG_NAMESPACE:()=>Tn,Landscape:()=>Je,PARAM_AOS_API_KEY:()=>ac,PARAM_ENV:()=>Pn,PARAM_LANDSCAPE:()=>Cn,PARAM_WCS_API_KEY:()=>cc,STATE_FAILED:()=>te,STATE_PENDING:()=>re,STATE_RESOLVED:()=>ne,TAG_NAME_SERVICE:()=>fe,WCS_PROD_URL:()=>$n,WCS_STAGE_URL:()=>On});var er="placeholder-failed",tr="placeholder-pending",rr="placeholder-resolved",nr="Bad WCS request",An="Commerce offer not found",Sn="Literals URL not provided",sc="wcms:commerce:error",or="wcms:placeholder:failed",ir="wcms:placeholder:pending",Ze="wcms:commerce:ready",sr="wcms:placeholder:resolved",Tn="wcms/commerce",Pn="commerce.env",Cn="commerce.landscape",ac="commerce.aosKey",cc="commerce.wcsKey",$n="https://www.adobe.com/web_commerce_artifact",On="https://www.stage.adobe.com/web_commerce_artifact_stage",te="failed",re="pending",ne="resolved",fe="wcms-commerce",Je={DRAFT:"DRAFT",PUBLISHED:"PUBLISHED"};var Nn={clientId:"merch-at-scale",delimiter:"\xB6",ignoredProperties:["analytics","literals"],serializableTypes:["Array","Object"],sampleRate:30,tags:"consumer=milo/commerce"},wi=new Set,lc=e=>e instanceof Error||typeof e.originatingRequest=="string";function Ai(e){if(e==null)return;let t=typeof e;if(t==="function"){let{name:r}=e;return r?`${t} ${r}`:t}if(t==="object"){if(e instanceof Error)return e.message;if(typeof e.originatingRequest=="string"){let{message:n,originatingRequest:o,status:i}=e;return[n,i,o].filter(s=>s).join(" ")}let r=e[Symbol.toStringTag]??Object.getPrototypeOf(e).constructor.name;if(!Nn.serializableTypes.includes(r))return r}return e}function hc(e,t){if(!Nn.ignoredProperties.includes(e))return Ai(t)}var Rn={append(e){let{delimiter:t,sampleRate:r,tags:n,clientId:o}=Nn,{message:i,params:s}=e,c=[],a=i,h=[];s.forEach(u=>{u!=null&&(lc(u)?c:h).push(u)}),c.length&&(a+=" ",a+=c.map(Ai).join(" "));let{pathname:l,search:d}=window.location;a+=`${t}page=`,a+=l+d,h.length&&(a+=`${t}facts=`,a+=JSON.stringify(h,hc)),wi.has(a)||(wi.add(a),window.lana?.log(a,{sampleRate:r,tags:n,clientId:o}))}};var b=Object.freeze({checkoutClientId:"adobe_com",checkoutWorkflow:ce.V3,checkoutWorkflowStep:le.EMAIL,country:"US",displayOldPrice:!0,displayPerUnit:!1,displayRecurrence:!0,displayTax:!1,env:$e.PRODUCTION,forceTaxExclusive:!1,language:"en",entitlement:!1,extraOptions:{},modal:!1,promotionCode:"",quantity:1,wcsApiKey:"wcms-commerce-ims-ro-user-milo",wcsBufferDelay:1,wcsURL:"https://www.adobe.com/web_commerce_artifact",landscape:Je.PUBLISHED,wcsBufferLimit:1});function Si(e,{once:t=!1}={}){let r=null;function n(){let o=document.querySelector(fe);o!==r&&(r=o,o&&e(o))}return document.addEventListener(Ze,n,{once:t}),ge(n),()=>document.removeEventListener(Ze,n)}function yt(e,{country:t,forceTaxExclusive:r,perpetual:n}){let o;if(e.length<2)o=e;else{let i=t==="GB"||n?"EN":"MULT",[s,c]=e;o=[s.language===i?s:c]}return r&&(o=o.map(en)),o}var ge=e=>window.setTimeout(e);function Qe(e,t=1){if(e==null)return[t];let r=(Array.isArray(e)?e:String(e).split(",")).map(Xe).filter(qe);return r.length||(r=[t]),r}function ar(e){return e==null?[]:(Array.isArray(e)?e:String(e).split(",")).filter(Wr)}function G(){return window.customElements.get(fe)?.instance}var dc="en_US",p={ar:"AR_es",be_en:"BE_en",be_fr:"BE_fr",be_nl:"BE_nl",br:"BR_pt",ca:"CA_en",ch_de:"CH_de",ch_fr:"CH_fr",ch_it:"CH_it",cl:"CL_es",co:"CO_es",la:"DO_es",mx:"MX_es",pe:"PE_es",africa:"MU_en",dk:"DK_da",de:"DE_de",ee:"EE_et",eg_ar:"EG_ar",eg_en:"EG_en",es:"ES_es",fr:"FR_fr",gr_el:"GR_el",gr_en:"GR_en",ie:"IE_en",il_he:"IL_iw",it:"IT_it",lv:"LV_lv",lt:"LT_lt",lu_de:"LU_de",lu_en:"LU_en",lu_fr:"LU_fr",my_en:"MY_en",my_ms:"MY_ms",hu:"HU_hu",mt:"MT_en",mena_en:"DZ_en",mena_ar:"DZ_ar",nl:"NL_nl",no:"NO_nb",pl:"PL_pl",pt:"PT_pt",ro:"RO_ro",si:"SI_sl",sk:"SK_sk",fi:"FI_fi",se:"SE_sv",tr:"TR_tr",uk:"GB_en",at:"AT_de",cz:"CZ_cs",bg:"BG_bg",ru:"RU_ru",ua:"UA_uk",au:"AU_en",in_en:"IN_en",in_hi:"IN_hi",id_en:"ID_en",id_id:"ID_in",nz:"NZ_en",sa_ar:"SA_ar",sa_en:"SA_en",sg:"SG_en",cn:"CN_zh-Hans",tw:"TW_zh-Hant",hk_zh:"HK_zh-hant",jp:"JP_ja",kr:"KR_ko",za:"ZA_en",ng:"NG_en",cr:"CR_es",ec:"EC_es",pr:"US_es",gt:"GT_es",cis_en:"AZ_en",cis_ru:"AZ_ru",sea:"SG_en",th_en:"TH_en",th_th:"TH_th"},cr=Object.freeze({LOCAL:"local",PROD:"prod",STAGE:"stage"});function uc({locale:e={}}={}){if(!e.prefix)return{country:b.country,language:b.language,locale:dc};let t=e.prefix.replace("/","")??"",[r=b.country,n=b.language]=(p[t]??t).split("_",2);return r=r.toUpperCase(),n=n.toLowerCase(),{country:r,language:n,locale:`${n}_${r}`}}function Ti(e={}){let{commerce:t={},locale:r=void 0}=e,n=$e.PRODUCTION,o=$n,i=["local","stage"].includes(e.env?.name),s=N(Pn,t,{metadata:!1})?.toLowerCase()==="stage";i&&s&&(n=$e.STAGE,o=On);let c=N("checkoutClientId",t)??b.checkoutClientId,a=pe(N("checkoutWorkflow",t),ce,b.checkoutWorkflow),h=le.CHECKOUT;a===ce.V3&&(h=pe(N("checkoutWorkflowStep",t),le,b.checkoutWorkflowStep));let l=w(N("displayOldPrice",t),b.displayOldPrice),d=w(N("displayPerUnit",t),b.displayPerUnit),u=w(N("displayRecurrence",t),b.displayRecurrence),m=w(N("displayTax",t),b.displayTax),f=w(N("entitlement",t),b.entitlement),g=w(N("modal",t),b.modal),_=w(N("forceTaxExclusive",t),b.forceTaxExclusive),P=N("promotionCode",t)??b.promotionCode,C=Qe(N("quantity",t)),A=N("wcsApiKey",t)??b.wcsApiKey,I=e.env?.name===cr.PROD?Je.PUBLISHED:pe(N(Cn,t),Je,b.landscape),S=Xe(N("wcsBufferDelay",t),b.wcsBufferDelay),$=Xe(N("wcsBufferLimit",t),b.wcsBufferLimit);return{...uc({locale:r}),displayOldPrice:l,checkoutClientId:c,checkoutWorkflow:a,checkoutWorkflowStep:h,displayPerUnit:d,displayRecurrence:u,displayTax:m,entitlement:f,extraOptions:b.extraOptions,modal:g,env:n,forceTaxExclusive:_,priceLiteralsURL:t.priceLiteralsURL,priceLiteralsPromise:t.priceLiteralsPromise,promotionCode:P,quantity:C,wcsApiKey:A,wcsBufferDelay:S,wcsBufferLimit:$,wcsURL:o,landscape:I}}var Ci="debug",mc="error",pc="info",fc="warn",gc=Date.now(),In=new Set,kn=new Set,Pi=new Map,Et=Object.freeze({DEBUG:Ci,ERROR:mc,INFO:pc,WARN:fc}),$i={append({level:e,message:t,params:r,timestamp:n,source:o}){console[e](`${n}ms [${o}] %c${t}`,"font-weight: bold;",...r)}},Oi={filter:({level:e})=>e!==Ci},xc={filter:()=>!1};function vc(e,t,r,n,o){return{level:e,message:t,namespace:r,get params(){if(n.length===1){let[i]=n;me(i)&&(n=i(),Array.isArray(n)||(n=[n]))}return n},source:o,timestamp:Date.now()-gc}}function yc(e){[...kn].every(t=>t(e))&&In.forEach(t=>t(e))}function Li(e){let t=(Pi.get(e)??0)+1;Pi.set(e,t);let r=`${e} #${t}`,n=i=>(s,...c)=>yc(vc(i,s,e,c,r)),o=Object.seal({id:r,namespace:e,module(i){return Li(`${o.namespace}/${i}`)},debug:n(Et.DEBUG),error:n(Et.ERROR),info:n(Et.INFO),warn:n(Et.WARN)});return o}function lr(...e){e.forEach(t=>{let{append:r,filter:n}=t;me(n)?kn.add(n):me(r)&&In.add(r)})}function Ec(e={}){let{name:t}=e,r=w(N("commerce.debug",{search:!0,storage:!0}),t===cr.LOCAL);return lr(r?$i:Oi),t===cr.PROD&&lr(Rn),F}function bc(){In.clear(),kn.clear()}var F={...Li(Tn),Level:Et,Plugins:{consoleAppender:$i,debugFilter:Oi,quietFilter:xc,lanaAppender:Rn},init:Ec,reset:bc,use:lr};var _c={CLASS_NAME_FAILED:er,CLASS_NAME_PENDING:tr,CLASS_NAME_RESOLVED:rr,EVENT_TYPE_FAILED:or,EVENT_TYPE_PENDING:ir,EVENT_TYPE_RESOLVED:sr,STATE_FAILED:te,STATE_PENDING:re,STATE_RESOLVED:ne},wc={[te]:er,[re]:tr,[ne]:rr},Ac={[te]:or,[re]:ir,[ne]:sr},ur=new WeakMap;function V(e){if(!ur.has(e)){let t=F.module(e.constructor.is);ur.set(e,{changes:new Map,connected:!1,dispose:We,error:void 0,log:t,options:void 0,promises:[],state:re,timer:null,value:void 0,version:0})}return ur.get(e)}function hr(e){let t=V(e),{error:r,promises:n,state:o}=t;(o===ne||o===te)&&(t.promises=[],o===ne?n.forEach(({resolve:i})=>i(e)):o===te&&n.forEach(({reject:i})=>i(r))),e.dispatchEvent(new CustomEvent(Ac[o],{bubbles:!0}))}function dr(e){let t=ur.get(e);[te,re,ne].forEach(r=>{e.classList.toggle(wc[r],r===t.state)})}var Sc={get error(){return V(this).error},get log(){return V(this).log},get options(){return V(this).options},get state(){return V(this).state},get value(){return V(this).value},attributeChangedCallback(e,t,r){V(this).changes.set(e,r),this.requestUpdate()},connectedCallback(){V(this).dispose=Si(()=>this.requestUpdate(!0))},disconnectedCallback(){let e=V(this);e.connected&&(e.connected=!1,e.log.debug("Disconnected:",{element:this})),e.dispose(),e.dispose=We},onceSettled(){let{error:e,promises:t,state:r}=V(this);return ne===r?Promise.resolve(this):te===r?Promise.reject(e):new Promise((n,o)=>{t.push({resolve:n,reject:o})})},toggleResolved(e,t,r){let n=V(this);return e!==n.version?!1:(r!==void 0&&(n.options=r),n.state=ne,n.value=t,dr(this),this.log.debug("Resolved:",{element:this,value:t}),ge(()=>hr(this)),!0)},toggleFailed(e,t,r){let n=V(this);return e!==n.version?!1:(r!==void 0&&(n.options=r),n.error=t,n.state=te,dr(this),n.log.error("Failed:",{element:this,error:t}),ge(()=>hr(this)),!0)},togglePending(e){let t=V(this);return t.version++,e&&(t.options=e),t.state=re,dr(this),ge(()=>hr(this)),t.version},requestUpdate(e=!1){if(!this.isConnected||!G())return;let t=V(this);if(t.timer)return;let{error:r,options:n,state:o,value:i,version:s}=t;t.state=re,t.timer=ge(async()=>{t.timer=null;let c=null;if(t.changes.size&&(c=Object.fromEntries(t.changes.entries()),t.changes.clear()),t.connected?t.log.debug("Updated:",{element:this,changes:c}):(t.connected=!0,t.log.debug("Connected:",{element:this,changes:c})),c||e)try{await this.render?.()===!1&&t.state===re&&t.version===s&&(t.state=o,t.error=r,t.value=i,dr(this),hr(this))}catch(a){this.toggleFailed(t.version,a,n)}})}};function Ni(e={}){return Object.entries(e).forEach(([t,r])=>{(r==null||r===""||r?.length===0)&&delete e[t]}),e}function mr(e,t={}){let{tag:r,is:n}=e,o=document.createElement(r,{is:n});return o.setAttribute("is",n),Object.assign(o.dataset,Ni(t)),o}function pr(e){let{tag:t,is:r,prototype:n}=e,o=window.customElements.get(r);return o||(Object.defineProperties(n,Object.getOwnPropertyDescriptors(Sc)),o=Object.defineProperties(e,Object.getOwnPropertyDescriptors(_c)),window.customElements.define(r,o,{extends:t})),o}function fr(e,t=document.body){return Array.from(t?.querySelectorAll(`${e.tag}[is="${e.is}"]`)??[])}function gr(e,t={}){return e instanceof HTMLElement?(Object.assign(e.dataset,Ni(t)),e):null}var Tc="download",Pc="upgrade",Oe,Ke=class Ke extends HTMLAnchorElement{constructor(){super();Q(this,Oe);this.addEventListener("click",this.clickHandler)}static get observedAttributes(){return["data-checkout-workflow","data-checkout-workflow-step","data-extra-options","data-ims-country","data-perpetual","data-promotion-code","data-quantity","data-template","data-wcs-osi","data-entitlement","data-upgrade","data-modal"]}static createCheckoutLink(r={},n=""){let o=G();if(!o)return null;let{checkoutMarketSegment:i,checkoutWorkflow:s,checkoutWorkflowStep:c,entitlement:a,upgrade:h,modal:l,perpetual:d,promotionCode:u,quantity:m,wcsOsi:f,extraOptions:g}=o.collectCheckoutOptions(r),_=mr(Ke,{checkoutMarketSegment:i,checkoutWorkflow:s,checkoutWorkflowStep:c,entitlement:a,upgrade:h,modal:l,perpetual:d,promotionCode:u,quantity:m,wcsOsi:f,extraOptions:g});return n&&(_.innerHTML=`${n}`),_}static getCheckoutLinks(r){return fr(Ke,r)}get isCheckoutLink(){return!0}get placeholder(){return this}clickHandler(r){var n;(n=R(this,Oe))==null||n.call(this,r)}async render(r={}){if(!this.isConnected)return!1;let n=G();if(!n)return!1;this.dataset.imsCountry||n.imsCountryPromise.then(l=>{l&&(this.dataset.imsCountry=l)},We);let o=n.collectCheckoutOptions(r,this.placeholder);if(!o.wcsOsi.length)return!1;let i;try{i=JSON.parse(o.extraOptions??"{}")}catch(l){this.placeholder.log.error("cannot parse exta checkout options",l)}let s=this.placeholder.togglePending(o);this.href="";let c=n.resolveOfferSelectors(o),a=await Promise.all(c);a=a.map(l=>yt(l,o));let h=await n.buildCheckoutAction(a.flat(),{...i,...o});return this.renderOffers(a.flat(),o,{},h,s)}renderOffers(r,n,o={},i=void 0,s=void 0){if(!this.isConnected)return!1;let c=G();if(!c)return!1;if(n={...JSON.parse(this.placeholder.dataset.extraOptions??"null"),...n,...o},s??(s=this.placeholder.togglePending(n)),R(this,Oe)&&K(this,Oe,void 0),i){this.classList.remove(Tc,Pc),this.placeholder.toggleResolved(s,r,n);let{url:h,text:l,className:d,handler:u}=i;return h&&(this.href=h),l&&(this.firstElementChild.innerHTML=l),d&&this.classList.add(...d.split(" ")),u&&(this.setAttribute("href","#"),K(this,Oe,u.bind(this))),!0}else if(r.length){if(this.placeholder.toggleResolved(s,r,n)){let h=c.buildCheckoutURL(r,n);return this.setAttribute("href",h),!0}}else{let h=new Error(`Not provided: ${n?.wcsOsi??"-"}`);if(this.placeholder.toggleFailed(s,h,n))return this.setAttribute("href","#"),!0}return!1}updateOptions(r={}){let n=G();if(!n)return!1;let{checkoutMarketSegment:o,checkoutWorkflow:i,checkoutWorkflowStep:s,entitlement:c,upgrade:a,modal:h,perpetual:l,promotionCode:d,quantity:u,wcsOsi:m}=n.collectCheckoutOptions(r);return gr(this,{checkoutMarketSegment:o,checkoutWorkflow:i,checkoutWorkflowStep:s,entitlement:c,upgrade:a,modal:h,perpetual:l,promotionCode:d,quantity:u,wcsOsi:m}),!0}};Oe=new WeakMap,O(Ke,"is","checkout-link"),O(Ke,"tag","a");var Mn=Ke,Un=pr(Mn);var Ri=[p.uk,p.au,p.fr,p.at,p.be_en,p.be_fr,p.be_nl,p.bg,p.ch_de,p.ch_fr,p.ch_it,p.cz,p.de,p.dk,p.ee,p.eg_ar,p.eg_en,p.es,p.fi,p.fr,p.gr_el,p.gr_en,p.hu,p.ie,p.it,p.lu_de,p.lu_en,p.lu_fr,p.nl,p.no,p.pl,p.pt,p.ro,p.se,p.si,p.sk,p.tr,p.ua,p.id_en,p.id_id,p.in_en,p.in_hi,p.jp,p.my_en,p.my_ms,p.nz,p.th_en,p.th_th],Cc={INDIVIDUAL_COM:[p.za,p.lt,p.lv,p.ng,p.sa_ar,p.sa_en,p.za,p.sg,p.kr],TEAM_COM:[p.za,p.lt,p.lv,p.ng,p.za,p.co,p.kr],INDIVIDUAL_EDU:[p.lt,p.lv,p.sa_en,p.sea],TEAM_EDU:[p.sea,p.kr]},et=class et extends HTMLSpanElement{static get observedAttributes(){return["data-display-old-price","data-display-per-unit","data-display-recurrence","data-display-tax","data-perpetual","data-promotion-code","data-tax-exclusive","data-template","data-wcs-osi"]}static createInlinePrice(t){let r=G();if(!r)return null;let{displayOldPrice:n,displayPerUnit:o,displayRecurrence:i,displayTax:s,forceTaxExclusive:c,perpetual:a,promotionCode:h,quantity:l,template:d,wcsOsi:u}=r.collectPriceOptions(t);return mr(et,{displayOldPrice:n,displayPerUnit:o,displayRecurrence:i,displayTax:s,forceTaxExclusive:c,perpetual:a,promotionCode:h,quantity:l,template:d,wcsOsi:u})}static getInlinePrices(t){return fr(et,t)}get isInlinePrice(){return!0}get placeholder(){return this}resolveDisplayTaxForGeoAndSegment(t,r,n,o){let i=`${t}_${r}`;if(Ri.includes(t)||Ri.includes(i))return!0;let s=Cc[`${n}_${o}`];return s?!!(s.includes(t)||s.includes(i)):!1}async resolveDisplayTax(t,r){let[n]=await t.resolveOfferSelectors(r),o=yt(await n,r);if(o?.length){let{country:i,language:s}=r,c=o[0],[a=""]=c.marketSegments;return this.resolveDisplayTaxForGeoAndSegment(i,s,c.customerSegment,a)}}async render(t={}){if(!this.isConnected)return!1;let r=G();if(!r)return!1;let n=r.collectPriceOptions(t,this.placeholder);if(!n.wcsOsi.length)return!1;let o=this.placeholder.togglePending(n);this.innerHTML="";let[i]=r.resolveOfferSelectors(n);return this.renderOffers(yt(await i,n),n,o)}renderOffers(t,r={},n=void 0){if(!this.isConnected)return;let o=G();if(!o)return!1;let i=o.collectPriceOptions({...this.dataset,...r});if(n??(n=this.placeholder.togglePending(i)),t.length){if(this.placeholder.toggleResolved(n,t,i))return this.innerHTML=o.buildPriceHTML(t,i),!0}else{let s=new Error(`Not provided: ${i?.wcsOsi??"-"}`);if(this.placeholder.toggleFailed(n,s,i))return this.innerHTML="",!0}return!1}updateOptions(t){let r=G();if(!r)return!1;let{displayOldPrice:n,displayPerUnit:o,displayRecurrence:i,displayTax:s,forceTaxExclusive:c,perpetual:a,promotionCode:h,quantity:l,template:d,wcsOsi:u}=r.collectPriceOptions(t);return gr(this,{displayOldPrice:n,displayPerUnit:o,displayRecurrence:i,displayTax:s,forceTaxExclusive:c,perpetual:a,promotionCode:h,quantity:l,template:d,wcsOsi:u}),!0}};O(et,"is","inline-price"),O(et,"tag","span");var Dn=et,Fn=pr(Dn);function Ii({providers:e,settings:t},r){let n=F.module("checkout");function o(h,l){let{checkoutClientId:d,checkoutWorkflow:u,checkoutWorkflowStep:m,country:f,language:g,promotionCode:_,quantity:P}=t,{checkoutMarketSegment:C,checkoutWorkflow:A=u,checkoutWorkflowStep:I=m,imsCountry:S,country:$=S??f,language:z=g,quantity:X=P,entitlement:ie,upgrade:ue,modal:Z,perpetual:_e,promotionCode:we=_,wcsOsi:se,extraOptions:J,...Ge}=Object.assign({},l?.dataset??{},h??{}),Ut=pe(A,ce,b.checkoutWorkflow),Dt=le.CHECKOUT;Ut===ce.V3&&(Dt=pe(I,le,b.checkoutWorkflowStep));let ct=Ye({...Ge,extraOptions:J,checkoutClientId:d,checkoutMarketSegment:C,country:$,quantity:Qe(X,b.quantity),checkoutWorkflow:Ut,checkoutWorkflowStep:Dt,language:z,entitlement:w(ie),upgrade:w(ue),modal:w(Z),perpetual:w(_e),promotionCode:dt(we).effectivePromoCode,wcsOsi:ar(se)});if(l)for(let Rr of e.checkout)Rr(l,ct);return ct}async function i(h,l){let d=G(),u=await r.getCheckoutAction?.(h,l,d.imsSignedInPromise);return u||null}function s(h,l){if(!Array.isArray(h)||!h.length||!l)return"";let{env:d,landscape:u}=t,{checkoutClientId:m,checkoutMarketSegment:f,checkoutWorkflow:g,checkoutWorkflowStep:_,country:P,promotionCode:C,quantity:A,...I}=o(l),S=window.frameElement?"if":"fp",$={checkoutPromoCode:C,clientId:m,context:S,country:P,env:d,items:[],marketSegment:f,workflowStep:_,landscape:u,...I};if(h.length===1){let[{offerId:z,offerType:X,productArrangementCode:ie}]=h,{marketSegments:[ue]}=h[0];Object.assign($,{marketSegment:ue,offerType:X,productArrangementCode:ie}),$.items.push(A[0]===1?{id:z}:{id:z,quantity:A[0]})}else $.items.push(...h.map(({offerId:z},X)=>({id:z,quantity:A[X]??b.quantity})));return Hr(g,$)}let{createCheckoutLink:c,getCheckoutLinks:a}=Un;return{CheckoutLink:Un,CheckoutWorkflow:ce,CheckoutWorkflowStep:le,buildCheckoutAction:i,buildCheckoutURL:s,collectCheckoutOptions:o,createCheckoutLink:c,getCheckoutLinks:a}}function $c({interval:e=200,maxAttempts:t=25}={}){let r=F.module("ims");return new Promise(n=>{r.debug("Waing for IMS to be ready");let o=0;function i(){window.adobeIMS?.initialized?n():++o>t?(r.debug("Timeout"),n()):setTimeout(i,e)}i()})}function Oc(e){return e.then(()=>window.adobeIMS?.isSignedInUser()??!1)}function Lc(e){let t=F.module("ims");return e.then(r=>r?window.adobeIMS.getProfile().then(({countryCode:n})=>(t.debug("Got user country:",n),n),n=>{t.error("Unable to get user country:",n)}):null)}function ki({}){let e=$c(),t=Oc(e),r=Lc(t);return{imsReadyPromise:e,imsSignedInPromise:t,imsCountryPromise:r}}function Nc(e){if(!e.priceLiteralsURL)throw new Error(Sn);return new Promise(t=>{window.fetch(e.priceLiteralsURL).then(r=>{r.json().then(({data:n})=>{t(n)})})})}async function Mi(e){let r=await(e.priceLiteralsPromise||Nc(e));if(Array.isArray(r)){let n=i=>r.find(s=>zt(s.lang,i)),o=n(e.language)??n(b.language);if(o)return Object.freeze(o)}return{}}function Ui({literals:e,providers:t,settings:r}){function n(c,a){let{country:h,displayOldPrice:l,displayPerUnit:d,displayRecurrence:u,displayTax:m,forceTaxExclusive:f,language:g,promotionCode:_,quantity:P}=r,{displayOldPrice:C=l,displayPerUnit:A=d,displayRecurrence:I=u,displayTax:S=m,forceTaxExclusive:$=f,country:z=h,language:X=g,perpetual:ie,promotionCode:ue=_,quantity:Z=P,template:_e,wcsOsi:we,...se}=Object.assign({},a?.dataset??{},c??{}),J=Ye({...se,country:z,displayOldPrice:w(C),displayPerUnit:w(A),displayRecurrence:w(I),displayTax:w(S),forceTaxExclusive:w($),language:X,perpetual:w(ie),promotionCode:dt(ue).effectivePromoCode,quantity:Qe(Z,b.quantity),template:_e,wcsOsi:ar(we)});if(a)for(let Ge of t.price)Ge(a,J);return J}function o(c,a){if(!Array.isArray(c)||!c.length||!a)return"";let{template:h}=a,l;switch(h){case"discount":l=wn;break;case"strikethrough":l=bn;break;case"optical":l=En;break;case"annual":l=_n;break;default:l=a.promotionCode?yn:vn}let d=n(a);d.literals=Object.assign({},e.price,Ye(a.literals??{}));let[u]=c;return u={...u,...u.priceDetails},l(d,u)}let{createInlinePrice:i,getInlinePrices:s}=Fn;return{InlinePrice:Fn,buildPriceHTML:o,collectPriceOptions:n,createInlinePrice:i,getInlinePrices:s}}function Di({settings:e}){let t=F.module("wcs"),{env:r,wcsApiKey:n}=e,o=new Map,i=new Map,s;async function c(l,d,u=!0){let m=An;t.debug("Fetching:",l);try{l.offerSelectorIds=l.offerSelectorIds.sort();let f=new URL(e.wcsURL);f.searchParams.set("offer_selector_ids",l.offerSelectorIds.join(",")),f.searchParams.set("country",l.country),f.searchParams.set("locale",l.locale),f.searchParams.set("landscape",r===$e.STAGE?"ALL":e.landscape),f.searchParams.set("api_key",n),l.language&&f.searchParams.set("language",l.language),l.promotionCode&&f.searchParams.set("promotion_code",l.promotionCode),l.currency&&f.searchParams.set("currency",l.currency);let g=await fetch(f.toString(),{credentials:"omit"});if(g.ok){let _=await g.json();t.debug("Fetched:",l,_);let P=_.resolvedOffers??[];P=P.map(Kr),d.forEach(({resolve:C},A)=>{let I=P.filter(({offerSelectorIds:S})=>S.includes(A)).flat();I.length&&(d.delete(A),C(I))})}else g.status===404&&l.offerSelectorIds.length>1?(t.debug("Multi-osi 404, fallback to fetch-by-one strategy"),await Promise.allSettled(l.offerSelectorIds.map(_=>c({...l,offerSelectorIds:[_]},d,!1)))):(m=nr,t.error(m,l))}catch(f){m=nr,t.error(m,l,f)}u&&d.size&&(t.debug("Missing:",{offerSelectorIds:[...d.keys()]}),d.forEach(f=>{f.reject(new Error(m))}))}function a(){clearTimeout(s);let l=[...i.values()];i.clear(),l.forEach(({options:d,promises:u})=>c(d,u))}function h({country:l,language:d,perpetual:u=!1,promotionCode:m="",wcsOsi:f=[]}){let g=`${d}_${l}`;l!=="GB"&&(d=u?"EN":"MULT");let _=[l,d,m].filter(P=>P).join("-").toLowerCase();return f.map(P=>{let C=`${P}-${_}`;if(!o.has(C)){let A=new Promise((I,S)=>{let $=i.get(_);if(!$){let z={country:l,locale:g,offerSelectorIds:[]};l!=="GB"&&(z.language=d),$={options:z,promises:new Map},i.set(_,$)}m&&($.options.promotionCode=m),$.options.offerSelectorIds.push(P),$.promises.set(P,{resolve:I,reject:S}),$.options.offerSelectorIds.length>=e.wcsBufferLimit?a():(t.debug("Queued:",$.options),s||(s=setTimeout(a,e.wcsBufferDelay)))});o.set(C,A)}return o.get(C)})}return{WcsCommitment:Ei,WcsPlanType:bi,WcsTerm:_i,resolveOfferSelectors:h}}var j=class extends HTMLElement{get isWcmsCommerce(){return!0}};O(j,"instance"),O(j,"promise",null);window.customElements.define(fe,j);async function Rc(e,t){let r=F.init(e.env).module("service");r.debug("Activating:",e);let n={price:{}},o=Object.freeze(Ti(e));try{n.price=await Mi(o)}catch(a){r.warn("Price literals were not fetched:",a)}let i={checkout:new Set,price:new Set},s=document.createElement(fe),c={literals:n,providers:i,settings:o};return j.instance=Object.defineProperties(s,Object.getOwnPropertyDescriptors({...Ii(c,t),...ki(c),...Ui(c),...Di(c),...Ln,Log:F,get defaults(){return b},get literals(){return n},get log(){return F},get providers(){return{checkout(a){return i.checkout.add(a),()=>i.checkout.delete(a)},price(a){return i.price.add(a),()=>i.price.delete(a)}}},get settings(){return o}})),r.debug("Activated:",{literals:n,settings:o,element:s}),document.head.append(s),ge(()=>{let a=new CustomEvent(Ze,{bubbles:!0,cancelable:!1,detail:j.instance});j.instance.dispatchEvent(a)}),j.instance}function Fi(){document.head.querySelector(fe)?.remove(),j.promise=null,F.reset()}function bt(e,t){let r=me(e)?e():null,n=me(t)?t():{};return r&&(n.force&&Fi(),Rc(r,n).then(o=>{bt.resolve(o)})),j.promise??(j.promise=new Promise(o=>{bt.resolve=o})),j.promise}var xr=window,yr=xr.ShadowRoot&&(xr.ShadyCSS===void 0||xr.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,zi=Symbol(),Hi=new WeakMap,vr=class{constructor(t,r,n){if(this._$cssResult$=!0,n!==zi)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=r}get styleSheet(){let t=this.o,r=this.t;if(yr&&t===void 0){let n=r!==void 0&&r.length===1;n&&(t=Hi.get(r)),t===void 0&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),n&&Hi.set(r,t))}return t}toString(){return this.cssText}},Gi=e=>new vr(typeof e=="string"?e:e+"",void 0,zi);var Hn=(e,t)=>{yr?e.adoptedStyleSheets=t.map(r=>r instanceof CSSStyleSheet?r:r.styleSheet):t.forEach(r=>{let n=document.createElement("style"),o=xr.litNonce;o!==void 0&&n.setAttribute("nonce",o),n.textContent=r.cssText,e.appendChild(n)})},Er=yr?e=>e:e=>e instanceof CSSStyleSheet?(t=>{let r="";for(let n of t.cssRules)r+=n.cssText;return Gi(r)})(e):e;var zn,br=window,Vi=br.trustedTypes,Ic=Vi?Vi.emptyScript:"",ji=br.reactiveElementPolyfillSupport,Vn={toAttribute(e,t){switch(t){case Boolean:e=e?Ic:null;break;case Object:case Array:e=e==null?e:JSON.stringify(e)}return e},fromAttribute(e,t){let r=e;switch(t){case Boolean:r=e!==null;break;case Number:r=e===null?null:Number(e);break;case Object:case Array:try{r=JSON.parse(e)}catch{r=null}}return r}},Bi=(e,t)=>t!==e&&(t==t||e==e),Gn={attribute:!0,type:String,converter:Vn,reflect:!1,hasChanged:Bi},jn="finalized",Le=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this._$Eu()}static addInitializer(t){var r;this.finalize(),((r=this.h)!==null&&r!==void 0?r:this.h=[]).push(t)}static get observedAttributes(){this.finalize();let t=[];return this.elementProperties.forEach((r,n)=>{let o=this._$Ep(n,r);o!==void 0&&(this._$Ev.set(o,n),t.push(o))}),t}static createProperty(t,r=Gn){if(r.state&&(r.attribute=!1),this.finalize(),this.elementProperties.set(t,r),!r.noAccessor&&!this.prototype.hasOwnProperty(t)){let n=typeof t=="symbol"?Symbol():"__"+t,o=this.getPropertyDescriptor(t,n,r);o!==void 0&&Object.defineProperty(this.prototype,t,o)}}static getPropertyDescriptor(t,r,n){return{get(){return this[r]},set(o){let i=this[t];this[r]=o,this.requestUpdate(t,i,n)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||Gn}static finalize(){if(this.hasOwnProperty(jn))return!1;this[jn]=!0;let t=Object.getPrototypeOf(this);if(t.finalize(),t.h!==void 0&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){let r=this.properties,n=[...Object.getOwnPropertyNames(r),...Object.getOwnPropertySymbols(r)];for(let o of n)this.createProperty(o,r[o])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){let r=[];if(Array.isArray(t)){let n=new Set(t.flat(1/0).reverse());for(let o of n)r.unshift(Er(o))}else t!==void 0&&r.push(Er(t));return r}static _$Ep(t,r){let n=r.attribute;return n===!1?void 0:typeof n=="string"?n:typeof t=="string"?t.toLowerCase():void 0}_$Eu(){var t;this._$E_=new Promise(r=>this.enableUpdating=r),this._$AL=new Map,this._$Eg(),this.requestUpdate(),(t=this.constructor.h)===null||t===void 0||t.forEach(r=>r(this))}addController(t){var r,n;((r=this._$ES)!==null&&r!==void 0?r:this._$ES=[]).push(t),this.renderRoot!==void 0&&this.isConnected&&((n=t.hostConnected)===null||n===void 0||n.call(t))}removeController(t){var r;(r=this._$ES)===null||r===void 0||r.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach((t,r)=>{this.hasOwnProperty(r)&&(this._$Ei.set(r,this[r]),delete this[r])})}createRenderRoot(){var t;let r=(t=this.shadowRoot)!==null&&t!==void 0?t:this.attachShadow(this.constructor.shadowRootOptions);return Hn(r,this.constructor.elementStyles),r}connectedCallback(){var t;this.renderRoot===void 0&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),(t=this._$ES)===null||t===void 0||t.forEach(r=>{var n;return(n=r.hostConnected)===null||n===void 0?void 0:n.call(r)})}enableUpdating(t){}disconnectedCallback(){var t;(t=this._$ES)===null||t===void 0||t.forEach(r=>{var n;return(n=r.hostDisconnected)===null||n===void 0?void 0:n.call(r)})}attributeChangedCallback(t,r,n){this._$AK(t,n)}_$EO(t,r,n=Gn){var o;let i=this.constructor._$Ep(t,n);if(i!==void 0&&n.reflect===!0){let s=(((o=n.converter)===null||o===void 0?void 0:o.toAttribute)!==void 0?n.converter:Vn).toAttribute(r,n.type);this._$El=t,s==null?this.removeAttribute(i):this.setAttribute(i,s),this._$El=null}}_$AK(t,r){var n;let o=this.constructor,i=o._$Ev.get(t);if(i!==void 0&&this._$El!==i){let s=o.getPropertyOptions(i),c=typeof s.converter=="function"?{fromAttribute:s.converter}:((n=s.converter)===null||n===void 0?void 0:n.fromAttribute)!==void 0?s.converter:Vn;this._$El=i,this[i]=c.fromAttribute(r,s.type),this._$El=null}}requestUpdate(t,r,n){let o=!0;t!==void 0&&(((n=n||this.constructor.getPropertyOptions(t)).hasChanged||Bi)(this[t],r)?(this._$AL.has(t)||this._$AL.set(t,r),n.reflect===!0&&this._$El!==t&&(this._$EC===void 0&&(this._$EC=new Map),this._$EC.set(t,n))):o=!1),!this.isUpdatePending&&o&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(r){Promise.reject(r)}let t=this.scheduleUpdate();return t!=null&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach((o,i)=>this[i]=o),this._$Ei=void 0);let r=!1,n=this._$AL;try{r=this.shouldUpdate(n),r?(this.willUpdate(n),(t=this._$ES)===null||t===void 0||t.forEach(o=>{var i;return(i=o.hostUpdate)===null||i===void 0?void 0:i.call(o)}),this.update(n)):this._$Ek()}catch(o){throw r=!1,this._$Ek(),o}r&&this._$AE(n)}willUpdate(t){}_$AE(t){var r;(r=this._$ES)===null||r===void 0||r.forEach(n=>{var o;return(o=n.hostUpdated)===null||o===void 0?void 0:o.call(n)}),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){this._$EC!==void 0&&(this._$EC.forEach((r,n)=>this._$EO(n,this[n],r)),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}};Le[jn]=!0,Le.elementProperties=new Map,Le.elementStyles=[],Le.shadowRootOptions={mode:"open"},ji?.({ReactiveElement:Le}),((zn=br.reactiveElementVersions)!==null&&zn!==void 0?zn:br.reactiveElementVersions=[]).push("1.6.3");var Bn,_r=window,tt=_r.trustedTypes,Wi=tt?tt.createPolicy("lit-html",{createHTML:e=>e}):void 0,qn="$lit$",xe=`lit$${(Math.random()+"").slice(9)}$`,Ki="?"+xe,kc=`<${Ki}>`,Ie=document,wr=()=>Ie.createComment(""),wt=e=>e===null||typeof e!="object"&&typeof e!="function",es=Array.isArray,Mc=e=>es(e)||typeof e?.[Symbol.iterator]=="function",Wn=`[ +\f\r]`,_t=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,qi=/-->/g,Yi=/>/g,Ne=RegExp(`>|${Wn}(?:([^\\s"'>=/]+)(${Wn}*=${Wn}*(?:[^ +\f\r"'\`<>=]|("|')|))|$)`,"g"),Xi=/'/g,Zi=/"/g,ts=/^(?:script|style|textarea|title)$/i,rs=e=>(t,...r)=>({_$litType$:e,strings:t,values:r}),nu=rs(1),ou=rs(2),At=Symbol.for("lit-noChange"),M=Symbol.for("lit-nothing"),Ji=new WeakMap,Re=Ie.createTreeWalker(Ie,129,null,!1);function ns(e,t){if(!Array.isArray(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return Wi!==void 0?Wi.createHTML(t):t}var Uc=(e,t)=>{let r=e.length-1,n=[],o,i=t===2?"":"",s=_t;for(let c=0;c"?(s=o??_t,d=-1):l[1]===void 0?d=-2:(d=s.lastIndex-l[2].length,h=l[1],s=l[3]===void 0?Ne:l[3]==='"'?Zi:Xi):s===Zi||s===Xi?s=Ne:s===qi||s===Yi?s=_t:(s=Ne,o=void 0);let m=s===Ne&&e[c+1].startsWith("/>")?" ":"";i+=s===_t?a+kc:d>=0?(n.push(h),a.slice(0,d)+qn+a.slice(d)+xe+m):a+xe+(d===-2?(n.push(void 0),c):m)}return[ns(e,i+(e[r]||"")+(t===2?"":"")),n]},St=class e{constructor({strings:t,_$litType$:r},n){let o;this.parts=[];let i=0,s=0,c=t.length-1,a=this.parts,[h,l]=Uc(t,r);if(this.el=e.createElement(h,n),Re.currentNode=this.el.content,r===2){let d=this.el.content,u=d.firstChild;u.remove(),d.append(...u.childNodes)}for(;(o=Re.nextNode())!==null&&a.length0){o.textContent=tt?tt.emptyScript:"";for(let m=0;m2||n[0]!==""||n[1]!==""?(this._$AH=Array(n.length-1).fill(new String),this.strings=n):this._$AH=M}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,r=this,n,o){let i=this.strings,s=!1;if(i===void 0)t=rt(this,t,r,0),s=!wt(t)||t!==this._$AH&&t!==At,s&&(this._$AH=t);else{let c=t,a,h;for(t=i[0],a=0;anew Tt(typeof e=="string"?e:e+"",void 0,Kn),ke=(e,...t)=>{let r=e.length===1?e[0]:t.reduce((n,o,i)=>n+(s=>{if(s._$cssResult$===!0)return s.cssText;if(typeof s=="number")return s;throw Error("Value passed to 'css' function must be a 'css' function result: "+s+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(o)+e[i+1],e[0]);return new Tt(r,e,Kn)},eo=(e,t)=>{Tr?e.adoptedStyleSheets=t.map(r=>r instanceof CSSStyleSheet?r:r.styleSheet):t.forEach(r=>{let n=document.createElement("style"),o=Sr.litNonce;o!==void 0&&n.setAttribute("nonce",o),n.textContent=r.cssText,e.appendChild(n)})},Pr=Tr?e=>e:e=>e instanceof CSSStyleSheet?(t=>{let r="";for(let n of t.cssRules)r+=n.cssText;return ve(r)})(e):e;var to,Cr=window,is=Cr.trustedTypes,Fc=is?is.emptyScript:"",ss=Cr.reactiveElementPolyfillSupport,no={toAttribute(e,t){switch(t){case Boolean:e=e?Fc:null;break;case Object:case Array:e=e==null?e:JSON.stringify(e)}return e},fromAttribute(e,t){let r=e;switch(t){case Boolean:r=e!==null;break;case Number:r=e===null?null:Number(e);break;case Object:case Array:try{r=JSON.parse(e)}catch{r=null}}return r}},as=(e,t)=>t!==e&&(t==t||e==e),ro={attribute:!0,type:String,converter:no,reflect:!1,hasChanged:as},oo="finalized",he=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this._$Eu()}static addInitializer(t){var r;this.finalize(),((r=this.h)!==null&&r!==void 0?r:this.h=[]).push(t)}static get observedAttributes(){this.finalize();let t=[];return this.elementProperties.forEach((r,n)=>{let o=this._$Ep(n,r);o!==void 0&&(this._$Ev.set(o,n),t.push(o))}),t}static createProperty(t,r=ro){if(r.state&&(r.attribute=!1),this.finalize(),this.elementProperties.set(t,r),!r.noAccessor&&!this.prototype.hasOwnProperty(t)){let n=typeof t=="symbol"?Symbol():"__"+t,o=this.getPropertyDescriptor(t,n,r);o!==void 0&&Object.defineProperty(this.prototype,t,o)}}static getPropertyDescriptor(t,r,n){return{get(){return this[r]},set(o){let i=this[t];this[r]=o,this.requestUpdate(t,i,n)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||ro}static finalize(){if(this.hasOwnProperty(oo))return!1;this[oo]=!0;let t=Object.getPrototypeOf(this);if(t.finalize(),t.h!==void 0&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){let r=this.properties,n=[...Object.getOwnPropertyNames(r),...Object.getOwnPropertySymbols(r)];for(let o of n)this.createProperty(o,r[o])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){let r=[];if(Array.isArray(t)){let n=new Set(t.flat(1/0).reverse());for(let o of n)r.unshift(Pr(o))}else t!==void 0&&r.push(Pr(t));return r}static _$Ep(t,r){let n=r.attribute;return n===!1?void 0:typeof n=="string"?n:typeof t=="string"?t.toLowerCase():void 0}_$Eu(){var t;this._$E_=new Promise(r=>this.enableUpdating=r),this._$AL=new Map,this._$Eg(),this.requestUpdate(),(t=this.constructor.h)===null||t===void 0||t.forEach(r=>r(this))}addController(t){var r,n;((r=this._$ES)!==null&&r!==void 0?r:this._$ES=[]).push(t),this.renderRoot!==void 0&&this.isConnected&&((n=t.hostConnected)===null||n===void 0||n.call(t))}removeController(t){var r;(r=this._$ES)===null||r===void 0||r.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach((t,r)=>{this.hasOwnProperty(r)&&(this._$Ei.set(r,this[r]),delete this[r])})}createRenderRoot(){var t;let r=(t=this.shadowRoot)!==null&&t!==void 0?t:this.attachShadow(this.constructor.shadowRootOptions);return eo(r,this.constructor.elementStyles),r}connectedCallback(){var t;this.renderRoot===void 0&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),(t=this._$ES)===null||t===void 0||t.forEach(r=>{var n;return(n=r.hostConnected)===null||n===void 0?void 0:n.call(r)})}enableUpdating(t){}disconnectedCallback(){var t;(t=this._$ES)===null||t===void 0||t.forEach(r=>{var n;return(n=r.hostDisconnected)===null||n===void 0?void 0:n.call(r)})}attributeChangedCallback(t,r,n){this._$AK(t,n)}_$EO(t,r,n=ro){var o;let i=this.constructor._$Ep(t,n);if(i!==void 0&&n.reflect===!0){let s=(((o=n.converter)===null||o===void 0?void 0:o.toAttribute)!==void 0?n.converter:no).toAttribute(r,n.type);this._$El=t,s==null?this.removeAttribute(i):this.setAttribute(i,s),this._$El=null}}_$AK(t,r){var n;let o=this.constructor,i=o._$Ev.get(t);if(i!==void 0&&this._$El!==i){let s=o.getPropertyOptions(i),c=typeof s.converter=="function"?{fromAttribute:s.converter}:((n=s.converter)===null||n===void 0?void 0:n.fromAttribute)!==void 0?s.converter:no;this._$El=i,this[i]=c.fromAttribute(r,s.type),this._$El=null}}requestUpdate(t,r,n){let o=!0;t!==void 0&&(((n=n||this.constructor.getPropertyOptions(t)).hasChanged||as)(this[t],r)?(this._$AL.has(t)||this._$AL.set(t,r),n.reflect===!0&&this._$El!==t&&(this._$EC===void 0&&(this._$EC=new Map),this._$EC.set(t,n))):o=!1),!this.isUpdatePending&&o&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(r){Promise.reject(r)}let t=this.scheduleUpdate();return t!=null&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach((o,i)=>this[i]=o),this._$Ei=void 0);let r=!1,n=this._$AL;try{r=this.shouldUpdate(n),r?(this.willUpdate(n),(t=this._$ES)===null||t===void 0||t.forEach(o=>{var i;return(i=o.hostUpdate)===null||i===void 0?void 0:i.call(o)}),this.update(n)):this._$Ek()}catch(o){throw r=!1,this._$Ek(),o}r&&this._$AE(n)}willUpdate(t){}_$AE(t){var r;(r=this._$ES)===null||r===void 0||r.forEach(n=>{var o;return(o=n.hostUpdated)===null||o===void 0?void 0:o.call(n)}),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){this._$EC!==void 0&&(this._$EC.forEach((r,n)=>this._$EO(n,this[n],r)),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}};he[oo]=!0,he.elementProperties=new Map,he.elementStyles=[],he.shadowRootOptions={mode:"open"},ss?.({ReactiveElement:he}),((to=Cr.reactiveElementVersions)!==null&&to!==void 0?to:Cr.reactiveElementVersions=[]).push("1.6.3");var io,$r=window,ot=$r.trustedTypes,cs=ot?ot.createPolicy("lit-html",{createHTML:e=>e}):void 0,ao="$lit$",ye=`lit$${(Math.random()+"").slice(9)}$`,fs="?"+ye,Hc=`<${fs}>`,De=document,Ct=()=>De.createComment(""),$t=e=>e===null||typeof e!="object"&&typeof e!="function",gs=Array.isArray,zc=e=>gs(e)||typeof e?.[Symbol.iterator]=="function",so=`[ +\f\r]`,Pt=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,ls=/-->/g,hs=/>/g,Me=RegExp(`>|${so}(?:([^\\s"'>=/]+)(${so}*=${so}*(?:[^ +\f\r"'\`<>=]|("|')|))|$)`,"g"),ds=/'/g,us=/"/g,xs=/^(?:script|style|textarea|title)$/i,vs=e=>(t,...r)=>({_$litType$:e,strings:t,values:r}),x=vs(1),hu=vs(2),Fe=Symbol.for("lit-noChange"),U=Symbol.for("lit-nothing"),ms=new WeakMap,Ue=De.createTreeWalker(De,129,null,!1);function ys(e,t){if(!Array.isArray(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return cs!==void 0?cs.createHTML(t):t}var Gc=(e,t)=>{let r=e.length-1,n=[],o,i=t===2?"":"",s=Pt;for(let c=0;c"?(s=o??Pt,d=-1):l[1]===void 0?d=-2:(d=s.lastIndex-l[2].length,h=l[1],s=l[3]===void 0?Me:l[3]==='"'?us:ds):s===us||s===ds?s=Me:s===ls||s===hs?s=Pt:(s=Me,o=void 0);let m=s===Me&&e[c+1].startsWith("/>")?" ":"";i+=s===Pt?a+Hc:d>=0?(n.push(h),a.slice(0,d)+ao+a.slice(d)+ye+m):a+ye+(d===-2?(n.push(void 0),c):m)}return[ys(e,i+(e[r]||"")+(t===2?"":"")),n]},Ot=class e{constructor({strings:t,_$litType$:r},n){let o;this.parts=[];let i=0,s=0,c=t.length-1,a=this.parts,[h,l]=Gc(t,r);if(this.el=e.createElement(h,n),Ue.currentNode=this.el.content,r===2){let d=this.el.content,u=d.firstChild;u.remove(),d.append(...u.childNodes)}for(;(o=Ue.nextNode())!==null&&a.length0){o.textContent=ot?ot.emptyScript:"";for(let m=0;m2||n[0]!==""||n[1]!==""?(this._$AH=Array(n.length-1).fill(new String),this.strings=n):this._$AH=U}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,r=this,n,o){let i=this.strings,s=!1;if(i===void 0)t=it(this,t,r,0),s=!$t(t)||t!==this._$AH&&t!==Fe,s&&(this._$AH=t);else{let c=t,a,h;for(t=i[0],a=0;a{var n,o;let i=(n=r?.renderBefore)!==null&&n!==void 0?n:t,s=i._$litPart$;if(s===void 0){let c=(o=r?.renderBefore)!==null&&o!==void 0?o:null;i._$litPart$=s=new Lt(t.insertBefore(Ct(),c),c,void 0,r??{})}return s._$AI(e),s};var po,fo;var oe=class extends he{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){var t,r;let n=super.createRenderRoot();return(t=(r=this.renderOptions).renderBefore)!==null&&t!==void 0||(r.renderBefore=n.firstChild),n}update(t){let r=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=Es(r,this.renderRoot,this.renderOptions)}connectedCallback(){var t;super.connectedCallback(),(t=this._$Do)===null||t===void 0||t.setConnected(!0)}disconnectedCallback(){var t;super.disconnectedCallback(),(t=this._$Do)===null||t===void 0||t.setConnected(!1)}render(){return Fe}};oe.finalized=!0,oe._$litElement$=!0,(po=globalThis.litElementHydrateSupport)===null||po===void 0||po.call(globalThis,{LitElement:oe});var bs=globalThis.litElementPolyfillSupport;bs?.({LitElement:oe});((fo=globalThis.litElementVersions)!==null&&fo!==void 0?fo:globalThis.litElementVersions=[]).push("3.3.3");var Nt="(max-width: 767px)",Rt="(max-width: 1199px)",B="(min-width: 768px)",H="(min-width: 1200px)",q="(min-width: 1600px)";var _s=ke` + :host { + position: relative; + display: flex; + flex-direction: column; + text-align: start; + background-color: var(--consonant-merch-card-background-color); + grid-template-columns: repeat(auto-fit, minmax(300px, max-content)); + background-color: var(--consonant-merch-card-background-color); + font-family: var(--body-font-family, 'Adobe Clean'); + border-radius: var(--consonant-merch-spacing-xs); + border: 1px solid var(--consonant-merch-card-border-color); + box-sizing: border-box; + } + + :host(.placeholder) { + visibility: hidden; + } + + :host([variant='special-offers']) { + min-height: 439px; + } + + :host([variant='catalog']) { + min-height: 330px; + } + + :host([variant='plans']) { + min-height: 348px; + } + + :host([variant='segment']) { + min-height: 214px; + } + + :host([variant='ccd-action']:not([size])) { + width: var(--consonant-merch-card-ccd-action-width); + } + + :host([aria-selected]) { + outline: none; + box-sizing: border-box; + box-shadow: inset 0 0 0 2px var(--color-accent); + } + + .invisible { + visibility: hidden; + } + + :host(:hover) .invisible { + visibility: visible; + background-image: var(--ellipsis-icon); + cursor: pointer; + } + + .action-menu.always-visible { + visibility: visible; + background-image: var(--ellipsis-icon); + } + + :host([variant='mini-compare-chart']) > slot:not([name='icons']) { + display: block; + } + + .top-section { + display: flex; + justify-content: flex-start; + align-items: flex-start; + gap: 16px; + } + + .top-section.badge { + min-height: 32px; + } + + .body { + flex: 1; + display: flex; + flex-direction: column; + justify-content: flex-start; + height: 100%; + gap: var(--consonant-merch-spacing-xxs); + padding: var(--consonant-merch-spacing-xs); + } + + footer { + display: flex; + justify-content: flex-end; + box-sizing: border-box; + align-items: flex-end; + width: 100%; + flex-flow: wrap; + gap: var(--consonant-merch-spacing-xs); + + padding: var(--consonant-merch-spacing-xs); + } + + hr { + background-color: var(--color-gray-200); + border: none; + height: 1px; + width: auto; + margin-top: 0; + margin-bottom: 0; + margin-left: var(--consonant-merch-spacing-xs); + margin-right: var(--consonant-merch-spacing-xs); + } + + div[class$='-badge'] { + position: absolute; + top: 16px; + right: 0; + font-size: var(--type-heading-xxs-size); + font-weight: 500; + max-width: 180px; + line-height: 16px; + text-align: center; + padding: 8px 11px; + border-radius: 5px 0 0 5px; + } + + div[class$='-badge']:dir(rtl) { + left: 0; + right: initial; + padding: 8px 11px; + border-radius: 0 5px 5px 0; + } + + .body .catalog-badge { + display: flex; + height: fit-content; + flex-direction: column; + width: fit-content; + max-width: 140px; + border-radius: 5px; + position: relative; + top: 0; + margin-left: var(--consonant-merch-spacing-xxs); + box-sizing: border-box; + } + + .detail-bg-container { + right: 0; + padding: var(--consonant-merch-spacing-xs); + border-radius: 5px; + font-size: var(--consonant-merch-card-body-font-size); + margin: var(--consonant-merch-spacing-xs); + } + + .action-menu { + display: flex; + width: 32px; + height: 32px; + position: absolute; + top: 16px; + right: 16px; + background-color: #f6f6f6; + background-repeat: no-repeat; + background-position: center; + background-size: 16px 16px; + } + .hidden { + visibility: hidden; + } + + #stock-checkbox, + .secure-transaction-label { + font-size: var(--consonant-merch-card-body-xxs-font-size); + line-height: 1.3; + color: var(--color-gray-600); + } + + #stock-checkbox { + display: inline-flex; + align-items: center; + cursor: pointer; + gap: 10px; /*same as spectrum */ + } + + #stock-checkbox > input { + display: none; + } + + #stock-checkbox > span { + display: inline-block; + box-sizing: border-box; + border: 2px solid rgb(117, 117, 117); + border-radius: 2px; + width: 14px; + height: 14px; + } + + #stock-checkbox > input:checked + span { + background: var(--checkmark-icon) no-repeat var(--color-accent); + border-color: var(--color-accent); + } + + .secure-transaction-label { + white-space: nowrap; + display: inline-flex; + gap: var(--consonant-merch-spacing-xxs); + align-items: center; + flex: 1; + line-height: normal; + } + + .secure-transaction-label::before { + display: inline-block; + content: ''; + width: 12px; + height: 15px; + background: var(--secure-icon) no-repeat; + background-position: center; + background-size: contain; + } + + .checkbox-container { + display: flex; + align-items: center; + gap: var(--consonant-merch-spacing-xxs); + } + + .checkbox-container input[type='checkbox']:checked + .checkmark { + background-color: var(--color-accent); + background-image: var(--checkmark-icon); + border-color: var(--color-accent); + } + + .checkbox-container input[type='checkbox'] { + display: none; + } + + .checkbox-container .checkmark { + position: relative; + display: inline-block; + width: 12px; + height: 12px; + border: 2px solid #757575; + background: #fff; + border-radius: 2px; + cursor: pointer; + margin-top: 2px; + } + + .twp-badge { + padding: 4px 10px 5px 10px; + } + + :host([aria-selected]) .twp-badge { + margin-inline-end: 2px; + padding-inline-end: 9px; + } + + :host([variant='twp']) { + padding: 4px 10px 5px 10px; + } + + slot[name='icons'] { + display: flex; + gap: 8px; + } + + :host([variant='twp']) ::slotted(merch-offer-select) { + display: none; + } + + :host([variant='twp']) .top-section { + flex: 0; + display: flex; + flex-direction: column; + justify-content: flex-start; + height: 100%; + gap: var(--consonant-merch-spacing-xxs); + padding: var(--consonant-merch-spacing-xs) + var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-xs) + var(--consonant-merch-spacing-xs); + align-items: flex-start; + } + + :host([variant='twp']) .body { + padding: 0 var(--consonant-merch-spacing-xs); + } + + :host([variant='twp']) footer { + gap: var(--consonant-merch-spacing-xxs); + flex-direction: column; + align-self: flex-start; + } + + :host([variant='special-offers'].center) { + text-align: center; + } + + /* plans */ + :host([variant='plans']) { + min-height: 348px; + } + + :host([variant='mini-compare-chart']) footer { + min-height: var(--consonant-merch-card-mini-compare-footer-height); + padding: var(--consonant-merch-spacing-xs); + } + + /* mini-compare card */ + :host([variant='mini-compare-chart']) .top-section { + padding-top: var(--consonant-merch-spacing-s); + padding-inline-start: var(--consonant-merch-spacing-s); + height: var(--consonant-merch-card-mini-compare-top-section-height); + } + + @media screen and ${ve(Rt)} { + [class*'-merch-cards'] :host([variant='mini-compare-chart']) footer { + flex-direction: column; + align-items: stretch; + text-align: center; + } + } + + @media screen and ${ve(H)} { + :host([variant='mini-compare-chart']) footer { + padding: var(--consonant-merch-spacing-xs) + var(--consonant-merch-spacing-s) + var(--consonant-merch-spacing-s) + var(--consonant-merch-spacing-s); + } + } + + :host([variant='mini-compare-chart']) slot[name='footer-rows'] { + flex: 1; + display: flex; + flex-direction: column; + justify-content: end; + } + /* mini-compare card heights for the slots: heading-m, body-m, heading-m-price, price-commitment, offers, promo-text, footer */ + :host([variant='mini-compare-chart']) slot[name='heading-m'] { + min-height: var(--consonant-merch-card-mini-compare-heading-m-height); + } + :host([variant='mini-compare-chart']) slot[name='body-m'] { + min-height: var(--consonant-merch-card-mini-compare-body-m-height); + } + :host([variant='mini-compare-chart']) slot[name='heading-m-price'] { + min-height: var( + --consonant-merch-card-mini-compare-heading-m-price-height + ); + } + :host([variant='mini-compare-chart']) slot[name='price-commitment'] { + min-height: var( + --consonant-merch-card-mini-compare-price-commitment-height + ); + } + :host([variant='mini-compare-chart']) slot[name='offers'] { + min-height: var(--consonant-merch-card-mini-compare-offers-height); + } + :host([variant='mini-compare-chart']) slot[name='promo-text'] { + min-height: var(--consonant-merch-card-mini-compare-promo-text-height); + } + :host([variant='mini-compare-chart']) slot[name='callout-content'] { + min-height: var( + --consonant-merch-card-mini-compare-callout-content-height + ); + } + + :host([variant='plans']) ::slotted([slot='heading-xs']), + :host([variant='segment']) ::slotted([slot='heading-xs']) { + max-width: var(--consonant-merch-card-heading-xs-max-width, 100%); + } +`,ws=()=>{let e=[ke` + /* Tablet */ + @media screen and ${ve(B)} { + :host([size='wide']), + :host([size='super-wide']) { + grid-column: span 3; + width: 100%; + max-width: var(--consonant-merch-card-tablet-wide-width); + margin: 0 auto; + } + } + + /* Laptop */ + @media screen and ${ve(H)} { + :host([size='super-wide']) { + grid-column: span 3; + } + `];return e.push(ke` + /* Large desktop */ + @media screen and ${ve(q)} { + :host([size='super-wide']) { + grid-column: span 4; + } + } + `),e};function Ee(e,t={},r){let n=document.createElement(e);r instanceof HTMLElement?n.appendChild(r):n.innerHTML=r;for(let[o,i]of Object.entries(t))n.setAttribute(o,i);return n}function As(){return window.matchMedia("(max-width: 767px)").matches}function Ss(){return window.matchMedia("(max-width: 1024px)").matches}function Ts(e=1e3){return new Promise(t=>setTimeout(t,e))}var Ps=document.createElement("style");Ps.innerHTML=` +:root { + --consonant-merch-card-detail-font-size: 12px; + --consonant-merch-card-detail-font-weight: 500; + --consonant-merch-card-detail-letter-spacing: 0.8px; + --consonant-merch-card-background-color: #fff; + + --consonant-merch-card-heading-font-size: 18px; + --consonant-merch-card-heading-line-height: 22.5px; + --consonant-merch-card-heading-secondary-font-size: 14px; + --consonant-merch-card-body-font-size: 14px; + --consonant-merch-card-body-line-height: 21px; + --consonant-merch-card-promo-text-height: var(--consonant-merch-card-body-font-size); + + /* responsive width */ + --consonant-merch-card-mobile-width: 300px; + --consonant-merch-card-tablet-wide-width: 700px; + + /* spacing */ + --consonant-merch-spacing-xxxs: 4px; + --consonant-merch-spacing-xxs: 8px; + --consonant-merch-spacing-xs: 16px; + --consonant-merch-spacing-s: 24px; + --consonant-merch-spacing-m: 32px; + + /* cta */ + --consonant-merch-card-cta-font-size: 15px; + + /* headings */ + --consonant-merch-card-heading-xs-font-size: 18px; + --consonant-merch-card-heading-xs-line-height: 22.5px; + --consonant-merch-card-heading-s-font-size: 20px; + --consonant-merch-card-heading-s-line-height: 25px; + --consonant-merch-card-heading-m-font-size: 24px; + --consonant-merch-card-heading-m-line-height: 30px; + --consonant-merch-card-heading-l-font-size: 20px; + --consonant-merch-card-heading-l-line-height: 30px; + --consonant-merch-card-heading-xl-font-size: 36px; + --consonant-merch-card-heading-xl-line-height: 45px; + + /* detail */ + --consonant-merch-card-detail-m-font-size: 12px; + --consonant-merch-card-detail-m-line-height: 15px; + --consonant-merch-card-detail-m-font-weight: 700; + --consonant-merch-card-detail-m-letter-spacing: 1px; + + /* body */ + --consonant-merch-card-body-xxs-font-size: 12px; + --consonant-merch-card-body-xxs-line-height: 18px; + --consonant-merch-card-body-xxs-letter-spacing: 1px; + --consonant-merch-card-body-xs-font-size: 14px; + --consonant-merch-card-body-xs-line-height: 21px; + --consonant-merch-card-body-s-font-size: 16px; + --consonant-merch-card-body-s-line-height: 24px; + --consonant-merch-card-body-m-font-size: 18px; + --consonant-merch-card-body-m-line-height: 27px; + --consonant-merch-card-body-l-font-size: 20px; + --consonant-merch-card-body-l-line-height: 30px; + --consonant-merch-card-body-xl-font-size: 22px; + --consonant-merch-card-body-xl-line-height: 33px; + + + --consonant-merch-card-heading-padding: 0; + --consonant-merch-card-image-height: 180px; + + /* colors */ + --consonant-merch-card-border-color: #eaeaea; + --color-accent: #1473E6; + --merch-color-focus-ring: #1473E6; + --merch-color-grey-80: #2c2c2c; + --merch-color-green-promo: #2D9D78; + + /* merch card generic */ + --consonant-merch-card-max-width: 300px; + --transition: cmax-height 0.3s linear, opacity 0.3s linear; + + /* special offers */ + --consonant-merch-card-special-offers-width: 378px; + + /* image */ + --consonant-merch-card-image-width: 300px; + + /* segment */ + --consonant-merch-card-segment-width: 378px; + + /* inline-heading */ + --consonant-merch-card-inline-heading-width: 300px; + + /* product */ + --consonant-merch-card-product-width: 300px; + + /* plans */ + --consonant-merch-card-plans-width: 300px; + --consonant-merch-card-plans-icon-size: 40px; + + /* catalog */ + --consonant-merch-card-catalog-width: 276px; + --consonant-merch-card-catalog-icon-size: 40px; + + /* twp */ + --consonant-merch-card-twp-width: 268px; + --consonant-merch-card-twp-mobile-width: 300px; + --consonant-merch-card-twp-mobile-height: 358px; + + /* ccd-action */ + --consonant-merch-card-ccd-action-width: 276px; + --consonant-merch-card-ccd-action-min-height: 320px; + + + /*mini compare chart */ + --consonant-merch-card-mini-compare-chart-icon-size: 32px; + --consonant-merch-card-mini-compare-mobile-cta-font-size: 15px; + --consonant-merch-card-mini-compare-mobile-cta-width: 75px; + --consonant-merch-card-mini-compare-badge-mobile-max-width: 50px; + + /* inline SVGs */ + --checkmark-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath fill='%23fff' d='M3.788 9A.999.999 0 0 1 3 8.615l-2.288-3a1 1 0 1 1 1.576-1.23l1.5 1.991 3.924-4.991a1 1 0 1 1 1.576 1.23l-4.712 6A.999.999 0 0 1 3.788 9z' class='spectrum-UIIcon--medium'/%3E%3C/svg%3E%0A"); + + --secure-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23757575' viewBox='0 0 12 15'%3E%3Cpath d='M11.5 6H11V5A5 5 0 1 0 1 5v1H.5a.5.5 0 0 0-.5.5v8a.5.5 0 0 0 .5.5h11a.5.5 0 0 0 .5-.5v-8a.5.5 0 0 0-.5-.5ZM3 5a3 3 0 1 1 6 0v1H3Zm4 6.111V12.5a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1.389a1.5 1.5 0 1 1 2 0Z'/%3E%3C/svg%3E"); + + --info-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 36 36'>'); + + /* callout */ + --consonant-merch-card-callout-line-height: 21px; + --consonant-merch-card-callout-font-size: 14px; + --consonant-merch-card-callout-font-color: #2C2C2C; + --consonant-merch-card-callout-icon-size: 16px; + --consonant-merch-card-callout-icon-top: 6px; + --consonant-merch-card-callout-icon-right: 8px; + --consonant-merch-card-callout-letter-spacing: 0px; + --consonant-merch-card-callout-icon-padding: 34px; + --consonant-merch-card-callout-spacing-xxs: 8px; +} + +merch-card-collection { + display: contents; +} + +merch-card-collection > merch-card:not([style]) { + display: none; +} + +merch-card-collection > p[slot], +merch-card-collection > div[slot] p { + margin: 0; +} + +.one-merch-card, +.two-merch-cards, +.three-merch-cards, +.four-merch-cards { + display: grid; + justify-content: center; + justify-items: stretch; + gap: var(--consonant-merch-spacing-m); + padding: var(--spacing-m); +} + +merch-card.background-opacity-70 { + background-color: rgba(255 255 255 / 70%); +} + +merch-card.has-divider hr { + margin-bottom: var(--consonant-merch-spacing-xs); + height: 1px; + border: none; +} + +merch-card[variant="special-offers"] span[is="inline-price"][data-template="strikethrough"] { + font-size: var(--consonant-merch-card-body-xs-font-size); +} + +merch-card p, merch-card h3, merch-card h4 { + margin: 0; +} + +merch-card span[is=inline-price] { + display: inline-block; +} + +merch-card [slot='heading-xs'] { + color: var(--merch-color-grey-80); + font-size: var(--consonant-merch-card-heading-xs-font-size); + line-height: var(--consonant-merch-card-heading-xs-line-height); + margin: 0; + text-decoration: none; +} + +merch-card.dc-pricing [slot='heading-xs'] { + margin-bottom: var(--consonant-merch-spacing-xxs); +} + +merch-card:not([variant='inline-heading']) [slot='heading-xs'] a { + color: var(--merch-color-grey-80); +} + +merch-card [slot='starting-at'] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + font-weight: 500; +} + +merch-card [slot='heading-xs'] a:not(:hover) { + text-decoration: inherit; +} + +merch-card [slot='heading-s'] { + font-size: var(--consonant-merch-card-heading-s-font-size); + line-height: var(--consonant-merch-card-heading-s-line-height); + margin: 0; + color: var(--merch-color-grey-80); +} + +merch-card [slot='heading-m'] { + font-size: var(--consonant-merch-card-heading-m-font-size); + line-height: var(--consonant-merch-card-heading-m-line-height); + margin: 0; + color: var(--merch-color-grey-80); + font-weight: 700; +} + +merch-card [slot='heading-m-price'] { + font-size: var(--consonant-merch-card-heading-m-font-size); + line-height: var(--consonant-merch-card-heading-m-line-height); + padding: 0 var(--consonant-merch-spacing-s); + font-weight: 700; + margin: 0; + color: var(--merch-color-grey-80); +} + +merch-card [slot='offers'] { + padding: var(--consonant-merch-spacing-xxs) var(--consonant-merch-spacing-s); +} + +merch-card [slot='heading-l'] { + font-size: var(--consonant-merch-card-heading-l-font-size); + line-height: var(--consonant-merch-card-heading-l-line-height); + margin: 0; + color: var(--merch-color-grey-80); +} + +merch-card [slot='heading-xl'] { + font-size: var(--consonant-merch-card-heading-xl-font-size); + line-height: var(--consonant-merch-card-heading-xl-line-height); + margin: 0; + color: var(--merch-color-grey-80); +} + +merch-card [slot='callout-content'] { + display: flex; + flex-direction: column; + margin: var(--consonant-merch-spacing-xxxs) 0px; + gap: var(--consonant-merch-card-callout-spacing-xxs); +} + +merch-card [slot='callout-content'] > div { + display: flex; + flex-direction: column; + margin: var(--consonant-merch-spacing-xxxs) 0px; + gap: var(--consonant-merch-card-callout-spacing-xxs); + align-items: flex-start; +} + +merch-card [slot='callout-content'] > div > div { + display: flex; + background: rgba(203 203 203 / 50%); + border-radius: var(--consonant-merch-spacing-xxxs); + padding: var(--consonant-merch-spacing-xxxs) var(--consonant-merch-spacing-xxxs) var(--consonant-merch-spacing-xxxs) var(--consonant-merch-spacing-xxs); +} + +merch-card [slot='callout-content'] > div > div > div { + display: inline-block; + text-align: left; + font: normal normal normal var(--consonant-merch-card-callout-font-size)/var(--consonant-merch-card-callout-line-height) var(--body-font-family, 'Adobe Clean'); + letter-spacing: var(--consonant-merch-card-callout-letter-spacing); + color: var(--consonant-merch-card-callout-font-color); +} + +merch-card [slot='callout-content'] img { + width: var(--consonant-merch-card-callout-icon-size); + height: var(--consonant-merch-card-callout-icon-size); + margin: 2.5px 0px 0px 9px; +} + +merch-card [slot='detail-m'] { + font-size: var(--consonant-merch-card-detail-m-font-size); + letter-spacing: var(--consonant-merch-card-detail-m-letter-spacing); + font-weight: var(--consonant-merch-card-detail-m-font-weight); + text-transform: uppercase; + margin: 0; + color: var(--merch-color-grey-80); +} + +merch-card [slot="body-xxs"] { + font-size: var(--consonant-merch-card-body-xxs-font-size); + line-height: var(--consonant-merch-card-body-xxs-line-height); + font-weight: normal; + letter-spacing: var(--consonant-merch-card-body-xxs-letter-spacing); + color: var(--merch-color-grey-80); + margin: 0; +} + +merch-card [slot="body-xs"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + color: var(--merch-color-grey-80); +} + +merch-card [slot="body-m"] { + font-size: var(--consonant-merch-card-body-m-font-size); + line-height: var(--consonant-merch-card-body-m-line-height); + color: var(--merch-color-grey-80); +} + +merch-card [slot="body-l"] { + font-size: var(--consonant-merch-card-body-l-font-size); + line-height: var(--consonant-merch-card-body-l-line-height); + color: var(--merch-color-grey-80); +} + +merch-card [slot="body-xl"] { + font-size: var(--consonant-merch-card-body-xl-font-size); + line-height: var(--consonant-merch-card-body-xl-line-height); + color: var(--merch-color-grey-80); +} + +merch-card[variant="plans"] [slot="description"] { + min-height: 84px; +} + +merch-card[variant="catalog"] [slot="action-menu-content"] { + background-color: #000; + color: var(--color-white, #fff); + font-size: var(--consonant-merch-card-body-xs-font-size); + width: fit-content; + padding: var(--consonant-merch-spacing-xs); + border-radius: var(--consonant-merch-spacing-xxxs); + position: absolute; + top: 55px; + right: 15px; + line-height: var(--consonant-merch-card-body-line-height); +} + +merch-card[variant="catalog"] [slot="action-menu-content"] ul { + padding-left: 0; + padding-bottom: var(--consonant-merch-spacing-xss); + margin-top: 0; + margin-bottom: 0; + list-style-position: inside; + list-style-type: '\u2022 '; +} + +merch-card[variant="catalog"] [slot="action-menu-content"] ul li { + padding-left: 0; + line-height: var(--consonant-merch-card-body-line-height); +} + +merch-card[variant="catalog"] [slot="action-menu-content"] ::marker { + margin-right: 0; +} + +merch-card[variant="catalog"] [slot="action-menu-content"] p { + color: var(--color-white, #fff); +} + +merch-card[variant="catalog"] [slot="action-menu-content"] a { + color: var(--consonant-merch-card-background-color); + text-decoration: underline; +} + +merch-card[variant="catalog"] .payment-details { + font-size: var(--consonant-merch-card-body-font-size); + font-style: italic; + font-weight: 400; + line-height: var(--consonant-merch-card-body-line-height); +} + +merch-card[variant="ccd-action"] .price-strikethrough { + font-size: 18px; +} + +merch-card[variant="plans"] [slot="quantity-select"] { + display: flex; + justify-content: flex-start; + box-sizing: border-box; + width: 100%; + padding: var(--consonant-merch-spacing-xs); +} + +merch-card[variant="twp"] div[class$='twp-badge'] { + padding: 4px 10px 5px 10px; +} + +merch-card[variant="twp"] [slot="body-xs-top"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + color: var(--merch-color-grey-80); +} + +merch-card[variant="twp"] [slot="body-xs"] ul { + padding: 0; + margin: 0; +} + +merch-card[variant="twp"] [slot="body-xs"] ul li { + list-style-type: none; + padding-left: 0; +} + +merch-card[variant="twp"] [slot="body-xs"] ul li::before { + content: '\xB7'; + font-size: 20px; + padding-right: 5px; + font-weight: bold; +} + +merch-card[variant="twp"] [slot="footer"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + padding: var(--consonant-merch-spacing-s) + var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-xs); + color: var(--merch-color-grey-80); + display: flex; + flex-flow: wrap; +} + +merch-card[variant='twp'] merch-quantity-select, +merch-card[variant='twp'] merch-offer-select { + display: none; +} + +[slot="cci-footer"] p, +[slot="cct-footer"] p, +[slot="cce-footer"] p { + margin: 0; +} + +/* mini compare chart card styles */ + +merch-card[variant="mini-compare-chart"] [slot="heading-m"] { + padding: 0 var(--consonant-merch-spacing-s) 0; +} + +merch-card[variant="mini-compare-chart"] [slot="body-m"] { + padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s); +} + +merch-card[variant="mini-compare-chart"] [is="inline-price"] { + display: inline-block; + min-height: 30px; + min-width: 1px; +} + +merch-card[variant="mini-compare-chart"] [slot='callout-content'] { + padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s) 0px; +} + +merch-card[variant="mini-compare-chart"] [slot='callout-content'] [is="inline-price"] { + min-height: unset; +} + +merch-card[variant="mini-compare-chart"] [slot="price-commitment"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + padding: 0 var(--consonant-merch-spacing-s); +} + +merch-card[variant="mini-compare-chart"] [slot="price-commitment"] a { + display: inline-block; + height: 27px; +} + +merch-card[variant="mini-compare-chart"] [slot="offers"] { + font-size: var(--consonant-merch-card-body-xs-font-size); +} + +merch-card [slot="promo-text"] { + color: var(--merch-color-green-promo); + font-size: var(--consonant-merch-card-promo-text-height); + font-weight: 700; + line-height: var(--consonant-merch-card-promo-text-height); + margin: 0; + min-height: var(--consonant-merch-card-promo-text-height); + padding: 0; +} + + +merch-card[variant="mini-compare-chart"] [slot="body-xxs"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s) 0; +} + +merch-card[variant="mini-compare-chart"] [slot="promo-text"] { + font-size: var(--consonant-merch-card-body-m-font-size); + padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s) 0; +} + +merch-card[variant="mini-compare-chart"] [slot="promo-text"] a { + text-decoration: underline; +} + +merch-card[variant="mini-compare-chart"] .footer-row-icon { + display: flex; + place-items: center; +} + +merch-card[variant="mini-compare-chart"] .footer-row-icon img { + max-width: initial; + width: var(--consonant-merch-card-mini-compare-chart-icon-size); + height: var(--consonant-merch-card-mini-compare-chart-icon-size); +} + +merch-card[variant="mini-compare-chart"] .footer-row-cell { + border-top: 1px solid var(--consonant-merch-card-border-color); + display: flex; + gap: var(--consonant-merch-spacing-xs); + justify-content: start; + place-items: center; + padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s); +} + +merch-card[variant="mini-compare-chart"] .footer-row-cell-description { + font-size: var(--consonant-merch-card-body-s-font-size); + line-height: var(--consonant-merch-card-body-s-line-height); +} + +merch-card[variant="mini-compare-chart"] .footer-row-cell-description p { + color: var(--merch-color-grey-80); + vertical-align: bottom; +} + +merch-card[variant="mini-compare-chart"] .footer-row-cell-description a { + color: var(--color-accent); + text-decoration: solid; +} + +/* mini compare mobile */ +@media screen and ${Nt} { + merch-card[variant="mini-compare-chart"] [slot='heading-m'] { + font-size: var(--consonant-merch-card-body-s-font-size); + line-height: var(--consonant-merch-card-body-s-line-height); + } + + merch-card[variant="mini-compare-chart"] [slot='heading-m-price'] { + font-size: var(--consonant-merch-card-body-s-font-size); + line-height: var(--consonant-merch-card-body-s-line-height); + } + + merch-card[variant="mini-compare-chart"] [slot='body-m'] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + } + + merch-card[variant="mini-compare-chart"] [slot="promo-text"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + } + merch-card[variant="mini-compare-chart"] .footer-row-cell-description { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + } +} + +/* mini compare tablet */ +@media screen and ${Rt} { + merch-card[variant="mini-compare-chart"] [slot='heading-m'] { + font-size: var(--consonant-merch-card-body-s-font-size); + line-height: var(--consonant-merch-card-body-s-line-height); + } + + merch-card[variant="mini-compare-chart"] [slot='heading-m-price'] { + font-size: var(--consonant-merch-card-body-s-font-size); + line-height: var(--consonant-merch-card-body-s-line-height); + } + + merch-card[variant="mini-compare-chart"] [slot='body-m'] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + } + + merch-card[variant="mini-compare-chart"] [slot="promo-text"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + } + + merch-card[variant="mini-compare-chart"] .footer-row-cell-description { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + } +} + +div[slot="footer"] { + display: contents; +} + +[slot="footer"] a { + word-wrap: break-word; + text-align: center; +} + +[slot="footer"] a:not([class]) { + font-weight: 700; + font-size: var(--consonant-merch-card-cta-font-size); +} + +div[slot='bg-image'] img { + position: relative; + width: 100%; + min-height: var(--consonant-merch-card-image-height); + max-height: var(--consonant-merch-card-image-height); + object-fit: cover; + border-top-left-radius: 16px; + border-top-right-radius: 16px; +} + +/* Mobile */ +@media screen and ${Nt} { + :root { + --consonant-merch-card-mini-compare-chart-width: 302px; + --consonant-merch-card-segment-width: 276px; + --consonant-merch-card-mini-compare-chart-wide-width: 302px; + --consonant-merch-card-special-offers-width: 302px; + --consonant-merch-card-twp-width: 300px; + } +} + + +/* Tablet */ +@media screen and ${B} { + :root { + --consonant-merch-card-catalog-width: 302px; + --consonant-merch-card-plans-width: 302px; + --consonant-merch-card-segment-width: 276px; + --consonant-merch-card-mini-compare-chart-width: 302px; + --consonant-merch-card-mini-compare-chart-wide-width: 302px; + --consonant-merch-card-special-offers-width: 302px; + --consonant-merch-card-twp-width: 268px; + } +} + +/* desktop */ +@media screen and ${H} { + :root { + --consonant-merch-card-catalog-width: 276px; + --consonant-merch-card-plans-width: 276px; + --consonant-merch-card-segment-width: 302px; + --consonant-merch-card-inline-heading-width: 378px; + --consonant-merch-card-product-width: 378px; + --consonant-merch-card-image-width: 378px; + --consonant-merch-card-mini-compare-chart-width: 378px; + --consonant-merch-card-mini-compare-chart-wide-width: 484px; + --consonant-merch-card-twp-width: 268px; + } +} + +/* supported cards */ +/* grid style for plans */ +.one-merch-card.plans, +.two-merch-cards.plans, +.three-merch-cards.plans, +.four-merch-cards.plans { + grid-template-columns: var(--consonant-merch-card-plans-width); +} + +/* Tablet */ +@media screen and ${B} { + .two-merch-cards.plans, + .three-merch-cards.plans, + .four-merch-cards.plans { + grid-template-columns: repeat(2, var(--consonant-merch-card-plans-width)); + } +} + +/* desktop */ +@media screen and ${H} { + .three-merch-cards.plans, + .four-merch-cards.plans { + grid-template-columns: repeat(3, var(--consonant-merch-card-plans-width)); + } +} + +/* Large desktop */ + @media screen and ${q} { + .four-merch-cards.plans { + grid-template-columns: repeat(4, var(--consonant-merch-card-plans-width)); + } +} + + +/* grid style for catalog */ +.one-merch-card.catalog, +.two-merch-cards.catalog, +.three-merch-cards.catalog, +.four-merch-cards.catalog { + grid-template-columns: var(--consonant-merch-card-catalog-width); +} + +/* Tablet */ +@media screen and ${B} { + .two-merch-cards.catalog, + .three-merch-cards.catalog, + .four-merch-cards.catalog { + grid-template-columns: repeat(2, var(--consonant-merch-card-catalog-width)); + } +} + +/* desktop */ +@media screen and ${H} { + .three-merch-cards.catalog, + .four-merch-cards.catalog { + grid-template-columns: repeat(3, var(--consonant-merch-card-catalog-width)); + } +} + +/* Large desktop */ + @media screen and ${q} { + .four-merch-cards.catalog { + grid-template-columns: repeat(4, var(--consonant-merch-card-catalog-width)); + } +} + + +/* grid style for special-offers */ +.one-merch-card.special-offers, +.two-merch-cards.special-offers, +.three-merch-cards.special-offers, +.four-merch-cards.special-offers { + grid-template-columns: minmax(300px, var(--consonant-merch-card-special-offers-width)); +} + +/* Tablet */ +@media screen and ${B} { + .two-merch-cards.special-offers, + .three-merch-cards.special-offers, + .four-merch-cards.special-offers { + grid-template-columns: repeat(2, minmax(300px, var(--consonant-merch-card-special-offers-width))); + } +} + +/* desktop */ +@media screen and ${H} { + .three-merch-cards.special-offers, + .four-merch-cards.special-offers { + grid-template-columns: repeat(3, minmax(300px, var(--consonant-merch-card-special-offers-width))); + } +} + +@media screen and ${q} { + .four-merch-cards.special-offers { + grid-template-columns: repeat(4, minmax(300px, var(--consonant-merch-card-special-offers-width))); + } +} + + +/* grid style for image */ +.one-merch-card.image, +.two-merch-cards.image, +.three-merch-cards.image, +.four-merch-cards.image { + grid-template-columns: var(--consonant-merch-card-image-width); +} + +/* Tablet */ +@media screen and ${B} { + .two-merch-cards.image, + .three-merch-cards.image, + .four-merch-cards.image { + grid-template-columns: repeat(2, var(--consonant-merch-card-image-width)); + } +} + +/* desktop */ +@media screen and ${H} { + .three-merch-cards.image, + .four-merch-cards.image { + grid-template-columns: repeat(3, var(--consonant-merch-card-image-width)); + } +} + +/* Large desktop */ + @media screen and ${q} { + .four-merch-cards.image { + grid-template-columns: repeat(4, var(--consonant-merch-card-image-width)); + } +} + + +/* grid style for segment */ +.one-merch-card.segment, +.two-merch-cards.segment, +.three-merch-cards.segment, +.four-merch-cards.segment { + grid-template-columns: minmax(276px, var(--consonant-merch-card-segment-width)); +} + +/* Tablet */ +@media screen and ${B} { + .two-merch-cards.segment, + .three-merch-cards.segment, + .four-merch-cards.segment { + grid-template-columns: repeat(2, minmax(276px, var(--consonant-merch-card-segment-width))); + } +} + +/* desktop */ +@media screen and ${H} { + .three-merch-cards.segment { + grid-template-columns: repeat(3, minmax(276px, var(--consonant-merch-card-segment-width))); + } + + .four-merch-cards.segment { + grid-template-columns: repeat(4, minmax(276px, var(--consonant-merch-card-segment-width))); + } +} + + +/* grid style for product */ +.one-merch-card.product, +.two-merch-cards.product, +.three-merch-cards.product, +.four-merch-cards.product { + grid-template-columns: var(--consonant-merch-card-product-width); +} + +/* Tablet */ +@media screen and ${B} { + .two-merch-cards.product, + .three-merch-cards.product, + .four-merch-cards.product { + grid-template-columns: repeat(2, var(--consonant-merch-card-product-width)); + } +} + +/* desktop */ +@media screen and ${H} { + .three-merch-cards.product, + .four-merch-cards.product { + grid-template-columns: repeat(3, var(--consonant-merch-card-product-width)); + } +} + +/* Large desktop */ + @media screen and ${q} { + .four-merch-cards.product { + grid-template-columns: repeat(4, var(--consonant-merch-card-product-width)); + } +} + +/* grid style for twp */ +.one-merch-card.twp, +.two-merch-cards.twp, +.three-merch-cards.twp { + grid-template-columns: var(--consonant-merch-card-image-width); +} + +/* Tablet */ +@media screen and ${B} { + .one-merch-card.twp, + .two-merch-cards.twp { + grid-template-columns: repeat(2, var(--consonant-merch-card-twp-width)); + } + .three-merch-cards.twp { + grid-template-columns: repeat(3, var(--consonant-merch-card-twp-width)); + } +} + +/* desktop */ +@media screen and ${H} { + .one-merch-card.twp + .two-merch-cards.twp { + grid-template-columns: repeat(2, var(--consonant-merch-card-twp-width)); + } + .three-merch-cards.twp { + grid-template-columns: repeat(3, var(--consonant-merch-card-twp-width)); + } +} + +/* Large desktop */ + @media screen and ${q} { + .one-merch-card.twp + .two-merch-cards.twp { + grid-template-columns: repeat(2, var(--consonant-merch-card-twp-width)); + } + .three-merch-cards.twp { + grid-template-columns: repeat(3, var(--consonant-merch-card-twp-width)); + } +} + +/* Mobile */ +@media screen and ${Nt} { + .one-merch-card.twp, + .two-merch-cards.twp, + .three-merch-cards.twp { + grid-template-columns: repeat(1, var(--consonant-merch-card-twp-mobile-width)); + } +} + +/* grid style for inline-heading */ +.one-merch-card.inline-heading, +.two-merch-cards.inline-heading, +.three-merch-cards.inline-heading, +.four-merch-cards.inline-heading { + grid-template-columns: var(--consonant-merch-card-inline-heading-width); +} + +/* Tablet */ +@media screen and ${B} { + .two-merch-cards.inline-heading, + .three-merch-cards.inline-heading, + .four-merch-cards.inline-heading { + grid-template-columns: repeat(2, var(--consonant-merch-card-inline-heading-width)); + } +} + +/* desktop */ +@media screen and ${H} { + .three-merch-cards.inline-heading, + .four-merch-cards.inline-heading { + grid-template-columns: repeat(3, var(--consonant-merch-card-inline-heading-width)); + } +} + +/* Large desktop */ + @media screen and ${q} { + .four-merch-cards.inline-heading { + grid-template-columns: repeat(4, var(--consonant-merch-card-inline-heading-width)); + } +} + +/* grid style for ccd-action */ +.one-merch-card.ccd-action, +.two-merch-cards.ccd-action, +.three-merch-cards.ccd-action, +.four-merch-cards.ccd-action { + grid-template-columns: var(--consonant-merch-card-ccd-action-width); +} + +/* Tablet */ +@media screen and ${B} { + .two-merch-cards.ccd-action, + .three-merch-cards.ccd-action, + .four-merch-cards.ccd-action { + grid-template-columns: repeat(2, var(--consonant-merch-card-ccd-action-width)); + } +} + +/* desktop */ +@media screen and ${H} { + .three-merch-cards.ccd-action, + .four-merch-cards.ccd-action { + grid-template-columns: repeat(3, var(--consonant-merch-card-ccd-action-width)); + } +} + +/* Large desktop */ + @media screen and ${q} { + .four-merch-cards.ccd-action { + grid-template-columns: repeat(4, var(--consonant-merch-card-ccd-action-width)); + } +} + +/* grid style for mini-compare-chart */ +.one-merch-card.mini-compare-chart { + grid-template-columns: var(--consonant-merch-card-mini-compare-chart-wide-width); + gap: var(--consonant-merch-spacing-xs); +} + +.two-merch-cards.mini-compare-chart, +.three-merch-cards.mini-compare-chart, +.four-merch-cards.mini-compare-chart { + grid-template-columns: repeat(2, var(--consonant-merch-card-mini-compare-chart-width)); + gap: var(--consonant-merch-spacing-xs); +} + +@media screen and ${Nt} { + .two-merch-cards.mini-compare-chart, + .three-merch-cards.mini-compare-chart, + .four-merch-cards.mini-compare-chart { + grid-template-columns: var(--consonant-merch-card-mini-compare-chart-width); + gap: var(--consonant-merch-spacing-xs); + } +} + +@media screen and ${Rt} { + .three-merch-cards.mini-compare-chart merch-card [slot="footer"] a, + .four-merch-cards.mini-compare-chart merch-card [slot="footer"] a { + flex: 1; + } +} + +/* Tablet */ +@media screen and ${B} { + .two-merch-cards.mini-compare-chart { + grid-template-columns: repeat(2, minmax(var(--consonant-merch-card-mini-compare-chart-width), var(--consonant-merch-card-mini-compare-chart-wide-width))); + gap: var(--consonant-merch-spacing-m); + } + + .three-merch-cards.mini-compare-chart, + .four-merch-cards.mini-compare-chart { + grid-template-columns: repeat(2, minmax(var(--consonant-merch-card-mini-compare-chart-width), var(--consonant-merch-card-mini-compare-chart-wide-width))); + } +} + +/* desktop */ +@media screen and ${H} { + .one-merch-card.mini-compare-chart { + grid-template-columns: var(--consonant-merch-card-mini-compare-chart-wide-width); + } + + .two-merch-cards.mini-compare-chart { + grid-template-columns: repeat(2, var(--consonant-merch-card-mini-compare-chart-wide-width)); + gap: var(--consonant-merch-spacing-m); + } + + .three-merch-cards.mini-compare-chart, + .four-merch-cards.mini-compare-chart { + grid-template-columns: repeat(3, var(--consonant-merch-card-mini-compare-chart-width)); + gap: var(--consonant-merch-spacing-m); + } +} + +@media screen and ${q} { + .four-merch-cards.mini-compare-chart { + grid-template-columns: repeat(4, var(--consonant-merch-card-mini-compare-chart-width)); + } +} + +/* mini-compare card footer rows */ +merch-card .footer-row-cell:nth-child(1) { + min-height: var(--consonant-merch-card-footer-row-1-min-height); +} + +merch-card .footer-row-cell:nth-child(2) { + min-height: var(--consonant-merch-card-footer-row-2-min-height); +} + +merch-card .footer-row-cell:nth-child(3) { + min-height: var(--consonant-merch-card-footer-row-3-min-height); +} + +merch-card .footer-row-cell:nth-child(4) { + min-height: var(--consonant-merch-card-footer-row-4-min-height); +} + +merch-card .footer-row-cell:nth-child(5) { + min-height: var(--consonant-merch-card-footer-row-5-min-height); +} + +merch-card .footer-row-cell:nth-child(6) { + min-height: var(--consonant-merch-card-footer-row-6-min-height); +} + +merch-card .footer-row-cell:nth-child(7) { + min-height: var(--consonant-merch-card-footer-row-7-min-height); +} + +merch-card .footer-row-cell:nth-child(8) { + min-height: var(--consonant-merch-card-footer-row-8-min-height); +} + +span[is="inline-price"][data-template='strikethrough'] { + text-decoration: line-through; +} + +merch-card sp-button a { + text-decoration: none; + color: var( + --highcontrast-button-content-color-default, + var( + --mod-button-content-color-default, + var(--spectrum-button-content-color-default) + ) + ); +} + +merch-card span.placeholder-resolved[data-template='strikethrough'], +merch-card span.price.price-strikethrough { + font-size: var(--consonant-merch-card-body-xs-font-size); + font-weight: normal; +} + +/* merch-offer-select */ +merch-offer-select[variant="subscription-options"] merch-offer span[is="inline-price"][data-display-tax='true'] .price-tax-inclusivity { + font-size: 12px; + font-style: italic; + font-weight: normal; + position: absolute; + left: 0; + top: 20px; +} + +body.merch-modal { + overflow: hidden; + scrollbar-gutter: stable; + height: 100vh; +} +`;document.head.appendChild(Ps);var Cs="merch-offer-select:ready",$s="merch-card:ready",Os="merch-card:action-menu-toggle";var go="merch-storage:change",xo="merch-quantity-selector:change";var jc="merch-card",Bc=32,Or="mini-compare-chart",Ls=e=>`--consonant-merch-card-footer-row-${e}-min-height`,de,It=class extends oe{constructor(){super();O(this,"customerSegment");O(this,"marketSegment");Q(this,de);this.filters={},this.types="",this.selected=!1}updated(r){(r.has("badgeBackgroundColor")||r.has("borderColor"))&&(this.style.border=this.computedBorderStyle),this.updateComplete.then(async()=>{let o=Array.from(this.querySelectorAll('span[is="inline-price"][data-wcs-osi]')).filter(i=>!i.closest('[slot="callout-content"]'));await Promise.all(o.map(i=>i.onceSettled())),this.adjustTitleWidth(),As()?this.removeEmptyRows():(this.adjustMiniCompareBodySlots(),this.adjustMiniCompareFooterRows())})}get computedBorderStyle(){return this.variant!=="twp"?`1px solid ${this.borderColor?this.borderColor:this.badgeBackgroundColor}`:""}get evergreen(){return this.classList.contains("intro-pricing")}get stockCheckbox(){return this.checkboxLabel?x``:""}get cardImage(){return x`
+ + ${this.badge} +
`}get secureLabelFooter(){let r=this.secureLabel?x`${this.secureLabel}`:"";return x`
${r}
`}get miniCompareFooter(){let r=this.secureLabel?x` + ${this.secureLabel}`:x``;return x`
${r}
`}get badge(){let r;if(!(!this.badgeBackgroundColor||!this.badgeColor||!this.badgeText))return this.evergreen&&(r=`border: 1px solid ${this.badgeBackgroundColor}; border-right: none;`),x` +
+ ${this.badgeText} +
+ `}get badgeElement(){return this.shadowRoot.getElementById("badge")}getContainer(){return this.closest('[class*="-merch-cards"]')??this.parentElement}get headingmMSlot(){return this.shadowRoot.querySelector('slot[name="heading-m"]').assignedElements()[0]}get footerSlot(){return this.shadowRoot.querySelector('slot[name="footer"]')?.assignedElements()[0]}get price(){return this.headingmMSlot?.querySelector('span[is="inline-price"]')}get checkoutLinks(){return[...this.footerSlot?.querySelectorAll('a[is="checkout-link"]')??[]]}async toggleStockOffer({target:r}){if(!this.stockOfferOsis)return;let n=this.checkoutLinks;if(n.length!==0)for(let o of n){await o.onceSettled();let i=o.value?.[0]?.planType;if(!i)return;let s=this.stockOfferOsis[i];if(!s)return;let c=o.dataset.wcsOsi.split(",").filter(a=>a!==s);r.checked&&c.push(s),o.dataset.wcsOsi=c.join(",")}}toggleActionMenu(r){let n=r?.type==="mouseleave"?!0:void 0,o=this.shadowRoot.querySelector('slot[name="action-menu-content"]');o&&(n||this.dispatchEvent(new CustomEvent(Os,{bubbles:!0,composed:!0,detail:{card:this.name,type:"action-menu"}})),o.classList.toggle("hidden",n))}handleQuantitySelection(r){let n=this.checkoutLinks;for(let o of n)o.dataset.quantity=r.detail.option}get titleElement(){return this.variant==="special-offers"?this.querySelector('[slot="detail-m"]'):this.querySelector('[slot="heading-xs"]')}get title(){return this.titleElement?.textContent?.trim()}get description(){return this.querySelector('[slot="body-xs"]')?.textContent?.trim()}updateFilters(r){let n={...this.filters};Object.keys(n).forEach(o=>{if(r){n[o].order=Math.min(n[o].order||2,2);return}let i=n[o].order;i===1||isNaN(i)||(n[o].order=Number(i)+1)}),this.filters=n}includes(r){return this.textContent.match(new RegExp(r,"i"))!==null}render(){if(!(!this.isConnected||this.style.display==="none"))switch(this.variant){case"special-offers":return this.renderSpecialOffer();case"segment":return this.renderSegment();case"plans":return this.renderPlans();case"catalog":return this.renderCatalog();case"image":return this.renderImage();case"product":return this.renderProduct();case"inline-heading":return this.renderInlineHeading();case Or:return this.renderMiniCompareChart();case"ccd-action":return this.renderCcdAction();case"twp":return this.renderTwp();default:return this.renderProduct()}}renderSpecialOffer(){return x`${this.cardImage} +
+ + + +
+ ${this.evergreen?x` +
+ +
+ `:x` +
+ ${this.secureLabelFooter} + `} + `}get promoBottom(){return this.classList.contains("promo-bottom")}get startingAt(){return this.classList.contains("starting-at")}renderSegment(){return x` ${this.badge} +
+ + + ${this.promoBottom?"":x``} + + ${this.promoBottom?x``:""} +
+
+ ${this.secureLabelFooter}`}renderPlans(){return x` ${this.badge} +
+ + + + + ${this.promoBottom?"":x` `} + + ${this.promoBottom?x` `:""} + ${this.stockCheckbox} +
+ + ${this.secureLabelFooter}`}renderCatalog(){return x`
+
+ ${this.badge} +
+
+ ${this.actionMenuContent} + + + + ${this.promoBottom?"":x``} + + ${this.promoBottom?x``:""} +
+ ${this.secureLabelFooter}`}renderImage(){return x`${this.cardImage} +
+ + + + ${this.promoBottom?x``:x``} +
+ ${this.evergreen?x` +
+ +
+ `:x` +
+ ${this.secureLabelFooter} + `}`}renderInlineHeading(){return x` ${this.badge} +
+
+ + +
+ +
+ ${this.customHr?"":x`
`} ${this.secureLabelFooter}`}renderProduct(){return x` ${this.badge} +
+ + + + ${this.promoBottom?"":x``} + + ${this.promoBottom?x``:""} +
+ ${this.secureLabelFooter}`}renderMiniCompareChart(){let{badge:r}=this;return x`
+ ${r} +
+ + + + + + + + + ${this.miniCompareFooter} + `}renderTwp(){return x`${this.badge} +
+ + + +
+
+ +
+
`}renderCcdAction(){return x`
+ ${this.badge} + + + ${this.promoBottom?x``:x``} +
+ +
`}connectedCallback(){super.connectedCallback(),K(this,de,this.getContainer()),this.setAttribute("tabindex",this.getAttribute("tabindex")??"0"),this.addEventListener("mouseleave",this.toggleActionMenu),this.addEventListener(xo,this.handleQuantitySelection),this.addEventListener(Cs,this.merchCardReady,{once:!0}),this.updateComplete.then(()=>{this.merchCardReady()}),this.storageOptions?.addEventListener("change",this.handleStorageChange)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener(xo,this.handleQuantitySelection),this.storageOptions?.removeEventListener(go,this.handleStorageChange)}updateMiniCompareElementMinHeight(r,n){let o=`--consonant-merch-card-mini-compare-${n}-height`,i=Math.max(0,parseInt(window.getComputedStyle(r).height)||0),s=parseInt(R(this,de).style.getPropertyValue(o))||0;i>s&&R(this,de).style.setProperty(o,`${i}px`)}async adjustTitleWidth(){if(!["segment","plans"].includes(this.variant))return;let r=this.getBoundingClientRect().width,n=this.badgeElement?.getBoundingClientRect().width||0;r===0||n===0||this.style.setProperty("--consonant-merch-card-heading-xs-max-width",`${Math.round(r-n-16)}px`)}async adjustMiniCompareBodySlots(){if(this.variant!==Or||this.getBoundingClientRect().width===0)return;this.updateMiniCompareElementMinHeight(this.shadowRoot.querySelector(".top-section"),"top-section"),["heading-m","body-m","heading-m-price","price-commitment","offers","promo-text","callout-content","secure-transaction-label"].forEach(o=>this.updateMiniCompareElementMinHeight(this.shadowRoot.querySelector(`slot[name="${o}"]`),o)),this.updateMiniCompareElementMinHeight(this.shadowRoot.querySelector("footer"),"footer");let n=this.shadowRoot.querySelector(".mini-compare-chart-badge");n&&n.textContent!==""&&R(this,de).style.setProperty("--consonant-merch-card-mini-compare-top-section-mobile-height","32px")}adjustMiniCompareFooterRows(){if(this.variant!==Or||this.getBoundingClientRect().width===0)return;[...this.querySelector('[slot="footer-rows"]').children].forEach((n,o)=>{let i=Math.max(Bc,parseInt(window.getComputedStyle(n).height)||0),s=parseInt(R(this,de).style.getPropertyValue(Ls(o+1)))||0;i>s&&R(this,de).style.setProperty(Ls(o+1),`${i}px`)})}removeEmptyRows(){if(this.variant!==Or)return;this.querySelectorAll(".footer-row-cell").forEach(n=>{let o=n.querySelector(".footer-row-cell-description");o&&!o.textContent.trim()&&n.remove()})}get storageOptions(){return this.querySelector("sp-radio-group#storage")}get storageSpecificOfferSelect(){let r=this.storageOptions?.selected;if(r){let n=this.querySelector(`merch-offer-select[storage="${r}"]`);if(n)return n}return this.querySelector("merch-offer-select")}get offerSelect(){return this.storageOptions?this.storageSpecificOfferSelect:this.querySelector("merch-offer-select")}get quantitySelect(){return this.querySelector("merch-quantity-select")}merchCardReady(){this.offerSelect&&!this.offerSelect.planType||this.dispatchEvent(new CustomEvent($s,{bubbles:!0}))}handleStorageChange(){let r=this.closest("merch-card")?.offerSelect.cloneNode(!0);r&&this.dispatchEvent(new CustomEvent(go,{detail:{offerSelect:r},bubbles:!0}))}get dynamicPrice(){return this.querySelector('[slot="price"]')}selectMerchOffer(r){if(r===this.merchOffer)return;this.merchOffer=r;let n=this.dynamicPrice;if(r.price&&n){let o=r.price.cloneNode(!0);n.onceSettled?n.onceSettled().then(()=>{n.replaceWith(o)}):n.replaceWith(o)}}};de=new WeakMap,O(It,"properties",{name:{type:String,attribute:"name",reflect:!0},variant:{type:String,reflect:!0},size:{type:String,attribute:"size",reflect:!0},badgeColor:{type:String,attribute:"badge-color"},borderColor:{type:String,attribute:"border-color"},badgeBackgroundColor:{type:String,attribute:"badge-background-color"},badgeText:{type:String,attribute:"badge-text"},actionMenu:{type:Boolean,attribute:"action-menu"},actionMenuContent:{type:String,attribute:"action-menu-content"},customHr:{type:Boolean,attribute:"custom-hr"},detailBg:{type:String,attribute:"detail-bg"},secureLabel:{type:String,attribute:"secure-label"},checkboxLabel:{type:String,attribute:"checkbox-label"},selected:{type:Boolean,attribute:"aria-selected",reflect:!0},storageOption:{type:String,attribute:"storage",reflect:!0},stockOfferOsis:{type:Object,attribute:"stock-offer-osis",converter:{fromAttribute:r=>{let[n,o,i]=r.split(",");return{PUF:n,ABM:o,M2M:i}}}},filters:{type:String,reflect:!0,converter:{fromAttribute:r=>Object.fromEntries(r.split(",").map(n=>{let[o,i,s]=n.split(":"),c=Number(i);return[o,{order:isNaN(c)?void 0:c,size:s}]})),toAttribute:r=>Object.entries(r).map(([n,{order:o,size:i}])=>[n,o,i].filter(s=>s!=null).join(":")).join(",")}},types:{type:String,attribute:"types",reflect:!0},merchOffer:{type:Object}}),O(It,"styles",[_s,...ws()]);customElements.define(jc,It);var at=class extends oe{constructor(){super(),this.size="m",this.alt=""}render(){let{href:t}=this;return t?x`
+ ${this.alt} + `:x` ${this.alt}`}};O(at,"properties",{size:{type:String,attribute:!0},src:{type:String,attribute:!0},alt:{type:String,attribute:!0},href:{type:String,attribute:!0}}),O(at,"styles",ke` + :host { + --img-width: 32px; + --img-height: 32px; + display: block; + width: var(--img-width); + height: var(--img-height); + } + + :host([size='s']) { + --img-width: 24px; + --img-height: 24px; + } + + :host([size='l']) { + --img-width: 40px; + --img-height: 40px; + } + + img { + width: var(--img-width); + height: var(--img-height); + } + `);customElements.define("merch-icon",at);var He="Network error",kt,Lr=class{constructor(t){Q(this,kt);O(this,"sites",{cf:{fragments:{search:this.searchFragment.bind(this),getByPath:this.getFragmentByPath.bind(this),getById:this.getFragmentById.bind(this),save:this.saveFragment.bind(this),copy:this.copyFragmentClassic.bind(this),publish:this.publishFragment.bind(this),delete:this.deleteFragment.bind(this)}}});K(this,kt,/^author-/.test(t));let r=`https://${t}.adobeaemcloud.com`,n=`${r}/adobe/sites`;this.cfFragmentsUrl=`${n}/cf/fragments`,this.cfSearchUrl=`${this.cfFragmentsUrl}/search`,this.cfPublishUrl=`${this.cfFragmentsUrl}/publish`,this.wcmcommandUrl=`${r}/bin/wcmcommand`,this.csrfTokenUrl=`${r}/libs/granite/csrf/token.json`,this.headers={Authorization:`Bearer ${sessionStorage.getItem("masAccessToken")??window.adobeid?.authorize?.()}`,pragma:"no-cache","cache-control":"no-cache"}}async getCsrfToken(){let t=await fetch(this.csrfTokenUrl,{headers:this.headers}).catch(n=>{throw new Error(`${He}: ${n.message}`)});if(!t.ok)throw new Error(`Failed to get CSRF token: ${t.status} ${t.statusText}`);let{token:r}=await t.json();return r}async searchFragment({path:t,query:r,variant:n}){let o={};t&&(o.path=t),r&&(o.fullText={text:encodeURIComponent(r),queryMode:"EXACT_WORDS"});let i=new URLSearchParams({query:JSON.stringify({filter:o})}).toString(),s=await fetch(`${this.cfSearchUrl}?${i}`,{headers:this.headers}).catch(h=>{throw new Error(`${He}: ${h.message}`)});if(!s.ok)throw new Error(`Search failed: ${s.status} ${s.statusText}`);let a=(await s.json()).items;return n&&(a=a.filter(h=>{let[l]=h.fields.find(d=>d.name==="variant")?.values;return l===n})),a}async getFragmentByPath(t){let r=R(this,kt)?this.headers:{},n=await fetch(`${this.cfFragmentsUrl}?path=${t}`,{headers:r}).catch(i=>{throw new Error(`${He}: ${i.message}`)});if(!n.ok)throw new Error(`Failed to get fragment: ${n.status} ${n.statusText}`);let{items:o}=await n.json();if(!o||o.length===0)throw new Error("Fragment not found");return o[0]}async getFragment(t){let r=t.headers.get("Etag"),n=await t.json();return n.etag=r,n}async getFragmentById(t){let r=await fetch(`${this.cfFragmentsUrl}/${t}`,{headers:this.headers});if(!r.ok)throw new Error(`Failed to get fragment: ${r.status} ${r.statusText}`);return await this.getFragment(r)}async saveFragment(t){let{title:r,fields:n}=t,o=await fetch(`${this.cfFragmentsUrl}/${t.id}`,{method:"PUT",headers:{"Content-Type":"application/json","If-Match":t.etag,...this.headers},body:JSON.stringify({title:r,fields:n})}).catch(i=>{throw new Error(`${He}: ${i.message}`)});if(!o.ok)throw new Error(`Failed to save fragment: ${o.status} ${o.statusText}`);return await this.getFragment(o)}async copyFragmentClassic(t){let r=await this.getCsrfToken(),n=t.path.split("/").slice(0,-1).join("/"),o=new FormData;o.append("cmd","copyPage"),o.append("srcPath",t.path),o.append("destParentPath",n),o.append("shallow","false"),o.append("_charset_","UTF-8");let i=await fetch(this.wcmcommandUrl,{method:"POST",headers:{...this.headers,"csrf-token":r},body:o}).catch(u=>{throw new Error(`${He}: ${u.message}`)});if(!i.ok)throw new Error(`Failed to copy fragment: ${i.status} ${i.statusText}`);let s=await i.text(),l=new DOMParser().parseFromString(s,"text/html").getElementById("Message")?.textContent.trim();if(!l)throw new Error("Failed to extract new path from copy response");await Ts();let d=await this.getFragmentByPath(l);return d&&(d=await this.getFragmentById(d.id)),d}async publishFragment(t){let r=await fetch(this.cfPublishUrl,{method:"POST",headers:{"Content-Type":"application/json","If-Match":t.etag,...this.headers},body:JSON.stringify({paths:[t.path],filterReferencesByStatus:["DRAFT","UNPUBLISHED"],workflowModelId:"/var/workflow/models/scheduled_activation_with_references"})}).catch(n=>{throw new Error(`${He}: ${n.message}`)});if(!r.ok)throw new Error(`Failed to publish fragment: ${r.status} ${r.statusText}`);return await r.json()}async deleteFragment(t){let r=await fetch(`${this.cfFragmentsUrl}/${t.id}`,{method:"DELETE",headers:{"Content-Type":"application/json","If-Match":t.etag,...this.headers}}).catch(n=>{throw new Error(`${He}: ${n.message}`)});if(!r.ok)throw new Error(`Failed to delete fragment: ${r.status} ${r.statusText}`);return r}};kt=new WeakMap;var Wc="aem-bucket",Nr={CATALOG:"catalog",AH:"ah",CCD_ACTION:"ccd-action",SPECIAL_OFFERS:"special-offers"},qc={[Nr.CATALOG]:{title:{tag:"h3",slot:"heading-xs"},prices:{tag:"h3",slot:"heading-xs"},description:{tag:"div",slot:"body-xs"},ctas:{size:"l"}},[Nr.AH]:{title:{tag:"h3",slot:"heading-xxs"},prices:{tag:"h3",slot:"heading-xs"},description:{tag:"div",slot:"body-xxs"},ctas:{size:"s"}},[Nr.CCD_ACTION]:{title:{tag:"h3",slot:"heading-xs"},prices:{tag:"h3",slot:"heading-xs"},description:{tag:"div",slot:"body-xs"},ctas:{size:"l"}},[Nr.SPECIAL_OFFERS]:{name:{tag:"h4",slot:"detail-m"},title:{tag:"h4",slot:"detail-m"},backgroundImage:{tag:"div",slot:"bg-image"},prices:{tag:"h3",slot:"heading-xs"},description:{tag:"div",slot:"body-xs"},ctas:{size:"l"}}};async function Yc(e,t,r,n){let o=e.fields.reduce((c,{name:a,multiple:h,values:l})=>(c[a]=h?l:l[0],c),{id:e.id});o.model=o.model;let{variant:i="catalog"}=o;r.setAttribute("variant",i);let s=qc[i]??"catalog";if(o.icon?.forEach(c=>{let a=Ee("merch-icon",{slot:"icons",src:c,alt:"",href:"",size:"l"});t(a)}),o.title&&s.title&&t(Ee(s.title.tag,{slot:s.title.slot},o.title)),o.backgroundImage&&s.backgroundImage&&t(Ee(s.backgroundImage.tag,{slot:s.backgroundImage.slot},``)),o.prices&&s.prices){let c=o.prices,a=Ee(s.prices.tag,{slot:s.prices.slot},c);t(a)}if(o.description&&s.description){let c=Ee(s.description.tag,{slot:s.description.slot},o.description);t(c)}if(o.ctas){let c=Ee("div",{slot:"footer"},o.ctas),a=[];[...c.querySelectorAll("a")].forEach(h=>{let l=h.parentElement.tagName==="STRONG";if(n)h.classList.add("con-button"),l&&h.classList.add("blue"),a.push(h);else{let m=Ee("sp-button",{treatment:l?"fill":"outline",variant:l?"accent":"primary"},h);m.addEventListener("click",f=>{f.stopPropagation(),h.click()}),a.push(m)}}),c.innerHTML="",c.append(...a),t(c)}}var be,yo=class{constructor(){Q(this,be,new Map)}clear(){R(this,be).clear()}add(...t){t.forEach(r=>{let{path:n}=r;n&&R(this,be).set(n,r)})}has(t){return R(this,be).has(t)}get(t){return R(this,be).get(t)}remove(t){R(this,be).delete(t)}};be=new WeakMap;var vo=new yo,Mt,ze,Eo=class extends HTMLElement{constructor(){super(...arguments);Q(this,Mt);O(this,"cache",vo);O(this,"refs",[]);O(this,"path");O(this,"consonant",!1);Q(this,ze)}static get observedAttributes(){return["source","path","consonant"]}attributeChangedCallback(r,n,o){this[r]=o}connectedCallback(){this.consonant=this.hasAttribute("consonant"),this.clearRefs();let r=this.getAttribute(Wc)??"publish-p22655-e59341";K(this,Mt,new Lr(r)),this.refresh(!1)}clearRefs(){this.refs.forEach(r=>{r.remove()})}async refresh(r=!0){this.path&&(R(this,ze)&&!await Promise.race([R(this,ze),Promise.resolve(!1)])||(this.clearRefs(),this.refs=[],r&&this.cache.remove(this.path),K(this,ze,this.fetchData().then(()=>!0))))}async fetchData(){let r=vo.get(this.path);if(r||(r=await R(this,Mt).sites.cf.fragments.getByPath(this.path),vo.add(r)),r){Yc(r,o=>{this.parentElement.appendChild(o),this.refs.push(o)},this.parentElement,this.consonant);return}}get updateComplete(){return R(this,ze)??Promise.reject(new Error("datasource is not correctly configured"))}};Mt=new WeakMap,ze=new WeakMap;customElements.define("merch-datasource",Eo);var{searchParams:Ns}=new URL(import.meta.url),Xc=Ns.get("locale")??"US_en",Rs=Ns.get("env")==="stage",Zc=Rs?"stage":"prod",Jc=Rs?"STAGE":"PROD",Qc=fetch("https://www.adobe.com/federal/commerce/price-literals.json").then(e=>e.json().then(({data:t})=>t)),Kc=()=>({env:{name:Zc},commerce:{"commerce.env":Jc,priceLiteralsPromise:Qc},locale:{prefix:Xc}}),el=bt(Kc),Qu=el;export{Qu as default}; +/*! Bundled license information: + +@lit/reactive-element/css-tag.js: + (** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +@lit/reactive-element/reactive-element.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +lit-html/lit-html.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +@lit/reactive-element/css-tag.js: + (** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +@lit/reactive-element/reactive-element.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +lit-html/lit-html.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +lit-element/lit-element.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +lit-html/is-server.js: + (** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) +*/ diff --git a/libs/deps/mas/merch-card-collection.js b/libs/deps/mas/merch-card-collection.js index 07e8efd230..e0333d6fc3 100644 --- a/libs/deps/mas/merch-card-collection.js +++ b/libs/deps/mas/merch-card-collection.js @@ -1,4 +1,4 @@ -import{html as l,LitElement as A}from"/libs/deps/lit-all.min.js";var m=class{constructor(e,t){this.key=Symbol("match-media-key"),this.matches=!1,this.host=e,this.host.addController(this),this.media=window.matchMedia(t),this.matches=this.media.matches,this.onChange=this.onChange.bind(this),e.addController(this)}hostConnected(){var e;(e=this.media)==null||e.addEventListener("change",this.onChange)}hostDisconnected(){var e;(e=this.media)==null||e.removeEventListener("change",this.onChange)}onChange(e){this.matches!==e.matches&&(this.matches=e.matches,this.host.requestUpdate(this.key,!this.matches))}};var E="hashchange";function S(r=window.location.hash){let e=[],t=r.replace(/^#/,"").split("&");for(let o of t){let[n,c=""]=o.split("=");n&&e.push([n,decodeURIComponent(c.replace(/\+/g," "))])}return Object.fromEntries(e)}function u(r){let e=new URLSearchParams(window.location.hash.slice(1));Object.entries(r).forEach(([n,c])=>{c?e.set(n,c):e.delete(n)}),e.sort();let t=e.toString();if(t===window.location.hash)return;let o=window.scrollY||document.documentElement.scrollTop;window.location.hash=t,window.scrollTo(0,o)}function x(r){let e=()=>{if(window.location.hash&&!window.location.hash.includes("="))return;let t=S(window.location.hash);r(t)};return e(),window.addEventListener(E,e),()=>{window.removeEventListener(E,e)}}var T="merch-card-collection:sort",g="merch-card-collection:showmore";var C=(r,e={})=>{r.querySelectorAll("span[data-placeholder]").forEach(t=>{let{placeholder:o}=t.dataset;t.innerText=e[o]??""})};var _="(max-width: 1199px)",y="(min-width: 768px)",w="(min-width: 1200px)";import{css as R,unsafeCSS as b}from"/libs/deps/lit-all.min.js";var N=R` +import{html as l,LitElement as N}from"../lit-all.min.js";var m=class{constructor(e,t){this.key=Symbol("match-media-key"),this.matches=!1,this.host=e,this.host.addController(this),this.media=window.matchMedia(t),this.matches=this.media.matches,this.onChange=this.onChange.bind(this),e.addController(this)}hostConnected(){var e;(e=this.media)==null||e.addEventListener("change",this.onChange)}hostDisconnected(){var e;(e=this.media)==null||e.removeEventListener("change",this.onChange)}onChange(e){this.matches!==e.matches&&(this.matches=e.matches,this.host.requestUpdate(this.key,!this.matches))}};var f="hashchange";function R(r=window.location.hash){let e=[],t=r.replace(/^#/,"").split("&");for(let o of t){let[i,c=""]=o.split("=");i&&e.push([i,decodeURIComponent(c.replace(/\+/g," "))])}return Object.fromEntries(e)}function p(r){let e=new URLSearchParams(window.location.hash.slice(1));Object.entries(r).forEach(([i,c])=>{c?e.set(i,c):e.delete(i)}),e.sort();let t=e.toString();if(t===window.location.hash)return;let o=window.scrollY||document.documentElement.scrollTop;window.location.hash=t,window.scrollTo(0,o)}function T(r){let e=()=>{if(window.location.hash&&!window.location.hash.includes("="))return;let t=R(window.location.hash);r(t)};return e(),window.addEventListener(f,e),()=>{window.removeEventListener(f,e)}}var x="merch-card-collection:sort",g="merch-card-collection:showmore";var C=(r,e={})=>{r.querySelectorAll("span[data-placeholder]").forEach(t=>{let{placeholder:o}=t.dataset;t.innerText=e[o]??""})};var _="(max-width: 1199px)",b="(min-width: 768px)",w="(min-width: 1200px)";import{css as A,unsafeCSS as y}from"../lit-all.min.js";var S=A` #header, #resultText, #footer { @@ -65,7 +65,7 @@ import{html as l,LitElement as A}from"/libs/deps/lit-all.min.js";var m=class{con } /* tablets */ - @media screen and ${b(y)} { + @media screen and ${y(b)} { #header { grid-template-columns: 1fr fit-content(100%) fit-content(100%); } @@ -84,7 +84,7 @@ import{html as l,LitElement as A}from"/libs/deps/lit-all.min.js";var m=class{con } /* Laptop */ - @media screen and ${b(w)} { + @media screen and ${y(w)} { #resultText { grid-column: span 2; order: -3; @@ -96,9 +96,9 @@ import{html as l,LitElement as A}from"/libs/deps/lit-all.min.js";var m=class{con justify-content: end; } } -`;var p=(r,e)=>r.querySelector(`[slot="${e}"]`).textContent.trim();var M="merch-card-collection",i={alphabetical:"alphabetical",authored:"authored"},v={filters:["noResultText","resultText","resultsText"],mobile:["noSearchResultsMobileText","searchResultMobileText","searchResultsMobileText"],desktop:["noSearchResultsText","searchResultText","searchResultsText"]},O=(r,{filter:e})=>r.filter(t=>t.filters.hasOwnProperty(e)),L=(r,{types:e})=>e?(e=e.split(","),r.filter(t=>e.some(o=>t.types.includes(o)))):r,D=r=>r.sort((e,t)=>(e.title??"").localeCompare(t.title??"","en",{sensitivity:"base"})),k=(r,{filter:e})=>r.sort((t,o)=>o.filters[e]?.order==null||isNaN(o.filters[e]?.order)?-1:t.filters[e]?.order==null||isNaN(t.filters[e]?.order)?1:t.filters[e].order-o.filters[e].order),H=(r,{search:e})=>e?.length?(e=e.toLowerCase(),r.filter(t=>(t.title??"").toLowerCase().includes(e))):r,f=class extends A{static properties={filter:{type:String,attribute:"filter",reflect:!0},filtered:{type:String,attribute:"filtered"},search:{type:String,attribute:"search",reflect:!0},sort:{type:String,attribute:"sort",default:i.authored,reflect:!0},types:{type:String,attribute:"types",reflect:!0},limit:{type:Number,attribute:"limit"},page:{type:Number,attribute:"page",reflect:!0},singleApp:{type:String,attribute:"single-app",reflect:!0},hasMore:{type:Boolean},displayResult:{type:Boolean,attribute:"display-result"},resultCount:{type:Number},sidenav:{type:Object}};mobileAndTablet=new m(this,_);constructor(){super(),this.filter="all",this.hasMore=!1,this.resultCount=void 0,this.displayResult=!1}render(){return l`${this.header} +`;var u=(r,e)=>r.querySelector(`[slot="${e}"]`).textContent.trim();var M="merch-card-collection",n={alphabetical:"alphabetical",authored:"authored"},O={filters:["noResultText","resultText","resultsText"],mobile:["noSearchResultsMobileText","searchResultMobileText","searchResultsMobileText"],desktop:["noSearchResultsText","searchResultText","searchResultsText"]},v=(r,{filter:e})=>r.filter(t=>t.filters.hasOwnProperty(e)),L=(r,{types:e})=>e?(e=e.split(","),r.filter(t=>e.some(o=>t.types.includes(o)))):r,D=r=>r.sort((e,t)=>(e.title??"").localeCompare(t.title??"","en",{sensitivity:"base"})),H=(r,{filter:e})=>r.sort((t,o)=>o.filters[e]?.order==null||isNaN(o.filters[e]?.order)?-1:t.filters[e]?.order==null||isNaN(t.filters[e]?.order)?1:t.filters[e].order-o.filters[e].order),B=(r,{search:e})=>e?.length?(e=e.toLowerCase(),r.filter(t=>(t.title??"").toLowerCase().includes(e))):r,E=class extends N{static properties={filter:{type:String,attribute:"filter",reflect:!0},filtered:{type:String,attribute:"filtered"},search:{type:String,attribute:"search",reflect:!0},sort:{type:String,attribute:"sort",default:n.authored,reflect:!0},types:{type:String,attribute:"types",reflect:!0},limit:{type:Number,attribute:"limit"},page:{type:Number,attribute:"page",reflect:!0},singleApp:{type:String,attribute:"single-app",reflect:!0},hasMore:{type:Boolean},displayResult:{type:Boolean,attribute:"display-result"},resultCount:{type:Number},sidenav:{type:Object}};mobileAndTablet=new m(this,_);constructor(){super(),this.filter="all",this.hasMore=!1,this.resultCount=void 0,this.displayResult=!1}render(){return l`${this.header} - ${this.footer}`}updated(e){if(!this.querySelector("merch-card"))return;let t=window.scrollY||document.documentElement.scrollTop,o=[...this.children].filter(s=>s.tagName==="MERCH-CARD");if(o.length===0)return;e.has("singleApp")&&this.singleApp&&o.forEach(s=>{s.updateFilters(s.name===this.singleApp)});let n=this.sort===i.alphabetical?D:k,h=[O,L,H,n].reduce((s,a)=>a(s,this),o).map((s,a)=>[s,a]);if(this.resultCount=h.length,this.page&&this.limit){let s=this.page*this.limit;this.hasMore=h.length>s,h=h.filter(([,a])=>a{if(d.has(s)){let a=d.get(s);s.style.order=a,s.setAttribute("tabindex",a+1),s.size=s.filters[this.filter]?.size,s.style.removeProperty("display"),s.requestUpdate()}else s.style.display="none",s.size=void 0,s.style.removeProperty("order")}),window.scrollTo(0,t),this.updateComplete.then(()=>{let s=this.shadowRoot.getElementById("resultText")?.firstElementChild?.assignedElements?.()?.[0];s&&C(s,{resultCount:this.resultCount,searchTerm:this.search,filter:this.sidenav?.filters.selectedText})})}connectedCallback(){super.connectedCallback(),this.filtered?(this.filter=this.filtered,this.page=1):this.startDeeplink(),this.sidenav=document.querySelector("merch-sidenav")}disconnectedCallback(){super.disconnectedCallback(),this.stopDeeplink?.()}get header(){if(!this.filtered)return l` `}};customElements.define("merch-quantity-select",s);export{s as MerchQuantitySelect}; -//# sourceMappingURL=merch-quantity-select.js.map diff --git a/libs/deps/mas/merch-secure-transaction.js b/libs/deps/mas/merch-secure-transaction.js index af0e609c50..4542cb127d 100644 --- a/libs/deps/mas/merch-secure-transaction.js +++ b/libs/deps/mas/merch-secure-transaction.js @@ -1,4 +1,4 @@ -import{LitElement as p,html as n}from"/libs/deps/lit-all.min.js";import{css as l}from"/libs/deps/lit-all.min.js";var i=l` +import{LitElement as p,html as n}from"../lit-all.min.js";import{css as l}from"../lit-all.min.js";var i=l` #label { align-items: center; cursor: pointer; @@ -30,4 +30,3 @@ import{LitElement as p,html as n}from"/libs/deps/lit-all.min.js";import{css as l > `:o}};window.customElements.define(a,t);export{t as default}; -//# sourceMappingURL=merch-secure-transaction.js.map diff --git a/libs/deps/mas/merch-sidenav.js b/libs/deps/mas/merch-sidenav.js index 8376332a79..09057e8ecc 100644 --- a/libs/deps/mas/merch-sidenav.js +++ b/libs/deps/mas/merch-sidenav.js @@ -1,4 +1,4 @@ -import{html as k,css as H,LitElement as P}from"/libs/deps/lit-all.min.js";var a=class{constructor(e,t){this.key=Symbol("match-media-key"),this.matches=!1,this.host=e,this.host.addController(this),this.media=window.matchMedia(t),this.matches=this.media.matches,this.onChange=this.onChange.bind(this),e.addController(this)}hostConnected(){var e;(e=this.media)==null||e.addEventListener("change",this.onChange)}hostDisconnected(){var e;(e=this.media)==null||e.removeEventListener("change",this.onChange)}onChange(e){this.matches!==e.matches&&(this.matches=e.matches,this.host.requestUpdate(this.key,!this.matches))}};import{css as N}from"/libs/deps/lit-all.min.js";var c=N` +import{html as k,css as H,LitElement as P}from"/libs/deps/lit-all.min.js";var r=class{constructor(e,t){this.key=Symbol("match-media-key"),this.matches=!1,this.host=e,this.host.addController(this),this.media=window.matchMedia(t),this.matches=this.media.matches,this.onChange=this.onChange.bind(this),e.addController(this)}hostConnected(){var e;(e=this.media)==null||e.addEventListener("change",this.onChange)}hostDisconnected(){var e;(e=this.media)==null||e.removeEventListener("change",this.onChange)}onChange(e){this.matches!==e.matches&&(this.matches=e.matches,this.host.requestUpdate(this.key,!this.matches))}};import{css as L}from"/libs/deps/lit-all.min.js";var c=L` h2 { font-size: 11px; font-style: normal; @@ -9,7 +9,7 @@ import{html as k,css as H,LitElement as P}from"/libs/deps/lit-all.min.js";var a= line-height: 32px; color: #747474; } -`;import{html as L,LitElement as D}from"/libs/deps/lit-all.min.js";var x="merch-search:change";var v="merch-sidenav:select";function d(s,e){let t;return function(){let o=this,n=arguments;clearTimeout(t),t=setTimeout(()=>s.apply(o,n),e)}}var g="hashchange";function i(s=window.location.hash){let e=[],t=s.replace(/^#/,"").split("&");for(let o of t){let[n,l=""]=o.split("=");n&&e.push([n,decodeURIComponent(l.replace(/\+/g," "))])}return Object.fromEntries(e)}function r(s,e){if(s.deeplink){let t={};t[s.deeplink]=e,A(t)}}function A(s){let e=new URLSearchParams(window.location.hash.slice(1));Object.entries(s).forEach(([n,l])=>{l?e.set(n,l):e.delete(n)}),e.sort();let t=e.toString();if(t===window.location.hash)return;let o=window.scrollY||document.documentElement.scrollTop;window.location.hash=t,window.scrollTo(0,o)}function b(s){let e=()=>{if(window.location.hash&&!window.location.hash.includes("="))return;let t=i(window.location.hash);s(t)};return e(),window.addEventListener(g,e),()=>{window.removeEventListener(g,e)}}var p=class extends D{static properties={deeplink:{type:String}};get search(){return this.querySelector("sp-search")}constructor(){super(),this.handleInput=()=>{r(this,this.search.value),this.search.value&&this.dispatchEvent(new CustomEvent(x,{bubbles:!0,composed:!0,detail:{type:"search",value:this.search.value}}))},this.handleInputDebounced=d(this.handleInput.bind(this))}connectedCallback(){super.connectedCallback(),this.search&&(this.search.addEventListener("input",this.handleInputDebounced),this.search.addEventListener("submit",this.handleInputSubmit),this.updateComplete.then(()=>{this.setStateFromURL()}),this.startDeeplink())}disconnectedCallback(){super.disconnectedCallback(),this.search.removeEventListener("input",this.handleInputDebounced),this.search.removeEventListener("submit",this.handleInputSubmit),this.stopDeeplink?.()}setStateFromURL(){let t=i()[this.deeplink];t&&(this.search.value=t)}startDeeplink(){this.stopDeeplink=b(({search:e})=>{this.search.value=e??""})}handleInputSubmit(e){e.preventDefault()}render(){return L``}};customElements.define("merch-search",p);import{html as C,LitElement as R,css as M}from"/libs/deps/lit-all.min.js";var m=class extends R{static properties={sidenavListTitle:{type:String},label:{type:String},deeplink:{type:String,attribute:"deeplink"},selectedText:{type:String,reflect:!0,attribute:"selected-text"},selectedValue:{type:String,reflect:!0,attribute:"selected-value"}};static styles=[M` +`;import{html as N,LitElement as R}from"/libs/deps/lit-all.min.js";function d(s,e){let t;return function(){let o=this,i=arguments;clearTimeout(t),t=setTimeout(()=>s.apply(o,i),e)}}var x="merch-search:change";var v="merch-sidenav:select";var g="hashchange";function n(s=window.location.hash){let e=[],t=s.replace(/^#/,"").split("&");for(let o of t){let[i,l=""]=o.split("=");i&&e.push([i,decodeURIComponent(l.replace(/\+/g," "))])}return Object.fromEntries(e)}function a(s,e){if(s.deeplink){let t={};t[s.deeplink]=e,A(t)}}function A(s){let e=new URLSearchParams(window.location.hash.slice(1));Object.entries(s).forEach(([i,l])=>{l?e.set(i,l):e.delete(i)}),e.sort();let t=e.toString();if(t===window.location.hash)return;let o=window.scrollY||document.documentElement.scrollTop;window.location.hash=t,window.scrollTo(0,o)}function b(s){let e=()=>{if(window.location.hash&&!window.location.hash.includes("="))return;let t=n(window.location.hash);s(t)};return e(),window.addEventListener(g,e),()=>{window.removeEventListener(g,e)}}var p=class extends R{static properties={deeplink:{type:String}};get search(){return this.querySelector("sp-search")}constructor(){super(),this.handleInput=()=>{a(this,this.search.value),this.search.value&&this.dispatchEvent(new CustomEvent(x,{bubbles:!0,composed:!0,detail:{type:"search",value:this.search.value}}))},this.handleInputDebounced=d(this.handleInput.bind(this))}connectedCallback(){super.connectedCallback(),this.search&&(this.search.addEventListener("input",this.handleInputDebounced),this.search.addEventListener("submit",this.handleInputSubmit),this.updateComplete.then(()=>{this.setStateFromURL()}),this.startDeeplink())}disconnectedCallback(){super.disconnectedCallback(),this.search.removeEventListener("input",this.handleInputDebounced),this.search.removeEventListener("submit",this.handleInputSubmit),this.stopDeeplink?.()}setStateFromURL(){let t=n()[this.deeplink];t&&(this.search.value=t)}startDeeplink(){this.stopDeeplink=b(({search:e})=>{this.search.value=e??""})}handleInputSubmit(e){e.preventDefault()}render(){return N``}};customElements.define("merch-search",p);import{html as C,LitElement as D,css as M}from"/libs/deps/lit-all.min.js";var m=class extends D{static properties={sidenavListTitle:{type:String},label:{type:String},deeplink:{type:String,attribute:"deeplink"},selectedText:{type:String,reflect:!0,attribute:"selected-text"},selectedValue:{type:String,reflect:!0,attribute:"selected-value"}};static styles=[M` :host { display: block; contain: content; @@ -30,7 +30,7 @@ import{html as k,css as H,LitElement as P}from"/libs/deps/lit-all.min.js";var a= ) ); } - `,c];constructor(){super(),this.handleClickDebounced=d(this.handleClick.bind(this))}selectElement(e,t=!0){e.parentNode.tagName==="SP-SIDENAV-ITEM"&&this.selectElement(e.parentNode,!1),e.firstElementChild?.tagName==="SP-SIDENAV-ITEM"&&(e.expanded=!0),t&&(this.selectedElement=e,this.selectedText=e.label,this.selectedValue=e.value,setTimeout(()=>{e.selected=!0},1),this.dispatchEvent(new CustomEvent(v,{bubbles:!0,composed:!0,detail:{type:"sidenav",value:this.selectedValue,elt:this.selectedElement}})))}setStateFromURL(){let t=i()[this.deeplink]??"all";if(t){let o=this.querySelector(`sp-sidenav-item[value="${t}"]`);if(!o)return;this.updateComplete.then(()=>{this.selectElement(o)})}}handleClick({target:e}){let{value:t,parentNode:o}=e;this.selectElement(e),o&&o.tagName==="SP-SIDENAV"&&(r(this,t),e.selected=!0,o.querySelectorAll("sp-sidenav-item[expanded],sp-sidenav-item[selected]").forEach(n=>{n.value!==t&&(n.expanded=!1,n.selected=!1)}))}selectionChanged({target:{value:e,parentNode:t}}){this.selectElement(this.querySelector(`sp-sidenav-item[value="${e}"]`)),r(this,e)}connectedCallback(){super.connectedCallback(),this.addEventListener("click",this.handleClickDebounced),this.updateComplete.then(()=>{this.setStateFromURL()})}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener("click",this.handleClickDebounced)}render(){return C`
{e.selected=!0},1),this.dispatchEvent(new CustomEvent(v,{bubbles:!0,composed:!0,detail:{type:"sidenav",value:this.selectedValue,elt:this.selectedElement}})))}setStateFromURL(){let t=n()[this.deeplink]??"all";if(t){let o=this.querySelector(`sp-sidenav-item[value="${t}"]`);if(!o)return;this.updateComplete.then(()=>{this.selectElement(o)})}}handleClick({target:e}){let{value:t,parentNode:o}=e;this.selectElement(e),o&&o.tagName==="SP-SIDENAV"&&(a(this,t),e.selected=!0,o.querySelectorAll("sp-sidenav-item[expanded],sp-sidenav-item[selected]").forEach(i=>{i.value!==t&&(i.expanded=!1,i.selected=!1)}))}selectionChanged({target:{value:e,parentNode:t}}){this.selectElement(this.querySelector(`sp-sidenav-item[value="${e}"]`)),a(this,e)}connectedCallback(){super.connectedCallback(),this.addEventListener("click",this.handleClickDebounced),this.updateComplete.then(()=>{this.setStateFromURL()})}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener("click",this.handleClickDebounced)}render(){return C`
@@ -58,7 +58,7 @@ import{html as k,css as H,LitElement as P}from"/libs/deps/lit-all.min.js";var a= display: flex; flex-direction: column; } - `;setStateFromURL(){this.selectedValues=[];let{types:e}=i();e&&(this.selectedValues=e.split(","),this.selectedValues.forEach(t=>{let o=this.querySelector(`sp-checkbox[name=${t}]`);o&&(o.checked=!0)}))}selectionChanged(e){let{target:t}=e,o=t.getAttribute("name");if(o){let n=this.selectedValues.indexOf(o);t.checked&&n===-1?this.selectedValues.push(o):!t.checked&&n>=0&&this.selectedValues.splice(n,1)}r(this,this.selectedValues.join(","))}connectedCallback(){super.connectedCallback(),this.updateComplete.then(async()=>{this.setStateFromURL()})}render(){return V`
+ `;setStateFromURL(){this.selectedValues=[];let{types:e}=n();e&&(this.selectedValues=e.split(","),this.selectedValues.forEach(t=>{let o=this.querySelector(`sp-checkbox[name=${t}]`);o&&(o.checked=!0)}))}selectionChanged(e){let{target:t}=e,o=t.getAttribute("name");if(o){let i=this.selectedValues.indexOf(o);t.checked&&i===-1?this.selectedValues.push(o):!t.checked&&i>=0&&this.selectedValues.splice(i,1)}a(this,this.selectedValues.join(","))}connectedCallback(){super.connectedCallback(),this.updateComplete.then(async()=>{this.setStateFromURL()})}render(){return V`

${this.sidenavCheckboxTitle}

-
`}};customElements.define("merch-sidenav-checkbox-group",u);var y="(max-width: 700px)";var S="(max-width: 1199px)";var T=/iP(ad|hone|od)/.test(window?.navigator?.platform)||window?.navigator?.platform==="MacIntel"&&window.navigator.maxTouchPoints>1,E=!1,h,w=s=>{s&&(T?(document.body.style.position="fixed",s.ontouchmove=e=>{e.targetTouches.length===1&&e.stopPropagation()},E||(document.addEventListener("touchmove",e=>e.preventDefault()),E=!0)):(h=document.body.style.overflow,document.body.style.overflow="hidden"))},_=s=>{s&&(T?(s.ontouchstart=null,s.ontouchmove=null,document.body.style.position="",document.removeEventListener("touchmove",e=>e.preventDefault()),E=!1):h!==void 0&&(document.body.style.overflow=h,h=void 0))};document.addEventListener("sp-opened",()=>{document.body.classList.add("merch-modal")});document.addEventListener("sp-closed",()=>{document.body.classList.remove("merch-modal")});var f=class extends P{static properties={sidenavTitle:{type:String},closeText:{type:String,attribute:"close-text"},modal:{type:Boolean,attribute:"modal",reflect:!0}};#e;constructor(){super(),this.modal=!1}static styles=[H` +
`}};customElements.define("merch-sidenav-checkbox-group",u);var y="(max-width: 700px)";var S="(max-width: 1199px)";var T=/iP(ad|hone|od)/.test(window?.navigator?.platform)||window?.navigator?.platform==="MacIntel"&&window.navigator.maxTouchPoints>1,E=!1,h,w=s=>{s&&(T?(document.body.style.position="fixed",s.ontouchmove=e=>{e.targetTouches.length===1&&e.stopPropagation()},E||(document.addEventListener("touchmove",e=>e.preventDefault()),E=!0)):(h=document.body.style.overflow,document.body.style.overflow="hidden"))},_=s=>{s&&(T?(s.ontouchstart=null,s.ontouchmove=null,document.body.style.position="",document.removeEventListener("touchmove",e=>e.preventDefault()),E=!1):h!==void 0&&(document.body.style.overflow=h,h=void 0))};var f=class extends P{static properties={sidenavTitle:{type:String},closeText:{type:String,attribute:"close-text"},modal:{type:Boolean,attribute:"modal",reflect:!0}};#e;constructor(){super(),this.modal=!1}static styles=[H` :host { display: block; } @@ -114,7 +114,7 @@ import{html as k,css as H,LitElement as P}from"/libs/deps/lit-all.min.js";var a= top: 16px; right: 16px; } - `,c];mobileDevice=new a(this,y);mobileAndTablet=new a(this,S);get filters(){return this.querySelector("merch-sidenav-list")}get search(){return this.querySelector("merch-search")}render(){return this.mobileAndTablet.matches?this.asDialog:this.asAside}get asDialog(){if(this.modal)return k` + `,c];mobileDevice=new r(this,y);mobileAndTablet=new r(this,S);get filters(){return this.querySelector("merch-sidenav-list")}get search(){return this.querySelector("merch-search")}render(){return this.mobileAndTablet.matches?this.asDialog:this.asAside}get asDialog(){if(this.modal)return k`

${this.sidenavTitle}

`}get dialog(){return this.shadowRoot.querySelector("sp-dialog-base")}closeModal(e){e.preventDefault(),this.dialog?.close()}openModal(){this.updateComplete.then(async()=>{w(this.dialog);let e={trigger:this.#e,notImmediatelyClosable:!0,type:"auto"},t=await window.__merch__spectrum_Overlay.open(this.dialog,e);t.addEventListener("close",()=>{this.modal=!1,_(this.dialog)}),this.shadowRoot.querySelector("sp-theme").append(t)})}updated(){this.modal&&this.openModal()}showModal({target:e}){this.#e=e,this.modal=!0}};customElements.define("merch-sidenav",f);export{f as MerchSideNav}; + >`}get dialog(){return this.shadowRoot.querySelector("sp-dialog-base")}closeModal(e){e.preventDefault(),this.dialog?.close(),document.body.classList.remove("merch-modal")}openModal(){this.updateComplete.then(async()=>{w(this.dialog),document.body.classList.add("merch-modal");let e={trigger:this.#e,notImmediatelyClosable:!0,type:"auto"},t=await window.__merch__spectrum_Overlay.open(this.dialog,e);t.addEventListener("close",()=>{this.modal=!1,_(this.dialog)}),this.shadowRoot.querySelector("sp-theme").append(t)})}updated(){this.modal&&this.openModal()}showModal({target:e}){this.#e=e,this.modal=!0}};customElements.define("merch-sidenav",f);export{f as MerchSideNav}; diff --git a/libs/deps/mas/merch-stock.js b/libs/deps/mas/merch-stock.js index bd5fa0c22e..2f15c0b489 100644 --- a/libs/deps/mas/merch-stock.js +++ b/libs/deps/mas/merch-stock.js @@ -1,4 +1,4 @@ -import{LitElement as h,css as a,html as i}from"/libs/deps/lit-all.min.js";var s="merch-stock:change";var t=class{constructor(e,r){this.key=Symbol("match-media-key"),this.matches=!1,this.host=e,this.host.addController(this),this.media=window.matchMedia(r),this.matches=this.media.matches,this.onChange=this.onChange.bind(this),e.addController(this)}hostConnected(){var e;(e=this.media)==null||e.addEventListener("change",this.onChange)}hostDisconnected(){var e;(e=this.media)==null||e.removeEventListener("change",this.onChange)}onChange(e){this.matches!==e.matches&&(this.matches=e.matches,this.host.requestUpdate(this.key,!this.matches))}};var c="(max-width: 767px)";var o=class extends h{static styles=[a` +import{LitElement as h,css as a,html as i}from"../lit-all.min.js";var s="merch-stock:change";var t=class{constructor(e,r){this.key=Symbol("match-media-key"),this.matches=!1,this.host=e,this.host.addController(this),this.media=window.matchMedia(r),this.matches=this.media.matches,this.onChange=this.onChange.bind(this),e.addController(this)}hostConnected(){var e;(e=this.media)==null||e.addEventListener("change",this.onChange)}hostDisconnected(){var e;(e=this.media)==null||e.removeEventListener("change",this.onChange)}onChange(e){this.matches!==e.matches&&(this.matches=e.matches,this.host.requestUpdate(this.key,!this.matches))}};var c="(max-width: 767px)";var o=class extends h{static styles=[a` ::slotted(div) { display: none; } @@ -27,4 +27,3 @@ import{LitElement as h,css as a,html as i}from"/libs/deps/lit-all.min.js";var s= `}get osi(){if(this.checked)return this.querySelector(`div[data-plan-type="${this.planType}"] [is="inline-price"]`)?.value?.[0].offerSelectorIds[0]}};window.customElements.define("merch-stock",o);export{o as MerchStock}; -//# sourceMappingURL=merch-stock.js.map diff --git a/libs/deps/mas/merch-subscription-panel.js b/libs/deps/mas/merch-subscription-panel.js index d7b4d829ee..0a6963701e 100644 --- a/libs/deps/mas/merch-subscription-panel.js +++ b/libs/deps/mas/merch-subscription-panel.js @@ -1,4 +1,4 @@ -import{html as s,LitElement as m}from"/libs/deps/lit-all.min.js";var i=class{constructor(t,o){this.key=Symbol("match-media-key"),this.matches=!1,this.host=t,this.host.addController(this),this.media=window.matchMedia(o),this.matches=this.media.matches,this.onChange=this.onChange.bind(this),t.addController(this)}hostConnected(){var t;(t=this.media)==null||t.addEventListener("change",this.onChange)}hostDisconnected(){var t;(t=this.media)==null||t.removeEventListener("change",this.onChange)}onChange(t){this.matches!==t.matches&&(this.matches=t.matches,this.host.requestUpdate(this.key,!this.matches))}};import{css as E}from"/libs/deps/lit-all.min.js";var f=E` +import{html as s,LitElement as m}from"../lit-all.min.js";var i=class{constructor(t,o){this.key=Symbol("match-media-key"),this.matches=!1,this.host=t,this.host.addController(this),this.media=window.matchMedia(o),this.matches=this.media.matches,this.onChange=this.onChange.bind(this),t.addController(this)}hostConnected(){var t;(t=this.media)==null||t.addEventListener("change",this.onChange)}hostDisconnected(){var t;(t=this.media)==null||t.removeEventListener("change",this.onChange)}onChange(t){this.matches!==t.matches&&(this.matches=t.matches,this.host.requestUpdate(this.key,!this.matches))}};import{css as E}from"../lit-all.min.js";var f=E` :host { --merch-focused-outline: var(--merch-color-focus-ring) auto 1px; background-color: #f5f5f5; @@ -139,4 +139,3 @@ import{html as s,LitElement as m}from"/libs/deps/lit-all.min.js";var i=class{con data-quantity="${this.quantity}" href="#" >`}};window.customElements.define("merch-subscription-panel",l); -//# sourceMappingURL=merch-subscription-panel.js.map diff --git a/libs/deps/mas/merch-twp-d2p.js b/libs/deps/mas/merch-twp-d2p.js index bd374ed644..8509d98e20 100644 --- a/libs/deps/mas/merch-twp-d2p.js +++ b/libs/deps/mas/merch-twp-d2p.js @@ -1,4 +1,4 @@ -import{LitElement as T,html as s}from"/libs/deps/lit-all.min.js";var r=class{constructor(e,t){this.key=Symbol("match-media-key"),this.matches=!1,this.host=e,this.host.addController(this),this.media=window.matchMedia(t),this.matches=this.media.matches,this.onChange=this.onChange.bind(this),e.addController(this)}hostConnected(){var e;(e=this.media)==null||e.addEventListener("change",this.onChange)}hostDisconnected(){var e;(e=this.media)==null||e.removeEventListener("change",this.onChange)}onChange(e){this.matches!==e.matches&&(this.matches=e.matches,this.host.requestUpdate(this.key,!this.matches))}};import{css as C}from"/libs/deps/lit-all.min.js";var b=C` +import{LitElement as T,html as s}from"../lit-all.min.js";var r=class{constructor(e,t){this.key=Symbol("match-media-key"),this.matches=!1,this.host=e,this.host.addController(this),this.media=window.matchMedia(t),this.matches=this.media.matches,this.onChange=this.onChange.bind(this),e.addController(this)}hostConnected(){var e;(e=this.media)==null||e.addEventListener("change",this.onChange)}hostDisconnected(){var e;(e=this.media)==null||e.removeEventListener("change",this.onChange)}onChange(e){this.matches!==e.matches&&(this.matches=e.matches,this.host.requestUpdate(this.key,!this.matches))}};import{css as C}from"../lit-all.min.js";var b=C` :host { display: flex; box-sizing: border-box; @@ -324,4 +324,3 @@ import{LitElement as T,html as s}from"/libs/deps/lit-all.min.js";var r=class{con
`:s``}connectedCallback(){super.connectedCallback(),this.style.setProperty("--mod-tabs-font-weight",700),this.addEventListener(h,this.merchTwpReady),this.subscriptionPanel.addEventListener(p,this.#t),this.addEventListener(f,this.handleQuantityChange),this.whatsIncludedLink?.addEventListener("click",this.handleWhatsIncludedClick),this.addEventListener(u,this.handleStorageChange)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener(h,this.merchTwpReady),this.subscriptionPanel.removeEventListener(p,this.#t),this.whatsIncludedLink?.removeEventListener("click",this.handleWhatsIncludedClick),this.removeEventListener(u,this.handleStorageChange)}handleOfferSelected(e){this.log.debug("Selecting plan type",e.target.planType),this.selectedCard.selectMerchOffer(e.target.selectedOffer)}handleQuantityChange(e){this.selectedTabPanel&&(this.selectedCard.quantitySelect.defaultValue=e.detail.option,this.requestUpdate())}get whatsIncludedLink(){return this.querySelector("merch-card .merch-whats-included")}get whatsIncluded(){return this.querySelector('[slot="merch-whats-included"]')}setOfferSelectOnPanel(e){e.setAttribute("variant","subscription-options"),this.subscriptionPanel.offerSelect?.remove(),this.subscriptionPanel.appendChild(e)}handleStorageChange(e){let t=e.detail.offerSelect;t&&this.setOfferSelectOnPanel(t)}get preselectedCardId(){let e=x()["select-cards"]?.split(",").reduce((t,n)=>{let i=decodeURIComponent(n.trim().toLowerCase());return i&&t.push(i),t},[])||[];if(e.length&&this.selectedTab===a)return e[0];if(e.length>1&&this.selectedTab===l)return e[1];if(e.length>2&&this.selectedTab===c)return e[2]}get cardToBePreselected(){return this.selectedTabPanel?.querySelector("slot").assignedElements().find(e=>{let t=e.querySelector(".heading-xs")?.textContent.trim().toLowerCase()||"";return this.preselectedCardId&&t.includes(this.preselectedCardId)})}selectCard(e,t=!1){let n=this.selectedTabPanel,i=n?.card;(t||!i)&&(i&&(i.selected=void 0),i=this.cardToBePreselected||e,i.selected=!0,n?n.card=i:this.selectSingleCard(i)),i.focus(),this.subscriptionPanel.quantitySelect?.remove();let o=i.quantitySelect?.cloneNode(!0);o&&this.subscriptionPanel.appendChild(o);let E=i.offerSelect.cloneNode(!0);this.setOfferSelectOnPanel(E)}handleWhatsIncludedClick(e){e.preventDefault(),this.whatsIncluded?.classList.toggle("hidden")}async processCards(){let e=[...this.querySelectorAll("merch-card")];e.forEach((t,n)=>{let{customerSegment:i,marketSegment:o}=t.offerSelect;i==="INDIVIDUAL"?o==="COM"?t.setAttribute("slot","individuals"):o==="EDU"&&t.setAttribute("slot","education"):i==="TEAM"&&t.setAttribute("slot","business"),t.addEventListener("click",()=>this.selectCard(t,!0))}),this.ready=!0,this.requestUpdate(),await this.updateComplete,await this.tabElement?.updateComplete,this.selectCard(e.length===1?e[0]:this.firstCardInSelectedTab,!0)}merchTwpReady(){this.querySelector("merch-card merch-offer-select:not([plan-type])")||this.processCards()}get cards(){return this.querySelectorAll("merch-card[slot]")}get cciCards(){return this.querySelectorAll('merch-card[slot="individuals"]')}get cctCards(){return this.querySelectorAll('merch-card[slot="business"]')}get cceCards(){return this.querySelectorAll('merch-card[slot="education"]')}get subscriptionPanel(){return this.querySelector("merch-subscription-panel")}get tabElement(){return this.shadowRoot.querySelector("sp-tabs")}};window.customElements.define(S,m);export{m as MerchTwpD2P}; -//# sourceMappingURL=merch-twp-d2p.js.map diff --git a/libs/deps/mas/merch-whats-included.js b/libs/deps/mas/merch-whats-included.js index bee34475b6..6472694664 100644 --- a/libs/deps/mas/merch-whats-included.js +++ b/libs/deps/mas/merch-whats-included.js @@ -1,4 +1,4 @@ -import{html as e,css as o,LitElement as l}from"/libs/deps/lit-all.min.js";var t=class extends l{static styles=o` +import{html as e,css as o,LitElement as l}from"../lit-all.min.js";var t=class extends l{static styles=o` :host { display: inline-grid; place-items: end start; @@ -40,4 +40,3 @@ import{html as e,css as o,LitElement as l}from"/libs/deps/lit-all.min.js";var t= ${this.isMobile&&this.rows.length>this.mobileRows?e`
${this.showAll?"- See less":"+ See more"}
`:e``}`}get isMobile(){return window.matchMedia("(max-width: 767px)").matches}get rows(){return this.querySelectorAll("merch-mnemonic-list")}};customElements.define("merch-whats-included",t);export{t as MerchWhatsIncluded}; -//# sourceMappingURL=merch-whats-included.js.map diff --git a/libs/deps/mas/plans-modal.js b/libs/deps/mas/plans-modal.js index 986dd50015..93c8e38a63 100644 --- a/libs/deps/mas/plans-modal.js +++ b/libs/deps/mas/plans-modal.js @@ -4999,13 +4999,6 @@ var MatchMediaController = class { } }; -// src/media.js -var MOBILE_LANDSCAPE = "(max-width: 767px)"; -var TABLET_DOWN = "(max-width: 1199px)"; -var TABLET_UP = "(min-width: 768px)"; -var DESKTOP_UP = "(min-width: 1200px)"; -var LARGE_DESKTOP = "(min-width: 1600px)"; - // src/global.css.js var styles = document.createElement("style"); styles.innerHTML = ` @@ -5071,7 +5064,6 @@ styles.innerHTML = ` --consonant-merch-card-heading-padding: 0; - --consonant-merch-card-image-height: 180px; /* colors */ --consonant-merch-card-border-color: #eaeaea; @@ -5084,45 +5076,6 @@ styles.innerHTML = ` --consonant-merch-card-max-width: 300px; --transition: cmax-height 0.3s linear, opacity 0.3s linear; - /* special offers */ - --consonant-merch-card-special-offers-width: 378px; - - /* image */ - --consonant-merch-card-image-width: 300px; - - /* segment */ - --consonant-merch-card-segment-width: 378px; - - /* inline-heading */ - --consonant-merch-card-inline-heading-width: 300px; - - /* product */ - --consonant-merch-card-product-width: 300px; - - /* plans */ - --consonant-merch-card-plans-width: 300px; - --consonant-merch-card-plans-icon-size: 40px; - - /* catalog */ - --consonant-merch-card-catalog-width: 276px; - --consonant-merch-card-catalog-icon-size: 40px; - - /* twp */ - --consonant-merch-card-twp-width: 268px; - --consonant-merch-card-twp-mobile-width: 300px; - --consonant-merch-card-twp-mobile-height: 358px; - - /* ccd-action */ - --consonant-merch-card-ccd-action-width: 276px; - --consonant-merch-card-ccd-action-min-height: 320px; - - - /*mini compare chart */ - --consonant-merch-card-mini-compare-chart-icon-size: 32px; - --consonant-merch-card-mini-compare-mobile-cta-font-size: 15px; - --consonant-merch-card-mini-compare-mobile-cta-width: 75px; - --consonant-merch-card-mini-compare-badge-mobile-max-width: 50px; - /* inline SVGs */ --checkmark-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath fill='%23fff' d='M3.788 9A.999.999 0 0 1 3 8.615l-2.288-3a1 1 0 1 1 1.576-1.23l1.5 1.991 3.924-4.991a1 1 0 1 1 1.576 1.23l-4.712 6A.999.999 0 0 1 3.788 9z' class='spectrum-UIIcon--medium'/%3E%3C/svg%3E%0A"); @@ -5178,10 +5131,6 @@ merch-card.has-divider hr { border: none; } -merch-card[variant="special-offers"] span[is="inline-price"][data-template="strikethrough"] { - font-size: var(--consonant-merch-card-body-xs-font-size); -} - merch-card p, merch-card h3, merch-card h4 { margin: 0; } @@ -5336,155 +5285,12 @@ merch-card [slot="body-xl"] { color: var(--merch-color-grey-80); } -merch-card[variant="plans"] [slot="description"] { - min-height: 84px; -} - -merch-card[variant="catalog"] [slot="action-menu-content"] { - background-color: #000; - color: var(--color-white, #fff); - font-size: var(--consonant-merch-card-body-xs-font-size); - width: fit-content; - padding: var(--consonant-merch-spacing-xs); - border-radius: var(--consonant-merch-spacing-xxxs); - position: absolute; - top: 55px; - right: 15px; - line-height: var(--consonant-merch-card-body-line-height); -} - -merch-card[variant="catalog"] [slot="action-menu-content"] ul { - padding-left: 0; - padding-bottom: var(--consonant-merch-spacing-xss); - margin-top: 0; - margin-bottom: 0; - list-style-position: inside; - list-style-type: '\u2022 '; -} - -merch-card[variant="catalog"] [slot="action-menu-content"] ul li { - padding-left: 0; - line-height: var(--consonant-merch-card-body-line-height); -} - -merch-card[variant="catalog"] [slot="action-menu-content"] ::marker { - margin-right: 0; -} - -merch-card[variant="catalog"] [slot="action-menu-content"] p { - color: var(--color-white, #fff); -} - -merch-card[variant="catalog"] [slot="action-menu-content"] a { - color: var(--consonant-merch-card-background-color); - text-decoration: underline; -} - -merch-card[variant="catalog"] [slot="payment-details"] { - font-size: var(--consonant-merch-card-body-font-size); - font-style: italic; - font-weight: 400; - line-height: var(--consonant-merch-card-body-line-height); -} - -merch-card[variant="ccd-action"] .price-strikethrough { - font-size: 18px; -} - -merch-card[variant="plans"] [slot="quantity-select"] { - display: flex; - justify-content: flex-start; - box-sizing: border-box; - width: 100%; - padding: var(--consonant-merch-spacing-xs); -} - -merch-card[variant="twp"] div[class$='twp-badge'] { - padding: 4px 10px 5px 10px; -} - -merch-card[variant="twp"] [slot="body-xs-top"] { - font-size: var(--consonant-merch-card-body-xs-font-size); - line-height: var(--consonant-merch-card-body-xs-line-height); - color: var(--merch-color-grey-80); -} - -merch-card[variant="twp"] [slot="body-xs"] ul { - padding: 0; - margin: 0; -} - -merch-card[variant="twp"] [slot="body-xs"] ul li { - list-style-type: none; - padding-left: 0; -} - -merch-card[variant="twp"] [slot="body-xs"] ul li::before { - content: '\xB7'; - font-size: 20px; - padding-right: 5px; - font-weight: bold; -} - -merch-card[variant="twp"] [slot="footer"] { - font-size: var(--consonant-merch-card-body-xs-font-size); - line-height: var(--consonant-merch-card-body-xs-line-height); - padding: var(--consonant-merch-spacing-s) - var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-xs); - color: var(--merch-color-grey-80); - display: flex; - flex-flow: wrap; -} - -merch-card[variant='twp'] merch-quantity-select, -merch-card[variant='twp'] merch-offer-select { - display: none; -} - [slot="cci-footer"] p, [slot="cct-footer"] p, [slot="cce-footer"] p { margin: 0; } -/* mini compare chart card styles */ - -merch-card[variant="mini-compare-chart"] [slot="heading-m"] { - padding: 0 var(--consonant-merch-spacing-s) 0; -} - -merch-card[variant="mini-compare-chart"] [slot="body-m"] { - padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s); -} - -merch-card[variant="mini-compare-chart"] [is="inline-price"] { - display: inline-block; - min-height: 30px; - min-width: 1px; -} - -merch-card[variant="mini-compare-chart"] [slot='callout-content'] { - padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s) 0px; -} - -merch-card[variant="mini-compare-chart"] [slot='callout-content'] [is="inline-price"] { - min-height: unset; -} - -merch-card[variant="mini-compare-chart"] [slot="price-commitment"] { - font-size: var(--consonant-merch-card-body-xs-font-size); - padding: 0 var(--consonant-merch-spacing-s); -} - -merch-card[variant="mini-compare-chart"] [slot="price-commitment"] a { - display: inline-block; - height: 27px; -} - -merch-card[variant="mini-compare-chart"] [slot="offers"] { - font-size: var(--consonant-merch-card-body-xs-font-size); -} - merch-card [slot="promo-text"] { color: var(--merch-color-green-promo); font-size: var(--consonant-merch-card-promo-text-height); @@ -5495,111 +5301,6 @@ merch-card [slot="promo-text"] { padding: 0; } - -merch-card[variant="mini-compare-chart"] [slot="body-xxs"] { - font-size: var(--consonant-merch-card-body-xs-font-size); - padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s) 0; -} - -merch-card[variant="mini-compare-chart"] [slot="promo-text"] { - font-size: var(--consonant-merch-card-body-m-font-size); - padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s) 0; -} - -merch-card[variant="mini-compare-chart"] [slot="promo-text"] a { - text-decoration: underline; -} - -merch-card[variant="mini-compare-chart"] .footer-row-icon { - display: flex; - place-items: center; -} - -merch-card[variant="mini-compare-chart"] .footer-row-icon img { - max-width: initial; - width: var(--consonant-merch-card-mini-compare-chart-icon-size); - height: var(--consonant-merch-card-mini-compare-chart-icon-size); -} - -merch-card[variant="mini-compare-chart"] .footer-row-cell { - border-top: 1px solid var(--consonant-merch-card-border-color); - display: flex; - gap: var(--consonant-merch-spacing-xs); - justify-content: start; - place-items: center; - padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s); -} - -merch-card[variant="mini-compare-chart"] .footer-row-cell-description { - font-size: var(--consonant-merch-card-body-s-font-size); - line-height: var(--consonant-merch-card-body-s-line-height); -} - -merch-card[variant="mini-compare-chart"] .footer-row-cell-description p { - color: var(--merch-color-grey-80); - vertical-align: bottom; -} - -merch-card[variant="mini-compare-chart"] .footer-row-cell-description a { - color: var(--color-accent); - text-decoration: solid; -} - -/* mini compare mobile */ -@media screen and ${MOBILE_LANDSCAPE} { - merch-card[variant="mini-compare-chart"] [slot='heading-m'] { - font-size: var(--consonant-merch-card-body-s-font-size); - line-height: var(--consonant-merch-card-body-s-line-height); - } - - merch-card[variant="mini-compare-chart"] [slot='heading-m-price'] { - font-size: var(--consonant-merch-card-body-s-font-size); - line-height: var(--consonant-merch-card-body-s-line-height); - } - - merch-card[variant="mini-compare-chart"] [slot='body-m'] { - font-size: var(--consonant-merch-card-body-xs-font-size); - line-height: var(--consonant-merch-card-body-xs-line-height); - } - - merch-card[variant="mini-compare-chart"] [slot="promo-text"] { - font-size: var(--consonant-merch-card-body-xs-font-size); - line-height: var(--consonant-merch-card-body-xs-line-height); - } - merch-card[variant="mini-compare-chart"] .footer-row-cell-description { - font-size: var(--consonant-merch-card-body-xs-font-size); - line-height: var(--consonant-merch-card-body-xs-line-height); - } -} - -/* mini compare tablet */ -@media screen and ${TABLET_DOWN} { - merch-card[variant="mini-compare-chart"] [slot='heading-m'] { - font-size: var(--consonant-merch-card-body-s-font-size); - line-height: var(--consonant-merch-card-body-s-line-height); - } - - merch-card[variant="mini-compare-chart"] [slot='heading-m-price'] { - font-size: var(--consonant-merch-card-body-s-font-size); - line-height: var(--consonant-merch-card-body-s-line-height); - } - - merch-card[variant="mini-compare-chart"] [slot='body-m'] { - font-size: var(--consonant-merch-card-body-xs-font-size); - line-height: var(--consonant-merch-card-body-xs-line-height); - } - - merch-card[variant="mini-compare-chart"] [slot="promo-text"] { - font-size: var(--consonant-merch-card-body-xs-font-size); - line-height: var(--consonant-merch-card-body-xs-line-height); - } - - merch-card[variant="mini-compare-chart"] .footer-row-cell-description { - font-size: var(--consonant-merch-card-body-xs-font-size); - line-height: var(--consonant-merch-card-body-xs-line-height); - } -} - div[slot="footer"] { display: contents; } @@ -5624,451 +5325,6 @@ div[slot='bg-image'] img { border-top-right-radius: 16px; } -/* Mobile */ -@media screen and ${MOBILE_LANDSCAPE} { - :root { - --consonant-merch-card-mini-compare-chart-width: 302px; - --consonant-merch-card-segment-width: 276px; - --consonant-merch-card-mini-compare-chart-wide-width: 302px; - --consonant-merch-card-special-offers-width: 302px; - --consonant-merch-card-twp-width: 300px; - } -} - - -/* Tablet */ -@media screen and ${TABLET_UP} { - :root { - --consonant-merch-card-catalog-width: 302px; - --consonant-merch-card-plans-width: 302px; - --consonant-merch-card-segment-width: 276px; - --consonant-merch-card-mini-compare-chart-width: 302px; - --consonant-merch-card-mini-compare-chart-wide-width: 302px; - --consonant-merch-card-special-offers-width: 302px; - --consonant-merch-card-twp-width: 268px; - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - :root { - --consonant-merch-card-catalog-width: 276px; - --consonant-merch-card-plans-width: 276px; - --consonant-merch-card-segment-width: 302px; - --consonant-merch-card-inline-heading-width: 378px; - --consonant-merch-card-product-width: 378px; - --consonant-merch-card-image-width: 378px; - --consonant-merch-card-mini-compare-chart-width: 378px; - --consonant-merch-card-mini-compare-chart-wide-width: 484px; - --consonant-merch-card-twp-width: 268px; - } -} - -/* supported cards */ -/* grid style for plans */ -.one-merch-card.plans, -.two-merch-cards.plans, -.three-merch-cards.plans, -.four-merch-cards.plans { - grid-template-columns: var(--consonant-merch-card-plans-width); -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .two-merch-cards.plans, - .three-merch-cards.plans, - .four-merch-cards.plans { - grid-template-columns: repeat(2, var(--consonant-merch-card-plans-width)); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .three-merch-cards.plans, - .four-merch-cards.plans { - grid-template-columns: repeat(3, var(--consonant-merch-card-plans-width)); - } -} - -/* Large desktop */ - @media screen and ${LARGE_DESKTOP} { - .four-merch-cards.plans { - grid-template-columns: repeat(4, var(--consonant-merch-card-plans-width)); - } -} - - -/* grid style for catalog */ -.one-merch-card.catalog, -.two-merch-cards.catalog, -.three-merch-cards.catalog, -.four-merch-cards.catalog { - grid-template-columns: var(--consonant-merch-card-catalog-width); -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .two-merch-cards.catalog, - .three-merch-cards.catalog, - .four-merch-cards.catalog { - grid-template-columns: repeat(2, var(--consonant-merch-card-catalog-width)); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .three-merch-cards.catalog, - .four-merch-cards.catalog { - grid-template-columns: repeat(3, var(--consonant-merch-card-catalog-width)); - } -} - -/* Large desktop */ - @media screen and ${LARGE_DESKTOP} { - .four-merch-cards.catalog { - grid-template-columns: repeat(4, var(--consonant-merch-card-catalog-width)); - } -} - - -/* grid style for special-offers */ -.one-merch-card.special-offers, -.two-merch-cards.special-offers, -.three-merch-cards.special-offers, -.four-merch-cards.special-offers { - grid-template-columns: minmax(300px, var(--consonant-merch-card-special-offers-width)); -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .two-merch-cards.special-offers, - .three-merch-cards.special-offers, - .four-merch-cards.special-offers { - grid-template-columns: repeat(2, minmax(300px, var(--consonant-merch-card-special-offers-width))); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .three-merch-cards.special-offers, - .four-merch-cards.special-offers { - grid-template-columns: repeat(3, minmax(300px, var(--consonant-merch-card-special-offers-width))); - } -} - -@media screen and ${LARGE_DESKTOP} { - .four-merch-cards.special-offers { - grid-template-columns: repeat(4, minmax(300px, var(--consonant-merch-card-special-offers-width))); - } -} - - -/* grid style for image */ -.one-merch-card.image, -.two-merch-cards.image, -.three-merch-cards.image, -.four-merch-cards.image { - grid-template-columns: var(--consonant-merch-card-image-width); -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .two-merch-cards.image, - .three-merch-cards.image, - .four-merch-cards.image { - grid-template-columns: repeat(2, var(--consonant-merch-card-image-width)); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .three-merch-cards.image, - .four-merch-cards.image { - grid-template-columns: repeat(3, var(--consonant-merch-card-image-width)); - } -} - -/* Large desktop */ - @media screen and ${LARGE_DESKTOP} { - .four-merch-cards.image { - grid-template-columns: repeat(4, var(--consonant-merch-card-image-width)); - } -} - - -/* grid style for segment */ -.one-merch-card.segment, -.two-merch-cards.segment, -.three-merch-cards.segment, -.four-merch-cards.segment { - grid-template-columns: minmax(276px, var(--consonant-merch-card-segment-width)); -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .two-merch-cards.segment, - .three-merch-cards.segment, - .four-merch-cards.segment { - grid-template-columns: repeat(2, minmax(276px, var(--consonant-merch-card-segment-width))); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .three-merch-cards.segment { - grid-template-columns: repeat(3, minmax(276px, var(--consonant-merch-card-segment-width))); - } - - .four-merch-cards.segment { - grid-template-columns: repeat(4, minmax(276px, var(--consonant-merch-card-segment-width))); - } -} - - -/* grid style for product */ -.one-merch-card.product, -.two-merch-cards.product, -.three-merch-cards.product, -.four-merch-cards.product { - grid-template-columns: var(--consonant-merch-card-product-width); -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .two-merch-cards.product, - .three-merch-cards.product, - .four-merch-cards.product { - grid-template-columns: repeat(2, var(--consonant-merch-card-product-width)); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .three-merch-cards.product, - .four-merch-cards.product { - grid-template-columns: repeat(3, var(--consonant-merch-card-product-width)); - } -} - -/* Large desktop */ - @media screen and ${LARGE_DESKTOP} { - .four-merch-cards.product { - grid-template-columns: repeat(4, var(--consonant-merch-card-product-width)); - } -} - -/* grid style for twp */ -.one-merch-card.twp, -.two-merch-cards.twp, -.three-merch-cards.twp { - grid-template-columns: var(--consonant-merch-card-image-width); -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .one-merch-card.twp, - .two-merch-cards.twp { - grid-template-columns: repeat(2, var(--consonant-merch-card-twp-width)); - } - .three-merch-cards.twp { - grid-template-columns: repeat(3, var(--consonant-merch-card-twp-width)); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .one-merch-card.twp - .two-merch-cards.twp { - grid-template-columns: repeat(2, var(--consonant-merch-card-twp-width)); - } - .three-merch-cards.twp { - grid-template-columns: repeat(3, var(--consonant-merch-card-twp-width)); - } -} - -/* Large desktop */ - @media screen and ${LARGE_DESKTOP} { - .one-merch-card.twp - .two-merch-cards.twp { - grid-template-columns: repeat(2, var(--consonant-merch-card-twp-width)); - } - .three-merch-cards.twp { - grid-template-columns: repeat(3, var(--consonant-merch-card-twp-width)); - } -} - -/* Mobile */ -@media screen and ${MOBILE_LANDSCAPE} { - .one-merch-card.twp, - .two-merch-cards.twp, - .three-merch-cards.twp { - grid-template-columns: repeat(1, var(--consonant-merch-card-twp-mobile-width)); - } -} - -/* grid style for inline-heading */ -.one-merch-card.inline-heading, -.two-merch-cards.inline-heading, -.three-merch-cards.inline-heading, -.four-merch-cards.inline-heading { - grid-template-columns: var(--consonant-merch-card-inline-heading-width); -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .two-merch-cards.inline-heading, - .three-merch-cards.inline-heading, - .four-merch-cards.inline-heading { - grid-template-columns: repeat(2, var(--consonant-merch-card-inline-heading-width)); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .three-merch-cards.inline-heading, - .four-merch-cards.inline-heading { - grid-template-columns: repeat(3, var(--consonant-merch-card-inline-heading-width)); - } -} - -/* Large desktop */ - @media screen and ${LARGE_DESKTOP} { - .four-merch-cards.inline-heading { - grid-template-columns: repeat(4, var(--consonant-merch-card-inline-heading-width)); - } -} - -/* grid style for ccd-action */ -.one-merch-card.ccd-action, -.two-merch-cards.ccd-action, -.three-merch-cards.ccd-action, -.four-merch-cards.ccd-action { - grid-template-columns: var(--consonant-merch-card-ccd-action-width); -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .two-merch-cards.ccd-action, - .three-merch-cards.ccd-action, - .four-merch-cards.ccd-action { - grid-template-columns: repeat(2, var(--consonant-merch-card-ccd-action-width)); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .three-merch-cards.ccd-action, - .four-merch-cards.ccd-action { - grid-template-columns: repeat(3, var(--consonant-merch-card-ccd-action-width)); - } -} - -/* Large desktop */ - @media screen and ${LARGE_DESKTOP} { - .four-merch-cards.ccd-action { - grid-template-columns: repeat(4, var(--consonant-merch-card-ccd-action-width)); - } -} - -/* grid style for mini-compare-chart */ -.one-merch-card.mini-compare-chart { - grid-template-columns: var(--consonant-merch-card-mini-compare-chart-wide-width); - gap: var(--consonant-merch-spacing-xs); -} - -.two-merch-cards.mini-compare-chart, -.three-merch-cards.mini-compare-chart, -.four-merch-cards.mini-compare-chart { - grid-template-columns: repeat(2, var(--consonant-merch-card-mini-compare-chart-width)); - gap: var(--consonant-merch-spacing-xs); -} - -@media screen and ${MOBILE_LANDSCAPE} { - .two-merch-cards.mini-compare-chart, - .three-merch-cards.mini-compare-chart, - .four-merch-cards.mini-compare-chart { - grid-template-columns: var(--consonant-merch-card-mini-compare-chart-width); - gap: var(--consonant-merch-spacing-xs); - } -} - -@media screen and ${TABLET_DOWN} { - .three-merch-cards.mini-compare-chart merch-card [slot="footer"] a, - .four-merch-cards.mini-compare-chart merch-card [slot="footer"] a { - flex: 1; - } -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .two-merch-cards.mini-compare-chart { - grid-template-columns: repeat(2, minmax(var(--consonant-merch-card-mini-compare-chart-width), var(--consonant-merch-card-mini-compare-chart-wide-width))); - gap: var(--consonant-merch-spacing-m); - } - - .three-merch-cards.mini-compare-chart, - .four-merch-cards.mini-compare-chart { - grid-template-columns: repeat(2, minmax(var(--consonant-merch-card-mini-compare-chart-width), var(--consonant-merch-card-mini-compare-chart-wide-width))); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .one-merch-card.mini-compare-chart { - grid-template-columns: var(--consonant-merch-card-mini-compare-chart-wide-width); - } - - .two-merch-cards.mini-compare-chart { - grid-template-columns: repeat(2, var(--consonant-merch-card-mini-compare-chart-wide-width)); - gap: var(--consonant-merch-spacing-m); - } - - .three-merch-cards.mini-compare-chart, - .four-merch-cards.mini-compare-chart { - grid-template-columns: repeat(3, var(--consonant-merch-card-mini-compare-chart-width)); - gap: var(--consonant-merch-spacing-m); - } -} - -@media screen and ${LARGE_DESKTOP} { - .four-merch-cards.mini-compare-chart { - grid-template-columns: repeat(4, var(--consonant-merch-card-mini-compare-chart-width)); - } -} - -/* mini-compare card footer rows */ -merch-card .footer-row-cell:nth-child(1) { - min-height: var(--consonant-merch-card-footer-row-1-min-height); -} - -merch-card .footer-row-cell:nth-child(2) { - min-height: var(--consonant-merch-card-footer-row-2-min-height); -} - -merch-card .footer-row-cell:nth-child(3) { - min-height: var(--consonant-merch-card-footer-row-3-min-height); -} - -merch-card .footer-row-cell:nth-child(4) { - min-height: var(--consonant-merch-card-footer-row-4-min-height); -} - -merch-card .footer-row-cell:nth-child(5) { - min-height: var(--consonant-merch-card-footer-row-5-min-height); -} - -merch-card .footer-row-cell:nth-child(6) { - min-height: var(--consonant-merch-card-footer-row-6-min-height); -} - -merch-card .footer-row-cell:nth-child(7) { - min-height: var(--consonant-merch-card-footer-row-7-min-height); -} - -merch-card .footer-row-cell:nth-child(8) { - min-height: var(--consonant-merch-card-footer-row-8-min-height); -} - span[is="inline-price"][data-template='strikethrough'] { text-decoration: line-through; } @@ -6231,6 +5487,9 @@ var styles2 = css` `; var plans_modal_css_default = styles2; +// src/media.js +var MOBILE_LANDSCAPE = "(max-width: 767px)"; + // src/plans-modal.js var PlansModal = class extends LitElement { static properties = { diff --git a/libs/features/dynamic-navigation.js b/libs/features/dynamic-navigation.js index e959365e2e..7f901a2629 100644 --- a/libs/features/dynamic-navigation.js +++ b/libs/features/dynamic-navigation.js @@ -1,6 +1,19 @@ import { getMetadata } from '../utils/utils.js'; +function isDynamicNavDisabled() { + const dynamicNavDisableValues = getMetadata('dynamic-nav-disable'); + if (!dynamicNavDisableValues) return false; + + const metadataPairsMap = dynamicNavDisableValues.split(',').map((pair) => pair.split(';')); + return metadataPairsMap.some(([metadataKey, metadataContent]) => { + const metaTagContent = getMetadata(metadataKey.toLowerCase()); + return (metaTagContent + && metaTagContent.toLowerCase() === metadataContent.toLowerCase()); + }); +} + export default function dynamicNav(url, key) { + if (isDynamicNavDisabled()) return url; const metadataContent = getMetadata('dynamic-nav'); if (metadataContent === 'entry') { diff --git a/libs/features/footer-promo.js b/libs/features/footer-promo.js index 78466a0055..2d47501b84 100644 --- a/libs/features/footer-promo.js +++ b/libs/features/footer-promo.js @@ -20,7 +20,7 @@ async function getPromoFromTaxonomy(contentRoot, doc) { if (primaryTag) return primaryTag[FOOTER_PROMO_LINK_KEY]; } catch (error) { /* c8 ignore next 2 */ - window.lana.log(`Footer Promo - Taxonomy error: ${error}`, { tags: 'errorType=info,module=footer-promo' }); + window.lana.log(`Footer Promo - Taxonomy error: ${error}`, { tags: 'footer-promo', errorType: 'i' }); } return undefined; } diff --git a/libs/features/georoutingv2/georoutingv2.css b/libs/features/georoutingv2/georoutingv2.css index 3563ac8306..b4d495b206 100644 --- a/libs/features/georoutingv2/georoutingv2.css +++ b/libs/features/georoutingv2/georoutingv2.css @@ -21,10 +21,6 @@ z-index: -1; } -.dialog-modal.locale-modal-v2 .georouting-bg svg { - width: 100%; -} - .dialog-modal.locale-modal-v2 .picker { position: fixed; margin-top: 2px; @@ -140,19 +136,6 @@ padding: initial; } -@media (min-width: 480px) { - .dialog-modal.locale-modal-v2 { - background-image: url('/libs/features/georoutingv2/img/GeoModal_BG_Map_Tablet.png'); - background-position: center; - background-repeat: no-repeat; - background-size: cover; - } - - .dialog-modal.locale-modal-v2 .georouting-bg { - display: none; - } -} - @media (min-width: 600px) { .dialog-modal.locale-modal-v2 .georouting-wrapper { padding: 56px 56px 40px; @@ -168,9 +151,3 @@ max-width: 720px; } } - -@media (min-width: 1200px) { - .dialog-modal.locale-modal-v2 { - background-image: url('/libs/features/georoutingv2/img/GeoModal_BG_Map_Desktop.png'); - } -} diff --git a/libs/features/georoutingv2/georoutingv2.js b/libs/features/georoutingv2/georoutingv2.js index cb6c817db3..a37967a95b 100644 --- a/libs/features/georoutingv2/georoutingv2.js +++ b/libs/features/georoutingv2/georoutingv2.js @@ -228,13 +228,7 @@ function buildContent(currentPage, locale, geoData, locales) { async function getDetails(currentPage, localeMatches, geoData) { const availableLocales = await getAvailableLocales(localeMatches); if (!availableLocales.length) return null; - const { innerWidth } = window; - let svgDiv = null; - if (innerWidth < 480) { - const { default: getMobileBg } = await import('./getMobileBg.js'); - svgDiv = createTag('div', { class: 'georouting-bg' }, getMobileBg()); - } - const georoutingWrapper = createTag('div', { class: 'georouting-wrapper fragment', style: 'display:none;' }, svgDiv); + const georoutingWrapper = createTag('div', { class: 'georouting-wrapper fragment', style: 'display:none;' }); currentPage.url = window.location.hash ? document.location.href : '#'; if (availableLocales.length === 1) { const content = buildContent(currentPage, availableLocales[0], geoData); diff --git a/libs/features/georoutingv2/getMobileBg.js b/libs/features/georoutingv2/getMobileBg.js deleted file mode 100644 index 2f59d33750..0000000000 --- a/libs/features/georoutingv2/getMobileBg.js +++ /dev/null @@ -1,13 +0,0 @@ -export default function getMobileBg() { - return ` - - - - - `; -} diff --git a/libs/features/georoutingv2/img/GeoModal_BG_Map_Desktop.png b/libs/features/georoutingv2/img/GeoModal_BG_Map_Desktop.png deleted file mode 100644 index c24e5e7648..0000000000 Binary files a/libs/features/georoutingv2/img/GeoModal_BG_Map_Desktop.png and /dev/null differ diff --git a/libs/features/georoutingv2/img/GeoModal_BG_Map_Mobile.png b/libs/features/georoutingv2/img/GeoModal_BG_Map_Mobile.png deleted file mode 100644 index 0d04bdfc8f..0000000000 Binary files a/libs/features/georoutingv2/img/GeoModal_BG_Map_Mobile.png and /dev/null differ diff --git a/libs/features/georoutingv2/img/GeoModal_BG_Map_Tablet.png b/libs/features/georoutingv2/img/GeoModal_BG_Map_Tablet.png deleted file mode 100644 index 13dda58bb6..0000000000 Binary files a/libs/features/georoutingv2/img/GeoModal_BG_Map_Tablet.png and /dev/null differ diff --git a/libs/features/mas/commerce/build.mjs b/libs/features/mas/commerce/build.mjs index aa439045e0..7dfe9467e2 100644 --- a/libs/features/mas/commerce/build.mjs +++ b/libs/features/mas/commerce/build.mjs @@ -12,7 +12,6 @@ const { metafile } = await build({ minify: true, outfile: '../../../../libs/deps/mas/commerce.js', platform: 'browser', - sourcemap: true, target: ['es2020'], }); diff --git a/libs/features/mas/commerce/libs/commerce.d.ts b/libs/features/mas/commerce/libs/commerce.d.ts index 20c92d59e8..e7372d2203 100644 --- a/libs/features/mas/commerce/libs/commerce.d.ts +++ b/libs/features/mas/commerce/libs/commerce.d.ts @@ -22,7 +22,7 @@ declare global { Commerce.Checkout.Settings & Commerce.Price.Settings & Commerce.Wcs.Settings, - 'locale' | 'priceLiteralsURL' | 'quantity' + 'locale' | 'quantity' > & { quantity: number; } @@ -514,8 +514,6 @@ declare global { displayRecurrence: boolean; displayTax: boolean; forceTaxExclusive: boolean; - priceLiteralsURL: string; - priceLiteralsPromise: Promise; } } diff --git a/libs/features/mas/commerce/package.json b/libs/features/mas/commerce/package.json index 7a1964d597..13d6777263 100644 --- a/libs/features/mas/commerce/package.json +++ b/libs/features/mas/commerce/package.json @@ -16,7 +16,7 @@ "build:types": "cp ./src/index.d.ts ./libs/commerce.d.ts", "build:watch": "npm run build:bundle --watch", "test": "wtr --config ./web-test-runner.config.mjs --coverage --watch", - "test:ci": "wtr --config ./web-test-runner.config.mjs" + "test:ci": "wtr --config ./web-test-runner.config.mjs --coverage" }, "dependencies": { "@dexter/tacocat-consonant-templates": "file:./internal/tacocat-consonant-templates-1.13.0.tgz", diff --git a/libs/features/mas/commerce/price-literals.json b/libs/features/mas/commerce/price-literals.json new file mode 100644 index 0000000000..52cfa06257 --- /dev/null +++ b/libs/features/mas/commerce/price-literals.json @@ -0,0 +1 @@ +{"total":38,"offset":0,"limit":38,"data":[{"lang":"ar","recurrenceLabel":"{recurrenceTerm, select, MONTH {/الشهر} YEAR {/العام} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {كل شهر} YEAR {كل عام} other {}}","perUnitLabel":"{perUnit, select, LICENSE {لكل ترخيص} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {لكل ترخيص} other {}}","freeLabel":"مجانًا","freeAriaLabel":"مجانًا","taxExclusiveLabel":"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}","alternativePriceAriaLabel":"أو بدلاً من ذلك بقيمة {alternativePrice}","strikethroughAriaLabel":"بشكل منتظم بقيمة {strikethroughPrice}"},{"lang":"bg","recurrenceLabel":"{recurrenceTerm, select, MONTH {/мес.} YEAR {/год.} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {на месец} YEAR {на година} other {}}","perUnitLabel":"{perUnit, select, LICENSE {на лиценз} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {на лиценз} other {}}","freeLabel":"Безплатно","freeAriaLabel":"Безплатно","taxExclusiveLabel":"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}","taxInclusiveLabel":"{incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}","alternativePriceAriaLabel":"Алтернативно на {alternativePrice}","strikethroughAriaLabel":"Редовно на {strikethroughPrice}"},{"lang":"cs","recurrenceLabel":"{recurrenceTerm, select, MONTH {/měsíc} YEAR {/rok} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {za měsíc} YEAR {za rok} other {}}","perUnitLabel":"{perUnit, select, LICENSE {za licenci} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {za licenci} other {}}","freeLabel":"Zdarma","freeAriaLabel":"Zdarma","taxExclusiveLabel":"{taxTerm, select, GST {bez daně ze zboží a služeb} VAT {bez DPH} TAX {bez daně} IVA {bez IVA} SST {bez SST} KDV {bez KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {včetně daně ze zboží a služeb} VAT {včetně DPH} TAX {včetně daně} IVA {včetně IVA} SST {včetně SST} KDV {včetně KDV} other {}}","alternativePriceAriaLabel":"Případně za {alternativePrice}","strikethroughAriaLabel":"Pravidelně za {strikethroughPrice}"},{"lang":"da","recurrenceLabel":"{recurrenceTerm, select, MONTH {/md} YEAR {/år} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {pr. måned} YEAR {pr. år} other {}}","perUnitLabel":"{perUnit, select, LICENSE {pr. licens} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {pr. licens} other {}}","freeLabel":"Gratis","freeAriaLabel":"Gratis","taxExclusiveLabel":"{taxTerm, select, GST {ekskl. GST} VAT {ekskl. moms} TAX {ekskl. skat} IVA {ekskl. IVA} SST {ekskl. SST} KDV {ekskl. KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {inkl. GST} VAT {inkl. moms} TAX {inkl. skat} IVA {inkl. IVA} SST {inkl. SST} KDV {inkl. KDV} other {}}","alternativePriceAriaLabel":"Alternativt til {alternativePrice}","strikethroughAriaLabel":"Normalpris {strikethroughPrice}"},{"lang":"de","recurrenceLabel":"{recurrenceTerm, select, MONTH {/Monat} YEAR {/Jahr} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {pro Monat} YEAR {pro Jahr} other {}}","perUnitLabel":"{perUnit, select, LICENSE {pro Lizenz} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {pro Lizenz} other {}}","freeLabel":"Kostenlos","freeAriaLabel":"Kostenlos","taxExclusiveLabel":"{taxTerm, select, GST {zzgl. GST} VAT {zzgl. MwSt.} TAX {zzgl. Steuern} IVA {zzgl. IVA} SST {zzgl. SST} KDV {zzgl. KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {inkl. GST} VAT {inkl. MwSt.} TAX {inkl. Steuern} IVA {inkl. IVA} SST {inkl. SST} KDV {inkl. KDV} other {}}","alternativePriceAriaLabel":"Alternativ: {alternativePrice}","strikethroughAriaLabel":"Regulär: {strikethroughPrice}"},{"lang":"en","recurrenceLabel":"{recurrenceTerm, select, MONTH {/mo} YEAR {/yr} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {per month} YEAR {per year} other {}}","perUnitLabel":"{perUnit, select, LICENSE {per license} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {per license} other {}}","freeLabel":"Free","freeAriaLabel":"Free","taxExclusiveLabel":"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}","alternativePriceAriaLabel":"Alternatively at {alternativePrice}","strikethroughAriaLabel":"Regularly at {strikethroughPrice}"},{"lang":"et","recurrenceLabel":"{recurrenceTerm, select, MONTH {kuus} YEAR {aastas} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {kuus} YEAR {aastas} other {}}","perUnitLabel":"{perUnit, select, LICENSE {litsentsi kohta} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {litsentsi kohta} other {}}","freeLabel":"Tasuta","freeAriaLabel":"Tasuta","taxExclusiveLabel":"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}","alternativePriceAriaLabel":"Teise võimalusena hinnaga {alternativePrice}","strikethroughAriaLabel":"Tavahind {strikethroughPrice}"},{"lang":"fi","recurrenceLabel":"{recurrenceTerm, select, MONTH {/kk} YEAR {/v} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {kuukausittain} YEAR {vuosittain} other {}}","perUnitLabel":"{perUnit, select, LICENSE {käyttöoikeutta kohti} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {käyttöoikeutta kohti} other {}}","freeLabel":"Maksuton","freeAriaLabel":"Maksuton","taxExclusiveLabel":"{taxTerm, select, GST {ilman GST:tä} VAT {ilman ALV:tä} TAX {ilman veroja} IVA {ilman IVA:ta} SST {ilman SST:tä} KDV {ilman KDV:tä} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {sis. GST:n} VAT {sis. ALV:n} TAX {sis. verot} IVA {sis. IVA:n} SST {sis. SST:n} KDV {sis. KDV:n} other {}}","alternativePriceAriaLabel":"Vaihtoehtoisesti hintaan {alternativePrice}","strikethroughAriaLabel":"Säännöllisesti hintaan {strikethroughPrice}"},{"lang":"fr","recurrenceLabel":"{recurrenceTerm, select, MONTH {/mois} YEAR {/an} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {par mois} YEAR {par an} other {}}","perUnitLabel":"{perUnit, select, LICENSE {par licence} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {par licence} other {}}","freeLabel":"Gratuit","freeAriaLabel":"Gratuit","taxExclusiveLabel":"{taxTerm, select, GST {hors TPS} VAT {hors TVA} TAX {hors taxes} IVA {hors IVA} SST {hors SST} KDV {hors KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {TPS comprise} VAT {TVA comprise} TAX {taxes comprises} IVA {IVA comprise} SST {SST comprise} KDV {KDV comprise} other {}}","alternativePriceAriaLabel":"Autre prix {alternativePrice}","strikethroughAriaLabel":"Prix habituel {strikethroughPrice}"},{"lang":"he","recurrenceLabel":"{recurrenceTerm, select, MONTH {/חודש} YEAR {/שנה} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {לחודש} YEAR {לשנה} other {}}","perUnitLabel":"{perUnit, select, LICENSE {לרישיון} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {לרישיון} other {}}","freeLabel":"חינם","freeAriaLabel":"חינם","taxExclusiveLabel":"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}","alternativePriceAriaLabel":"לחלופין ב-{alternativePrice}","strikethroughAriaLabel":"באופן קבוע ב-{strikethroughPrice}"},{"lang":"hu","recurrenceLabel":"{recurrenceTerm, select, MONTH {/hó} YEAR {/év} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {havonta} YEAR {évente} other {}}","perUnitLabel":"{perUnit, select, LICENSE {licencenként} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {licencenként} other {}}","freeLabel":"Ingyenes","freeAriaLabel":"Ingyenes","taxExclusiveLabel":"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}","alternativePriceAriaLabel":"Másik lehetőség: {alternativePrice}","strikethroughAriaLabel":"Általában {strikethroughPrice} áron"},{"lang":"it","recurrenceLabel":"{recurrenceTerm, select, MONTH {/mese} YEAR {/anno} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {al mese} YEAR {all'anno} other {}}","perUnitLabel":"{perUnit, select, LICENSE {per licenza} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {per licenza} other {}}","freeLabel":"Gratuito","freeAriaLabel":"Gratuito","taxExclusiveLabel":"{taxTerm, select, GST {escl. GST} VAT {escl. IVA.} TAX {escl. imposte} IVA {escl. IVA} SST {escl. SST} KDV {escl. KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {incl. GST} VAT {incl. IVA} TAX {incl. imposte} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}","alternativePriceAriaLabel":"In alternativa a {alternativePrice}","strikethroughAriaLabel":"Regolarmente a {strikethroughPrice}"},{"lang":"ja","recurrenceLabel":"{recurrenceTerm, select, MONTH {/月} YEAR {/年} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {毎月} YEAR {毎年} other {}}","perUnitLabel":"{perUnit, select, LICENSE {ライセンスごと} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {ライセンスごと} other {}}","freeLabel":"無料","freeAriaLabel":"無料","taxExclusiveLabel":"{taxTerm, select, GST {GST 別} VAT {VAT 別} TAX {税別} IVA {IVA 別} SST {SST 別} KDV {KDV 別} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {GST 込} VAT {VAT 込} TAX {税込} IVA {IVA 込} SST {SST 込} KDV {KDV 込} other {}}","alternativePriceAriaLabel":"特別価格 : {alternativePrice}","strikethroughAriaLabel":"通常価格 : {strikethroughPrice}"},{"lang":"ko","recurrenceLabel":"{recurrenceTerm, select, MONTH {/월} YEAR {/년} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {월간} YEAR {연간} other {}}","perUnitLabel":"{perUnit, select, LICENSE {라이선스당} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {라이선스당} other {}}","freeLabel":"무료","freeAriaLabel":"무료","taxExclusiveLabel":"{taxTerm, select, GST {GST 제외} VAT {VAT 제외} TAX {세금 제외} IVA {IVA 제외} SST {SST 제외} KDV {KDV 제외} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {GST 포함} VAT {VAT 포함} TAX {세금 포함} IVA {IVA 포함} SST {SST 포함} KDV {KDV 포함} other {}}","alternativePriceAriaLabel":"또는 {alternativePrice}에","strikethroughAriaLabel":"또는 {alternativePrice}에"},{"lang":"lt","recurrenceLabel":"{recurrenceTerm, select, MONTH { per mėn.} YEAR { per metus} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {per mėn.} YEAR {per metus} other {}}","perUnitLabel":"{perUnit, select, LICENSE {už licenciją} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {už licenciją} other {}}","freeLabel":"Nemokamai","freeAriaLabel":"Nemokamai","taxExclusiveLabel":"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}","alternativePriceAriaLabel":"Arba už {alternativePrice}","strikethroughAriaLabel":"Normaliai už {strikethroughPrice}"},{"lang":"lv","recurrenceLabel":"{recurrenceTerm, select, MONTH {mēnesī} YEAR {gadā} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {mēnesī} YEAR {gadā} other {}}","perUnitLabel":"{perUnit, select, LICENSE {vienai licencei} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {vienai licencei} other {}}","freeLabel":"Bezmaksas","freeAriaLabel":"Bezmaksas","taxExclusiveLabel":"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}","alternativePriceAriaLabel":"Alternatīvi par {alternativePrice}","strikethroughAriaLabel":"Regulāri par {strikethroughPrice}"},{"lang":"nb","recurrenceLabel":"{recurrenceTerm, select, MONTH {/mnd.} YEAR {/år} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {per måned} YEAR {per år} other {}}","perUnitLabel":"{perUnit, select, LICENSE {per lisens} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {per lisens} other {}}","freeLabel":"Fri","freeAriaLabel":"Fri","taxExclusiveLabel":"{taxTerm, select, GST {ekskl. GST} VAT {ekskl. moms} TAX {ekskl. avgift} IVA {ekskl. IVA} SST {ekskl. SST} KDV {ekskl. KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {inkl. GST} VAT {inkl. moms} TAX {inkl. avgift} IVA {inkl. IVA} SST {inkl. SST} KDV {inkl. KDV} other {}}","alternativePriceAriaLabel":"Alternativt til {alternativePrice}","strikethroughAriaLabel":"Regelmessig til {strikethroughPrice}"},{"lang":"nl","recurrenceLabel":"{recurrenceTerm, select, MONTH {/mnd} YEAR {/jr} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {per maand} YEAR {per jaar} other {}}","perUnitLabel":"{perUnit, select, LICENSE {per licentie} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {per licentie} other {}}","freeLabel":"Gratis","freeAriaLabel":"Gratis","taxExclusiveLabel":"{taxTerm, select, GST {excl. GST} VAT {excl. btw} TAX {excl. belasting} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {incl. GST} VAT {incl. btw} TAX {incl. belasting} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}","alternativePriceAriaLabel":"Nu {alternativePrice}","strikethroughAriaLabel":"Normaal {strikethroughPrice}"},{"lang":"pl","recurrenceLabel":"{recurrenceTerm, select, MONTH { / mies.} YEAR { / rok} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH { / miesiąc} YEAR { / rok} other {}}","perUnitLabel":"{perUnit, select, LICENSE {za licencję} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {za licencję} other {}}","freeLabel":"Bezpłatne","freeAriaLabel":"Bezpłatne","taxExclusiveLabel":"{taxTerm, select, GST {bez GST} VAT {bez VAT} TAX {netto} IVA {bez IVA} SST {bez SST} KDV {bez KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {z GST} VAT {z VAT} TAX {brutto} IVA {z IVA} SST {z SST} KDV {z KDV} other {}}","alternativePriceAriaLabel":"Lub za {alternativePrice}","strikethroughAriaLabel":"Cena zwykła: {strikethroughPrice}"},{"lang":"pt","recurrenceLabel":"{recurrenceTerm, select, MONTH {/mês} YEAR {/ano} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {por mês} YEAR {por ano} other {}}","perUnitLabel":"{perUnit, select, LICENSE {por licença} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {por licença} other {}}","freeLabel":"Gratuito","freeAriaLabel":"Gratuito","taxExclusiveLabel":"{taxTerm, select, GST {ICMS não incluso} VAT {IVA não incluso} TAX {impostos não inclusos} IVA {IVA não incluso} SST { SST não incluso} KDV {KDV não incluso} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {ICMS incluso} VAT {IVA incluso} TAX {impostos inclusos} IVA {IVA incluso} SST {SST incluso} KDV {KDV incluso} other {}}","alternativePriceAriaLabel":"Ou a {alternativePrice}","strikethroughAriaLabel":"Preço normal: {strikethroughPrice}"},{"lang":"ro","recurrenceLabel":"{recurrenceTerm, select, MONTH {/lună} YEAR {/an} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {pe lună} YEAR {pe an} other {}}","perUnitLabel":"{perUnit, select, LICENSE {pe licență} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {pe licență} other {}}","freeLabel":"Gratuit","freeAriaLabel":"Gratuit","taxExclusiveLabel":"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}","alternativePriceAriaLabel":"Alternativ, la {alternativePrice}","strikethroughAriaLabel":"În mod normal, la {strikethroughPrice}"},{"lang":"ru","recurrenceLabel":"{recurrenceTerm, select, MONTH {/мес.} YEAR {/г.} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {в месяц} YEAR {в год} other {}}","perUnitLabel":"{perUnit, select, LICENSE {за лицензию} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {за лицензию} other {}}","freeLabel":"Бесплатно","freeAriaLabel":"Бесплатно","taxExclusiveLabel":"{taxTerm, select, GST {искл. налог на товары и услуги} VAT {искл. НДС} TAX {искл. налог} IVA {искл. ИВА} SST {искл. SST} KDV {искл. КДВ} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {вкл. налог на товары и услуги} VAT {вкл. НДС} TAX {вкл. налог} IVA {вкл. ИВА} SST {вкл. SST} KDV {вкл. КДВ} other {}}","alternativePriceAriaLabel":"Альтернативный вариант за {alternativePrice}","strikethroughAriaLabel":"Регулярно по цене {strikethroughPrice}"},{"lang":"sk","recurrenceLabel":"{recurrenceTerm, select, MONTH {/mesiac} YEAR {/rok} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {za mesiac} YEAR {za rok} other {}}","perUnitLabel":"{perUnit, select, LICENSE {za licenciu} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {za licenciu} other {}}","freeLabel":"Zadarmo","freeAriaLabel":"Zadarmo","taxExclusiveLabel":"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}","alternativePriceAriaLabel":"Prípadne za {alternativePrice}","strikethroughAriaLabel":"Pravidelne za {strikethroughPrice}"},{"lang":"sl","recurrenceLabel":"{recurrenceTerm, select, MONTH {/mesec} YEAR {/leto} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {na mesec} YEAR {na leto} other {}}","perUnitLabel":"{perUnit, select, LICENSE {na licenco} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {na licenco} other {}}","freeLabel":"Brezplačno","freeAriaLabel":"Brezplačno","taxExclusiveLabel":"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}","alternativePriceAriaLabel":"Druga možnost je: {alternativePrice}","strikethroughAriaLabel":"Redno po {strikethroughPrice}"},{"lang":"sv","recurrenceLabel":"{recurrenceTerm, select, MONTH {/mån} YEAR {/år} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {per månad} YEAR {per år} other {}}","perUnitLabel":"{perUnit, select, LICENSE {per licens} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {per licens} other {}}","freeLabel":"Kostnadsfritt","freeAriaLabel":"Kostnadsfritt","taxExclusiveLabel":"{taxTerm, select, GST {exkl. GST} VAT {exkl. moms} TAX {exkl. skatt} IVA {exkl. IVA} SST {exkl. SST} KDV {exkl. KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {inkl. GST} VAT {inkl. moms} TAX {inkl. skatt} IVA {inkl. IVA} SST {inkl. SST} KDV {inkl. KDV} other {}}","alternativePriceAriaLabel":"Alternativt för {alternativePrice}","strikethroughAriaLabel":"Normalpris {strikethroughPrice}"},{"lang":"tr","recurrenceLabel":"{recurrenceTerm, select, MONTH {/ay} YEAR {/yıl} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {(aylık)} YEAR {(yıllık)} other {}}","perUnitLabel":"{perUnit, select, LICENSE {(lisans başına)} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {(lisans başına)} other {}}","freeLabel":"Ücretsiz","freeAriaLabel":"Ücretsiz","taxExclusiveLabel":"{taxTerm, select, GST {GST hariç} VAT {KDV hariç} TAX {vergi hariç} IVA {IVA hariç} SST {SST hariç} KDV {KDV hariç} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {GST dahil} VAT {KDV dahil} TAX {vergi dahil} IVA {IVA dahil} SST {SST dahil} KDV {KDV dahil} other {}}","alternativePriceAriaLabel":"Ya da {alternativePrice}","strikethroughAriaLabel":"Standart fiyat: {strikethroughPrice}"},{"lang":"uk","recurrenceLabel":"{recurrenceTerm, select, MONTH {/міс.} YEAR {/рік} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {на місяць} YEAR {на рік} other {}}","perUnitLabel":"{perUnit, select, LICENSE {за ліцензію} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {за ліцензію} other {}}","freeLabel":"Безкоштовно","freeAriaLabel":"Безкоштовно","taxExclusiveLabel":"{taxTerm, select, GST {без GST} VAT {без ПДВ} TAX {без податку} IVA {без IVA} SST {без SST} KDV {без KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {разом із GST} VAT {разом із ПДВ} TAX {разом із податком} IVA {разом з IVA} SST {разом із SST} KDV {разом із KDV} other {}}","alternativePriceAriaLabel":"Або за {alternativePrice}","strikethroughAriaLabel":"Звичайна ціна {strikethroughPrice}"},{"lang":"zh-hans","recurrenceLabel":"{recurrenceTerm, select, MONTH {/月} YEAR {/年} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {每月} YEAR {每年} other {}}","perUnitLabel":"{perUnit, select, LICENSE {每个许可证} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {每个许可证} other {}}","freeLabel":"免费","freeAriaLabel":"免费","taxExclusiveLabel":"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}","alternativePriceAriaLabel":"或定价 {alternativePrice}","strikethroughAriaLabel":"正常价 {strikethroughPrice}"},{"lang":"zh-hant","recurrenceLabel":"{recurrenceTerm, select, MONTH {/月} YEAR {/年} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {每月} YEAR {每年} other {}}","perUnitLabel":"{perUnit, select, LICENSE {每個授權} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {每個授權} other {}}","freeLabel":"免費","freeAriaLabel":"免費","taxExclusiveLabel":"{taxTerm, select, GST {不含 GST} VAT {不含 VAT} TAX {不含稅} IVA {不含 IVA} SST {不含 SST} KDV {不含 KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {含 GST} VAT {含 VAT} TAX {含稅} IVA {含 IVA} SST {含 SST} KDV {含 KDV} other {}}","alternativePriceAriaLabel":"或者在 {alternativePrice}","strikethroughAriaLabel":"標準價格為 {strikethroughPrice}"},{"lang":"es","recurrenceLabel":"{recurrenceTerm, select, MONTH {/mes} YEAR {/año} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {al mes} YEAR {al año} other {}}","perUnitLabel":"{perUnit, select, LICENSE {por licencia} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {por licencia} other {}}","freeLabel":"Gratuito","freeAriaLabel":"Gratuito","taxExclusiveLabel":"{taxTerm, select, GST {GST no incluido} VAT {IVA no incluido} TAX {Impuestos no incluidos} IVA {IVA no incluido} SST {SST no incluido} KDV {KDV no incluido} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {GST incluido} VAT {IVA incluido} TAX {Impuestos incluidos} IVA {IVA incluido} SST {SST incluido} KDV {KDV incluido} other {}}","alternativePriceAriaLabel":"Alternativamente por {alternativePrice}","strikethroughAriaLabel":"Normalmente a {strikethroughPrice}"},{"lang":"in","recurrenceLabel":"{recurrenceTerm, select, MONTH {/bulan} YEAR {/tahun} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {per bulan} YEAR {per tahun} other {}}","perUnitLabel":"{perUnit, select, LICENSE {per lisensi} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {per lisensi} other {}}","freeLabel":"Gratis","freeAriaLabel":"Gratis","taxExclusiveLabel":"{taxTerm, select, GST {tidak termasuk PBJ} VAT {tidak termasuk PPN} TAX {tidak termasuk pajak} IVA {tidak termasuk IVA} SST {tidak termasuk SST} KDV {tidak termasuk KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {termasuk PBJ} VAT {termasuk PPN} TAX {termasuk pajak} IVA {termasuk IVA} SST {termasuk SST} KDV {termasuk KDV} other {}}","alternativePriceAriaLabel":"Atau seharga {alternativePrice}","strikethroughAriaLabel":"Normalnya seharga {strikethroughPrice}"},{"lang":"vi","recurrenceLabel":"{recurrenceTerm, select, MONTH {/tháng} YEAR {/năm} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {mỗi tháng} YEAR {mỗi năm} other {}}","perUnitLabel":"{perUnit, select, LICENSE {mỗi giấy phép} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {mỗi giấy phép} other {}}","freeLabel":"Miễn phí","freeAriaLabel":"Miễn phí","taxExclusiveLabel":"{taxTerm, select, GST {chưa bao gồm thuế hàng hóa và dịch vụ} VAT {chưa bao gồm thuế GTGT} TAX {chưa bao gồm thuế} IVA {chưa bao gồm IVA} SST {chưa bao gồm SST} KDV {chưa bao gồm KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {(đã bao gồm thuế hàng hóa và dịch vụ)} VAT {(đã bao gồm thuế GTGT)} TAX {(đã bao gồm thuế)} IVA {(đã bao gồm IVA)} SST {(đã bao gồm SST)} KDV {(đã bao gồm KDV)} other {}}","alternativePriceAriaLabel":"Giá ưu đãi {alternativePrice}","strikethroughAriaLabel":"Giá thông thường {strikethroughPrice}"},{"lang":"th","recurrenceLabel":"{recurrenceTerm, select, MONTH {/เดือน} YEAR {/ปี} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {ต่อเดือน} YEAR {ต่อปี} other {}}","perUnitLabel":"{perUnit, select, LICENSE {ต่อสิทธิ์การใช้งาน} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {ต่อสิทธิ์การใช้งาน} other {}}","freeLabel":"ฟรี","freeAriaLabel":"ฟรี","taxExclusiveLabel":"{taxTerm, select, GST {ไม่รวมภาษี GST} VAT {ไม่รวม VAT} TAX {ไม่รวมภาษี} IVA {ไม่รวม IVA} SST {ไม่รวม SST} KDV {ไม่รวม KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {รวมภาษี GST} VAT {รวม VAT} TAX {รวมภาษี} IVA {รวม IVA} SST {รวม SST} KDV {รวม KDV} other {}}","alternativePriceAriaLabel":"ราคาพิเศษ {alternativePrice}","strikethroughAriaLabel":"ราคาปกติ {strikethroughPrice}"},{"lang":"el","recurrenceLabel":"{recurrenceTerm, select, MONTH {/μήνα} YEAR {/έτος} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {κάθε μήνα} YEAR {ανά έτος} other {}}","perUnitLabel":"{perUnit, select, LICENSE {ανά άδεια χρήσης} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {ανά άδεια χρήσης} other {}}","freeLabel":"Δωρεάν","freeAriaLabel":"Δωρεάν","taxExclusiveLabel":"{taxTerm, select, GST {(μη συμπεριλαμβανομένου GST)} VAT {(μη συμπεριλαμβανομένου ΦΠΑ)} TAX {(μη συμπεριλαμβανομένου φόρο)} IVA {(μη συμπεριλαμβανομένου IVA)} SST {(μη συμπεριλαμβανομένου SST)} KDV {(μη συμπεριλαμβανομένου KDV)} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {(συμπεριλαμβανομένου του GST)} VAT {(συμπεριλαμβανομένου ΦΠΑ)} TAX {(συμπεριλαμβανομένου του φόρου)} IVA {(συμπεριλαμβανομένου του IVA)} SST {(συμπεριλαμβανομένου του SST)} KDV {(συμπεριλαμβανομένου του KDV)} other {}}","alternativePriceAriaLabel":"Διαφορετικά, {alternativePrice}","strikethroughAriaLabel":"Κανονική τιμή {strikethroughPrice}"},{"lang":"fil","recurrenceLabel":"{recurrenceTerm, select, MONTH {/buwan} YEAR {/taon} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {per buwan} YEAR {per taon} other {}}","perUnitLabel":"{perUnit, select, LICENSE {kada lisensya} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {kada lisensya} other {}}","freeLabel":"Libre","freeAriaLabel":"Libre","taxExclusiveLabel":"{taxTerm, select, GST {hindi kasama ang GST} VAT {hindi kasama ang VAT} TAX {hindi kasama ang Buwis} IVA {hindi kasama ang IVA} SST {hindi kasama ang SST} KDV {hindi kasama ang KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {kasama ang GST} VAT {kasama ang VAT} TAX {kasama ang Buwis} IVA {kasama ang IVA} SST {kasama ang SST} KDV {kasama ang KDV} other {}}","alternativePriceAriaLabel":"Alternatibong nasa halagang {alternativePrice}","strikethroughAriaLabel":"Regular na nasa halagang {strikethroughPrice}"},{"lang":"ms","recurrenceLabel":"{recurrenceTerm, select, MONTH {/bulan} YEAR {/tahun} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {per bulan} YEAR {per tahun} other {}}","perUnitLabel":"{perUnit, select, LICENSE {setiap lesen} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {setiap lesen} other {}}","freeLabel":"Percuma","freeAriaLabel":"Percuma","taxExclusiveLabel":"{taxTerm, select, GST {kecuali GST} VAT {kecuali VAT} TAX {kecuali Cukai} IVA {kecuali IVA} SST {kecuali SST} KDV {kecuali KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {termasuk GST} VAT {termasuk VAT} TAX {termasuk Cukai} IVA {termasuk IVA} SST {termasuk SST} KDV {termasuk KDV} other {}}","alternativePriceAriaLabel":"Secara alternatif pada {alternativePrice}","strikethroughAriaLabel":"Biasanya pada {strikethroughPrice}"},{"lang":"hi","recurrenceLabel":"{recurrenceTerm, select, MONTH {/माह} YEAR {/वर्ष} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {per माह} YEAR {per वर्ष} other {}}","perUnitLabel":"{perUnit, select, LICENSE {प्रति लाइसेंस} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {प्रति लाइसेंस} other {}}","freeLabel":"फ़्री","freeAriaLabel":"फ़्री","taxExclusiveLabel":"{taxTerm, select, GST {GST अतिरिक्त} VAT {VAT अतिरिक्त} TAX {कर अतिरिक्त} IVA {IVA अतिरिक्त} SST {SST अतिरिक्त} KDV {KDV अतिरिक्त} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {GST सहित} VAT {VAT सहित} TAX {कर सहित} IVA {IVA सहित} SST {SST सहित} KDV {KDV सहित} other {}}","alternativePriceAriaLabel":"वैकल्पिक रूप से इस पर {alternativePrice}","strikethroughAriaLabel":"नियमित रूप से इस पर {strikethroughPrice}"},{"lang":"iw","recurrenceLabel":"{recurrenceTerm, select, MONTH {/חודש} YEAR {/שנה} other {}}","recurrenceAriaLabel":"{recurrenceTerm, select, MONTH {לחודש} YEAR {לשנה} other {}}","perUnitLabel":"{perUnit, select, LICENSE {לרישיון} other {}}","perUnitAriaLabel":"{perUnit, select, LICENSE {לרישיון} other {}}","freeLabel":"חינם","freeAriaLabel":"חינם","taxExclusiveLabel":"{taxTerm, select, GST {ללא GST} VAT {ללא מע\"מ} TAX {ללא מס} IVA {ללא IVA} SST {ללא SST} KDV {ללא KDV} other {}}","taxInclusiveLabel":"{taxTerm, select, GST {כולל GST} VAT {כולל מע\"מ} TAX {כולל מס} IVA {כולל IVA} SST {כולל SST} KDV {כולל KDV} other {}}","alternativePriceAriaLabel":"לחלופין ב-{alternativePrice}","strikethroughAriaLabel":"באופן קבוע ב-{strikethroughPrice}"}],":type":"sheet"} diff --git a/libs/features/mas/commerce/src/index.d.ts b/libs/features/mas/commerce/src/index.d.ts index 20c92d59e8..e7372d2203 100644 --- a/libs/features/mas/commerce/src/index.d.ts +++ b/libs/features/mas/commerce/src/index.d.ts @@ -22,7 +22,7 @@ declare global { Commerce.Checkout.Settings & Commerce.Price.Settings & Commerce.Wcs.Settings, - 'locale' | 'priceLiteralsURL' | 'quantity' + 'locale' | 'quantity' > & { quantity: number; } @@ -514,8 +514,6 @@ declare global { displayRecurrence: boolean; displayTax: boolean; forceTaxExclusive: boolean; - priceLiteralsURL: string; - priceLiteralsPromise: Promise; } } diff --git a/libs/features/mas/commerce/src/inline-price.js b/libs/features/mas/commerce/src/inline-price.js index 4450e1903c..a5ba96dbd3 100644 --- a/libs/features/mas/commerce/src/inline-price.js +++ b/libs/features/mas/commerce/src/inline-price.js @@ -162,6 +162,7 @@ export class HTMLPriceSpanElement extends HTMLSpanElement { * Resolves default value of displayTax property, based on provided geo info and segments. * @returns {boolean} */ + /* c8 ignore next 26 */ resolveDisplayTaxForGeoAndSegment( country, language, @@ -192,8 +193,9 @@ export class HTMLPriceSpanElement extends HTMLSpanElement { /** * Resolves default value of displayTax property, based on provided geo info and segments extracted from offers object. * @returns {boolean} - */ - async resolveDisplayTax(service, options) { + */ + /* c8 ignore next 15 */ + async resolveDisplayTax(service, options) { const [offerSelectors] = await service.resolveOfferSelectors(options); const offers = selectOffers(await offerSelectors, options); if (offers?.length) { diff --git a/libs/features/mas/commerce/src/literals.js b/libs/features/mas/commerce/src/literals.js index c6d72752b5..7b36b805e6 100644 --- a/libs/features/mas/commerce/src/literals.js +++ b/libs/features/mas/commerce/src/literals.js @@ -1,29 +1,17 @@ -import { ERROR_MESSAGE_MISSING_LITERALS_URL } from './constants.js'; import { Defaults } from './defaults.js'; import { equalsCaseInsensitive } from './external.js'; -function generateLiteralsPromise(settings) { - if (!settings.priceLiteralsURL) { - throw new Error(ERROR_MESSAGE_MISSING_LITERALS_URL); - } - return new Promise((resolve) => { - window.fetch(settings.priceLiteralsURL).then((response) => { - response.json().then(({ data }) => { - resolve(data); - }); - }); - }); -} - /** + * Method resolves price literals for the given language from the group of price literals. + * That group is either imported from json file or it is received as a parameter (in case of unit tests). + * * @param {Commerce.Price.Settings} settings + * @param priceLiterals * @returns {Promise>} */ -export async function fetchPriceLiterals(settings) { - const priceLiteralsPromise = - settings.priceLiteralsPromise || generateLiteralsPromise(settings); +export async function getPriceLiterals(settings, priceLiterals) { //we are expecting an array of objects with lang and literals - const data = await priceLiteralsPromise; + const { data } = priceLiterals ? priceLiterals : await import('../price-literals.json'); if (Array.isArray(data)) { const find = (language) => data.find((candidate) => diff --git a/libs/features/mas/commerce/src/service.js b/libs/features/mas/commerce/src/service.js index eed7cc0eff..d8653ae373 100644 --- a/libs/features/mas/commerce/src/service.js +++ b/libs/features/mas/commerce/src/service.js @@ -4,7 +4,7 @@ import { EVENT_TYPE_READY, TAG_NAME_SERVICE } from './constants.js'; import { Defaults } from './defaults.js'; import { isFunction } from './external.js'; import { Ims } from './ims.js'; -import { fetchPriceLiterals } from './literals.js'; +import { getPriceLiterals } from './literals.js'; import { Log } from './log.js'; import { Price } from './price.js'; import { getSettings } from './settings.js'; @@ -43,7 +43,7 @@ async function activateService(config, dataProviders) { const settings = Object.freeze(getSettings(config)); // Fetch price literals try { - literals.price = await fetchPriceLiterals(settings); + literals.price = await getPriceLiterals(settings, config.commerce.priceLiterals); } catch (error) { log.warn('Price literals were not fetched:', error); } @@ -121,23 +121,18 @@ export function resetService() { /** @type {Commerce.initService} */ export function initService(getConfig, getProviders) { // Callback is provided, activate service or/and return its promise - if (isFunction(getConfig)) { - const dataProviders = isFunction(getProviders) ? getProviders() : {}; - if (dataProviders.force) resetService(); - return (HTMLWcmsCommerceElement.promise ??= activateService( - getConfig(), - dataProviders, - )); + const config = isFunction(getConfig) ? getConfig() : null; + const dataProviders = isFunction(getProviders) ? getProviders() : {}; + if (config) { + if (dataProviders.force) resetService(); + activateService(config, dataProviders).then((serviceElement) => { + // @ts-ignore + initService.resolve(serviceElement); + }); } - // Return existing promise - if (HTMLWcmsCommerceElement.promise) return HTMLWcmsCommerceElement.promise; - // Return new promise resolving on "ready" event with new instance of service - return new Promise((resolve) => { - const listener = (event) => { - resolve(event.detail); - }; - document.head.addEventListener(EVENT_TYPE_READY, listener, { - once: true, - }); + HTMLWcmsCommerceElement.promise ??= new Promise((resolve) => { + // @ts-ignore + initService.resolve = resolve; }); + return HTMLWcmsCommerceElement.promise; } diff --git a/libs/features/mas/commerce/src/settings.js b/libs/features/mas/commerce/src/settings.js index 1c47fdde58..3f3a9b795f 100644 --- a/libs/features/mas/commerce/src/settings.js +++ b/libs/features/mas/commerce/src/settings.js @@ -218,8 +218,6 @@ function getSettings(config = {}) { modal, env, forceTaxExclusive, - priceLiteralsURL: commerce.priceLiteralsURL, - priceLiteralsPromise: commerce.priceLiteralsPromise, promotionCode, quantity, wcsApiKey, diff --git a/libs/features/mas/commerce/test/checkout.test.js b/libs/features/mas/commerce/test/checkout.test.js index 55247612d3..e423c34186 100644 --- a/libs/features/mas/commerce/test/checkout.test.js +++ b/libs/features/mas/commerce/test/checkout.test.js @@ -19,7 +19,6 @@ import { mockConfig } from './mocks/config.js'; import { mockFetch } from './mocks/fetch.js'; import { mockIms, unmockIms } from './mocks/ims.js'; import { mockLana, unmockLana } from './mocks/lana.js'; -import { withLiterals } from './mocks/literals.js'; import { mockProviders } from './mocks/providers.js'; import { withWcs } from './mocks/wcs.js'; import { expect, sinon } from './utilities.js'; @@ -40,9 +39,6 @@ function mockCheckoutLink(wcsOsi, options = {}, append = true) { return element; } -/** @type {import('sinon').SinonStub} */ -let fetch; - afterEach(() => { document.body.innerHTML = ''; resetService(); @@ -51,7 +47,7 @@ afterEach(() => { }); beforeEach(async () => { - fetch = await mockFetch(withWcs, withLiterals); + await mockFetch(withWcs); mockLana(); }); @@ -66,7 +62,7 @@ describe('class "CheckoutLink"', () => { }); it('renders link with workflow step from settings', async () => { - await initService( + const commerce = await initService( mockConfig({ checkoutWorkflowStep: CheckoutWorkflowStep.SEGMENTATION, }), diff --git a/libs/features/mas/commerce/test/literals.test.js b/libs/features/mas/commerce/test/literals.test.js index c478decc64..fa05e41c44 100644 --- a/libs/features/mas/commerce/test/literals.test.js +++ b/libs/features/mas/commerce/test/literals.test.js @@ -1,44 +1,24 @@ import { Defaults } from '../src/index.js'; -import { fetchPriceLiterals } from '../src/literals.js'; - -import { mockFetch } from './mocks/fetch.js'; -import { priceLiteralsURL, withLiterals } from './mocks/literals.js'; +import { getPriceLiterals } from '../src/literals.js'; import { expect } from './utilities.js'; +import { readFile } from '@web/test-runner-commands'; -describe('function "fetchPriceLiterals"', () => { - beforeEach(async () => { - await mockFetch(withLiterals); - }); - - it('throws if settings argument does not have `priceLiteralsURL` nor `priceLiteralsPromise` property', async () => { - // @ts-ignore - expect(fetchPriceLiterals({})).eventually.be.rejected; - }); - - it('returns literals from promise', async () => { - const priceLiteralsPromise = new Promise((resolve) => - resolve([ - { - lang: 'en', - recurrenceLabel: - '{recurrenceTerm, select, MONTH {/mo} YEAR {/yr} other {}}', - }, - ]), - ); +describe('function "getPriceLiterals"', () => { + it('returns literals', async () => { + const priceLiterals = await (readFile({ path: '../price-literals.json' })); // @ts-ignore - const literals = await fetchPriceLiterals({ + const literals = await getPriceLiterals({ language: 'en', - priceLiteralsPromise, - }); + }, JSON.parse(priceLiterals)); expect(literals.lang).to.equal(Defaults.language); }); it('returns literals for default language if requested does not exist', async () => { + const priceLiterals = await (readFile({ path: '../price-literals.json' })); // @ts-ignore - const literals = await fetchPriceLiterals({ + const literals = await getPriceLiterals({ language: 'test', - priceLiteralsURL, - }); + }, JSON.parse(priceLiterals)); expect(literals.lang).to.equal(Defaults.language); }); }); diff --git a/libs/features/mas/commerce/test/placeholder.test.js b/libs/features/mas/commerce/test/placeholder.test.js index c99f115b61..8f679cbf52 100644 --- a/libs/features/mas/commerce/test/placeholder.test.js +++ b/libs/features/mas/commerce/test/placeholder.test.js @@ -14,7 +14,6 @@ import { mockLana, unmockLana } from './mocks/lana.js'; import { expect } from './utilities.js'; import { initService } from '../src/service.js'; import { mockFetch } from './mocks/fetch.js'; -import { withLiterals } from './mocks/literals.js'; let id = 1; /** @@ -62,8 +61,7 @@ describe('custom span-based placeholder', () => { document.body.innerHTML = ''; }); - before(async () => { - await mockFetch(withLiterals); + before( () => { mockLana(); }); diff --git a/libs/features/mas/commerce/test/price.test.js b/libs/features/mas/commerce/test/price.test.js index 96c1bccfa8..c35ad01e9e 100644 --- a/libs/features/mas/commerce/test/price.test.js +++ b/libs/features/mas/commerce/test/price.test.js @@ -9,7 +9,6 @@ import { initService, resetService } from '../src/service.js'; import { mockConfig } from './mocks/config.js'; import { mockFetch } from './mocks/fetch.js'; import { mockLana, unmockLana } from './mocks/lana.js'; -import { withLiterals } from './mocks/literals.js'; import snapshots from './mocks/snapshots.js'; import { withWcs } from './mocks/wcs.js'; import { expect } from './utilities.js'; @@ -25,9 +24,6 @@ function mockInlinePrice(wcsOsi = '', options = {}, append = true) { return element; } -/** @type {import('sinon').SinonStub} */ -let fetch; - afterEach(() => { document.body.innerHTML = ''; resetService(); @@ -35,7 +31,7 @@ afterEach(() => { }); beforeEach(async () => { - fetch = await mockFetch(withWcs, withLiterals); + await mockFetch(withWcs); mockLana(); }); diff --git a/libs/features/mas/commerce/test/service.test.js b/libs/features/mas/commerce/test/service.test.js index 390b99270a..9fc25f0f06 100644 --- a/libs/features/mas/commerce/test/service.test.js +++ b/libs/features/mas/commerce/test/service.test.js @@ -1,3 +1,4 @@ +import { readFile } from '@web/test-runner-commands'; import { delay } from '../src/external.js'; import { TAG_NAME_SERVICE, Defaults, init, reset } from '../src/index.js'; import { HTMLWcmsCommerceElement } from '../src//service.js'; @@ -8,11 +9,10 @@ import { mockIms, unmockIms } from './mocks/ims.js'; import { expect } from './utilities.js'; import { mockProviders } from './mocks/providers.js'; import { withWcs } from './mocks/wcs.js'; -import { withLiterals } from './mocks/literals.js'; describe('commerce service', () => { before(async () => { - await mockFetch(withWcs, withLiterals); + await mockFetch(withWcs); }); afterEach(() => { @@ -71,7 +71,11 @@ describe('commerce service', () => { describe('property "literals"', () => { it('returns "price literals" object', async () => { - const instance = await init(mockConfig()); + const priceLiterals = await (readFile({ path: '../price-literals.json' })); + const commerce = { + priceLiterals: JSON.parse(priceLiterals), + }; + const instance = await init(mockConfig(commerce)); [ 'alternativePriceAriaLabel', 'freeAriaLabel', diff --git a/libs/features/mas/commerce/test/settings.test.js b/libs/features/mas/commerce/test/settings.test.js index 4ab9f2da9c..8728ab90ff 100644 --- a/libs/features/mas/commerce/test/settings.test.js +++ b/libs/features/mas/commerce/test/settings.test.js @@ -27,8 +27,6 @@ describe('getSettings', () => { expect(getSettings()).to.deep.equal({ ...Defaults, locale: `${Defaults.language}_${Defaults.country}`, - priceLiteralsURL: undefined, - priceLiteralsPromise: undefined, quantity: [Defaults.quantity], }); }); @@ -76,8 +74,6 @@ describe('getSettings', () => { quantity: [2], wcsApiKey: 'testapikey', locale: "en_US", - priceLiteralsURL: undefined, - priceLiteralsPromise: undefined, env: "STAGE", wcsURL: WCS_STAGE_URL }); @@ -111,8 +107,6 @@ describe('getSettings', () => { env: Env.STAGE, language: 'nb', locale: 'nb_NO', - priceLiteralsURL: undefined, - priceLiteralsPromise: undefined, quantity: [Defaults.quantity], wcsApiKey, wcsURL: WCS_STAGE_URL, diff --git a/libs/features/mas/commerce/test/wcs.test.js b/libs/features/mas/commerce/test/wcs.test.js index f8f26fdfb5..f529c5753a 100644 --- a/libs/features/mas/commerce/test/wcs.test.js +++ b/libs/features/mas/commerce/test/wcs.test.js @@ -38,7 +38,7 @@ describe('resolveOfferSelectors', () => { }); it('groups WCS requests by promotion code', async () => { - let fetch = await mockFetch(withWcs); + await mockFetch(withWcs); const client = Wcs({ // @ts-ignore settings: { diff --git a/libs/features/mas/commerce/web-test-runner.config.mjs b/libs/features/mas/commerce/web-test-runner.config.mjs index 4ecfb5a6c7..b9c786fd91 100644 --- a/libs/features/mas/commerce/web-test-runner.config.mjs +++ b/libs/features/mas/commerce/web-test-runner.config.mjs @@ -93,10 +93,10 @@ export default { include: ['src/**'], exclude: ['test/mocks/**', 'test/**', '**/node_modules/**'], threshold: { - branches: 100, - functions: 100, - statements: 100, - lines: 100, + branches: 95, + functions: 94, + statements: 95, + lines: 95, }, }, debug: false, diff --git a/libs/features/mas/mas/.gitignore b/libs/features/mas/mas/.gitignore new file mode 100644 index 0000000000..0e9f82e330 --- /dev/null +++ b/libs/features/mas/mas/.gitignore @@ -0,0 +1,2 @@ +mas.json +stats.json diff --git a/libs/features/mas/mas/build.mjs b/libs/features/mas/mas/build.mjs index 8645a1dbaa..7f5be19070 100644 --- a/libs/features/mas/mas/build.mjs +++ b/libs/features/mas/mas/build.mjs @@ -1,18 +1,23 @@ import { build } from 'esbuild'; import fs from 'node:fs'; -const { metafile } = await build({ +const defaults = { alias: { react: '../mocks/react.js', }, bundle: true, - entryPoints: ['./src/mas.js'], format: 'esm', metafile: true, minify: true, - sourcemap: true, - outfile: '../../../../libs/deps/mas/mas.js', platform: 'browser', target: ['es2020'], + external: [], +}; + +let { metafile } = await build({ + ...defaults, + entryPoints: ['./src/mas.js'], + outfile: '../../../deps/mas/mas.js', }); -fs.writeFileSync('stats.json', JSON.stringify(metafile)); +fs.writeFileSync('mas.json', JSON.stringify(metafile)); + diff --git a/libs/features/mas/mas/src/mas.js b/libs/features/mas/mas/src/mas.js index 112a1db5b2..b045b7d09d 100644 --- a/libs/features/mas/mas/src/mas.js +++ b/libs/features/mas/mas/src/mas.js @@ -1,10 +1,11 @@ import { init } from '@adobe/mas-commerce'; -const { origin, searchParams } = new URL(import.meta.url); +import '@adobe/mas-web-components/src/merch-card.js'; +import '@adobe/mas-web-components/src/merch-icon.js'; +import '@adobe/mas-web-components/src/merch-datasource.js'; +const { searchParams } = new URL(import.meta.url); const locale = searchParams.get('locale') ?? 'US_en'; -const lang = searchParams.get('lang') ?? 'en'; const isStage = searchParams.get('env') === 'stage'; -const features = searchParams.get('features'); const envName = isStage ? 'stage' : 'prod'; const commerceEnv = isStage ? 'STAGE' : 'PROD'; @@ -15,12 +16,6 @@ const config = () => ({ locale: { prefix: locale }, }); -init(config); +const promise = init(config); -if (features.includes('merch-card')) { - await Promise.allSettled([ - import('../../web-components/src/merch-card.js'), - import('../../web-components/src/merch-icon.js'), - import('../../web-components/src/merch-datasource.js'), - ]); -} +export default promise; diff --git a/libs/features/mas/mocks/aem.js b/libs/features/mas/mocks/aem.js index b882f81219..c83e228b23 100644 --- a/libs/features/mas/mocks/aem.js +++ b/libs/features/mas/mocks/aem.js @@ -1,10 +1,24 @@ export async function withAem(originalFetch) { - return async ({ pathname }) => { + return async ({ pathname, searchParams }) => { if (/cf\/fragments\/search/.test(pathname)) { // TODO add conditional use case. return originalFetch( '/test/mocks/sites/cf/fragments/search/default.json', ); + } else if (/cf\/fragments/.test(pathname) && searchParams.has('path')) { + const path = searchParams.get('path'); + const item = await originalFetch( + '/test/mocks/sites/cf/fragments/search/default.json', + ) + .then((res) => res.json()) + .then(({ items }) => items.find((item) => item.path === path)); + if (item) { + return Promise.resolve({ + ok: true, + status: 200, + json: () => Promise.resolve({ items: [item] }), + }); + } } return false; }; diff --git a/libs/features/mas/mocks/config.js b/libs/features/mas/mocks/config.js index ec9639109d..5b5722b7c1 100644 --- a/libs/features/mas/mocks/config.js +++ b/libs/features/mas/mocks/config.js @@ -1,5 +1,3 @@ -import { priceLiteralsURL } from './literals.js'; - export const mockConfig = ( commerce = {}, @@ -11,7 +9,6 @@ export const mockConfig = ) => () => ({ commerce: { - priceLiteralsURL, ...commerce, }, env, diff --git a/libs/features/mas/mocks/fetch.js b/libs/features/mas/mocks/fetch.js index 596bbb68cf..c43d2222db 100644 --- a/libs/features/mas/mocks/fetch.js +++ b/libs/features/mas/mocks/fetch.js @@ -4,6 +4,9 @@ const { fetch: originalFetch } = window; export async function mockFetch(...stubs) { const mocks = await Promise.all(stubs.map((stub) => stub(originalFetch))); + mocks.forEach((mock) => { + mock.count = 0; + }); const stub = sinon.stub().callsFake(async (...args) => { const { href, pathname, searchParams } = new URL( String(args[0]), @@ -13,6 +16,7 @@ export async function mockFetch(...stubs) { for await (const mock of mocks) { found = await mock({ href, pathname, searchParams }); if (found === false) continue; + mock.count++; break; } if (found === false) { @@ -21,5 +25,5 @@ export async function mockFetch(...stubs) { return found; }); window.fetch = stub; - return stub; + return mocks; } diff --git a/libs/features/mas/mocks/literals.js b/libs/features/mas/mocks/literals.js deleted file mode 100644 index b69ae00825..0000000000 --- a/libs/features/mas/mocks/literals.js +++ /dev/null @@ -1,17 +0,0 @@ -export const priceLiteralsURL = - 'https://www.adobe.com/federal/commerce/price-literals.json'; - -export async function withLiterals(originalFetch) { - const literals = await originalFetch('/test/mocks/literals.json').then( - (res) => res.json(), - ); - return async ({ href }) => { - if (href === priceLiteralsURL) { - return Promise.resolve({ - ok: true, - json: async () => literals, - }); - } - return false; - }; -} diff --git a/libs/features/mas/mocks/literals.json b/libs/features/mas/mocks/literals.json deleted file mode 100644 index 64155c54a4..0000000000 --- a/libs/features/mas/mocks/literals.json +++ /dev/null @@ -1,502 +0,0 @@ -{ - "total": 38, - "offset": 0, - "limit": 38, - "data": [ - { - "lang": "ar", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/الشهر} YEAR {/العام} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {كل شهر} YEAR {كل عام} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {لكل ترخيص} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {لكل ترخيص} other {}}", - "freeLabel": "مجانًا", - "freeAriaLabel": "مجانًا", - "taxExclusiveLabel": "{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}", - "alternativePriceAriaLabel": "أو بدلاً من ذلك بقيمة {alternativePrice}", - "strikethroughAriaLabel": "بشكل منتظم بقيمة {strikethroughPrice}" - }, - { - "lang": "bg", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/мес.} YEAR {/год.} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {на месец} YEAR {на година} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {на лиценз} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {на лиценз} other {}}", - "freeLabel": "Безплатно", - "freeAriaLabel": "Безплатно", - "taxExclusiveLabel": "{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}", - "taxInclusiveLabel": "{incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}", - "alternativePriceAriaLabel": "Алтернативно на {alternativePrice}", - "strikethroughAriaLabel": "Редовно на {strikethroughPrice}" - }, - { - "lang": "cs", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/měsíc} YEAR {/rok} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {za měsíc} YEAR {za rok} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {za licenci} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {za licenci} other {}}", - "freeLabel": "Zdarma", - "freeAriaLabel": "Zdarma", - "taxExclusiveLabel": "{taxTerm, select, GST {bez daně ze zboží a služeb} VAT {bez DPH} TAX {bez daně} IVA {bez IVA} SST {bez SST} KDV {bez KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {včetně daně ze zboží a služeb} VAT {včetně DPH} TAX {včetně daně} IVA {včetně IVA} SST {včetně SST} KDV {včetně KDV} other {}}", - "alternativePriceAriaLabel": "Případně za {alternativePrice}", - "strikethroughAriaLabel": "Pravidelně za {strikethroughPrice}" - }, - { - "lang": "da", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/md} YEAR {/år} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {pr. måned} YEAR {pr. år} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {pr. licens} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {pr. licens} other {}}", - "freeLabel": "Gratis", - "freeAriaLabel": "Gratis", - "taxExclusiveLabel": "{taxTerm, select, GST {ekskl. GST} VAT {ekskl. moms} TAX {ekskl. skat} IVA {ekskl. IVA} SST {ekskl. SST} KDV {ekskl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {inkl. GST} VAT {inkl. moms} TAX {inkl. skat} IVA {inkl. IVA} SST {inkl. SST} KDV {inkl. KDV} other {}}", - "alternativePriceAriaLabel": "Alternativt til {alternativePrice}", - "strikethroughAriaLabel": "Normalpris {strikethroughPrice}" - }, - { - "lang": "de", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/Monat} YEAR {/Jahr} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {pro Monat} YEAR {pro Jahr} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {pro Lizenz} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {pro Lizenz} other {}}", - "freeLabel": "Kostenlos", - "freeAriaLabel": "Kostenlos", - "taxExclusiveLabel": "{taxTerm, select, GST {zzgl. GST} VAT {zzgl. MwSt.} TAX {zzgl. Steuern} IVA {zzgl. IVA} SST {zzgl. SST} KDV {zzgl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {inkl. GST} VAT {inkl. MwSt.} TAX {inkl. Steuern} IVA {inkl. IVA} SST {inkl. SST} KDV {inkl. KDV} other {}}", - "alternativePriceAriaLabel": "Alternativ: {alternativePrice}", - "strikethroughAriaLabel": "Regulär: {strikethroughPrice}" - }, - { - "lang": "en", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/mo} YEAR {/yr} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {per month} YEAR {per year} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {per license} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {per license} other {}}", - "freeLabel": "Free", - "freeAriaLabel": "Free", - "taxExclusiveLabel": "{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}", - "alternativePriceAriaLabel": "Alternatively at {alternativePrice}", - "strikethroughAriaLabel": "Regularly at {strikethroughPrice}" - }, - { - "lang": "et", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {kuus} YEAR {aastas} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {kuus} YEAR {aastas} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {litsentsi kohta} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {litsentsi kohta} other {}}", - "freeLabel": "Tasuta", - "freeAriaLabel": "Tasuta", - "taxExclusiveLabel": "{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}", - "alternativePriceAriaLabel": "Teise võimalusena hinnaga {alternativePrice}", - "strikethroughAriaLabel": "Tavahind {strikethroughPrice}" - }, - { - "lang": "fi", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/kk} YEAR {/v} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {kuukausittain} YEAR {vuosittain} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {käyttöoikeutta kohti} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {käyttöoikeutta kohti} other {}}", - "freeLabel": "Maksuton", - "freeAriaLabel": "Maksuton", - "taxExclusiveLabel": "{taxTerm, select, GST {ilman GST:tä} VAT {ilman ALV:tä} TAX {ilman veroja} IVA {ilman IVA:ta} SST {ilman SST:tä} KDV {ilman KDV:tä} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {sis. GST:n} VAT {sis. ALV:n} TAX {sis. verot} IVA {sis. IVA:n} SST {sis. SST:n} KDV {sis. KDV:n} other {}}", - "alternativePriceAriaLabel": "Vaihtoehtoisesti hintaan {alternativePrice}", - "strikethroughAriaLabel": "Säännöllisesti hintaan {strikethroughPrice}" - }, - { - "lang": "fr", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/mois} YEAR {/an} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {par mois} YEAR {par an} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {par licence} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {par licence} other {}}", - "freeLabel": "Gratuit", - "freeAriaLabel": "Gratuit", - "taxExclusiveLabel": "{taxTerm, select, GST {hors TPS} VAT {hors TVA} TAX {hors taxes} IVA {hors IVA} SST {hors SST} KDV {hors KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {TPS comprise} VAT {TVA comprise} TAX {taxes comprises} IVA {IVA comprise} SST {SST comprise} KDV {KDV comprise} other {}}", - "alternativePriceAriaLabel": "Autre prix {alternativePrice}", - "strikethroughAriaLabel": "Prix habituel {strikethroughPrice}" - }, - { - "lang": "he", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/חודש} YEAR {/שנה} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {לחודש} YEAR {לשנה} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {לרישיון} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {לרישיון} other {}}", - "freeLabel": "חינם", - "freeAriaLabel": "חינם", - "taxExclusiveLabel": "{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}", - "alternativePriceAriaLabel": "לחלופין ב-{alternativePrice}", - "strikethroughAriaLabel": "באופן קבוע ב-{strikethroughPrice}" - }, - { - "lang": "hu", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/hó} YEAR {/év} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {havonta} YEAR {évente} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {licencenként} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {licencenként} other {}}", - "freeLabel": "Ingyenes", - "freeAriaLabel": "Ingyenes", - "taxExclusiveLabel": "{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}", - "alternativePriceAriaLabel": "Másik lehetőség: {alternativePrice}", - "strikethroughAriaLabel": "Általában {strikethroughPrice} áron" - }, - { - "lang": "it", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/mese} YEAR {/anno} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {al mese} YEAR {all'anno} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {per licenza} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {per licenza} other {}}", - "freeLabel": "Gratuito", - "freeAriaLabel": "Gratuito", - "taxExclusiveLabel": "{taxTerm, select, GST {escl. GST} VAT {escl. IVA.} TAX {escl. imposte} IVA {escl. IVA} SST {escl. SST} KDV {escl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {incl. GST} VAT {incl. IVA} TAX {incl. imposte} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}", - "alternativePriceAriaLabel": "In alternativa a {alternativePrice}", - "strikethroughAriaLabel": "Regolarmente a {strikethroughPrice}" - }, - { - "lang": "ja", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/月} YEAR {/年} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {毎月} YEAR {毎年} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {ライセンスごと} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {ライセンスごと} other {}}", - "freeLabel": "無料", - "freeAriaLabel": "無料", - "taxExclusiveLabel": "{taxTerm, select, GST {GST 別} VAT {VAT 別} TAX {税別} IVA {IVA 別} SST {SST 別} KDV {KDV 別} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {GST 込} VAT {VAT 込} TAX {税込} IVA {IVA 込} SST {SST 込} KDV {KDV 込} other {}}", - "alternativePriceAriaLabel": "特別価格 : {alternativePrice}", - "strikethroughAriaLabel": "通常価格 : {strikethroughPrice}" - }, - { - "lang": "ko", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/월} YEAR {/년} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {월간} YEAR {연간} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {라이선스당} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {라이선스당} other {}}", - "freeLabel": "무료", - "freeAriaLabel": "무료", - "taxExclusiveLabel": "{taxTerm, select, GST {GST 제외} VAT {VAT 제외} TAX {세금 제외} IVA {IVA 제외} SST {SST 제외} KDV {KDV 제외} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {GST 포함} VAT {VAT 포함} TAX {세금 포함} IVA {IVA 포함} SST {SST 포함} KDV {KDV 포함} other {}}", - "alternativePriceAriaLabel": "또는 {alternativePrice}에", - "strikethroughAriaLabel": "또는 {alternativePrice}에" - }, - { - "lang": "lt", - "recurrenceLabel": "{recurrenceTerm, select, MONTH { per mėn.} YEAR { per metus} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {per mėn.} YEAR {per metus} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {už licenciją} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {už licenciją} other {}}", - "freeLabel": "Nemokamai", - "freeAriaLabel": "Nemokamai", - "taxExclusiveLabel": "{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}", - "alternativePriceAriaLabel": "Arba už {alternativePrice}", - "strikethroughAriaLabel": "Normaliai už {strikethroughPrice}" - }, - { - "lang": "lv", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {mēnesī} YEAR {gadā} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {mēnesī} YEAR {gadā} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {vienai licencei} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {vienai licencei} other {}}", - "freeLabel": "Bezmaksas", - "freeAriaLabel": "Bezmaksas", - "taxExclusiveLabel": "{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}", - "alternativePriceAriaLabel": "Alternatīvi par {alternativePrice}", - "strikethroughAriaLabel": "Regulāri par {strikethroughPrice}" - }, - { - "lang": "nb", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/mnd.} YEAR {/år} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {per måned} YEAR {per år} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {per lisens} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {per lisens} other {}}", - "freeLabel": "Fri", - "freeAriaLabel": "Fri", - "taxExclusiveLabel": "{taxTerm, select, GST {ekskl. GST} VAT {ekskl. moms} TAX {ekskl. avgift} IVA {ekskl. IVA} SST {ekskl. SST} KDV {ekskl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {inkl. GST} VAT {inkl. moms} TAX {inkl. avgift} IVA {inkl. IVA} SST {inkl. SST} KDV {inkl. KDV} other {}}", - "alternativePriceAriaLabel": "Alternativt til {alternativePrice}", - "strikethroughAriaLabel": "Regelmessig til {strikethroughPrice}" - }, - { - "lang": "nl", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/mnd} YEAR {/jr} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {per maand} YEAR {per jaar} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {per licentie} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {per licentie} other {}}", - "freeLabel": "Gratis", - "freeAriaLabel": "Gratis", - "taxExclusiveLabel": "{taxTerm, select, GST {excl. GST} VAT {excl. btw} TAX {excl. belasting} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {incl. GST} VAT {incl. btw} TAX {incl. belasting} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}", - "alternativePriceAriaLabel": "Nu {alternativePrice}", - "strikethroughAriaLabel": "Normaal {strikethroughPrice}" - }, - { - "lang": "pl", - "recurrenceLabel": "{recurrenceTerm, select, MONTH { / mies.} YEAR { / rok} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH { / miesiąc} YEAR { / rok} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {za licencję} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {za licencję} other {}}", - "freeLabel": "Bezpłatne", - "freeAriaLabel": "Bezpłatne", - "taxExclusiveLabel": "{taxTerm, select, GST {bez GST} VAT {bez VAT} TAX {netto} IVA {bez IVA} SST {bez SST} KDV {bez KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {z GST} VAT {z VAT} TAX {brutto} IVA {z IVA} SST {z SST} KDV {z KDV} other {}}", - "alternativePriceAriaLabel": "Lub za {alternativePrice}", - "strikethroughAriaLabel": "Cena zwykła: {strikethroughPrice}" - }, - { - "lang": "pt", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/mês} YEAR {/ano} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {por mês} YEAR {por ano} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {por licença} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {por licença} other {}}", - "freeLabel": "Gratuito", - "freeAriaLabel": "Gratuito", - "taxExclusiveLabel": "{taxTerm, select, GST {ICMS não incluso} VAT {IVA não incluso} TAX {impostos não inclusos} IVA {IVA não incluso} SST { SST não incluso} KDV {KDV não incluso} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {ICMS incluso} VAT {IVA incluso} TAX {impostos inclusos} IVA {IVA incluso} SST {SST incluso} KDV {KDV incluso} other {}}", - "alternativePriceAriaLabel": "Ou a {alternativePrice}", - "strikethroughAriaLabel": "Preço normal: {strikethroughPrice}" - }, - { - "lang": "ro", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/lună} YEAR {/an} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {pe lună} YEAR {pe an} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {pe licență} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {pe licență} other {}}", - "freeLabel": "Gratuit", - "freeAriaLabel": "Gratuit", - "taxExclusiveLabel": "{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}", - "alternativePriceAriaLabel": "Alternativ, la {alternativePrice}", - "strikethroughAriaLabel": "În mod normal, la {strikethroughPrice}" - }, - { - "lang": "ru", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/мес.} YEAR {/г.} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {в месяц} YEAR {в год} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {за лицензию} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {за лицензию} other {}}", - "freeLabel": "Бесплатно", - "freeAriaLabel": "Бесплатно", - "taxExclusiveLabel": "{taxTerm, select, GST {искл. налог на товары и услуги} VAT {искл. НДС} TAX {искл. налог} IVA {искл. ИВА} SST {искл. SST} KDV {искл. КДВ} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {вкл. налог на товары и услуги} VAT {вкл. НДС} TAX {вкл. налог} IVA {вкл. ИВА} SST {вкл. SST} KDV {вкл. КДВ} other {}}", - "alternativePriceAriaLabel": "Альтернативный вариант за {alternativePrice}", - "strikethroughAriaLabel": "Регулярно по цене {strikethroughPrice}" - }, - { - "lang": "sk", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/mesiac} YEAR {/rok} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {za mesiac} YEAR {za rok} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {za licenciu} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {za licenciu} other {}}", - "freeLabel": "Zadarmo", - "freeAriaLabel": "Zadarmo", - "taxExclusiveLabel": "{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}", - "alternativePriceAriaLabel": "Prípadne za {alternativePrice}", - "strikethroughAriaLabel": "Pravidelne za {strikethroughPrice}" - }, - { - "lang": "sl", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/mesec} YEAR {/leto} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {na mesec} YEAR {na leto} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {na licenco} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {na licenco} other {}}", - "freeLabel": "Brezplačno", - "freeAriaLabel": "Brezplačno", - "taxExclusiveLabel": "{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}", - "alternativePriceAriaLabel": "Druga možnost je: {alternativePrice}", - "strikethroughAriaLabel": "Redno po {strikethroughPrice}" - }, - { - "lang": "sv", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/mån} YEAR {/år} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {per månad} YEAR {per år} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {per licens} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {per licens} other {}}", - "freeLabel": "Kostnadsfritt", - "freeAriaLabel": "Kostnadsfritt", - "taxExclusiveLabel": "{taxTerm, select, GST {exkl. GST} VAT {exkl. moms} TAX {exkl. skatt} IVA {exkl. IVA} SST {exkl. SST} KDV {exkl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {inkl. GST} VAT {inkl. moms} TAX {inkl. skatt} IVA {inkl. IVA} SST {inkl. SST} KDV {inkl. KDV} other {}}", - "alternativePriceAriaLabel": "Alternativt för {alternativePrice}", - "strikethroughAriaLabel": "Normalpris {strikethroughPrice}" - }, - { - "lang": "tr", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/ay} YEAR {/yıl} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {(aylık)} YEAR {(yıllık)} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {(lisans başına)} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {(lisans başına)} other {}}", - "freeLabel": "Ücretsiz", - "freeAriaLabel": "Ücretsiz", - "taxExclusiveLabel": "{taxTerm, select, GST {GST hariç} VAT {KDV hariç} TAX {vergi hariç} IVA {IVA hariç} SST {SST hariç} KDV {KDV hariç} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {GST dahil} VAT {KDV dahil} TAX {vergi dahil} IVA {IVA dahil} SST {SST dahil} KDV {KDV dahil} other {}}", - "alternativePriceAriaLabel": "Ya da {alternativePrice}", - "strikethroughAriaLabel": "Standart fiyat: {strikethroughPrice}" - }, - { - "lang": "uk", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/міс.} YEAR {/рік} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {на місяць} YEAR {на рік} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {за ліцензію} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {за ліцензію} other {}}", - "freeLabel": "Безкоштовно", - "freeAriaLabel": "Безкоштовно", - "taxExclusiveLabel": "{taxTerm, select, GST {без GST} VAT {без ПДВ} TAX {без податку} IVA {без IVA} SST {без SST} KDV {без KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {разом із GST} VAT {разом із ПДВ} TAX {разом із податком} IVA {разом з IVA} SST {разом із SST} KDV {разом із KDV} other {}}", - "alternativePriceAriaLabel": "Або за {alternativePrice}", - "strikethroughAriaLabel": "Звичайна ціна {strikethroughPrice}" - }, - { - "lang": "zh-hans", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/月} YEAR {/年} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {每月} YEAR {每年} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {每个许可证} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {每个许可证} other {}}", - "freeLabel": "免费", - "freeAriaLabel": "免费", - "taxExclusiveLabel": "{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}", - "alternativePriceAriaLabel": "或定价 {alternativePrice}", - "strikethroughAriaLabel": "正常价 {strikethroughPrice}" - }, - { - "lang": "zh-hant", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/月} YEAR {/年} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {每月} YEAR {每年} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {每個授權} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {每個授權} other {}}", - "freeLabel": "免費", - "freeAriaLabel": "免費", - "taxExclusiveLabel": "{taxTerm, select, GST {不含 GST} VAT {不含 VAT} TAX {不含稅} IVA {不含 IVA} SST {不含 SST} KDV {不含 KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {含 GST} VAT {含 VAT} TAX {含稅} IVA {含 IVA} SST {含 SST} KDV {含 KDV} other {}}", - "alternativePriceAriaLabel": "或者在 {alternativePrice}", - "strikethroughAriaLabel": "標準價格為 {strikethroughPrice}" - }, - { - "lang": "es", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/mes} YEAR {/año} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {al mes} YEAR {al año} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {por licencia} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {por licencia} other {}}", - "freeLabel": "Gratuito", - "freeAriaLabel": "Gratuito", - "taxExclusiveLabel": "{taxTerm, select, GST {GST no incluido} VAT {IVA no incluido} TAX {Impuestos no incluidos} IVA {IVA no incluido} SST {SST no incluido} KDV {KDV no incluido} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {GST incluido} VAT {IVA incluido} TAX {Impuestos incluidos} IVA {IVA incluido} SST {SST incluido} KDV {KDV incluido} other {}}", - "alternativePriceAriaLabel": "Alternativamente por {alternativePrice}", - "strikethroughAriaLabel": "Normalmente a {strikethroughPrice}" - }, - { - "lang": "in", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/bulan} YEAR {/tahun} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {per bulan} YEAR {per tahun} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {per lisensi} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {per lisensi} other {}}", - "freeLabel": "Gratis", - "freeAriaLabel": "Gratis", - "taxExclusiveLabel": "{taxTerm, select, GST {tidak termasuk PBJ} VAT {tidak termasuk PPN} TAX {tidak termasuk pajak} IVA {tidak termasuk IVA} SST {tidak termasuk SST} KDV {tidak termasuk KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {termasuk PBJ} VAT {termasuk PPN} TAX {termasuk pajak} IVA {termasuk IVA} SST {termasuk SST} KDV {termasuk KDV} other {}}", - "alternativePriceAriaLabel": "Atau seharga {alternativePrice}", - "strikethroughAriaLabel": "Normalnya seharga {strikethroughPrice}" - }, - { - "lang": "vi", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/tháng} YEAR {/năm} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {mỗi tháng} YEAR {mỗi năm} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {mỗi giấy phép} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {mỗi giấy phép} other {}}", - "freeLabel": "Miễn phí", - "freeAriaLabel": "Miễn phí", - "taxExclusiveLabel": "{taxTerm, select, GST {chưa bao gồm thuế hàng hóa và dịch vụ} VAT {chưa bao gồm thuế GTGT} TAX {chưa bao gồm thuế} IVA {chưa bao gồm IVA} SST {chưa bao gồm SST} KDV {chưa bao gồm KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {(đã bao gồm thuế hàng hóa và dịch vụ)} VAT {(đã bao gồm thuế GTGT)} TAX {(đã bao gồm thuế)} IVA {(đã bao gồm IVA)} SST {(đã bao gồm SST)} KDV {(đã bao gồm KDV)} other {}}", - "alternativePriceAriaLabel": "Giá ưu đãi {alternativePrice}", - "strikethroughAriaLabel": "Giá thông thường {strikethroughPrice}" - }, - { - "lang": "th", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/เดือน} YEAR {/ปี} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {ต่อเดือน} YEAR {ต่อปี} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {ต่อสิทธิ์การใช้งาน} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {ต่อสิทธิ์การใช้งาน} other {}}", - "freeLabel": "ฟรี", - "freeAriaLabel": "ฟรี", - "taxExclusiveLabel": "{taxTerm, select, GST {ไม่รวมภาษี GST} VAT {ไม่รวม VAT} TAX {ไม่รวมภาษี} IVA {ไม่รวม IVA} SST {ไม่รวม SST} KDV {ไม่รวม KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {รวมภาษี GST} VAT {รวม VAT} TAX {รวมภาษี} IVA {รวม IVA} SST {รวม SST} KDV {รวม KDV} other {}}", - "alternativePriceAriaLabel": "ราคาพิเศษ {alternativePrice}", - "strikethroughAriaLabel": "ราคาปกติ {strikethroughPrice}" - }, - { - "lang": "el", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/μήνα} YEAR {/έτος} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {κάθε μήνα} YEAR {ανά έτος} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {ανά άδεια χρήσης} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {ανά άδεια χρήσης} other {}}", - "freeLabel": "Δωρεάν", - "freeAriaLabel": "Δωρεάν", - "taxExclusiveLabel": "{taxTerm, select, GST {(μη συμπεριλαμβανομένου GST)} VAT {(μη συμπεριλαμβανομένου ΦΠΑ)} TAX {(μη συμπεριλαμβανομένου φόρο)} IVA {(μη συμπεριλαμβανομένου IVA)} SST {(μη συμπεριλαμβανομένου SST)} KDV {(μη συμπεριλαμβανομένου KDV)} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {(συμπεριλαμβανομένου του GST)} VAT {(συμπεριλαμβανομένου ΦΠΑ)} TAX {(συμπεριλαμβανομένου του φόρου)} IVA {(συμπεριλαμβανομένου του IVA)} SST {(συμπεριλαμβανομένου του SST)} KDV {(συμπεριλαμβανομένου του KDV)} other {}}", - "alternativePriceAriaLabel": "Διαφορετικά, {alternativePrice}", - "strikethroughAriaLabel": "Κανονική τιμή {strikethroughPrice}" - }, - { - "lang": "fil", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/buwan} YEAR {/taon} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {per buwan} YEAR {per taon} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {kada lisensya} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {kada lisensya} other {}}", - "freeLabel": "Libre", - "freeAriaLabel": "Libre", - "taxExclusiveLabel": "{taxTerm, select, GST {hindi kasama ang GST} VAT {hindi kasama ang VAT} TAX {hindi kasama ang Buwis} IVA {hindi kasama ang IVA} SST {hindi kasama ang SST} KDV {hindi kasama ang KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {kasama ang GST} VAT {kasama ang VAT} TAX {kasama ang Buwis} IVA {kasama ang IVA} SST {kasama ang SST} KDV {kasama ang KDV} other {}}", - "alternativePriceAriaLabel": "Alternatibong nasa halagang {alternativePrice}", - "strikethroughAriaLabel": "Regular na nasa halagang {strikethroughPrice}" - }, - { - "lang": "ms", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/bulan} YEAR {/tahun} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {per bulan} YEAR {per tahun} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {setiap lesen} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {setiap lesen} other {}}", - "freeLabel": "Percuma", - "freeAriaLabel": "Percuma", - "taxExclusiveLabel": "{taxTerm, select, GST {kecuali GST} VAT {kecuali VAT} TAX {kecuali Cukai} IVA {kecuali IVA} SST {kecuali SST} KDV {kecuali KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {termasuk GST} VAT {termasuk VAT} TAX {termasuk Cukai} IVA {termasuk IVA} SST {termasuk SST} KDV {termasuk KDV} other {}}", - "alternativePriceAriaLabel": "Secara alternatif pada {alternativePrice}", - "strikethroughAriaLabel": "Biasanya pada {strikethroughPrice}" - }, - { - "lang": "hi", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/माह} YEAR {/वर्ष} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {per माह} YEAR {per वर्ष} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {प्रति लाइसेंस} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {प्रति लाइसेंस} other {}}", - "freeLabel": "फ़्री", - "freeAriaLabel": "फ़्री", - "taxExclusiveLabel": "{taxTerm, select, GST {GST अतिरिक्त} VAT {VAT अतिरिक्त} TAX {कर अतिरिक्त} IVA {IVA अतिरिक्त} SST {SST अतिरिक्त} KDV {KDV अतिरिक्त} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {GST सहित} VAT {VAT सहित} TAX {कर सहित} IVA {IVA सहित} SST {SST सहित} KDV {KDV सहित} other {}}", - "alternativePriceAriaLabel": "वैकल्पिक रूप से इस पर {alternativePrice}", - "strikethroughAriaLabel": "नियमित रूप से इस पर {strikethroughPrice}" - }, - { - "lang": "iw", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/חודש} YEAR {/שנה} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {לחודש} YEAR {לשנה} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {לרישיון} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {לרישיון} other {}}", - "freeLabel": "חינם", - "freeAriaLabel": "חינם", - "taxExclusiveLabel": "{taxTerm, select, GST {ללא GST} VAT {ללא מע\"מ} TAX {ללא מס} IVA {ללא IVA} SST {ללא SST} KDV {ללא KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {כולל GST} VAT {כולל מע\"מ} TAX {כולל מס} IVA {כולל IVA} SST {כולל SST} KDV {כולל KDV} other {}}", - "alternativePriceAriaLabel": "לחלופין ב-{alternativePrice}", - "strikethroughAriaLabel": "באופן קבוע ב-{strikethroughPrice}" - } - ], - ":type": "sheet" -} diff --git a/libs/features/mas/mocks/sites/cf/fragments/search/default.json b/libs/features/mas/mocks/sites/cf/fragments/search/default.json index acf15662bd..cf638313f1 100644 --- a/libs/features/mas/mocks/sites/cf/fragments/search/default.json +++ b/libs/features/mas/mocks/sites/cf/fragments/search/default.json @@ -26,33 +26,25 @@ "name": "type", "type": "enumeration", "multiple": false, - "values": [ - "ccd-action" - ] + "values": ["ccd-action"] }, { "name": "icon", "type": "text", "multiple": true, - "values": [ - "/test/mocks/img/creative-cloud.svg" - ] + "values": ["/test/mocks/img/creative-cloud.svg"] }, { "name": "name", "type": "text", "multiple": false, - "values": [ - "creative-cloud" - ] + "values": ["creative-cloud"] }, { "name": "title", "type": "text", "multiple": false, - "values": [ - "Creative Cloud All Apps" - ] + "values": ["Creative Cloud All Apps"] }, { "name": "link", @@ -82,17 +74,13 @@ "name": "secure", "type": "boolean", "multiple": false, - "values": [ - true - ] + "values": [true] }, { "name": "stock", "type": "boolean", "multiple": false, - "values": [ - true - ] + "values": [true] }, { "name": "ctas", @@ -144,33 +132,25 @@ "name": "type", "type": "enumeration", "multiple": false, - "values": [ - "ccd-action" - ] + "values": ["ccd-action"] }, { "name": "icon", "type": "text", "multiple": true, - "values": [ - "/test/mocks/img/photoshop.svg" - ] + "values": ["/test/mocks/img/photoshop.svg"] }, { "name": "name", "type": "text", "multiple": false, - "values": [ - "photoshop" - ] + "values": ["photoshop"] }, { "name": "title", "type": "text", "multiple": false, - "values": [ - "Photoshop" - ] + "values": ["Photoshop"] }, { "name": "link", @@ -200,17 +180,13 @@ "name": "secure", "type": "boolean", "multiple": false, - "values": [ - true - ] + "values": [true] }, { "name": "stock", "type": "boolean", "multiple": false, - "values": [ - true - ] + "values": [true] }, { "name": "ctas", @@ -235,6 +211,146 @@ "validationStatus": [], "fieldTags": [], "etag": "\"343f00d61244effce6e55c31cc90d4f9\"" + }, + { + "path": "/content/dam/sandbox/mas/special-offers-students-and-teachers", + "title": "Special Offers Students and Teachers", + "description": "", + "id": "64382bf2-6308-4245-85c6-ea43e0edb287", + "created": { + "at": "2024-08-15T21:48:42.597Z", + "by": "johndoe@adobe.com", + "fullName": "John Doe" + }, + "modified": { + "at": "2024-08-27T11:58:39.08Z", + "by": "johndoe@adobe.com", + "fullName": "John Doe" + }, + "published": { + "at": "2024-08-26T09:22:58.047Z", + "by": "johndoe@adobe.com", + "fullName": "John Doe" + }, + "status": "MODIFIED", + "fields": [ + { + "name": "variant", + "type": "enumeration", + "multiple": false, + "locked": false, + "values": ["special-offers"] + }, + { + "name": "icon", + "type": "text", + "multiple": true, + "locked": false, + "values": [] + }, + { + "name": "name", + "type": "text", + "multiple": false, + "locked": false, + "values": ["Special Offers Students and Teachers"] + }, + { + "name": "title", + "type": "text", + "multiple": false, + "locked": false, + "values": ["Individuals"] + }, + { + "name": "link", + "type": "text", + "multiple": false, + "locked": false, + "values": [] + }, + { + "name": "backgroundImage", + "type": "text", + "multiple": false, + "locked": false, + "values": [ + "/test/img/special-offers-img2.png" + ] + }, + { + "name": "prices", + "type": "long-text", + "multiple": false, + "locked": false, + "mimeType": "text/html", + "values": [ + "

Save over 30% on Creative Cloud All Apps.

" + ] + }, + { + "name": "description", + "type": "long-text", + "multiple": false, + "locked": false, + "mimeType": "text/html", + "values": [ + "

Join us for the creative event of the year Oct 14-16. Register by August 31 and save US$200 on a full conference pass.

See terms

" + ] + }, + { + "name": "secure", + "type": "boolean", + "multiple": false, + "locked": false, + "values": [false] + }, + { + "name": "stock", + "type": "boolean", + "multiple": false, + "locked": false, + "values": [false] + }, + { + "name": "ctas", + "type": "long-text", + "multiple": false, + "locked": false, + "mimeType": "text/html", + "values": [ + "

Buy now

" + ] + }, + { + "name": "xlg", + "type": "text", + "multiple": false, + "locked": false, + "values": ["photoshop-lapsed"] + } + ], + "variations": [], + "tags": [], + "references": [], + "model": { + "id": "L2NvbmYvc2FuZGJveC9zZXR0aW5ncy9kYW0vY2ZtL21vZGVscy9tZXJjaC1jYXJk", + "path": "/conf/sandbox/settings/dam/cfm/models/merch-card", + "name": "Merch Card", + "title": "Merch Card", + "description": "content for adobe.com merch-card web component." + }, + "validationStatus": [ + { + "property": "fields.description.values[0].", + "message": "has unsafe html content" + }, + { + "property": "fields.ctas.values[0].", + "message": "has unsafe html content" + } + ], + "fieldTags": [] } ] } diff --git a/libs/features/mas/web-components/README.md b/libs/features/mas/web-components/README.md index 411d223e70..3f622ef29d 100644 --- a/libs/features/mas/web-components/README.md +++ b/libs/features/mas/web-components/README.md @@ -70,12 +70,6 @@ To preview all available 'merch-card' components, open `http://localhost:2023`, Please refer to the main tacocat.js/README.md for more details. -#### On Milo - -By default, Milo will load `merch-*.js` from `/libs/deps`
-We will use the Redirector extension for [Chrome](https://chrome.google.com/webstore/detail/redirector/ocgpenflpmgnfapjedencafcfakcekcd)/[Firefox](https://addons.mozilla.org/en-US/firefox/addon/redirector/) for redirecting those files to web-test-runner on the port `2023`.
-Please import `./redirector.json` and toggle `Debug commerce web components` rule. - ### Build run ``` diff --git a/libs/features/mas/web-components/build.mjs b/libs/features/mas/web-components/build.mjs index 303a97ff90..5283a4bc9a 100644 --- a/libs/features/mas/web-components/build.mjs +++ b/libs/features/mas/web-components/build.mjs @@ -14,7 +14,7 @@ async function buildLitComponent(name) { platform: 'browser', outfile: `${outfolder}/${name}.js`, plugins: [rewriteImports()], - sourcemap: true, + // sourcemap: true, }); writeFileSync(`../../../../libs/deps/mas/${name}.json`, JSON.stringify(metafile)); @@ -27,12 +27,10 @@ Promise.all([ inject: [ './src/merch-card.js', './src/merch-icon.js', - './src/merch-datasource.js', ], format: 'esm', minify: true, outfile: `${outfolder}/merch-card.js`, - sourcemap: true, plugins: [rewriteImports()], }), build({ @@ -53,20 +51,13 @@ Promise.all([ plugins: [rewriteImports()], outfile: `${outfolder}/merch-card-collection.js`, }), - build({ - bundle: true, - entryPoints: ['./src/plans-modal.js'], - format: 'esm', - plugins: [rewriteImports()], - outfile: `${outfolder}/plans-modal.js`, - }), build({ entryPoints: ['./src/sidenav/merch-sidenav.js'], bundle: true, minify: true, outfile: `${outfolder}/merch-sidenav.js`, format: 'esm', - plugins: [rewriteImports()], + plugins: [rewriteImportsToLibsFolder()], external: ['lit'], }), buildLitComponent('merch-icon'), @@ -77,6 +68,7 @@ Promise.all([ buildLitComponent('merch-twp-d2p'), buildLitComponent('merch-whats-included'), buildLitComponent('merch-mnemonic-list'), + buildLitComponent('merch-datasource'), ]).catch(() => process.exit(1)); function rewriteImports(rew) { @@ -85,10 +77,24 @@ function rewriteImports(rew) { setup(build) { build.onResolve({ filter: /^lit(\/.*)?$/ }, (args) => { return { - path: '/libs/deps/lit-all.min.js', + path: '../lit-all.min.js', external: true, }; }); }, }; } + +function rewriteImportsToLibsFolder(rew) { + return { + name: 'rewrite-imports-to-libs-folder', + setup(build) { + build.onResolve({ filter: /^lit(\/.*)?$/ }, (args) => { + return { + path: '/libs/deps/lit-all.min.js', + external: true, + }; + }); + }, + }; +} diff --git a/libs/features/mas/web-components/package.json b/libs/features/mas/web-components/package.json index c3df957214..aeffa8ef91 100644 --- a/libs/features/mas/web-components/package.json +++ b/libs/features/mas/web-components/package.json @@ -14,7 +14,7 @@ "scripts": { "build": "node build.mjs", "test": "wtr --config ./web-test-runner.config.mjs --coverage --watch --debug", - "test:ci": "wtr --config ./web-test-runner.config.mjs" + "test:ci": "wtr --config ./web-test-runner.config.mjs --coverage" }, "dependencies": { }, diff --git a/libs/features/mas/web-components/redirector.json b/libs/features/mas/web-components/redirector.json deleted file mode 100644 index 938d3b0fc6..0000000000 --- a/libs/features/mas/web-components/redirector.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "createdBy": "Redirector v3.5.3", - "createdAt": "2023-09-19T09:47:16.033Z", - "redirects": [ - { - "description": "Debug commerce web components", - "exampleUrl": "https://www.adobe.com/libs/deps/mas/merch-card.js", - "exampleResult": "http://localhost:2023/src/merch-card.js", - "error": null, - "includePattern": "/libs/deps/(merch.*)", - "excludePattern": "", - "patternDesc": "", - "redirectUrl": "http://localhost:2023/src/$1", - "patternType": "R", - "processMatches": "noProcessing", - "disabled": false, - "grouped": false, - "appliesTo": [ - "stylesheet", - "script", - "xmlhttprequest", - "other" - ] - }, - { - "description": "Debug commerce web components", - "exampleUrl": "https://www.adobe.com/libs/deps/mas/merch-offer-select.js", - "exampleResult": "http://localhost:2023/src/merch-offer-select.js", - "error": null, - "includePattern": "/libs/deps/(merch.*)", - "excludePattern": "", - "patternDesc": "", - "redirectUrl": "http://localhost:2023/src/$1", - "patternType": "R", - "processMatches": "noProcessing", - "disabled": false, - "grouped": false, - "appliesTo": [ - "stylesheet", - "script", - "xmlhttprequest", - "other" - ] - }, - { - "description": "Debug commerce web components", - "exampleUrl": "https://www.adobe.com/libs/deps/mas/merch-offer.js", - "exampleResult": "http://localhost:2023/src/merch-offer.js", - "error": null, - "includePattern": "/libs/deps/(merch.*)", - "excludePattern": "", - "patternDesc": "", - "redirectUrl": "http://localhost:2023/src/$1", - "patternType": "R", - "processMatches": "noProcessing", - "disabled": false, - "grouped": false, - "appliesTo": [ - "stylesheet", - "script", - "xmlhttprequest", - "other" - ] - } - ] -} diff --git a/libs/features/mas/web-components/src/aem.js b/libs/features/mas/web-components/src/aem.js index 27d8d027b6..6a19d6c27c 100644 --- a/libs/features/mas/web-components/src/aem.js +++ b/libs/features/mas/web-components/src/aem.js @@ -1,63 +1,296 @@ -const accessToken = localStorage.getItem('masAccessToken'); - -const headers = { - Authorization: `Bearer ${accessToken}`, - pragma: 'no-cache', - 'cache-control': 'no-cache', -}; - -/** - * Search for content fragments - * @param {Object} params - The search options - * @param {string} [params.path] - The path to search in - * @param {string} [params.query] - The search query - * @returns {Promise} - A promise that resolves to an array of search results - */ -async function fragmentSearch({ path, query }) { - const filter = {}; - if (path) { - filter.path = path; - } - if (query) { - filter.fullText = { - text: encodeURIComponent(query), - queryMode: 'EXACT_WORDS', +import { wait } from './utils.js'; + +const NETWORK_ERROR_MESSAGE = 'Network error'; + +class AEM { + #author; + constructor(bucket) { + this.#author = /^author-/.test(bucket); + const baseUrl = `https://${bucket}.adobeaemcloud.com`; + const sitesUrl = `${baseUrl}/adobe/sites`; + this.cfFragmentsUrl = `${sitesUrl}/cf/fragments`; + this.cfSearchUrl = `${this.cfFragmentsUrl}/search`; + this.cfPublishUrl = `${this.cfFragmentsUrl}/publish`; + this.wcmcommandUrl = `${baseUrl}/bin/wcmcommand`; + this.csrfTokenUrl = `${baseUrl}/libs/granite/csrf/token.json`; + + this.headers = { + // IMS users might not have all the permissions, token in the sessionStorage is a temporary workaround + Authorization: `Bearer ${sessionStorage.getItem('masAccessToken') ?? window.adobeid?.authorize?.()}`, + pragma: 'no-cache', + 'cache-control': 'no-cache', }; } - const searchParams = new URLSearchParams({ - query: JSON.stringify({ filter }), - }).toString(); - return fetch(`${this.cfSearchUrl}?${searchParams}`, { - headers, - }) - .then((res) => res.json()) - .then(({ items }) => items); -} -async function getCfByPath(path) { - return fetch(`${this.cfFragmentsUrl}?path=${path}`, { - headers, - }) - .then((res) => res.json()) - .then(({ items: [item] }) => item); -} + async getCsrfToken() { + const response = await fetch(this.csrfTokenUrl, { + headers: this.headers, + }).catch((err) => { + throw new Error(`${NETWORK_ERROR_MESSAGE}: ${err.message}`); + }); + if (!response.ok) { + throw new Error( + `Failed to get CSRF token: ${response.status} ${response.statusText}`, + ); + } + const { token } = await response.json(); + return token; + } + + /** + * Search for content fragments + * @param {Object} params - The search options + * @param {string} [params.path] - The path to search in + * @param {string} [params.query] - The search query + * @param {string} [params.variant] - The variant to filter by + * @returns {Promise} - A promise that resolves to an array of search results + */ + async searchFragment({ path, query, variant }) { + const filter = {}; + if (path) { + filter.path = path; + } + if (query) { + filter.fullText = { + text: encodeURIComponent(query), + queryMode: 'EXACT_WORDS', + }; + } + + const searchParams = new URLSearchParams({ + query: JSON.stringify({ filter }), + }).toString(); + const response = await fetch(`${this.cfSearchUrl}?${searchParams}`, { + headers: this.headers, + }).catch((err) => { + throw new Error(`${NETWORK_ERROR_MESSAGE}: ${err.message}`); + }); + if (!response.ok) { + throw new Error( + `Search failed: ${response.status} ${response.statusText}`, + ); + } + const json = await response.json(); + let items = json.items; + if (variant) { + items = items.filter((item) => { + const [itemVariant] = item.fields.find( + (field) => field.name === 'variant', + )?.values; + return itemVariant === variant; + }); + } + return items; + } + + /** + * Get fragment by path + * @param {string} path fragment path + * @returns {Promise} the raw fragment item + */ + async getFragmentByPath(path) { + const headers = this.#author ? this.headers : {}; + const response = await fetch(`${this.cfFragmentsUrl}?path=${path}`, { + headers, + }).catch((err) => { + throw new Error(`${NETWORK_ERROR_MESSAGE}: ${err.message}`); + }); + if (!response.ok) { + throw new Error( + `Failed to get fragment: ${response.status} ${response.statusText}`, + ); + } + const { items } = await response.json(); + if (!items || items.length === 0) { + throw new Error('Fragment not found'); + } + return items[0]; + } + + async getFragment(res) { + const eTag = res.headers.get('Etag'); + const fragment = await res.json(); + fragment.etag = eTag; + return fragment; + } + + /** + * Get fragment by ID + * @param {string} id fragment id + * @returns {Promise} the raw fragment item + */ + async getFragmentById(id) { + const response = await fetch(`${this.cfFragmentsUrl}/${id}`, { + headers: this.headers, + }); + if (!response.ok) { + throw new Error( + `Failed to get fragment: ${response.status} ${response.statusText}`, + ); + } + return await this.getFragment(response); + } + + /** + * Save given fragment + * @param {Object} fragment + * @returns {Promise} the updated fragment + */ + async saveFragment(fragment) { + const { title, fields } = fragment; + const response = await fetch(`${this.cfFragmentsUrl}/${fragment.id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + 'If-Match': fragment.etag, + ...this.headers, + }, + body: JSON.stringify({ title, fields }), + }).catch((err) => { + throw new Error(`${NETWORK_ERROR_MESSAGE}: ${err.message}`); + }); + if (!response.ok) { + throw new Error( + `Failed to save fragment: ${response.status} ${response.statusText}`, + ); + } + return await this.getFragment(response); + } + + /** + * Copy a content fragment using the AEM classic API + * @param {Object} fragment + * @returns {Promise} the copied fragment + */ + async copyFragmentClassic(fragment) { + const csrfToken = await this.getCsrfToken(); + let parentPath = fragment.path.split('/').slice(0, -1).join('/'); + const formData = new FormData(); + formData.append('cmd', 'copyPage'); + formData.append('srcPath', fragment.path); + formData.append('destParentPath', parentPath); + formData.append('shallow', 'false'); + formData.append('_charset_', 'UTF-8'); + + const res = await fetch(this.wcmcommandUrl, { + method: 'POST', + headers: { + ...this.headers, + 'csrf-token': csrfToken, + }, + body: formData, + }).catch((err) => { + throw new Error(`${NETWORK_ERROR_MESSAGE}: ${err.message}`); + }); + if (!res.ok) { + throw new Error( + `Failed to copy fragment: ${res.status} ${res.statusText}`, + ); + } + const responseText = await res.text(); + const parser = new DOMParser(); + const doc = parser.parseFromString(responseText, 'text/html'); + const message = doc.getElementById('Message'); + const newPath = message?.textContent.trim(); + if (!newPath) { + throw new Error('Failed to extract new path from copy response'); + } + await wait(); // give AEM time to process the copy + let newFragment = await this.getFragmentByPath(newPath); + if (newFragment) { + newFragment = await this.getFragmentById(newFragment.id); + } + return newFragment; + } + + /** + * Publish a fragment + * @param {Object} fragment + * @returns {Promise} + */ + async publishFragment(fragment) { + const response = await fetch(this.cfPublishUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'If-Match': fragment.etag, + ...this.headers, + }, + body: JSON.stringify({ + paths: [fragment.path], + filterReferencesByStatus: ['DRAFT', 'UNPUBLISHED'], + workflowModelId: + '/var/workflow/models/scheduled_activation_with_references', + }), + }).catch((err) => { + throw new Error(`${NETWORK_ERROR_MESSAGE}: ${err.message}`); + }); + if (!response.ok) { + throw new Error( + `Failed to publish fragment: ${response.status} ${response.statusText}`, + ); + } + return await response.json(); + } + + /** + * Delete a fragment + * @param {Object} fragment + * @returns {Promise} + */ + async deleteFragment(fragment) { + const response = await fetch(`${this.cfFragmentsUrl}/${fragment.id}`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + 'If-Match': fragment.etag, + ...this.headers, + }, + }).catch((err) => { + throw new Error(`${NETWORK_ERROR_MESSAGE}: ${err.message}`); + }); + if (!response.ok) { + throw new Error( + `Failed to delete fragment: ${response.status} ${response.statusText}`, + ); + } + return response; //204 No Content + } -class AEM { sites = { cf: { fragments: { - search: fragmentSearch.bind(this), - getCfByPath: getCfByPath.bind(this), + /** + * @see AEM#searchFragment + */ + search: this.searchFragment.bind(this), + /** + * @see AEM#getFragmentByPath + */ + getByPath: this.getFragmentByPath.bind(this), + /** + * @see AEM#getFragmentById + */ + getById: this.getFragmentById.bind(this), + /** + * @see AEM#saveFragment + */ + save: this.saveFragment.bind(this), + /** + * @see AEM#copyFragmentClassic + */ + copy: this.copyFragmentClassic.bind(this), + /** + * @see AEM#publishFragment + */ + publish: this.publishFragment.bind(this), + /** + * @see AEM#deleteFragment + */ + delete: this.deleteFragment.bind(this), }, }, }; - - constructor(bucket) { - const baseUrl = `https://${bucket}.adobeaemcloud.com`; - const sitesUrl = `${baseUrl}/adobe/sites`; - this.cfFragmentsUrl = `${sitesUrl}/cf/fragments`; - this.cfSearchUrl = `${this.cfFragmentsUrl}/search`; - } } export { AEM }; diff --git a/libs/features/mas/web-components/src/global.css.js b/libs/features/mas/web-components/src/global.css.js index b0198162b3..0d4d5bfe9e 100644 --- a/libs/features/mas/web-components/src/global.css.js +++ b/libs/features/mas/web-components/src/global.css.js @@ -1,11 +1,3 @@ -import { - TABLET_UP, - DESKTOP_UP, - LARGE_DESKTOP, - MOBILE_LANDSCAPE, - TABLET_DOWN, -} from './media.js'; - const styles = document.createElement('style'); styles.innerHTML = ` @@ -71,7 +63,6 @@ styles.innerHTML = ` --consonant-merch-card-heading-padding: 0; - --consonant-merch-card-image-height: 180px; /* colors */ --consonant-merch-card-border-color: #eaeaea; @@ -84,44 +75,8 @@ styles.innerHTML = ` --consonant-merch-card-max-width: 300px; --transition: cmax-height 0.3s linear, opacity 0.3s linear; - /* special offers */ - --consonant-merch-card-special-offers-width: 378px; - - /* image */ - --consonant-merch-card-image-width: 300px; - - /* segment */ - --consonant-merch-card-segment-width: 378px; - - /* inline-heading */ - --consonant-merch-card-inline-heading-width: 300px; - - /* product */ - --consonant-merch-card-product-width: 300px; - - /* plans */ - --consonant-merch-card-plans-width: 300px; - --consonant-merch-card-plans-icon-size: 40px; - - /* catalog */ - --consonant-merch-card-catalog-width: 276px; - --consonant-merch-card-catalog-icon-size: 40px; - - /* twp */ - --consonant-merch-card-twp-width: 268px; - --consonant-merch-card-twp-mobile-width: 300px; - --consonant-merch-card-twp-mobile-height: 358px; - - /* ccd-action */ - --consonant-merch-card-ccd-action-width: 276px; - --consonant-merch-card-ccd-action-min-height: 320px; - - - /*mini compare chart */ - --consonant-merch-card-mini-compare-chart-icon-size: 32px; - --consonant-merch-card-mini-compare-mobile-cta-font-size: 15px; - --consonant-merch-card-mini-compare-mobile-cta-width: 75px; - --consonant-merch-card-mini-compare-badge-mobile-max-width: 50px; + /* background image */ + --consonant-merch-card-bg-img-height: 180px; /* inline SVGs */ --checkmark-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath fill='%23fff' d='M3.788 9A.999.999 0 0 1 3 8.615l-2.288-3a1 1 0 1 1 1.576-1.23l1.5 1.991 3.924-4.991a1 1 0 1 1 1.576 1.23l-4.712 6A.999.999 0 0 1 3.788 9z' class='spectrum-UIIcon--medium'/%3E%3C/svg%3E%0A"); @@ -178,10 +133,6 @@ merch-card.has-divider hr { border: none; } -merch-card[variant="special-offers"] span[is="inline-price"][data-template="strikethrough"] { - font-size: var(--consonant-merch-card-body-xs-font-size); -} - merch-card p, merch-card h3, merch-card h4 { margin: 0; } @@ -336,155 +287,12 @@ merch-card [slot="body-xl"] { color: var(--merch-color-grey-80); } -merch-card[variant="plans"] [slot="description"] { - min-height: 84px; -} - -merch-card[variant="catalog"] [slot="action-menu-content"] { - background-color: #000; - color: var(--color-white, #fff); - font-size: var(--consonant-merch-card-body-xs-font-size); - width: fit-content; - padding: var(--consonant-merch-spacing-xs); - border-radius: var(--consonant-merch-spacing-xxxs); - position: absolute; - top: 55px; - right: 15px; - line-height: var(--consonant-merch-card-body-line-height); -} - -merch-card[variant="catalog"] [slot="action-menu-content"] ul { - padding-left: 0; - padding-bottom: var(--consonant-merch-spacing-xss); - margin-top: 0; - margin-bottom: 0; - list-style-position: inside; - list-style-type: '• '; -} - -merch-card[variant="catalog"] [slot="action-menu-content"] ul li { - padding-left: 0; - line-height: var(--consonant-merch-card-body-line-height); -} - -merch-card[variant="catalog"] [slot="action-menu-content"] ::marker { - margin-right: 0; -} - -merch-card[variant="catalog"] [slot="action-menu-content"] p { - color: var(--color-white, #fff); -} - -merch-card[variant="catalog"] [slot="action-menu-content"] a { - color: var(--consonant-merch-card-background-color); - text-decoration: underline; -} - -merch-card[variant="catalog"] .payment-details { - font-size: var(--consonant-merch-card-body-font-size); - font-style: italic; - font-weight: 400; - line-height: var(--consonant-merch-card-body-line-height); -} - -merch-card[variant="ccd-action"] .price-strikethrough { - font-size: 18px; -} - -merch-card[variant="plans"] [slot="quantity-select"] { - display: flex; - justify-content: flex-start; - box-sizing: border-box; - width: 100%; - padding: var(--consonant-merch-spacing-xs); -} - -merch-card[variant="twp"] div[class$='twp-badge'] { - padding: 4px 10px 5px 10px; -} - -merch-card[variant="twp"] [slot="body-xs-top"] { - font-size: var(--consonant-merch-card-body-xs-font-size); - line-height: var(--consonant-merch-card-body-xs-line-height); - color: var(--merch-color-grey-80); -} - -merch-card[variant="twp"] [slot="body-xs"] ul { - padding: 0; - margin: 0; -} - -merch-card[variant="twp"] [slot="body-xs"] ul li { - list-style-type: none; - padding-left: 0; -} - -merch-card[variant="twp"] [slot="body-xs"] ul li::before { - content: '·'; - font-size: 20px; - padding-right: 5px; - font-weight: bold; -} - -merch-card[variant="twp"] [slot="footer"] { - font-size: var(--consonant-merch-card-body-xs-font-size); - line-height: var(--consonant-merch-card-body-xs-line-height); - padding: var(--consonant-merch-spacing-s) - var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-xs); - color: var(--merch-color-grey-80); - display: flex; - flex-flow: wrap; -} - -merch-card[variant='twp'] merch-quantity-select, -merch-card[variant='twp'] merch-offer-select { - display: none; -} - [slot="cci-footer"] p, [slot="cct-footer"] p, [slot="cce-footer"] p { margin: 0; } -/* mini compare chart card styles */ - -merch-card[variant="mini-compare-chart"] [slot="heading-m"] { - padding: 0 var(--consonant-merch-spacing-s) 0; -} - -merch-card[variant="mini-compare-chart"] [slot="body-m"] { - padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s); -} - -merch-card[variant="mini-compare-chart"] [is="inline-price"] { - display: inline-block; - min-height: 30px; - min-width: 1px; -} - -merch-card[variant="mini-compare-chart"] [slot='callout-content'] { - padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s) 0px; -} - -merch-card[variant="mini-compare-chart"] [slot='callout-content'] [is="inline-price"] { - min-height: unset; -} - -merch-card[variant="mini-compare-chart"] [slot="price-commitment"] { - font-size: var(--consonant-merch-card-body-xs-font-size); - padding: 0 var(--consonant-merch-spacing-s); -} - -merch-card[variant="mini-compare-chart"] [slot="price-commitment"] a { - display: inline-block; - height: 27px; -} - -merch-card[variant="mini-compare-chart"] [slot="offers"] { - font-size: var(--consonant-merch-card-body-xs-font-size); -} - merch-card [slot="promo-text"] { color: var(--merch-color-green-promo); font-size: var(--consonant-merch-card-promo-text-height); @@ -495,111 +303,6 @@ merch-card [slot="promo-text"] { padding: 0; } - -merch-card[variant="mini-compare-chart"] [slot="body-xxs"] { - font-size: var(--consonant-merch-card-body-xs-font-size); - padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s) 0; -} - -merch-card[variant="mini-compare-chart"] [slot="promo-text"] { - font-size: var(--consonant-merch-card-body-m-font-size); - padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s) 0; -} - -merch-card[variant="mini-compare-chart"] [slot="promo-text"] a { - text-decoration: underline; -} - -merch-card[variant="mini-compare-chart"] .footer-row-icon { - display: flex; - place-items: center; -} - -merch-card[variant="mini-compare-chart"] .footer-row-icon img { - max-width: initial; - width: var(--consonant-merch-card-mini-compare-chart-icon-size); - height: var(--consonant-merch-card-mini-compare-chart-icon-size); -} - -merch-card[variant="mini-compare-chart"] .footer-row-cell { - border-top: 1px solid var(--consonant-merch-card-border-color); - display: flex; - gap: var(--consonant-merch-spacing-xs); - justify-content: start; - place-items: center; - padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s); -} - -merch-card[variant="mini-compare-chart"] .footer-row-cell-description { - font-size: var(--consonant-merch-card-body-s-font-size); - line-height: var(--consonant-merch-card-body-s-line-height); -} - -merch-card[variant="mini-compare-chart"] .footer-row-cell-description p { - color: var(--merch-color-grey-80); - vertical-align: bottom; -} - -merch-card[variant="mini-compare-chart"] .footer-row-cell-description a { - color: var(--color-accent); - text-decoration: solid; -} - -/* mini compare mobile */ -@media screen and ${MOBILE_LANDSCAPE} { - merch-card[variant="mini-compare-chart"] [slot='heading-m'] { - font-size: var(--consonant-merch-card-body-s-font-size); - line-height: var(--consonant-merch-card-body-s-line-height); - } - - merch-card[variant="mini-compare-chart"] [slot='heading-m-price'] { - font-size: var(--consonant-merch-card-body-s-font-size); - line-height: var(--consonant-merch-card-body-s-line-height); - } - - merch-card[variant="mini-compare-chart"] [slot='body-m'] { - font-size: var(--consonant-merch-card-body-xs-font-size); - line-height: var(--consonant-merch-card-body-xs-line-height); - } - - merch-card[variant="mini-compare-chart"] [slot="promo-text"] { - font-size: var(--consonant-merch-card-body-xs-font-size); - line-height: var(--consonant-merch-card-body-xs-line-height); - } - merch-card[variant="mini-compare-chart"] .footer-row-cell-description { - font-size: var(--consonant-merch-card-body-xs-font-size); - line-height: var(--consonant-merch-card-body-xs-line-height); - } -} - -/* mini compare tablet */ -@media screen and ${TABLET_DOWN} { - merch-card[variant="mini-compare-chart"] [slot='heading-m'] { - font-size: var(--consonant-merch-card-body-s-font-size); - line-height: var(--consonant-merch-card-body-s-line-height); - } - - merch-card[variant="mini-compare-chart"] [slot='heading-m-price'] { - font-size: var(--consonant-merch-card-body-s-font-size); - line-height: var(--consonant-merch-card-body-s-line-height); - } - - merch-card[variant="mini-compare-chart"] [slot='body-m'] { - font-size: var(--consonant-merch-card-body-xs-font-size); - line-height: var(--consonant-merch-card-body-xs-line-height); - } - - merch-card[variant="mini-compare-chart"] [slot="promo-text"] { - font-size: var(--consonant-merch-card-body-xs-font-size); - line-height: var(--consonant-merch-card-body-xs-line-height); - } - - merch-card[variant="mini-compare-chart"] .footer-row-cell-description { - font-size: var(--consonant-merch-card-body-xs-font-size); - line-height: var(--consonant-merch-card-body-xs-line-height); - } -} - div[slot="footer"] { display: contents; } @@ -617,462 +320,28 @@ div[slot="footer"] { div[slot='bg-image'] img { position: relative; width: 100%; - min-height: var(--consonant-merch-card-image-height); - max-height: var(--consonant-merch-card-image-height); + min-height: var(--consonant-merch-card-bg-img-height); + max-height: var(--consonant-merch-card-bg-img-height); object-fit: cover; border-top-left-radius: 16px; border-top-right-radius: 16px; } -/* Mobile */ -@media screen and ${MOBILE_LANDSCAPE} { - :root { - --consonant-merch-card-mini-compare-chart-width: 302px; - --consonant-merch-card-segment-width: 276px; - --consonant-merch-card-mini-compare-chart-wide-width: 302px; - --consonant-merch-card-special-offers-width: 302px; - --consonant-merch-card-twp-width: 300px; - } -} - - -/* Tablet */ -@media screen and ${TABLET_UP} { - :root { - --consonant-merch-card-catalog-width: 302px; - --consonant-merch-card-plans-width: 302px; - --consonant-merch-card-segment-width: 276px; - --consonant-merch-card-mini-compare-chart-width: 302px; - --consonant-merch-card-mini-compare-chart-wide-width: 302px; - --consonant-merch-card-special-offers-width: 302px; - --consonant-merch-card-twp-width: 268px; - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - :root { - --consonant-merch-card-catalog-width: 276px; - --consonant-merch-card-plans-width: 276px; - --consonant-merch-card-segment-width: 302px; - --consonant-merch-card-inline-heading-width: 378px; - --consonant-merch-card-product-width: 378px; - --consonant-merch-card-image-width: 378px; - --consonant-merch-card-mini-compare-chart-width: 378px; - --consonant-merch-card-mini-compare-chart-wide-width: 484px; - --consonant-merch-card-twp-width: 268px; - } -} - -/* supported cards */ -/* grid style for plans */ -.one-merch-card.plans, -.two-merch-cards.plans, -.three-merch-cards.plans, -.four-merch-cards.plans { - grid-template-columns: var(--consonant-merch-card-plans-width); -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .two-merch-cards.plans, - .three-merch-cards.plans, - .four-merch-cards.plans { - grid-template-columns: repeat(2, var(--consonant-merch-card-plans-width)); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .three-merch-cards.plans, - .four-merch-cards.plans { - grid-template-columns: repeat(3, var(--consonant-merch-card-plans-width)); - } -} - -/* Large desktop */ - @media screen and ${LARGE_DESKTOP} { - .four-merch-cards.plans { - grid-template-columns: repeat(4, var(--consonant-merch-card-plans-width)); - } -} - - -/* grid style for catalog */ -.one-merch-card.catalog, -.two-merch-cards.catalog, -.three-merch-cards.catalog, -.four-merch-cards.catalog { - grid-template-columns: var(--consonant-merch-card-catalog-width); -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .two-merch-cards.catalog, - .three-merch-cards.catalog, - .four-merch-cards.catalog { - grid-template-columns: repeat(2, var(--consonant-merch-card-catalog-width)); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .three-merch-cards.catalog, - .four-merch-cards.catalog { - grid-template-columns: repeat(3, var(--consonant-merch-card-catalog-width)); - } -} - -/* Large desktop */ - @media screen and ${LARGE_DESKTOP} { - .four-merch-cards.catalog { - grid-template-columns: repeat(4, var(--consonant-merch-card-catalog-width)); - } -} - - -/* grid style for special-offers */ -.one-merch-card.special-offers, -.two-merch-cards.special-offers, -.three-merch-cards.special-offers, -.four-merch-cards.special-offers { - grid-template-columns: minmax(300px, var(--consonant-merch-card-special-offers-width)); -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .two-merch-cards.special-offers, - .three-merch-cards.special-offers, - .four-merch-cards.special-offers { - grid-template-columns: repeat(2, minmax(300px, var(--consonant-merch-card-special-offers-width))); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .three-merch-cards.special-offers, - .four-merch-cards.special-offers { - grid-template-columns: repeat(3, minmax(300px, var(--consonant-merch-card-special-offers-width))); - } -} - -@media screen and ${LARGE_DESKTOP} { - .four-merch-cards.special-offers { - grid-template-columns: repeat(4, minmax(300px, var(--consonant-merch-card-special-offers-width))); - } -} - - -/* grid style for image */ -.one-merch-card.image, -.two-merch-cards.image, -.three-merch-cards.image, -.four-merch-cards.image { - grid-template-columns: var(--consonant-merch-card-image-width); -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .two-merch-cards.image, - .three-merch-cards.image, - .four-merch-cards.image { - grid-template-columns: repeat(2, var(--consonant-merch-card-image-width)); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .three-merch-cards.image, - .four-merch-cards.image { - grid-template-columns: repeat(3, var(--consonant-merch-card-image-width)); - } -} - -/* Large desktop */ - @media screen and ${LARGE_DESKTOP} { - .four-merch-cards.image { - grid-template-columns: repeat(4, var(--consonant-merch-card-image-width)); - } -} - - -/* grid style for segment */ -.one-merch-card.segment, -.two-merch-cards.segment, -.three-merch-cards.segment, -.four-merch-cards.segment { - grid-template-columns: minmax(276px, var(--consonant-merch-card-segment-width)); -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .two-merch-cards.segment, - .three-merch-cards.segment, - .four-merch-cards.segment { - grid-template-columns: repeat(2, minmax(276px, var(--consonant-merch-card-segment-width))); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .three-merch-cards.segment { - grid-template-columns: repeat(3, minmax(276px, var(--consonant-merch-card-segment-width))); - } - - .four-merch-cards.segment { - grid-template-columns: repeat(4, minmax(276px, var(--consonant-merch-card-segment-width))); - } -} - - -/* grid style for product */ -.one-merch-card.product, -.two-merch-cards.product, -.three-merch-cards.product, -.four-merch-cards.product { - grid-template-columns: var(--consonant-merch-card-product-width); -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .two-merch-cards.product, - .three-merch-cards.product, - .four-merch-cards.product { - grid-template-columns: repeat(2, var(--consonant-merch-card-product-width)); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .three-merch-cards.product, - .four-merch-cards.product { - grid-template-columns: repeat(3, var(--consonant-merch-card-product-width)); - } -} - -/* Large desktop */ - @media screen and ${LARGE_DESKTOP} { - .four-merch-cards.product { - grid-template-columns: repeat(4, var(--consonant-merch-card-product-width)); - } -} - -/* grid style for twp */ -.one-merch-card.twp, -.two-merch-cards.twp, -.three-merch-cards.twp { - grid-template-columns: var(--consonant-merch-card-image-width); -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .one-merch-card.twp, - .two-merch-cards.twp { - grid-template-columns: repeat(2, var(--consonant-merch-card-twp-width)); - } - .three-merch-cards.twp { - grid-template-columns: repeat(3, var(--consonant-merch-card-twp-width)); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .one-merch-card.twp - .two-merch-cards.twp { - grid-template-columns: repeat(2, var(--consonant-merch-card-twp-width)); - } - .three-merch-cards.twp { - grid-template-columns: repeat(3, var(--consonant-merch-card-twp-width)); - } -} - -/* Large desktop */ - @media screen and ${LARGE_DESKTOP} { - .one-merch-card.twp - .two-merch-cards.twp { - grid-template-columns: repeat(2, var(--consonant-merch-card-twp-width)); - } - .three-merch-cards.twp { - grid-template-columns: repeat(3, var(--consonant-merch-card-twp-width)); - } -} - -/* Mobile */ -@media screen and ${MOBILE_LANDSCAPE} { - .one-merch-card.twp, - .two-merch-cards.twp, - .three-merch-cards.twp { - grid-template-columns: repeat(1, var(--consonant-merch-card-twp-mobile-width)); - } -} - -/* grid style for inline-heading */ -.one-merch-card.inline-heading, -.two-merch-cards.inline-heading, -.three-merch-cards.inline-heading, -.four-merch-cards.inline-heading { - grid-template-columns: var(--consonant-merch-card-inline-heading-width); -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .two-merch-cards.inline-heading, - .three-merch-cards.inline-heading, - .four-merch-cards.inline-heading { - grid-template-columns: repeat(2, var(--consonant-merch-card-inline-heading-width)); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .three-merch-cards.inline-heading, - .four-merch-cards.inline-heading { - grid-template-columns: repeat(3, var(--consonant-merch-card-inline-heading-width)); - } -} - -/* Large desktop */ - @media screen and ${LARGE_DESKTOP} { - .four-merch-cards.inline-heading { - grid-template-columns: repeat(4, var(--consonant-merch-card-inline-heading-width)); - } -} - -/* grid style for ccd-action */ -.one-merch-card.ccd-action, -.two-merch-cards.ccd-action, -.three-merch-cards.ccd-action, -.four-merch-cards.ccd-action { - grid-template-columns: var(--consonant-merch-card-ccd-action-width); -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .two-merch-cards.ccd-action, - .three-merch-cards.ccd-action, - .four-merch-cards.ccd-action { - grid-template-columns: repeat(2, var(--consonant-merch-card-ccd-action-width)); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .three-merch-cards.ccd-action, - .four-merch-cards.ccd-action { - grid-template-columns: repeat(3, var(--consonant-merch-card-ccd-action-width)); - } -} - -/* Large desktop */ - @media screen and ${LARGE_DESKTOP} { - .four-merch-cards.ccd-action { - grid-template-columns: repeat(4, var(--consonant-merch-card-ccd-action-width)); - } -} - -/* grid style for mini-compare-chart */ -.one-merch-card.mini-compare-chart { - grid-template-columns: var(--consonant-merch-card-mini-compare-chart-wide-width); - gap: var(--consonant-merch-spacing-xs); -} - -.two-merch-cards.mini-compare-chart, -.three-merch-cards.mini-compare-chart, -.four-merch-cards.mini-compare-chart { - grid-template-columns: repeat(2, var(--consonant-merch-card-mini-compare-chart-width)); - gap: var(--consonant-merch-spacing-xs); -} - -@media screen and ${MOBILE_LANDSCAPE} { - .two-merch-cards.mini-compare-chart, - .three-merch-cards.mini-compare-chart, - .four-merch-cards.mini-compare-chart { - grid-template-columns: var(--consonant-merch-card-mini-compare-chart-width); - gap: var(--consonant-merch-spacing-xs); - } -} - -@media screen and ${TABLET_DOWN} { - .three-merch-cards.mini-compare-chart merch-card [slot="footer"] a, - .four-merch-cards.mini-compare-chart merch-card [slot="footer"] a { - flex: 1; - } -} - -/* Tablet */ -@media screen and ${TABLET_UP} { - .two-merch-cards.mini-compare-chart { - grid-template-columns: repeat(2, minmax(var(--consonant-merch-card-mini-compare-chart-width), var(--consonant-merch-card-mini-compare-chart-wide-width))); - gap: var(--consonant-merch-spacing-m); - } - - .three-merch-cards.mini-compare-chart, - .four-merch-cards.mini-compare-chart { - grid-template-columns: repeat(2, minmax(var(--consonant-merch-card-mini-compare-chart-width), var(--consonant-merch-card-mini-compare-chart-wide-width))); - } -} - -/* desktop */ -@media screen and ${DESKTOP_UP} { - .one-merch-card.mini-compare-chart { - grid-template-columns: var(--consonant-merch-card-mini-compare-chart-wide-width); - } - - .two-merch-cards.mini-compare-chart { - grid-template-columns: repeat(2, var(--consonant-merch-card-mini-compare-chart-wide-width)); - gap: var(--consonant-merch-spacing-m); - } - - .three-merch-cards.mini-compare-chart, - .four-merch-cards.mini-compare-chart { - grid-template-columns: repeat(3, var(--consonant-merch-card-mini-compare-chart-width)); - gap: var(--consonant-merch-spacing-m); - } -} - -@media screen and ${LARGE_DESKTOP} { - .four-merch-cards.mini-compare-chart { - grid-template-columns: repeat(4, var(--consonant-merch-card-mini-compare-chart-width)); - } -} - -/* mini-compare card footer rows */ -merch-card .footer-row-cell:nth-child(1) { - min-height: var(--consonant-merch-card-footer-row-1-min-height); -} - -merch-card .footer-row-cell:nth-child(2) { - min-height: var(--consonant-merch-card-footer-row-2-min-height); -} - -merch-card .footer-row-cell:nth-child(3) { - min-height: var(--consonant-merch-card-footer-row-3-min-height); -} - -merch-card .footer-row-cell:nth-child(4) { - min-height: var(--consonant-merch-card-footer-row-4-min-height); -} - -merch-card .footer-row-cell:nth-child(5) { - min-height: var(--consonant-merch-card-footer-row-5-min-height); -} - -merch-card .footer-row-cell:nth-child(6) { - min-height: var(--consonant-merch-card-footer-row-6-min-height); -} - -merch-card .footer-row-cell:nth-child(7) { - min-height: var(--consonant-merch-card-footer-row-7-min-height); -} - -merch-card .footer-row-cell:nth-child(8) { - min-height: var(--consonant-merch-card-footer-row-8-min-height); -} - span[is="inline-price"][data-template='strikethrough'] { text-decoration: line-through; } +merch-card sp-button a { + text-decoration: none; + color: var( + --highcontrast-button-content-color-default, + var( + --mod-button-content-color-default, + var(--spectrum-button-content-color-default) + ) + ); +} + merch-card span.placeholder-resolved[data-template='strikethrough'], merch-card span.price.price-strikethrough { font-size: var(--consonant-merch-card-body-xs-font-size); diff --git a/libs/features/mas/web-components/src/index.d.ts b/libs/features/mas/web-components/src/index.d.ts deleted file mode 100644 index 851c4cacad..0000000000 --- a/libs/features/mas/web-components/src/index.d.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { LitElement } from "lit"; - -declare global { - namespace Commerce { - type ComponentConstructor = { - new(): LitElement; - } - - type filterOffer = (offer: Offer, index: number) => boolean; - type filterPlaceholder = (placeholder: Commerce.Placeholder, index: number) => boolean; - type failed = (error: Error | undefined, target: Element) => void; - type resolved = (value: any, target: Element) => void; - - type Offers = Map; - - interface EventDetail { - error?: Error; - value?: any; - } - - interface Offer { - /** List of checkout links associated with this offer. */ - readonly checkoutLinks: Checkout.Placeholder[]; - /** Common ancestor of all placeholders in this offer, the offer container. */ - readonly container: Element | undefined; - /** List of inline prices associated with this offer. */ - readonly inlinePrices: Price.Placeholder[]; - /** Payments plan type. */ - readonly planType: Wcs.PlanType | "UNKNOWN"; - } - } -} - -export {}; diff --git a/libs/features/mas/web-components/src/index.js b/libs/features/mas/web-components/src/index.js index 54a1fd6dc1..fb9855248c 100644 --- a/libs/features/mas/web-components/src/index.js +++ b/libs/features/mas/web-components/src/index.js @@ -1,2 +1,3 @@ export * from './merch-card.js'; export * from './merch-card-collection.js'; +export * from './aem.js'; diff --git a/libs/features/mas/web-components/src/merch-card.css.js b/libs/features/mas/web-components/src/merch-card.css.js index a40e6116d9..bcc2081651 100644 --- a/libs/features/mas/web-components/src/merch-card.css.js +++ b/libs/features/mas/web-components/src/merch-card.css.js @@ -1,11 +1,5 @@ import { css, unsafeCSS } from 'lit'; -import { - DESKTOP_UP, - LARGE_DESKTOP, - TABLET_UP, - MOBILE_LANDSCAPE, - TABLET_DOWN, -} from './media.js'; +import { DESKTOP_UP, LARGE_DESKTOP, TABLET_UP, } from './media.js'; export const styles = css` :host { @@ -26,26 +20,6 @@ export const styles = css` visibility: hidden; } - :host([variant='special-offers']) { - min-height: 439px; - } - - :host([variant='catalog']) { - min-height: 330px; - } - - :host([variant='plans']) { - min-height: 348px; - } - - :host([variant='segment']) { - min-height: 214px; - } - - :host([variant='ccd-action']:not([size])) { - width: var(--consonant-merch-card-ccd-action-width); - } - :host([aria-selected]) { outline: none; box-sizing: border-box; @@ -67,10 +41,6 @@ export const styles = css` background-image: var(--ellipsis-icon); } - :host([variant='mini-compare-chart']) > slot:not([name='icons']) { - display: block; - } - .top-section { display: flex; justify-content: flex-start; @@ -135,19 +105,6 @@ export const styles = css` border-radius: 0 5px 5px 0; } - .body .catalog-badge { - display: flex; - height: fit-content; - flex-direction: column; - width: fit-content; - max-width: 140px; - border-radius: 5px; - position: relative; - top: 0; - margin-left: var(--consonant-merch-spacing-xxs); - box-sizing: border-box; - } - .detail-bg-container { right: 0; padding: var(--consonant-merch-spacing-xs); @@ -251,128 +208,10 @@ export const styles = css` margin-top: 2px; } - .twp-badge { - padding: 4px 10px 5px 10px; - } - - :host([aria-selected]) .twp-badge { - margin-inline-end: 2px; - padding-inline-end: 9px; - } - - :host([variant='twp']) { - padding: 4px 10px 5px 10px; - } - slot[name='icons'] { display: flex; gap: 8px; } - - :host([variant='twp']) ::slotted(merch-offer-select) { - display: none; - } - - :host([variant='twp']) .top-section { - flex: 0; - display: flex; - flex-direction: column; - justify-content: flex-start; - height: 100%; - gap: var(--consonant-merch-spacing-xxs); - padding: var(--consonant-merch-spacing-xs) - var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-xs) - var(--consonant-merch-spacing-xs); - align-items: flex-start; - } - - :host([variant='twp']) .body { - padding: 0 var(--consonant-merch-spacing-xs); - } - - :host([variant='twp']) footer { - gap: var(--consonant-merch-spacing-xxs); - flex-direction: column; - align-self: flex-start; - } - - :host([variant='special-offers'].center) { - text-align: center; - } - - /* plans */ - :host([variant='plans']) { - min-height: 348px; - } - - :host([variant='mini-compare-chart']) footer { - min-height: var(--consonant-merch-card-mini-compare-footer-height); - padding: var(--consonant-merch-spacing-xs); - } - - /* mini-compare card */ - :host([variant='mini-compare-chart']) .top-section { - padding-top: var(--consonant-merch-spacing-s); - padding-inline-start: var(--consonant-merch-spacing-s); - height: var(--consonant-merch-card-mini-compare-top-section-height); - } - - @media screen and ${unsafeCSS(TABLET_DOWN)} { - [class*'-merch-cards'] :host([variant='mini-compare-chart']) footer { - flex-direction: column; - align-items: stretch; - text-align: center; - } - } - - @media screen and ${unsafeCSS(DESKTOP_UP)} { - :host([variant='mini-compare-chart']) footer { - padding: var(--consonant-merch-spacing-xs) - var(--consonant-merch-spacing-s) - var(--consonant-merch-spacing-s) - var(--consonant-merch-spacing-s); - } - } - - :host([variant='mini-compare-chart']) slot[name='footer-rows'] { - flex: 1; - display: flex; - flex-direction: column; - justify-content: end; - } - /* mini-compare card heights for the slots: heading-m, body-m, heading-m-price, price-commitment, offers, promo-text, footer */ - :host([variant='mini-compare-chart']) slot[name='heading-m'] { - min-height: var(--consonant-merch-card-mini-compare-heading-m-height); - } - :host([variant='mini-compare-chart']) slot[name='body-m'] { - min-height: var(--consonant-merch-card-mini-compare-body-m-height); - } - :host([variant='mini-compare-chart']) slot[name='heading-m-price'] { - min-height: var( - --consonant-merch-card-mini-compare-heading-m-price-height - ); - } - :host([variant='mini-compare-chart']) slot[name='price-commitment'] { - min-height: var( - --consonant-merch-card-mini-compare-price-commitment-height - ); - } - :host([variant='mini-compare-chart']) slot[name='offers'] { - min-height: var(--consonant-merch-card-mini-compare-offers-height); - } - :host([variant='mini-compare-chart']) slot[name='promo-text'] { - min-height: var(--consonant-merch-card-mini-compare-promo-text-height); - } - :host([variant='mini-compare-chart']) slot[name='callout-content'] { - min-height: var( - --consonant-merch-card-mini-compare-callout-content-height - ); - } - - :host([variant='plans']) ::slotted([slot='heading-xs']), - :host([variant='segment']) ::slotted([slot='heading-xs']) { - max-width: var(--consonant-merch-card-heading-xs-max-width, 100%); - } `; export const sizeStyles = () => { diff --git a/libs/features/mas/web-components/src/merch-card.js b/libs/features/mas/web-components/src/merch-card.js index 82b022c76d..92136552a1 100644 --- a/libs/features/mas/web-components/src/merch-card.js +++ b/libs/features/mas/web-components/src/merch-card.js @@ -1,6 +1,7 @@ -import { html, LitElement, nothing } from 'lit'; +import { LitElement } from 'lit'; import { sizeStyles, styles } from './merch-card.css.js'; -import { isMobile, isMobileOrTablet } from './utils.js'; +import { getVariantLayout, getVariantStyles } from './variants/variants.js'; + import './global.css.js'; import { @@ -8,20 +9,11 @@ import { EVENT_MERCH_OFFER_SELECT_READY, EVENT_MERCH_QUANTITY_SELECTOR_CHANGE, EVENT_MERCH_STORAGE_CHANGE, - EVENT_MERCH_CARD_ACTION_MENU_TOGGLE, } from './constants.js'; -import { getTextNodes } from './utils.js'; export const MERCH_CARD_NODE_NAME = 'MERCH-CARD'; export const MERCH_CARD = 'merch-card'; -const FOOTER_ROW_MIN_HEIGHT = 32; // as per the XD. - -const MINI_COMPARE_CHART = 'mini-compare-chart'; - -const getRowMinHeightPropertyName = (index) => - `--consonant-merch-card-footer-row-${index}-min-height`; - export class MerchCard extends LitElement { static properties = { name: { type: String, attribute: 'name', reflect: true }, @@ -90,10 +82,11 @@ export class MerchCard extends LitElement { merchOffer: { type: Object }, }; - static styles = [styles, ...sizeStyles()]; + static styles = [styles, getVariantStyles(), ...sizeStyles()]; customerSegment; marketSegment; + variantLayout; constructor() { super(); @@ -102,8 +95,6 @@ export class MerchCard extends LitElement { this.selected = false; } - #container; - updated(changedProperties) { if ( changedProperties.has('badgeBackgroundColor') || @@ -113,23 +104,22 @@ export class MerchCard extends LitElement { } this.updateComplete.then(async () => { const allPrices = Array.from( - this.querySelectorAll('span[is="inline-price"][data-wcs-osi]'), + this.querySelectorAll('span[is="inline-price"][data-wcs-osi]'), ); // Filter out prices within the callout-content slot const prices = allPrices.filter( (price) => !price.closest('[slot="callout-content"]'), ); await Promise.all(prices.map((price) => price.onceSettled())); - this.adjustTitleWidth(); - if (!isMobile()) { - this.adjustMiniCompareBodySlots(); - this.adjustMiniCompareFooterRows(); - } else { - this.removeEmptyRows(); - } + this.variantLayout.postCardUpdateHook(this); }); } + render() { + if (!this.isConnected || this.style.display === 'none') return; + return this.variantLayout.renderLayout(); + } + get computedBorderStyle() { if (this.variant !== 'twp') { return `1px solid ${ @@ -139,76 +129,10 @@ export class MerchCard extends LitElement { return ''; } - get evergreen() { - return this.classList.contains('intro-pricing'); - } - - get stockCheckbox() { - return this.checkboxLabel - ? html`` - : ''; - } - - get cardImage() { - return html`
- - ${this.badge} -
`; - } - - get secureLabelFooter() { - const secureLabel = this.secureLabel - ? html`${this.secureLabel}` - : ''; - return html`
${secureLabel}
`; - } - - get miniCompareFooter() { - const secureLabel = this.secureLabel - ? html` - ${this.secureLabel}` - : html``; - return html`
${secureLabel}
`; - } - - get badge() { - let additionalStyles; - if (!this.badgeBackgroundColor || !this.badgeColor || !this.badgeText) { - return; - } - if (this.evergreen) { - additionalStyles = `border: 1px solid ${this.badgeBackgroundColor}; border-right: none;`; - } - return html` -
- ${this.badgeText} -
- `; - } - get badgeElement() { return this.shadowRoot.getElementById('badge'); } - getContainer() { - return this.closest('[class*="-merch-cards"]') ?? this.parentElement; - } - get headingmMSlot() { return this.shadowRoot .querySelector('slot[name="heading-m"]') @@ -253,27 +177,6 @@ export class MerchCard extends LitElement { } } - toggleActionMenu(e) { - const retract = e?.type === 'mouseleave' ? true : undefined; - const actionMenuContentSlot = this.shadowRoot.querySelector( - 'slot[name="action-menu-content"]', - ); - if (!actionMenuContentSlot) return; - if (!retract) { - this.dispatchEvent( - new CustomEvent(EVENT_MERCH_CARD_ACTION_MENU_TOGGLE, { - bubbles: true, - composed: true, - detail: { - card: this.name, - type: 'action-menu', - }, - }), - ); - } - actionMenuContentSlot.classList.toggle('hidden', retract); - } - handleQuantitySelection(event) { const elements = this.checkoutLinks; for (const element of elements) { @@ -281,12 +184,8 @@ export class MerchCard extends LitElement { } } - get titleElement() { - const heading = - this.variant === 'special-offers' - ? this.querySelector('[slot="detail-m"]') - : this.querySelector('[slot="heading-xs"]'); - return heading; + get titleElement() { + return this.querySelector(this.variantLayout?.headingSelector || '.card-heading'); } get title() { @@ -322,232 +221,15 @@ export class MerchCard extends LitElement { return this.textContent.match(new RegExp(text, 'i')) !== null; } - render() { - if (!this.isConnected || this.style.display === 'none') return; - switch (this.variant) { - case 'special-offers': - return this.renderSpecialOffer(); - case 'segment': - return this.renderSegment(); - case 'plans': - return this.renderPlans(); - case 'catalog': - return this.renderCatalog(); - case 'image': - return this.renderImage(); - case 'product': - return this.renderProduct(); - case 'inline-heading': - return this.renderInlineHeading(); - case MINI_COMPARE_CHART: - return this.renderMiniCompareChart(); - case 'ccd-action': - return this.renderCcdAction(); - case 'twp': - return this.renderTwp(); - default: - // this line should never hit, to check. - return this.renderProduct(); - } - } - - renderSpecialOffer() { - return html`${this.cardImage} -
- - - -
- ${this.evergreen - ? html` -
- -
- ` - : html` -
- ${this.secureLabelFooter} - `}`; - } - - get promoBottom() { - return this.classList.contains('promo-bottom'); - } - get startingAt() { return this.classList.contains('starting-at'); } - renderSegment() { - return html` ${this.badge} -
- - - ${!this.promoBottom ? html`` : ''} - - ${this.promoBottom ? html`` : ''} -
-
- ${this.secureLabelFooter}`; - } - - renderPlans() { - return html` ${this.badge} -
- - - - - ${!this.promoBottom ? html` ` : ''} - - ${this.promoBottom ? html` ` : ''} - ${this.stockCheckbox} -
- - ${this.secureLabelFooter}`; - } - - renderCatalog() { - return html`
-
- ${this.badge} -
-
- ${this.actionMenuContent} - - - - ${!this.promoBottom - ? html`` - : ''} - - ${this.promoBottom - ? html`` - : ''} -
- ${this.secureLabelFooter}`; - } - - renderImage() { - return html`${this.cardImage} -
- - - - ${this.promoBottom ? html`` : html``} -
- ${this.evergreen - ? html` -
- -
- ` - : html` -
- ${this.secureLabelFooter} - `}`; - } - - renderInlineHeading() { - return html` ${this.badge} -
-
- - -
- -
- ${!this.customHr ? html`
` : ''} ${this.secureLabelFooter}`; - } - - renderProduct() { - return html` ${this.badge} -
- - - - ${!this.promoBottom ? html`` : ''} - - ${this.promoBottom ? html`` : ''} -
- ${this.secureLabelFooter}`; - } - - renderMiniCompareChart() { - // Define the HTML structure for the 'mini-compare-chart' variant here - const { badge } = this; - return html`
- ${badge} -
- - - - - - - - - ${this.miniCompareFooter} - `; - } - - renderTwp() { - return html`${this.badge} -
- - - -
-
- -
-
`; - } - - get defaultSlot() { - const defaultSlotElement = this.querySelector( - ':scope > a:not([slot]),:scope > p:not([slot]),:scope > div:not([slot]),:scope > span:not([slot])', - ); - if (!defaultSlotElement) return nothing; - return html``; - } - - renderCcdAction() { - return html`
- ${this.badge} - - - ${this.promoBottom ? html`` : html``} -
- ${this.defaultSlot} -
`; - } - connectedCallback() { super.connectedCallback(); - this.#container = this.getContainer(); + this.variantLayout = getVariantLayout(this); + this.variantLayout.connectedCallbackHook(); this.setAttribute('tabindex', this.getAttribute('tabindex') ?? '0'); - this.addEventListener('mouseleave', this.toggleActionMenu); this.addEventListener( EVENT_MERCH_QUANTITY_SELECTOR_CHANGE, this.handleQuantitySelection, @@ -564,11 +246,11 @@ export class MerchCard extends LitElement { 'change', this.handleStorageChange, ); - // this.appendInvisibleSpacesToFooterLinks(); } disconnectedCallback() { super.disconnectedCallback(); + this.variantLayout.disconnectedCallbackHook(); this.removeEventListener( EVENT_MERCH_QUANTITY_SELECTOR_CHANGE, @@ -579,135 +261,8 @@ export class MerchCard extends LitElement { this.handleStorageChange, ); } - - appendInvisibleSpacesToFooterLinks() { - // append invisible spaces every 7 chars so that text wraps correctly on mobile. - [...this.querySelectorAll('[slot="footer"] a')].forEach((link) => { - const textNodes = getTextNodes(link); - // find words and add invisible space - textNodes.forEach((node) => { - const text = node.textContent; - const words = text.split(' '); - const newText = words - .map((word) => word.match(/.{1,7}/g)?.join('\u200B')) - .join(' '); - node.textContent = newText; - }); - }); - } - // custom methods - updateMiniCompareElementMinHeight(el, name) { - const elMinHeightPropertyName = `--consonant-merch-card-mini-compare-${name}-height`; - const height = Math.max( - 0, - parseInt(window.getComputedStyle(el).height) || 0, - ); - const maxMinHeight = - parseInt( - this.#container.style.getPropertyValue(elMinHeightPropertyName), - ) || 0; - if (height > maxMinHeight) { - this.#container.style.setProperty( - elMinHeightPropertyName, - `${height}px`, - ); - } - } - - async adjustTitleWidth() { - if (!['segment', 'plans'].includes(this.variant)) return; - const cardWidth = this.getBoundingClientRect().width; - const badgeWidth = - this.badgeElement?.getBoundingClientRect().width || 0; - if (cardWidth === 0 || badgeWidth === 0) return; - this.style.setProperty( - '--consonant-merch-card-heading-xs-max-width', - `${Math.round(cardWidth - badgeWidth - 16)}px`, // consonant-merch-spacing-xs - ); - } - - async adjustMiniCompareBodySlots() { - if (this.variant !== MINI_COMPARE_CHART) return; - if (this.getBoundingClientRect().width === 0) return; - - this.updateMiniCompareElementMinHeight( - this.shadowRoot.querySelector('.top-section'), - 'top-section', - ); - - const slots = [ - 'heading-m', - 'body-m', - 'heading-m-price', - 'price-commitment', - 'offers', - 'promo-text', - 'callout-content', - 'secure-transaction-label', - ]; - - slots.forEach((slot) => - this.updateMiniCompareElementMinHeight( - this.shadowRoot.querySelector(`slot[name="${slot}"]`), - slot, - ), - ); - this.updateMiniCompareElementMinHeight( - this.shadowRoot.querySelector('footer'), - 'footer', - ); - - const badge = this.shadowRoot.querySelector( - '.mini-compare-chart-badge', - ); - if (badge && badge.textContent !== '') { - this.#container.style.setProperty( - '--consonant-merch-card-mini-compare-top-section-mobile-height', - '32px', - ); - } - } - - adjustMiniCompareFooterRows() { - if (this.variant !== MINI_COMPARE_CHART) return; - if (this.getBoundingClientRect().width === 0) return; - const footerRows = this.querySelector('[slot="footer-rows"]'); - [...footerRows.children].forEach((el, index) => { - const height = Math.max( - FOOTER_ROW_MIN_HEIGHT, - parseInt(window.getComputedStyle(el).height) || 0, - ); - const maxMinHeight = - parseInt( - this.#container.style.getPropertyValue( - getRowMinHeightPropertyName(index + 1), - ), - ) || 0; - if (height > maxMinHeight) { - this.#container.style.setProperty( - getRowMinHeightPropertyName(index + 1), - `${height}px`, - ); - } - }); - } - - removeEmptyRows() { - if (this.variant !== MINI_COMPARE_CHART) return; - const footerRows = this.querySelectorAll('.footer-row-cell'); - footerRows.forEach((row) => { - const rowDescription = row.querySelector('.footer-row-cell-description'); - if (rowDescription) { - const isEmpty = !rowDescription.textContent.trim(); - if (isEmpty) { - row.remove(); - } - } - }); - } - get storageOptions() { return this.querySelector('sp-radio-group#storage'); } @@ -741,6 +296,8 @@ export class MerchCard extends LitElement { ); } + // TODO enable with TWP // + /* c8 ignore next 11 */ handleStorageChange() { const offerSelect = this.closest('merch-card')?.offerSelect.cloneNode(true); @@ -757,6 +314,8 @@ export class MerchCard extends LitElement { return this.querySelector('[slot="price"]'); } + // TODO enable with TWP // + /* c8 ignore next 16 */ selectMerchOffer(offer) { if (offer === this.merchOffer) return; this.merchOffer = offer; diff --git a/libs/features/mas/web-components/src/merch-datasource.js b/libs/features/mas/web-components/src/merch-datasource.js index ba5e84ca32..c0ea3fd8f9 100644 --- a/libs/features/mas/web-components/src/merch-datasource.js +++ b/libs/features/mas/web-components/src/merch-datasource.js @@ -3,69 +3,56 @@ import { createTag } from './utils.js'; const ATTR_AEM_BUCKET = 'aem-bucket'; +const VARIANTS = { + CATALOG: 'catalog', + AH: 'ah', + CCD_ACTION: 'ccd-action', + SPECIAL_OFFERS: 'special-offers', +}; + const cardContent = { - catalog: { - name: 'catalog', - title: { - tag: 'h3', - slot: 'heading-xs', - }, - prices: { - tag: 'h3', - slot: 'heading-xs', - }, - description: { - tag: 'div', - slot: 'body-xs', - }, + [VARIANTS.CATALOG]: { + title: { tag: 'h3', slot: 'heading-xs' }, + prices: { tag: 'h3', slot: 'heading-xs' }, + description: { tag: 'div', slot: 'body-xs' }, ctas: { size: 'l' }, }, - ah: { - name: 'ah', - title: { - tag: 'h3', - slot: 'heading-xxs', - }, - prices: { - tag: 'h3', - slot: 'heading-xs', - }, - description: { - tag: 'div', - slot: 'body-xxs', - }, + [VARIANTS.AH]: { + title: { tag: 'h3', slot: 'heading-xxs' }, + prices: { tag: 'h3', slot: 'heading-xs' }, + description: { tag: 'div', slot: 'body-xxs' }, ctas: { size: 's' }, }, - 'ccd-action': { - name: 'ccd-action', - title: { - tag: 'h3', - slot: 'heading-xs', - }, - prices: { - tag: 'h3', - slot: 'heading-xs', - }, - description: { - tag: 'div', - slot: 'body-xs', - }, + [VARIANTS.CCD_ACTION]: { + title: { tag: 'h3', slot: 'heading-xs' }, + prices: { tag: 'h3', slot: 'heading-xs' }, + description: { tag: 'div', slot: 'body-xs' }, + ctas: { size: 'l' }, + }, + [VARIANTS.SPECIAL_OFFERS]: { + name: { tag: 'h4', slot: 'detail-m' }, + title: { tag: 'h4', slot: 'detail-m' }, + backgroundImage: { tag: 'div', slot: 'bg-image' }, + prices: { tag: 'h3', slot: 'heading-xs' }, + description: { tag: 'div', slot: 'body-xs' }, ctas: { size: 'l' }, }, }; -async function parseMerchCard(item, merchCard) { - const cardJson = Array.isArray(item.fields) ? item.fields.reduce((acc, { name, multiple, values }) => { - acc[name] = multiple ? values : values[0]; - return acc; - }, {}) : {}; - const { type = 'catalog' } = cardJson; - const cardType = cardContent[type] || cardContent.catalog; - - merchCard.variant = type; +async function parseMerchCard(fragmentData, appendFn, merchCard, consonant) { + const item = fragmentData.fields.reduce( + (acc, { name, multiple, values }) => { + acc[name] = multiple ? values : values[0]; + return acc; + }, + { id: fragmentData.id }, + ); + item.model = item.model; - merchCard.setAttribute('variant', type); - cardJson.icon?.forEach((icon) => { + const { variant = 'catalog' } = item; + merchCard.setAttribute('variant', variant); + const cardMapping = cardContent[variant] ?? 'catalog'; + item.icon?.forEach((icon) => { const merchIcon = createTag('merch-icon', { slot: 'icons', src: icon, @@ -73,54 +60,89 @@ async function parseMerchCard(item, merchCard) { href: '', size: 'l', }); - merchCard.append(merchIcon); + appendFn(merchIcon); }); - if (cardJson.title) { - merchCard.append( + if (item.title && cardMapping.title) { + appendFn( + createTag( + cardMapping.title.tag, + { slot: cardMapping.title.slot }, + item.title, + ), + ); + } + + if (item.backgroundImage && cardMapping.backgroundImage) { + // TODO improve image logic + appendFn( createTag( - cardType.title.tag, - { slot: cardType.title.slot }, - cardJson.title, + cardMapping.backgroundImage.tag, + { slot: cardMapping.backgroundImage.slot }, + ``, ), ); } - if (cardJson.prices) { - const prices = cardJson.prices; + if (item.prices && cardMapping.prices) { + const prices = item.prices; const headingM = createTag( - cardType.prices.tag, - { slot: cardType.prices.slot }, + cardMapping.prices.tag, + { slot: cardMapping.prices.slot }, prices, ); - merchCard.append(headingM); + appendFn(headingM); } - merchCard.append( - createTag('p', { slot: 'body-xxs', id: 'individuals1' }, 'Desktop'), - ); - - if (cardJson.description) { + if (item.description && cardMapping.description) { const body = createTag( - cardType.description.tag, - { slot: cardType.description.slot }, - cardJson.description, + cardMapping.description.tag, + { slot: cardMapping.description.slot }, + item.description, ); - merchCard.append(body); + appendFn(body); } - if (cardJson.ctas) { - let ctas = cardJson.ctas; - const footer = createTag('div', { slot: 'footer' }, ctas); - merchCard.append(footer); + if (item.ctas) { + const footer = createTag('div', { slot: 'footer' }, item.ctas); + const ctas = []; + [...footer.querySelectorAll('a')].forEach((cta) => { + const strong = cta.parentElement.tagName === 'STRONG'; + if (consonant) { + cta.classList.add('con-button'); + if (strong) { + cta.classList.add('blue'); + } + ctas.push(cta); + } else { + const treatment = strong ? 'fill' : 'outline'; + const variant = strong ? 'accent' : 'primary'; + const spectrumCta = createTag( + 'sp-button', + { treatment, variant }, + cta, + ); + spectrumCta.addEventListener('click', (e) => { + /* c8 ignore next 2 */ + e.stopPropagation(); + cta.click(); + }); + ctas.push(spectrumCta); + } + }); + footer.innerHTML = ''; + footer.append(...ctas); + appendFn(footer); } } class FragmentCache { #fragmentCache = new Map(); + clear() { this.#fragmentCache.clear(); } + add(...items) { items.forEach((item) => { const { path } = item; @@ -129,12 +151,15 @@ class FragmentCache { } }); } + has(path) { return this.#fragmentCache.has(path); } + get(path) { return this.#fragmentCache.get(path); } + remove(path) { this.#fragmentCache.delete(path); } @@ -148,18 +173,33 @@ const cache = new FragmentCache(); */ export class MerchDataSource extends HTMLElement { /** - * @type {import('./aem.js').AEM} + * @type {import('@adobe/mas-web-components').AEM} */ #aem; cache = cache; + /** + * @type {HtmlElement[]} + */ + refs = []; + /** * @type {string} fragment path */ path; + /** + * Consonant styling for CTAs. + */ + consonant = false; + + /** + * Internal promise to track the readiness of the web-component to render. + */ + #readyPromise; + static get observedAttributes() { - return ['source', 'path']; + return ['source', 'path', 'consonant']; } attributeChangedCallback(name, oldValue, newValue) { @@ -167,41 +207,59 @@ export class MerchDataSource extends HTMLElement { } connectedCallback() { + this.consonant = this.hasAttribute('consonant'); + this.clearRefs(); const bucket = - this.getAttribute(ATTR_AEM_BUCKET) ?? - document.querySelector('mas-studio')?.getAttribute(ATTR_AEM_BUCKET); + this.getAttribute(ATTR_AEM_BUCKET) ?? 'publish-p22655-e59341'; this.#aem = new AEM(bucket); - this.fetchData(); + this.refresh(false); } - refresh() { - this.cache.remove(this.path); - this.fetchData(); + clearRefs() { + this.refs.forEach((ref) => { + ref.remove(); + }); + } + + async refresh(flushCache = true) { + if (!this.path) return; + + if (this.#readyPromise) { + const ready = await Promise.race([ + this.#readyPromise, + Promise.resolve(false), + ]); + if (!ready) return; // already fetching data + } + + this.clearRefs(); + this.refs = []; + if (flushCache) { + this.cache.remove(this.path); + } + this.#readyPromise = this.fetchData().then(() => true); } async fetchData() { let item = cache.get(this.path); if (!item) { - item = await this.#aem.sites.cf.fragments.getCfByPath(this.path); + item = await this.#aem.sites.cf.fragments.getByPath(this.path); + cache.add(item); } if (item) { - parseMerchCard(item, this.parentElement); - this.render(); + const appendFn = (element) => { + this.parentElement.appendChild(element); + this.refs.push(element); + }; + parseMerchCard(item, appendFn, this.parentElement, this.consonant); return; } - - this.render(); } - async render() { - if (!this.isConnected) return; - if (this.parentElement.tagName !== 'MERCH-CARD') return; - await Promise.all( - [ - ...this.parentElement.querySelectorAll( - '[is="inline-price"],[is="checkout-link"]', - ), - ].map((el) => el.onceSettled()), + get updateComplete() { + return ( + this.#readyPromise ?? + Promise.reject(new Error('datasource is not correctly configured')) ); } } diff --git a/libs/features/mas/web-components/src/plans-modal.js b/libs/features/mas/web-components/src/plans-modal.js index 30b6fb4867..6e9f9e4a1e 100644 --- a/libs/features/mas/web-components/src/plans-modal.js +++ b/libs/features/mas/web-components/src/plans-modal.js @@ -1,17 +1,7 @@ import { LitElement, html } from 'lit'; -import '@spectrum-web-components/theme/theme-light.js'; -import '@spectrum-web-components/theme/scale-large.js'; -import '@spectrum-web-components/theme/sp-theme.js'; -import '@spectrum-web-components/dialog/sp-dialog-wrapper.js'; -import '@spectrum-web-components/button/sp-button.js'; -import '@spectrum-web-components/overlay/sp-overlay.js'; - -import { Overlay } from '@spectrum-web-components/overlay'; import { MatchMediaController } from '@spectrum-web-components/reactive-controllers/src/MatchMedia.js'; -import './global.css.js'; - import styles from './plans-modal.css.js'; import { MOBILE_LANDSCAPE } from './media.js'; diff --git a/libs/features/mas/web-components/src/sidenav/merch-sidenav.js b/libs/features/mas/web-components/src/sidenav/merch-sidenav.js index 9fefd5033c..737c741e37 100644 --- a/libs/features/mas/web-components/src/sidenav/merch-sidenav.js +++ b/libs/features/mas/web-components/src/sidenav/merch-sidenav.js @@ -7,13 +7,6 @@ import './merch-sidenav-checkbox-group.js'; import { SPECTRUM_MOBILE_LANDSCAPE, TABLET_DOWN } from '../media.js'; import { disableBodyScroll, enableBodyScroll } from '../bodyScrollLock.js'; -document.addEventListener('sp-opened', () => { - document.body.classList.add('merch-modal'); -}); -document.addEventListener('sp-closed', () => { - document.body.classList.remove('merch-modal'); -}); - export class MerchSideNav extends LitElement { static properties = { sidenavTitle: { type: String }, @@ -137,11 +130,13 @@ export class MerchSideNav extends LitElement { closeModal(e) { e.preventDefault(); this.dialog?.close(); + document.body.classList.remove('merch-modal'); } openModal() { this.updateComplete.then(async () => { disableBodyScroll(this.dialog); + document.body.classList.add('merch-modal'); const options = { trigger: this.#target, notImmediatelyClosable: true, diff --git a/libs/features/mas/web-components/src/utils.js b/libs/features/mas/web-components/src/utils.js index 7ce5cea037..beda6ed6d5 100644 --- a/libs/features/mas/web-components/src/utils.js +++ b/libs/features/mas/web-components/src/utils.js @@ -1,5 +1,3 @@ -import { CLASS_NAME_HIDDEN, NAMESPACE } from './constants.js'; - export function debounce(func, delay) { let debounceTimer; return function () { @@ -13,141 +11,20 @@ export function debounce(func, delay) { export const getSlotText = (element, name) => element.querySelector(`[slot="${name}"]`).textContent.trim(); -/** - * Dispatches custom event of the given `type` on the given `target`. - * @template T - * @param {EventTarget} target - * @param {string} type - * @param {CustomEventInit} options - */ -export const dispatchAsyncEvent = ( - target, - type, - { bubbles = true, cancelable, composed, detail } = {}, -) => - window.setTimeout(() => - target?.dispatchEvent( - new CustomEvent(type, { - bubbles, - cancelable, - composed, - detail, - }), - ), - ); - -/** - * Finds the closest common ancestor for given array of DOM elements. - * Note: the search may go up DOM tree beyoud some expected container (e.g. card) and event result in `document.body`. - * For a good solution, @see https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition - * @param { Element[] } elements - * Array of DOM elements to find the common ancestor for. - * @returns { Element | null } - * The closest common ancestor of the given elements or `null` if none found. - */ -export function getCommonAncestor(...elements) { - const [first, ...rest] = elements; - if (!first) return null; - let ancestor = first.parentElement; - if (!rest.length) return ancestor; - while (ancestor) { - if (rest.every((element) => ancestor.contains(element))) { - return ancestor; - } - ancestor = ancestor.parentElement; - } - return null; -} - -/** - * @param {Commerce.Placeholder} placeholder - * @returns {boolean} - */ -export function isRegularPrice(placeholder) { - if (placeholder.isInlinePrice) { - const { template } = placeholder.dataset; - if (template === 'price' || !template) { - return true; - } - } - return false; -} - -/** - * Joins an array of string `tokens`, - * filtering out `falsy` values and replacing non-word characters in each token. - * @param { string[] } tokens - * Array of strings to be joined. - * @param { string } delimiter - * Delimiter used to join the tokens and replace non-word characters. - */ -const joinTokens = (tokens, delimiter) => - tokens - .flatMap((token) => token?.split?.(/\W+/g)) - .filter((token, index) => token && (index > 0 || token !== NAMESPACE)) - .join(delimiter); - -/** - * Creates a namespaced CSS class name. - * Replaces any sequence of non-word characters with a single hyphen. - * @param {...string} args - Components of the class name. - * @returns {string} - The namespaced and cleaned CSS class name. - */ -export const makeCssClassName = (...args) => - `${NAMESPACE}-${joinTokens(args, '-')}`; - -/** - * Creates a namespaced event type. - * Replaces any sequence of non-word characters with a single colon. - * @param {...string} args - Components of the event type. - * @returns {string} The namespaced and cleaned event type. - */ -export const makeEventTypeName = (...args) => - `${NAMESPACE}:${joinTokens(args, ':')}`; - -/** - * @param {Commerce.Offers} offers - * @param {Commerce.filterOffer} filter - */ -export function showOffers(offers, filter = () => true) { - let i = 0; - for (const offer of offers.values()) { - offer.container?.classList.toggle( - CLASS_NAME_HIDDEN, - !filter(offer, i++), - ); - } -} - -/** - * Returns the text nodes of the given element. - * @param {HTMLElement} element - * @returns array of text nodes - */ -export function getTextNodes(element) { - const textNodes = []; - function findTextNodes(node) { - if (node.nodeType === Node.TEXT_NODE) { - textNodes.push(node); - } else { - node.childNodes.forEach(findTextNodes); - } - } - - findTextNodes(element); - return textNodes; -} - /** * Helper function to create an element with attributes * @param {string} tag * @param {Object} attributes - * @param {*} innerHTML + * @param {*} content * @returns {HTMLElement} */ -export function createTag(tag, attributes = {}, innerHTML) { +export function createTag(tag, attributes = {}, content) { const element = document.createElement(tag); - element.innerHTML = innerHTML; + if (content instanceof HTMLElement) { + element.appendChild(content); + } else { + element.innerHTML = content; + } // Set attributes for (const [key, value] of Object.entries(attributes)) { @@ -161,7 +38,7 @@ export function createTag(tag, attributes = {}, innerHTML) { * @returns {boolean} True if the device is mobile, otherwise false. */ export function isMobile() { - return window.matchMedia('(max-width: 767px)').matches; + return window.matchMedia('(max-width: 767px)').matches; } /** @@ -169,5 +46,10 @@ export function isMobile() { * @returns {boolean} True if the device is mobile, otherwise false. */ export function isMobileOrTablet() { - return window.matchMedia('(max-width: 1024px)').matches; + return window.matchMedia('(max-width: 1024px)').matches; +} + +/* c8 ignore next 4 */ +export function wait(ms = 1000) { + return new Promise((resolve) => setTimeout(resolve, ms)); } diff --git a/libs/features/mas/web-components/src/variants/catalog.css.js b/libs/features/mas/web-components/src/variants/catalog.css.js new file mode 100644 index 0000000000..db23735641 --- /dev/null +++ b/libs/features/mas/web-components/src/variants/catalog.css.js @@ -0,0 +1,89 @@ +import { TABLET_UP, DESKTOP_UP, LARGE_DESKTOP,} from '../media.js'; + +export const CSS = ` +:root { + --consonant-merch-card-catalog-width: 276px; + --consonant-merch-card-catalog-icon-size: 40px; +} +.one-merch-card.catalog, +.two-merch-cards.catalog, +.three-merch-cards.catalog, +.four-merch-cards.catalog { + grid-template-columns: var(--consonant-merch-card-catalog-width); +} + +@media screen and ${TABLET_UP} { + :root { + --consonant-merch-card-catalog-width: 302px; + } + + .two-merch-cards.catalog, + .three-merch-cards.catalog, + .four-merch-cards.catalog { + grid-template-columns: repeat(2, var(--consonant-merch-card-catalog-width)); + } +} + +@media screen and ${DESKTOP_UP} { + :root { + --consonant-merch-card-catalog-width: 276px; + } + + .three-merch-cards.catalog, + .four-merch-cards.catalog { + grid-template-columns: repeat(3, var(--consonant-merch-card-catalog-width)); + } +} + +@media screen and ${LARGE_DESKTOP} { + .four-merch-cards.catalog { + grid-template-columns: repeat(4, var(--consonant-merch-card-catalog-width)); + } +} + +merch-card[variant="catalog"] [slot="action-menu-content"] { + background-color: #000; + color: var(--color-white, #fff); + font-size: var(--consonant-merch-card-body-xs-font-size); + width: fit-content; + padding: var(--consonant-merch-spacing-xs); + border-radius: var(--consonant-merch-spacing-xxxs); + position: absolute; + top: 55px; + right: 15px; + line-height: var(--consonant-merch-card-body-line-height); +} + +merch-card[variant="catalog"] [slot="action-menu-content"] ul { + padding-left: 0; + padding-bottom: var(--consonant-merch-spacing-xss); + margin-top: 0; + margin-bottom: 0; + list-style-position: inside; + list-style-type: '• '; +} + +merch-card[variant="catalog"] [slot="action-menu-content"] ul li { + padding-left: 0; + line-height: var(--consonant-merch-card-body-line-height); +} + +merch-card[variant="catalog"] [slot="action-menu-content"] ::marker { + margin-right: 0; +} + +merch-card[variant="catalog"] [slot="action-menu-content"] p { + color: var(--color-white, #fff); +} + +merch-card[variant="catalog"] [slot="action-menu-content"] a { + color: var(--consonant-merch-card-background-color); + text-decoration: underline; +} + +merch-card[variant="catalog"] .payment-details { + font-size: var(--consonant-merch-card-body-font-size); + font-style: italic; + font-weight: 400; + line-height: var(--consonant-merch-card-body-line-height); +}`; diff --git a/libs/features/mas/web-components/src/variants/catalog.js b/libs/features/mas/web-components/src/variants/catalog.js new file mode 100644 index 0000000000..8a9f80162c --- /dev/null +++ b/libs/features/mas/web-components/src/variants/catalog.js @@ -0,0 +1,97 @@ +import { VariantLayout } from './variant-layout.js'; +import { html, css } from 'lit'; +import { isMobileOrTablet } from '../utils.js'; +import { EVENT_MERCH_CARD_ACTION_MENU_TOGGLE } from '../constants.js'; +import { CSS } from './catalog.css.js'; + +export class Catalog extends VariantLayout { + constructor(card) { + super(card); + } + + renderLayout() { + return html`
+
+ ${this.badge} +
+
+ ${this.card.actionMenuContent} + + + + ${!this.promoBottom + ? html`` + : ''} + + ${this.promoBottom + ? html`` + : ''} +
+ ${this.secureLabelFooter}`; + } + + getGlobalCSS() { + return CSS; + } + + toggleActionMenu = (e) => { + //beware this is an event on card, so this points to the card, not the layout + const retract = e?.type === 'mouseleave' ? true : undefined; + const actionMenuContentSlot = this.card.shadowRoot.querySelector( + 'slot[name="action-menu-content"]', + ); + if (!actionMenuContentSlot) return; + if (!retract) { + this.card.dispatchEvent( + new CustomEvent(EVENT_MERCH_CARD_ACTION_MENU_TOGGLE, { + bubbles: true, + composed: true, + detail: { + card: this.card.name, + type: 'action-menu', + }, + }), + ); + } + actionMenuContentSlot.classList.toggle('hidden', retract); + } + + connectedCallbackHook() { + this.card.addEventListener('mouseleave', this.toggleActionMenu); + } + disconnectedCallbackHook() { + this.card.removeEventListener('mouseleave', this.toggleActionMenu); + } + static variantStyle = css` + :host([variant='catalog']) { + min-height: 330px; + } + + .body .catalog-badge { + display: flex; + height: fit-content; + flex-direction: column; + width: fit-content; + max-width: 140px; + border-radius: 5px; + position: relative; + top: 0; + margin-left: var(--consonant-merch-spacing-xxs); + box-sizing: border-box; + } + `; +} diff --git a/libs/features/mas/web-components/src/variants/ccd-action.css.js b/libs/features/mas/web-components/src/variants/ccd-action.css.js new file mode 100644 index 0000000000..1bae9d8f3c --- /dev/null +++ b/libs/features/mas/web-components/src/variants/ccd-action.css.js @@ -0,0 +1,40 @@ +import { TABLET_UP, DESKTOP_UP, LARGE_DESKTOP } from "../media.js"; + +export const CSS = ` +:root { + --consonant-merch-card-ccd-action-width: 276px; + --consonant-merch-card-ccd-action-min-height: 320px; +} + +.one-merch-card.ccd-action, +.two-merch-cards.ccd-action, +.three-merch-cards.ccd-action, +.four-merch-cards.ccd-action { + grid-template-columns: var(--consonant-merch-card-ccd-action-width); +} + +merch-card[variant="ccd-action"] .price-strikethrough { + font-size: 18px; +} + +@media screen and ${TABLET_UP} { + .two-merch-cards.ccd-action, + .three-merch-cards.ccd-action, + .four-merch-cards.ccd-action { + grid-template-columns: repeat(2, var(--consonant-merch-card-ccd-action-width)); + } +} + +@media screen and ${DESKTOP_UP} { + .three-merch-cards.ccd-action, + .four-merch-cards.ccd-action { + grid-template-columns: repeat(3, var(--consonant-merch-card-ccd-action-width)); + } +} + +@media screen and ${LARGE_DESKTOP} { + .four-merch-cards.ccd-action { + grid-template-columns: repeat(4, var(--consonant-merch-card-ccd-action-width)); + } +} +`; diff --git a/libs/features/mas/web-components/src/variants/ccd-action.js b/libs/features/mas/web-components/src/variants/ccd-action.js new file mode 100644 index 0000000000..5fb02cc9a5 --- /dev/null +++ b/libs/features/mas/web-components/src/variants/ccd-action.js @@ -0,0 +1,29 @@ +import { VariantLayout } from './variant-layout.js'; +import { html, css } from 'lit'; +import { CSS } from './ccd-action.css.js'; + +export class CCDAction extends VariantLayout { + constructor(card) { + super(card); + } + + getGlobalCSS() { + return CSS; + } + + renderLayout() { + return html`
+ ${this.badge} + + + ${this.promoBottom ? html`` : html``} +
+ +
`; + } + static variantStyle = css` + :host([variant='ccd-action']:not([size])) { + width: var(--consonant-merch-card-ccd-action-width); + } + `; +} diff --git a/libs/features/mas/web-components/src/variants/image.css.js b/libs/features/mas/web-components/src/variants/image.css.js new file mode 100644 index 0000000000..30551c0a84 --- /dev/null +++ b/libs/features/mas/web-components/src/variants/image.css.js @@ -0,0 +1,38 @@ +import { TABLET_UP, DESKTOP_UP, LARGE_DESKTOP,} from '../media.js'; +export const CSS = ` +:root { + --consonant-merch-card-image-width: 300px; +} + +.one-merch-card.image, +.two-merch-cards.image, +.three-merch-cards.image, +.four-merch-cards.image { + grid-template-columns: var(--consonant-merch-card-image-width); +} + +@media screen and ${TABLET_UP} { + .two-merch-cards.image, + .three-merch-cards.image, + .four-merch-cards.image { + grid-template-columns: repeat(2, var(--consonant-merch-card-image-width)); + } +} + +@media screen and ${DESKTOP_UP} { + :root { + --consonant-merch-card-image-width: 378px; + } + + .three-merch-cards.image, + .four-merch-cards.image { + grid-template-columns: repeat(3, var(--consonant-merch-card-image-width)); + } +} + +@media screen and ${LARGE_DESKTOP} { + .four-merch-cards.image { + grid-template-columns: repeat(4, var(--consonant-merch-card-image-width)); + } +} +`; diff --git a/libs/features/mas/web-components/src/variants/image.js b/libs/features/mas/web-components/src/variants/image.js new file mode 100644 index 0000000000..dcc5599bd3 --- /dev/null +++ b/libs/features/mas/web-components/src/variants/image.js @@ -0,0 +1,36 @@ +import { VariantLayout } from "./variant-layout.js"; +import { html } from 'lit'; +import { CSS } from './image.css.js'; + +export class Image extends VariantLayout { + constructor(card) { + super(card); + } + + getGlobalCSS() { + return CSS; + } + + renderLayout() { + return html`${this.cardImage} +
+ + + + ${this.promoBottom ? html`` : html``} +
+ ${this.evergreen + ? html` +
+ +
+ ` + : html` +
+ ${this.secureLabelFooter} + `}`; + } +} diff --git a/libs/features/mas/web-components/src/variants/inline-heading.css.js b/libs/features/mas/web-components/src/variants/inline-heading.css.js new file mode 100644 index 0000000000..7bb9c25a1e --- /dev/null +++ b/libs/features/mas/web-components/src/variants/inline-heading.css.js @@ -0,0 +1,39 @@ +import { TABLET_UP, DESKTOP_UP, LARGE_DESKTOP,} from '../media.js'; + +export const CSS = ` +:root { + --consonant-merch-card-inline-heading-width: 300px; +} + +.one-merch-card.inline-heading, +.two-merch-cards.inline-heading, +.three-merch-cards.inline-heading, +.four-merch-cards.inline-heading { + grid-template-columns: var(--consonant-merch-card-inline-heading-width); +} + +@media screen and ${TABLET_UP} { + .two-merch-cards.inline-heading, + .three-merch-cards.inline-heading, + .four-merch-cards.inline-heading { + grid-template-columns: repeat(2, var(--consonant-merch-card-inline-heading-width)); + } +} + +@media screen and ${DESKTOP_UP} { + :root { + --consonant-merch-card-inline-heading-width: 378px; + } + + .three-merch-cards.inline-heading, + .four-merch-cards.inline-heading { + grid-template-columns: repeat(3, var(--consonant-merch-card-inline-heading-width)); + } +} + +@media screen and ${LARGE_DESKTOP} { + .four-merch-cards.inline-heading { + grid-template-columns: repeat(4, var(--consonant-merch-card-inline-heading-width)); + } +} +`; diff --git a/libs/features/mas/web-components/src/variants/inline-heading.js b/libs/features/mas/web-components/src/variants/inline-heading.js new file mode 100644 index 0000000000..a038819f3c --- /dev/null +++ b/libs/features/mas/web-components/src/variants/inline-heading.js @@ -0,0 +1,24 @@ +import { VariantLayout } from './variant-layout'; +import { html } from 'lit'; +import { CSS } from './inline-heading.css.js' +export class InlineHeading extends VariantLayout { + constructor(card) { + super(card); + } + + getGlobalCSS() { + return CSS; + } + + renderLayout() { + return html` ${this.badge} +
+
+ + +
+ +
+ ${!this.card.customHr ? html`
` : ''} ${this.secureLabelFooter}`; + } +} diff --git a/libs/features/mas/web-components/src/variants/mini-compare-chart.css.js b/libs/features/mas/web-components/src/variants/mini-compare-chart.css.js new file mode 100644 index 0000000000..167102700d --- /dev/null +++ b/libs/features/mas/web-components/src/variants/mini-compare-chart.css.js @@ -0,0 +1,253 @@ +import { MOBILE_LANDSCAPE, TABLET_DOWN, TABLET_UP, DESKTOP_UP, LARGE_DESKTOP } from "../media.js"; +export const CSS = ` + :root { + --consonant-merch-card-mini-compare-chart-icon-size: 32px; + --consonant-merch-card-mini-compare-mobile-cta-font-size: 15px; + --consonant-merch-card-mini-compare-mobile-cta-width: 75px; + --consonant-merch-card-mini-compare-badge-mobile-max-width: 50px; + } + + merch-card[variant="mini-compare-chart"] [slot="heading-m"] { + padding: 0 var(--consonant-merch-spacing-s) 0; + } + + merch-card[variant="mini-compare-chart"] [slot="body-m"] { + padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s); + } + + merch-card[variant="mini-compare-chart"] [is="inline-price"] { + display: inline-block; + min-height: 30px; + min-width: 1px; + } + + merch-card[variant="mini-compare-chart"] [slot='callout-content'] { + padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s) 0px; + } + + merch-card[variant="mini-compare-chart"] [slot='callout-content'] [is="inline-price"] { + min-height: unset; + } + + merch-card[variant="mini-compare-chart"] [slot="price-commitment"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + padding: 0 var(--consonant-merch-spacing-s); + } + + merch-card[variant="mini-compare-chart"] [slot="price-commitment"] a { + display: inline-block; + height: 27px; + } + + merch-card[variant="mini-compare-chart"] [slot="offers"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + } + + merch-card[variant="mini-compare-chart"] [slot="body-xxs"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s) 0; + } + + merch-card[variant="mini-compare-chart"] [slot="promo-text"] { + font-size: var(--consonant-merch-card-body-m-font-size); + padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s) 0; + } + + merch-card[variant="mini-compare-chart"] [slot="promo-text"] a { + text-decoration: underline; + } + + merch-card[variant="mini-compare-chart"] .footer-row-icon { + display: flex; + place-items: center; + } + + merch-card[variant="mini-compare-chart"] .footer-row-icon img { + max-width: initial; + width: var(--consonant-merch-card-mini-compare-chart-icon-size); + height: var(--consonant-merch-card-mini-compare-chart-icon-size); + } + + merch-card[variant="mini-compare-chart"] .footer-row-cell { + border-top: 1px solid var(--consonant-merch-card-border-color); + display: flex; + gap: var(--consonant-merch-spacing-xs); + justify-content: start; + place-items: center; + padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s); + } + + merch-card[variant="mini-compare-chart"] .footer-row-cell-description { + font-size: var(--consonant-merch-card-body-s-font-size); + line-height: var(--consonant-merch-card-body-s-line-height); + } + + merch-card[variant="mini-compare-chart"] .footer-row-cell-description p { + color: var(--merch-color-grey-80); + vertical-align: bottom; + } + + merch-card[variant="mini-compare-chart"] .footer-row-cell-description a { + color: var(--color-accent); + text-decoration: solid; + } + +.one-merch-card.mini-compare-chart { + grid-template-columns: var(--consonant-merch-card-mini-compare-chart-wide-width); + gap: var(--consonant-merch-spacing-xs); +} + +.two-merch-cards.mini-compare-chart, +.three-merch-cards.mini-compare-chart, +.four-merch-cards.mini-compare-chart { + grid-template-columns: repeat(2, var(--consonant-merch-card-mini-compare-chart-width)); + gap: var(--consonant-merch-spacing-xs); +} + +/* mini compare mobile */ +@media screen and ${MOBILE_LANDSCAPE} { + :root { + --consonant-merch-card-mini-compare-chart-width: 302px; + --consonant-merch-card-mini-compare-chart-wide-width: 302px; + } + + .two-merch-cards.mini-compare-chart, + .three-merch-cards.mini-compare-chart, + .four-merch-cards.mini-compare-chart { + grid-template-columns: var(--consonant-merch-card-mini-compare-chart-width); + gap: var(--consonant-merch-spacing-xs); + } + + merch-card[variant="mini-compare-chart"] [slot='heading-m'] { + font-size: var(--consonant-merch-card-body-s-font-size); + line-height: var(--consonant-merch-card-body-s-line-height); + } + + merch-card[variant="mini-compare-chart"] [slot='heading-m-price'] { + font-size: var(--consonant-merch-card-body-s-font-size); + line-height: var(--consonant-merch-card-body-s-line-height); + } + + merch-card[variant="mini-compare-chart"] [slot='body-m'] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + } + + merch-card[variant="mini-compare-chart"] [slot="promo-text"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + } + merch-card[variant="mini-compare-chart"] .footer-row-cell-description { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + } +} + +@media screen and ${TABLET_DOWN} { + .three-merch-cards.mini-compare-chart merch-card [slot="footer"] a, + .four-merch-cards.mini-compare-chart merch-card [slot="footer"] a { + flex: 1; + } + + merch-card[variant="mini-compare-chart"] [slot='heading-m'] { + font-size: var(--consonant-merch-card-body-s-font-size); + line-height: var(--consonant-merch-card-body-s-line-height); + } + + merch-card[variant="mini-compare-chart"] [slot='heading-m-price'] { + font-size: var(--consonant-merch-card-body-s-font-size); + line-height: var(--consonant-merch-card-body-s-line-height); + } + + merch-card[variant="mini-compare-chart"] [slot='body-m'] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + } + + merch-card[variant="mini-compare-chart"] [slot="promo-text"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + } + + merch-card[variant="mini-compare-chart"] .footer-row-cell-description { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + } +} +@media screen and ${TABLET_UP} { + :root { + --consonant-merch-card-mini-compare-chart-width: 302px; + --consonant-merch-card-mini-compare-chart-wide-width: 302px; + } + + .two-merch-cards.mini-compare-chart { + grid-template-columns: repeat(2, minmax(var(--consonant-merch-card-mini-compare-chart-width), var(--consonant-merch-card-mini-compare-chart-wide-width))); + gap: var(--consonant-merch-spacing-m); + } + + .three-merch-cards.mini-compare-chart, + .four-merch-cards.mini-compare-chart { + grid-template-columns: repeat(2, minmax(var(--consonant-merch-card-mini-compare-chart-width), var(--consonant-merch-card-mini-compare-chart-wide-width))); + } +} + +/* desktop */ +@media screen and ${DESKTOP_UP} { + :root { + --consonant-merch-card-mini-compare-chart-width: 378px; + --consonant-merch-card-mini-compare-chart-wide-width: 484px; + } + .one-merch-card.mini-compare-chart { + grid-template-columns: var(--consonant-merch-card-mini-compare-chart-wide-width); + } + + .two-merch-cards.mini-compare-chart { + grid-template-columns: repeat(2, var(--consonant-merch-card-mini-compare-chart-wide-width)); + gap: var(--consonant-merch-spacing-m); + } + + .three-merch-cards.mini-compare-chart, + .four-merch-cards.mini-compare-chart { + grid-template-columns: repeat(3, var(--consonant-merch-card-mini-compare-chart-width)); + gap: var(--consonant-merch-spacing-m); + } +} + +@media screen and ${LARGE_DESKTOP} { + .four-merch-cards.mini-compare-chart { + grid-template-columns: repeat(4, var(--consonant-merch-card-mini-compare-chart-width)); + } +} + +merch-card .footer-row-cell:nth-child(1) { + min-height: var(--consonant-merch-card-footer-row-1-min-height); +} + +merch-card .footer-row-cell:nth-child(2) { + min-height: var(--consonant-merch-card-footer-row-2-min-height); +} + +merch-card .footer-row-cell:nth-child(3) { + min-height: var(--consonant-merch-card-footer-row-3-min-height); +} + +merch-card .footer-row-cell:nth-child(4) { + min-height: var(--consonant-merch-card-footer-row-4-min-height); +} + +merch-card .footer-row-cell:nth-child(5) { + min-height: var(--consonant-merch-card-footer-row-5-min-height); +} + +merch-card .footer-row-cell:nth-child(6) { + min-height: var(--consonant-merch-card-footer-row-6-min-height); +} + +merch-card .footer-row-cell:nth-child(7) { + min-height: var(--consonant-merch-card-footer-row-7-min-height); +} + +merch-card .footer-row-cell:nth-child(8) { + min-height: var(--consonant-merch-card-footer-row-8-min-height); +} +`; diff --git a/libs/features/mas/web-components/src/variants/mini-compare-chart.js b/libs/features/mas/web-components/src/variants/mini-compare-chart.js new file mode 100644 index 0000000000..b72a092367 --- /dev/null +++ b/libs/features/mas/web-components/src/variants/mini-compare-chart.js @@ -0,0 +1,223 @@ +import { html, css, unsafeCSS } from 'lit'; +import { isMobile } from '../utils.js'; +import { VariantLayout } from './variant-layout.js'; +import { CSS } from './mini-compare-chart.css.js'; +import { DESKTOP_UP, TABLET_DOWN } from '../media.js'; +const FOOTER_ROW_MIN_HEIGHT = 32; // as per the XD. + +export class MiniCompareChart extends VariantLayout { + constructor(card) { + super(card); + } + + #container; + + getRowMinHeightPropertyName = (index) => + `--consonant-merch-card-footer-row-${index}-min-height`; + + getContainer() { + this.#container = this.#container ?? this.card.closest('[class*="-merch-cards"]') ?? this.card.parentElement; + return this.#container; + } + + getGlobalCSS() { + return CSS; + } + + getMiniCompareFooter = () => { + const secureLabel = this.card.secureLabel + ? html` + ${this.card.secureLabel}` + : html``; + return html`
${secureLabel}
`; + } + + updateMiniCompareElementMinHeight (el, name) { + const elMinHeightPropertyName = `--consonant-merch-card-mini-compare-${name}-height`; + const height = Math.max( + 0, + parseInt(window.getComputedStyle(el).height) || 0, + ); + const maxMinHeight = + parseInt( + this.getContainer().style.getPropertyValue(elMinHeightPropertyName), + ) || 0; + if (height > maxMinHeight) { + this.getContainer().style.setProperty( + elMinHeightPropertyName, + `${height}px`, + ); + } + } + + adjustMiniCompareBodySlots () { + if (this.card.getBoundingClientRect().width === 0) return; + + this.updateMiniCompareElementMinHeight( + this.card.shadowRoot.querySelector('.top-section'), + 'top-section', + ); + + const slots = [ + 'heading-m', + 'body-m', + 'heading-m-price', + 'price-commitment', + 'offers', + 'promo-text', + 'callout-content', + 'secure-transaction-label', + ]; + + slots.forEach((slot) => + this.updateMiniCompareElementMinHeight( + this.card.shadowRoot.querySelector(`slot[name="${slot}"]`), + slot, + ), + ); + this.updateMiniCompareElementMinHeight( + this.card.shadowRoot.querySelector('footer'), + 'footer', + ); + + const badge = this.card.shadowRoot.querySelector( + '.mini-compare-chart-badge', + ); + if (badge && badge.textContent !== '') { + this.getContainer().style.setProperty( + '--consonant-merch-card-mini-compare-top-section-mobile-height', + '32px', + ); + } + } + adjustMiniCompareFooterRows () { + if (this.card.getBoundingClientRect().width === 0) return; + const footerRows = this.card.querySelector('[slot="footer-rows"]'); + [...footerRows?.children].forEach((el, index) => { + const height = Math.max( + FOOTER_ROW_MIN_HEIGHT, + parseInt(window.getComputedStyle(el).height) || 0, + ); + const maxMinHeight = + parseInt( + this.getContainer().style.getPropertyValue( + this.getRowMinHeightPropertyName(index + 1), + ), + ) || 0; + if (height > maxMinHeight) { + this.getContainer().style.setProperty( + this.getRowMinHeightPropertyName(index + 1), + `${height}px`, + ); + } + }); + } + + removeEmptyRows() { + const footerRows = this.card.querySelectorAll('.footer-row-cell'); + footerRows.forEach((row) => { + const rowDescription = row.querySelector('.footer-row-cell-description'); + if (rowDescription) { + const isEmpty = !rowDescription.textContent.trim(); + if (isEmpty) { + row.remove(); + } + } + }); + } + + renderLayout () { + return html`
+ ${this.badge} +
+ + + + + + + + + ${this.getMiniCompareFooter()} + `; + } + postCardUpdateHook() { + if (!isMobile()) { + this.adjustMiniCompareBodySlots(); + this.adjustMiniCompareFooterRows(); + } else { + this.removeEmptyRows(); + } + } + static variantStyle = css` + :host([variant='mini-compare-chart']) > slot:not([name='icons']) { + display: block; + } + :host([variant='mini-compare-chart']) footer { + min-height: var(--consonant-merch-card-mini-compare-footer-height); + padding: var(--consonant-merch-spacing-xs); + } + + /* mini-compare card */ + :host([variant='mini-compare-chart']) .top-section { + padding-top: var(--consonant-merch-spacing-s); + padding-inline-start: var(--consonant-merch-spacing-s); + height: var(--consonant-merch-card-mini-compare-top-section-height); + } + + @media screen and ${unsafeCSS(TABLET_DOWN)} { + [class*'-merch-cards'] :host([variant='mini-compare-chart']) footer { + flex-direction: column; + align-items: stretch; + text-align: center; + } + } + + @media screen and ${unsafeCSS(DESKTOP_UP)} { + :host([variant='mini-compare-chart']) footer { + padding: var(--consonant-merch-spacing-xs) + var(--consonant-merch-spacing-s) + var(--consonant-merch-spacing-s) + var(--consonant-merch-spacing-s); + } + } + + :host([variant='mini-compare-chart']) slot[name='footer-rows'] { + flex: 1; + display: flex; + flex-direction: column; + justify-content: end; + } + /* mini-compare card heights for the slots: heading-m, body-m, heading-m-price, price-commitment, offers, promo-text, footer */ + :host([variant='mini-compare-chart']) slot[name='heading-m'] { + min-height: var(--consonant-merch-card-mini-compare-heading-m-height); + } + :host([variant='mini-compare-chart']) slot[name='body-m'] { + min-height: var(--consonant-merch-card-mini-compare-body-m-height); + } + :host([variant='mini-compare-chart']) slot[name='heading-m-price'] { + min-height: var( + --consonant-merch-card-mini-compare-heading-m-price-height + ); + } + :host([variant='mini-compare-chart']) slot[name='price-commitment'] { + min-height: var( + --consonant-merch-card-mini-compare-price-commitment-height + ); + } + :host([variant='mini-compare-chart']) slot[name='offers'] { + min-height: var(--consonant-merch-card-mini-compare-offers-height); + } + :host([variant='mini-compare-chart']) slot[name='promo-text'] { + min-height: var(--consonant-merch-card-mini-compare-promo-text-height); + } + :host([variant='mini-compare-chart']) slot[name='callout-content'] { + min-height: var( + --consonant-merch-card-mini-compare-callout-content-height + ); + } + `; +}; diff --git a/libs/features/mas/web-components/src/variants/plans.css.js b/libs/features/mas/web-components/src/variants/plans.css.js new file mode 100644 index 0000000000..d4f22fc53d --- /dev/null +++ b/libs/features/mas/web-components/src/variants/plans.css.js @@ -0,0 +1,56 @@ +import { TABLET_UP, DESKTOP_UP, LARGE_DESKTOP,} from '../media.js'; +export const CSS = ` +:root { + --consonant-merch-card-plans-width: 300px; + --consonant-merch-card-plans-icon-size: 40px; +} + +merch-card[variant="plans"] [slot="description"] { + min-height: 84px; +} + +merch-card[variant="plans"] [slot="quantity-select"] { + display: flex; + justify-content: flex-start; + box-sizing: border-box; + width: 100%; + padding: var(--consonant-merch-spacing-xs); +} + +.one-merch-card.plans, +.two-merch-cards.plans, +.three-merch-cards.plans, +.four-merch-cards.plans { + grid-template-columns: var(--consonant-merch-card-plans-width); +} + +/* Tablet */ +@media screen and ${TABLET_UP} { + :root { + --consonant-merch-card-plans-width: 302px; + } + .two-merch-cards.plans, + .three-merch-cards.plans, + .four-merch-cards.plans { + grid-template-columns: repeat(2, var(--consonant-merch-card-plans-width)); + } +} + +/* desktop */ +@media screen and ${DESKTOP_UP} { + :root { + --consonant-merch-card-plans-width: 276px; + } + .three-merch-cards.plans, + .four-merch-cards.plans { + grid-template-columns: repeat(3, var(--consonant-merch-card-plans-width)); + } +} + +/* Large desktop */ + @media screen and ${LARGE_DESKTOP} { + .four-merch-cards.plans { + grid-template-columns: repeat(4, var(--consonant-merch-card-plans-width)); + } +} +`; diff --git a/libs/features/mas/web-components/src/variants/plans.js b/libs/features/mas/web-components/src/variants/plans.js new file mode 100644 index 0000000000..02fcdb7dc1 --- /dev/null +++ b/libs/features/mas/web-components/src/variants/plans.js @@ -0,0 +1,53 @@ +import { VariantLayout } from "./variant-layout"; +import { html, css } from 'lit'; +import { CSS } from './plans.css.js'; + +export class Plans extends VariantLayout { + constructor(card) { + super(card); + } + + getGlobalCSS() { + return CSS; + } + + postCardUpdateHook() { + this.adjustTitleWidth(); + } + + get stockCheckbox() { + return this.card.checkboxLabel + ? html`` + : ''; + } + + renderLayout() { + return html` ${this.badge} +
+ + + + + ${!this.promoBottom ? html` ` : ''} + + ${this.promoBottom ? html` ` : ''} + ${this.stockCheckbox} +
+ + ${this.secureLabelFooter}`; + } + + static variantStyle = css` + :host([variant='plans']) { + min-height: 348px; + } + + :host([variant='plans']) ::slotted([slot='heading-xs']) { + max-width: var(--consonant-merch-card-heading-xs-max-width, 100%); + } + `; +} diff --git a/libs/features/mas/web-components/src/variants/product.css.js b/libs/features/mas/web-components/src/variants/product.css.js new file mode 100644 index 0000000000..0bfb799d11 --- /dev/null +++ b/libs/features/mas/web-components/src/variants/product.css.js @@ -0,0 +1,42 @@ +import { TABLET_UP, DESKTOP_UP, LARGE_DESKTOP,} from '../media.js'; +export const CSS = ` +:root { + --consonant-merch-card-product-width: 300px; +} + +/* grid style for product */ +.one-merch-card.product, +.two-merch-cards.product, +.three-merch-cards.product, +.four-merch-cards.product { + grid-template-columns: var(--consonant-merch-card-product-width); +} + +/* Tablet */ +@media screen and ${TABLET_UP} { + .two-merch-cards.product, + .three-merch-cards.product, + .four-merch-cards.product { + grid-template-columns: repeat(2, var(--consonant-merch-card-product-width)); + } +} + +/* desktop */ +@media screen and ${DESKTOP_UP} { + :root { + --consonant-merch-card-product-width: 378px; + } + + .three-merch-cards.product, + .four-merch-cards.product { + grid-template-columns: repeat(3, var(--consonant-merch-card-product-width)); + } +} + +/* Large desktop */ +@media screen and ${LARGE_DESKTOP} { + .four-merch-cards.product { + grid-template-columns: repeat(4, var(--consonant-merch-card-product-width)); + } +} +`; diff --git a/libs/features/mas/web-components/src/variants/product.js b/libs/features/mas/web-components/src/variants/product.js new file mode 100644 index 0000000000..b9aced8379 --- /dev/null +++ b/libs/features/mas/web-components/src/variants/product.js @@ -0,0 +1,26 @@ +import { VariantLayout } from "./variant-layout"; +import { html } from 'lit'; +import { CSS } from './product.css.js'; + +export class Product extends VariantLayout { + constructor(card) { + super(card); + } + + getGlobalCSS() { + return CSS; + } + + renderLayout() { + return html` ${this.badge} +
+ + + + ${!this.promoBottom ? html`` : ''} + + ${this.promoBottom ? html`` : ''} +
+ ${this.secureLabelFooter}`; + } +} diff --git a/libs/features/mas/web-components/src/variants/segment.css.js b/libs/features/mas/web-components/src/variants/segment.css.js new file mode 100644 index 0000000000..010b74bdf8 --- /dev/null +++ b/libs/features/mas/web-components/src/variants/segment.css.js @@ -0,0 +1,48 @@ +import { TABLET_UP, DESKTOP_UP, MOBILE_LANDSCAPE,} from '../media.js'; +export const CSS = ` +:root { + --consonant-merch-card-segment-width: 378px; +} + +/* grid style for segment */ +.one-merch-card.segment, +.two-merch-cards.segment, +.three-merch-cards.segment, +.four-merch-cards.segment { + grid-template-columns: minmax(276px, var(--consonant-merch-card-segment-width)); +} + +/* Mobile */ +@media screen and ${MOBILE_LANDSCAPE} { + :root { + --consonant-merch-card-segment-width: 276px; + } +} + +@media screen and ${TABLET_UP} { + :root { + --consonant-merch-card-segment-width: 276px; + } + + .two-merch-cards.segment, + .three-merch-cards.segment, + .four-merch-cards.segment { + grid-template-columns: repeat(2, minmax(276px, var(--consonant-merch-card-segment-width))); + } +} + +/* desktop */ +@media screen and ${DESKTOP_UP} { + :root { + --consonant-merch-card-segment-width: 302px; + } + + .three-merch-cards.segment { + grid-template-columns: repeat(3, minmax(276px, var(--consonant-merch-card-segment-width))); + } + + .four-merch-cards.segment { + grid-template-columns: repeat(4, minmax(276px, var(--consonant-merch-card-segment-width))); + } +} +`; diff --git a/libs/features/mas/web-components/src/variants/segment.js b/libs/features/mas/web-components/src/variants/segment.js new file mode 100644 index 0000000000..f62a9e00d2 --- /dev/null +++ b/libs/features/mas/web-components/src/variants/segment.js @@ -0,0 +1,39 @@ +import { VariantLayout } from "./variant-layout" +import { html, css } from 'lit'; +import { CSS } from './segment.css.js'; + +export class Segment extends VariantLayout { + constructor(card) { + super(card); + } + + getGlobalCSS() { + return CSS; + } + + postCardUpdateHook() { + this.adjustTitleWidth(); + } + + renderLayout() { + return html` ${this.badge} +
+ + + ${!this.promoBottom ? html`` : ''} + + ${this.promoBottom ? html`` : ''} +
+
+ ${this.secureLabelFooter }`; + } + + static variantStyle = css` + :host([variant='segment']) { + min-height: 214px; + } + :host([variant='segment']) ::slotted([slot='heading-xs']) { + max-width: var(--consonant-merch-card-heading-xs-max-width, 100%); + } + `; +} diff --git a/libs/features/mas/web-components/src/variants/special-offer.css.js b/libs/features/mas/web-components/src/variants/special-offer.css.js new file mode 100644 index 0000000000..ed4024d1a0 --- /dev/null +++ b/libs/features/mas/web-components/src/variants/special-offer.css.js @@ -0,0 +1,50 @@ +import { TABLET_UP, DESKTOP_UP, LARGE_DESKTOP, MOBILE_LANDSCAPE,} from '../media.js'; +export const CSS = ` +:root { + --consonant-merch-card-special-offers-width: 378px; +} + +merch-card[variant="special-offers"] span[is="inline-price"][data-template="strikethrough"] { + font-size: var(--consonant-merch-card-body-xs-font-size); +} + +/* grid style for special-offers */ +.one-merch-card.special-offers, +.two-merch-cards.special-offers, +.three-merch-cards.special-offers, +.four-merch-cards.special-offers { + grid-template-columns: minmax(300px, var(--consonant-merch-card-special-offers-width)); +} + +@media screen and ${MOBILE_LANDSCAPE} { + :root { + --consonant-merch-card-special-offers-width: 302px; + } +} + +@media screen and ${TABLET_UP} { + :root { + --consonant-merch-card-special-offers-width: 302px; + } + + .two-merch-cards.special-offers, + .three-merch-cards.special-offers, + .four-merch-cards.special-offers { + grid-template-columns: repeat(2, minmax(300px, var(--consonant-merch-card-special-offers-width))); + } +} + +/* desktop */ +@media screen and ${DESKTOP_UP} { + .three-merch-cards.special-offers, + .four-merch-cards.special-offers { + grid-template-columns: repeat(3, minmax(300px, var(--consonant-merch-card-special-offers-width))); + } +} + +@media screen and ${LARGE_DESKTOP} { + .four-merch-cards.special-offers { + grid-template-columns: repeat(4, minmax(300px, var(--consonant-merch-card-special-offers-width))); + } +} +`; diff --git a/libs/features/mas/web-components/src/variants/special-offer.js b/libs/features/mas/web-components/src/variants/special-offer.js new file mode 100644 index 0000000000..b76faec004 --- /dev/null +++ b/libs/features/mas/web-components/src/variants/special-offer.js @@ -0,0 +1,50 @@ +import { html, css } from 'lit'; +import { VariantLayout } from './variant-layout'; +import { CSS } from './special-offer.css.js'; + +export class SpecialOffer extends VariantLayout { + constructor(card) { + super(card); + } + + getGlobalCSS() { + return CSS; + } + + get headingSelector() { + return '[slot="detail-m"]'; + } + + renderLayout () { + return html`${this.cardImage} +
+ + + +
+ ${this.evergreen + ? html` +
+ +
+ ` + : html` +
+ ${this.secureLabelFooter} + `} + `; + } + + static variantStyle = css` + :host([variant='special-offers']) { + min-height: 439px; + } + + :host([variant='special-offers'].center) { + text-align: center; + } + `; +}; diff --git a/libs/features/mas/web-components/src/variants/twp.css.js b/libs/features/mas/web-components/src/variants/twp.css.js new file mode 100644 index 0000000000..76b8ff8d92 --- /dev/null +++ b/libs/features/mas/web-components/src/variants/twp.css.js @@ -0,0 +1,103 @@ +import { TABLET_UP, DESKTOP_UP, LARGE_DESKTOP, MOBILE_LANDSCAPE,} from '../media.js'; +export const CSS = ` +:root { + --consonant-merch-card-twp-width: 268px; + --consonant-merch-card-twp-mobile-width: 300px; + --consonant-merch-card-twp-mobile-height: 358px; +} + +merch-card[variant="twp"] div[class$='twp-badge'] { + padding: 4px 10px 5px 10px; +} + +merch-card[variant="twp"] [slot="body-xs-top"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + color: var(--merch-color-grey-80); +} + +merch-card[variant="twp"] [slot="body-xs"] ul { + padding: 0; + margin: 0; +} + +merch-card[variant="twp"] [slot="body-xs"] ul li { + list-style-type: none; + padding-left: 0; +} + +merch-card[variant="twp"] [slot="body-xs"] ul li::before { + content: '·'; + font-size: 20px; + padding-right: 5px; + font-weight: bold; +} + +merch-card[variant="twp"] [slot="footer"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + padding: var(--consonant-merch-spacing-s); + var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-xs); + color: var(--merch-color-grey-80); + display: flex; + flex-flow: wrap; +} + +merch-card[variant='twp'] merch-quantity-select, +merch-card[variant='twp'] merch-offer-select { + display: none; +} + +.one-merch-card.twp, +.two-merch-cards.twp, +.three-merch-cards.twp { + grid-template-columns: var(--consonant-merch-card-image-width); +} + +@media screen and ${MOBILE_LANDSCAPE} { + :root { + --consonant-merch-card-twp-width: 300px; + } + .one-merch-card.twp, + .two-merch-cards.twp, + .three-merch-cards.twp { + grid-template-columns: repeat(1, var(--consonant-merch-card-twp-mobile-width)); + } +} + +@media screen and ${TABLET_UP} { + :root { + --consonant-merch-card-twp-width: 268px; + } + .one-merch-card.twp, + .two-merch-cards.twp { + grid-template-columns: repeat(2, var(--consonant-merch-card-twp-width)); + } + .three-merch-cards.twp { + grid-template-columns: repeat(3, var(--consonant-merch-card-twp-width)); + } +} + +@media screen and ${DESKTOP_UP} { + :root { + --consonant-merch-card-twp-width: 268px; + } + .one-merch-card.twp + .two-merch-cards.twp { + grid-template-columns: repeat(2, var(--consonant-merch-card-twp-width)); + } + .three-merch-cards.twp { + grid-template-columns: repeat(3, var(--consonant-merch-card-twp-width)); + } +} + +@media screen and ${LARGE_DESKTOP} { + .one-merch-card.twp + .two-merch-cards.twp { + grid-template-columns: repeat(2, var(--consonant-merch-card-twp-width)); + } + .three-merch-cards.twp { + grid-template-columns: repeat(3, var(--consonant-merch-card-twp-width)); + } +} +`; diff --git a/libs/features/mas/web-components/src/variants/twp.js b/libs/features/mas/web-components/src/variants/twp.js new file mode 100644 index 0000000000..0da885c4cb --- /dev/null +++ b/libs/features/mas/web-components/src/variants/twp.js @@ -0,0 +1,67 @@ +import { VariantLayout } from "./variant-layout"; +import { html, css } from 'lit'; +import { CSS } from './twp.css.js'; + +export class TWP extends VariantLayout { + constructor(card) { + super(card); + } + + getGlobalCSS() { + return CSS; + } + + renderLayout() { + return html`${this.badge} +
+ + + +
+
+ +
+
`; + } + + static variantStyle = css` + :host([variant='twp']) { + padding: 4px 10px 5px 10px; + } + .twp-badge { + padding: 4px 10px 5px 10px; + } + + :host([variant='twp']) ::slotted(merch-offer-select) { + display: none; + } + + :host([variant='twp']) .top-section { + flex: 0; + display: flex; + flex-direction: column; + justify-content: flex-start; + height: 100%; + gap: var(--consonant-merch-spacing-xxs); + padding: var(--consonant-merch-spacing-xs) + var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-xs) + var(--consonant-merch-spacing-xs); + align-items: flex-start; + } + + :host([variant='twp']) .body { + padding: 0 var(--consonant-merch-spacing-xs); + } + + :host([aria-selected]) .twp-badge { + margin-inline-end: 2px; + padding-inline-end: 9px; + } + + :host([variant='twp']) footer { + gap: var(--consonant-merch-spacing-xxs); + flex-direction: column; + align-self: flex-start; + } + `; +} diff --git a/libs/features/mas/web-components/src/variants/variant-layout.js b/libs/features/mas/web-components/src/variants/variant-layout.js new file mode 100644 index 0000000000..d75cc465cc --- /dev/null +++ b/libs/features/mas/web-components/src/variants/variant-layout.js @@ -0,0 +1,103 @@ +import { html } from 'lit'; + +export class VariantLayout { + static styleMap = {}; + + card; + + insertVariantStyle() { + if (!VariantLayout.styleMap[this.card.variant]) { + VariantLayout.styleMap[this.card.variant] = true; + const styles = document.createElement('style'); + styles.innerHTML = this.getGlobalCSS(); + document.head.appendChild(styles); + } + } + + constructor(card) { + this.card = card; + setTimeout(() => this.insertVariantStyle(), 1); + } + + get badge() { + let additionalStyles; + if (!this.card.badgeBackgroundColor || !this.card.badgeColor || !this.card.badgeText) { + return; + } + if (this.evergreen) { + additionalStyles = `border: 1px solid ${this.card.badgeBackgroundColor}; border-right: none;`; + } + return html` +
+ ${this.card.badgeText} +
+ `; + } + + get cardImage() { + return html`
+ + ${this.badge} +
`; + } + + /* c8 ignore next 3 */ + getGlobalCSS() { + return ''; + } + + get evergreen() { + return this.card.classList.contains('intro-pricing'); + } + + get promoBottom() { + return this.card.classList.contains('promo-bottom'); + } + + get headingSelector() { + return '[slot="heading-xs"]'; + } + + get secureLabelFooter() { + const secureLabel = this.card.secureLabel + ? html`${this.card.secureLabel}` + : ''; + return html`
${secureLabel}
`; + } + + async adjustTitleWidth() { + const cardWidth = this.card.getBoundingClientRect().width; + const badgeWidth = + this.card.badgeElement?.getBoundingClientRect().width || 0; + if (cardWidth === 0 || badgeWidth === 0) return; + this.card.style.setProperty( + '--consonant-merch-card-heading-xs-max-width', + `${Math.round(cardWidth - badgeWidth - 16)}px`, // consonant-merch-spacing-xs + ); + } + + postCardUpdateHook() { + //nothing to do by default + } + + connectedCallbackHook() { + //nothing to do by default + } + + disconnectedCallbackHook() { + //nothing to do by default + } + + /* c8 ignore next 3 */ + renderLayout () { + //nothing to do by default + } +} diff --git a/libs/features/mas/web-components/src/variants/variants.js b/libs/features/mas/web-components/src/variants/variants.js new file mode 100644 index 0000000000..934cf0998f --- /dev/null +++ b/libs/features/mas/web-components/src/variants/variants.js @@ -0,0 +1,51 @@ +import { Catalog } from './catalog.js'; +import { CCDAction } from './ccd-action.js'; +import { Image } from './image.js'; +import { InlineHeading } from './inline-heading.js'; +import { MiniCompareChart } from './mini-compare-chart.js'; +import { Plans } from './plans.js'; +import { Product } from './product.js'; +import { Segment } from './segment.js'; +import { SpecialOffer } from './special-offer.js'; +import { TWP } from './twp.js'; + +const getVariantLayout = (card) => { + switch (card.variant) { + case 'catalog': + return new Catalog(card); + case 'ccd-action': + return new CCDAction(card); + case 'image': + return new Image(card); + case 'inline-heading': + return new InlineHeading(card); + case 'mini-compare-chart': + return new MiniCompareChart(card); + case 'plans': + return new Plans(card); + case 'product': + return new Product(card); + case 'segment': + return new Segment(card); + case 'special-offers': + return new SpecialOffer(card); + case 'twp': + return new TWP(card); + default: + return new Product(card); + } +}; + +const getVariantStyles = () => { + const styles = []; + styles.push(Catalog.variantStyle); + styles.push(CCDAction.variantStyle); + styles.push(MiniCompareChart.variantStyle); + styles.push(Plans.variantStyle); + styles.push(Segment.variantStyle); + styles.push(SpecialOffer.variantStyle); + styles.push(TWP.variantStyle); + return styles; +} + +export { getVariantLayout, getVariantStyles }; diff --git a/libs/features/mas/mocks/mas.js b/libs/features/mas/web-components/test/mas.js similarity index 71% rename from libs/features/mas/mocks/mas.js rename to libs/features/mas/web-components/test/mas.js index eabf21c623..c03cbe3a52 100644 --- a/libs/features/mas/mocks/mas.js +++ b/libs/features/mas/web-components/test/mas.js @@ -1,7 +1,7 @@ import { init } from '@adobe/mas-commerce'; -import '../../../web-components/src/merch-card.js'; -import '../../../web-components/src/merch-icon.js'; -import '../../../web-components/src/merch-datasource.js'; +import '../../src/merch-card.js'; +import '../../src/merch-icon.js'; +import '../../src/merch-datasource.js'; const locale = document diff --git a/libs/features/mas/web-components/test/merch-card-collection.test.html.js b/libs/features/mas/web-components/test/merch-card-collection.test.html.js index 1f58004090..5b7cd5a477 100644 --- a/libs/features/mas/web-components/test/merch-card-collection.test.html.js +++ b/libs/features/mas/web-components/test/merch-card-collection.test.html.js @@ -16,8 +16,7 @@ import '../src/sidenav/merch-sidenav.js'; import '../src/merch-card-collection.js'; import { withWcs } from './mocks/wcs.js'; -import { withLiterals } from './mocks/literals.js'; -import mas from './mocks/mas.js'; +import mas from './mas.js'; const searchParams = new URLSearchParams(document.location.search); @@ -73,14 +72,40 @@ let merchCards; const shouldSkipTests = sessionStorage.getItem('skipTests') ? 'true' : 'false'; runTests(async () => { - await toggleLargeDesktop(); + let render; mockLana(); - await mockFetch(withWcs, withLiterals); + await mockFetch(withWcs); await mas(); if (shouldSkipTests !== 'true') { - describe('merch-card-collection web component', () => { - let render; - beforeEach(() => { + describe("merch-card-collection web component on phones and tablets", () => { + beforeEach(async () => { + [merchCards, render] = prepareTemplate('catalogCards', false); + }); + + it('sets the class for modal when opening filters in a modal', async () => { + render(); + await delay(100); + expect(document.body.classList.contains('merch-modal')).to.be.false; + merchCards.shadowRoot.querySelector('#filtersButton').click(); + await delay(100); + expect(document.body.classList.contains('merch-modal')).to.be.true; + }); + + it('removes the class for modal when closing the filters modal', async () => { + render(); + await delay(100); + merchCards.shadowRoot.querySelector('#filtersButton').click(); + await delay(100); + expect(document.body.classList.contains('merch-modal')).to.be.true; + document.querySelector('merch-sidenav').shadowRoot.querySelector('#sidenav').querySelector('sp-link').click(); + await delay(100); + expect(document.body.classList.contains('merch-modal')).to.be.false; + }); + }); + + describe('merch-card-collection web component on desktop', () => { + beforeEach(async () => { + await toggleLargeDesktop(); document.location.hash = ''; [merchCards, render] = prepareTemplate('catalogCards', false); }); diff --git a/libs/features/mas/web-components/test/merch-card.catalog.test.html b/libs/features/mas/web-components/test/merch-card.catalog.test.html new file mode 100644 index 0000000000..8d7f0c1029 --- /dev/null +++ b/libs/features/mas/web-components/test/merch-card.catalog.test.html @@ -0,0 +1,276 @@ + + + + + + + Merch Card Web Component demo page + + + + + + +
+
+ + + +
+
+ + +
+

Best for:

+
    +
  • Photo editing
  • +
  • Compositing
  • +
  • Drawing and painting
  • +
  • Graphic design
  • +
+

Storage:

+

100 GB of cloud storage

+

See system requirements

+
+

Photography

+

+ +

+
+

Lightroom, Lightroom Classic, Photoshop on desktop and iPad, and 1TB of cloud storage.

+

Learn more

+
+ +
+ + +
+

Best for:

+
    +
  • Photo editing
  • +
  • Compositing
  • +
  • Drawing and painting
  • +
  • Graphic design
  • +
+

Storage:

+

100 GB of cloud storage

+

See system requirements

+
+

Photography

+

+ +
Inclusive of VAT
+

+
+

Lightroom, Lightroom Classic, Photoshop on desktop and iPad, and 1TB of cloud storage.

+

Learn more

+
+
+ Android + IOS + Save now +
+
+ +

Creative Cloud All Apps

+

+
Desktop
+
+

+

Get 20+ creative apps including Photoshop, Illustrator, Premiere Pro, Acrobat Pro, and Adobe Express. + (Substance 3D apps are not included.)

+

See what’s included | Learn more

+
+

Buy now free trial

+
+
+
+ + +
+

Best for:

+
    +
  • Photo editing
  • +
  • Compositing
  • +
  • Drawing and painting
  • +
  • Graphic design
  • +
+

Storage:

+

100 GB of cloud storage

+

See system requirements

+
+

Photography

+

+ +

+
+

Lightroom, Lightroom Classic, Photoshop on desktop and iPad, and 1TB of cloud storage.

+

Learn more

+
+
+
+
+
AI Assistant add-on available
+ +
+
+
+
+
+ For a limited time pay + when you add AI assistant to Acrobat Reader, Standard, or Pro. Ends 6/4.Learn More +
+ +
+
+
+ +
+ + +
+

Best for:

+
    +
  • Photo editing
  • +
  • Compositing
  • +
  • Drawing and painting
  • +
  • Graphic design
  • +
+

Storage:

+

100 GB of cloud storage

+

See system requirements

+
+

Photography

+

+ +

+
+

Lightroom, Lightroom Classic, Photoshop on desktop and iPad, and 1TB of cloud storage.

+

Learn more

+
+
+
+
+
AI Assistant add-on available
+ +
+
+
+
+
+ For a limited time pay + when you add AI assistant to Acrobat Reader, Standard, or Pro. Ends 6/4.Learn More +
+ +
+
+
+
+ Android + IOS + Save now +
+
+ +

Creative Cloud All Apps

+

+
Desktop
+
+

+

Get 20+ creative apps including Photoshop, Illustrator, Premiere Pro, Acrobat Pro, and Adobe Express. + (Substance 3D apps are not included.)

+

See what’s included | Learn more

+
+
+
+
+
AI Assistant add-on available
+ +
+
+
+
+
+ For a limited time pay + when you add AI assistant to Acrobat Reader, Standard, or Pro. Ends 6/4.Learn More +
+ +
+
+
+ +

Buy now free trial

+
+
+
+ + diff --git a/libs/features/mas/web-components/test/merch-card.catalog.test.html.js b/libs/features/mas/web-components/test/merch-card.catalog.test.html.js new file mode 100644 index 0000000000..1284a2224f --- /dev/null +++ b/libs/features/mas/web-components/test/merch-card.catalog.test.html.js @@ -0,0 +1,71 @@ +// @ts-nocheck +import { runTests } from '@web/test-runner-mocha'; +import { expect } from '@esm-bundle/chai'; + +import { mockLana } from './mocks/lana.js'; +import { mockFetch } from './mocks/fetch.js'; +import { mockConfig } from './mocks/config.js'; + +import '../src/merch-offer.js'; +import '../src/merch-offer-select.js'; +import '../src/merch-quantity-select.js'; + +import { appendMiloStyles, delay } from './utils.js'; +import { mockIms } from './mocks/ims.js'; +import { withWcs } from './mocks/wcs.js'; +import mas from './mas.js'; + +const skipTests = sessionStorage.getItem('skipTests'); + +runTests(async () => { + mockIms(); + mockLana(); + await mockFetch(withWcs); + await mas(); + if (skipTests !== null) { + appendMiloStyles(); + return; + } + describe('merch-card web component', () => { + it('should exist in the HTML document', async () => { + expect(document.querySelector('merch-card')).to.exist; + }); + + it('should display an action menu on hover for catalog variant', async () => { + const catalogCard = document.querySelector( + 'merch-card[variant="catalog"]', + ); + catalogCard.dispatchEvent( + new MouseEvent('mouseover', { bubbles: true }), + ); + await delay(100); + const shadowRoot = catalogCard.shadowRoot; + const actionMenu = shadowRoot.querySelector('.action-menu'); + const actionMenuContent = shadowRoot.querySelector( + '.action-menu-content', + ); + expect(actionMenu.classList.contains('invisible')).to.be.true; + expect(actionMenuContent.classList.contains('hidden')).to.be.true; + expect(actionMenu).to.exist; + expect(actionMenuContent).to.exist; + }); + + it('should display some content when action is clicked for catalog variant', async () => { + const catalogCard = document.querySelector( + 'merch-card[variant="catalog"]', + ); + catalogCard.dispatchEvent( + new MouseEvent('mouseover', { bubbles: true }), + ); + await delay(100); + const actionMenu = catalogCard.shadowRoot.querySelector('.action-menu'); + const actionMenuContent = catalogCard.shadowRoot.querySelector( + '.action-menu-content', + ); + await actionMenu.click(); + await delay(100); + expect(actionMenuContent.classList.contains('hidden')).to.be.false; + }); + }); + +}); diff --git a/libs/features/mas/web-components/test/merch-card.mini-compare.mobile.test.html b/libs/features/mas/web-components/test/merch-card.mini-compare.mobile.test.html new file mode 100644 index 0000000000..3354124554 --- /dev/null +++ b/libs/features/mas/web-components/test/merch-card.mini-compare.mobile.test.html @@ -0,0 +1,634 @@ + + + + + + + Merch Card Web Component demo page + + + + + + +
+
+ + + +
+
+ + + +

This is promo text with a + link

+
+

+

Get Illustrator on desktop and iPad as part of Creative Cloud.

+
+

+

Creative Cloud All Apps +

+ +
+
+
+
AI Assistant add-on available
+ +
+
+
+
+

+ free + trial + Buy now +

+
+ +
+
+ + + + + +
+
+ + +

+
+

+

Get Illustrator on desktop and iPad as part of Creative Cloud.

+
+

+

Illustrator

+

+

+ free trial + Buy nowBuy +

+
+
+ + + + + +
+
+ + +

Save 19% ($US7.99/mo) + for the first year if you buy now.

+
+

+

Get Illustrator on desktop and iPad as part of Creative Cloud.

+

See terms.

+
+

+

Illustrator

+

Save 19% ($US7.99/mo) for the + first year if you buy now.

+
+
+

+ Free + trial + Buy now +

+ + +
+ + + + + +
+
+
+
+ + +

This is promo text with a + link

+
+

+

Get Illustrator on desktop and iPad as part of Creative Cloud.

+
+

+

Creative Cloud All Apps +

+ +
+

+ free + trial + Buy now +

+
+ +
+
+ + + + + +
+
+ + +

This is promo text with a + link

+
+

+

Get Illustrator on desktop and iPad as part of Creative Cloud.

+
+

+

Creative Cloud All Apps +

+ +
+

+ free + trial + Buy now +

+
+ +
+
+ + + + + +
+
+
+
+ + +

This is promo text with a + link

+
+

+

Get Illustrator on desktop and iPad as part of Creative Cloud.

+
+

+

Creative Cloud All Apps +

+ +
+
+
+
AI Assistant add-on available
+ +
+
+
+
+

+ free + trial + Buy now +

+
+ +
+
+ + + + + +
+
+ + +

This is promo text with a + link

+
+

+

Get Illustrator on desktop and iPad as part of Creative Cloud.

+
+

+

Creative Cloud All Apps +

+ +
+
+
+
AI Assistant add-on available
+ +
+
+
+
+

+ free + trial + Buy now +

+
+ +
+
+ + + + + +
+
+
+
+ + + +

This is promo text with a + link

+
+

+

Get Illustrator on desktop and iPad as part of Creative Cloud.

+
+

+

Creative Cloud All Apps +

+ +
+

+ free + trial + Buy now +

+
+ +
+
+ + + + + +
+
+
+
+ + + diff --git a/libs/features/mas/web-components/test/merch-card.mini-compare.mobile.test.html.js b/libs/features/mas/web-components/test/merch-card.mini-compare.mobile.test.html.js new file mode 100644 index 0000000000..f86ba5895f --- /dev/null +++ b/libs/features/mas/web-components/test/merch-card.mini-compare.mobile.test.html.js @@ -0,0 +1,83 @@ +// @ts-nocheck +import { runTests } from '@web/test-runner-mocha'; +import { expect } from '@esm-bundle/chai'; + +import { mockLana } from './mocks/lana.js'; +import { mockFetch } from './mocks/fetch.js'; +import { mockConfig } from './mocks/config.js'; + +import '../src/merch-offer.js'; +import '../src/merch-offer-select.js'; +import '../src/merch-quantity-select.js'; + +import { appendMiloStyles, toggleMobile } from './utils.js'; +import { mockIms } from './mocks/ims.js'; +import { withWcs } from './mocks/wcs.js'; +import mas from './mas.js'; + +const skipTests = sessionStorage.getItem('skipTests'); + +runTests(async () => { + await toggleMobile(); + mockIms(); + mockLana(); + await mockFetch(withWcs); + await mas(); + if (skipTests !== null) { + appendMiloStyles(); + return; + } + describe('[mobile] merch-card web component with mini-compare variant', () => { + it('[mobile] mini-compare-chart should have same body slot heights', async () => { + const miniCompareCharts = document.querySelectorAll( + 'merch-card[variant="mini-compare-chart"]', + ); + await Promise.all( + [...miniCompareCharts].flatMap((card) => { + return [ + card.updateComplete, + ...[...card.querySelectorAll('[data-wcs-osi]')].map( + (osi) => osi.onceSettled(), + ), + ]; + }), + ); + const [card1Slots, card2Slots, card3Slots] = [ + ...miniCompareCharts, + ].map((miniCompareChart) => { + const heights = [ + 'slot[name="heading-m"]', + 'slot[name="body-m"]', + 'slot[name="heading-m-price"]', + 'slot[name="price-commitment"]', + 'slot[name="offers"]', + 'slot[name="promo-text"]', + 'slot[name="callout-content"]', + 'footer', + ] + .map((selector) => + Math.round( + window.getComputedStyle( + miniCompareChart.shadowRoot.querySelector( + selector, + ), + ).height, + ), + ) + .join(','); + return heights; + }); + expect(card1Slots).to.not.contain('auto'); + expect(card1Slots).to.equal(card2Slots); + expect(card2Slots).to.equal(card3Slots); + }); + + it('[mobile] mini-compare-chart should ', async () => { + const miniCompareChart = document.querySelector( + 'merch-card[variant="mini-compare-chart"]', + ); + miniCompareChart?.variantLayout?.removeEmptyRows(); + expect(true, 'removing empty lines do not fail').to.be.true; // TODO improve the assertion + }); + }); +}); diff --git a/libs/features/mas/web-components/test/merch-card.mini-compare.test.html b/libs/features/mas/web-components/test/merch-card.mini-compare.test.html index f19c91e6c4..a822c47b96 100644 --- a/libs/features/mas/web-components/test/merch-card.mini-compare.test.html +++ b/libs/features/mas/web-components/test/merch-card.mini-compare.test.html @@ -63,7 +63,7 @@

This is promo text <

-
- - -
-

Best for:

-
    -
  • Photo editing
  • -
  • Compositing
  • -
  • Drawing and painting
  • -
  • Graphic design
  • -
-

Storage:

-

100 GB of cloud storage

-

See system requirements

-
-

Photography

-

- -

-
-

Lightroom, Lightroom Classic, Photoshop on desktop and iPad, and 1TB of cloud storage.

-

Learn more

-
-
-
-
-
AI Assistant add-on available
- -
-
-
-
-
- For a limited time pay - when you add AI assistant to Acrobat Reader, Standard, or Pro. Ends 6/4.Learn More -
- -
-
-
- -
- - -
-

Best for:

-
    -
  • Photo editing
  • -
  • Compositing
  • -
  • Drawing and painting
  • -
  • Graphic design
  • -
-

Storage:

-

100 GB of cloud storage

-

See system requirements

-
-

Photography

-

- -

-
-

Lightroom, Lightroom Classic, Photoshop on desktop and iPad, and 1TB of cloud storage.

-

Learn more

-
-
-
-
-
AI Assistant add-on available
- -
-
-
-
-
- For a limited time pay - when you add AI assistant to Acrobat Reader, Standard, or Pro. Ends 6/4.Learn More -
- -
-
-
-
- Android - IOS - Save now -
-
- -

Creative Cloud All Apps

-

-
Desktop
-
-

-

Get 20+ creative apps including Photoshop, Illustrator, Premiere Pro, Acrobat Pro, and Adobe Express. - (Substance 3D apps are not included.)

-

See what’s included | Learn more

-
-
-
-
-
AI Assistant add-on available
- -
-
-
-
-
- For a limited time pay - when you add AI assistant to Acrobat Reader, Standard, or Pro. Ends 6/4.Learn More -
- -
-
-
- -

Buy now free trial

-
-
-

Creative Cloud All Apps

@@ -1079,7 +746,7 @@

-

Photography

@@ -1116,7 +783,7 @@

-

Photography

@@ -1219,6 +886,7 @@

Desktop + Mobile

secure-label="Secure Transaction" tabindex="0" filters="" + variant="product" types="" > @@ -1233,6 +901,81 @@

Desktop + Mobile

+
+ + + undefined +

CC All Apps

+

+

+ US$35.99/mo US$59.99/mo +

+

+
+
+ Get 20+ creative apps including Photoshop and Illustrator.  +
+
+
+
+ diff --git a/libs/features/mas/web-components/test/merch-card.test.html.js b/libs/features/mas/web-components/test/merch-card.test.html.js index e949bcffc1..0bbd0c477d 100644 --- a/libs/features/mas/web-components/test/merch-card.test.html.js +++ b/libs/features/mas/web-components/test/merch-card.test.html.js @@ -13,7 +13,7 @@ import '../src/merch-quantity-select.js'; import { appendMiloStyles, delay } from './utils.js'; import { mockIms } from './mocks/ims.js'; import { withWcs } from './mocks/wcs.js'; -import mas from './mocks/mas.js'; +import mas from './mas.js'; const skipTests = sessionStorage.getItem('skipTests'); @@ -30,18 +30,6 @@ runTests(async () => { it('should exist in the HTML document', async () => { expect(document.querySelector('merch-card')).to.exist; }); - it('should exist special offers card in HTML document', async () => { - expect( - document.querySelector('merch-card[variant="special-offers"]'), - ).to.exist; - }); - it('should display a merch-badge', async () => { - expect( - document - .querySelector('merch-card[variant="special-offers"]') - .shadowRoot.querySelector('.special-offers-badge'), - ).to.exist; - }); it('should exist segment card in HTML document', async () => { expect(document.querySelector('merch-card[variant="segment"]')).to .exist; @@ -81,24 +69,6 @@ runTests(async () => { 'm2m,stock-m2m', ); }); - it('should display an action menu on hover for catalog variant', async () => { - const catalogCard = document.querySelector( - 'merch-card[variant="catalog"]', - ); - catalogCard.dispatchEvent( - new MouseEvent('mouseover', { bubbles: true }), - ); - await delay(100); - const shadowRoot = catalogCard.shadowRoot; - const actionMenu = shadowRoot.querySelector('.action-menu'); - const actionMenuContent = shadowRoot.querySelector( - '.action-menu-content', - ); - expect(actionMenu.classList.contains('invisible')).to.be.true; - expect(actionMenuContent.classList.contains('hidden')).to.be.true; - expect(actionMenu).to.exist; - expect(actionMenuContent).to.exist; - }); it('should have and interact with quantity-selector', async () => { const plansCard = document.querySelector('merch-card[type="q-ty"]'); @@ -122,13 +92,6 @@ runTests(async () => { }); }); - it('should return title for special offer card', async () => { - const title = document.querySelector( - 'merch-card[variant="special-offers"]', - ).title; - expect(title).to.equal('INDIVIDUALS'); - }); - it('should return title for segment card', async () => { const title = document.querySelector( 'merch-card[variant="segment"]', diff --git a/libs/features/mas/web-components/test/merch-datasource.test.html b/libs/features/mas/web-components/test/merch-datasource.test.html index d8696505a1..db8afd95dd 100644 --- a/libs/features/mas/web-components/test/merch-datasource.test.html +++ b/libs/features/mas/web-components/test/merch-datasource.test.html @@ -1,42 +1,50 @@ - + + + + + + Merch Datasource Web Component test page + + + - - - - - Merch Datasource Web Component test page - - - - - - -
- -
- - - + + +
+ + diff --git a/libs/features/mas/web-components/test/merch-datasource.test.html.js b/libs/features/mas/web-components/test/merch-datasource.test.html.js index 1a0373eafc..70ba7f4ee4 100644 --- a/libs/features/mas/web-components/test/merch-datasource.test.html.js +++ b/libs/features/mas/web-components/test/merch-datasource.test.html.js @@ -1,18 +1,35 @@ import { runTests } from '@web/test-runner-mocha'; -import { expect } from '@esm-bundle/chai'; +import chai from '@esm-bundle/chai'; +import chaiAsPromised from '@esm-bundle/chai-as-promised'; import { mockFetch } from './mocks/fetch.js'; import { withWcs } from './mocks/wcs.js'; -import mas from './mocks/mas.js'; +import { withAem } from './mocks/aem.js'; +import mas from './mas.js'; import { getTemplateContent } from './utils.js'; +chai.use(chaiAsPromised); +const expect = chai.expect; + runTests(async () => { - await mockFetch(withWcs); + const [cc, photoshop] = await fetch( + 'mocks/sites/cf/fragments/search/default.json', + ) + .then((res) => res.json()) + .then(({ items }) => items); + await mas(); await customElements.whenDefined('merch-datasource'); const { cache } = document.createElement('merch-datasource'); describe('merch-datasource web component', () => { + let aemMock; + + beforeEach(async () => { + [, aemMock] = await mockFetch(withWcs, withAem); + cache.clear(); + }); + it('has fragment cache', async () => { expect(cache).to.exist; expect(cache.has('/test')).to.false; @@ -23,15 +40,37 @@ runTests(async () => { }); it('renders a merch card from cache', async () => { - cache.clear(); + cache.add(cc, photoshop); + const [ccCard, photoshopCard] = getTemplateContent('cards'); + document.querySelector('main').append(ccCard, photoshopCard); + expect(aemMock.count).to.equal(0); + }); + + it('re-renders a card after clearing the cache', async () => { + const [, , ccCard] = getTemplateContent('cards'); //special offers students-and-teachers. + const dataSource = ccCard.querySelector('merch-datasource'); + + document.querySelector('main').append(ccCard); + await dataSource.updateComplete; + const before = ccCard.innerHTML; + ccCard.footerSlot.test = true; + await dataSource.refresh(true); + await dataSource.refresh(true); // for extra coverage + await dataSource.updateComplete; + const after = ccCard.innerHTML; + expect(before).to.equal(after); + expect(ccCard.footerSlot.test).to.undefined; + expect(aemMock.count).to.equal(2); + }); - const searchResult = await fetch( - '/test/mocks/sites/cf/fragments/search/default.json', - ).then((res) => res.json()); - cache.add(...searchResult.items); + it('ignores incomplete markup', async () => { + const [, , , cardWithMissingPath] = getTemplateContent('cards'); + const dataSource = + cardWithMissingPath.querySelector('merch-datasource'); - const cards = getTemplateContent('cards'); - document.querySelector('main').append(...cards); + document.querySelector('main').append(cardWithMissingPath); + await expect(dataSource.updateComplete).to.be.rejectedWith('datasource is not correctly configured', + ); }); }); }); diff --git a/libs/features/mas/web-components/test/merch-offer-select.test.html.js b/libs/features/mas/web-components/test/merch-offer-select.test.html.js index a43ee6a8b9..37ccfd58c9 100644 --- a/libs/features/mas/web-components/test/merch-offer-select.test.html.js +++ b/libs/features/mas/web-components/test/merch-offer-select.test.html.js @@ -3,7 +3,6 @@ import { expect } from '@esm-bundle/chai'; import { mockLana } from './mocks/lana.js'; import { mockFetch } from './mocks/fetch.js'; -import { mockConfig } from './mocks/config.js'; import '../src/merch-offer.js'; import '../src/merch-offer-select.js'; @@ -11,8 +10,7 @@ import '../src/merch-quantity-select.js'; import { delay } from './utils.js'; import { withWcs } from './mocks/wcs.js'; -import { withLiterals } from './mocks/literals.js'; -import mas from './mocks/mas.js'; +import mas from './mas.js'; function getDynamicElements(merchCard, merchOfferSelect) { const price = merchOfferSelect.price; @@ -51,7 +49,7 @@ const renderCard = async (id) => { runTests(async () => { mockLana(); - await mockFetch(withWcs, withLiterals); + await mockFetch(withWcs); await mas(); describe('merch-offer-select web component', async () => { diff --git a/libs/features/mas/web-components/test/merch-quantity-select.test.html.js b/libs/features/mas/web-components/test/merch-quantity-select.test.html.js index 243007acd5..e673a07a5c 100644 --- a/libs/features/mas/web-components/test/merch-quantity-select.test.html.js +++ b/libs/features/mas/web-components/test/merch-quantity-select.test.html.js @@ -3,15 +3,13 @@ import { expect } from '@esm-bundle/chai'; import { mockLana } from './mocks/lana.js'; import { mockFetch } from './mocks/fetch.js'; -import { mockConfig } from './mocks/config.js'; import '../src/merch-quantity-select.js'; import { appendMiloStyles, delay } from './utils.js'; import { ARROW_DOWN, ARROW_UP } from '../src/focus.js'; import { withWcs } from './mocks/wcs.js'; -import { withLiterals } from './mocks/literals.js'; -import mas from './mocks/mas.js'; +import mas from './mas.js'; const skipTests = sessionStorage.getItem('skipTests'); @@ -20,7 +18,7 @@ runTests(async () => { if (skipTests === null) { mockLana(); - await mockFetch(withWcs, withLiterals); + await mockFetch(withWcs); await mas(); describe('merch-quantity-selector web component', () => { const quantitySelect = document.querySelector( diff --git a/libs/features/mas/web-components/test/merch-subscription-panel.test.html.js b/libs/features/mas/web-components/test/merch-subscription-panel.test.html.js index d2c8ec511f..4f57ee8c17 100644 --- a/libs/features/mas/web-components/test/merch-subscription-panel.test.html.js +++ b/libs/features/mas/web-components/test/merch-subscription-panel.test.html.js @@ -15,10 +15,8 @@ import '../src/merch-subscription-panel.js'; import { delay } from './utils.js'; import { mockIms } from './mocks/ims.js'; -import { _$LE } from 'lit'; import { withWcs } from './mocks/wcs.js'; -import { withLiterals } from './mocks/literals.js'; -import mas from './mocks/mas.js'; +import mas from './mas.js'; import { getTemplateContent } from './utils.js'; const shouldSkipTests = sessionStorage.getItem('skipTests') === 'true'; @@ -38,7 +36,7 @@ runTests(async () => { // Activate mocks mockLana(); await mockIms(); - await mockFetch(withWcs, withLiterals); + await mockFetch(withWcs); await mas(); }); diff --git a/libs/features/mas/web-components/test/merch-twp-d2p.test.html.js b/libs/features/mas/web-components/test/merch-twp-d2p.test.html.js index 7437d75c6e..cf0b78667c 100644 --- a/libs/features/mas/web-components/test/merch-twp-d2p.test.html.js +++ b/libs/features/mas/web-components/test/merch-twp-d2p.test.html.js @@ -6,7 +6,6 @@ import { expect } from '@esm-bundle/chai'; import { mockLana } from './mocks/lana.js'; import { mockIms } from './mocks/ims.js'; import { mockFetch } from './mocks/fetch.js'; -import { mockConfig } from './mocks/config.js'; import '../src/global.css.js'; import '../src/merch-offer-select.js'; @@ -32,8 +31,7 @@ import { } from './merch-twp-d2p.utils.js'; import { appendMiloStyles, delay } from './utils.js'; import { withWcs } from './mocks/wcs.js'; -import { withLiterals } from './mocks/literals.js'; -import mas from './mocks/mas.js'; +import mas from './mas.js'; const ABM = 'ABM'; const PUF = 'PUF'; @@ -42,7 +40,7 @@ const M2M = 'M2M'; runTests(async () => { appendMiloStyles(); mockLana(); - await mockFetch(withWcs, withLiterals); + await mockFetch(withWcs); await mockIms(); await mas(); diff --git a/libs/features/mas/web-components/test/merch-whats-included.html.js b/libs/features/mas/web-components/test/merch-whats-included.html.js index f1873d01e9..0107310e6a 100644 --- a/libs/features/mas/web-components/test/merch-whats-included.html.js +++ b/libs/features/mas/web-components/test/merch-whats-included.html.js @@ -5,7 +5,7 @@ import { expect } from '@esm-bundle/chai'; import { mockLana } from '/test/mocks/lana.js'; import { mockFetch } from '/test/mocks/fetch.js'; import { mockConfig } from '/test/mocks/config.js'; -import mas from './mocks/mas.js'; +import mas from './mas.js'; import '../src/merch-mnemonic-list.js'; import '../src/merch-whats-included.js'; diff --git a/libs/features/mas/web-components/test/plans-modal.test.html.js b/libs/features/mas/web-components/test/plans-modal.test.html.js index 09f971e8eb..5ab3f369e1 100644 --- a/libs/features/mas/web-components/test/plans-modal.test.html.js +++ b/libs/features/mas/web-components/test/plans-modal.test.html.js @@ -3,18 +3,16 @@ import { expect } from '@esm-bundle/chai'; import { mockLana } from './mocks/lana.js'; import { mockFetch } from './mocks/fetch.js'; -import { mockConfig } from './mocks/config.js'; import './utils.js'; import { withWcs } from './mocks/wcs.js'; -import { withLiterals } from './mocks/literals.js'; -import mas from './mocks/mas.js'; +import mas from './mas.js'; const shouldSkipTests = sessionStorage.getItem('skipTests') ?? false; runTests(async () => { mockLana(); - await mockFetch(withWcs, withLiterals); + await mockFetch(withWcs); await mas(); if (shouldSkipTests !== 'true') { describe('plans-modal web component', () => { diff --git a/libs/features/mas/web-components/test/utils.js b/libs/features/mas/web-components/test/utils.js index 1fef69beac..9fc4e11acb 100644 --- a/libs/features/mas/web-components/test/utils.js +++ b/libs/features/mas/web-components/test/utils.js @@ -23,6 +23,12 @@ export async function toggleLargeDesktop() { } catch {} } +export async function toggleMobile() { + try { + await setViewport({ width: 430, height: 932 }); + } catch {} +} + window.skipTests = () => { window.location.hash = ''; sessionStorage.setItem('skipTests', 'true'); diff --git a/libs/features/mas/web-components/web-test-runner.config.mjs b/libs/features/mas/web-components/web-test-runner.config.mjs index a4a118f426..75cde5195c 100644 --- a/libs/features/mas/web-components/web-test-runner.config.mjs +++ b/libs/features/mas/web-components/web-test-runner.config.mjs @@ -19,7 +19,23 @@ export default { ], coverageConfig: { include: ['src/**'], - exclude: ['test/mocks/**', 'test/**', '**/node_modules/**'], + exclude: [ + 'test/mocks/**', + 'test/**', + '**/node_modules/**', + 'src/merch-twp-d2p.js', // on hold + 'src/aem.js', // WIP + 'src/bodyScrollLock.js', // todo + 'src/merch-subscription-panel.js', // on hold + 'src/ merch-whats-included.js', // on hold + ], + threshold: { + // TODO bump to 100% + branches: 85, + functions: 65, + statements: 85, + lines: 85, + }, }, debug: false, files: ['test/**/*.test.(js|html)'], diff --git a/libs/features/personalization/personalization.js b/libs/features/personalization/personalization.js index 29cfddd65d..dc0ae2c1fb 100644 --- a/libs/features/personalization/personalization.js +++ b/libs/features/personalization/personalization.js @@ -1,13 +1,11 @@ /* eslint-disable no-underscore-dangle */ /* eslint-disable no-console */ -import { - createTag, getConfig, loadLink, loadScript, localizeLink, updateConfig, -} from '../../utils/utils.js'; +import { createTag, getConfig, loadLink, loadScript, localizeLink } from '../../utils/utils.js'; import { getEntitlementMap } from './entitlements.js'; /* c8 ignore start */ -const PHONE_SIZE = window.screen.width < 768 || window.screen.height < 768; +const PHONE_SIZE = window.screen.width < 550 || window.screen.height < 550; export const PERSONALIZATION_TAGS = { all: () => true, chrome: () => navigator.userAgent.includes('Chrome') && !navigator.userAgent.includes('Edg'), @@ -356,24 +354,32 @@ function modifySelectorTerm(termParam) { let term = termParam; const specificSelectors = { section: 'main > div', - 'primary-cta': 'p strong a', - 'secondary-cta': 'p em a', - 'action-area': 'p:has(em a, strong a)', + 'primary-cta': 'strong a', + 'secondary-cta': 'em a', + 'action-area': '*:has(> em a, > strong a)', + 'any-marquee-section': 'main > div:has([class*="marquee"])', + 'any-marquee': '[class*="marquee"]', + 'any-header': ':is(h1, h2, h3, h4, h5, h6)', }; const otherSelectors = ['row', 'col']; - const htmlEls = ['main', 'div', 'a', 'p', 'strong', 'em', 'picture', 'source', 'img', 'h']; + const htmlEls = [ + 'html', 'body', 'header', 'footer', 'main', + 'div', 'a', 'p', 'strong', 'em', 'picture', 'source', 'img', 'h', + 'ul', 'ol', 'li', + ]; const startTextMatch = term.match(/^[a-zA-Z/./-]*/); const startText = startTextMatch ? startTextMatch[0].toLowerCase() : ''; + const startTextPart1 = startText.split(/\.|:/)[0]; const endNumberMatch = term.match(/[0-9]*$/); - const endNumber = endNumberMatch ? endNumberMatch[0] : ''; + const endNumber = endNumberMatch && startText.match(/^[a-zA-Z]/) ? endNumberMatch[0] : ''; if (!startText || htmlEls.includes(startText)) return term; if (otherSelectors.includes(startText)) { term = term.replace(startText, '> div'); term = updateEndNumber(endNumber, term); return term; } - if (Object.keys(specificSelectors).includes(startText)) { - term = term.replace(startText, specificSelectors[startText]); + if (Object.keys(specificSelectors).includes(startTextPart1)) { + term = term.replace(startTextPart1, specificSelectors[startTextPart1]); term = updateEndNumber(endNumber, term); return term; } @@ -784,28 +790,13 @@ export async function getManifestConfig(info, variantOverride = false) { } manifestConfig.manifestPath = normalizePath(manifestPath); - const selectedVariantName = await getPersonalizationVariant( + manifestConfig.selectedVariantName = await getPersonalizationVariant( manifestConfig.manifestPath, manifestConfig.variantNames, variantLabel, ); - if (selectedVariantName && manifestConfig.variantNames.includes(selectedVariantName)) { - manifestConfig.run = true; - manifestConfig.selectedVariantName = selectedVariantName; - manifestConfig.selectedVariant = manifestConfig.variants[selectedVariantName]; - } else { - /* c8 ignore next 2 */ - manifestConfig.selectedVariantName = 'default'; - manifestConfig.selectedVariant = 'default'; - } - const placeholders = manifestPlaceholders || data?.placeholders?.data; - if (placeholders) { - updateConfig( - parsePlaceholders(placeholders, getConfig(), manifestConfig.selectedVariantName), - ); - } - + manifestConfig.placeholderData = manifestPlaceholders || data?.placeholders?.data; manifestConfig.name = name; manifestConfig.manifest = manifestPath; manifestConfig.manifestUrl = manifestUrl; @@ -893,6 +884,20 @@ export function cleanAndSortManifestList(manifests) { } else { manifestObj[manifest.manifestPath] = manifest; } + + const manifestConfig = manifestObj[manifest.manifestPath]; + const { selectedVariantName, variantNames, placeholderData } = manifestConfig; + if (selectedVariantName && variantNames.includes(selectedVariantName)) { + manifestConfig.run = true; + manifestConfig.selectedVariantName = selectedVariantName; + manifestConfig.selectedVariant = manifestConfig.variants[selectedVariantName]; + } else { + /* c8 ignore next 2 */ + manifestConfig.selectedVariantName = 'default'; + manifestConfig.selectedVariant = 'default'; + } + + parsePlaceholders(placeholderData, getConfig(), manifestConfig.selectedVariantName); } catch (e) { console.warn(e); window.lana?.log(`MEP Error parsing manifests: ${e.toString()}`); diff --git a/libs/features/placeholders.js b/libs/features/placeholders.js index 5898fef5c0..73bdd5d1fa 100644 --- a/libs/features/placeholders.js +++ b/libs/features/placeholders.js @@ -118,7 +118,8 @@ export async function replaceText( // The .shift method is very slow, thus using normal iterator let i = 0; // eslint-disable-next-line no-plusplus - const finalText = text.replaceAll(regex, () => placeholders[i++]); + let finalText = text.replaceAll(regex, () => placeholders[i++]); + finalText = finalText.replace(/ /g, '\u00A0'); return finalText; } @@ -130,9 +131,13 @@ export async function decoratePlaceholderArea({ if (!nodes.length) return; const config = getConfig(); await fetchPlaceholders({ placeholderPath, config, placeholderRequest }); - const replaceNodes = nodes.map(async (textNode) => { - textNode.nodeValue = await replaceText(textNode.nodeValue, config); - textNode.nodeValue = textNode.nodeValue.replace(/ /g, '\u00A0'); + const replaceNodes = nodes.map(async (nodeEl) => { + if (nodeEl.nodeType === Node.TEXT_NODE) { + nodeEl.nodeValue = await replaceText(nodeEl.nodeValue, config); + } else if (nodeEl.nodeType === Node.ELEMENT_NODE) { + const hrefVal = await replaceText(nodeEl.getAttribute('href'), config); + nodeEl.setAttribute('href', hrefVal); + } }); await Promise.all(replaceNodes); } diff --git a/libs/features/webapp-prompt/webapp-prompt.css b/libs/features/webapp-prompt/webapp-prompt.css index 39d012916a..dee60577b1 100644 --- a/libs/features/webapp-prompt/webapp-prompt.css +++ b/libs/features/webapp-prompt/webapp-prompt.css @@ -2,6 +2,7 @@ .appPrompt { --pep-background-prompt: #ffffff; --pep-background-progress: #e9e9e9; + --pep-dismiss-button-focus-border-color: #3b63fb; display: none; } @@ -159,10 +160,7 @@ } .appPrompt-close:focus { - /* For Firefox */ - outline: auto; - /* For Chrome, Edge, and Safari */ - outline: 2px solid -webkit-focus-ring-color; + outline: 2px solid var(--pep-dismiss-button-focus-border-color); } .appPrompt-progressWrapper { diff --git a/libs/martech/martech.js b/libs/martech/martech.js index 91906910bc..01492cadfe 100644 --- a/libs/martech/martech.js +++ b/libs/martech/martech.js @@ -118,14 +118,14 @@ export const getTargetPersonalization = async () => { const responseStart = Date.now(); window.addEventListener(ALLOY_SEND_EVENT, () => { const responseTime = calculateResponseTime(responseStart); - window.lana.log(`target response time: ${responseTime}`, { tags: 'errorType=info,module=martech' }); + window.lana.log(`target response time: ${responseTime}`, { tags: 'martech', errorType: 'i' }); }, { once: true }); let manifests = []; let propositions = []; const response = await waitForEventOrTimeout(ALLOY_SEND_EVENT, timeout); if (response.error) { - window.lana.log('target response time: ad blocker', { tags: 'errorType=info,module=martech' }); + window.lana.log('target response time: ad blocker', { tags: 'martech', errorType: 'i' }); return []; } if (response.timeout) { diff --git a/libs/navigation/navigation.js b/libs/navigation/navigation.js index e31dd77195..36e70eab8b 100644 --- a/libs/navigation/navigation.js +++ b/libs/navigation/navigation.js @@ -4,7 +4,7 @@ const blockConfig = [ name: 'global-navigation', targetEl: 'header', appendType: 'prepend', - params: ['imsClientId'], + params: ['imsClientId', 'searchEnabled'], }, { key: 'footer', diff --git a/libs/scripts/scripts.js b/libs/scripts/scripts.js index 4388eea325..7c97265400 100644 --- a/libs/scripts/scripts.js +++ b/libs/scripts/scripts.js @@ -22,11 +22,19 @@ import locales from '../utils/locales.js'; const prodDomains = ['milo.adobe.com']; const stageDomainsMap = { - 'www.adobe.com': 'www.stage.adobe.com', - 'blog.adobe.com': 'blog.stage.adobe.com', - 'business.adobe.com': 'business.stage.adobe.com', - 'helpx.adobe.com': 'helpx.stage.adobe.com', - 'news.adobe.com': 'news.stage.adobe.com', + 'www.stage.adobe.com': { + 'www.adobe.com': 'origin', + 'helpx.adobe.com': 'helpx.stage.adobe.com', + }, + '--bacom--adobecom.hlx.live': { + 'business.adobe.com': 'origin', + 'news.adobe.com': 'main--news--adobecom.hlx.live', + }, + '--blog--adobecom.hlx.page': { + 'blog.adobe.com': 'origin', + 'business.adobe.com': 'main--bacom--adobecom.hlx.page', + }, + '.business-graybox.adobe.com': { 'business.adobe.com': 'origin' }, }; const config = { diff --git a/libs/styles/styles.css b/libs/styles/styles.css index 53eca9bd3c..749ca43a02 100644 --- a/libs/styles/styles.css +++ b/libs/styles/styles.css @@ -211,7 +211,8 @@ .detail-xl, .detail-l, .detail-m, -.detail-s { +.detail-s, +.detail-xs { margin: 0; } @@ -808,6 +809,10 @@ a.static:hover { text-decoration: underline; } +.copy-link { + font-weight: 700; +} + /* Tooltip */ .tooltip-wrapper { visibility: hidden; diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 06b84a971b..556670c79f 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -61,9 +61,10 @@ export function decorateIconArea(el) { } export function decorateBlockText(el, config = ['m', 's', 'm'], type = null) { - const headings = el.querySelectorAll('h1, h2, h3, h4, h5, h6'); + let headings = el.querySelectorAll('h1, h2, h3, h4, h5, h6'); if (!el.classList.contains('default')) { if (headings) { + if (type === 'hasDetailHeading' && headings.length > 1) headings = [...headings].splice(1); headings.forEach((h) => h.classList.add(`heading-${config[0]}`)); if (config[2]) { const prevSib = headings[0]?.previousElementSibling; @@ -104,10 +105,10 @@ export function handleFocalpoint(pic, child, removeChild) { image.style.objectPosition = `${x} ${y}`; } -export async function decorateBlockBg(block, node, { useHandleFocalpoint = false } = {}) { +export async function decorateBlockBg(block, node, { useHandleFocalpoint = false, className = 'background' } = {}) { const childCount = node.childElementCount; if (node.querySelector('img, video, a[href*=".mp4"]') || childCount > 1) { - node.classList.add('background'); + node.classList.add(className); const binaryVP = [['mobile-only'], ['tablet-only', 'desktop-only']]; const allVP = [['mobile-only'], ['tablet-only'], ['desktop-only']]; const viewports = childCount === 2 ? binaryVP : allVP; diff --git a/libs/utils/helpers.js b/libs/utils/helpers.js index 73274a4988..f461139f3e 100644 --- a/libs/utils/helpers.js +++ b/libs/utils/helpers.js @@ -19,3 +19,21 @@ export function updateLinkWithLangRoot(link) { return link; } } + +/** + * Replaces the origin of the provided link with location.origin. + * + * @param link + * @returns {string|*} + */ +export function overrideUrlOrigin(link) { + try { + const url = new URL(link); + if (url.hostname !== window.location.hostname) { + return link.replace(url.origin, window.location.origin); + } + } catch (e) { + // ignore + } + return link; +} diff --git a/libs/utils/utils.js b/libs/utils/utils.js index ede1d199c0..07fee41492 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -620,17 +620,50 @@ export function decorateAutoBlock(a) { }); } +const decorateCopyLink = (a, evt) => { + const userAgent = navigator.userAgent.toLowerCase(); + const isMobile = /android|iphone|mobile/.test(userAgent) && !/ipad/.test(userAgent); + if (!isMobile || !navigator.share) { + a.remove(); + return; + } + const link = a.href.replace(evt, ''); + const isConButton = ['EM', 'STRONG'].includes(a.parentElement.nodeName) + || a.classList.contains('con-button'); + if (!isConButton) a.classList.add('static', 'copy-link'); + a.href = ''; + a.addEventListener('click', async (e) => { + e.preventDefault(); + if (navigator.share) await navigator.share({ title: link, url: link }); + }); +}; + +export function convertStageLinks({ anchors, config, hostname }) { + if (config.env?.name === 'prod' || !config.stageDomainsMap) return; + const matchedRules = Object.entries(config.stageDomainsMap) + .find(([domain]) => hostname.includes(domain)); + if (!matchedRules) return; + const [, domainsMap] = matchedRules; + [...anchors].forEach((a) => { + const matchedDomain = Object.keys(domainsMap) + .find((domain) => a.href.includes(domain)); + if (!matchedDomain) return; + a.href = a.href.replace(a.hostname, domainsMap[matchedDomain] === 'origin' + ? hostname + : domainsMap[matchedDomain]); + }); +} + export function decorateLinks(el) { const config = getConfig(); decorateImageLinks(el); const anchors = el.getElementsByTagName('a'); + const { hostname } = window.location; + convertStageLinks({ anchors, config, hostname }); return [...anchors].reduce((rdx, a) => { appendHtmlToLink(a); a.href = localizeLink(a.href); decorateSVG(a); - if (config.env?.name === 'stage' && config.stageDomainsMap?.[a.hostname]) { - a.href = a.href.replace(a.hostname, config.stageDomainsMap[a.hostname]); - } if (a.href.includes('#_blank')) { a.setAttribute('target', '_blank'); a.href = a.href.replace('#_blank', ''); @@ -656,6 +689,10 @@ export function decorateLinks(el) { window.adobeIMS?.signIn(); }); } + const copyEvent = '#_evt-copy'; + if (a.href.includes(copyEvent)) { + decorateCopyLink(a, copyEvent); + } return rdx; }, []); } @@ -738,23 +775,21 @@ export async function customFetch({ resource, withCacheRules }) { const findReplaceableNodes = (area) => { const regex = /{{(.*?)}}|%7B%7B(.*?)%7D%7D/g; - const walker = document.createTreeWalker( - area, - NodeFilter.SHOW_TEXT, - { - acceptNode(node) { - const a = regex.test(node.nodeValue) - ? NodeFilter.FILTER_ACCEPT - : NodeFilter.FILTER_REJECT; - regex.lastIndex = 0; - return a; - }, - }, - ); + const walker = document.createTreeWalker(area, NodeFilter.SHOW_ALL); const nodes = []; let node = walker.nextNode(); while (node !== null) { - nodes.push(node); + let matchFound = false; + if (node.nodeType === Node.TEXT_NODE) { + matchFound = regex.test(node.nodeValue); + } else if (node.nodeType === Node.ELEMENT_NODE && node.hasAttribute('href')) { + const hrefValue = node.getAttribute('href'); + matchFound = regex.test(hrefValue); + } + if (matchFound) { + nodes.push(node); + regex.lastIndex = 0; + } node = walker.nextNode(); } return nodes; diff --git a/nala/blocks/accordion/accordion.spec.js b/nala/blocks/accordion/accordion.spec.js index a40d8740e3..de3a6a19e5 100644 --- a/nala/blocks/accordion/accordion.spec.js +++ b/nala/blocks/accordion/accordion.spec.js @@ -11,7 +11,7 @@ module.exports = { heading1: 'What size PDFs can I compress?', heading2: 'How do I check my PDF file size?', }, - tags: '@accordion @t1 @smoke @regression @milo', + tags: '@accordion @smoke @regression @milo', }, { tcid: '1', diff --git a/nala/blocks/actionitem/actionitem.page.js b/nala/blocks/actionitem/actionitem.page.js new file mode 100644 index 0000000000..552c5ce9e1 --- /dev/null +++ b/nala/blocks/actionitem/actionitem.page.js @@ -0,0 +1,33 @@ +export default class ActionItem { + constructor(page, nth = 0) { + this.page = page; + + this.actionItem = this.page.locator('.action-item').nth(nth); + this.small = this.page.locator('.action-item.small').nth(nth); + this.medium = this.page.locator('.action-item.medium').nth(nth); + this.large = this.page.locator('.action-item.large').nth(nth); + this.center = this.page.locator('.action-item.center').nth(nth); + this.rounded = this.page.locator('.action-item.rounded').nth(nth); + this.actionItemFloat = this.page.locator('.action-item.float-button').nth(nth); + this.floatButton = this.page.locator('.action-item.float-button > div > div> p > a'); + this.libraryContainerStart = this.page.locator('.library-container-start').nth(nth); + this.libraryContainerEnd = this.page.locator('.library-container-end').nth(nth); + this.actionScroller = this.page.locator('.action-scroller').nth(nth); + this.scroller = this.page.locator('.scroller ').nth(nth); + this.scrollerActionItems = this.scroller.locator('.action-item'); + + this.navigationPrevious = this.actionScroller.locator('.nav-grad.previous'); + this.navigationNext = this.actionScroller.locator('.nav-grad.next'); + this.nextButton = this.navigationNext.locator('.nav-button.next-button'); + this.previousButton = this.navigationPrevious.locator('.nav-button.previous-button'); + + this.scrollerActionItems = this.scroller.locator('.action-item'); + + this.mainImage = this.actionItem.locator('.main-image').nth(nth); + this.mainImageDark = this.actionItem.locator('.main-image.dark').nth(nth); + this.image = this.mainImage.locator('img').nth(0); + this.bodyText = this.actionItem.locator('p').nth(1); + this.bodyTextLink = this.actionItem.locator('a').nth(0); + this.floatOutlineButton = this.mainImage.locator('a'); + } +} diff --git a/nala/blocks/actionitem/actionitem.spec.js b/nala/blocks/actionitem/actionitem.spec.js new file mode 100644 index 0000000000..54b8f92efe --- /dev/null +++ b/nala/blocks/actionitem/actionitem.spec.js @@ -0,0 +1,87 @@ +module.exports = { + FeatureName: 'Action Item Block', + features: [ + { + tcid: '0', + name: '@Action-item (small)', + path: '/drafts/nala/blocks/action-item/action-item-small', + data: { + bodyText: 'Body XS Regular - Image min-height 56px', + imgMinHeight: '56px', + }, + tags: '@action-item @smoke @regression @milo', + }, + { + tcid: '1', + name: '@Action-item (medium)', + path: '/drafts/nala/blocks/action-item/action-item-medium', + data: { + bodyText: 'Body S Regular - Image min-height 80px', + imgMinHeight: '80px', + }, + tags: '@action-item @smoke @regression @milo', + }, + { + tcid: '2', + name: '@Action-item (large)', + path: '/drafts/nala/blocks/action-item/action-item-large', + data: { + bodyText: 'Body M Regular - Image min-height 104px', + imgMinHeight: '104px', + }, + tags: '@action-item @smoke @regression @milo', + }, + { + tcid: '3', + name: '@Action-item (center)', + path: '/drafts/nala/blocks/action-item/action-item-center', + data: { + bodyText: 'Center content', + margin: '0 auto', + }, + tags: '@action-item @smoke @regression @milo', + }, + { + tcid: '4', + name: '@Action-item (rounded)', + path: '/drafts/nala/blocks/action-item/action-item-rounded', + data: { + bodyText: 'Border radius 4px', + borderRadius: '4px', + }, + tags: '@action-item @smoke @regression @milo', + }, + { + tcid: '5', + name: '@Action-item (float-button)', + path: '/drafts/nala/blocks/action-item/action-item-float-button', + data: { + bodyText: 'Float button', + floatButtonText: 'Edit', + }, + tags: '@action-item @smoke @regression @milo', + }, + { + tcid: '6', + name: '@Action-item (scroller)', + path: '/drafts/nala/blocks/action-item/action-scroller', + data: { + bodyText: 'content', + floatButtonText: 'Edit', + actionItemsCount: 6, + }, + tags: '@action-item @smoke @regression @milo', + }, + { + tcid: '7', + name: '@Action-item (scroller with navigation)', + path: '/drafts/nala/blocks/action-item/action-scroller-navigation', + data: { + bodyText: 'content', + floatButtonText: 'Edit', + actionItemsCount: 8, + }, + tags: '@action-item @smoke @regression @milo', + }, + ], +}; diff --git a/nala/blocks/actionitem/actionitem.test.js b/nala/blocks/actionitem/actionitem.test.js new file mode 100644 index 0000000000..75cda610ba --- /dev/null +++ b/nala/blocks/actionitem/actionitem.test.js @@ -0,0 +1,188 @@ +import { expect, test } from '@playwright/test'; +import { features } from './actionitem.spec.js'; +import ActionItem from './actionitem.page.js'; + +let actionItem; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo Action-Item block test suite', () => { + test.beforeEach(async ({ page }) => { + actionItem = new ActionItem(page); + }); + + // Test 0 : Action-Item (Small) + test(`0: @Action-item (small), ${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + const { data } = features[0]; + + await test.step('step-1: Go to Action item block test page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Action item content/specs', async () => { + await expect(await actionItem.small).toBeVisible(); + await expect(await actionItem.image).toBeVisible(); + await expect(await actionItem.image).toHaveCSS('min-height', data.imgMinHeight); + + await expect(await actionItem.bodyTextLink).toBeVisible(); + await expect(await actionItem.bodyText).toContainText(data.bodyText); + }); + }); + + // Test 1 : Action-Item (Medium) + test(`1: @Action-item (medium), ${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + const { data } = features[1]; + + await test.step('step-1: Go to Action item block test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Action item content/specs', async () => { + await expect(await actionItem.medium).toBeVisible(); + await expect(await actionItem.image).toBeVisible(); + await expect(await actionItem.image).toHaveCSS('min-height', data.imgMinHeight); + + await expect(await actionItem.bodyTextLink).toBeVisible(); + await expect(await actionItem.bodyText).toContainText(data.bodyText); + }); + }); + + // Test 2 : Action-Item (Large) + test(`2: @Action-item (large), ${features[2].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + const { data } = features[2]; + + await test.step('step-1: Go to Action item block test page', async () => { + await page.goto(`${baseURL}${features[2].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[2].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Action item content/specs', async () => { + await expect(await actionItem.large).toBeVisible(); + await expect(await actionItem.image).toBeVisible(); + await expect(await actionItem.image).toHaveCSS('min-height', data.imgMinHeight); + + await expect(await actionItem.bodyTextLink).toBeVisible(); + await expect(await actionItem.bodyText).toContainText(data.bodyText); + }); + }); + + // Test 3 : Action-Item (Center) + test(`3: @Action-item (center), ${features[3].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[3].path}${miloLibs}`); + const { data } = features[3]; + + await test.step('step-1: Go to Action item block test page', async () => { + await page.goto(`${baseURL}${features[3].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[3].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Action item content/specs', async () => { + await expect(await actionItem.center).toBeVisible(); + await expect(await actionItem.image).toBeVisible(); + + await expect(await actionItem.bodyTextLink).toBeVisible(); + await expect(await actionItem.bodyText).toContainText(data.bodyText); + }); + }); + + // Test 4 : Action-Item (Rounded) + test(`4: @Action-item (rounded), ${features[4].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[4].path}${miloLibs}`); + const { data } = features[4]; + + await test.step('step-1: Go to Action item block test page', async () => { + await page.goto(`${baseURL}${features[4].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[4].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Action item content/specs', async () => { + await expect(await actionItem.rounded).toBeVisible(); + await expect(await actionItem.image).toBeVisible(); + await expect(await actionItem.image).toHaveCSS('border-radius', data.borderRadius); + + await expect(await actionItem.bodyTextLink).toBeVisible(); + await expect(await actionItem.bodyText).toContainText(data.bodyText); + }); + }); + + // Test 5 : Action-Item (Float Button) + test(`5: @Action-item (float-button), ${features[5].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[5].path}${miloLibs}`; + console.info(`[Test Page]: ${testPage}`); + const { data } = features[5]; + + await test.step('step-1: Go to Action item block test page', async () => { + await page.goto(`${baseURL}${features[5].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[5].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Action item content/specs', async () => { + await expect(await actionItem.actionItemFloat).toBeVisible(); + await expect(await actionItem.image).toBeVisible(); + await expect(await actionItem.floatOutlineButton).toBeVisible(); + await expect(await actionItem.floatOutlineButton).toContainText(data.floatButtonText); + }); + await test.step('step-3: Click the float button', async () => { + await actionItem.floatButton.click(); + expect(await page.url()).not.toBe(testPage); + }); + }); + + // Test 6 : Action-Item (scroller) + test(`6: @Action-item (scroller), ${features[6].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[6].path}${miloLibs}`); + const { data } = features[6]; + + await test.step('step-1: Go to Action item block test page', async () => { + await page.goto(`${baseURL}${features[6].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[6].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Action item content/specs', async () => { + await expect(await actionItem.actionScroller).toBeVisible(); + await expect(await actionItem.scroller).toBeVisible(); + await expect(await actionItem.scrollerActionItems).toHaveCount(data.actionItemsCount); + + await expect(await actionItem.image).toBeVisible(); + await expect(await actionItem.bodyText).toContainText(data.bodyText); + }); + }); + + // Test 7 : Action-Item (scroller) + test(`7: @Action-item (scroller with navigation), ${features[7].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[7].path}${miloLibs}`); + const { data } = features[7]; + + await test.step('step-1: Go to Action item block test page', async () => { + await page.goto(`${baseURL}${features[7].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[7].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Action item content/specs', async () => { + await expect(await actionItem.actionScroller).toBeVisible(); + await expect(await actionItem.scroller).toBeVisible(); + await expect(await actionItem.scrollerActionItems).toHaveCount(data.actionItemsCount); + + await expect(await actionItem.image).toBeVisible(); + await expect(await actionItem.bodyText).toContainText(data.bodyText); + + await expect(await actionItem.nextButton).toBeVisible({ timeout: 1000 }); + await actionItem.nextButton.click(); + await expect(await actionItem.previousButton).toBeVisible({ timeout: 1000 }); + await expect(await actionItem.navigationNext).toHaveAttribute('hide-btn', 'false'); + }); + }); +}); diff --git a/nala/blocks/aside/aside.page.js b/nala/blocks/aside/aside.page.js new file mode 100644 index 0000000000..15cc507475 --- /dev/null +++ b/nala/blocks/aside/aside.page.js @@ -0,0 +1,65 @@ +/* eslint-disable import/no-import-module-exports */ + +export default class Aside { + constructor(page) { + this.page = page; + + this.aside = page.locator('.aside'); + this.textField = this.aside.locator('.text'); + this.textFieldSmall = this.aside.locator('p.body-s').first(); + this.textFieldMedium = this.aside.locator('p.body-m').first(); + this.textFieldLarge = this.aside.locator('p.body-l').first(); + // ASIDE HEADINGS: + this.h2TitleXLarge = this.aside.locator('h2.heading-xl'); + this.h3TitleXLarge = this.aside.locator('h3.heading-xl'); + this.h2TitleLarge = this.aside.locator('h2.heading-l'); + this.h3TitleLarge = this.aside.locator('h3.heading-l'); + this.h2TitleSmall = this.aside.locator('h2.heading-s'); + this.h3TitleSmall = this.aside.locator('h3.heading-s'); + // ASIDE BLOCK ELEMENTS: + this.icon = this.aside.locator('p.icon-area picture'); + this.image = this.aside.locator('div.image'); + this.noImage = this.aside.locator('div.no-image'); + this.iconArea = this.aside.locator('p.icon-area'); + this.detailLabel = this.aside.locator('p.detail-m'); + this.actionArea = this.aside.locator('p.action-area'); + this.textLink = this.textField.locator('a').first(); + this.linkTextCta = this.aside.locator('a[daa-ll*="Link"], a[daa-ll*="link"], a[daa-ll*="action"]'); + this.actionLinks = this.aside.locator('div[data-valign="middle"] a'); + this.actionButtons = this.aside.locator('p.action-area a'); + this.blueButtonCta = this.aside.locator('a.con-button.blue'); + this.blackButtonCta = this.aside.locator('a.con-button.outline'); + // ASIDE DEFAULT BLOCKS: + this.asideSmall = page.locator('div.aside.small'); + this.asideMedium = page.locator('div.aside.medium'); + this.asideLarge = page.locator('div.aside.large'); + // ASIDE INLINE BLOCKS: + this.asideInline = page.locator('div.aside.inline'); + this.asideInlineDark = page.locator('div.aside.inline.dark'); + // ASIDE SPLIT BLOCKS: + this.asideSplitSmallDark = page.locator('div.aside.split.small.dark'); + this.asideSplitSmallHalfDark = page.locator('div.aside.split.small.half.dark'); + this.asideSplitMedium = page.locator('div.aside.split.medium'); + this.asideSplitMedidumHalf = page.locator('div.aside.split.medium.half'); + this.asideSplitLarge = page.locator('div.aside.split.large'); + this.asideSplitLargeHalfDark = page.locator('div.aside.split.large.half.dark'); + // ASIDE NOTIFICATION BLOCKS: + this.asideNotifSmall = page.locator('div.aside.notification.small'); + this.asideNotifMedium = page.locator('div.aside.notification.medium'); + this.asideNotifLarge = page.locator('div.aside.notification.large'); + this.asideNotifMediumCenter = page.locator('div.aside.notification.center'); + this.asideNotifLargeCenter = page.locator('div.aside.notification.large.center'); + this.asideNotifExtraSmallDark = page.locator('div.aside.notification.extra-small.dark'); + + // ASIDE PROPS: + this.props = { + background: { + black: 'rgb(17, 17, 17)', + darkGrey: 'rgb(171, 171, 171)', + lightGrey1: 'rgb(238, 238, 238)', + lightGrey2: 'rgb(245, 245, 245)', + lightGrey3: 'rgb(249, 249, 249)', + }, + }; + } +} diff --git a/nala/blocks/aside/aside.spec.js b/nala/blocks/aside/aside.spec.js new file mode 100644 index 0000000000..aa73285964 --- /dev/null +++ b/nala/blocks/aside/aside.spec.js @@ -0,0 +1,107 @@ +module.exports = { + name: 'Aside Block', + features: [ + { + name: '@Aside-Small', + path: '/drafts/nala/blocks/aside/aside-small', + browserParams: '?georouting=off', + tags: '@aside @aside-small @smoke @regression @milo', + }, + { + name: '@Aside-Medium', + path: '/drafts/nala/blocks/aside/aside-medium', + browserParams: '?georouting=off', + tags: '@aside @aside-medium @smoke @regression @milo', + }, + { + name: '@Aside-Large', + path: '/drafts/nala/blocks/aside/aside-large', + browserParams: '?georouting=off', + tags: '@aside @aside-large @smoke @regression @milo', + }, + { + name: '@Aside-Split-Small-Dark', + path: '/drafts/nala/blocks/aside/aside-split-small-dark', + browserParams: '?georouting=off', + tags: '@aside @aside-split-small-dark @smoke @regression @milo', + }, + { + name: '@Aside-Split-Small-Half-Dark', + path: '/drafts/nala/blocks/aside/aside-split-small-half-dark', + browserParams: '?georouting=off', + tags: '@aside @aside-split-small-half-dark @smoke @regression @milo', + }, + { + name: '@Aside-Split-Medium', + path: '/drafts/nala/blocks/aside/aside-split-medium', + browserParams: '?georouting=off', + tags: '@aside @aside-split-medium @smoke @regression @milo', + }, + { + name: '@Aside-Split-Medium-Half', + path: '/drafts/nala/blocks/aside/aside-split-medium-half', + browserParams: '?georouting=off', + tags: '@aside @aside-split-medium-half @smoke @regression @milo', + }, + { + name: '@Aside-Split-Large', + path: '/drafts/nala/blocks/aside/aside-split-large', + browserParams: '?georouting=off', + tags: '@aside @aside-split-large @smoke @regression @milo', + }, + { + name: '@Aside-Split-Large-Half-Dark', + path: '/drafts/nala/blocks/aside/aside-split-large-half-dark', + browserParams: '?georouting=off', + tags: '@aside @aside-split-large-half-dark @smoke @regression @milo', + }, + { + name: '@Aside-Inline', + path: '/drafts/nala/blocks/aside/aside-inline', + browserParams: '?georouting=off', + tags: '@aside @aside-inline @smoke @regression @milo', + }, + { + name: '@Aside-Inline-Dark', + path: '/drafts/nala/blocks/aside/aside-inline-dark', + browserParams: '?georouting=off', + tags: '@aside @aside-inline-dark @smoke @regression @milo', + }, + { + name: '@Aside-Notif-Extra-Small-Dark', + path: '/drafts/nala/blocks/aside/aside-notification-extrasmall-dark', + browserParams: '?georouting=off', + tags: '@aside @aside-notif-extra-small-dark @smoke @regression @milo', + }, + { + name: '@Aside-Notif-Small', + path: '/drafts/nala/blocks/aside/aside-notification-small', + browserParams: '?georouting=off', + tags: '@aside @aside-notif-small @smoke @regression @milo', + }, + { + name: '@Aside-Notif-Medium', + path: '/drafts/nala/blocks/aside/aside-notification-medium', + browserParams: '?georouting=off', + tags: '@aside @aside-notif-medium @smoke @regression @milo', + }, + { + name: '@Aside-Notif-Medium-Center', + path: '/drafts/nala/blocks/aside/aside-notification-medium-center', + browserParams: '?georouting=off', + tags: '@aside @aside-notif-medium-center @smoke @regression @milo', + }, + { + name: '@Aside-Notif-Large', + path: '/drafts/nala/blocks/aside/aside-notification-large', + browserParams: '?georouting=off', + tags: '@aside @aside-notif-large @smoke @regression @milo', + }, + { + name: '@Aside-Notif-Large-Center', + path: '/drafts/nala/blocks/aside/aside-notification-large-center', + browserParams: '?georouting=off', + tags: '@aside @aside-notif-large-center @smoke @regression @milo', + }, + ], +}; diff --git a/nala/blocks/aside/aside.test.js b/nala/blocks/aside/aside.test.js new file mode 100644 index 0000000000..2fe6e659f1 --- /dev/null +++ b/nala/blocks/aside/aside.test.js @@ -0,0 +1,525 @@ +import { expect, test } from '@playwright/test'; +import { features } from './aside.spec.js'; +import AsideBlock from './aside.page.js'; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Aside Block test suite', () => { + // Aside Small Checks: + test(`${features[0].name}, ${features[0].tags}`, async ({ page, baseURL }) => { + const Aside = new AsideBlock(page); + console.info(`[Test Page]: ${baseURL}${features[0].path}`); + + await test.step('Navigate to page with Aside block', async () => { + await page.goto(`${baseURL}${features[0].path}${features[0].browserParams}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${features[0].browserParams}&${miloLibs}`); + }); + + await test.step('Validate Aside block content', async () => { + await expect(Aside.asideSmall).toBeVisible(); + await expect(Aside.icon).toBeVisible(); + await expect(Aside.image).toBeVisible(); + await expect(Aside.actionArea).toBeVisible(); + await expect(Aside.detailLabel).toBeVisible(); + await expect(Aside.h2TitleXLarge).toBeVisible(); + await expect(Aside.textFieldSmall).toBeVisible(); + // Check Aside block buttons: + await expect(Aside.textLink).toBeVisible(); + await expect(Aside.blueButtonCta).toBeVisible(); + await expect(Aside.blackButtonCta).toBeVisible(); + expect(await Aside.actionButtons.count()).toEqual(2); + // Check Aside block background: + const bgdColor = await Aside.asideSmall.evaluate( + (e) => window.getComputedStyle(e).getPropertyValue('background-color'), + ); + expect(bgdColor).toBe(Aside.props.background.lightGrey1); + }); + }); + + // Aside Medium Checks: + test(`${features[1].name}, ${features[1].tags}`, async ({ page, baseURL }) => { + const Aside = new AsideBlock(page); + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + + await test.step('Navigate to page with Aside block', async () => { + await page.goto(`${baseURL}${features[1].path}${features[1].browserParams}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${features[1].browserParams}&${miloLibs}`); + }); + + await test.step('Validate Aside block content', async () => { + await expect(Aside.asideMedium).toBeVisible(); + await expect(Aside.icon).toBeVisible(); + await expect(Aside.image).toBeVisible(); + await expect(Aside.actionArea).toBeVisible(); + await expect(Aside.detailLabel).toBeVisible(); + await expect(Aside.h3TitleXLarge).toBeVisible(); + await expect(Aside.textFieldSmall).toBeVisible(); + // Check Aside block buttons: + await expect(Aside.blueButtonCta).not.toBeVisible(); + await expect(Aside.blackButtonCta).toBeVisible(); + await expect(Aside.linkTextCta).toBeVisible(); + expect(await Aside.actionButtons.count()).toEqual(2); + // Check Aside block background: + const bgdColor = await Aside.asideMedium.evaluate((e) => window.getComputedStyle(e).getPropertyValue('background-color')); + expect(bgdColor).toBe(Aside.props.background.lightGrey1); + }); + }); + + // Aside Large Checks: + test(`${features[2].name}, ${features[2].tags}`, async ({ page, baseURL }) => { + const Aside = new AsideBlock(page); + console.info(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + + await test.step('Navigate to page with Aside block', async () => { + await page.goto(`${baseURL}${features[2].path}${features[2].browserParams}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[2].path}${features[2].browserParams}&${miloLibs}`); + }); + + await test.step('Validate Aside block content', async () => { + await expect(Aside.asideLarge).toBeVisible(); + await expect(Aside.icon).toBeVisible(); + await expect(Aside.image).toBeVisible(); + await expect(Aside.actionArea).toBeVisible(); + await expect(Aside.detailLabel).toBeVisible(); + await expect(Aside.h3TitleXLarge).toBeVisible(); + await expect(Aside.textFieldSmall).toBeVisible(); + // Check Aside block buttons: + await expect(Aside.blueButtonCta).toBeVisible(); + await expect(Aside.blackButtonCta).not.toBeVisible(); + await expect(Aside.linkTextCta).toBeVisible(); + expect(await Aside.actionButtons.count()).toEqual(2); + // Check Aside block background: + const bgdColor = await Aside.asideLarge.evaluate((e) => window.getComputedStyle(e).getPropertyValue('background-color')); + expect(bgdColor).toBe(Aside.props.background.lightGrey1); + }); + }); + + // Aside Split Small Dark Checks: + test(`${features[3].name}, ${features[3].tags}`, async ({ page, baseURL }) => { + const Aside = new AsideBlock(page); + console.info(`[Test Page]: ${baseURL}${features[3].path}${miloLibs}`); + + await test.step('Navigate to page with Aside block', async () => { + await page.goto(`${baseURL}${features[3].path}${features[3].browserParams}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[3].path}${features[3].browserParams}&${miloLibs}`); + }); + + await test.step('Validate Aside block content', async () => { + await expect(Aside.asideSplitSmallDark).toBeVisible(); + await expect(Aside.icon).not.toBeVisible(); + await expect(Aside.image).toBeVisible(); + await expect(Aside.actionArea).toBeVisible(); + await expect(Aside.detailLabel).toBeVisible(); + await expect(Aside.h3TitleXLarge).toBeVisible(); + await expect(Aside.textFieldSmall).toBeVisible(); + // Check Aside block buttons: + await expect(Aside.blueButtonCta).toBeVisible(); + await expect(Aside.blackButtonCta).toBeVisible(); + await expect(Aside.linkTextCta).not.toBeVisible(); + expect(await Aside.actionButtons.count()).toEqual(2); + // Check Aside block background: + const bgdColor = await Aside.asideSplitSmallDark.evaluate((e) => window.getComputedStyle(e).getPropertyValue('background-color')); + expect(bgdColor).toBe(Aside.props.background.black); + }); + }); + + // Aside Split Small Half Dark Checks: + test(`${features[4].name}, ${features[4].tags}`, async ({ page, baseURL }) => { + const Aside = new AsideBlock(page); + console.info(`[Test Page]: ${baseURL}${features[4].path}${miloLibs}`); + + await test.step('Navigate to page with Aside block', async () => { + await page.goto(`${baseURL}${features[4].path}${features[4].browserParams}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[4].path}${features[4].browserParams}&${miloLibs}`); + }); + + await test.step('Validate Aside block content', async () => { + await expect(Aside.asideSplitSmallHalfDark).toBeVisible(); + await expect(Aside.icon).not.toBeVisible(); + await expect(Aside.image).toBeVisible(); + await expect(Aside.actionArea).toBeVisible(); + await expect(Aside.detailLabel).toBeVisible(); + await expect(Aside.h3TitleXLarge).toBeVisible(); + await expect(Aside.textFieldSmall).toBeVisible(); + // Check Aside block buttons: + await expect(Aside.blueButtonCta).toBeVisible(); + await expect(Aside.blackButtonCta).toBeVisible(); + await expect(Aside.linkTextCta).not.toBeVisible(); + expect(await Aside.actionButtons.count()).toEqual(2); + // Check Aside block background: + const bgdColor = await Aside.asideSplitSmallHalfDark.evaluate((e) => window.getComputedStyle(e).getPropertyValue('background-color')); + expect(bgdColor).toBe(Aside.props.background.black); + }); + }); + + // Aside Split Medium Checks: + test(`${features[5].name}, ${features[5].tags}`, async ({ page, baseURL }) => { + const Aside = new AsideBlock(page); + console.info(`[Test Page]: ${baseURL}${features[5].path}${miloLibs}`); + + await test.step('Navigate to page with Aside block', async () => { + await page.goto(`${baseURL}${features[5].path}${features[5].browserParams}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[5].path}${features[5].browserParams}&${miloLibs}`); + }); + + await test.step('Validate Aside block content', async () => { + await expect(Aside.asideSplitMedium).toBeVisible(); + await expect(Aside.icon).not.toBeVisible(); + await expect(Aside.image).toBeVisible(); + await expect(Aside.actionArea).toBeVisible(); + await expect(Aside.detailLabel).toBeVisible(); + await expect(Aside.h3TitleXLarge).toBeVisible(); + await expect(Aside.textFieldSmall).toBeVisible(); + // Check Aside block buttons: + await expect(Aside.blueButtonCta).toBeVisible(); + await expect(Aside.blackButtonCta).toBeVisible(); + await expect(Aside.linkTextCta).not.toBeVisible(); + expect(await Aside.actionButtons.count()).toEqual(2); + // Check Aside block background: + const bgdColor = await Aside.asideSplitMedium.evaluate((e) => window.getComputedStyle(e).getPropertyValue('background-color')); + expect(bgdColor).toBe(Aside.props.background.lightGrey3); + }); + }); + + // Aside Split Medium Half Checks: + test(`${features[6].name}, ${features[6].tags}`, async ({ page, baseURL }) => { + const Aside = new AsideBlock(page); + console.info(`[Test Page]: ${baseURL}${features[6].path}${miloLibs}`); + + await test.step('Navigate to page with Aside block', async () => { + await page.goto(`${baseURL}${features[6].path}${features[6].browserParams}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[6].path}${features[6].browserParams}&${miloLibs}`); + }); + + await test.step('Validate Aside block content', async () => { + await expect(Aside.asideSplitMedidumHalf).toBeVisible(); + await expect(Aside.icon).not.toBeVisible(); + await expect(Aside.image).toBeVisible(); + await expect(Aside.actionArea).toBeVisible(); + await expect(Aside.detailLabel).toBeVisible(); + await expect(Aside.h3TitleXLarge).toBeVisible(); + await expect(Aside.textFieldSmall).toBeVisible(); + // Check Aside block buttons: + await expect(Aside.blueButtonCta).toBeVisible(); + await expect(Aside.blackButtonCta).toBeVisible(); + await expect(Aside.linkTextCta).not.toBeVisible(); + expect(await Aside.actionButtons.count()).toEqual(2); + // Check Aside block background: + const bgdColor = await Aside.asideSplitMedidumHalf.evaluate((e) => window.getComputedStyle(e).getPropertyValue('background-color')); + expect(bgdColor).toBe(Aside.props.background.lightGrey3); + }); + }); + + // Aside Split Large Checks: + test(`${features[7].name}, ${features[7].tags}`, async ({ page, baseURL }) => { + const Aside = new AsideBlock(page); + console.info(`[Test Page]: ${baseURL}${features[7].path}${miloLibs}`); + + await test.step('Navigate to page with Aside block', async () => { + await page.goto(`${baseURL}${features[7].path}${features[7].browserParams}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[7].path}${features[7].browserParams}&${miloLibs}`); + }); + + await test.step('Validate Aside block content', async () => { + await expect(Aside.asideSplitLarge).toBeVisible(); + await expect(Aside.icon).not.toBeVisible(); + await expect(Aside.image).toBeVisible(); + await expect(Aside.actionArea).toBeVisible(); + await expect(Aside.detailLabel).toBeVisible(); + await expect(Aside.h3TitleXLarge).toBeVisible(); + await expect(Aside.textFieldSmall).toBeVisible(); + // Check Aside block buttons: + await expect(Aside.blueButtonCta).toBeVisible(); + await expect(Aside.blackButtonCta).toBeVisible(); + await expect(Aside.linkTextCta).not.toBeVisible(); + expect(await Aside.actionButtons.count()).toEqual(2); + // !Note: Aside Split Large doesn't have default background! + }); + }); + + // Aside Split Large Half Dark Checks: + test(`${features[8].name}, ${features[8].tags}`, async ({ page, baseURL }) => { + const Aside = new AsideBlock(page); + console.info(`[Test Page]: ${baseURL}${features[8].path}${miloLibs}`); + + await test.step('Navigate to page with Aside block', async () => { + await page.goto(`${baseURL}${features[8].path}${features[8].browserParams}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[8].path}${features[8].browserParams}&${miloLibs}`); + }); + + await test.step('Validate Aside block content', async () => { + await expect(Aside.asideSplitLargeHalfDark).toBeVisible(); + await expect(Aside.icon).not.toBeVisible(); + await expect(Aside.image).toBeVisible(); + await expect(Aside.actionArea).toBeVisible(); + await expect(Aside.detailLabel).toBeVisible(); + await expect(Aside.h3TitleXLarge).toBeVisible(); + await expect(Aside.textFieldSmall).toBeVisible(); + // Check Aside block buttons: + await expect(Aside.blueButtonCta).toBeVisible(); + await expect(Aside.blackButtonCta).toBeVisible(); + await expect(Aside.linkTextCta).not.toBeVisible(); + expect(await Aside.actionButtons.count()).toEqual(2); + // Check Aside block background: + const bgdColor = await Aside.asideSplitLargeHalfDark.evaluate((e) => window.getComputedStyle(e).getPropertyValue('background-color')); + expect(bgdColor).toBe(Aside.props.background.black); + }); + }); + + // Aside Inline Checks: + test(`${features[9].name}, ${features[9].tags}`, async ({ page, baseURL }) => { + const Aside = new AsideBlock(page); + console.info(`[Test Page]: ${baseURL}${features[9].path}${miloLibs}`); + + await test.step('Navigate to page with Aside block', async () => { + await page.goto(`${baseURL}${features[9].path}${features[9].browserParams}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[9].path}${features[9].browserParams}&${miloLibs}`); + }); + + await test.step('Validate Aside block content', async () => { + await expect(Aside.asideInline).toBeVisible(); + await expect(Aside.icon).not.toBeVisible(); + await expect(Aside.image).toBeVisible(); + await expect(Aside.actionArea).toBeVisible(); + await expect(Aside.detailLabel).not.toBeVisible(); + await expect(Aside.h3TitleSmall).toBeVisible(); + await expect(Aside.textFieldMedium).toBeVisible(); + // Check Aside block buttons: + await expect(Aside.blueButtonCta).not.toBeVisible(); + await expect(Aside.blackButtonCta).toBeVisible(); + await expect(Aside.linkTextCta).not.toBeVisible(); + expect(await Aside.actionButtons.count()).toEqual(1); + // Check Aside block background: + const bgdColor = await Aside.asideInline.evaluate((e) => window.getComputedStyle(e).getPropertyValue('background-color')); + expect(bgdColor).toBe(Aside.props.background.lightGrey2); + }); + }); + + // Aside Inline Dark Checks: + test(`${features[10].name}, ${features[10].tags}`, async ({ page, baseURL }) => { + const Aside = new AsideBlock(page); + console.info(`[Test Page]: ${baseURL}${features[10].path}${miloLibs}`); + + await test.step('Navigate to page with Aside block', async () => { + await page.goto(`${baseURL}${features[10].path}${features[10].browserParams}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[10].path}${features[10].browserParams}&${miloLibs}`); + }); + + await test.step('Validate Aside block content', async () => { + await expect(Aside.asideInline).toBeVisible(); + await expect(Aside.icon).not.toBeVisible(); + await expect(Aside.image).toBeVisible(); + await expect(Aside.actionArea).toBeVisible(); + await expect(Aside.detailLabel).not.toBeVisible(); + await expect(Aside.h3TitleSmall).toBeVisible(); + await expect(Aside.textFieldMedium).toBeVisible(); + // Check Aside block buttons: + await expect(Aside.blueButtonCta).not.toBeVisible(); + await expect(Aside.blackButtonCta).toBeVisible(); + await expect(Aside.linkTextCta).not.toBeVisible(); + expect(await Aside.actionButtons.count()).toEqual(1); + // Check Aside block background: + const bgdColor = await Aside.asideInline.evaluate((e) => window.getComputedStyle(e).getPropertyValue('background-color')); + expect(bgdColor).toBe(Aside.props.background.black); + }); + }); + + // Aside Notification Extra Small Dark: + test(`${features[11].name}, ${features[11].tags}`, async ({ page, baseURL }) => { + const Aside = new AsideBlock(page); + console.info(`[Test Page]: ${baseURL}${features[11].path}${miloLibs}`); + + await test.step('Navigate to page with Aside block', async () => { + await page.goto(`${baseURL}${features[11].path}${features[11].browserParams}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[11].path}${features[11].browserParams}&${miloLibs}`); + }); + + await test.step('Validate Aside block content', async () => { + await expect(Aside.asideNotifExtraSmallDark).toBeVisible(); + await expect(Aside.icon).not.toBeVisible(); + await expect(Aside.noImage).toBeVisible(); + await expect(Aside.actionArea).not.toBeVisible(); + await expect(Aside.detailLabel).not.toBeVisible(); + await expect(Aside.h2TitleSmall).not.toBeVisible(); + await expect(Aside.h2TitleXLarge).not.toBeVisible(); + await expect(Aside.h3TitleSmall).not.toBeVisible(); + await expect(Aside.h3TitleXLarge).not.toBeVisible(); + await expect(Aside.textFieldSmall).not.toBeVisible(); + await expect(Aside.textFieldMedium).not.toBeVisible(); + await expect(Aside.textFieldLarge).not.toBeVisible(); + // Check Aside block buttons: + await expect(Aside.linkTextCta.first()).toBeVisible(); + await expect(Aside.blueButtonCta).not.toBeVisible(); + await expect(Aside.blackButtonCta).not.toBeVisible(); + expect(await Aside.actionLinks.count()).toEqual(2); + // Check Aside block background: + const bgdColor = await Aside.asideNotifExtraSmallDark.evaluate((e) => window.getComputedStyle(e).getPropertyValue('background-color')); + expect(bgdColor).toBe(Aside.props.background.black); + }); + }); + + // Aside Notification Small: + test(`${features[12].name}, ${features[12].tags}`, async ({ page, baseURL }) => { + const Aside = new AsideBlock(page); + console.info(`[Test Page]: ${baseURL}${features[12].path}${miloLibs}`); + + await test.step('Navigate to page with Aside block', async () => { + await page.goto(`${baseURL}${features[12].path}${features[12].browserParams}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[12].path}${features[12].browserParams}&${miloLibs}`); + }); + + await test.step('Validate Aside block content', async () => { + await expect(Aside.asideNotifSmall).toBeVisible(); + await expect(Aside.icon).toBeVisible(); + await expect(Aside.image).not.toBeVisible(); + await expect(Aside.actionArea).toBeVisible(); + await expect(Aside.detailLabel).not.toBeVisible(); + await expect(Aside.h2TitleSmall).not.toBeVisible(); + await expect(Aside.h2TitleXLarge).not.toBeVisible(); + await expect(Aside.h3TitleSmall).not.toBeVisible(); + await expect(Aside.h3TitleXLarge).not.toBeVisible(); + await expect(Aside.textFieldMedium).toBeVisible(); + // Check Aside block buttons: + await expect(Aside.textLink).toBeVisible(); + await expect(Aside.blueButtonCta).not.toBeVisible(); + await expect(Aside.blackButtonCta).toBeVisible(); + expect(await Aside.actionButtons.count()).toEqual(1); + // Check Aside block background: + const bgdColor = await Aside.asideNotifSmall.evaluate((e) => window.getComputedStyle(e).getPropertyValue('background-color')); + expect(bgdColor).toBe(Aside.props.background.darkGrey); + }); + }); + + // Aside Notification Medium: + test(`${features[13].name}, ${features[13].tags}`, async ({ page, baseURL }) => { + const Aside = new AsideBlock(page); + console.info(`[Test Page]: ${baseURL}${features[13].path}${miloLibs}`); + + await test.step('Navigate to page with Aside block', async () => { + await page.goto(`${baseURL}${features[13].path}${features[13].browserParams}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[13].path}${features[13].browserParams}&${miloLibs}`); + }); + + await test.step('Validate Aside block content', async () => { + await expect(Aside.asideNotifMedium).toBeVisible(); + await expect(Aside.icon).not.toBeVisible(); + await expect(Aside.image).toBeVisible(); + await expect(Aside.actionArea).toBeVisible(); + await expect(Aside.detailLabel).not.toBeVisible(); + await expect(Aside.h3TitleSmall).toBeVisible(); + await expect(Aside.textFieldSmall).toBeVisible(); + // Check Aside block buttons: + await expect(Aside.linkTextCta).toBeVisible(); + await expect(Aside.blueButtonCta).not.toBeVisible(); + await expect(Aside.blackButtonCta).toBeVisible(); + expect(await Aside.actionButtons.count()).toEqual(1); + // Check Aside block background: + const bgdColor = await Aside.asideNotifMedium.evaluate((e) => window.getComputedStyle(e).getPropertyValue('background-color')); + expect(bgdColor).toBe(Aside.props.background.darkGrey); + }); + }); + + // Aside Notification Medium Center: + test(`${features[14].name}, ${features[14].tags}`, async ({ page, baseURL }) => { + const Aside = new AsideBlock(page); + console.info(`[Test Page]: ${baseURL}${features[14].path}${miloLibs}`); + + await test.step('Navigate to page with Aside block', async () => { + await page.goto(`${baseURL}${features[14].path}${features[14].browserParams}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[14].path}${features[14].browserParams}&${miloLibs}`); + }); + + await test.step('Validate Aside block content', async () => { + await expect(Aside.asideNotifMedium).toBeVisible(); + await expect(Aside.icon).not.toBeVisible(); + await expect(Aside.image).not.toBeVisible(); + await expect(Aside.actionArea).toBeVisible(); + await expect(Aside.detailLabel).not.toBeVisible(); + await expect(Aside.h3TitleSmall).toBeVisible(); + await expect(Aside.textFieldSmall).toBeVisible(); + // Check Aside block buttons: + await expect(Aside.linkTextCta).toBeVisible(); + await expect(Aside.blueButtonCta).not.toBeVisible(); + await expect(Aside.blackButtonCta).toBeVisible(); + expect(await Aside.actionButtons.count()).toEqual(1); + // Check Aside block background: + const bgdColor = await Aside.asideNotifMedium.evaluate((e) => window.getComputedStyle(e).getPropertyValue('background-color')); + expect(bgdColor).toBe(Aside.props.background.darkGrey); + }); + }); + + // Aside Notification Large: + test(`${features[15].name}, ${features[15].tags}`, async ({ page, baseURL }) => { + const Aside = new AsideBlock(page); + console.info(`[Test Page]: ${baseURL}${features[15].path}${miloLibs}`); + + await test.step('Navigate to page with Aside block', async () => { + await page.goto(`${baseURL}${features[15].path}${features[15].browserParams}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[15].path}${features[15].browserParams}&${miloLibs}`); + }); + + await test.step('Validate Aside block content', async () => { + await expect(Aside.asideNotifLarge).toBeVisible(); + await expect(Aside.icon).not.toBeVisible(); + await expect(Aside.image).toBeVisible(); + await expect(Aside.actionArea).toBeVisible(); + await expect(Aside.detailLabel).not.toBeVisible(); + await expect(Aside.h3TitleLarge).toBeVisible(); + await expect(Aside.textFieldMedium).toBeVisible(); + // Check Aside block buttons: + await expect(Aside.linkTextCta).toBeVisible(); + await expect(Aside.blueButtonCta).not.toBeVisible(); + await expect(Aside.blackButtonCta).toBeVisible(); + expect(await Aside.actionButtons.count()).toEqual(1); + // Check Aside block background: + const bgdColor = await Aside.asideNotifLarge.evaluate((e) => window.getComputedStyle(e).getPropertyValue('background-color')); + expect(bgdColor).toBe(Aside.props.background.darkGrey); + }); + }); + + // Aside Notification Large Center: + test(`${features[16].name}, ${features[16].tags}`, async ({ page, baseURL }) => { + const Aside = new AsideBlock(page); + console.info(`[Test Page]: ${baseURL}${features[16].path}${miloLibs}`); + + await test.step('Navigate to page with Aside block', async () => { + await page.goto(`${baseURL}${features[16].path}${features[16].browserParams}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[16].path}${features[16].browserParams}&${miloLibs}`); + }); + + await test.step('Validate Aside block content', async () => { + await expect(Aside.asideNotifLargeCenter).toBeVisible(); + await expect(Aside.icon).not.toBeVisible(); + await expect(Aside.image).not.toBeVisible(); + await expect(Aside.actionArea).toBeVisible(); + await expect(Aside.detailLabel).not.toBeVisible(); + await expect(Aside.h3TitleLarge).toBeVisible(); + await expect(Aside.textFieldMedium).toBeVisible(); + // Check Aside block buttons: + await expect(Aside.linkTextCta).toBeVisible(); + await expect(Aside.blueButtonCta).not.toBeVisible(); + await expect(Aside.blackButtonCta).toBeVisible(); + expect(await Aside.actionButtons.count()).toEqual(1); + // Check Aside block background: + const bgdColor = await Aside.asideNotifLargeCenter.evaluate((e) => window.getComputedStyle(e).getPropertyValue('background-color')); + expect(bgdColor).toBe(Aside.props.background.darkGrey); + }); + }); +}); diff --git a/nala/blocks/card/card.page.js b/nala/blocks/card/card.page.js new file mode 100644 index 0000000000..3e634e38ca --- /dev/null +++ b/nala/blocks/card/card.page.js @@ -0,0 +1,57 @@ +export default class Card { + constructor(page, nth = 0) { + this.page = page; + // card locators + this.card = this.page.locator('.card').nth(nth); + + // One half card locators + this.oneHalfCard = this.page.locator('.consonant-OneHalfCard').nth(nth); + this.oneHalfCardImage = this.oneHalfCard.locator('.consonant-OneHalfCard-img'); + this.oneHalfCardInner = this.oneHalfCard.locator('.consonant-OneHalfCard-inner'); + this.oneHalfCardTitleH3 = this.oneHalfCard.locator('h3.consonant-OneHalfCard-title'); + this.oneHalfCardText = this.oneHalfCard.locator('.consonant-OneHalfCard-text'); + // Double width card locators + this.doubleWidthCard = this.page.locator('.double-width-card').nth(nth); + this.doubleWidthCardImage = this.doubleWidthCard.locator('.consonant-DoubleWideCard-img'); + this.doubleWidthCardInner = this.doubleWidthCard.locator('.consonant-DoubleWideCard-inner'); + this.doubleWidthCardTitleH3 = this.doubleWidthCard.locator('h3.consonant-DoubleWideCard-title'); + this.doubleWidthCardText = this.doubleWidthCard.locator('.consonant-DoubleWideCard-text'); + // Product card locators + this.productCard = this.page.locator('.product-card').nth(nth); + this.productCardImage = this.productCard.locator('.consonant-ProductCard-img'); + this.productCardInner = this.productCard.locator('.consonant-ProductCard-inner'); + this.productCardTitleH3 = this.productCard.locator('h3.consonant-ProductCard-title'); + this.productCardText = this.productCard.locator('.consonant-ProductCard-text'); + // Half height card locators + this.halfHeightCard = this.page.locator('.half-height-card').nth(nth); + this.halfHeightCardImage = this.halfHeightCard.locator('.consonant-HalfHeightCard-img'); + this.halfHeightCardLink = this.halfHeightCard.locator('a.consonant-HalfHeightCard'); + this.halfHeightCardInner = this.halfHeightCard.locator('.consonant-HalfHeightCard-inner'); + this.halfHeightCardTitleH3 = this.halfHeightCard.locator('h3.consonant-HalfHeightCard-title'); + this.halfHeightCardText = this.halfHeightCard.locator('.consonant-HalfHeightCard-text'); + // Horizontal card locators + this.horizontalCard = this.page.locator('.card-horizontal').nth(nth); + this.horizontalCardImage = this.horizontalCard.locator('.card-image'); + this.horizontalCardImg = this.horizontalCard.locator('img'); + this.horizontalCardContent = this.horizontalCard.locator('card-content'); + this.horizontalCardBodyXS = this.horizontalCard.locator('.body-xs'); + this.horizontalCardHeadingXS = this.horizontalCard.locator('h2.heading-xs'); + this.horizontalCardHeadingXSLink = this.horizontalCardHeadingXS.locator('a'); + + // card footer sections + this.footer = this.card.locator('.consonant-CardFooter'); + this.footerOutlineButton = this.card.locator('a.con-button.outline').nth(0); + this.footerOutlineButton2 = this.card.locator('a.con-button.outline').nth(1); + this.footerBlueButton = this.card.locator('a.con-button.blue').nth(0); + this.footerBlueButton2 = this.card.locator('a.con-button.blue').nth(1); + + // card attributes + this.attributes = { + oneHalfCardImage: { style: 'background-image: url' }, + 'card-image': { + loading: 'eager', + fetchpriority: 'hight', + }, + }; + } +} diff --git a/nala/blocks/card/card.spec.js b/nala/blocks/card/card.spec.js new file mode 100644 index 0000000000..bafea2898f --- /dev/null +++ b/nala/blocks/card/card.spec.js @@ -0,0 +1,84 @@ +/* eslint-disable max-len */ + +module.exports = { + FeatureName: 'Consonant Card Block', + features: [ + { + tcid: '0', + name: '@Card', + path: '/drafts/nala/blocks/card/card', + data: { + titleH3: 'Lorem ipsum dolor sit amet', + text: 'Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis', + footerOutlineButtonText: 'Sign up', + footerBlueButtonText: 'Learn more', + }, + tags: '@card @smoke @regression @milo', + }, + { + tcid: '1', + name: '@Card (half-card, border)', + path: '/drafts/nala/blocks/card/half-card-border', + data: { + titleH3: 'Lorem ipsum dolor sit amet', + text: 'Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis', + footerOutlineButtonText: 'Sign up', + footerBlueButtonText: 'Learn more', + }, + tags: '@card @smoke @regression @milo', + }, + { + tcid: '2', + name: '@Card (double-width-card, border)', + path: '/drafts/nala/blocks/card/double-width-card-border', + data: { + titleH3: 'Lorem ipsum dolor sit amet', + text: 'Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis', + }, + tags: '@card @smoke @regression @milo', + }, + { + tcid: '3', + name: '@Card (product-card, border) ', + path: '/drafts/nala/blocks/card/product-card-border', + data: { + titleH3: 'Lorem ipsum dolor sit amet', + text: 'Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis', + footerOutlineButtonText: 'Learn more', + footerBlueButtonText: 'Sign up', + }, + tags: '@card @smoke @regression @milo', + }, + { + tcid: '4', + name: '@Card (half-height-card, border)', + path: '/drafts/nala/blocks/card/half-height-card-border', + data: { titleH3: 'Lorem ipsum dolor sit amet' }, + tags: '@card @smoke @regression @milo', + }, + { + tcid: '5', + name: '@Card-horizontal', + path: '/drafts/nala/blocks/card/card-horizontal', + data: { + bodyXS: 'Body XS Regular', + headingXS: 'Heading XS Bold Lorem ipsum dolo sit amet, consectetur adipis cing elit.', + imgWidth: '180', + imgHeight: '132', + }, + tags: '@card @smoke @regression @milo', + }, + { + tcid: '6', + name: '@Card-horizontal (tile)', + path: '/drafts/nala/blocks/card/card-horizontal-tile', + data: { + bodyXS: 'Body XS Regular', + headingXS: 'Heading XS Bold Lorem ipsum dolo sit amet, consectetur adipis cing elit.', + imgWidth: '80', + imgHeight: '78', + }, + tags: '@card @smoke @regression @milo', + }, + ], +}; diff --git a/nala/blocks/card/card.test.js b/nala/blocks/card/card.test.js new file mode 100644 index 0000000000..bac6dbddd6 --- /dev/null +++ b/nala/blocks/card/card.test.js @@ -0,0 +1,192 @@ +import { expect, test } from '@playwright/test'; +import { features } from './card.spec.js'; +import ConsonantCard from './card.page.js'; + +let card; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo Consonant card feature test suite', () => { + test.beforeEach(async ({ page }) => { + card = new ConsonantCard(page); + }); + + // Test 0 : Card + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + const { data } = features[0]; + + await test.step('step-1: Go to Consonant Card feature test page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Card content/specs', async () => { + await expect(await card.oneHalfCard).toBeVisible(); + await expect(await card.oneHalfCardImage).toBeVisible(); + + await expect(await card.oneHalfCardTitleH3).toContainText(data.titleH3); + await expect(await card.oneHalfCardText).toContainText(data.text); + + await expect(await card.footer).toBeVisible(); + await expect(await card.footerOutlineButton).toBeVisible(); + await expect(await card.footerOutlineButton).toContainText(data.footerOutlineButtonText); + + await expect(await card.footerBlueButton).toBeVisible(); + await expect(await card.footerBlueButton).toContainText(data.footerBlueButtonText); + }); + }); + + // Test 1 : Card (half-card, border) + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + const { data } = features[1]; + + await test.step('step-1: Go to Consonant Card feature test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Half Card with Boarder content/specs', async () => { + await expect(await card.oneHalfCard).toBeVisible(); + + expect(await card.oneHalfCard.getAttribute('class')).toContain('border'); + + await expect(await card.oneHalfCardImage).toBeVisible(); + await expect(await card.oneHalfCardTitleH3).toContainText(data.titleH3); + await expect(await card.oneHalfCardText).toContainText(data.text); + + await expect(await card.footer).toBeVisible(); + await expect(await card.footerOutlineButton).toBeVisible(); + await expect(await card.footerOutlineButton).toContainText(data.footerOutlineButtonText); + + await expect(await card.footerBlueButton).toBeVisible(); + await expect(await card.footerBlueButton).toContainText(data.footerBlueButtonText); + }); + }); + + // Test 2 : card (double-width-card, border) + test(`${features[2].name},${features[2].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + const { data } = features[2]; + + await test.step('step-2: Go to Consonant Card feature test page', async () => { + await page.goto(`${baseURL}${features[2].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[2].path}${miloLibs}`); + }); + + await test.step('step-2: Verify card (double-width-card, border) content/specs', async () => { + await expect(await card.doubleWidthCard).toBeVisible(); + + expect(await card.doubleWidthCard.getAttribute('class')).toContain('border'); + + await expect(await card.doubleWidthCardImage).toBeVisible(); + await expect(await card.doubleWidthCardTitleH3).toContainText(data.titleH3); + await expect(await card.doubleWidthCardText).toContainText(data.text); + }); + }); + + // Test 3 : Card (product-card, border) + test(`${features[3].name},${features[3].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[3].path}${miloLibs}`); + const { data } = features[3]; + + await test.step('step-2: Go to Consonant Card feature test page', async () => { + await page.goto(`${baseURL}${features[3].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[3].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Card (product-card, border) content/specs', async () => { + await expect(await card.productCard).toBeVisible(); + + expect(await card.productCard.getAttribute('class')).toContain('border'); + + await expect(await card.productCardTitleH3).toContainText(data.titleH3); + await expect(await card.productCardText).toContainText(data.text); + + await expect(await card.footer).toBeVisible(); + await expect(await card.footerOutlineButton).toBeVisible(); + await expect(await card.footerOutlineButton).toContainText(data.footerOutlineButtonText); + + await expect(await card.footerBlueButton).toBeVisible(); + await expect(await card.footerBlueButton).toContainText(data.footerBlueButtonText); + }); + }); + + // Test 4 : Card (half-height-card, border) + test(`${features[4].name},${features[4].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[4].path}${miloLibs}`); + const { data } = features[4]; + + await test.step('step-2: Go to Consonant Card feature test page', async () => { + await page.goto(`${baseURL}${features[4].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[4].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Card (half-height-card, border) content/specs', async () => { + await expect(await card.halfHeightCard).toBeVisible(); + + expect(await card.halfHeightCard.getAttribute('class')).toContain('border'); + + await expect(await card.halfHeightCardImage).toBeVisible(); + await expect(await card.halfHeightCardLink).toBeVisible(); + + await expect(await card.halfHeightCardTitleH3).toContainText(data.titleH3); + }); + }); + + // Test 5 : Card-horizontal + test(`${features[5].name},${features[5].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[5].path}${miloLibs}`); + const { data } = features[5]; + + await test.step('step-2: Go to Consonant Card feature test page', async () => { + await page.goto(`${baseURL}${features[5].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[5].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Card-horizontal content/specs', async () => { + await expect(await card.horizontalCard).toBeVisible(); + await expect(await card.horizontalCardImage).toBeVisible(); + + expect(await card.horizontalCardImg.getAttribute('width')).toContain(data.imgWidth); + expect(await card.horizontalCardImg.getAttribute('height')).toContain(data.imgHeight); + + await expect(await card.horizontalCardBodyXS).toContainText(data.bodyXS); + await expect(await card.horizontalCardHeadingXS).toContainText(data.headingXS); + await expect(await card.horizontalCardHeadingXSLink).toContainText(data.headingXS); + }); + }); + + // Test 6 : Card-horizontal (tile) + test(`${features[6].name},${features[6].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[6].path}${miloLibs}`); + const { data } = features[6]; + + await test.step('step-2: Go to Consonant Card feature test page', async () => { + await page.goto(`${baseURL}${features[6].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[6].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Card-horizontal (tile) content/specs', async () => { + await expect(await card.horizontalCard).toBeVisible(); + expect(await card.horizontalCard.getAttribute('class')).toContain('tile'); + + await expect(await card.horizontalCardImage).toBeVisible(); + + expect(await card.horizontalCardImg.getAttribute('width')).toContain(data.imgWidth); + expect(await card.horizontalCardImg.getAttribute('height')).toContain(data.imgHeight); + + await expect(await card.horizontalCardBodyXS).toContainText(data.bodyXS); + await expect(await card.horizontalCardHeadingXS).toContainText(data.headingXS); + await expect(await card.horizontalCardHeadingXSLink).toContainText(data.headingXS); + }); + }); +}); diff --git a/nala/blocks/carousel/carousel.page.js b/nala/blocks/carousel/carousel.page.js new file mode 100644 index 0000000000..e121a54d69 --- /dev/null +++ b/nala/blocks/carousel/carousel.page.js @@ -0,0 +1,222 @@ +export default class Carousel { + constructor(page) { + this.page = page; + // carousel types selectors + this.carouselContainer = page.locator('.carousel.container'); + this.carouselLightbox = page.locator('.carousel.lightbox'); + this.carouselContainerShow2 = page.locator('.carousel.show-2.container'); + this.carousel = page.locator('.carousel'); + + // carousel selectors + this.slides = this.carousel.locator('.carousel-slides'); + this.activeSlide = this.slides.locator('.section.carousel-slide.active'); + this.slidesCount = this.slides.locator('.section.carousel-slide'); + this.indicator = this.carousel.locator('.carousel-indicators'); + this.indicatorCount = this.indicator.locator('.carousel-indicator'); + this.activeIndicator = this.indicator.locator('.carousel-indicator.active'); + this.nextButton = this.carousel.locator('.carousel-next'); + this.previousButton = this.carousel.locator('.carousel-previous'); + this.lightboxExpandButton = this.carouselLightbox.locator('.lightbox-button.carousel-expand'); + this.lightboxCloseButton = this.carouselLightbox.locator('.lightbox-button.carousel-close'); + } + + /** + * Get the index of the current slide. + * @return {Promise}. + */ + async getCurrentSlideIndex() { + const currentIndex = await this.activeSlide.getAttribute('data-index'); + return currentIndex; + } + + /** + * Get the count of slides of carousel. + * @return {Promise}. + */ + async getNumberOfSlides() { + const numberOfSlides = await this.slidesCount.count(); + return numberOfSlides; + } + + /** + * Move to next slide . + */ + async moveToNextSlide() { + await this.nextButton.click(); + } + + /** + * Move to previous slide . + */ + async moveToPreviousSlide() { + await this.previousButton.click(); + } + + /** + * Move to nth slide . + */ + async moveToSlide(index) { + await this.indicator.nth(index).click(); + } + + /** + * Are carousel indictors are displayed. + * @return {Promise}. + */ + async areIndicatorsDisplayed() { + const isDisplayed = await this.indicator.isVisible(); + return isDisplayed; + } + + /** + * Get the active indictor index. + * @return {Promise}. + */ + async getCurrentIndicatorIndex() { + const currentIndex = await this.activeIndicator.getAttribute('tabindex'); + return currentIndex; + } + + /** + * Get the count of indicators of carousel. + * @return {Promise}. + */ + async getNumberOfIndicators() { + const numberOfIndicators = await this.indicatorCount.count(); + return numberOfIndicators; + } + + /** + * Move to nth slide by clicking nth indicator + */ + async moveToIndicator(index) { + await this.indicatorCount.nth(index).click(); + } + + /** + * Check carousel button is visible + * @return {Promise}. + */ + async isNextButtonlVisible() { + const isDisplayed = await this.nextButton.isVisible(); + return isDisplayed; + } + + /** + * Check carousel button is visible + * @return {Promise}. + */ + async isPreviousButtonlVisible() { + const isDisplayed = await this.previousButton.isVisible(); + return isDisplayed; + } + + /** + * Check carousel button is visible + * @return {Promise}. + */ + async isLightboxExpandButtonVisible() { + const isDisplayed = await this.lightboxExpandButton.isVisible(); + return isDisplayed; + } + + /** + * Check carousel button is visible + * @return {Promise}. + */ + async isLightboxCloseButtonVisible() { + const isDisplayed = await this.lightboxCloseButton.isVisible(); + return isDisplayed; + } + + /** + * Click carousel button + */ + async expandLightboxModal() { + await this.lightboxExpandButton.click(); + } + + /** + * Click carousel button + */ + async closeLightboxModal() { + await this.lightboxCloseButton.click(); + } + + /** + * Gets the text content of a specified carousel slide. + * @param {number} index - The index of the carousel slide to get the text from. + * @param {string} tagOrClass - The tag name or class name of the element containing the text. + * @returns {Promise} The text content of the specified carousel slide. + */ + async getSlideText(index, tagOrClass) { + let slideSelector = `.carousel-slide:nth-child(${index}) `; + if (tagOrClass.startsWith('.')) { + slideSelector += tagOrClass; + } else { + slideSelector += `${tagOrClass}`; + } + await this.page.waitForSelector(slideSelector); + const slide = await this.page.$(slideSelector); + const text = await slide.textContent(); + return text; + } + + /** + * Gets the text content of a specified carousel slide. + * @param {number} index - The index of the carousel slide to get the text from. + * @param {string} tagOrClass - The tag name or class name of the element containing the text. + * @param {string} expectedText - The text to be validated. + * @returns {Promise} . + */ + async validateSlideText(index, tagOrClass, expectedText) { + let slideSelector = `.carousel-slide:nth-child(${index}) `; + if (tagOrClass.startsWith('.')) { + slideSelector += tagOrClass; + } else { + slideSelector += `${tagOrClass}`; + } + await this.page.waitForSelector(slideSelector); + const slide = await this.page.$(slideSelector); + const slideText = await slide.textContent(); + if (slideText === expectedText) { + return true; + } + return false; + } + + /** + * Check if the specified carousel type is displayed on the page. + * @param {string} type - The type of carousel to check. + * @return {Promise} Returns a Promise that resolves to true or false. + * @throws {Error} Throws an error if an invalid carousel type is provided. + */ + async isCarouselDisplayed(type, timeout = 3000) { + let isDisplayed; + switch (type) { + case 'carouselLightbox': + await this.carouselLightbox.waitFor({ state: 'visible', timeout }); + isDisplayed = await this.carouselLightbox.isVisible(); + break; + case 'carouselFullpage': + await this.carouselFullpage.waitFor({ state: 'visible', timeout }); + isDisplayed = await this.carouselFullpage.isVisible(); + break; + case 'carouselContainer': + await this.carouselContainer.waitFor({ state: 'visible', timeout }); + isDisplayed = await this.carouselContainer.isVisible(); + break; + case 'carouselShow-2': + await this.carouselContainerShow2.waitFor({ state: 'visible', timeout }); + isDisplayed = await this.carouselContainerShow2.isVisible(); + break; + case 'carousel': + await this.carouselDefault.waitFor({ state: 'visible', timeout }); + isDisplayed = await this.carouselDefault.isVisible(); + break; + default: + throw new Error(`Invalid carousel type: ${type}`); + } + return isDisplayed; + } +} diff --git a/nala/blocks/carousel/carousel.spec.js b/nala/blocks/carousel/carousel.spec.js new file mode 100644 index 0000000000..ffc52f7de7 --- /dev/null +++ b/nala/blocks/carousel/carousel.spec.js @@ -0,0 +1,26 @@ +module.exports = { + BlockName: 'Carousel Block', + features: [ + { + tcid: '0', + name: '@Carousel(container)', + path: '/drafts/nala/blocks/carousel/lightbox', + tags: '@carousel @carousel-container @smoke @regression @milo', + envs: '@milo-live @milo-prod', + }, + { + tcid: '1', + name: '@Carousel(lightbox)', + path: '/drafts/nala/blocks/carousel/fullpage-carousel', + tags: '@carousel @carousel-container @smoke @regression @milo', + envs: '@milo-live milo-prod', + }, + { + tcid: '2', + name: '@Carousel Multi slide(show-2)', + path: '/drafts/nala/blocks/carousel/carousel-show-2', + tags: '@carousel @carousel-container @regression @milo', + envs: '@milo-live milo-prod', + }, + ], +}; diff --git a/nala/blocks/carousel/carousel.test.js b/nala/blocks/carousel/carousel.test.js new file mode 100644 index 0000000000..2a9136e4dd --- /dev/null +++ b/nala/blocks/carousel/carousel.test.js @@ -0,0 +1,115 @@ +import { expect, test } from '@playwright/test'; +import { features } from './carousel.spec.js'; +import CarouselBlock from './carousel.page.js'; + +let carousel; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo Carousel Block test suite', () => { + test.beforeEach(async ({ page }) => { + carousel = new CarouselBlock(page); + }); + + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + + await test.step('step-1: Go to Carousel block test page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('networkidle'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Carousel container', async () => { + // verify carousel elements + expect(await carousel.isCarouselDisplayed('carouselContainer')).toBeTruthy(); + + // verify carousel slides count and active slide index + expect(await carousel.getNumberOfSlides()).toBe(4); + expect(await carousel.getCurrentSlideIndex()).toBe('0'); + + // verify carousel indictor and active indicator + expect(await carousel.areIndicatorsDisplayed()).toBeTruthy(); + expect(await carousel.getNumberOfIndicators()).toBe(4); + expect(await carousel.getCurrentIndicatorIndex()).toBe('0'); + + // verify carousel next and previous buttons + expect(await carousel.isNextButtonlVisible()).toBeTruthy(); + expect(await carousel.isPreviousButtonlVisible()).toBeTruthy(); + }); + + await test.step('step-3: Perform carousel slides and controls operation and verify contents', async () => { + // move to next slide by clicking next button and verify h2 tag header + await carousel.moveToNextSlide(); + expect(await carousel.getCurrentSlideIndex()).toBe('1'); + expect(await carousel.getSlideText(1, 'h2', 'Orange Slices')).toBeTruthy(); + + // move to 3rd slide by clicking indicator and verify h2 tag header + await carousel.moveToIndicator(3); + expect(await carousel.getCurrentIndicatorIndex()).toBe('0'); + expect(await carousel.getSlideText(3, 'h2', 'Apples')).toBeTruthy(); + }); + }); + + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + + await test.step('step-1: Go to Carousel lightbox block test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('networkidle'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify carousel with lightbox features', async () => { + expect(await carousel.isCarouselDisplayed('carouselLightbox')).toBeTruthy(); + + // verify active slide and slides count + expect(await carousel.getNumberOfSlides()).toBe(4); + expect(await carousel.getCurrentSlideIndex()).toBe('0'); + + // verify indicator visibility, count and index of active slide + expect(await carousel.areIndicatorsDisplayed()).toBeTruthy(); + expect(await carousel.getNumberOfIndicators()).toBe(4); + expect(await carousel.getCurrentIndicatorIndex()).toBe('0'); + + expect(await carousel.isNextButtonlVisible()).toBeTruthy(); + expect(await carousel.isPreviousButtonlVisible()).toBeTruthy(); + + // verify expand and close lightbox + expect(await carousel.isLightboxExpandButtonVisible()).toBeTruthy(); + await carousel.expandLightboxModal(); + + expect(await carousel.isLightboxCloseButtonVisible()).toBeTruthy(); + await carousel.closeLightboxModal(); + }); + }); + + test(`${features[2].name},${features[2].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + + await test.step('step-1: Go to Carousel multi-slide show-2 block test page', async () => { + await page.goto(`${baseURL}${features[2].path}${miloLibs}`); + await page.waitForLoadState('networkidle'); + await expect(page).toHaveURL(`${baseURL}${features[2].path}${miloLibs}`); + }); + + await test.step('step-2: Verify multi slide carousel show-2 features', async () => { + expect(await carousel.isCarouselDisplayed('carouselShow-2')).toBeTruthy(); + + // In multi-slide 2 number of slides will be n-slides +1 so it will be 5 + expect(await carousel.getNumberOfSlides()).toBe(5); + expect(await carousel.getCurrentSlideIndex()).toBe('0'); + + // In multi-slide carousel indicators are not shown + expect(await carousel.areIndicatorsDisplayed()).toBeFalsy(); + expect(await carousel.isNextButtonlVisible()).toBeTruthy(); + expect(await carousel.isPreviousButtonlVisible()).toBeTruthy(); + }); + + await test.step('step-3: Perform carousel slides and controls operation and verify contents', async () => { + // move to next slide by clicking next button and verify h2 tag header + await carousel.moveToNextSlide(); + expect(await carousel.getSlideText(1, 'h2', 'Melon')).toBeTruthy(); + }); + }); +}); diff --git a/nala/blocks/chart/chart.page.js b/nala/blocks/chart/chart.page.js new file mode 100644 index 0000000000..91576c07ce --- /dev/null +++ b/nala/blocks/chart/chart.page.js @@ -0,0 +1,49 @@ +export default class Chart { + constructor(page, nth = 0) { + this.page = page; + // chart locators + this.chart = this.page.locator('.chart').nth(nth); + this.type = this.page.locator('.chart').nth(nth); + + this.title = this.chart.locator('.title'); + this.subTitle = this.chart.locator('.subtitle').nth(0); + this.container = this.chart.locator('.chart-container'); + this.svgImg = this.container.locator('svg'); + this.svgImgCircle = this.container.locator('circle'); + this.svgImgCircleNumber = this.container.locator('text.number'); + this.svgImgCircleSubTitle = this.container.locator('text.subtitle'); + + this.legendChrome = page.locator('text[dominant-baseline="central"]').filter({ hasText: 'Chrome' }); + this.legendFirefox = page.locator('text[dominant-baseline="central"]').filter({ hasText: 'Firefox' }); + this.legendEdge = page.locator('text[dominant-baseline="central"]').filter({ hasText: 'Edge' }); + this.legendSafari = page.locator('text[dominant-baseline="central"]').filter({ hasText: 'Safari' }); + this.legendOpera = page.locator('text[dominant-baseline="central"]').filter({ hasText: 'Opera' }); + this.legendChromeAndroid = page.locator('text[dominant-baseline="central"]').filter({ hasText: 'Chrome Android' }); + this.legendFirefoxAndroid = page.locator('text[dominant-baseline="central"]').filter({ hasText: 'Firefox Android' }); + this.legendSafariIos = page.locator('text[dominant-baseline="central"]').filter({ hasText: 'Safari iOS' }); + this.legendOperaAndroid = page.locator('text[dominant-baseline="central"]').filter({ hasText: 'Opera Android' }); + this.legendSamsungInternet = page.locator('text[dominant-baseline="central"]').filter({ hasText: 'Samsung Internet' }); + this.legendAdobeAcrobat = page.locator('text[dominant-baseline="central"][fill="#333"]').filter({ hasText: 'Adobe Acrobat' }); + this.legendAdobeExperienceManager = page.locator('text[dominant-baseline="central"][fill="#333"]').filter({ hasText: 'Adobe Experience Manager' }); + + this.x_axisMonday = page.locator('text[dominant-baseline="central"]').filter({ hasText: 'Monday' }); + this.x_axisTuesday = page.locator('text[dominant-baseline="central"]').filter({ hasText: 'Tuesday' }); + this.x_axisSunday = page.locator('text[dominant-baseline="central"]').filter({ hasText: 'Sunday' }); + this.y_axis50K = page.locator('text[dominant-baseline="central"]').filter({ hasText: '50K', exact: true }); + this.y_axis100K = page.locator('text[dominant-baseline="central"]').filter({ hasText: '100K', exact: true }); + this.y_axis250K = page.locator('text[dominant-baseline="central"]').filter({ hasText: '250K', exact: true }); + this.y_axis300K = page.locator('text[dominant-baseline="central"]').filter({ hasText: '300K', exact: true }); + + this.donutTitle = page.locator('text[dominant-baseline="central"]').filter({ hasText: 'Hello World', exact: true }); + + this.pieChartLabelAdobeSign = page.locator('text[dominant-baseline="central"][text-anchor="end"]').filter({ hasText: 'Adobe Sign' }); + this.pieChartLabelAdobePhotoshop = page.locator('text[dominant-baseline="central"][text-anchor="end"]').filter({ hasText: 'Adobe Photoshop' }); + this.pieChartLabelAdobePremier = page.locator('text[dominant-baseline="central"][text-anchor="end"]').filter({ hasText: 'Adobe Premier' }); + + // chart footer + this.footNote = this.chart.locator('.footnote'); + + // chart attributes + this.attributes = { svgViewBox: { viewBox: '0 0 430 430' } }; + } +} diff --git a/nala/blocks/chart/chart.spec.js b/nala/blocks/chart/chart.spec.js new file mode 100644 index 0000000000..b8ee9b1e52 --- /dev/null +++ b/nala/blocks/chart/chart.spec.js @@ -0,0 +1,91 @@ +module.exports = { + FeatureName: 'Chart Block', + features: [ + { + tcid: '0', + name: '@Chart (area, green, border)', + path: '/drafts/nala/blocks/chart/chart-area-green-border', + data: { + chartType: 'area green border', + titleH3: 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.', + subTitle: 'Revenue dollars: 2020 vs 2021 forecasted vs 2021 actuals.', + footNote: 'Footnote lorem ipsum dolor sit amet, consectetuer adipiscing elit.', + }, + tags: '@chart @smoke @regression @milo', + }, + { + tcid: '1', + name: '@Chart (bar, border)', + path: '/drafts/nala/blocks/chart/chart-bar-border', + data: { + chartType: 'bar border large', + titleH3: 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.', + subTitle: 'Revenue dollars: 2020 vs 2021 forecasted vs 2021 actuals.', + footNote: 'Footnote lorem ipsum dolor sit amet, consectetuer adipiscing elit.', + }, + tags: '@chart @smoke @regression @milo', + }, + { + tcid: '2', + name: '@Chart (column, border)', + path: '/drafts/nala/blocks/chart/chart-column-border', + data: { + chartType: 'column border large', + titleH3: 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.', + subTitle: 'Revenue dollars: 2020 vs 2021 forecasted vs 2021 actuals.', + footNote: 'Footnote lorem ipsum dolor sit amet, consectetuer adipiscing elit.', + }, + tags: '@chart @smoke @regression @milo', + }, + { + tcid: '3', + name: '@Chart (donut, border)', + path: '/drafts/nala/blocks/chart/chart-donut-border', + data: { + chartType: 'donut border large', + titleH3: 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.', + subTitle: 'Revenue dollars: 2020 vs 2021 forecasted vs 2021 actuals.', + footNote: 'Footnote lorem ipsum dolor sit amet, consectetuer adipiscing elit.', + }, + tags: '@chart @smoke @regression @milo', + }, + { + tcid: '4', + name: '@Chart (line, border)', + path: '/drafts/nala/blocks/chart/chart-line-border', + data: { + chartType: 'line border large', + titleH3: 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.', + subTitle: 'Revenue dollars: 2020 vs 2021 forecasted vs 2021 actuals.', + footNote: 'Footnote lorem ipsum dolor sit amet, consectetuer adipiscing elit.', + }, + tags: '@chart @smoke @regression @milo', + }, + { + tcid: '5', + name: '@Chart (oversized-number, border)', + path: '/drafts/nala/blocks/chart/chart-oversized-number-border', + data: { + chartType: 'oversized-number border large', + titleH3: 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.', + subTitle: 'Revenue dollars: 2020 vs 2021 forecasted vs 2021 actuals.', + circleNumber: '25', + circleSubTitle: 'Out of 60 days', + footNote: 'Footnote lorem ipsum dolor sit amet, consectetuer adipiscing elit.', + }, + tags: '@chart @smoke @regression @milo', + }, + { + tcid: '6', + name: '@Chart (pie, border)', + path: '/drafts/nala/blocks/chart/chart-pie-border', + data: { + chartType: 'pie border large', + titleH3: 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.', + subTitle: 'Revenue dollars: 2020 vs 2021 forecasted vs 2021 actuals.', + footNote: 'Footnote lorem ipsum dolor sit amet, consectetuer adipiscing elit.', + }, + tags: '@chart @smoke @regression @milo', + }, + ], +}; diff --git a/nala/blocks/chart/chart.test.js b/nala/blocks/chart/chart.test.js new file mode 100644 index 0000000000..e8e82942e0 --- /dev/null +++ b/nala/blocks/chart/chart.test.js @@ -0,0 +1,197 @@ +import { expect, test } from '@playwright/test'; +import { features } from './chart.spec.js'; +import ChartBlock from './chart.page.js'; + +let chart; +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo Chart feature test suite', () => { + test.beforeEach(async ({ page }) => { + chart = new ChartBlock(page); + }); + + // Test 0 : Chart (area, green, border) + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + const { data } = features[0]; + + await test.step('step-1: Go to Chart feature test page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('step-2: Verify chart content/specs', async () => { + await expect(await chart.chart).toBeVisible(); + await expect(await chart.title).toContainText(data.titleH3); + await expect(await chart.subTitle).toContainText(data.subTitle); + expect(await chart.type.getAttribute('class')).toContain(data.chartType); + await expect(await chart.footNote).toContainText(data.footNote); + }); + }); + + // Test 1 : Chart (bar, border) + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + const { data } = features[1]; + + await test.step('step-1: Go to Chart feature test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify chart content/specs', async () => { + await expect(await chart.chart).toBeVisible(); + await expect(await chart.title).toContainText(data.titleH3); + await expect(await chart.subTitle).toContainText(data.subTitle); + expect(await chart.type.getAttribute('class')).toContain(data.chartType); + await expect(await chart.legendChrome).toBeVisible(); + await expect(await chart.legendFirefox).toBeVisible(); + await expect(await chart.legendEdge).toBeVisible(); + + await expect(await chart.footNote).toContainText(data.footNote); + }); + }); + + // Test 2 : Chart (column, border) + test(`${features[2].name},${features[2].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + const { data } = features[2]; + + await test.step('step-1: Go to Chart feature test page', async () => { + await page.goto(`${baseURL}${features[2].path}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[2].path}${miloLibs}`); + }); + + await test.step('step-2: Verify chart content/specs', async () => { + await expect(await chart.chart).toBeVisible(); + await expect(await chart.title).toContainText(data.titleH3); + await expect(await chart.subTitle).toContainText(data.subTitle); + expect(await chart.type.getAttribute('class')).toContain(data.chartType); + + await expect(await chart.x_axisMonday).toBeVisible(); + await expect(await chart.x_axisTuesday).toBeVisible(); + await expect(await chart.x_axisSunday).toBeVisible(); + + await expect(await chart.y_axis100K).toBeVisible(); + await expect(await chart.y_axis250K).toBeVisible(); + await expect(await chart.y_axis300K).toBeVisible(); + + await expect(await chart.legendChrome).toBeVisible(); + await expect(await chart.legendFirefox).toBeVisible(); + await expect(await chart.legendSafari).toBeVisible(); + + await expect(await chart.footNote).toContainText(data.footNote); + }); + }); + + // Test 3 : Chart (donut, border) + test(`${features[3].name},${features[3].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[3].path}${miloLibs}`); + const { data } = features[3]; + + await test.step('step-1: Go to Chart feature test page', async () => { + await page.goto(`${baseURL}${features[3].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[3].path}${miloLibs}`); + }); + + await test.step('step-2: Verify chart content/specs', async () => { + await expect(await chart.chart).toBeVisible(); + await expect(await chart.title).toContainText(data.titleH3); + await expect(await chart.subTitle).toContainText(data.subTitle); + expect(await chart.type.getAttribute('class')).toContain(data.chartType); + + await expect(await chart.donutTitle).toBeVisible(); + await expect(await chart.x_axisMonday).toBeVisible(); + await expect(await chart.x_axisTuesday).toBeVisible(); + await expect(await chart.x_axisSunday).toBeVisible(); + + await expect(await chart.footNote).toContainText(data.footNote); + }); + }); + + // Test 4 : Chart (line, border) + test(`${features[4].name},${features[4].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[4].path}${miloLibs}`); + const { data } = features[4]; + + await test.step('step-1: Go to Chart feature test page', async () => { + await page.goto(`${baseURL}${features[4].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[4].path}${miloLibs}`); + }); + + await test.step('step-2: Verify chart content/specs', async () => { + await expect(await chart.chart).toBeVisible(); + await expect(await chart.title).toContainText(data.titleH3); + await expect(await chart.subTitle).toContainText(data.subTitle); + expect(await chart.type.getAttribute('class')).toContain(data.chartType); + + await expect(await chart.x_axisMonday).toBeVisible(); + await expect(await chart.x_axisTuesday).toBeVisible(); + await expect(await chart.x_axisSunday).toBeVisible(); + + await expect(await chart.legendChromeAndroid).toBeVisible(); + await expect(await chart.legendFirefoxAndroid).toBeVisible(); + await expect(await chart.legendSafariIos).toBeVisible(); + + await expect(await chart.footNote).toContainText(data.footNote); + }); + }); + + // Test 5 : Chart (oversized-number, border) + test(`${features[5].name},${features[5].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[5].path}${miloLibs}`); + const { data } = features[5]; + + await test.step('step-1: Go to Chart feature test page', async () => { + await page.goto(`${baseURL}${features[5].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[5].path}${miloLibs}`); + }); + + await test.step('step-2: Verify chart content/specs', async () => { + await expect(await chart.chart).toBeVisible(); + await expect(await chart.title).toContainText(data.titleH3); + await expect(await chart.subTitle).toContainText(data.subTitle); + + expect(await chart.type.getAttribute('class')).toContain(data.chartType); + expect(await chart.svgImg.getAttribute('viewBox')).toContain(chart.attributes.svgViewBox.viewBox); + + await expect(await chart.svgImgCircleNumber).toContainText(data.circleNumber); + await expect(await chart.svgImgCircleSubTitle).toContainText(data.circleSubTitle); + + await expect(await chart.footNote).toContainText(data.footNote); + }); + }); + + // Test 6 : Chart (pie, border) + test(`${features[6].name},${features[6].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[6].path}${miloLibs}`); + const { data } = features[6]; + + await test.step('step-1: Go to Chart feature test page', async () => { + await page.goto(`${baseURL}${features[6].path}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[6].path}${miloLibs}`); + }); + + await test.step('step-2: Verify chart content/specs', async () => { + await expect(await chart.chart).toBeVisible(); + await expect(await chart.title).toContainText(data.titleH3); + await expect(await chart.subTitle).toContainText(data.subTitle); + + await expect(await chart.pieChartLabelAdobeSign).toBeVisible(); + await expect(await chart.pieChartLabelAdobePhotoshop).toBeVisible(); + await expect(await chart.pieChartLabelAdobePremier).toBeVisible(); + + await expect(await chart.legendAdobeAcrobat).toBeVisible(); + await expect(await chart.legendAdobeExperienceManager).toBeVisible(); + + await expect(await chart.footNote).toContainText(data.footNote); + }); + }); +}); diff --git a/nala/blocks/columns/columns.page.js b/nala/blocks/columns/columns.page.js new file mode 100644 index 0000000000..873a75982c --- /dev/null +++ b/nala/blocks/columns/columns.page.js @@ -0,0 +1,52 @@ +export default class Columns { + constructor(page, nth = 0) { + this.page = page; + // columns locators + this.column = this.page.locator('.columns').nth(nth); + this.rows = this.column.locator('.row'); + this.columns = this.column.locator('.col'); + + // columns blocks css + this.cssProperties = { + '.columns > .row': { + display: 'grid', + gap: /^32px.*/, + 'margin-bottom': '16px', + 'grid-template-columns': /^(\d+(?:\.\d+)?px\s?)+$/, + }, + + '.columns.contained': { + 'max-width': /^\d{2}/, + margin: /^0px.*/, + }, + + '.columns.contained.middle': { 'align-items': 'center' }, + + '.columns.table': { 'font-size': '14px' }, + + '.columns.table > .row:first-child': { + 'text-transform': 'uppercase', + 'font-size': '11px', + 'font-weight': '700', + 'letter-spacing': '1px', + }, + + '.columns.table > .row': { + 'margin-bottom': '0px', + padding: /^16px.*/, + 'grid-template-columns': /^(\d+(?:\.\d+)?px\s?)+$/, + 'border-bottom': /^1px.*/, + 'align-items': 'center', + }, + }; + + // columns blocks attributes + this.attProperties = { + columns: { class: 'columns' }, + 'columns-contained': { class: 'columns contained' }, + 'columns-contained-middle': { class: 'columns contained middle' }, + 'columns-table': { class: 'columns columns-table' }, + 'columns-contained-table': { class: 'columns contained columns-table' }, + }; + } +} diff --git a/nala/blocks/columns/columns.spec.js b/nala/blocks/columns/columns.spec.js new file mode 100644 index 0000000000..14a089b449 --- /dev/null +++ b/nala/blocks/columns/columns.spec.js @@ -0,0 +1,70 @@ +module.exports = { + FeatureName: 'Columns Block', + features: [ + { + tcid: '0', + name: '@Columns', + path: '/drafts/nala/blocks/columns/columns', + data: { + rows: 1, + columns: 3, + col0: 'Other glossary terms', + col1: 'Related Adobe products', + col2: 'Adobe Target', + }, + tags: '@columns @smoke @regression @milo', + }, + { + tcid: '1', + name: '@Columns (contained)', + path: '/drafts/nala/blocks/columns/columns-contained', + data: { + rows: 1, + columns: 2, + col0: 'Market Segmentation', + col1: 'Adobe Analytics', + }, + tags: '@columns @columns-contained @smoke @regression @milo', + }, + { + tcid: '2', + name: '@Columns (contained,middle)', + path: '/drafts/nala/blocks/columns/columns-contained-middle', + data: { + rows: 1, + columns: 2, + col0: 'Descriptive Analytics', + col1: 'Adobe Target', + }, + tags: '@columns @columns-contained-middle @smoke @regression @milo', + }, + { + tcid: '3', + name: '@Columns (table)', + path: '/drafts/nala/blocks/columns/columns-table', + data: { + rows: 4, + columns: 8, + col0: 'PROS', + col1: 'CONS', + col2: 'Detail: Waterfall’s meticulous upfront planning results in detailed project plans.', + col3: 'Rigid: With a strict blueprint, departure from the original plan is difficult.', + }, + tags: '@columns @columns-table @smoke @regression @milo', + }, + { + tcid: '4', + name: '@Columns (contained,table)', + path: '/drafts/nala/blocks/columns/columns-contained-table', + data: { + rows: 10, + columns: 20, + col0: 'Role', + col1: 'Name', + col2: 'Engineering Manager', + col3: 'Chris Millar', + }, + tags: '@columns @columns-contained-table @smoke @regression @milo', + }, + ], +}; diff --git a/nala/blocks/columns/columns.test.js b/nala/blocks/columns/columns.test.js new file mode 100644 index 0000000000..ccab01b1bb --- /dev/null +++ b/nala/blocks/columns/columns.test.js @@ -0,0 +1,160 @@ +import { expect, test } from '@playwright/test'; +import WebUtil from '../../libs/webutil.js'; +import { features } from './columns.spec.js'; +import ColumnsBlock from './columns.page.js'; + +let column; +let webUtil; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo Columns Block test suite', () => { + test.beforeEach(async ({ page }) => { + column = new ColumnsBlock(page); + webUtil = new WebUtil(page); + }); + + // Test 0 : Column default block + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + const { data } = features[0]; + + await test.step('step-1: Go to Columns block test page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Columns block content/specs', async () => { + // verify colums visibility, rows count, columns count and text content + await expect(await column.column).toBeVisible(); + + await expect(await column.rows).toHaveCount(data.rows); + await expect(await column.columns).toHaveCount(data.columns); + + await expect(await column.columns.nth(0)).toContainText(data.col0); + await expect(await column.columns.nth(1)).toContainText(data.col1); + await expect(await column.columns.nth(2)).toContainText(data.col2); + + // verify the css and attributes + expect(await webUtil.verifyCSS(await column.rows.nth(0), column.cssProperties['.columns > .row'])).toBeTruthy(); + expect(await webUtil.verifyAttributes(await column.column, column.attProperties.columns)).toBeTruthy(); + }); + }); + + // Test 1 : Columns (contained) block + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + const { data } = features[1]; + + await test.step('step-1: Go to Columns block test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Columns(contained) block content/specs', async () => { + // verify colums visibility, rows count, columns count and text content + await expect(await column.column).toBeVisible(); + + await expect(await column.rows).toHaveCount(data.rows); + await expect(await column.columns).toHaveCount(data.columns); + + await expect(await column.columns.nth(0)).toContainText(data.col0); + await expect(await column.columns.nth(1)).toContainText(data.col1); + + // verify the css and attributes + expect(await webUtil.verifyCSS(await column.column, column.cssProperties['.columns.contained'])).toBeTruthy(); + expect(await webUtil.verifyAttributes(await column.column, column.attProperties['columns-contained'])).toBeTruthy(); + }); + }); + + // Test 2 : Columns (contained,middle) block + test(`${features[2].name},${features[2].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + const { data } = features[2]; + + await test.step('step-1: Go to Columns block test page', async () => { + await page.goto(`${baseURL}${features[2].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[2].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Columns(contained,middle) block content/specs', async () => { + // verify colums visibility, rows count, columns count and text content + await expect(await column.column).toBeVisible(); + + await expect(await column.rows).toHaveCount(data.rows); + await expect(await column.columns).toHaveCount(data.columns); + + await expect(await column.columns.nth(0)).toContainText(data.col0); + await expect(await column.columns.nth(1)).toContainText(data.col1); + + // verify the css and attributes + expect(await webUtil.verifyCSS(await column.column, column.cssProperties['.columns.contained'])).toBeTruthy(); + expect(await webUtil.verifyAttributes(await column.column, column.attProperties['columns-contained-middle'])).toBeTruthy(); + }); + }); + + // Test 3 : Columns (table) block + test(`${features[3].name},${features[3].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[3].path}${miloLibs}`); + const { data } = features[3]; + + await test.step('step-1: Go to Columns block test page', async () => { + await page.goto(`${baseURL}${features[3].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[3].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Columns(table) block content/specs', async () => { + // verify colums visibility, rows count, columns count and text content + await expect(await column.column).toBeVisible(); + + await expect(await column.rows).toHaveCount(data.rows); + await expect(await column.columns).toHaveCount(data.columns); + + await expect(await column.columns.nth(0)).toContainText(data.col0); + await expect(await column.columns.nth(1)).toContainText(data.col1); + await expect(await column.columns.nth(2)).toContainText(data.col2); + await expect(await column.columns.nth(3)).toContainText(data.col3); + + // verify the css and attributes + expect(await webUtil.verifyCSS(await column.rows.nth(0), column.cssProperties['.columns.table > .row:first-child'])).toBeTruthy(); + expect(await webUtil.verifyCSS(await column.rows.nth(1), column.cssProperties['.columns.table > .row'])).toBeTruthy(); + + expect(await webUtil.verifyAttributes(await column.column, column.attProperties['columns-table'])).toBeTruthy(); + }); + }); + + // Test 4 : Columns (contained,table) block + test(`${features[4].name},${features[4].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[4].path}${miloLibs}`); + const { data } = features[4]; + + await test.step('step-1: Go to Columns block test page', async () => { + await page.goto(`${baseURL}${features[4].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[4].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Columns(contained,table) block content/specs', async () => { + // verify colums visibility, rows count, columns count and text content + await expect(await column.column).toBeVisible(); + + await expect(await column.rows).toHaveCount(data.rows); + await expect(await column.columns).toHaveCount(data.columns); + + await expect(await column.columns.nth(0)).toContainText(data.col0); + await expect(await column.columns.nth(1)).toContainText(data.col1); + await expect(await column.columns.nth(2)).toContainText(data.col2); + await expect(await column.columns.nth(3)).toContainText(data.col3); + + // verify the css and attributes + expect(await webUtil.verifyCSS(await column.rows.nth(0), column.cssProperties['.columns.table > .row:first-child'])).toBeTruthy(); + expect(await webUtil.verifyCSS(await column.rows.nth(1), column.cssProperties['.columns.table > .row'])).toBeTruthy(); + + expect(await webUtil.verifyAttributes(await column.column, column.attProperties['columns-contained-table'])).toBeTruthy(); + }); + }); +}); diff --git a/nala/blocks/figure/figure.page.js b/nala/blocks/figure/figure.page.js new file mode 100644 index 0000000000..785efa832d --- /dev/null +++ b/nala/blocks/figure/figure.page.js @@ -0,0 +1,9 @@ +export default class Figure { + constructor(page) { + this.page = page; + // figure block locators + this.figure = this.page.locator('figure.figure'); + this.image = this.figure.locator('picture img'); + this.figCaption = this.figure.locator('figcaption .caption'); + } +} diff --git a/nala/blocks/figure/figure.spec.js b/nala/blocks/figure/figure.spec.js new file mode 100644 index 0000000000..84e8b37193 --- /dev/null +++ b/nala/blocks/figure/figure.spec.js @@ -0,0 +1,23 @@ +module.exports = { + FeatureName: 'Figure Block', + features: [ + { + tcid: '0', + name: '@Image with caption', + path: '/drafts/nala/blocks/figure/image-with-caption', + data: { figCaption: '100 Orange Captions' }, + tags: '@figure @smoke @regression @milo', + }, + { + tcid: '01', + name: '@Multiple images with caption', + path: '/drafts/nala/blocks/figure/multiple-images-with-captions', + data: { + figBlockCount: 2, + figCaption_1: 'Adobe Logo', + figCaption_2: 'Adobe Products', + }, + tags: '@figure @figure-with-mulitple-images @smoke @regression @milo', + }, + ], +}; diff --git a/nala/blocks/figure/figure.test.js b/nala/blocks/figure/figure.test.js new file mode 100644 index 0000000000..0486b96207 --- /dev/null +++ b/nala/blocks/figure/figure.test.js @@ -0,0 +1,51 @@ +import { expect, test } from '@playwright/test'; +import { features } from './figure.spec.js'; +import FigureBlock from './figure.page.js'; + +let figureBlock; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo Figure Block test suite', () => { + test.beforeEach(async ({ page }) => { + figureBlock = new FigureBlock(page); + }); + + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + + await test.step('step-1: Go to figure Block test page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('step-2: Verify figure block ', async () => { + const { data } = features[0]; + await expect(await figureBlock.figure).toBeVisible(); + await expect(await figureBlock.image).toBeVisible(); + await expect(await figureBlock.figCaption).toContainText(data.figCaption); + }); + }); + + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + + await test.step('step-1: Go to figure block test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify figure block multiple images with caption ', async () => { + const { data } = features[1]; + await expect(await figureBlock.figure).toHaveCount(data.figBlockCount); + + await expect(await figureBlock.image.nth(0)).toBeVisible(); + await expect(await figureBlock.figCaption.nth(0)).toContainText(data.figCaption_1); + + await expect(await figureBlock.image.nth(1)).toBeVisible(); + await expect(await figureBlock.figCaption.nth(1)).toContainText(data.figCaption_2); + }); + }); +}); diff --git a/nala/blocks/howto/howto.page.js b/nala/blocks/howto/howto.page.js new file mode 100644 index 0000000000..af06eafeee --- /dev/null +++ b/nala/blocks/howto/howto.page.js @@ -0,0 +1,52 @@ +export default class HowTo { + constructor(page, nth = 0) { + this.page = page; + // how-to locators + this.howTo = page.locator('.how-to').nth(nth); + this.foreground = this.howTo.locator('.foreground'); + this.howToLarge = this.page.locator('.how-to.large-image').nth(nth); + this.howToSeo = this.page.locator('.how-to.seo').nth(nth); + this.heading = this.howTo.locator('.how-to-heading'); + this.image = this.howTo.locator('.how-to-media'); + this.list = this.howTo.locator('li'); + this.largeImage = page.locator('.how-to-media img'); + + // howto contents css + this.cssProperties = { + '.how-to .foreground': { + padding: '80px 0px', + 'max-width': /%$/, + display: 'grid', + }, + 'how-to-media': { + 'align-self': 'center', + 'justify-self': 'center', + 'min-height': '100%', + }, + 'body-m': { + 'font-size': '18px', + 'line-height': '27px', + }, + 'how-to-large': { + padding: '80px 24px', + 'max-width': '700px', + }, + 'how-to-large-image': { + display: 'block', + 'grid-template-areas': 'none', + }, + 'how-to-seo': { + display: 'block', + 'grid-template-areas': 'none', + }, + }; + + // howto contents attributes + this.attProperties = { + 'how-to-large-image': { + width: '600', + height: '300', + }, + }; + } +} diff --git a/nala/blocks/howto/howto.spec.js b/nala/blocks/howto/howto.spec.js new file mode 100644 index 0000000000..e62227e90c --- /dev/null +++ b/nala/blocks/howto/howto.spec.js @@ -0,0 +1,23 @@ +module.exports = { + BlockName: 'HowTo Block', + features: [ + { + tcid: '1', + name: '@HowTo', + path: '/drafts/nala/blocks/howto/how-to', + tags: '@howto @smoke @regression @milo', + }, + { + tcid: '2', + name: '@HowTo large Image', + path: '/drafts/nala/blocks/howto/how-to-large', + tags: '@howto @howto-large-image @smoke @regression @milo', + }, + { + tcid: '3', + name: '@HowTo SEO', + path: '/drafts/nala/blocks/howto/how-to-seo', + tags: '@howto @howto-seo @smoke @regression @milo', + }, + ], +}; diff --git a/nala/blocks/howto/howto.test.js b/nala/blocks/howto/howto.test.js new file mode 100644 index 0000000000..ed25ac2227 --- /dev/null +++ b/nala/blocks/howto/howto.test.js @@ -0,0 +1,76 @@ +import { expect, test } from '@playwright/test'; +import WebUtil from '../../libs/webutil.js'; +import { features } from './howto.spec.js'; +import HowToBlock from './howto.page.js'; + +let webUtil; +let howTo; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo HowTo block test suite', () => { + test.beforeEach(async ({ page }) => { + webUtil = new WebUtil(page); + howTo = new HowToBlock(page); + }); + + // Test 0 : HowTo default block + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + + await test.step('step-1: Go to HowTo block test page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('step-2: Verify HowTo specs', async () => { + await expect(howTo.howTo).toBeVisible(); + await expect(await howTo.list).toHaveCount(4); + + expect(await webUtil.verifyCSS(howTo.foreground, howTo.cssProperties['.how-to .foreground'])).toBeTruthy(); + expect(await webUtil.verifyCSS(howTo.heading, howTo.cssProperties['body-m'])).toBeTruthy(); + expect(await webUtil.verifyCSS(howTo.image, howTo.cssProperties['how-to-media'])).toBeTruthy(); + }); + }); + + // Test 1 : how-to (large) block + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + + await test.step('step-1: Go to HowTo large block test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify HowTo large specs', async () => { + await expect(howTo.howToLarge).toBeVisible(); + await expect(await howTo.list).toHaveCount(4); + + expect(await webUtil.verifyCSS(howTo.heading, howTo.cssProperties['body-m'])).toBeTruthy(); + expect(await webUtil.verifyCSS(howTo.howToLarge, howTo.cssProperties['how-to-large-image'])).toBeTruthy(); + // eslint-disable-next-line max-len + expect(await webUtil.verifyAttributes(await howTo.largeImage, howTo.attProperties['how-to-large-image'])).toBeTruthy(); + }); + }); + + // Test 2 : how-to (seo) block + test(`${features[2].name},${features[2].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + + await test.step('step-1: Go to HowTo SEO block test page', async () => { + await page.goto(`${baseURL}${features[2].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[2].path}${miloLibs}`); + }); + + await test.step('step-2: Verify HowTo SEO specs', async () => { + await expect(howTo.howToSeo).toBeVisible(); + await expect(await howTo.list).toHaveCount(4); + + expect(await webUtil.verifyCSS(howTo.heading, howTo.cssProperties['body-m'])).toBeTruthy(); + expect(await webUtil.verifyCSS(howTo.howToSeo, howTo.cssProperties['how-to-seo'])).toBeTruthy(); + }); + }); +}); diff --git a/nala/blocks/icon/icon.page.js b/nala/blocks/icon/icon.page.js new file mode 100644 index 0000000000..bcd170dca9 --- /dev/null +++ b/nala/blocks/icon/icon.page.js @@ -0,0 +1,102 @@ +/* eslint-disable import/no-extraneous-dependencies, max-len, no-console, no-await-in-loop, import/extensions */ +import { expect } from '@playwright/test'; +import WebUtil from '../../libs/webutil.js'; + +export default class Icon { + constructor(page) { + this.page = page; + this.webUtil = new WebUtil(page); + // Icon block locators + this.icon = this.page.locator('.icon-block').nth(0); + this.iconImage = this.icon.locator('img'); + this.iconHeadingL = this.icon.locator('.heading-l'); + this.iconHeadingXL = this.icon.locator('.heading-xl'); + this.iconBodyM = this.icon.locator('p.body-m').nth(0); + this.iconActionAreaBodyM = this.icon.locator('p.body-m.action-area').nth(0); + this.iconActionAreaLink = this.icon.locator('.action-area a').nth(0); + this.iconActionArea = this.icon.locator('.action-area'); + this.iconSupplemental = this.icon.locator('.supplemental-text'); + + // icon blocks css + this.cssProperties = { + '.icon-block': { + display: 'block', + width: /^\d+px$/, + position: 'relative', + }, + '.con-block.xl-spacing-static-bottom': { 'padding-bottom': '56px' }, + '.con-block.xl-spacing-static-top': { 'padding-top': '104px' }, + }; + + // icon blocks attributes + this.attProperties = { + icon: { class: 'icon-block' }, + 'icon-fullwidth-medium': { class: 'icon-block full-width medium con-block' }, + 'icon-fullwidth-medium-intro': { + class: + 'icon-block full-width medium intro con-block xxxl-spacing-top xl-spacing-static-bottom', + }, + 'icon-fullwidth-large': { class: 'icon-block full-width large con-block' }, + }; + } + + /** + * Verifies the visibility, css, attributes, styles, of elements or sections of + * the specified Icon block. + * + * @param {string} iconType - The type of the Icon block to verify. + * Possible values are 'icon-block (fullwidth, medium) ', 'icon-block (fullwidth, medium, intro)', and + * 'icon-block (fullwidth, large)'. + * @returns {Promise} - Returns true if the specified Quote type has the expected values. + */ + async verifyIcon(iconType, data) { + // verify icon block and image visibility + await expect(await this.icon).toBeVisible(); + await expect(await this.iconImage).toBeVisible(); + + switch (iconType) { + case 'icon block (fullwidth, medium)': + // verify icon block contents + await expect(await this.iconHeadingL).toContainText(data.headline); + await expect(await this.iconBodyM).toContainText(data.body); + await expect(await this.iconActionAreaBodyM).toContainText(data.buttonText); + await expect(await this.iconSupplemental).toContainText(data.supplementalText); + + // verify icon block attributes and css + expect(await this.webUtil.verifyAttributes(await this.icon, this.attProperties['icon-fullwidth-medium'])).toBeTruthy(); + + expect(await this.webUtil.verifyCSS(await this.icon, this.cssProperties['.icon-block'])).toBeTruthy(); + + return true; + case 'icon block (fullwidth, medium, intro)': + // verify icon block contents + await expect(await this.iconHeadingL).toContainText(data.headline); + await expect(await this.iconBodyM).toContainText(data.body); + await expect(await this.iconActionAreaBodyM).toContainText(data.buttonText); + await expect(await this.iconSupplemental).toContainText(data.supplementalText); + + // verify icon block attributes and css + expect(await this.webUtil.verifyAttributes(await this.icon, this.attProperties['icon-fullwidth-medium-intro'])).toBeTruthy(); + + expect(await this.webUtil.verifyCSS(await this.icon, this.cssProperties['.icon-block'])).toBeTruthy(); + expect(await this.webUtil.verifyCSS(await this.icon, this.cssProperties['.con-block.xl-spacing-static-bottom'])).toBeTruthy(); + expect(await this.webUtil.verifyCSS(await this.icon, this.cssProperties['.con-block.xl-spacing-static-top'])).toBeTruthy(); + + return true; + case 'icon block (fullwidth, large)': + // verify icon block contents + await expect(await this.iconHeadingXL).toContainText(data.headline); + await expect(await this.iconBodyM).toContainText(data.body); + await expect(await this.iconActionAreaLink).toContainText(data.linkText); + + // verify icon block attributes and css + expect(await this.webUtil.verifyAttributes(await this.icon, this.attProperties['icon-fullwidth-large'])).toBeTruthy(); + + expect(await this.webUtil.verifyCSS(await this.icon, this.cssProperties['.icon-block'])).toBeTruthy(); + + return true; + default: + throw new Error(`Unsupported Text type: ${this.iconType}`); + } + } +} diff --git a/nala/blocks/icon/icon.spec.js b/nala/blocks/icon/icon.spec.js new file mode 100644 index 0000000000..407aad1090 --- /dev/null +++ b/nala/blocks/icon/icon.spec.js @@ -0,0 +1,43 @@ +module.exports = { + FeatureName: 'Icon Block', + features: [ + { + tcid: '0', + name: '@Icon block (fullwidth, medium)', + path: '/drafts/nala/blocks/icon/icon-fullwidth-medium', + data: { + image: 'photoshop', + headline: 'Heading L Bold Icon Block', + body: 'Body M Lorem ipsum dolor sit', + buttonText: 'Learn more', + supplementalText: 'Body M BOLD Text link', + }, + tags: '@icon @icon-fullwidth-medium @smoke @regression @milo,', + }, + { + tcid: '1', + name: '@Icon block (fullwidth, medium, intro)', + path: '/drafts/nala/blocks/icon/fullwidth-medium-intro', + data: { + image: 'photoshop', + headline: 'Heading L Bold Icon Block', + body: 'Body M Lorem ipsum dolor sit', + buttonText: 'Learn more', + supplementalText: 'Body M BOLD Text link', + }, + tags: '@icon @icon-fullwidth-medium-intro @smoke @regression @milo,', + }, + { + tcid: '2', + name: '@Icon block (fullwidth,large)', + path: '/drafts/nala/blocks/icon/fullwidth-large', + data: { + image: 'photoshop', + headline: 'Heading XL Bold Icon Block', + body: 'Body M Lorem ipsum dolor sit', + linkText: 'Body M BOLD Text link', + }, + tags: '@icon @icon-fullwidth-large, @smoke @regression @milo,', + }, + ], +}; diff --git a/nala/blocks/icon/icon.test.js b/nala/blocks/icon/icon.test.js new file mode 100644 index 0000000000..c6f6e9fb91 --- /dev/null +++ b/nala/blocks/icon/icon.test.js @@ -0,0 +1,58 @@ +import { expect, test } from '@playwright/test'; +import { features } from './icon.spec.js'; +import IconBlock from './icon.page.js'; + +let icon; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo Icon Block test suite', () => { + test.beforeEach(async ({ page }) => { + icon = new IconBlock(page); + }); + + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + + await test.step('step-1: Go to Icon block test page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Icon block content/specs', async () => { + const { data } = features[0]; + expect(await icon.verifyIcon('icon block (fullwidth, medium)', data)).toBeTruthy(); + }); + }); + + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + + await test.step('step-1: Go to Icon block test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Icon block content/specs', async () => { + const { data } = features[1]; + expect(await icon.verifyIcon('icon block (fullwidth, medium, intro)', data)).toBeTruthy(); + }); + }); + + test(`${features[2].name},${features[2].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + + await test.step('step-1: Go to Icon block test page', async () => { + await page.goto(`${baseURL}${features[2].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[2].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Icon block content/specs', async () => { + const { data } = features[2]; + expect(await icon.verifyIcon('icon block (fullwidth, large)', data)).toBeTruthy(); + }); + }); +}); diff --git a/nala/blocks/iframe/iframe.page.js b/nala/blocks/iframe/iframe.page.js new file mode 100644 index 0000000000..33dbf3762c --- /dev/null +++ b/nala/blocks/iframe/iframe.page.js @@ -0,0 +1,14 @@ +/* eslint-disable import/no-import-module-exports */ + +export default class Iframe { + constructor(page) { + this.page = page; + + // IFRAME ELEMENTS: + this.miloIframeContainer = page.locator('.milo-iframe'); + this.iframeContainer = this.miloIframeContainer.locator('iframe'); + + // IFRAME PROPS: + this.props = {}; + } +} diff --git a/nala/blocks/iframe/iframe.spec.js b/nala/blocks/iframe/iframe.spec.js new file mode 100644 index 0000000000..d5def97fc5 --- /dev/null +++ b/nala/blocks/iframe/iframe.spec.js @@ -0,0 +1,11 @@ +module.exports = { + name: 'Iframe Block', + features: [ + { + name: '@Iframe', + path: '/drafts/nala/blocks/iframe/iframe', + browserParams: '?georouting=off', + tags: '@iframe @smoke @regression @milo', + }, + ], +}; diff --git a/nala/blocks/iframe/iframe.test.js b/nala/blocks/iframe/iframe.test.js new file mode 100644 index 0000000000..7f42c37e8c --- /dev/null +++ b/nala/blocks/iframe/iframe.test.js @@ -0,0 +1,24 @@ +import { expect, test } from '@playwright/test'; +import { features } from './iframe.spec.js'; +import IframeBlock from './iframe.page.js'; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Iframe Block test suite', () => { + // Iframe Block Checks: + test(`${features[0].name}, ${features[0].tags}`, async ({ page, baseURL }) => { + const Iframe = new IframeBlock(page); + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + + await test.step('Navigate to page with Iframe block', async () => { + await page.goto(`${baseURL}${features[0].path}${features[0].browserParams}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${features[0].browserParams}&${miloLibs}`); + }); + + await test.step('Validate Iframe block content', async () => { + await expect(Iframe.miloIframeContainer).toBeVisible(); + await expect(Iframe.iframeContainer).toBeVisible(); + }); + }); +}); diff --git a/nala/blocks/marketo/marketo.page.js b/nala/blocks/marketo/marketo.page.js new file mode 100644 index 0000000000..47bfe2533c --- /dev/null +++ b/nala/blocks/marketo/marketo.page.js @@ -0,0 +1,131 @@ +/* eslint-disable import/no-extraneous-dependencies, max-len, no-console, no-await-in-loop, import/extensions */ +import { expect } from '@playwright/test'; + +const FIRST_NAME = 'firstNameTest'; +const LAST_NAME = 'lastNameTest'; +const PHONE = '415-123-4567'; +const EMAIL = 'test+nosub@adobetest.com'; +const COMPANY = 'Adobe'; +const POSTAL_CODE = '94111'; + +export default class Marketo { + constructor(page) { + this.page = page; + this.marketo = this.page.locator('.marketo'); + this.firstName = this.marketo.locator('input[name="FirstName"]'); + this.lastName = this.marketo.locator('input[name="LastName"]'); + this.email = this.marketo.locator('input[name="Email"]'); + this.phone = this.marketo.locator('input[name="Phone"]'); + this.company = this.marketo.locator('input[name="mktoFormsCompany"]'); + this.functionalArea = this.marketo.locator( + 'select[name="mktoFormsFunctionalArea"]', + ); + this.country = this.marketo.locator('select[name="Country"]'); + this.state = this.marketo.locator('select[name="State"]'); + this.postalCode = this.marketo.locator('input[name="PostalCode"]'); + this.jobTitle = this.marketo.locator('select[name="mktoFormsJobTitle"]'); + this.primaryProductInterest = this.marketo.locator( + 'select[name="mktoFormsPrimaryProductInterest"]', + ); + this.companyType = this.marketo.locator( + 'select[name="mktoFormsCompanyType"]', + ); + this.submitButton = this.marketo.locator('#mktoButton_new'); + this.message = this.marketo.locator('.ty-message'); + } + + async submitFullTemplateForm(poi) { + await this.country.selectOption({ index: 1 }); + await this.jobTitle.selectOption({ index: 1 }); + await this.company.fill(COMPANY); + await this.firstName.fill(FIRST_NAME); + await this.lastName.fill(LAST_NAME); + await this.email.fill(EMAIL); + await this.phone.fill(PHONE); + await this.functionalArea.selectOption({ index: 1 }); + await this.postalCode.fill(POSTAL_CODE); + + // Setting index 2 to test so that the 'Company Type' field doesn't display + await this.primaryProductInterest.selectOption(poi !== undefined ? poi : { index: 2 }); + + await this.state.selectOption({ index: 1 }); + await this.selectCompanyType(); + await this.submitButton.click(); + } + + async submitExpandedTemplateForm() { + await this.country.selectOption({ index: 1 }); + await this.jobTitle.selectOption({ index: 1 }); + await this.firstName.fill(FIRST_NAME); + await this.lastName.fill(LAST_NAME); + await this.email.fill(EMAIL); + await this.functionalArea.selectOption({ index: 1 }); + await this.company.fill(COMPANY); + await this.selectCompanyType(); + await this.submitButton.click(); + } + + async submitEssentialTemplateForm() { + await this.country.selectOption({ index: 1 }); + await this.firstName.fill(FIRST_NAME); + await this.lastName.fill(LAST_NAME); + await this.email.fill(EMAIL); + await this.company.fill(COMPANY); + await this.selectCompanyType(); + await this.submitButton.click(); + } + + async getPOI() { + const poi = await this.page.evaluate( + 'window.mcz_marketoForm_pref.program.poi', + ); + return poi; + } + + async getFormTemplate() { + const template = await this.page.evaluate( + 'window.mcz_marketoForm_pref.form.template', + ); + return template; + } + + async selectCompanyType() { + // The company type field will display if the poi is one of the below + const expectedPOI = ['Commerce', 'ADOBEADVERTISINGCLOUD']; + + if (expectedPOI.includes(await this.getPOI())) { + this.companyType.selectOption({ index: 1 }); + } + } + + /** + * @description Checks that the input fields have the placeholder attribute + * and that the value isn't empty. + */ + async checkInputPlaceholders() { + const template = await this.page.evaluate( + 'window.mcz_marketoForm_pref.form.template', + ); + + if (!template) { + throw new Error('Template not found'); + } + + const inputFields = [ + this.firstName, + this.lastName, + this.email, + this.company, + ]; + + if (template === 'flex_contact') inputFields.push(this.phone, this.postalCode); + + inputFields.forEach(async (field) => { + await expect(async () => { + expect(await field).toHaveAttribute('placeholder', { timeout: 10000 }); + const placeholder = await field.getAttribute('placeholder'); + expect(placeholder.length).toBeGreaterThan(1); + }).toPass(); + }); + } +} diff --git a/nala/blocks/marketo/marketo.spec.js b/nala/blocks/marketo/marketo.spec.js new file mode 100644 index 0000000000..755f63ac5c --- /dev/null +++ b/nala/blocks/marketo/marketo.spec.js @@ -0,0 +1,73 @@ +module.exports = { + name: 'Marketo Forms block', + features: [ + { + tcid: '0', + name: '@marketo full template', + path: [ + '/drafts/nala/blocks/marketo/full', + ], + tags: '@marketo @marketoFullRedirect @marketoRedirect @milo @smoke @regression', + }, + { + tcid: '1', + name: '@marketo full template with company type', + path: [ + '/drafts/nala/blocks/marketo/full-with-company-type', + ], + tags: '@marketo @marketoFullRedirect @marketoRedirect @milo @smoke @regression', + }, + { + tcid: '2', + name: '@marketo expanded template', + path: [ + '/drafts/nala/blocks/marketo/expanded', + '/drafts/nala/blocks/marketo/expanded-with-company-type', + ], + tags: '@marketo @marketoExpandedRedirect @marketoRedirect @milo @smoke @regression', + }, + { + tcid: '3', + name: '@marketo essential template', + path: [ + '/drafts/nala/blocks/marketo/essential', + '/drafts/nala/blocks/marketo/essential-with-company-type', + ], + tags: '@marketo @marketoEssentialRedirect @marketoRedirect @milo @smoke @regression', + }, + { + tcid: '4', + name: '@marketo full template message', + path: [ + '/drafts/nala/blocks/marketo/full-message', + ], + tags: '@marketo @marketoFullMessage @marketoMessage @milo @smoke @regression', + }, + { + tcid: '5', + name: '@marketo full template with company type', + path: [ + '/drafts/nala/blocks/marketo/full-message-with-company-type', + ], + tags: '@marketo @marketoFullMessage @marketoMessage @milo @smoke @regression', + }, + { + tcid: '6', + name: '@marketo expanded template', + path: [ + '/drafts/nala/blocks/marketo/expanded-message', + '/drafts/nala/blocks/marketo/expanded-message-with-company-type', + ], + tags: '@marketo @marketoExpandedMessage @marketoMessage @milo @smoke @regression', + }, + { + tcid: '7', + name: '@marketo essential template', + path: [ + '/drafts/nala/blocks/marketo/essential-message', + '/drafts/nala/blocks/marketo/essential-message-with-company-type', + ], + tags: '@marketo @marketoEssentialMessage @marketoMessage @milo @smoke @regression', + }, + ], +}; diff --git a/nala/blocks/marketo/marketo.test.js b/nala/blocks/marketo/marketo.test.js new file mode 100644 index 0000000000..d303896a32 --- /dev/null +++ b/nala/blocks/marketo/marketo.test.js @@ -0,0 +1,278 @@ +import { expect, test } from '@playwright/test'; +import { features } from './marketo.spec.js'; +import MarketoBlock from './marketo.page.js'; + +let marketoBlock; +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Marketo block test suite', () => { + test.beforeAll(async ({ browserName }) => { + if (browserName === 'chromium' && process.env.CI) test.skip('TODO: debug why this is failing on github actions'); + + if (process.env.CI) test.setTimeout(1000 * 60 * 3); // 3 minutes + }); + + test.beforeEach(async ({ page }) => { + marketoBlock = new MarketoBlock(page); + }); + + features[0].path.forEach((path) => { + test(`0: @marketo full template (redirect), ${features[0].tags}, path: ${path}`, async ({ + page, + baseURL, + }) => { + const testPage = `${baseURL}${path}${miloLibs}`.toLowerCase(); + console.info(`[Test Page]: ${testPage}`); + + await test.step('step-1: Go to the Marketo block full template test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + await expect(page.url()).toBe(testPage); + await expect(marketoBlock.email).toBeVisible({ timeout: 10000 }); + }); + + await test.step('step-2: check the input field placeholders', async () => { + await marketoBlock.checkInputPlaceholders(); + }); + + await test.step('step-3: Submit the form with valid inputs', async () => { + await marketoBlock.submitFullTemplateForm(); + }); + + await test.step('step-4: Verify the form submission redirect', async () => { + await expect(async () => { + await marketoBlock.submitButton.waitFor({ state: 'detached' }); + const redirectedUrl = await page.url(); + await expect(redirectedUrl).toContain('?submissionid'); + }).toPass(); + }); + }); + }); + + features[1].path.forEach((path) => { + test(`1: @marketo full template (redirect) with company type, ${features[1].tags}, path: ${path}`, async ({ + page, + baseURL, + }) => { + const testPage = `${baseURL}${path}${miloLibs}`.toLowerCase(); + console.info(`[Test Page]: ${testPage}`); + + await test.step('step-1: Go to the Marketo block full template test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + await expect(page.url()).toBe(testPage); + await expect(marketoBlock.email).toBeVisible({ timeout: 10000 }); + }); + + await test.step('step-2: check the input field placeholders', async () => { + await marketoBlock.checkInputPlaceholders(); + }); + + await test.step('step-3: Submit the form with valid inputs', async () => { + await marketoBlock.submitFullTemplateForm('Digital commerce'); + }); + + await test.step('step-4: Verify the form submission redirect', async () => { + await expect(async () => { + await marketoBlock.submitButton.waitFor({ state: 'detached' }); + const redirectedUrl = await page.url(); + await expect(redirectedUrl).toContain('?submissionid'); + }).toPass(); + }); + }); + }); + + features[2].path.forEach((path) => { + test(`2: @marketo expanded template (redirect), ${features[2].tags}}, path: ${path}`, async ({ + page, + baseURL, + }) => { + const testPage = `${baseURL}${path}${miloLibs}`.toLowerCase(); + console.info(`[Test Page]: ${testPage}`); + + await test.step('step-1: Go to the Marketo block expanded template test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + await expect(page.url()).toBe(testPage); + await expect(marketoBlock.email).toBeVisible({ timeout: 10000 }); + }); + + await test.step('step-2: check the input field placeholders', async () => { + await marketoBlock.checkInputPlaceholders(); + }); + + await test.step('step-3: Submit the form with valid inputs', async () => { + await marketoBlock.submitExpandedTemplateForm(); + }); + + await test.step('step-4: Verify the form submission redirect', async () => { + await expect(async () => { + await marketoBlock.submitButton.waitFor({ state: 'detached' }); + const redirectedUrl = await page.url(); + await expect(redirectedUrl).toContain('?submissionid'); + }).toPass(); + }); + }); + }); + + features[3].path.forEach((path) => { + test(`3: @marketo essential template (redirect), ${features[3].tags}, path: ${path}`, async ({ + page, + baseURL, + }) => { + const testPage = `${baseURL}${path}${miloLibs}`.toLowerCase(); + console.info(`[Test Page]: ${testPage}`); + + await test.step('step-1: Go to the Marketo block essential template test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + await expect(page.url()).toBe(testPage); + await expect(marketoBlock.email).toBeVisible({ timeout: 10000 }); + }); + + await test.step('step-2: check the input field placeholders', async () => { + await marketoBlock.checkInputPlaceholders(); + }); + + await test.step('step-3: Submit the form with valid inputs', async () => { + await marketoBlock.submitEssentialTemplateForm(); + }); + + await test.step('step-4: Verify the form submission redirect', async () => { + await expect(async () => { + await marketoBlock.submitButton.waitFor({ state: 'detached' }); + const redirectedUrl = await page.url(); + await expect(redirectedUrl).toContain('?submissionid'); + }).toPass(); + }); + }); + }); + + features[4].path.forEach((path) => { + test(`4: @marketo full template (message), ${features[4].tags}, path: ${path}`, async ({ + page, + baseURL, + }) => { + const testPage = `${baseURL}${path}${miloLibs}`.toLowerCase(); + console.info(`[Test Page]: ${testPage}`); + + await test.step('step-1: Go to the Marketo block full template test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + await expect(page.url()).toBe(testPage); + await expect(marketoBlock.email).toBeVisible({ timeout: 10000 }); + }); + + await test.step('step-2: check the input field placeholders', async () => { + await marketoBlock.checkInputPlaceholders(); + }); + + await test.step('step-3: Submit the form with valid inputs', async () => { + await marketoBlock.submitFullTemplateForm(); + }); + + await test.step('step-4: Verify the form submission redirect', async () => { + await expect(async () => { + await expect(marketoBlock.message).toBeAttached(); + await expect(page.url()).toBe(testPage); + }).toPass(); + }); + }); + }); + + features[5].path.forEach((path) => { + test(`5: @marketo full template (message) with company type, ${features[5].tags}, path: ${path}`, async ({ + page, + baseURL, + }) => { + const testPage = `${baseURL}${path}${miloLibs}`.toLowerCase(); + console.info(`[Test Page]: ${testPage}`); + + await test.step('step-1: Go to the Marketo block full template test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + await expect(page.url()).toBe(testPage); + await expect(marketoBlock.email).toBeVisible({ timeout: 10000 }); + }); + + await test.step('step-2: check the input field placeholders', async () => { + await marketoBlock.checkInputPlaceholders(); + }); + + await test.step('step-3: Submit the form with valid inputs', async () => { + await marketoBlock.submitFullTemplateForm('Digital commerce'); + }); + + await test.step('step-4: Verify the form submission redirect', async () => { + await expect(async () => { + await expect(marketoBlock.message).toBeAttached(); + await expect(page.url()).toBe(testPage); + }).toPass(); + }); + }); + }); + + features[6].path.forEach((path) => { + test(`6: @marketo expanded (message) template, ${features[6].tags}}, path: ${path}`, async ({ + page, + baseURL, + }) => { + const testPage = `${baseURL}${path}${miloLibs}`.toLowerCase(); + console.info(`[Test Page]: ${testPage}`); + + await test.step('step-1: Go to the Marketo block expanded template test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + await expect(page.url()).toBe(testPage); + await expect(marketoBlock.email).toBeVisible({ timeout: 10000 }); + }); + + await test.step('step-2: check the input field placeholders', async () => { + await marketoBlock.checkInputPlaceholders(); + }); + + await test.step('step-3: Submit the form with valid inputs', async () => { + await marketoBlock.submitExpandedTemplateForm(); + }); + + await test.step('step-4: Verify the form submission redirect', async () => { + await expect(async () => { + await expect(marketoBlock.message).toBeAttached(); + await expect(page.url()).toBe(testPage); + }).toPass(); + }); + }); + }); + + features[7].path.forEach((path) => { + test(`7: @marketo essential (message) template, ${features[7].tags}, path: ${path}`, async ({ + page, + baseURL, + }) => { + const testPage = `${baseURL}${path}${miloLibs}`.toLowerCase(); + console.info(`[Test Page]: ${testPage}`); + + await test.step('step-1: Go to the Marketo block essential template test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + await expect(page.url()).toBe(testPage); + await expect(marketoBlock.email).toBeVisible({ timeout: 10000 }); + }); + + await test.step('step-2: check the input field placeholders', async () => { + await marketoBlock.checkInputPlaceholders(); + }); + + await test.step('step-3: Submit the form with valid inputs', async () => { + await marketoBlock.submitEssentialTemplateForm(); + }); + + await test.step('step-4: Verify the form submission redirect', async () => { + await expect(async () => { + await expect(marketoBlock.message).toBeAttached(); + await expect(page.url()).toBe(testPage); + }).toPass(); + }); + }); + }); +}); diff --git a/nala/blocks/marquee/marquee.page.js b/nala/blocks/marquee/marquee.page.js new file mode 100644 index 0000000000..84673f0110 --- /dev/null +++ b/nala/blocks/marquee/marquee.page.js @@ -0,0 +1,186 @@ +export default class Marquee { + constructor(page, nth = 0) { + this.page = page; + // marquee types locators + this.marquee = page.locator('.marquee').nth(nth); + this.marqueeLight = page.locator('.marquee.light'); + this.marqueeSmall = page.locator('.marquee.small'); + this.marqueeSmallLight = page.locator('.marquee.small.light'); + this.marqueeSmallDark = page.locator('.marquee.small.dark'); + this.marqueeLarge = page.locator('.marquee.large'); + this.marqueeLargeLight = page.locator('.marquee.large.light'); + this.marqueeLargeDark = page.locator('.marquee.large.dark'); + this.marqueeQuiet = page.locator('.marquee.quiet'); + this.marqueeInline = page.locator('.marquee'); + this.marqueeSplitSmall = page.locator('.marquee.split.small'); + this.marqueeSplitLarge = page.locator('.marquee.split.large'); + this.marqueeSplitLargeLight = page.locator('.marquee.split.one-third.large.light'); + this.marqueeSplitOneThirdLargeLight = page.locator('.marquee.split.one-third.large.light'); + this.marqueeSplitOneThird = page.locator('.marquee.split.one-third'); + this.marqueeSplitOneThirdSmallLight = page.locator('.marquee.split.one-third.small.light'); + + // marque section(s) locators + // marquee details + this.detailM = this.marquee.locator('.detail-m'); + this.detailL = this.marquee.locator('.detail-l'); + this.brandImage = this.marquee.locator('.detail-m'); + + // marquee headings + this.headingXL = this.marquee.locator('.heading-xl'); + this.headingXXL = this.marquee.locator('.heading-xxl'); + + // marquee body area + this.bodyM = this.marquee.locator('.body-m'); + this.bodyXL = this.marquee.locator('.body-xl'); + + // marquee actions area + this.actionArea = this.marquee.locator('.action-area'); + this.outlineButton = this.marquee.locator('.con-button.outline'); + this.outlineButtonS = this.marquee.locator('.con-button.outline.button-s'); + this.outlineButtonM = this.marquee.locator('.con-button.outline.button-m'); + this.outlineButtonL = this.marquee.locator('.con-button.outline.button-l'); + this.outlineButtonXL = this.marquee.locator('.con-button.outline.button-xl'); + + this.blueButton = this.marquee.locator('.con-button.blue'); + this.blueButtonL = this.marquee.locator('.con-button.blue.button-l'); + this.blueButtonXL = this.marquee.locator('.con-button.blue.button-xl'); + this.filledBlueButton = this.marquee.locator('.con-button.blue'); + this.filledButtonM = this.marquee.locator('.con-button.blue.button-s'); + this.filledButtonM = this.marquee.locator('.con-button.blue.button-m'); + this.filledButtonL = this.marquee.locator('.con-button.blue.button-l'); + this.filledButtonXL = this.marquee.locator('.con-button.blue.button-xl'); + + this.actionLink1 = this.marquee.locator('a').nth(0); + this.actionLink2 = this.marquee.locator('a').nth(1); + + // background images + this.background = this.marquee.locator('.background'); + this.backgroundImage = this.marquee.locator('div.background img'); + this.backgroundImageMobile = this.marquee.locator('div .background .mobile-only img'); + this.backgroundImageTablet = this.marquee.locator('div.background .tablet-only img'); + this.backgroundImageDesktop = this.marquee.locator('div.background .desktop-only img'); + + // background video + this.backgroundVideo = this.marquee.locator('div video'); + this.backgroundVideoDesktop = this.marquee.locator('div .desktop-only video'); + + // foreground images + this.foreground = this.marquee.locator('.foreground'); + this.foregroundImage = this.marquee.locator('div.foreground img'); + this.iconImage = this.foreground.locator('.icon-area img'); + + // media images + this.mediaImage = this.marquee.locator('div.asset img'); + + // marquee attributes + this.attributes = { + 'marquee.light': { + backgroundImg: { + loading: 'eager', + fetchpriority: 'high', + width: '1369', + height: '685', + }, + }, + 'marquee.small': { + backgroundImg: { + loading: 'eager', + fetchpriority: 'high', + width: '750', + height: '375', + }, + }, + 'marquee.small.light': { + backgroundImg: { + loading: 'eager', + fetchpriority: 'high', + width: '750', + height: '375', + }, + }, + 'marquee.large': { + backgroundImg: { + loading: 'eager', + fetchpriority: 'high', + width: '750', + height: '375', + }, + }, + 'marquee.large.light': { + backgroundImg: { + loading: 'eager', + fetchpriority: 'high', + width: '750', + height: '375', + style: 'object-position: left center;', + }, + }, + 'marquee.split.small': { style: /^background:\s+rgb\(0, 0, 0\)$/ }, + 'marquee.split.large': { + iconImg: { + loading: 'eager', + fetchpriority: 'high', + width: '49', + height: '48', + }, + mediaImg: { + loading: 'eager', + fetchpriority: 'high', + width: '720', + height: '520', + }, + }, + 'marquee.split.one-third-large': { + style: /^background:\s+rgb\(245, 245, 245\)$/, + iconImg: { + loading: 'eager', + fetchpriority: 'high', + width: '200', + height: '80', + }, + mediaImg: { + loading: 'eager', + fetchpriority: 'high', + width: '720', + height: '520', + }, + }, + 'marquee.split.one-third': { + style: /^background:\s+rgb\(0, 0, 0\)$/, + iconImg: { + loading: 'eager', + fetchpriority: 'high', + width: '65', + height: '64', + }, + mediaImg: { + loading: 'eager', + fetchpriority: 'high', + width: '720', + height: '520', + }, + }, + backgroundMobileImg: { + loading: 'eager', + fetchpriority: 'high', + }, + 'backgroundVideo.inline': { + playsinline: '', + autoplay: '', + loop: '', + muted: '', + }, + 'backgroundVideo.loopOnce': { + playsinline: '', + autoplay: '', + muted: '', + }, + 'backgroundVideo.controls': { + controls: '', + autoplay: '', + loop: '', + muted: '', + }, + }; + } +} diff --git a/nala/blocks/marquee/marquee.spec.js b/nala/blocks/marquee/marquee.spec.js new file mode 100644 index 0000000000..87b18b33ee --- /dev/null +++ b/nala/blocks/marquee/marquee.spec.js @@ -0,0 +1,187 @@ +module.exports = { + name: 'Marquee Block', + features: [ + { + tcid: '0', + name: '@Marquee (light)', + path: '/drafts/nala/blocks/marquee/marquee-light', + data: { + h2Text: 'Heading XL Marquee standard medium left light', + bodyText: 'Body M Lorem ipsum dolor sit amet,', + outlineButtonText: 'Lorem ipsum', + blueButtonText: 'Call to action', + }, + tags: '@marquee @marquee-light @smoke @regression @milo', + }, + { + tcid: '1', + name: '@Marquee (small)', + path: '/drafts/nala/blocks/marquee/marquee-small', + data: { + h2Text: 'Marquee standard small dark', + bodyText: 'Lorem ipsum dolor sit amet', + blueButtonText: 'Call to action', + }, + tags: '@marquee @marquee-small @smoke @regression @milo', + }, + { + tcid: '2', + name: '@Marquee (small,light)', + path: '/drafts/nala/blocks/marquee/marquee-small-light', + data: { + detailText: 'Detail', + h2Text: 'Heading XL Marquee standard small light', + bodyText: 'Lorem ipsum dolor sit amet', + outlineButtonText: 'Lorem ipsum', + blueButtonText: 'Call to action', + }, + tags: '@marquee @marquee-small-light @smoke @regression @milo', + }, + { + tcid: '3', + name: '@Marquee (large)', + path: '/drafts/nala/blocks/marquee/marquee-large', + data: { + h2Text: 'Marquee Large Dark', + bodyText: 'Lorem ipsum dolor sit amet', + outlineButtonText: 'Lorem ipsum', + blueButtonText: 'Call to action', + }, + tags: '@marquee @marquee-large @smoke @regression @milo', + }, + { + tcid: '4', + name: '@Marquee (large,light)', + path: '/drafts/nala/blocks/marquee/marquee-large-light', + data: { + h2Text: 'Marquee Large Light', + bodyText: 'Lorem ipsum dolor sit amet', + outlineButtonText: 'Secondary action', + blueButtonText: 'Call to action', + }, + tags: '@marquee @marquee-large-light @smoke @regression @milo', + }, + { + tcid: '5', + name: '@Marquee (quiet)', + path: '/drafts/nala/blocks/marquee/marquee-quiet', + data: { + detailText: 'Detail', + h2Text: 'Marquee quiet', + bodyText: 'Marquee’s variants are small,', + blueButtonText: 'Watch the video', + }, + tags: '@marquee @marquee-quiet @smoke @regression @milo', + }, + { + tcid: '6', + name: '@Marquee (inline)', + path: '/drafts/nala/blocks/marquee/marquee-inline', + data: { + detailText: 'Detail', + h2Text: 'Marquee inline', + bodyText: 'Marquee’s variants are small,', + }, + tags: '@marquee @marquee-inline @smoke @regression @milo', + }, + { + tcid: '7', + name: '@Marquee (split,small)', + path: '/drafts/nala/blocks/marquee/marquee-split-small', + data: { + detailText: 'DETAIL M BOLD 12/15 OPTIONAL', + h2Text: 'Marquee Split ½ dark', + bodyText: 'Lorem ipsum dolor sit amet', + outlineButtonText: 'Secondary action', + blueButtonText: 'Call to action', + }, + tags: '@marquee @marquee-split-small @smoke @regression @milo', + }, + { + tcid: '8', + name: '@Marquee (split,large)', + path: '/drafts/nala/blocks/marquee/marquee-split-large', + data: { + detailText: 'DETAIL L BOLD 16/20', + h2Text: 'Heading XXL 44/55 Lorem', + bodyText: 'Body XL Regular (22/33) Lorem ipsum dolor sit amet', + blueButtonText: 'Call to action', + linkText: 'Body M 18/27', + }, + tags: '@marquee @marquee-split-large @smoke @regression @milo', + }, + { + tcid: '9', + name: '@Marquee (split,one-third,large,light)', + path: '/drafts/nala/blocks/marquee/marquee-split-one-third-large-light', + data: { + detailText: 'DETAIL L BOLD 16/20', + h2Text: 'Heading XXL 44/55 Lorem', + bodyText: 'Body XL Regular (22/33) Lorem ipsum dolor sit amet', + blueButtonText: 'Call to action', + linkText: 'Body M 18/27', + }, + tags: '@marquee @marquee-split-one-third-large-light @smoke @regression @milo', + }, + { + tcid: '10', + name: '@Marquee (split,one-third)', + path: '/drafts/nala/blocks/marquee/marquee-split-one-third', + data: { + detailText: 'DETAIL M BOLD 12/15 OPTIONAL', + h2Text: 'Heading XL 36/45 Lorem', + bodyText: 'Body M Regular (18/27) Lorem ipsum dolor sit amet', + blueButtonText: 'Call to action', + linkText: 'Body M 18/27', + }, + tags: '@marquee @marquee-split-one-third @smoke @regression @milo', + }, + { + tcid: '11', + name: '@Marquee (split,one-third,small,light)', + path: '/drafts/nala/blocks/marquee/marquee-split-one-third-small-light', + data: { + detailText: 'DETAIL M BOLD 12/15 OPTIONAL', + h2Text: 'Heading XL 36/45 Lorem', + bodyText: 'Body M Regular (18/27) Lorem ipsum dolor sit amet', + blueButtonText: 'Call to action', + }, + tags: '@marquee @marquee-split-one-third-small-light @smoke @regression @milo', + }, + { + tcid: '12', + name: '@Marquee small (background video playsinline)', + path: '/drafts/nala/blocks/marquee/marquee-small-background-video', + data: { + h2Text: 'Marquee standard small dark', + bodyText: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit', + blueButtonText: 'Call to action', + }, + tags: '@marquee @marquee-video @smoke @regression @milo', + }, + { + tcid: '13', + name: '@Marquee large (background video playsinline desktop)', + path: '/drafts/nala/blocks/marquee/marquee-large-desktop-video-autoplay', + data: { + h2Text: 'Desktop video only', + bodyText: 'From amazing AI-generated images in Photoshop', + blueButtonText: 'Free trial', + linkText: 'See all plans', + }, + tags: '@marquee @marquee-video @smoke @regression @milo', + }, + { + tcid: '14', + name: '@Marquee large (background video playsinline loop once)', + path: '/drafts/nala/blocks/marquee/video-autoplay-loop-once', + data: { + detailText: 'DETAIL L 16/20', + h2Text: 'Heading XL 36/45 Media (large, dark)', + bodyText: 'Body M 18/27 Lorem ipsum dolor sit amet', + blueButtonText: 'Learn More', + }, + tags: '@marquee @marquee-video @smoke @regression @milo', + }, + ], +}; diff --git a/nala/blocks/marquee/marquee.test.js b/nala/blocks/marquee/marquee.test.js new file mode 100644 index 0000000000..f0b61239a9 --- /dev/null +++ b/nala/blocks/marquee/marquee.test.js @@ -0,0 +1,572 @@ +import { expect, test } from '@playwright/test'; +import WebUtil from '../../libs/webutil.js'; +import { features } from './marquee.spec.js'; +import MarqueeBlock from './marquee.page.js'; + +let webUtil; +let marquee; +let consoleErrors = []; + +const miloLibs = process.env.MILO_LIBS || ''; +const knownConsoleErrors = [ + 'Access-Control-Allow-Origin', + 'Failed to load resource: net::ERR_FAILED', + 'adobeid-na1-stg1.services', + 'Attestation check for Topics', + 'Access to fetch at', + 'net::ERR_HTTP2_PROTOCOL_ERROR', +]; + +test.describe('Milo Marquee Block test suite', () => { + test.beforeEach(async ({ page }) => { + webUtil = new WebUtil(page); + marquee = new MarqueeBlock(page); + + page.on('console', (exception) => { + if (exception.type() === 'error') { + consoleErrors.push(exception.text()); + } + }); + }); + + test.afterEach(async () => { + consoleErrors = []; + }); + + // Test 0 : Marquee (light) + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + const { data } = features[0]; + + await test.step('step-1: Go to Marquee (light) block test page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('step-2: Verify marquee(light) specs', async () => { + await expect(await marquee.marqueeLight).toBeVisible(); + + await expect(await marquee.headingXL).toContainText(data.h2Text); + await expect(await marquee.bodyM).toContainText(data.bodyText); + await expect(await marquee.outlineButton).toContainText(data.outlineButtonText); + await expect(await marquee.blueButton).toContainText(data.blueButtonText); + + await expect(await marquee.backgroundImage).toBeVisible(); + expect(await webUtil.verifyAttributes(marquee.backgroundImage, marquee.attributes['marquee.light'].backgroundImg)).toBeTruthy(); + }); + + await test.step('step-3: Verify analytics attributes', async () => { + await expect(await marquee.marqueeLight).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 1)); + await expect(await marquee.outlineButton).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.outlineButtonText, 1, data.h2Text)); + await expect(await marquee.blueButton).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 2, data.h2Text)); + }); + + await test.step('step-4: Verify browser console errors', async () => { + consoleErrors.length > knownConsoleErrors.length && console.log('[Console error]:', consoleErrors); + expect.soft(consoleErrors.length).toBeLessThanOrEqual(knownConsoleErrors.length); + }); + }); + + // Test 1 : Marquee (small) + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + const { data } = features[1]; + + await test.step('step-1: Go to Marquee (small) block test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Marquee (small) specs', async () => { + await expect(await marquee.marqueeSmall).toBeVisible(); + + await expect(await marquee.headingXL).toContainText(data.h2Text); + await expect(await marquee.bodyM).toContainText(data.bodyText); + await expect(await marquee.blueButton).toContainText(data.blueButtonText); + + await expect(await marquee.backgroundImage).toBeVisible(); + expect(await webUtil.verifyAttributes(marquee.backgroundImage, marquee.attributes['marquee.small'].backgroundImg)).toBeTruthy(); + }); + + await test.step('step-3: Verify analytic attributes', async () => { + await expect(await marquee.marqueeSmall).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 1)); + await expect(await marquee.blueButton).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 1, data.h2Text)); + }); + + await test.step('step-4: Verify browser console errors', async () => { + consoleErrors.length > knownConsoleErrors.length && console.log('[Console error]:', consoleErrors); + expect.soft(consoleErrors.length).toBeLessThanOrEqual(knownConsoleErrors.length); + }); + }); + + // Test 2 : Marquee (small,light) + test(`${features[2].name},${features[2].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + const { data } = features[2]; + + await test.step('step-1: Go to Marquee (small, light ) block test page', async () => { + await page.goto(`${baseURL}${features[2].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[2].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Marquee (small, light) specs', async () => { + await expect(await marquee.marqueeSmallLight).toBeVisible(); + + await expect(await marquee.detailM).toContainText(data.detailText); + await expect(await marquee.headingXL).toContainText(data.h2Text); + await expect(await marquee.bodyM).toContainText(data.bodyText); + await expect(await marquee.outlineButton).toContainText(data.outlineButtonText); + await expect(await marquee.blueButton).toContainText(data.blueButtonText); + + await expect(await marquee.backgroundImage).toBeVisible(); + expect(await webUtil.verifyAttributes(marquee.backgroundImage, marquee.attributes['marquee.small.light'].backgroundImg)).toBeTruthy(); + }); + + await test.step('step-3: Verify analytic attributes', async () => { + await expect(await marquee.marqueeSmallLight).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 1)); + await expect(await marquee.outlineButton).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.outlineButtonText, 1, data.h2Text)); + await expect(await marquee.blueButton).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 2, data.h2Text)); + }); + + await test.step('step-4: Verify browser console errors', async () => { + consoleErrors.length > knownConsoleErrors.length && console.log('[Console error]:', consoleErrors); + expect.soft(consoleErrors.length).toBeLessThanOrEqual(knownConsoleErrors.length); + }); + }); + + // Test 3 : Marquee (large) + test(`${features[3].name},${features[3].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[3].path}${miloLibs}`); + const { data } = features[3]; + + await test.step('step-1: Go to Marquee (large ) block test page', async () => { + await page.goto(`${baseURL}${features[3].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[3].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Marquee (large) specs', async () => { + await expect(await marquee.marqueeLarge).toBeVisible(); + + await expect(await marquee.headingXXL).toContainText(data.h2Text); + await expect(await marquee.bodyXL).toContainText(data.bodyText); + await expect(await marquee.outlineButtonXL).toContainText(data.outlineButtonText); + await expect(await marquee.blueButtonXL).toContainText(data.blueButtonText); + + await expect(await marquee.backgroundImage).toBeVisible(); + expect(await webUtil.verifyAttributes(marquee.backgroundImage, marquee.attributes['marquee.large'].backgroundImg)).toBeTruthy(); + }); + + await test.step('step-3: Verify analytic attributes', async () => { + await expect(await marquee.marqueeLarge).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 1)); + await expect(await marquee.outlineButtonXL).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.outlineButtonText, 1, data.h2Text)); + await expect(await marquee.blueButtonXL).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 2, data.h2Text)); + }); + + await test.step('step-4: Verify browser console errors', async () => { + consoleErrors.length > knownConsoleErrors.length && console.log('[Console error]:', consoleErrors); + expect.soft(consoleErrors.length).toBeLessThanOrEqual(knownConsoleErrors.length); + }); + }); + + // Test 4 : Marquee (large,light) + test(`${features[4].name},${features[4].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[4].path}${miloLibs}`); + const { data } = features[4]; + + await test.step('step-1: Go to Marquee (large, light ) block test page', async () => { + await page.goto(`${baseURL}${features[4].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[4].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Marquee (large, light) specs', async () => { + await expect(await marquee.marqueeLargeLight).toBeVisible(); + + await expect(await marquee.headingXXL).toContainText(data.h2Text); + await expect(await marquee.bodyXL).toContainText(data.bodyText); + await expect(await marquee.outlineButtonXL).toContainText(data.outlineButtonText); + await expect(await marquee.blueButtonXL).toContainText(data.blueButtonText); + + await expect(await marquee.backgroundImage).toBeVisible(); + expect(await webUtil.verifyAttributes(marquee.backgroundImage, marquee.attributes['marquee.large.light'].backgroundImg)).toBeTruthy(); + }); + + await test.step('step-3: Verify analytic attributes', async () => { + await expect(await marquee.marqueeLargeLight).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 1)); + await expect(await marquee.outlineButtonXL).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.outlineButtonText, 1, data.h2Text)); + await expect(await marquee.blueButtonXL).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 2, data.h2Text)); + }); + + await test.step('step-4: Verify and log if any console errors', async () => { + consoleErrors.length > knownConsoleErrors.length && console.log('[Console error]:', consoleErrors); + expect.soft(consoleErrors.length).toBeLessThanOrEqual(knownConsoleErrors.length); + }); + }); + + // Test 5 : Marquee (quiet) + test(`${features[5].name},${features[5].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[5].path}${miloLibs}`); + const { data } = features[5]; + + await test.step('step-1: Go to Marquee (quiet ) block test page', async () => { + await page.goto(`${baseURL}${features[5].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[5].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Marquee (quiet) specs', async () => { + await expect(await marquee.marqueeQuiet).toBeVisible(); + + await expect(await marquee.detailM).toContainText(data.detailText); + await expect(await marquee.headingXL).toContainText(data.h2Text); + await expect(await marquee.bodyM).toContainText(data.bodyText); + await expect(await marquee.blueButton).toContainText(data.blueButtonText); + + await expect(await marquee.backgroundImage).toBeHidden(); + }); + + await test.step('step-3: Verify analytic attributes', async () => { + await expect(await marquee.marqueeQuiet).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 1)); + await expect(await marquee.blueButton).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 1, data.h2Text)); + }); + + await test.step('step-4: Verify and log if any console errors', async () => { + consoleErrors.length > knownConsoleErrors.length && console.log('[Console error]:', consoleErrors); + expect.soft(consoleErrors.length).toBeLessThanOrEqual(knownConsoleErrors.length); + }); + }); + + // Test 6 : Marquee (inline) + test(`${features[6].name},${features[6].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[6].path}${miloLibs}`); + const { data } = features[6]; + + await test.step('step-1: Go to Marquee (inline ) block test page', async () => { + await page.goto(`${baseURL}${features[6].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[6].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Marquee (inline) specs', async () => { + await expect(await marquee.marqueeInline).toBeVisible(); + + await expect(await marquee.detailM).toContainText(data.detailText); + await expect(await marquee.headingXL).toContainText(data.h2Text); + await expect(await marquee.bodyM).toContainText(data.bodyText); + + await expect(await marquee.backgroundImage).toBeHidden(); + }); + + await test.step('step-3: Verify analytic attributes', async () => { + await expect(await marquee.marqueeInline).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 1)); + }); + + await test.step('step-4: Verify and log if any console errors', async () => { + consoleErrors.length > knownConsoleErrors.length && console.log('[Console error]:', consoleErrors); + expect.soft(consoleErrors.length).toBeLessThanOrEqual(knownConsoleErrors.length); + }); + }); + + // Test 7 : Marquee (split,small) + test(`${features[7].name},${features[7].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[7].path}${miloLibs}`); + const { data } = features[7]; + + await test.step('step-1: Go to Marquee (split, small ) block test page', async () => { + await page.goto(`${baseURL}${features[7].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[7].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Marquee (split, small) specs', async () => { + await expect(marquee.marqueeSplitSmall).toBeVisible(); + + await expect(await marquee.detailM).toContainText(data.detailText); + await expect(await marquee.headingXL).toContainText(data.h2Text); + await expect(await marquee.bodyM).toContainText(data.bodyText); + await expect(await marquee.outlineButton).toContainText(data.outlineButtonText); + await expect(await marquee.blueButton).toContainText(data.blueButtonText); + + expect(await webUtil.verifyAttributes(marquee.marqueeSplitSmall, marquee.attributes['marquee.split.small'].style)).toBeTruthy(); + }); + + await test.step('step-3: Verify analytic attributes', async () => { + await expect(await marquee.marqueeSplitSmall).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 1)); + await expect(await marquee.outlineButton).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.outlineButtonText, 1, data.h2Text)); + await expect(await marquee.blueButton).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 2, data.h2Text)); + }); + + await test.step('step-4: Verify and log if any console errors', async () => { + consoleErrors.length > knownConsoleErrors.length && console.log('[Console error]:', consoleErrors); + expect.soft(consoleErrors.length).toBeLessThanOrEqual(knownConsoleErrors.length); + }); + }); + + // Test 8 : Marquee (split,large) + test(`${features[8].name},${features[8].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[8].path}${miloLibs}`); + const { data } = features[8]; + + await test.step('step-1: Go to Marquee (split, large ) block test page', async () => { + await page.goto(`${baseURL}${features[8].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[8].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Marquee (split, large) specs ', async () => { + await expect(await marquee.marqueeSplitLarge).toBeVisible(); + + await expect(await marquee.detailL).toContainText(data.detailText); + await expect(await marquee.headingXXL).toContainText(data.h2Text); + await expect(await marquee.bodyXL).toContainText(data.bodyText); + await expect(await marquee.blueButtonXL).toContainText(data.blueButtonText); + await expect(await marquee.actionLink2).toContainText(data.linkText); + + await expect(await marquee.iconImage).toBeVisible(); + expect(await webUtil.verifyAttributes(marquee.iconImage, marquee.attributes['marquee.split.large'].iconImg)).toBeTruthy(); + + await expect(await marquee.mediaImage).toBeVisible(); + expect(await webUtil.verifyAttributes(marquee.mediaImage, marquee.attributes['marquee.split.large'].mediaImg)).toBeTruthy(); + }); + + await test.step('step-3: Verify analytic attributes', async () => { + await expect(await marquee.marqueeSplitLarge).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 1)); + await expect(await marquee.blueButtonXL).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 1, data.h2Text)); + await expect(await marquee.actionLink2).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.linkText, 2, data.h2Text)); + }); + + await test.step('step-4: Verify and log if any console errors', async () => { + consoleErrors.length > knownConsoleErrors.length && console.log('[Console error]:', consoleErrors); + expect.soft(consoleErrors.length).toBeLessThanOrEqual(knownConsoleErrors.length); + }); + }); + + // Test 9 : Marquee (split,one-third,large,light) + test(`${features[9].name},${features[9].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[9].path}${miloLibs}`); + const { data } = features[9]; + + await test.step('step-1: Go to Marquee (split, one-third, large, light ) block test page', async () => { + await page.goto(`${baseURL}${features[9].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[9].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Marquee (split, one-third, large, light) specs', async () => { + await expect(marquee.marqueeSplitOneThirdLargeLight).toBeVisible(); + + await expect(await marquee.detailL).toContainText(data.detailText); + await expect(await marquee.headingXXL).toContainText(data.h2Text); + await expect(await marquee.bodyXL).toContainText(data.bodyText); + await expect(await marquee.blueButtonXL).toContainText(data.blueButtonText); + await expect(await marquee.actionLink2).toContainText(data.linkText); + + await expect(await marquee.iconImage).toBeVisible(); + expect(await webUtil.verifyAttributes(marquee.iconImage, marquee.attributes['marquee.split.one-third-large'].iconImg)).toBeTruthy(); + + await expect(await marquee.mediaImage).toBeVisible(); + expect(await webUtil.verifyAttributes(marquee.mediaImage, marquee.attributes['marquee.split.one-third-large'].mediaImg)).toBeTruthy(); + }); + + await test.step('step-3: Verify analytic attributes', async () => { + await expect(await marquee.marqueeSplitOneThirdLargeLight).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 1)); + await expect(await marquee.blueButtonXL).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 1, data.h2Text)); + await expect(await marquee.actionLink2).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.linkText, 2, data.h2Text)); + }); + + await test.step('step-3: Verify and log if any console errors', async () => { + consoleErrors.length > knownConsoleErrors.length && console.log('[Console error]:', consoleErrors); + expect.soft(consoleErrors.length).toBeLessThanOrEqual(knownConsoleErrors.length); + }); + }); + + // Test 10 : Marquee (split,one-third) + test(`${features[10].name},${features[10].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[10].path}${miloLibs}`); + const { data } = features[10]; + + await test.step('step-1: Go to Marquee (split, one-third ) block test page', async () => { + await page.goto(`${baseURL}${features[10].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[10].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Marquee (split, one-third) specs', async () => { + await expect(await marquee.marqueeSplitOneThird).toBeVisible(); + + await expect(await marquee.detailM).toContainText(data.detailText); + await expect(await marquee.headingXL).toContainText(data.h2Text); + await expect(await marquee.bodyM).toContainText(data.bodyText); + await expect(await marquee.blueButtonL).toContainText(data.blueButtonText); + await expect(await marquee.actionLink2).toContainText(data.linkText); + + await expect(await marquee.iconImage).toBeVisible(); + expect(await webUtil.verifyAttributes(marquee.iconImage, marquee.attributes['marquee.split.one-third'].iconImg)).toBeTruthy(); + + await expect(await marquee.mediaImage).toBeVisible(); + expect(await webUtil.verifyAttributes(marquee.mediaImage, marquee.attributes['marquee.split.one-third'].mediaImg)).toBeTruthy(); + }); + + await test.step('step-3: Verify analytic attributes', async () => { + await expect(await marquee.marqueeSplitOneThird).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 1)); + await expect(await marquee.blueButtonL).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 1, data.h2Text)); + await expect(await marquee.actionLink2).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.linkText, 2, data.h2Text)); + }); + + await test.step('step-4: Verify and log if any console errors', async () => { + consoleErrors.length > knownConsoleErrors.length && console.log('[Console error]:', consoleErrors); + expect.soft(consoleErrors.length).toBeLessThanOrEqual(knownConsoleErrors.length); + }); + }); + + // Test 11 : Marquee (split,one-third,small,light) + test(`${features[11].name},${features[11].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[11].path}${miloLibs}`); + const { data } = features[11]; + + await test.step('step-1: Go to Marquee (split,one-third,small,light ) block test page', async () => { + await page.goto(`${baseURL}${features[11].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[11].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Marquee (split,one-third,small,light) specs', async () => { + await expect(marquee.marqueeSplitOneThirdSmallLight).toBeVisible(); + + await expect(await marquee.detailM).toContainText(data.detailText); + await expect(await marquee.headingXL).toContainText(data.h2Text); + await expect(await marquee.bodyM).toContainText(data.bodyText); + await expect(await marquee.blueButtonL).toContainText(data.blueButtonText); + + await expect(await marquee.mediaImage).toBeVisible(); + expect(await webUtil.verifyAttributes(marquee.mediaImage, marquee.attributes['marquee.split.one-third'].mediaImg)).toBeTruthy(); + }); + + await test.step('step-3: Verify analytic attributes', async () => { + await expect(await marquee.marqueeSplitOneThirdSmallLight).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 1)); + await expect(await marquee.blueButtonL).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 1, data.h2Text)); + }); + + await test.step('step-4: Verify and log if any console errors', async () => { + consoleErrors.length > knownConsoleErrors.length && console.log('[Console error]:', consoleErrors); + expect.soft(consoleErrors.length).toBeLessThanOrEqual(knownConsoleErrors.length); + }); + }); + + // Test 12 : Marquee small (background video playsinline) + test(`${features[12].name},${features[12].tags}`, async ({ page, baseURL, browserName }) => { + test.slow(); + test.skip(browserName === 'webkit', 'This feature is failing on Webkit browsers'); + console.info(`[Test Page]: ${baseURL}${features[12].path}${miloLibs}`); + const { data } = features[12]; + + await test.step('step-1: Go to Marquee (small) block test page', async () => { + await page.goto(`${baseURL}${features[12].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[12].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Marquee (small) background video playsinline specs', async () => { + await expect(await marquee.marqueeSmallDark).toBeVisible(); + + await expect(await marquee.headingXL).toContainText(data.h2Text); + await expect(await marquee.bodyM).toContainText(data.bodyText); + await expect(await marquee.blueButton).toContainText(data.blueButtonText); + + await expect(await marquee.backgroundVideo).toBeVisible(); + expect(await webUtil.verifyAttributes(marquee.backgroundVideo, marquee.attributes['backgroundVideo.inline'])).toBeTruthy(); + }); + + await test.step('step-3: Verify analytic attributes', async () => { + await expect(await marquee.marqueeSmallDark).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 1)); + await expect(await marquee.blueButtonL).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 1, data.h2Text)); + }); + + await test.step('step-4: Verify and log if any console errors', async () => { + consoleErrors.length > knownConsoleErrors.length && console.log('[Console error]:', consoleErrors); + expect.soft(consoleErrors.length).toBeLessThanOrEqual(knownConsoleErrors.length); + }); + }); + + // Test 13 : Marquee large (background video playsinline desktop) + test(`${features[13].name},${features[13].tags}`, async ({ page, baseURL, browserName }) => { + test.skip(browserName === 'webkit', 'This feature is failing on Webkit browsers'); + test.slow(); + console.info(`[Test Page]: ${baseURL}${features[13].path}${miloLibs}`); + const { data } = features[13]; + + await test.step('step-1: Go to Marquee (large, light ) block test page', async () => { + await page.goto(`${baseURL}${features[13].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[13].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Marquee (large, light) desktop background specs', async () => { + await expect(await marquee.marqueeLargeLight).toBeVisible(); + + await expect(await marquee.headingXXL).toContainText(data.h2Text); + await expect(await marquee.bodyXL).toContainText(data.bodyText); + await expect(await marquee.blueButtonXL).toContainText(data.blueButtonText); + await expect(await marquee.actionLink2).toContainText(data.linkText); + + await expect(await marquee.backgroundVideoDesktop).toBeVisible(); + expect(await webUtil.verifyAttributes(marquee.backgroundVideoDesktop, marquee.attributes['backgroundVideo.inline'])).toBeTruthy(); + + const sourceElement = await marquee.backgroundVideoDesktop.locator('source'); + expect(await sourceElement.getAttribute('src')).toContain('.mp4'); + }); + + await test.step('step-3: Verify analytic attributes', async () => { + await expect(await marquee.marqueeLargeLight).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 1)); + await expect(await marquee.blueButtonXL).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 1, data.h2Text)); + await expect(await marquee.actionLink2).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.linkText, 2, data.h2Text)); + }); + + await test.step('step-4: Verify and log if any console errors', async () => { + consoleErrors.length > knownConsoleErrors.length && console.log('[Console error]:', consoleErrors); + expect.soft(consoleErrors.length).toBeLessThanOrEqual(knownConsoleErrors.length); + }); + }); + + // Test 14 : Marquee large (background video playsinline loop once) + test(`${features[14].name},${features[14].tags}`, async ({ page, baseURL }) => { + test.slow(); + console.info(`[Test Page]: ${baseURL}${features[14].path}${miloLibs}`); + const { data } = features[14]; + + await test.step('step-1: Go to Marquee (large, dark ) block test page', async () => { + await page.goto(`${baseURL}${features[14].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[14].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Marquee (large, dark) background specs', async () => { + await expect(await marquee.marqueeLargeDark).toBeVisible(); + + await expect(await marquee.headingXXL).toContainText(data.h2Text); + await expect(await marquee.bodyXL).toContainText(data.bodyText); + await expect(await marquee.blueButtonXL).toContainText(data.blueButtonText); + + await expect(await marquee.backgroundVideo).toBeVisible(); + expect(await webUtil.verifyAttributes(marquee.backgroundVideo, marquee.attributes['backgroundVideo.loopOnce'])).toBeTruthy(); + + const sourceElement = await marquee.backgroundVideo.locator('source'); + expect(await sourceElement.getAttribute('src')).toContain('.mp4'); + expect(await sourceElement.getAttribute('type')).toContain('video/mp4'); + }); + + await test.step('step-3: Verify analytic attributes', async () => { + await expect(await marquee.marqueeLargeDark).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('marquee', 2)); + await expect(await marquee.blueButtonXL).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 1, data.h2Text)); + }); + + await test.step('step-4: Verify and log if any console errors', async () => { + consoleErrors.length > knownConsoleErrors.length && console.log('[Console error]:', consoleErrors); + expect.soft(consoleErrors.length).toBeLessThanOrEqual(knownConsoleErrors.length); + }); + }); +}); diff --git a/nala/blocks/media/media.page.js b/nala/blocks/media/media.page.js new file mode 100644 index 0000000000..c1cfe4e606 --- /dev/null +++ b/nala/blocks/media/media.page.js @@ -0,0 +1,79 @@ +export default class Media { + constructor(page, nth = 0) { + this.page = page; + // media types + this.media = page.locator('.media').nth(nth); + this.mediaSmall = page.locator('.media.small'); + this.mediaLargeDark = page.locator('.media.large'); + + // media details + this.detailM = this.media.locator('.detail-m'); + this.detailL = this.media.locator('.detail-l'); + + // media headings + this.headingXS = this.media.locator('.heading-xs'); + this.headingM = this.media.locator('.heading-m'); + this.headingXL = this.media.locator('.heading-xl'); + + // media body area + this.bodyS = this.media.locator('.body-s').nth(0); + this.bodyM = this.media.locator('.body-m').nth(0); + this.bodyXL = this.media.locator('.body-xl').nth(0); + this.bodyTextM = this.media.locator('p:nth-of-type(2)'); + this.bodyTextS = this.media.locator('p:nth-of-type(2)'); + + // media actions area + this.actionArea = this.media.locator('.action-area'); + this.outlineButton = this.media.locator('.con-button.outline'); + this.blueButton = this.media.locator('.con-button.blue'); + + // media image + this.mediaImage = this.media.locator('.image'); + this.mediaImg = this.mediaImage.locator('img'); + + // background video + this.backgroundVideo = this.media.locator('div video'); + this.backgroundVideoDesktop = this.media.locator('div .desktop-only video'); + + // media attributes + this.attributes = { + 'media.small': { + image: { + loading: 'eager', + fetchpriority: 'high', + width: '400', + height: '300', + }, + }, + 'media.large': { + image: { + loading: 'lazy', + width: '700', + height: '525', + }, + }, + 'backgroundVideo.inline': { + playsinline: '', + autoplay: '', + loop: '', + muted: '', + }, + 'backgroundVideo.loopOnce': { + playsinline: '', + autoplay: '', + muted: '', + }, + 'backgroundVideo.controls': { + controls: '', + autoplay: '', + loop: '', + muted: '', + }, + analytics: { + 'media.daa-lh': { 'daa-lh': /b[1-9]|media|default|default/ }, + 'section.daa-lh': { 'daa-lh': /s[1-9]/ }, + 'content.daa-lh': { 'daa-lh': /b[1-9]|content|default|default/ }, + }, + }; + } +} diff --git a/nala/blocks/media/media.spec.js b/nala/blocks/media/media.spec.js new file mode 100644 index 0000000000..015b928cac --- /dev/null +++ b/nala/blocks/media/media.spec.js @@ -0,0 +1,66 @@ +module.exports = { + BlockName: 'Media Block', + features: [ + { + tcid: '0', + name: '@Media (small)', + path: '/drafts/nala/blocks/media/media-small', + data: { + detailText: 'Detail M 12/15', + h2Text: 'Heading XS 18/22 Media (small)', + bodyText: 'Body S 16/24 Lorem ipsum dolor sit amet', + blueButtonText: 'Learn More', + outlineButtonText: 'Watch the Video', + }, + tags: '@media @media-small @smoke @regression @milo', + }, + { + tcid: '1', + name: '@Media', + path: '/drafts/nala/blocks/media/media', + data: { + detailText: 'Detail M 12/15', + h2Text: 'Heading M 24/30 Media', + bodyText: 'Body S 16/24 Lorem ipsum dolor sit amet', + blueButtonText: 'Learn More', + }, + tags: '@media @smoke @regression @milo', + }, + { + tcid: '2', + name: '@media (large, dark)', + path: '/drafts/nala/blocks/media/media-large-dark', + data: { + detailText: 'Detail L 16/20', + h2Text: 'Heading XL 36/45 Media (large, dark)', + bodyText: 'Body M 18/27 Lorem ipsum dolor sit amet,', + blueButtonText: 'Learn More', + }, + tags: '@media @media-large-dark @smoke @regression @milo', + }, + { + tcid: '3', + name: '@media (large, dark) video, autoplay infinite looping', + path: '/drafts/nala/blocks/media/media-video-autoplay-infinite-loop', + data: { + detailText: 'Detail L 16/20', + h2Text: 'Heading XL 36/45 Media (large, dark)', + bodyText: 'Body M 18/27 Lorem ipsum dolor sit amet,', + blueButtonText: 'Learn More', + }, + tags: '@media @media-video @smoke @regression @milo', + }, + { + tcid: '4', + name: '@media video, autoplay loop once', + path: '/drafts/nala/blocks/media/media-video-autoplay-loop-once', + data: { + detailText: 'Detail L 16/20', + h2Text: 'Heading XL 36/45 Media (large, dark)', + bodyText: 'Body M 18/27 Lorem ipsum dolor sit amet,', + blueButtonText: 'Learn More', + }, + tags: '@media @media-video @smoke @regression @milo', + }, + ], +}; diff --git a/nala/blocks/media/media.test.js b/nala/blocks/media/media.test.js new file mode 100644 index 0000000000..b76bc03e4e --- /dev/null +++ b/nala/blocks/media/media.test.js @@ -0,0 +1,165 @@ +import { expect, test } from '@playwright/test'; +import WebUtil from '../../libs/webutil.js'; +import { features } from './media.spec.js'; +import MediaBlock from './media.page.js'; + +let webUtil; +let media; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo Media Block test suite', () => { + test.beforeEach(async ({ page }) => { + webUtil = new WebUtil(page); + media = new MediaBlock(page); + }); + + // Test 0 : Media (small) + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + const { data } = features[0]; + + await test.step('step-1: Go to Media (small) block test page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('step-2: Verify media (small) block specs', async () => { + await expect(media.mediaSmall).toBeVisible(); + + await expect(await media.detailM).toContainText(data.detailText); + await expect(await media.headingXS).toContainText(data.h2Text); + await expect(await media.bodyS).toContainText(data.bodyText); + await expect(await media.outlineButton).toContainText(data.outlineButtonText); + await expect(await media.blueButton).toContainText(data.blueButtonText); + + await expect(await media.mediaImage).toBeVisible(); + expect(await webUtil.verifyAttributes(media.mediaImg, media.attributes['media.small'].image)).toBeTruthy(); + }); + + await test.step('step-3: Verify analytics attributes', async () => { + await expect(await media.mediaSmall).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('media', 1)); + await expect(await media.blueButton).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 1, data.h2Text)); + }); + }); + + // Test 1 : Media + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + const { data } = features[1]; + + await test.step('step-1: Go to media block test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify media block specs', async () => { + await expect(media.media).toBeVisible(); + + await expect(await media.detailM).toContainText(data.detailText); + await expect(await media.headingM).toContainText(data.h2Text); + await expect(await media.bodyS).toContainText(data.bodyText); + await expect(await media.blueButton).toContainText(data.blueButtonText); + + await expect(await media.mediaImage).toBeVisible(); + expect(await webUtil.verifyAttributes(media.mediaImg, media.attributes['media.small'].image)).toBeTruthy(); + }); + + await test.step('step-3: Verify analytics attributes', async () => { + await expect(await media.media).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('media', 1)); + await expect(await media.blueButton).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 1, data.h2Text)); + }); + }); + + // Test 2 : Media (large, dark) + test(`${features[2].name},${features[2].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + const { data } = features[2]; + + await test.step('step-1: Go to media block test page', async () => { + await page.goto(`${baseURL}${features[2].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[2].path}${miloLibs}`); + }); + + await test.step('step-2: Verify media (large, dark) block specs', async () => { + await expect(media.mediaLargeDark).toBeVisible(); + + await expect(await media.detailL).toContainText(data.detailText); + await expect(await media.headingXL).toContainText(data.h2Text); + await expect(await media.bodyM).toContainText(data.bodyText); + await expect(await media.blueButton).toContainText(data.blueButtonText); + + await expect(await media.mediaImage).toBeVisible(); + expect(await webUtil.verifyAttributes(media.mediaImg, media.attributes['media.large'].image)).toBeTruthy(); + }); + + await test.step('step-3: Verify analytics attributes', async () => { + await expect(await media.mediaLargeDark).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('media', 1)); + await expect(await media.blueButton).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 1, data.h2Text)); + }); + }); + + // Test 3 : Media (large, dark) video, autoplay infinite looping + test(`${features[3].name},${features[3].tags}`, async ({ page, baseURL, browserName }) => { + test.skip(browserName === 'webkit', 'This feature is failing on Webkit browsers'); + test.slow(); + console.info(`[Test Page]: ${baseURL}${features[3].path}${miloLibs}`); + const { data } = features[3]; + + await test.step('step-1: Go to media block test page', async () => { + await page.goto(`${baseURL}${features[3].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[3].path}${miloLibs}`); + }); + + await test.step('step-2: Verify media (large, dark) block specs', async () => { + await expect(media.mediaLargeDark).toBeVisible(); + + await expect(await media.detailL).toContainText(data.detailText); + await expect(await media.headingXL).toContainText(data.h2Text); + await expect(await media.bodyM).toContainText(data.bodyText); + await expect(await media.blueButton).toContainText(data.blueButtonText); + + await expect(await media.backgroundVideo).toBeVisible(); + expect(await webUtil.verifyAttributes(media.backgroundVideo, media.attributes['backgroundVideo.inline'])).toBeTruthy(); + }); + + await test.step('step-3: Verify analytics attributes', async () => { + await expect(await media.mediaLargeDark).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('media', 2)); + await expect(await media.blueButton).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 1, data.h2Text)); + }); + }); + + // Test 5 : Media (large, dark) video, autoplay loop once + test(`${features[4].name},${features[4].tags}`, async ({ page, baseURL }) => { + test.slow(); + console.info(`[Test Page]: ${baseURL}${features[4].path}${miloLibs}`); + const { data } = features[4]; + + await test.step('step-1: Go to media block test page', async () => { + await page.goto(`${baseURL}${features[4].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[4].path}${miloLibs}`); + }); + + await test.step('step-2: Verify media (large, dark) block specs', async () => { + await expect(media.mediaLargeDark).toBeVisible(); + + await expect(await media.detailL).toContainText(data.detailText); + await expect(await media.headingXL).toContainText(data.h2Text); + await expect(await media.bodyM).toContainText(data.bodyText); + await expect(await media.blueButton).toContainText(data.blueButtonText); + + await expect(await media.backgroundVideo).toBeVisible(); + expect(await webUtil.verifyAttributes(media.backgroundVideo, media.attributes['backgroundVideo.loopOnce'])).toBeTruthy(); + }); + + await test.step('step-3: Verify analytics attributes', async () => { + await expect(await media.mediaLargeDark).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('media', 2)); + await expect(await media.blueButton).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.blueButtonText, 1, data.h2Text)); + }); + }); +}); diff --git a/nala/blocks/merchcard/merchcard.pages.js b/nala/blocks/merchcard/merchcard.pages.js new file mode 100644 index 0000000000..4bbac09c5c --- /dev/null +++ b/nala/blocks/merchcard/merchcard.pages.js @@ -0,0 +1,99 @@ +export default class Merchcard { + constructor(page, nth = 0) { + this.page = page; + + // merch card locators + this.merchCard = this.page.locator('.merch-card').nth(nth); + this.segment = this.page.locator('.merch-card.segment').nth(nth); + this.sepcialOffers = this.page.locator('.merch-card.special-offers').nth(nth); + this.plans = this.page.locator('.merch-card.plans').nth(nth); + this.catalog = this.page.locator('.merch-card.catalog').nth(nth); + + // inline price and strikethrough price + this.inlinePrice1 = this.merchCard.locator('span.placeholder-resolved').nth(0); + this.inlinePrice2 = this.merchCard.locator('span.placeholder-resolved').nth(1); + this.price = this.inlinePrice1.locator('.price'); + this.priceCurrencySymbol = this.inlinePrice1.locator('.price-currency-symbol'); + this.priceInteger = this.inlinePrice1.locator('.price-integer'); + this.priceDecimalDelimiter = this.inlinePrice1.locator('.price-decimals-delimiter'); + this.priceDecimals = this.inlinePrice1.locator('.price-decimals'); + this.priceRecurrence = this.inlinePrice1.locator('.price-recurrence'); + + this.strikethroughPrice = this.inlinePrice2.locator('.price'); + this.strikethroughPriceCurrencySymbol = this.inlinePrice2.locator('.price-currency-symbol'); + this.strikethroughPriceInteger = this.inlinePrice2.locator('.price-integer'); + this.strikethroughPriceDecimalDelimiter = this.inlinePrice2.locator('.price-decimals-delimiter'); + this.strikethroughPriceDecimals = this.inlinePrice2.locator('.price-decimals'); + this.strikethroughPriceRecurrence = this.inlinePrice2.locator('.price-recurrence'); + + // merch-card segment locators + this.segmentRibbon = this.merchCard.locator('.segment-badge'); + this.segmentTitle = this.segment.locator('h3[slot="heading-xs"]').nth(0); + this.segmentDescription1 = this.segment.locator('div[slot="body-xs"] p').nth(0); + this.segmentDescription2 = this.segment.locator('div[slot="body-xs"] p').nth(1); + + this.linkText1 = this.segmentDescription2.locator('a').nth(0); + this.linkText2 = this.segmentDescription2.locator('a').nth(1); + + // merch-card special offers + this.sepcialOffersImage = this.sepcialOffers.locator('div[slot="bg-image"] img'); + this.sepcialOffersRibbon = this.merchCard.locator('.special-offers-badge'); + this.sepcialOffersTitleH4 = this.sepcialOffers.locator('h4[slot="detail-m"]').nth(0); + this.sepcialOffersTitleH5 = this.sepcialOffers.locator('h5[slot="body-xs"]'); + this.sepcialOffersTitleH3 = this.sepcialOffers.locator('h3[slot="heading-xs"]').nth(0); + + this.sepcialOffersDescription1 = this.sepcialOffers.locator('div[slot="body-xs"] p').nth(1); + this.sepcialOffersDescription2 = this.sepcialOffers.locator('div[slot="body-xs"] p').nth(2); + this.sepcialOffersDescription3 = this.sepcialOffers.locator('div[slot="body-xs"] p').nth(3); + this.sepcialOffersLinkText3 = this.sepcialOffersDescription3.locator('a').nth(0); + + this.seeTermsTextLink = this.merchCard.locator('a:has-text("See terms")'); + + // merch-card plans locators + this.productIcon = this.plans.locator('img'); + this.plansRibbon = this.plans.locator('.plans-badge'); + this.plansCardTitleH3 = this.plans.locator('h3[slot="heading-xs"]'); + this.plansCardTitleH4 = this.plans.locator('h4[slot="body-xxs"]'); + this.plansCardTitleH5 = this.plans.locator('h5[slot="body-xxs"]'); + this.plansCardDescription1 = this.plans.locator('div[slot="body-xs"] p').nth(1); + this.plansCardDescription2 = this.plans.locator('div[slot="body-xs"] p').nth(2); + this.plansCardDescription3 = this.plans.locator('div[slot="body-xs"] p').nth(3); + this.seePlansTextLink = this.merchCard.locator('a:has-text("See plan & pricing details")'); + + // merch-card catalog + this.catalogProductIcon = this.catalog.locator('#shadow-root div.icons'); + this.catalogRibbon = this.catalog.locator('.catalog-badge'); + this.catalogActionMenu = this.catalog.locator('div[slot="action-menu-content"]'); + this.catalogActionMenuList = this.catalogActionMenu.locator('ul li'); + this.catalogActionMenuPText1 = this.catalogActionMenu.locator('p').nth(0); + this.catalogActionMenuPText2 = this.catalogActionMenu.locator('p').nth(1); + this.catalogActionMenuPText3 = this.catalogActionMenu.locator('p').nth(2); + this.catalogActionMenuPText4 = this.catalogActionMenu.locator('p').nth(3); + this.catalogActionMenuPText5 = this.catalogActionMenu.locator('p ').nth(4); + this.systemRequirementTextLink = this.merchCard.locator('a:has-text("See system requirements")'); + + this.catalogCardTitleH3 = this.catalog.locator('h3[slot="heading-xs"]'); + this.catalogCardTitleH4 = this.catalog.locator('h4[slot="body-xxs"]'); + this.catalogCardDescription2 = this.catalog.locator('div[slot="body-xs"] p').nth(2); + this.seeWhatsIncludedTextLink = this.merchCard.locator('a:has-text("See what’s included")'); + this.learnMoreTextLink = this.merchCard.locator('a:has-text("Learn more")'); + + // merch-card footer sections + this.footer = this.merchCard.locator('div[slot="footer"]'); + this.footerCheckbox = this.page.locator('#stock-checkbox input[type="checkbox"]'); + this.footerCheckboxLabel = this.merchCard.locator('#stock-checkbox'); + this.secureTransactionIcon = this.merchCard.locator('.secure-transaction-icon'); + this.secureTransactionLabel = this.merchCard.locator('.secure-transaction-label'); + this.footerOutlineButton = this.merchCard.locator('a.con-button.outline'); + this.footerOutlineButton2 = this.merchCard.locator('a.con-button.outline').nth(1); + this.footerBlueButton = this.merchCard.locator('a.con-button.blue').nth(0); + this.footerBlueButton2 = this.merchCard.locator('a.con-button.blue').nth(1); + + // merch-card attributes + this.attributes = { + segmentRibbon: { style: /background-color:\s*#EDCC2D;\s*color:\s*#000000;\s*/ }, + specialOfferRibbon: { style: /background-color:\s* #F68D2E;\s*color:\s*#000000;\s*/ }, + plansRibbon: { style: /background-color:\s*#EDCC2D;\s*color:\s*#000000;\s*/ }, + }; + } +} diff --git a/nala/blocks/merchcard/merchcard.spec.js b/nala/blocks/merchcard/merchcard.spec.js new file mode 100644 index 0000000000..bfc98221f0 --- /dev/null +++ b/nala/blocks/merchcard/merchcard.spec.js @@ -0,0 +1,194 @@ +/* eslint-disable max-len */ + +module.exports = { + FeatureName: 'Merch Card Block', + features: [ + { + tcid: '0', + name: '@Merch-card (Segment)', + path: '/drafts/nala/blocks/merch-card/merch-card-segment', + data: { + title: 'Individuals', + price: 'US$59.99/mo', + strikethroughPrice: 'US$89.99/mo', + description: 'Save over 25% on 20+ apps, including Photoshop, Illustrator, and more. First year only. Ends 27', + link1Text: 'See what\'s included', + link2Text: 'Learn more', + footerOutlineButtonText: 'Learn More', + footerBlueButtonText: 'Save now', + }, + tags: '@merch-card @smoke @regression @milo', + }, + { + tcid: '1', + name: '@Merch-card (Segment) with Badge', + path: '/drafts/nala/blocks/merch-card/merch-card-segment-with-badge', + data: { + title: 'Individuals', + badgeText: 'Best value', + price: 'US$59.99/mo', + strikethroughPrice: 'US$89.99/mo', + description: 'Save over 25% on 20+ apps, including Photoshop, Illustrator, and more. First year only. Ends 27', + link1Text: 'See what\'s included', + link2Text: 'Learn more', + footerOutlineButtonText: 'Learn More', + footerBlueButtonText: 'Save now', + }, + tags: '@merch-card @smoke @regression @milo', + }, + { + tcid: '2', + name: '@Merch-card (special-offers) ', + path: '/drafts/nala/blocks/merch-card/merch-card-special-offers', + data: { + titleH4: 'INDIVIDUALS', + titleH3: 'Save over 30% on Creative Cloud All Apps.', + description1: 'Get 20+ creative apps and save big when you choose a yearly plan instead of a monthly plan.', + description2: 'Create gorgeous images, rich graphics, and incredible art. Save 10% for the first year. Ends Mar 20.', + footerBlueButtonText: 'Save now', + }, + tags: '@merch-card @smoke @regression @milo', + }, + { + tcid: '3', + name: '@Merch-card (special-offers) with badge', + path: '/drafts/nala/blocks/merch-card/merch-card-special-offers-with-badge', + data: { + titleH4: 'INDIVIDUALS', + titleH3: 'Get 10% off Photoshop.', + badgeText: 'LIMITED TIME WEEKLY OFFER', + price: 'US$383.88/yr', + strikethroughPrice: 'US$32.99/mo', + description: 'Create gorgeous images, rich graphics, and incredible art. Save 10% for the first year. Ends Mar 20.', + link1Text: 'See terms', + footerOutlineButtonText: 'Learn More', + footerBlueButtonText: 'Save now', + }, + tags: '@merch-card @smoke @regression @milo', + }, + { + tcid: '4', + name: '@Merch-card (plans)', + path: '/drafts/nala/blocks/merch-card/merch-cards-plans', + data: { + titleH3: 'Creative Cloud All Apps', + titleH5: 'Desktop', + price: 'US$79.99/mo', + description: 'Get 20+ Creative Cloud apps including Photoshop, Illustrator, Adobe Express, Premiere Pro, and Acrobat Pro. (Substance 3D apps are not included.)', + link1Text: 'See plan & pricing details', + footerBlueButtonText: 'Buy now', + }, + tags: '@merch-card @smoke @regression @milo', + }, + { + tcid: '5', + name: '@Merch-card (plans) with badge', + path: '/drafts/nala/blocks/merch-card/merch-card-plans-with-badge', + data: { + titleH3: 'Creative Cloud All Apps', + titleH4: 'Desktop', + badgeText: 'Best value', + price: 'US$79.99/mo', + description: 'Get 20+ Creative Cloud apps including Photoshop, Illustrator, Adobe Express, Premiere Pro, and Acrobat Pro. (Substance 3D apps are not included.)', + link1Text: 'See plan & pricing details', + footerBlueButtonText: 'Buy now', + }, + tags: '@merch-card @smoke @regression @milo', + }, + { + tcid: '6', + name: '@Merch-card (plans, secure)', + path: '/drafts/nala/blocks/merch-card/merch-card-plans-secure', + data: { + titleH3: 'Acrobat', + titleH5: 'Desktop + Mobile', + price: 'US$79.99/mo', + description: 'The complete PDF solution for working anywhere (includes desktop, web, and mobile access).', + link1Text: 'See plan & pricing details', + checkboxLabel: 'Add a 30-day free trial of Adobe Stock.*', + secureLabel: /Secure transaction/i, + footerBlueButton1Text: 'Buy now', + footerBlueButton2Text: 'Buy now', + footerOutlineButtonText: 'Free trial', + }, + tags: '@merch-card @smoke @regression @milo', + }, + { + tcid: '7', + name: '@Merch-card (plans, secure) with badge', + path: '/drafts/nala/blocks/merch-card/merch-cards-plans-secure-with-badge', + data: { + titleH3: 'Creative Cloud All Apps', + titleH5: 'Desktop', + badgeText: 'Best value', + price: 'US$79.99/mo', + description: 'Get 20+ Creative Cloud apps including Photoshop, Illustrator, Adobe Express, Premiere Pro, and Acrobat Pro. (Substance 3D apps are not included.)', + link1Text: 'See plan & pricing details', + checkboxLabel: 'Add a 30-day free trial of Adobe Stock.*', + secureLabel: /Secure transaction/i, + footerBlueButton1Text: /Buy now/i, + footerOutlineButtonText: 'Free trial', + }, + tags: '@merch-card @smoke @regression @milo', + }, + { + tcid: '8', + name: '@Merch-card (catalog)', + path: '/drafts/nala/blocks/merch-card/merch-cards-catalog', + data: { + titleH3: 'Creative Cloud All Apps', + titleH4: 'Desktop', + price: 'US$79.99/mo', + description: 'Get 20+ creative apps including Photoshop, Illustrator, Premiere Pro, Acrobat Pro, and Adobe Express. (Substance 3D apps are not included.)', + link1Text: 'See what’s included', + link2Text: 'Learn more', + footerBlueButton1Text: 'Buy now', + footerOutlineButtonText: 'free trial', + }, + tags: '@merch-card @smoke @regression @milo', + }, + { + tcid: '9', + name: '@Merch-card (catalog) with badge', + path: '/drafts/nala/blocks/merch-card/march-cards-catalog-with-badge', + data: { + titleH3: 'Creative Cloud All Apps', + titleH4: 'Desktop', + badgeText: 'Most popular', + badgeBgColor: '#EDCC2D', + badgeColor: '#000000', + price: 'US$79.99/mo', + description: 'Get 20+ creative apps including Photoshop, Illustrator, Premiere Pro, Acrobat Pro, and Adobe Express. (Substance 3D apps are not included.)', + link1Text: 'See what’s included', + link2Text: 'Learn more', + footerBlueButton1Text: 'Buy now', + footerOutlineButtonText: 'free trial', + }, + tags: '@merch-card @smoke @regression @milo', + }, + { + tcid: '10', + name: '@Merch-card (catalog) with more info and badge', + path: '/drafts/nala/blocks/merch-card/merch-cards-catalog-with-more-info-and-badge', + data: { + titleH3: 'Creative Cloud All Apps', + titleH4: 'Desktop', + badgeText: 'Most popular', + badgeBgColor: '#EDCC2D', + badgeColor: '#000000', + actionMenuListCount: 4, + actionMenuText1: 'Best for', + actionMenuText2: 'Storage', + actionMenuText3: '100 GB of cloud storage', + actionMenuText4: '100 GB of cloud storage', + price: 'US$79.99/mo', + description: 'Get 20+ creative apps including Photoshop, Illustrator, Premiere Pro, Acrobat Pro, and Adobe Express. (Substance 3D apps are not included.)', + link1Text: 'See what’s included', + link2Text: 'Learn more', + footerBlueButton1Text: 'Buy now', + footerOutlineButtonText: 'free trial', + }, + tags: '@merch-card @smoke @regression @milo', + }, + ], +}; diff --git a/nala/blocks/merchcard/merchcard.test.js b/nala/blocks/merchcard/merchcard.test.js new file mode 100644 index 0000000000..2874e61728 --- /dev/null +++ b/nala/blocks/merchcard/merchcard.test.js @@ -0,0 +1,392 @@ +import { expect, test } from '@playwright/test'; +import { features } from './merchcard.spec.js'; +import MerchCard from './merchcard.pages.js'; + +let merchCard; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo Merchcard block test suite', () => { + test.beforeEach(async ({ page, browserName }) => { + merchCard = new MerchCard(page); + if (browserName === 'chromium') { + await page.setExtraHTTPHeaders({ 'sec-ch-ua': '"Chromium";v="123", "Not:A-Brand";v="8"' }); + } + }); + + // Test 0 : Merch Card (Segment) + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + const { data } = features[0]; + + await test.step('step-1: Go to Merch Card feature test page', async () => { + await page.goto(`${baseURL}${features[0].path}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}`); + }); + + await test.step('step-2: Verify Merch Card content/specs', async () => { + await expect(await merchCard.segment).toBeVisible(); + await expect(await merchCard.segmentTitle).toContainText(data.title); + // await expect(await merchCard.price).toContainText(data.price); + // await expect(await merchCard.strikethroughPrice).toContainText(data.strikethroughPrice); + + await expect(await merchCard.segmentDescription1).toContainText(data.description); + await expect(await merchCard.linkText1).toContainText(data.link1Text); + await expect(await merchCard.linkText2).toContainText(data.link2Text); + await expect(await merchCard.footer).toBeVisible(); + await expect(await merchCard.footerOutlineButton).toBeVisible(); + await expect(await merchCard.footerOutlineButton).toContainText(data.footerOutlineButtonText); + await expect(await merchCard.footerBlueButton).toBeVisible(); + await expect(await merchCard.footerBlueButton).toContainText(data.footerBlueButtonText); + }); + }); + + // Test 1 : Merch Card (Segment) with Badge + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + const { data } = features[1]; + + await test.step('step-1: Go to Merch Card feature test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Merch Card with Badge content/specs', async () => { + await expect(await merchCard.segment).toBeVisible(); + await expect(await merchCard.segmentTitle).toContainText(data.title); + + await expect(await merchCard.segmentRibbon).toBeVisible(); + await expect(await merchCard.segmentRibbon).toContainText(data.badgeText); + + // await expect(await merchCard.price).toContainText(data.price); + // await expect(await merchCard.strikethroughPrice).toContainText(data.strikethroughPrice); + + await expect(await merchCard.segmentDescription1).toContainText(data.description); + await expect(await merchCard.linkText1).toContainText(data.link1Text); + await expect(await merchCard.linkText2).toContainText(data.link2Text); + + await expect(await merchCard.footer).toBeVisible(); + await expect(await merchCard.footerOutlineButton).toBeVisible(); + await expect(await merchCard.footerOutlineButton).toContainText(data.footerOutlineButtonText); + + await expect(await merchCard.footerBlueButton).toBeVisible(); + await expect(await merchCard.footerBlueButton).toContainText(data.footerBlueButtonText); + }); + + await test.step('step-3: Verify Merch Card attributes', async () => { + await expect(await merchCard.segmentRibbon).toHaveAttribute('style', merchCard.attributes.segmentRibbon.style); + }); + }); + + // Test 2 : Merch Card (Special Offers) + test(`${features[2].name},${features[2].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + const { data } = features[2]; + + await test.step('step-1: Go to Merch Card feature test page', async () => { + await page.goto(`${baseURL}${features[2].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[2].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Merch Card special offers content/specs', async () => { + await expect(await merchCard.sepcialOffers).toBeVisible(); + await expect(await merchCard.sepcialOffersImage).toBeVisible(); + + await expect(await merchCard.sepcialOffersTitleH4).toBeVisible(); + await expect(await merchCard.sepcialOffersTitleH4).toContainText(data.titleH4); + await expect(await merchCard.sepcialOffersTitleH3).toContainText(data.titleH3); + + await expect(await merchCard.sepcialOffersDescription1).toContainText(data.description1); + await expect(await merchCard.sepcialOffersDescription2).toContainText(data.description2); + + await expect(await merchCard.footer).toBeVisible(); + await expect(await merchCard.footerBlueButton).toBeVisible(); + await expect(await merchCard.footerBlueButton).toContainText(data.footerBlueButtonText); + }); + }); + + // Test 3 : Merch Card (Special Offers) with badge + test(`${features[3].name},${features[3].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[3].path}${miloLibs}`); + const { data } = features[3]; + + await test.step('step-1: Go to Merch Card feature test page', async () => { + await page.goto(`${baseURL}${features[3].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[3].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Merch Card special offers content/specs', async () => { + await expect(await merchCard.sepcialOffers).toBeVisible(); + await expect(await merchCard.sepcialOffersImage).toBeVisible(); + + await expect(await merchCard.sepcialOffersRibbon).toBeVisible(); + await expect(await merchCard.sepcialOffersRibbon).toContainText(data.badgeText); + + await expect(await merchCard.sepcialOffersTitleH3).toContainText(data.titleH3); + await expect(await merchCard.sepcialOffersTitleH4).toContainText(data.titleH4); + + await expect(await merchCard.sepcialOffersDescription1).toContainText(data.description); + await expect(await merchCard.seeTermsTextLink).toContainText(data.link1Text); + + await expect(await merchCard.footer).toBeVisible(); + await expect(await merchCard.footerOutlineButton).toBeVisible(); + await expect(await merchCard.footerOutlineButton).toContainText(data.footerOutlineButtonText); + + await expect(await merchCard.footerBlueButton).toBeVisible(); + await expect(await merchCard.footerBlueButton).toContainText(data.footerBlueButtonText); + }); + + await test.step('step-3: Verify Merch Card attributes', async () => { + await expect(await merchCard.sepcialOffersRibbon).toHaveAttribute( + 'style', + merchCard.attributes.specialOfferRibbon.style, + ); + }); + }); + + // Test 4 : Merch Card (plans) + test(`${features[4].name},${features[4].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[4].path}${miloLibs}`); + const { data } = features[4]; + + await test.step('step-1: Go to Merch Card feature test page', async () => { + await page.goto(`${baseURL}${features[4].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[4].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Merch Card special offers content/specs', async () => { + await expect(await merchCard.plans).toBeVisible(); + await expect(await merchCard.productIcon).toBeVisible(); + + await expect(await merchCard.plansCardTitleH3).toContainText(data.titleH3); + await expect(await merchCard.plansCardTitleH5).toContainText(data.titleH5); + + // await expect(await merchCard.price).toContainText(data.price); + await expect(await merchCard.plansCardDescription1).toContainText(data.description); + await expect(await merchCard.seePlansTextLink).toContainText(data.link1Text); + + await expect(await merchCard.footer).toBeVisible(); + + await expect(await merchCard.footerBlueButton).toBeVisible(); + await expect(await merchCard.footerBlueButton).toContainText(data.footerBlueButtonText); + }); + }); + + // Test 5 : Merch Card (plans) with badge + test(`${features[5].name},${features[5].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[5].path}${miloLibs}`); + const { data } = features[5]; + + await test.step('step-1: Go to Merch Card feature test page', async () => { + await page.goto(`${baseURL}${features[5].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[5].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Merch Card special offers content/specs', async () => { + await expect(await merchCard.plans).toBeVisible(); + await expect(await merchCard.productIcon).toBeVisible(); + + await expect(await merchCard.plansRibbon).toBeVisible(); + await expect(await merchCard.plansRibbon).toContainText(data.badgeText); + + await expect(await merchCard.plansCardTitleH3).toContainText(data.titleH3); + await expect(await merchCard.plansCardTitleH4).toContainText(data.titleH4); + + // await expect(await merchCard.price).toContainText(data.price); + await expect(await merchCard.plansCardDescription2).toContainText(data.description); + await expect(await merchCard.seePlansTextLink).toContainText(data.link1Text); + + await expect(await merchCard.footer).toBeVisible(); + + await expect(await merchCard.footerBlueButton).toBeVisible(); + await expect(await merchCard.footerBlueButton).toContainText(data.footerBlueButtonText); + }); + }); + + // Test 6 : Merch Card (plans) with secure + test(`${features[6].name},${features[6].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[6].path}${miloLibs}`); + const { data } = features[6]; + + await test.step('step-1: Go to Merch Card feature test page', async () => { + await page.goto(`${baseURL}${features[6].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[6].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Merch Card special offers content/specs', async () => { + await expect(await merchCard.plans).toBeVisible(); + await expect(await merchCard.productIcon).toBeVisible(); + + await expect(await merchCard.plansCardTitleH3).toContainText(data.titleH3); + await expect(await merchCard.plansCardTitleH5).toContainText(data.titleH5); + + // await expect(await merchCard.price).toContainText(data.price); + await expect(await merchCard.plansCardDescription1).toContainText(data.description); + await expect(await merchCard.seePlansTextLink).toContainText(data.link1Text); + + await expect(await merchCard.footer).toBeVisible(); + + await expect(await merchCard.footerBlueButton).toBeVisible(); + await expect(await merchCard.footerBlueButton).toContainText(data.footerBlueButton1Text); + + await expect(await merchCard.secureTransactionLabel).toContainText(data.secureLabel); + }); + }); + + // Test 7 : Merch Card (plans, secure) with badge + test(`${features[7].name},${features[7].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[7].path}${miloLibs}`); + const { data } = features[7]; + + await test.step('step-1: Go to Merch Card feature test page', async () => { + await page.goto(`${baseURL}${features[7].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[7].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Merch Card special offers content/specs', async () => { + await expect(await merchCard.plans).toBeVisible(); + await expect(await merchCard.productIcon).toBeVisible(); + + await expect(await merchCard.plansRibbon).toBeVisible(); + await expect(await merchCard.plansRibbon).toContainText(data.badgeText); + + await expect(await merchCard.plansCardTitleH3).toContainText(data.titleH3); + await expect(await merchCard.plansCardTitleH5).toContainText(data.titleH5); + + // await expect(await merchCard.price).toContainText(data.price); + await expect(await merchCard.plansCardDescription1).toContainText(data.description); + await expect(await merchCard.seePlansTextLink).toContainText(data.link1Text); + + await expect(await merchCard.footer).toBeVisible(); + await expect(await merchCard.footerBlueButton).toBeVisible(); + await expect(await merchCard.footerBlueButton).toContainText(data.footerBlueButton1Text); + + await expect(await merchCard.secureTransactionLabel).toContainText(data.secureLabel); + }); + }); + + // Test 8 : Merch Card (catalog) + test(`${features[8].name},${features[8].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[8].path}${miloLibs}`); + const { data } = features[8]; + + await test.step('step-1: Go to Merch Card feature test page', async () => { + await page.goto(`${baseURL}${features[8].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[8].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Merch Card catalog content/specs', async () => { + await expect(await merchCard.catalog).toBeVisible(); + await expect(await merchCard.catalogCardTitleH3).toContainText(data.titleH3); + await expect(await merchCard.catalogCardTitleH4).toContainText(data.titleH4); + + // await expect(await merchCard.price).toContainText(data.price); + + await expect(await merchCard.catalogCardDescription2).toContainText(data.description); + await expect(await merchCard.seeWhatsIncludedTextLink).toContainText(data.link1Text); + await expect(await merchCard.learnMoreTextLink).toContainText(data.link2Text); + + await expect(await merchCard.footer).toBeVisible(); + + await expect(await merchCard.footerBlueButton).toBeVisible(); + await expect(await merchCard.footerBlueButton).toContainText(data.footerBlueButton1Text); + + await expect(await merchCard.footerOutlineButton).toBeVisible(); + await expect(await merchCard.footerOutlineButton).toContainText(data.footerOutlineButtonText); + }); + }); + + // Test 9 : Merch Card (catalog) with badge + test(`${features[9].name},${features[9].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[9].path}${miloLibs}`); + const { data } = features[9]; + + await test.step('step-1: Go to Merch Card feature test page', async () => { + await page.goto(`${baseURL}${features[9].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[9].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Merch Card catalog with badge content/specs', async () => { + await expect(await merchCard.catalog).toBeVisible(); + + await expect(await merchCard.catalog).toHaveAttribute('badge-background-color', data.badgeBgColor); + await expect(await merchCard.catalog).toHaveAttribute('badge-color', data.badgeColor); + await expect(await merchCard.catalog).toHaveAttribute('badge-text', data.badgeText); + + await expect(await merchCard.catalogCardTitleH3).toContainText(data.titleH3); + await expect(await merchCard.catalogCardTitleH4).toContainText(data.titleH4); + + // await expect(await merchCard.price).toContainText(data.price); + + await expect(await merchCard.catalogCardDescription2).toContainText(data.description); + await expect(await merchCard.seeWhatsIncludedTextLink).toContainText(data.link1Text); + await expect(await merchCard.learnMoreTextLink).toContainText(data.link2Text); + + await expect(await merchCard.footer).toBeVisible(); + + await expect(await merchCard.footerBlueButton).toBeVisible(); + await expect(await merchCard.footerBlueButton).toContainText(data.footerBlueButton1Text); + + await expect(await merchCard.footerOutlineButton).toBeVisible(); + await expect(await merchCard.footerOutlineButton).toContainText(data.footerOutlineButtonText); + }); + }); + + // Test 10 : Merch Card (catalog) with more info and badge + test(`${features[10].name},${features[10].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[10].path}${miloLibs}`); + const { data } = features[10]; + + await test.step('step-1: Go to Merch Card feature test page', async () => { + await page.goto(`${baseURL}${features[10].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[10].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Merch Card catalog with badge content/specs', async () => { + await expect(await merchCard.catalog).toBeVisible(); + + await expect(await merchCard.catalog).toHaveAttribute('badge-background-color', data.badgeBgColor); + await expect(await merchCard.catalog).toHaveAttribute('badge-color', data.badgeColor); + await expect(await merchCard.catalog).toHaveAttribute('badge-text', data.badgeText); + + await expect(await merchCard.catalogCardTitleH3).toContainText(data.titleH3); + await expect(await merchCard.catalogCardTitleH4).toContainText(data.titleH4); + + // await expect(await merchCard.price).toContainText(data.price); + + await expect(await merchCard.catalogCardDescription2).toContainText(data.description); + await expect(await merchCard.seeWhatsIncludedTextLink).toContainText(data.link1Text); + await expect(await merchCard.learnMoreTextLink).toContainText(data.link2Text); + + await expect(await merchCard.footer).toBeVisible(); + + await expect(await merchCard.footerBlueButton).toBeVisible(); + await expect(await merchCard.footerBlueButton).toContainText(data.footerBlueButton1Text); + + await expect(await merchCard.footerOutlineButton).toBeVisible(); + await expect(await merchCard.footerOutlineButton).toContainText(data.footerOutlineButtonText); + }); + + await test.step('step-3: click more info link and verify action menu list', async () => { + await merchCard.catalog.hover(); + await merchCard.catalog.click(); + await page.waitForTimeout(1000); + + await expect(await merchCard.catalogActionMenuList).toHaveCount(data.actionMenuListCount); + await expect(await merchCard.catalogActionMenuPText1).toContainText(data.actionMenuText1); + await expect(await merchCard.catalogActionMenuPText2).toContainText(data.actionMenuText2); + await expect(await merchCard.catalogActionMenuPText3).toContainText(data.actionMenuText3); + }); + }); +}); diff --git a/nala/blocks/modal/modal.page.js b/nala/blocks/modal/modal.page.js new file mode 100644 index 0000000000..4fc19bff09 --- /dev/null +++ b/nala/blocks/modal/modal.page.js @@ -0,0 +1,62 @@ +export default class Modal { + constructor(page) { + this.page = page; + // modal locators + this.dialog = this.page.locator('.dialog-modal'); + this.modal = this.page.locator('.dialog-modal'); + this.fragment = this.modal.locator('.fragment'); + this.headingXL = this.page.locator('.heading-xl'); + this.bodyM = this.page.locator('.body-m').nth(2); + this.modalCloseButton = this.modal.locator('.dialog-close'); + this.dialogCloseButton = this.modal.locator('.dialog-close').nth(0); + this.marqueeLight = this.dialog.locator('.marquee.light'); + this.modelSelector = '.dialog-modal'; + + // text block + this.textBlock = this.modal.locator('.text').nth(0); + this.textBlockHeading = this.textBlock.locator('h2'); + this.textBlockBodyM = this.textBlock.locator('.body-m'); + + // media block + this.mediaBlock = this.modal.locator('.media').nth(0); + this.mediaBlockdetailM = this.mediaBlock.locator('.detail-m'); + this.mediaBlockTextHeading = this.mediaBlock.locator('h2'); + this.mediaBlockTextBodyS = this.mediaBlock.locator('.body-s').first(); + + // video block + this.video = this.modal.locator('video').nth(0); + + // modal contents attributes + this.attributes = { + 'modal-link': { class: 'modal link-block ' }, + 'video.inline': { + playsinline: '', + autoplay: '', + loop: '', + muted: '', + }, + }; + } + + /** + * Gets the modal link based on the modal id. + * Waits for the link to be visible before returning the locator. + * @param {Object} data - The data object containing modalId. + * @param {number} [timeout=3000] - Optional timeout for waiting. + * @returns {Promise} - The locator for the modal link. + * @throws Will throw an error if the link is not found within the timeout. + */ + async getModalLink(modalId, timeout = 1000) { + if (!modalId) { + throw new Error('Invalid data, "modalId" property is required.'); + } + const selector = `a[href="#${modalId}"]`; + const modalLink = this.page.locator(selector); + try { + await modalLink.waitFor({ state: 'visible', timeout }); + return modalLink; + } catch (error) { + throw new Error(`The modal link with selector "${selector}" could not be found within ${timeout}ms.`); + } + } +} diff --git a/nala/blocks/modal/modal.spec.js b/nala/blocks/modal/modal.spec.js new file mode 100644 index 0000000000..432e754edb --- /dev/null +++ b/nala/blocks/modal/modal.spec.js @@ -0,0 +1,46 @@ +module.exports = { + FeatureName: 'Modal Block', + features: [ + { + tcid: '0', + name: '@Modal Text', + path: '/drafts/nala/blocks/modal/modal-text-intro', + data: { + modalId: 'modal-text-intro', + fragment: 'text', + contentType: 'text (intro)', + h2Text: 'Text (intro)', + bodyText: 'Body M Regular Lorem ipsum dolor sit amet', + }, + tags: '@modal @smoke @regression @milo', + }, + { + tcid: '1', + name: '@Modal Media', + path: '/drafts/nala/blocks/modal/modal-media', + data: { + modalId: 'modal-media', + detailText: 'Detail M 12/15', + fragment: 'media', + contentType: 'media', + h2Text: 'Heading M 24/30 Media', + bodyText: 'Body S 16/24 Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed', + }, + tags: '@modal @smoke @regression @milo', + }, + { + tcid: '2', + name: '@Modal Autoplay Video', + path: '/drafts/nala/blocks/modal/modal-autoplay-video', + data: { + modalId: 'modal-video-autoplay', + detailText: 'Detail M 12/15', + fragment: 'media', + contentType: 'media', + h2Text: 'Heading M 24/30 Media', + bodyText: 'Body S 16/24 Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed', + }, + tags: '@modal @smoke @regression @milo', + }, + ], +}; diff --git a/nala/blocks/modal/modal.test.js b/nala/blocks/modal/modal.test.js new file mode 100644 index 0000000000..be47c5be00 --- /dev/null +++ b/nala/blocks/modal/modal.test.js @@ -0,0 +1,114 @@ +import { expect, test } from '@playwright/test'; +import WebUtil from '../../libs/webutil.js'; +import { features } from './modal.spec.js'; +import ModalBlock from './modal.page.js'; + +let modal; +let webUtil; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo Modal feature test suite', () => { + test.beforeEach(async ({ page }) => { + modal = new ModalBlock(page); + webUtil = new WebUtil(page); + }); + + // Test 0 : Modal with Text block + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + + await test.step('step-1: Go to Modal feature test page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Modal text fragment content/specs', async () => { + const { data } = features[0]; + const modalLink = await modal.getModalLink(data.modalId); + await expect(await modalLink).toBeVisible(); + await expect(await modalLink).toHaveAttribute('class', modal.attributes['modal-link'].class); + + // click the modal link + await modalLink.click(); + await expect(await modal.dialog).toBeVisible(); + + await expect(await modal.textBlock).toBeVisible(); + await expect(await modal.textBlockHeading).toContainText(data.h2Text); + await expect(await modal.textBlockBodyM).toContainText(data.bodyText); + + expect(await WebUtil.isModalInViewport(modal.page, modal.modalSelector)).toBeTruthy(); + + // click the modal close button + await expect(await modal.dialogCloseButton).toBeVisible(); + await modal.dialogCloseButton.click(); + }); + }); + + // Test 1 : Modal with Media block + test(`${features[1].name}, ${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + + await test.step('step-1: Go to Modal feature test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Modal media fragement content/specs', async () => { + const { data } = features[1]; + // expect(await modal.verifyModal(modalData)).toBeTruthy(); + + const modalLink = await modal.getModalLink(data.modalId); + await expect(await modalLink).toBeVisible(); + await expect(await modalLink).toHaveAttribute('class', modal.attributes['modal-link'].class); + + // click the modal link + await modalLink.click(); + await expect(await modal.dialog).toBeVisible(); + + await expect(await modal.mediaBlock).toBeVisible(); + await expect(await modal.mediaBlockdetailM).toContainText(data.detailText); + await expect(await modal.mediaBlockTextHeading).toContainText(data.h2Text); + await expect(await modal.mediaBlockTextBodyS).toContainText(data.bodyText); + + expect(await WebUtil.isModalInViewport(modal.page, modal.modalSelector)).toBeTruthy(); + + // close the modal using escape key press + await expect(await modal.dialogCloseButton).toBeVisible(); + await modal.page.keyboard.press('Escape'); + }); + }); + + // Test 2 : Modal with Video Autoplay + test(`${features[2].name}, ${features[2].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + + await test.step('step-1: Go to Modal feature test page', async () => { + await page.goto(`${baseURL}${features[2].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[2].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Modal media fragement content/specs', async () => { + const { data } = features[2]; + + const modalLink = await modal.getModalLink(data.modalId); + await expect(await modalLink).toBeVisible(); + await expect(await modalLink).toHaveAttribute('class', modal.attributes['modal-link'].class); + + // click the modal link and verify video autoplay + await modalLink.click(); + await expect(await modal.dialog).toBeVisible(); + expect(await WebUtil.isModalInViewport(modal.page, modal.modalSelector)).toBeTruthy(); + + await expect(await modal.video).toBeVisible(); + expect(await webUtil.verifyAttributes(await modal.video, modal.attributes['video.inline'])).toBeTruthy(); + + // close the modal using escape key press + await expect(await modal.dialogCloseButton).toBeVisible(); + await modal.page.keyboard.press('Escape'); + }); + }); +}); diff --git a/nala/blocks/quote/quote.page.js b/nala/blocks/quote/quote.page.js new file mode 100644 index 0000000000..581e5ac442 --- /dev/null +++ b/nala/blocks/quote/quote.page.js @@ -0,0 +1,64 @@ +export default class Quote { + constructor(page, nth = 0) { + this.page = page; + // quote locators + this.quote = this.page.locator('.quote').nth(nth); + this.quoteImage = this.quote.locator('.quote-image'); + this.quoteCopy = this.quote.locator('p.quote-copy'); + this.quoteFigCaption = this.quote.locator('p.figcaption'); + this.quoteFigCaptionCite = this.quote.locator('cite p'); + this.sectionDark = this.page.locator('.section.dark'); + + // quote blocks css + this.cssProperties = { + quote: { + 'text-align': 'center', + margin: /^0px.*/, + }, + + 'quote-contained': { + 'text-align': 'center', + margin: /^0px.*/, + }, + + 'quote-align-right': { + 'text-align': 'right', + margin: /^0px.*/, + }, + + 'quote-copy': { + 'font-size': '24px', + 'font-weight': 'bold', + }, + + 'quote-inline-figure': { + display: 'flex', + 'align-content': 'center', + flex: '1 0 40%', + margin: '0px', + 'justify-content': 'center', + }, + + 'quote-inline-image': { + height: '200px', + 'max-height': '200px', + }, + + figcaption: { + 'font-size': '16px', + 'font-weight': 'bold', + }, + }; + + // quote blocks attributes + this.attProperties = { + quote: { class: 'quote con-block' }, + 'quote-contained': { class: 'quote contained con-block' }, + 'quote-inline': { class: 'quote inline contained con-block' }, + 'quote-borders': { class: 'quote borders contained con-block' }, + 'quote-align-right': { class: 'quote contained align-right con-block' }, + 'quote-xl-spacing': { class: 'quote contained xl-spacing con-block' }, + 'section-dark': { style: 'background: rgb(102, 102, 102);' }, + }; + } +} diff --git a/nala/blocks/quote/quote.spec.js b/nala/blocks/quote/quote.spec.js new file mode 100644 index 0000000000..ac1f36019f --- /dev/null +++ b/nala/blocks/quote/quote.spec.js @@ -0,0 +1,73 @@ +/* eslint-disable max-len */ + +module.exports = { + FeatureName: 'Quote Block', + features: [ + { + tcid: '0', + name: '@Quote ', + path: '/drafts/nala/blocks/quote/quote', + data: { + quoteCopy: '3D is a crucial part of how we explore the brand in a digital workflow', + figCaption: 'Benny Lee', + cite: 'Global Manager of Experiential Design, Coca-Cola Company', + }, + tags: '@Quote @smoke @regression @milo', + }, + { + tcid: '1', + name: '@Quote (contained)', + path: '/drafts/nala/blocks/quote/quote-contained', + data: { + quoteCopy: '3D is a crucial part of how we explore the brand in a digital workflow', + figCaption: 'Benny Lee', + cite: 'Global Manager of Experiential Design, Coca-Cola Company', + }, + tags: '@Quote @quote-contained @smoke @regression @milo', + }, + { + tcid: '2', + name: '@Quote (inline,contained)', + path: '/drafts/nala/blocks/quote/quote-inline-contained', + data: { + quoteCopy: 'We are the guardians of a 135-year-old brand.', + figCaption: 'Rapha Abreu', + cite: 'Global Vice President of Design, Coca-Cola Company', + }, + tags: '@Quote @quote-inline @smoke @regression @milo,', + }, + { + tcid: '3', + name: '@Quote (borders,contained)', + path: '/drafts/nala/blocks/quote/quote-borders-contained', + data: { + quoteCopy: 'This was our opportunity to be one of the first teams to delve into Adobe Experience Platform', + figCaption: 'Ron Nagy', + cite: 'Sr. Evangelist, Adobe@Adobe', + }, + tags: '@Quote @quote-borders @smoke @regression @milo,', + }, + { + tcid: '4', + name: '@Quote (contained, align-right)', + path: '/drafts/nala/blocks/quote/quote-contained-align-right', + data: { + quoteCopy: '“This was our opportunity to be one of the first teams to delve into Adobe Experience Platform, and we wanted to show people just how powerful it can be.”', + figCaption: 'Ron Nagy', + cite: 'Sr. Evangelist, Adobe@Adobe', + }, + tags: '@Quote @quote-align-right @smoke @regression @milo,', + }, + { + tcid: '5', + name: '@Quote (xl-spaced)', + path: '/drafts/nala/blocks/quote/quote-xl-spaced', + data: { + quoteCopy: 'This was our opportunity to be one of the first teams to delve into Adobe Experience Platform', + figCaption: 'Ron Nagy', + cite: 'Sr. Evangelist, Adobe@Adobe', + }, + tags: '@Quote @quote-xl-spaced @smoke @regression @milo,', + }, + ], +}; diff --git a/nala/blocks/quote/quote.test.js b/nala/blocks/quote/quote.test.js new file mode 100644 index 0000000000..70574691bd --- /dev/null +++ b/nala/blocks/quote/quote.test.js @@ -0,0 +1,151 @@ +import { expect, test } from '@playwright/test'; +import WebUtil from '../../libs/webutil.js'; +import { features } from './quote.spec.js'; +import QuoteBlock from './quote.page.js'; + +let quote; +let webUtil; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo Quote Block test suite', () => { + test.beforeEach(async ({ page }) => { + webUtil = new WebUtil(page); + quote = new QuoteBlock(page); + }); + + // Test 0 : Quote default block + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + const { data } = features[0]; + + await test.step('step-1: Go to Quote block test page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Quote block content/specs', async () => { + await expect(await quote.quoteImage).toBeVisible(); + await expect(await quote.quoteCopy).toContainText(data.quoteCopy); + await expect(await quote.quoteFigCaption).toContainText(data.figCaption); + await expect(await quote.quoteFigCaptionCite).toContainText(data.cite); + + expect(await webUtil.verifyAttributes(await quote.quote, quote.attProperties.quote)).toBeTruthy(); + expect(await webUtil.verifyCSS(await quote.quote, quote.cssProperties.quote)).toBeTruthy(); + }); + }); + + // Test 1 : quote (contained) + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + const { data } = features[1]; + + await test.step('step-1: Go to Quote block test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Quote (contained) block content/specs', async () => { + await expect(await quote.quoteImage).toBeVisible(); + await expect(await quote.quoteCopy).toContainText(data.quoteCopy); + await expect(await quote.quoteFigCaption).toContainText(data.figCaption); + await expect(await quote.quoteFigCaptionCite).toContainText(data.cite); + + expect(await webUtil.verifyAttributes(await quote.quote, quote.attProperties['quote-contained'])).toBeTruthy(); + expect(await webUtil.verifyCSS(await quote.quote, quote.cssProperties['quote-contained'])).toBeTruthy(); + }); + }); + + // Test 2 : Quote (inline,contained) + test(`${features[2].name},${features[2].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + const { data } = features[2]; + + await test.step('step-1: Go to Quote (inline) block test page', async () => { + await page.goto(`${baseURL}${features[2].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[2].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Quote (inline) block content/specs', async () => { + await expect(await quote.quoteImage).toBeVisible(); + await expect(await quote.quoteCopy).toContainText(data.quoteCopy); + await expect(await quote.quoteFigCaption).toContainText(data.figCaption); + await expect(await quote.quoteFigCaptionCite).toContainText(data.cite); + + expect(await webUtil.verifyAttributes(await quote.quote, quote.attProperties['quote-inline'])).toBeTruthy(); + expect(await webUtil.verifyCSS(await quote.quote, quote.cssProperties.quote)).toBeTruthy(); + expect(await webUtil.verifyCSS(await quote.quoteImage, quote.cssProperties['quote-inline-figure'])).toBeTruthy(); + }); + }); + + // Test 3 : quote (borders) + test(`${features[3].name},${features[3].tags}`, async ({ page, baseURL }) => { + console.info(`[MiloInfo] Checking page: ${baseURL}${features[3].path}${miloLibs}`); + const { data } = features[3]; + + await test.step('step-1: Go to Quote (borders) block test page', async () => { + await page.goto(`${baseURL}${features[3].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[3].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Quote (borders) block content/specs', async () => { + await expect(await quote.quoteImage).not.toBeVisible(); + await expect(await quote.quoteCopy).toContainText(data.quoteCopy); + await expect(await quote.quoteFigCaption).toContainText(data.figCaption); + await expect(await quote.quoteFigCaptionCite).toContainText(data.cite); + + expect(await webUtil.verifyAttributes(await quote.quote, quote.attProperties['quote-borders'])).toBeTruthy(); + expect(await webUtil.verifyCSS(await quote.quote, quote.cssProperties.quote)).toBeTruthy(); + }); + }); + + // Test 4 : quote (align-right) + test(`${features[4].name},${features[4].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[4].path}${miloLibs}`); + const { data } = features[4]; + + await test.step('step-1: Go to Quote (align-right) block test page', async () => { + await page.goto(`${baseURL}${features[4].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[4].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Quote (align-right) block content/specs', async () => { + await expect(await quote.quoteImage).toBeVisible(); + await expect(await quote.quoteCopy).toContainText(data.quoteCopy); + await expect(await quote.quoteFigCaption).toContainText(data.figCaption); + await expect(await quote.quoteFigCaptionCite).toContainText(data.cite); + + expect(await webUtil.verifyAttributes(await quote.quote, quote.attProperties['quote-align-right'])).toBeTruthy(); + expect(await webUtil.verifyCSS(await quote.quote, quote.cssProperties['quote-align-right'])).toBeTruthy(); + }); + }); + + // Test 5 : quote (xl-spaced) + test(`${features[5].name},${features[5].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[5].path}${miloLibs}`); + const { data } = features[5]; + + await test.step('step-1: Go to Quote (xl-spaced) block test page', async () => { + await page.goto(`${baseURL}${features[5].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[5].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Quote (xl-spaced) block content/specs', async () => { + await expect(await quote.sectionDark).toBeVisible(); + await expect(await quote.quoteImage).not.toBeVisible(); + await expect(await quote.quoteCopy).toContainText(data.quoteCopy); + await expect(await quote.quoteFigCaption).toContainText(data.figCaption); + await expect(await quote.quoteFigCaptionCite).toContainText(data.cite); + + expect(await webUtil.verifyAttributes(await quote.sectionDark, quote.attProperties['section-dark'])).toBeTruthy(); + expect(await webUtil.verifyAttributes(await quote.quote, quote.attProperties['quote-xl-spacing'])).toBeTruthy(); + expect(await webUtil.verifyCSS(await quote.quote, quote.cssProperties.quote)).toBeTruthy(); + }); + }); +}); diff --git a/nala/blocks/review/review.page.js b/nala/blocks/review/review.page.js new file mode 100644 index 0000000000..7fb93650c3 --- /dev/null +++ b/nala/blocks/review/review.page.js @@ -0,0 +1,89 @@ +/* eslint-disable import/no-extraneous-dependencies, max-len, no-console */ +import { expect } from '@playwright/test'; + +export default class Review { + constructor(page) { + this.page = page; + // review block locators + this.review = this.page.locator('.review'); + this.reviewTitle = this.review.locator('.hlx-reviewTitle'); + this.reviewFieldSet = this.review.locator('form input'); + this.reviewTextArea = this.review.locator('#rating-comments'); + this.sendButton = this.review.locator('input[type="submit"]'); + } + + /** + * Verifies milo review block . + * @param {string} data - data required to verify review block. + * @returns {Promise} - A Promise that resolves to true if the verification + * is successful, or false if an error occurs. + */ + async verifyReview(data) { + try { + // verify review blcok + await expect(await this.review).toBeVisible(); + await expect(await this.reviewTitle).toContainText(data.reviewTitle); + await expect(await this.reviewFieldSet).toHaveCount(data.reviewFields); + + // Expected values review checkboxes + const expectedValues = [ + { tooltip: 'Poor', ariaLabel: 'Poor 1 Star', value: '1' }, + { tooltip: 'Below Average', ariaLabel: 'Below Average 2 Star', value: '2' }, + { tooltip: 'Good', ariaLabel: 'Good 3 Star', value: '3' }, + { tooltip: 'Very Good', ariaLabel: 'Very Good 4 Star', value: '4' }, + { tooltip: 'Outstanding', ariaLabel: 'Outstanding 5 Star', value: '5' }, + ]; + const reviewCheckBoxes = await this.reviewFieldSet.all(); + const checkBoxes = await Promise.all(reviewCheckBoxes.map(async (el) => el)); + // eslint-disable-next-line no-restricted-syntax + for (const checkbox of checkBoxes) { + const tooltip = await checkbox.getAttribute('data-tooltip'); + const ariaLabel = await checkbox.getAttribute('aria-label'); + const value = await checkbox.getAttribute('value'); + // Find the matching expected value + const expectedValue = expectedValues.find((expected) => expected.tooltip === tooltip + && expected.ariaLabel === ariaLabel + && expected.value === value); + // Verify the expected value + if (!expectedValue) { + console.log('Attributes and values are incorrect'); + return false; + } + } + return true; + } catch (error) { + console.error(`Error review block: ${error}`); + return false; + } + } + + /** + * Submits the review rating / form. + * @param {string} checkboxValue - The value of the checkbox to be selected. + * @param {string} textareaValue - The value to be entered in the text area. + * @returns {Promise} - A Promise that resolves to true if the submission is successful, + * or false if an error occurs. + */ + async submitReview(data) { + try { + // Select the n-th rating checkbox + const checkbox = await this.reviewFieldSet.nth(data.rating); + + // if the rating less than 3 then text area field is visible + if (data.rating < 3) { + await checkbox.check(); + await expect(await this.reviewTextArea).toBeVisible(); + await this.reviewTextArea.fill(data.reviewComment); + + // Click the send button + await this.sendButton.click(); + return true; + } + await checkbox.check(); + return true; + } catch (error) { + console.error(`Error submitting the review: ${error}`); + return false; + } + } +} diff --git a/nala/blocks/review/review.spec.js b/nala/blocks/review/review.spec.js new file mode 100644 index 0000000000..575f2e0ae1 --- /dev/null +++ b/nala/blocks/review/review.spec.js @@ -0,0 +1,31 @@ +module.exports = { + FeatureName: 'Review', + features: [ + { + tcid: '0', + name: '@Review low ', + path: '/drafts/nala/blocks/review/review', + data: { + reviewTitle: 'Rate your Experience', + reviewFields: 5, + rating: 4, + reviewComment: 'This is great', + + }, + tags: '@Review @smoke @regression @milo', + }, + { + tcid: '1', + name: '@Review low', + path: '/drafts/nala/blocks/review/review', + data: { + reviewTitle: 'Rate your Experience', + reviewFields: 5, + rating: 2, + reviewComment: 'This is great', + + }, + tags: '@Review @smoke @regression @milo', + }, + ], +}; diff --git a/nala/blocks/review/review.test.js b/nala/blocks/review/review.test.js new file mode 100644 index 0000000000..d3eb7462ab --- /dev/null +++ b/nala/blocks/review/review.test.js @@ -0,0 +1,48 @@ +import { expect, test } from '@playwright/test'; +import { features } from './review.spec.js'; +import ReviewBlock from './review.page.js'; + +let review; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo Review Block test suite', () => { + test.beforeEach(async ({ page, browser }) => { + // review block requires clearing cookies + const context = await browser.newContext(); + await context.clearCookies(); + review = new ReviewBlock(page); + }); + + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + + await test.step('step-1: Go to review feature test page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('step-2: Verify review block and submit the review < 3', async () => { + const { data } = features[0]; + expect(await review.verifyReview(data)).toBeTruthy(); + expect(await review.submitReview(data)).toBeTruthy(); + }); + }); + + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + + await test.step('step-1: Go to review block test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify review block and submit the review > 3', async () => { + const { data } = features[1]; + expect(await review.verifyReview(data)).toBeTruthy(); + expect(await review.submitReview(data)).toBeTruthy(); + }); + }); +}); diff --git a/nala/blocks/table/table.page.js b/nala/blocks/table/table.page.js new file mode 100644 index 0000000000..03c25a2467 --- /dev/null +++ b/nala/blocks/table/table.page.js @@ -0,0 +1,75 @@ +/* eslint-disable no-return-await */ +export default class Table { + constructor(page, nth = 0) { + this.page = page; + // tabel locators + this.table = this.page.locator('.table').nth(nth); + this.highlightTable = this.page.locator('.table.highlight').nth(nth); + this.stickyTable = this.page.locator('.table.sticky').nth(nth); + this.collapseStickyTable = this.page.locator('.table.highlight.collapse.sticky').nth(nth); + this.merchTable = this.page.locator('.table.merch').nth(nth); + this.merchHighlightStickyTable = this.page.locator('.table.merch.highlight.sticky').nth(nth); + + this.highlightRow = this.table.locator('.row-highlight'); + this.headingRow = this.table.locator('.row-heading'); + this.stickyRow = this.table.locator('.row-heading'); + + this.headingRowColumns = this.headingRow.locator('.col'); + this.rows = this.table.locator('.row'); + this.sectionRows = this.table.locator('.section-row'); + } + + async getHighlightRowColumnTitle(colIndex) { + return await this.highlightRow.locator('.col-highlight').nth(colIndex); + } + + async getHeaderColumnTitle(colIndex) { + const headerColumn = await this.headingRow.locator(`.col-${colIndex}`); + return headerColumn.locator('.tracking-header'); + } + + async getHeaderColumnPricing(colIndex) { + const headerColumn = await this.headingRow.locator(`.col-${colIndex}`); + return headerColumn.locator('.pricing'); + } + + async getHeaderColumnImg(colIndex) { + const headerColumn = await this.headingRow.locator(`.col-${colIndex}`); + return headerColumn.locator('img'); + } + + async getHeaderColumnAdditionalText(colIndex) { + const headerColumn = await this.headingRow.locator(`.col-${colIndex}`); + return headerColumn.locator('p').nth(3); + } + + async getHeaderColumnOutlineButton(colIndex) { + const headerColumn = await this.headingRow.locator(`.col-${colIndex}`); + return headerColumn.locator('.con-button.outline'); + } + + async getHeaderColumnBlueButton(colIndex) { + const headerColumn = await this.headingRow.locator(`.col-${colIndex}`); + return headerColumn.locator('.con-button.blue'); + } + + async getSectionRowTitle(index) { + const sectionRow = await this.table.locator('.section-row').nth(index); + return sectionRow.locator('.section-row-title'); + } + + async getSectionRowMerchContent(index) { + const sectionRow = await this.table.locator('.section-row').nth(index); + return sectionRow.locator('.col-merch-content').nth(0); + } + + async getSectionRowMerchContentImg(index) { + const sectionRow = await this.table.locator('.section-row').nth(index); + return sectionRow.locator('.col-merch-content img'); + } + + async getSectionRowCell(rowIndex, colIndex) { + const sectionRow = await this.table.locator('.section-row').nth(rowIndex); + return sectionRow.locator(`.col-${colIndex}`); + } +} diff --git a/nala/blocks/table/table.spec.js b/nala/blocks/table/table.spec.js new file mode 100644 index 0000000000..8ac863fbcf --- /dev/null +++ b/nala/blocks/table/table.spec.js @@ -0,0 +1,121 @@ +module.exports = { + FeatureName: 'Table Block', + features: [ + { + tcid: '0', + name: '@Table (default)', + path: '/drafts/nala/blocks/table/table', + data: { + rowsCount: 9, + headerRowColCount: 5, + sectionRowCount: 8, + headerCell2: { + heading: 'Heading Title-2', + pricingText: 'Pricing-2', + outlineButtonText: 'Free trial', + blueButtonText: 'Buy now', + }, + sectionRow2: { + sectionRowTitle: 'Row-1.1, Title', + cell22: 'Content', + }, + }, + tags: '@table @smoke @regression @milo', + }, + { + tcid: '1', + name: '@Table (highlight)', + path: '/drafts/nala/blocks/table/table-hightlight', + data: { + rowsCount: 10, + headerRowColCount: 5, + sectionRowCount: 8, + hightlightRow: { + cell12: 'Highlight-2', + cell13: 'Highlight-3', + cell14: 'Highlight-4', + }, + headerCell3: { + heading: 'Heading Title-3', + pricingText: 'Pricing-3', + outlineButtonText: 'Free trial', + blueButtonText: 'Buy now', + }, + sectionRow2: { + sectionRowTitle: 'Row-1.1, Title', + cell22: 'Content', + }, + }, + tags: '@table @smoke @regression @milo', + }, + { + tcid: '2', + name: '@Table (sticky)', + path: '/drafts/nala/blocks/table/table-sticky', + data: { + rowsCount: 9, + headerRowColCount: 5, + sectionRowCount: 8, + headerCell4: { + heading: 'Heading Title-4', + pricingText: 'Pricing-4', + outlineButtonText: 'Free trial', + blueButtonText: 'Buy now', + }, + sectionRow2: { + sectionRowTitle: 'Row-1.1, Title', + cell22: 'Content', + }, + }, + tags: '@table @smoke @regression @milo', + }, + { + tcid: '3', + name: '@Table (highlight, collapse, sticky)', + path: '/drafts/nala/blocks/table/table-highlight-collapse-sticky', + data: { + rowsCount: 10, + headerRowColCount: 5, + sectionRowCount: 8, + hightlightRow: { + cell12: 'Highlight-2', + cell13: 'Highlight-3', + cell14: 'Highlight-4', + }, + headerCell5: { + heading: 'Heading Title-5', + pricingText: 'Pricing-5', + outlineButtonText: 'Free trial', + blueButtonText: 'Buy now', + }, + sectionRow2: { + sectionRowTitle: 'Row-1.1, Title', + cell22: 'Content', + }, + }, + tags: '@table @smoke @regression @milo', + }, + { + tcid: '4', + name: '@Table (merch)', + path: '/drafts/nala/blocks/table/table-merch', + data: { + rowsCount: 9, + headerRowColCount: 3, + sectionRowCount: 8, + headerCell1: { + heading: 'Heading Title-1', + pricingText: 'Pricing-1', + AdditionalText: 'Additional Text-1', + outlineButtonText: 'Free trial', + blueButtonText: 'Buy now', + }, + sectionRow2: { + merchContent: 'Section Content-1.1', + image: 'yes', + }, + }, + tags: '@table @smoke @regression @milo', + }, + ], +}; diff --git a/nala/blocks/table/table.test.js b/nala/blocks/table/table.test.js new file mode 100644 index 0000000000..d0b296ce55 --- /dev/null +++ b/nala/blocks/table/table.test.js @@ -0,0 +1,195 @@ +import { expect, test } from '@playwright/test'; +import { features } from './table.spec.js'; +import TableBlock from './table.page.js'; + +let table; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo Table block feature test suite', () => { + test.beforeEach(async ({ page }) => { + table = new TableBlock(page); + }); + + // Test 0 : Table block (default) + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + const { data } = features[0]; + + await test.step('step-1: Go to Table block test page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('step-2: Verify table content/specs', async () => { + await expect(await table.table).toBeVisible(); + await expect(await table.rows).toHaveCount(data.rowsCount); + await expect(await table.headingRowColumns).toHaveCount(data.headerRowColCount); + await expect(await table.sectionRows).toHaveCount(data.sectionRowCount); + + // verify header row cell + const headerCell = data.headerCell2; + await expect(await table.getHeaderColumnTitle(2)).toContainText(headerCell.heading); + await expect(await table.getHeaderColumnPricing(2)).toContainText(headerCell.pricingText); + await expect(await table.getHeaderColumnOutlineButton(2)).toContainText(headerCell.outlineButtonText); + await expect(await table.getHeaderColumnBlueButton(2)).toContainText(headerCell.blueButtonText); + + // verify section row cell + const sectionCell = data.sectionRow2; + await expect(await table.getSectionRowTitle(2)).toContainText(sectionCell.sectionRowTitle); + await expect(await table.getSectionRowCell(2, 2)).toContainText(sectionCell.cell22); + }); + }); + + // Test 1 : Table (highlight) + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + const { data } = features[1]; + + await test.step('step-1: Go to Table block test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify table content/specs', async () => { + await expect(await table.highlightTable).toBeVisible(); + await expect(await table.highlightRow).toBeVisible(); + + await expect(await table.rows).toHaveCount(data.rowsCount); + await expect(await table.headingRowColumns).toHaveCount(data.headerRowColCount); + await expect(await table.sectionRows).toHaveCount(data.sectionRowCount); + + // verify highlighter row + const highlighter = data.hightlightRow; + await expect(await table.getHighlightRowColumnTitle(1)).toContainText(highlighter.cell12); + await expect(await table.getHighlightRowColumnTitle(2)).toContainText(highlighter.cell13); + await expect(await table.getHighlightRowColumnTitle(3)).toContainText(highlighter.cell14); + + // verify header row cell + const headerCell = data.headerCell3; + await expect(await table.getHeaderColumnTitle(3)).toContainText(headerCell.heading); + await expect(await table.getHeaderColumnPricing(3)).toContainText(headerCell.pricingText); + await expect(await table.getHeaderColumnOutlineButton(3)).toContainText(headerCell.outlineButtonText); + await expect(await table.getHeaderColumnBlueButton(3)).toContainText(headerCell.blueButtonText); + + // verify section row cell + const sectionCell = data.sectionRow2; + await expect(await table.getSectionRowTitle(2)).toContainText(sectionCell.sectionRowTitle); + await expect(await table.getSectionRowCell(2, 2)).toContainText(sectionCell.cell22); + }); + }); + + // Test 2 : Table (sticky) + test(`${features[2].name},${features[2].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + const { data } = features[2]; + + await test.step('step-1: Go to Table block test page', async () => { + await page.goto(`${baseURL}${features[2].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[2].path}${miloLibs}`); + }); + + await test.step('step-2: Verify table content/specs', async () => { + // verify sticky table header and attributes + await expect(await table.stickyTable).toBeVisible(); + await expect(await table.stickyRow).toHaveAttribute('class', 'row row-1 row-heading top-border-transparent'); + + // verify table row, column count + await expect(await table.rows).toHaveCount(data.rowsCount); + await expect(await table.headingRowColumns).toHaveCount(data.headerRowColCount); + await expect(await table.sectionRows).toHaveCount(data.sectionRowCount); + + // verify header row cell + const headerCell = data.headerCell4; + await expect(await table.getHeaderColumnTitle(4)).toContainText(headerCell.heading); + await expect(await table.getHeaderColumnPricing(4)).toContainText(headerCell.pricingText); + await expect(await table.getHeaderColumnOutlineButton(4)).toContainText(headerCell.outlineButtonText); + await expect(await table.getHeaderColumnBlueButton(4)).toContainText(headerCell.blueButtonText); + + // verify section row cell + const sectionCell = data.sectionRow2; + await expect(await table.getSectionRowTitle(2)).toContainText(sectionCell.sectionRowTitle); + await expect(await table.getSectionRowCell(2, 2)).toContainText(sectionCell.cell22); + }); + }); + + // Test 3 : Table (highlight, collapse, sticky) + test(`${features[3].name},${features[3].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[3].path}${miloLibs}`); + const { data } = features[3]; + + await test.step('step-1: Go to Table block test page', async () => { + await page.goto(`${baseURL}${features[3].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[3].path}${miloLibs}`); + }); + + await test.step('step-2: Verify table content/specs', async () => { + // verify sticky table header and attributes + await expect(await table.collapseStickyTable).toBeVisible(); + await expect(table.highlightRow).toHaveClass(/row.*row-1.*row-highlight/); + await expect(table.stickyRow).toHaveClass(/row.*row-2.*row-heading/); + + // verify table row, column count + await expect(await table.rows).toHaveCount(data.rowsCount); + await expect(await table.headingRowColumns).toHaveCount(data.headerRowColCount); + await expect(await table.sectionRows).toHaveCount(data.sectionRowCount); + + // verify highlighter row + const highlighter = data.hightlightRow; + await expect(await table.getHighlightRowColumnTitle(1)).toContainText(highlighter.cell12); + await expect(await table.getHighlightRowColumnTitle(2)).toContainText(highlighter.cell13); + await expect(await table.getHighlightRowColumnTitle(3)).toContainText(highlighter.cell14); + + // verify header row cell + const headerCell = data.headerCell5; + await expect(await table.getHeaderColumnTitle(5)).toContainText(headerCell.heading); + await expect(await table.getHeaderColumnPricing(5)).toContainText(headerCell.pricingText); + await expect(await table.getHeaderColumnOutlineButton(5)).toContainText(headerCell.outlineButtonText); + await expect(await table.getHeaderColumnBlueButton(5)).toContainText(headerCell.blueButtonText); + + // verify section row cell + const sectionCell = data.sectionRow2; + await expect(await table.getSectionRowTitle(2)).toContainText(sectionCell.sectionRowTitle); + await expect(await table.getSectionRowCell(2, 2)).toContainText(sectionCell.cell22); + }); + }); + + // Test 4 : Table (merch) + test(`${features[4].name},${features[4].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[4].path}${miloLibs}`); + const { data } = features[4]; + + await test.step('step-1: Go to Table block test page', async () => { + await page.goto(`${baseURL}${features[4].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[4].path}${miloLibs}`); + }); + + await test.step('step-2: Verify table content/specs', async () => { + // verify merch table + await expect(await table.merchTable).toBeVisible(); + + // verify table row, column count + await expect(await table.rows).toHaveCount(data.rowsCount); + await expect(await table.headingRowColumns).toHaveCount(data.headerRowColCount); + await expect(await table.sectionRows).toHaveCount(data.sectionRowCount); + + // verify merch table header row cell + const headerCell = data.headerCell1; + await expect(await table.getHeaderColumnTitle(1)).toContainText(headerCell.heading); + await expect(await table.getHeaderColumnPricing(1)).toContainText(headerCell.pricingText); + await expect(await table.getHeaderColumnAdditionalText(1)).toContainText(headerCell.AdditionalText); + await expect(await table.getHeaderColumnOutlineButton(1)).toContainText(headerCell.outlineButtonText); + await expect(await table.getHeaderColumnBlueButton(1)).toContainText(headerCell.blueButtonText); + + // verify merch table section row cell + const sectionCell = data.sectionRow2; + await expect(await table.getSectionRowMerchContent(2)).toContainText(sectionCell.merchContent); + await expect(await table.getSectionRowMerchContentImg(2)).toBeVisible(); + }); + }); +}); diff --git a/nala/blocks/tabs/tabs.page.js b/nala/blocks/tabs/tabs.page.js new file mode 100644 index 0000000000..30aa9718a3 --- /dev/null +++ b/nala/blocks/tabs/tabs.page.js @@ -0,0 +1,26 @@ +export default class Tabs { + constructor(page, nth = 0) { + this.page = page; + // tabs locators + this.tab = this.page.locator('.tabs').nth(nth); + this.xlTab = this.page.locator('.tabs.xl-spacing').nth(nth); + this.queitDarkTab = this.page.locator('.tabs.quiet.dark.center').nth(nth); + // tabs list + this.tabList = this.tab.locator('.tabList'); + this.tabListContainer = this.tabList.locator('.tab-list-container'); + this.tabsCount = this.tabListContainer.locator('button[role="tab"]'); + this.tab1 = this.tabListContainer.locator('button[role="tab"]').nth(0); + this.tab2 = this.tabListContainer.locator('button[role="tab"]').nth(1); + this.tab3 = this.tabListContainer.locator('button[role="tab"]').nth(2); + this.tab9 = this.tabListContainer.locator('button[role="tab"]:nth-child(9)'); + // tabs panel and content + this.tabContent = this.tab.locator('.tab-content > .tab-content-container'); + this.tab1Panel = this.tabContent.locator('div[role="tabpanel"]:nth-child(1)'); + this.tab2Panel = this.tabContent.locator('div[role="tabpanel"]:nth-child(2)'); + this.tab3Panel = this.tabContent.locator('div[role="tabpanel"]:nth-child(3)'); + this.tab9Panel = this.tabContent.locator('div[role="tabpanel"]:nth-child(9)'); + + this.leftArrow = this.tab.locator('.tab-paddles > .paddle-left'); + this.rightArrow = this.tab.locator('.tab-paddles > .paddle-right'); + } +} diff --git a/nala/blocks/tabs/tabs.spec.js b/nala/blocks/tabs/tabs.spec.js new file mode 100644 index 0000000000..1edd7cfc0c --- /dev/null +++ b/nala/blocks/tabs/tabs.spec.js @@ -0,0 +1,37 @@ +module.exports = { + FeatureName: 'Tabs Block', + features: [ + { + tcid: '0', + name: '@Tabs (xl-spacing)', + path: '/drafts/nala/blocks/tabs/tabs-xl-spacing', + data: { + tabsCount: 3, + activeTab: 2, + tab1Text: 'Here is tab 1 content', + tab2Text: 'Here is tab 2 content and it is active tab', + tab3Text: 'Here is tab 3 content', + }, + tags: '@tabs @smoke @regression @milo @t1', + }, + { + tcid: '1', + name: '@Tabs (Quiet, Dark, Center)', + path: '/drafts/nala/blocks/tabs/tabs-quiet-dark-center', + data: { + tabsCount: 3, + activeTab: 2, + tab1Text: 'Here is tab 1 content', + tab2Text: 'Here is tab 2 content and it is active tab', + tab3Text: 'Here is tab 3 content', + }, + tags: '@tabs @smoke @t1 @regression @milo', + }, + { + tcid: '2', + name: 'Tabs scrolling', + path: '/drafts/nala/blocks/tabs/tabs-scrolling', + tags: '@tabs @tabs-scrolling @smoke @regression @milo @bacom', + }, + ], +}; diff --git a/nala/blocks/tabs/tabs.test.js b/nala/blocks/tabs/tabs.test.js new file mode 100644 index 0000000000..be296cf66b --- /dev/null +++ b/nala/blocks/tabs/tabs.test.js @@ -0,0 +1,140 @@ +import { expect, test } from '@playwright/test'; +import { features } from './tabs.spec.js'; +import TabBlock from './tabs.page.js'; + +let tab; + +const miloLibs = process.env.MILO_LIBS || ''; +const INTERVALS = Array(5).fill(1000); + +test.describe('Milo Tab block feature test suite', () => { + test.beforeEach(async ({ page }) => { + tab = new TabBlock(page); + }); + + // Test 0 : Tabs (xl-spacing) + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + const { data } = features[0]; + + await test.step('step-1: Go to Tabs block feature test page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('step-2: Verify tabs content/specs', async () => { + await expect(await tab.xlTab).toBeVisible(); + await expect(await tab.tabsCount).toHaveCount(data.tabsCount); + // verify default tab contents + await expect(await tab.tab2).toHaveAttribute('aria-selected', 'true'); + await expect(await tab.tab2Panel).toBeVisible(); + await expect(await tab.tab2Panel).toContainText(data.tab2Text); + + // click tabs and verify contents + await expect(await tab.tab1).toHaveAttribute('aria-selected', 'false'); + await tab.tab1.click(); + await expect(await tab.tab1Panel).toBeVisible(); + await expect(await tab.tab1Panel).toContainText(data.tab1Text); + + await expect(await tab.tab3).toHaveAttribute('aria-selected', 'false'); + await tab.tab3.click(); + await expect(await tab.tab3Panel).toBeVisible(); + await expect(await tab.tab3Panel).toContainText(data.tab3Text); + }); + }); + + // Test 1 : Tabs (Quiet, Dark, Center) + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + const { data } = features[1]; + + await test.step('step-1: Go to Tabs block feature test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify tabs content/specs', async () => { + await expect(await tab.queitDarkTab).toBeVisible(); + await expect(await tab.tabsCount).toHaveCount(data.tabsCount); + // verify default tab contents + await expect(await tab.tab2).toHaveAttribute('aria-selected', 'true'); + await expect(await tab.tab2Panel).toBeVisible(); + await expect(await tab.tab2Panel).toContainText(data.tab2Text); + + // click tabs and verify contents + await expect(await tab.tab1).toHaveAttribute('aria-selected', 'false'); + await tab.tab1.click(); + await expect(await tab.tab1Panel).toBeVisible(); + await expect(await tab.tab1Panel).toContainText(data.tab1Text); + + await expect(await tab.tab3).toHaveAttribute('aria-selected', 'false'); + await tab.tab3.click(); + await expect(await tab.tab3Panel).toBeVisible(); + await expect(await tab.tab3Panel).toContainText(data.tab3Text); + }); + }); + + test(`Tabs scrolling with arrow buttons, ${features[2].tags}`, async ({ page, baseURL, isMobile }) => { + console.log(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + await page.goto(`${baseURL}${features[2].path}${miloLibs}`); + await page.waitForLoadState('networkidle'); + + await test.step('checking the setup', async () => { + await expect(tab.tab1).toBeVisible(); + await expect(tab.tab1Panel).toBeVisible(); + await expect(tab.tab9).toBeVisible(); + await expect(tab.tab9Panel).not.toBeVisible(); + await expect(tab.tab1).toBeInViewport(); + await expect(tab.tab9).not.toBeInViewport(); + await expect(await tab.tab1.getAttribute('aria-selected')).toBe('true'); + await expect(await tab.tab9.getAttribute('aria-selected')).toBe('false'); + }); + + await test.step('select the right tab arrow to get to the last tab', async () => { + if (isMobile) { + await expect(async () => { + await tab.rightArrow.click(); + await expect(tab.tab9).toBeInViewport({ timeout: 1000 }); + await expect(tab.leftArrow).toBeVisible({ timeout: 1000 }); + }).toPass({ intervals: INTERVALS }); + } else { + await tab.rightArrow.click(); + await expect(tab.tab9).toBeInViewport(); + await expect(tab.leftArrow).toBeVisible(); + } + await tab.tab9.click(); + + await expect(await tab.tab1.getAttribute('aria-selected')).toBe('false'); + await expect(await tab.tab9.getAttribute('aria-selected')).toBe('true'); + await expect(tab.tab1).not.toBeInViewport(); + await expect(tab.tab9).toBeInViewport(); + await expect(tab.tab1Panel).not.toBeVisible(); + await expect(tab.tab9Panel).toBeVisible(); + }); + + await test.step('select the left tab arrow to get back to the first tab', async () => { + if (isMobile) { + await expect(async () => { + await tab.leftArrow.click(); + await expect(tab.tab1).toBeInViewport({ timeout: 1000 }); + await expect(tab.rightArrow).toBeVisible({ timeout: 1000 }); + }).toPass({ intervals: INTERVALS }); + } else { + await tab.leftArrow.click(); + await expect(tab.tab1).toBeInViewport(); + await expect(tab.rightArrow).toBeVisible(); + } + + await tab.tab1.click(); + + await expect(await tab.tab1.getAttribute('aria-selected')).toBe('true'); + await expect(await tab.tab9.getAttribute('aria-selected')).toBe('false'); + await expect(tab.tab1).toBeInViewport(); + await expect(tab.tab9).not.toBeInViewport(); + await expect(tab.tab1Panel).toBeVisible(); + await expect(tab.tab9Panel).not.toBeVisible(); + }); + }); +}); diff --git a/nala/blocks/text/text.page.js b/nala/blocks/text/text.page.js new file mode 100644 index 0000000000..ee70ba3b4f --- /dev/null +++ b/nala/blocks/text/text.page.js @@ -0,0 +1,115 @@ +export default class Text { + constructor(page, nth = 0) { + this.page = page; + // text locators + this.text = page.locator('.text').nth(nth); + this.textIntro = this.page.locator('.text.intro'); + this.textFullWidth = this.page.locator('.text.full-width'); + this.textFullWidthLarge = this.page.locator('.text.full-width.large'); + this.textLongFormLarge = this.page.locator('.text.long-form'); + this.textInsetLargeMSpacing = this.page.locator('.text.inset.medium.m-spacing'); + this.textlegal = this.page.locator('.text.legal.text-block.con-block.has-bg'); + this.textLinkFarm = this.page.locator('.text.link-farm.text-block.con-block.has-bg'); + + this.detailM = page.locator('.detail-m'); + this.introDetailM = page.locator('.detail-m'); + this.longFormDetailL = page.locator('.detail-l'); + this.legalDetail = page.locator('.foreground'); + + this.headline = this.text.locator('#text'); + this.introHeadline = this.text.locator('#text-intro'); + this.fullWidthHeadline = this.text.locator('#text-full-width'); + this.fullWidthLargeHeadline = this.text.locator('#text-full-width-large'); + this.longFormLargeHeadline = this.text.locator('#text-long-form-large'); + this.insetLargeMSpacingHeadline = this.text.locator('#text-inset-large-m-spacing'); + this.linkFarmHeadline = this.text.locator('#text-link-farm-title'); + this.linkFarmcolumnheading = this.text.locator('#heading-1'); + + this.linkFarmcolumns = this.text.locator('h3'); + this.linkColumnOne = this.text.locator('div div:nth-child(1) a'); + this.linkFormText = this.text.locator('p').nth(1); + + this.bodyXSS = this.text.locator('.body-xxs').first(); + this.bodyM = this.text.locator('.body-m').first(); + this.bodyL = this.text.locator('.body-l').first(); + this.propertiesHeadingM = this.text.locator('#properties-h3').first(); + + this.outlineButton = this.text.locator('.con-button.outline'); + this.actionAreaLink = this.page.locator('.body-m.action-area a').nth(1); + this.bodyLink = this.page.locator('.body-m a'); + + this.insetLargeMSpacingList1 = this.page.locator('.text.inset.medium.m-spacing ul').nth(0); + this.listOneItems = this.insetLargeMSpacingList1.locator('li'); + + this.insetLargeMSpacingList2 = this.page.locator('.text.inset.medium.m-spacing ul').nth(1); + this.listTwoItems = this.insetLargeMSpacingList2.locator('li'); + + this.generalTermsOfUse = this.textlegal.locator('.body-xxs').nth(1); + this.publishText = this.textlegal.locator('.body-xxs').nth(2); + this.generalTerms = this.textlegal.locator('.body-xxs').nth(4); + this.legalInfoLink = this.textlegal.locator('.body-xxs').nth(5); + + // text block contents css + this.cssProperties = { + 'detail-m': { + 'font-size': '12px', + 'line-height': '15px', + }, + 'detail-l': { + 'font-size': '16px', + 'line-height': '20px', + }, + 'heading-s': { + 'font-size': '20px', + 'line-height': '25px', + }, + 'heading-m': { + 'font-size': '24px', + 'line-height': '30px', + }, + 'heading-l': { + 'font-size': '28px', + 'line-height': '35px', + }, + 'heading-xl': { + 'font-size': '36px', + 'line-height': '45px', + }, + 'body-xss': { + 'font-size': '12px', + 'line-height': '18px', + }, + 'body-m': { + 'font-size': '18px', + 'line-height': '27px', + }, + 'body-l': { + 'font-size': '20px', + 'line-height': '30px', + }, + foreground: { + 'font-size': '12px', + 'line-height': '18px', + }, + }; + + // text block contents attributes + this.attProperties = { + text: { class: 'text text-block con-block' }, + 'text-intro': { + class: 'text intro text-block con-block has-bg max-width-8-desktop xxl-spacing-top xl-spacing-bottom', + style: 'background: rgb(255, 255, 255);', + }, + 'text-full-width': { class: 'text full-width text-block con-block max-width-8-desktop center xxl-spacing' }, + 'text-full-width-large': { class: 'text full-width large text-block con-block max-width-8-desktop center xxl-spacing' }, + 'text-long-form-large': { class: 'text long-form large text-block con-block max-width-8-desktop' }, + 'text-inset-medium-m-spacing': { class: 'text inset medium m-spacing text-block con-block max-width-8-desktop' }, + 'text-legal': { class: 'text legal text-block con-block has-bg' }, + 'text-Link-farm': { + class: 'text link-farm text-block con-block has-bg', + style: 'background: rgb(255, 255, 255);', + }, + headingprops: { id: 'heading-1' }, + }; + } +} diff --git a/nala/blocks/text/text.spec.js b/nala/blocks/text/text.spec.js new file mode 100644 index 0000000000..c89c731333 --- /dev/null +++ b/nala/blocks/text/text.spec.js @@ -0,0 +1,97 @@ +/* eslint-disable max-len */ + +module.exports = { + BlockName: 'Text Block', + features: [ + { + tcid: '0', + name: '@Text', + path: '/drafts/nala/blocks/text/text', + data: { + h3Text: 'Text', + bodyText: 'Kick things off with hundreds of premium and free presets you can access with your Lightroom subscription.', + outlineButtonText: 'Learn more', + linkText: 'Explore the premium collection', + }, + tags: '@text @smoke @regression @milo', + }, + { + tcid: '1', + name: '@Text (intro)', + path: '/drafts/nala/blocks/text/text-intro', + data: { + detailText: 'Detail', + h2Text: 'Text (intro)', + bodyText: 'Body L Regular (20/30) Lorem ipsum dolor sit amet,', + }, + tags: '@text @full-intro @smoke @regression @milo', + }, + { + tcid: '2', + name: '@Text (full-width)', + path: '/drafts/nala/blocks/text/text-full-width', + data: { + h3Text: 'Text (full width)', + bodyText: 'Featuring over 600,000 hand-picked stock photos and graphics, ', + linkText: 'Explore the premium collection', + }, + tags: '@text @full-width @smoke @regression @milo', + }, + { + tcid: '3', + name: '@Text (full-width, large)', + path: '/drafts/nala/blocks/text/text-full-width-large', + data: { + h2Text: 'Text (full width, large)', + bodyText: 'Whether your team is creating multichannel campaign assets,', + linkText: 'Learn more our solution', + }, + tags: '@text @full-width-large @smoke @regression @milo', + }, + { + tcid: '4', + name: '@Text (long-form, large)', + path: '/drafts/nala/blocks/text/text-long-form-large', + data: { + detailText: 'Detail', + h2Text: 'Text (long form, large)', + bodyText: 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr,', + }, + tags: '@text @long-form-large @smoke @regression @milo', + }, + { + tcid: '5', + name: '@Text (inset, medium, m-spacing)', + path: '/drafts/nala/blocks/text/text-inset-medium-m-spacing', + data: { + h3Text: 'Text (inset, large, m spacing)', + bodyText: 'Lorem ipsum dolor sit amet.', + listCount1: 3, + }, + tags: '@text @inset-medium-m-spacing @smoke @regression @milo', + }, + { + tcid: '6', + name: '@Text (legal)', + path: '/drafts/nala/blocks/text/text-legal', + data: { + termsOfUseText: 'Adobe General Terms of Use', + publishText: 'Published August 1, 2022. Effective as of September 19, 2022.', + generalTermsText: 'These General Terms of Use (“General Terms”), along with any applicable Additional Terms', + linkText: 'Please read complete legal information', + }, + tags: '@text @legal @smoke @regression @milo', + }, + { + tcid: '7', + name: '@Text (link-farm)', + path: '/drafts/nala/blocks/text/text-link-farm', + data: { + headingColumns: 4, + linksCount: 6, + linkText: 'example of a link', + }, + tags: '@text @link-farm @smoke @regression @milo', + }, + ], +}; diff --git a/nala/blocks/text/text.test.js b/nala/blocks/text/text.test.js new file mode 100644 index 0000000000..2b0ca15cb4 --- /dev/null +++ b/nala/blocks/text/text.test.js @@ -0,0 +1,243 @@ +import { expect, test } from '@playwright/test'; +import WebUtil from '../../libs/webutil.js'; +import { features } from './text.spec.js'; +import TextBlock from './text.page.js'; + +let text; +let webUtil; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo Text Block test suite', () => { + test.beforeEach(async ({ page }) => { + text = new TextBlock(page); + webUtil = new WebUtil(page); + }); + + // Test 0 : Text + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + const { data } = features[0]; + + await test.step('step-1: Go to Text block test page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Text specs', async () => { + await expect(await text.text).toBeVisible(); + await expect(await text.headline).toContainText(data.h3Text); + await expect(await text.bodyM).toContainText(data.bodyText); + await expect(await text.outlineButton).toContainText(data.outlineButtonText); + + expect(await webUtil.verifyCSS(text.headline, text.cssProperties['heading-l'])).toBeTruthy(); + expect(await webUtil.verifyCSS(text.bodyM, text.cssProperties['body-m'])).toBeTruthy(); + expect(await webUtil.verifyCSS(text.actionAreaLink, text.cssProperties['body-m'])).toBeTruthy(); + }); + + await test.step('step-3: Verify analytics attributes', async () => { + await expect(await text.text).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('text', 1)); + await expect(await text.outlineButton).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.outlineButtonText, 1, data.h3Text)); + await expect(await text.actionAreaLink).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.linkText, 2, data.h3Text)); + }); + }); + + // Test 1 : Text (intro) + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + const { data } = features[1]; + + await test.step('step-1: Go to Text (intro) block test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Text (intro) specs', async () => { + await expect(text.textIntro).toBeVisible(); + await expect(await text.introHeadline).toContainText(data.h2Text); + await expect(await text.bodyM).toContainText(data.bodyText); + + expect(await webUtil.verifyAttributes(text.textIntro, text.attProperties['text-intro'])).toBeTruthy(); + expect(await webUtil.verifyCSS(text.introDetailM, text.cssProperties['detail-m'])).toBeTruthy(); + expect(await webUtil.verifyCSS(text.introHeadline, text.cssProperties['heading-l'])).toBeTruthy(); + expect(await webUtil.verifyCSS(text.bodyM, text.cssProperties['body-m'])).toBeTruthy(); + }); + + await test.step('step-3: Verify analytics attributes', async () => { + await expect(await text.textIntro).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('text', 1)); + }); + }); + + // Test 2 : Text (full-width) + test(`${features[2].name},${features[2].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + const { data } = features[2]; + + await test.step('step-1: Go to Text (full width) block test page', async () => { + await page.goto(`${baseURL}${features[2].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[2].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Text (full width) specs', async () => { + await expect(text.textFullWidth).toBeVisible(); + + await expect(await text.fullWidthHeadline).toContainText(data.h3Text); + await expect(await text.bodyM).toContainText(data.bodyText); + await expect(await text.bodyLink).toContainText(data.linkText); + + expect(await webUtil.verifyAttributes(await text.textFullWidth, text.attProperties['text-full-width'])).toBeTruthy(); + expect(await webUtil.verifyCSS(await text.fullWidthHeadline, text.cssProperties['heading-l'])).toBeTruthy(); + expect(await webUtil.verifyCSS(await text.bodyM, text.cssProperties['body-m'])).toBeTruthy(); + }); + + await test.step('step-3: Verify analytics attributes', async () => { + await expect(await text.textFullWidth).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('text', 1)); + await expect(await text.bodyLink).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.linkText, 1, data.h3Text)); + }); + }); + + // Test 3 : Text (full-width, large) + test(`${features[3].name},${features[3].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[3].path}${miloLibs}`); + const { data } = features[3]; + + await test.step('step-1: Go to text (full-width, large) block test page', async () => { + await page.goto(`${baseURL}${features[3].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[3].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Text (full-width, large) specs', async () => { + await expect(text.textFullWidthLarge).toBeVisible(); + + await expect(await text.fullWidthLargeHeadline).toContainText(data.h2Text); + await expect(await text.bodyM).toContainText(data.bodyText); + await expect(await text.bodyLink).toContainText(data.linkText); + + expect(await webUtil.verifyAttributes(text.textFullWidthLarge, text.attProperties['text-full-width-large'])).toBeTruthy(); + expect(await webUtil.verifyCSS(text.fullWidthLargeHeadline, text.cssProperties['heading-xl'])).toBeTruthy(); + expect(await webUtil.verifyCSS(text.bodyM, text.cssProperties['body-m'])).toBeTruthy(); + }); + + await test.step('step-3: Verify analytics attributes', async () => { + await expect(await text.textFullWidthLarge).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('text', 1)); + await expect(await text.bodyLink).toHaveAttribute('daa-ll', await webUtil.getLinkDaall(data.linkText, 1, data.h2Text)); + }); + }); + + // Test 4 : Text (long-form, large) + test(`${features[4].name},${features[4].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[4].path}${miloLibs}`); + const { data } = features[4]; + + await test.step('step-1: Go to Text (long form, large) block test page', async () => { + await page.goto(`${baseURL}${features[4].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[4].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Text (long form, large) specs', async () => { + await expect(await text.textLongFormLarge).toBeVisible(); + + await expect(await text.longFormDetailL).toContainText(data.detailText); + await expect(await text.longFormLargeHeadline).toContainText(data.h2Text); + await expect(await text.bodyL).toContainText(data.bodyText); + + expect(await webUtil.verifyAttributes(text.textLongFormLarge, text.attProperties['text-long-form-large'])).toBeTruthy(); + expect(await webUtil.verifyCSS(text.longFormDetailL, text.cssProperties['detail-l'])).toBeTruthy(); + expect(await webUtil.verifyCSS(text.longFormLargeHeadline, text.cssProperties['heading-l'])).toBeTruthy(); + expect(await webUtil.verifyCSS(text.bodyL, text.cssProperties['body-l'])).toBeTruthy(); + }); + + await test.step('step-3: Verify analytics attributes', async () => { + await expect(await text.textLongFormLarge).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('text', 1)); + }); + }); + + // Test 5 : Text (inset, medium, m-spacing) + test(`${features[5].name},${features[5].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[5].path}${miloLibs}`); + const { data } = features[5]; + + await test.step('step-1: Go to Text (inset, medium, m-spacing ) block test page', async () => { + await page.goto(`${baseURL}${features[5].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[5].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Text (inset, large, m spacing) specs', async () => { + await expect(await text.textInsetLargeMSpacing).toBeVisible(); + + await expect(await text.insetLargeMSpacingHeadline).toContainText(data.h3Text); + await expect(await text.bodyL).toContainText(data.bodyText); + await expect(await text.listOneItems).toHaveCount(data.listCount1); + + expect(await webUtil.verifyAttributes(text.textInsetLargeMSpacing, text.attProperties['text-inset-medium-m-spacing'])).toBeTruthy(); + expect(await webUtil.verifyCSS(text.insetLargeMSpacingHeadline, text.cssProperties['heading-m'])).toBeTruthy(); + expect(await webUtil.verifyCSS(text.bodyL, text.cssProperties['body-l'])).toBeTruthy(); + expect(await webUtil.verifyCSS(text.propertiesHeadingM, text.cssProperties['heading-m'])).toBeTruthy(); + }); + + await test.step('step-3: Verify analytics attributes', async () => { + await expect(await text.textInsetLargeMSpacing).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('text', 1)); + }); + }); + + // Test 6 : Text (legal) + test(`${features[6].name},${features[6].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[6].path}${miloLibs}`); + const { data } = features[6]; + + await test.step('step-1: Go to Text (legal) block test page', async () => { + await page.goto(`${baseURL}${features[6].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[6].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Text (legal) specs', async () => { + await expect(await text.textlegal).toBeVisible(); + + await expect(await text.generalTermsOfUse).toContainText(data.termsOfUseText); + await expect(await text.publishText).toContainText(data.publishText); + await expect(await text.generalTerms).toContainText(data.generalTermsText); + await expect(await text.legalInfoLink).toContainText(data.linkText); + + expect(await webUtil.verifyAttributes(text.textlegal, text.attProperties['text-legal'])).toBeTruthy(); + expect(await webUtil.verifyCSS(text.bodyXSS, text.cssProperties['body-xss'])).toBeTruthy(); + }); + + await test.step('step-3: Verify analytics attributes', async () => { + await expect(await text.textlegal).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('text', 1)); + }); + }); + + test(`${features[7].name},${features[7].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[7].path}${miloLibs}`); + const { data } = features[7]; + + await test.step('step-1: Go to Text (link-farm) block test page', async () => { + await page.goto(`${baseURL}${features[7].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[7].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Text (link-farm) specs', async () => { + await expect(await text.textLinkFarm).toBeVisible(); + + await expect(await text.linkFarmcolumns).toHaveCount(data.headingColumns); + await expect(await text.linkColumnOne).toHaveCount(data.linksCount); + await expect(await text.linkFormText).toContainText(data.linkText); + + expect(await webUtil.verifyAttributes(text.textLinkFarm, text.attProperties['text-Link-farm'])).toBeTruthy(); + expect(await webUtil.verifyCSS(text.linkFarmHeadline, text.cssProperties['heading-l'])).toBeTruthy(); + expect(await webUtil.verifyAttributes(text.linkFarmcolumnheading, text.attProperties.headingprops)).toBeTruthy(); + }); + + await test.step('step-3: Verify analytics attributes', async () => { + await expect(await text.textLinkFarm).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('text', 1)); + }); + }); +}); diff --git a/nala/blocks/video/video.page.js b/nala/blocks/video/video.page.js new file mode 100644 index 0000000000..a68ae99911 --- /dev/null +++ b/nala/blocks/video/video.page.js @@ -0,0 +1,72 @@ +export default class Video { + constructor(page, nth = 0) { + this.page = page; + + // video locators + this.section = this.page.locator('.section').nth(nth); + this.content = this.page.locator('.content').nth(nth); + this.fragment = this.page.locator('.fragment'); + this.video = this.page.locator('.content video'); + this.videoSource = this.video.locator('source'); + this.miloVideo = this.page.locator('.milo-video'); + this.iframe = this.page.locator('iframe').first(); + this.mpcPlayerTitle = this.page.frameLocator('iframe').first().locator('.mpc-player__title'); + this.mpcPlayButton = this.page.frameLocator('iframe').first().locator('button .mpc-large-play.mpc-player__large-play'); + this.mpcMutedButton = this.page.frameLocator('iframe').first().locator('.mpc-player button[aria-label="Mute"]'); + this.mpcMutedLabel = this.page.frameLocator('iframe').first().locator('.mpc-player button[aria-label="Mute"] span'); + this.youtubePlayButton = this.page.locator('button.lty-playbtn'); + this.liteYoutube = this.page.locator('lite-youtube'); + this.modalVideo = this.fragment.locator('video'); + this.modalVideoSource = this.modalVideo.locator('source'); + this.consonantCardsGrid = this.page.locator('.consonant-CardsGrid'); + this.consonantCards = this.consonantCardsGrid.locator('.card.consonant-Card'); + this.video = this.page.locator('.content video'); + this.videoSource = this.video.locator('source'); + + // video block attributes + this.attributes = { + 'video.default': { + playsinline: '', + controls: '', + }, + 'video.source': { + type: 'video/mp4', + src: /.*.mp4/, + }, + 'video.autoplay': { + playsinline: '', + autoplay: '', + loop: '', + muted: '', + }, + 'video.autoplay.once': { + playsinline: '', + autoplay: '', + muted: '', + }, + 'video.hover.play': { + playsinline: '', + autoplay: '', + muted: '', + 'data-hoverplay': '', + 'data-mouseevent': 'true', + }, + 'iframe-mpc': { + class: 'adobetv', + scrolling: 'no', + allowfullscreen: '', + loading: 'lazy', + }, + 'iframe-youtube': { + class: 'youtube', + scrolling: 'no', + allowfullscreen: '', + allow: 'encrypted-media; accelerometer; gyroscope; picture-in-picture', + }, + analytics: { + 'section.daa-lh': { 'daa-lh': /s[1-9]/ }, + 'content.daa-lh': { 'daa-lh': /b[1-9]|content|default|default/ }, + }, + }; + } +} diff --git a/nala/blocks/video/video.spec.js b/nala/blocks/video/video.spec.js new file mode 100644 index 0000000000..80ca3350d8 --- /dev/null +++ b/nala/blocks/video/video.spec.js @@ -0,0 +1,84 @@ +module.exports = { + FeatureName: 'Video Block', + features: [ + { + tcid: '0', + name: '@Video Default', + path: '/drafts/nala/blocks/video/default-video', + data: { h2Text: 'Default video' }, + tags: '@video @smoke @regression @milo', + }, + { + tcid: '1', + name: '@Video autoplay loop', + path: '/drafts/nala/blocks/video/video-autoplay-loop', + data: { h2Text: 'Autoplay enabled video' }, + tags: '@video @smoke @regression @milo', + }, + { + tcid: '2', + name: '@Video autoplay loop once', + path: '/drafts/nala/blocks/video/autoplay-loop-once', + data: { h2Text: 'Autoplay once enabled video' }, + tags: '@video @smoke @regression @milo', + }, + { + tcid: '3', + name: '@Video hover play', + path: '/drafts/nala/blocks/video/video-hover-play', + data: { h2Text: 'Hover play enabled video (combined with #_autoplay1 for feature to work)' }, + tags: '@video @smoke @regression @milo', + }, + { + tcid: '4', + name: '@MPC Video', + path: '/drafts/nala/blocks/video/mpc-video', + data: { + h1Title: '1856730_Summit_2021_Marquee_1440x1028_v1.0.mp4', + iframeTitle: 'Adobe Video Publishing Cloud Player', + source: 'https://video.tv.adobe.com/v/332632', + }, + tags: '@video @smoke @regression @milo', + }, + { + tcid: '5', + name: '@MPC Video Autoplay Looping', + path: '/drafts/nala/blocks/video/mpc-video-autoplay-looping', + data: { + iframeTitle: 'Adobe Video Publishing Cloud Player', + source: 'https://video.tv.adobe.com/v/332632?autoplay=true&end=replay', + }, + tags: '@video @smoke @regression @milo', + }, + { + tcid: '6', + name: '@Youtube Video ', + path: '/drafts/nala/blocks/video/youtube-video', + data: { + h1Text: 'YouTube video', + playLabel: 'Adobe MAX Keynote 2022 | Adobe Creative Cloud', + source: 'https://www.youtube.com/embed/OfQKEzgPaBA?', + videoId: 'OfQKEzgPaBA', + }, + tags: '@video @smoke @regression @milo', + }, + { + tcid: '7', + name: '@Fragment Modal video inline', + path: '/drafts/nala/blocks/video/fragments-modal-video-autoplay', + data: + { source: 'https://main--milo--adobecom.hlx.live/libs/media_1e798d01c6ddc7e7eadc8f134d69e4f8d7193fdbb.mp4' }, + tags: '@video @smoke @regression @milo', + }, + { + tcid: '8', + name: '@Modal video with cards', + path: '/drafts/nala/blocks/video/modal-video-with-cards', + data: { + cardsCount: 3, + source: 'https://milo.adobe.com/libs/media_1e798d01c6ddc7e7eadc8f134d69e4f8d7193fdbb.mp4', + }, + tags: '@video @smoke @regression @milo', + }, + ], +}; diff --git a/nala/blocks/video/video.test.js b/nala/blocks/video/video.test.js new file mode 100644 index 0000000000..a3ab0e2e7d --- /dev/null +++ b/nala/blocks/video/video.test.js @@ -0,0 +1,212 @@ +import { expect, test } from '@playwright/test'; +import WebUtil from '../../libs/webutil.js'; +import { features } from './video.spec.js'; +import VideoBlock from './video.page.js'; + +let webUtil; +let video; +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo Video Block test suite', () => { + test.beforeEach(async ({ page }) => { + webUtil = new WebUtil(page); + video = new VideoBlock(page); + }); + + // Test 0 : Video default + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + const { data } = features[0]; + + await test.step('step-1: Go to video block test page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('step-2: Verify video block content/specs', async () => { + await expect(await video.video).toBeVisible(); + await expect(await video.content).toContainText(data.h2Text); + + await expect(await webUtil.verifyAttributes(video.video, video.attributes['video.default'])).toBeTruthy(); + await expect(await webUtil.verifyAttributes(video.videoSource, video.attributes['video.source'])).toBeTruthy(); + }); + }); + + // Test 1 : Video autoplay loop + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + const { data } = features[1]; + + await test.step('step-1: Go to video block test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify video block content/specs', async () => { + await expect(await video.video).toBeVisible(); + await expect(await video.content).toContainText(data.h2Text); + + expect(await webUtil.verifyAttributes(video.video, video.attributes['video.autoplay'])).toBeTruthy(); + expect(await webUtil.verifyAttributes(video.videoSource, video.attributes['video.source'])).toBeTruthy(); + }); + }); + + // Test 2 : Video autoplay loop once + test(`${features[2].name},${features[2].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + const { data } = features[2]; + + await test.step('step-1: Go to video block test page', async () => { + await page.goto(`${baseURL}${features[2].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[2].path}${miloLibs}`); + }); + + await test.step('step-2: Verify video block content/specs', async () => { + await expect(await video.video).toBeVisible(); + await expect(await video.content).toContainText(data.h2Text); + + expect(await webUtil.verifyAttributes(video.video, video.attributes['video.autoplay.once'])).toBeTruthy(); + expect(await webUtil.verifyAttributes(video.videoSource, video.attributes['video.source'])).toBeTruthy(); + }); + }); + + // Test 3 : Video hover play + test(`${features[3].name},${features[3].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[3].path}${miloLibs}`); + const { data } = features[3]; + + await test.step('step-1: Go to video block test page', async () => { + await page.goto(`${baseURL}${features[3].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[3].path}${miloLibs}`); + }); + + await test.step('step-2: Verify video block content/specs', async () => { + await expect(await video.video).toBeVisible(); + await expect(await video.content).toContainText(data.h2Text); + await new Promise((resolve) => { setTimeout(resolve, 5000); }); + await video.video.hover({ force: true }); + + expect(await webUtil.verifyAttributes(video.video, video.attributes['video.autoplay.once'])).toBeTruthy(); + expect(await webUtil.verifyAttributes(video.videoSource, video.attributes['video.source'])).toBeTruthy(); + }); + }); + + // Test 4 : MPC Video + test(`${features[4].name},${features[4].tags}`, async ({ page, baseURL }) => { + test.slow(); + console.info(`[Test Page]: ${baseURL}${features[4].path}${miloLibs}`); + const { data } = features[4]; + + await test.step('step-1: Go to video block test page', async () => { + await page.goto(`${baseURL}${features[4].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[4].path}${miloLibs}`); + }); + + await test.step('step-2: Verify video block content/specs', async () => { + await expect(await video.miloVideo).toBeVisible(); + await expect(await video.iframe).toBeVisible(); + + await expect(await video.iframe).toHaveAttribute('title', data.iframeTitle); + await expect(await video.iframe).toHaveAttribute('src', data.source); + expect(await webUtil.verifyAttributes(video.iframe, video.attributes['iframe-mpc'])).toBeTruthy(); + }); + }); + + // Test 5 : MPC Video Autoplay Looping + test(`${features[5].name},${features[5].tags}`, async ({ page, baseURL }) => { + test.slow(); + console.info(`[Test Page]: ${baseURL}${features[5].path}${miloLibs}`); + const { data } = features[5]; + + await test.step('step-1: Go to video block test page', async () => { + await page.goto(`${baseURL}${features[5].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[5].path}${miloLibs}`); + }); + + await test.step('step-2: Verify video block content/specs', async () => { + await expect(await video.miloVideo).toBeVisible(); + await expect(await video.iframe).toHaveAttribute('title', data.iframeTitle); + await expect(await video.iframe).toHaveAttribute('src', data.source); + expect(await webUtil.verifyAttributes(video.iframe, video.attributes['iframe-mpc'])).toBeTruthy(); + }); + }); + + // Test 6 : Youtube Video + test(`${features[6].name},${features[6].tags}`, async ({ page, baseURL }) => { + test.slow(); + console.info(`[Test Page]: ${baseURL}${features[6].path}${miloLibs}`); + const { data } = features[6]; + + await test.step('step-1: Go to video block test page', async () => { + await page.goto(`${baseURL}${features[6].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[6].path}${miloLibs}`); + }); + + await test.step('step-2: Verify video block content/specs', async () => { + await expect(await video.miloVideo).toBeVisible(); + await expect(await video.youtubePlayButton).toBeVisible(); + await expect(await video.youtubePlayButton).toHaveAttribute('type', 'button'); + + await expect(await video.liteYoutube).toHaveAttribute('playlabel', data.playLabel); + await expect(await video.liteYoutube).toHaveAttribute('videoid', data.videoId); + }); + }); + + // Test 7 : Modal Video default + test(`${features[7].name},${features[7].tags}`, async ({ page, baseURL }) => { + test.slow(); + console.info(`[Test Page]: ${baseURL}${features[7].path}${miloLibs}`); + // const { data } = features[7]; + + await test.step('step-1: Go to video block test page', async () => { + await page.goto(`${baseURL}${features[7].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[7].path}${miloLibs}`); + }); + + await test.step('step-2: Verify video block content/specs', async () => { + await expect(await video.modalVideo).toBeVisible(); + + expect(await webUtil.verifyAttributes(video.modalVideo, video.attributes['video.autoplay'])).toBeTruthy(); + expect(await webUtil.verifyAttributes(video.modalVideoSource, video.attributes['video.source'])).toBeTruthy(); + + const srcAttributeValue = await video.modalVideoSource.getAttribute('src'); + console.log('[video source]:', srcAttributeValue); + expect(srcAttributeValue).not.toBe(''); + }); + }); + + // Test 8 : Modal video with cards + test(`${features[8].name},${features[8].tags}`, async ({ page, baseURL }) => { + test.slow(); + console.info(`[Test Page]: ${baseURL}${features[8].path}${miloLibs}`); + const { data } = features[8]; + + await test.step('step-1: Go to video block test page', async () => { + await page.goto(`${baseURL}${features[8].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[8].path}${miloLibs}`); + }); + + await test.step('step-2: Verify consonant cards with modal video block content/specs', async () => { + await expect(await video.consonantCardsGrid).toBeVisible(); + await expect(await video.consonantCards.nth(0)).toBeVisible(); + await expect(await video.consonantCards).toHaveCount(data.cardsCount); + + await expect(await video.modalVideo).toBeVisible(); + expect(await webUtil.verifyAttributes(video.modalVideo, video.attributes['video.autoplay'])).toBeTruthy(); + expect(await webUtil.verifyAttributes(video.modalVideoSource, video.attributes['video.source'])).toBeTruthy(); + + const srcAttributeValue = await video.modalVideoSource.getAttribute('src'); + console.log('[video source]:', srcAttributeValue); + expect(srcAttributeValue).not.toBe(''); + }); + }); +}); diff --git a/nala/blocks/zpattern/zpattern.page.js b/nala/blocks/zpattern/zpattern.page.js new file mode 100644 index 0000000000..7ca8ddc2ae --- /dev/null +++ b/nala/blocks/zpattern/zpattern.page.js @@ -0,0 +1,39 @@ +export default class ZPattern { + constructor(page, nth = 0) { + this.page = page; + // z-pattern locators + this.zPattern = page.locator('.z-pattern').nth(nth); + + // zpatter header + this.zPatternHeader = this.zPattern.locator('.heading-row'); + this.zPatternPText = this.zPatternHeader.locator('p'); + + this.smallIntroHeadingText = this.zPattern.locator('#small-default-intro-text-optional'); + this.mediumIntroHeadingText = this.zPattern.locator('#medium-intro-text-optional'); + this.largeIntroHeadingText = this.zPattern.locator('#large-intro-text-optional'); + this.darkIntroHeadingText = this.zPattern.locator('#intuitive-block-authoring'); + + this.zPatternMediaBlocks = this.zPattern.locator('.media'); + this.mediaBlocks = this.zPattern.locator('.media'); + + // zpattern contents attributes + this.attProperties = { + 'z-pattern': { style: 'background: rgb(245, 245, 245);' }, + 'z-pattern-dark': { style: 'background: rgb(50, 50, 50);' }, + 'small-default-intro-text-optional': { class: 'heading-l headline' }, + 'medium-intro-text-optional': { class: 'heading-l headline' }, + 'large-intro-text-optional': { class: 'heading-xl headline' }, + 'dark-intro-text-optional': { class: 'heading-l headline' }, + 'media-medium': { class: 'media medium con-block' }, + 'small-media-reversed': { class: 'media small media-reversed con-block' }, + 'medium-media-reversed': { class: 'media medium media-reversed con-block' }, + 'medium-media-reverse-mobile': { class: 'media medium con-block media-reverse-mobile' }, + 'large-media-reversed': { class: 'media large media-reversed con-block' }, + 'media-image': { + width: '600', + height: '300', + }, + + }; + } +} diff --git a/nala/blocks/zpattern/zpattern.spec.js b/nala/blocks/zpattern/zpattern.spec.js new file mode 100644 index 0000000000..a531eb438a --- /dev/null +++ b/nala/blocks/zpattern/zpattern.spec.js @@ -0,0 +1,131 @@ +module.exports = { + BlockName: 'ZPattern', + features: [ + { + tcid: '0', + name: '@ZPattern', + path: '/drafts/nala/blocks/zpattern/z-pattern', + data: { + headingText: 'Medium Intro Text (optional)', + introText: 'Perspiciatis unde omnis iste natus error', + mediaBlockCount: 3, + mediaBlocks: [ + { + detailText: 'Detail M 12/15', + h2Text: 'Heading M 24/30 z-pattern medium', + bodyText: 'Body S 16/24 Lorem ipsum dolor sit amet', + blueButtonText: 'learn more', + }, + { + detailText: 'Detail M 12/15', + h2Text: 'Heading M 24/30 z-pattern medium', + bodyText: 'Body S 16/24 Lorem ipsum dolor sit amet', + blueButtonText: 'learn more', + }, + { + detailText: 'Detail M 12/15', + h2Text: 'Heading M 24/30 z-pattern medium', + bodyText: 'Body S 16/24 Lorem ipsum dolor sit amet', + blueButtonText: 'learn more', + }, + ], + }, + tags: '@zpattern @smoke @regression @milo', + }, + { + tcid: '1', + name: '@ZPattern (small)', + path: '/drafts/nala/blocks/zpattern/z-pattern-small', + data: { + headingText: 'Small (default) Intro Text (optional)', + introText: 'Media blocks may use one of three background colors', + mediaBlockCount: 3, + mediaBlocks: [ + { + detailText: 'Detail M 12/15', + h2Text: 'Heading XS 18/22 z-pattern small', + bodyText: 'Body S 16/24 Lorem ipsum dolor sit amet', + blueButtonText: 'learn more', + }, + { + detailText: 'Detail M 12/15', + h2Text: 'Heading XS 18/22 z-pattern small', + bodyText: 'Body S 16/24 Lorem ipsum dolor sit amet', + blueButtonText: 'learn more', + }, + { + detailText: 'Detail M 12/15', + h2Text: 'Heading XS 18/22 z-pattern small', + bodyText: 'Body S 16/24 Lorem ipsum dolor sit amet', + blueButtonText: 'learn more', + }, + ], + }, + tags: '@zpattern @zpattern-small @smoke @regression @milo', + }, + + { + tcid: '2', + name: '@Zpattern (large)', + path: '/drafts/nala/blocks/zpattern/z-pattern-large', + data: { + headingText: 'Large Intro Text (optional)', + introText: 'Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium', + mediaBlockCount: 3, + mediaBlocks: [ + { + detailText: 'Detail L 16/20', + h2Text: 'Heading XL 36/45 z-pattern large', + bodyText: 'Body M 18/27 Lorem ipsum dolor sit amet', + blueButtonText: 'learn more', + }, + { + detailText: 'Detail L 16/20', + h2Text: 'Heading XL 36/45 z-pattern large', + bodyText: 'Body M 18/27 Lorem ipsum dolor sit amet', + blueButtonText: 'learn more', + }, + { + detailText: 'Detail L 16/20', + h2Text: 'Heading XL 36/45 z-pattern large', + bodyText: 'Body M 18/27 Lorem ipsum dolor sit amet', + blueButtonText: 'learn more', + }, + ], + }, + tags: '@zpattern @zpattern-large @smoke @regression @milo', + }, + + { + tcid: '3', + name: '@Zpattern (dark)', + path: '/drafts/nala/blocks/zpattern/z-pattern-dark', + data: { + headingText: 'Intuitive block authoring', + introText: 'Supports alternating or inline authoring preferences', + mediaBlockCount: 3, + mediaBlocks: [ + { + detailText: 'Detail M 12/15', + h2Text: 'Heading M 24/30 z-pattern medium', + bodyText: 'Body S 16/24 Lorem ipsum dolor sit amet', + blueButtonText: 'Learn More', + }, + { + detailText: 'Detail M 12/15', + h2Text: 'Heading M 24/30 z-pattern medium', + bodyText: 'Body S 16/24 Lorem ipsum dolor sit amet', + blueButtonText: 'Learn More', + }, + { + detailText: 'Detail M 12/15', + h2Text: 'Heading M 24/30 z-pattern medium', + bodyText: 'Body S 16/24 Lorem ipsum dolor sit amet', + blueButtonText: 'Learn More', + }, + ], + }, + tags: '@zpattern @zpattern-dark @smoke @regression @milo', + }, + ], +}; diff --git a/nala/blocks/zpattern/zpattern.test.js b/nala/blocks/zpattern/zpattern.test.js new file mode 100644 index 0000000000..a4e298677d --- /dev/null +++ b/nala/blocks/zpattern/zpattern.test.js @@ -0,0 +1,168 @@ +import { expect, test } from '@playwright/test'; +import WebUtil from '../../libs/webutil.js'; +import { features } from './zpattern.spec.js'; +import ZPatternBlock from './zpattern.page.js'; + +let webUtil; +let zpattern; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo Z Pattern Block test suite', () => { + test.beforeEach(async ({ page }) => { + webUtil = new WebUtil(page); + zpattern = new ZPatternBlock(page); + }); + + // Test 0 : ZPattern default block + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + const { data } = features[0]; + + await test.step('step-1: Go to Z Pattern block test page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('step-2: Verify Z Pattern block specs', async () => { + await expect(await zpattern.zPatternHeader).toContainText(data.headingText); + await expect(await zpattern.zPatternPText).toContainText(data.introText); + await expect(await zpattern.mediaBlocks).toHaveCount(data.mediaBlockCount); + + const mediaBlocks = await zpattern.mediaBlocks.all(); + const mediaBlocksArray = await Promise.all(mediaBlocks.map(async (block) => block)); + + for (let i = 0; i < mediaBlocksArray.length; i++) { + const mediaBlock = mediaBlocksArray[i]; + await expect(await mediaBlock.locator('.detail-m')).toContainText(data.mediaBlocks[i].detailText); + await expect(await mediaBlock.locator('.heading-m')).toContainText(data.mediaBlocks[i].h2Text); + await expect(await mediaBlock.locator('.body-s').nth(0)).toContainText(data.mediaBlocks[i].bodyText); + await expect(await mediaBlock.locator('.blue')).toContainText(data.mediaBlocks[i].blueButtonText); + + const image = await mediaBlock.locator('.image img').nth(0); + const classAttribute = await mediaBlock.getAttribute('class'); + const hasReversedClass = classAttribute.includes('media-reversed'); + + if (hasReversedClass) { + expect(await webUtil.verifyAttributes(mediaBlock, zpattern.attProperties['medium-media-reversed'])).toBeTruthy(); + } + expect(await webUtil.verifyAttributes(image, zpattern.attProperties['media-image'])).toBeTruthy(); + } + }); + }); + + // Test 1 :ZPattern (small) block + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}${miloLibs}`); + const { data } = features[1]; + + await test.step('step-1: Go to z-pattern (small) block test page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('step-2: Verify z-pattern (small) block specs', async () => { + await expect(await zpattern.zPatternHeader).toContainText(data.headingText); + await expect(await zpattern.zPatternPText).toContainText(data.introText); + await expect(await zpattern.mediaBlocks).toHaveCount(data.mediaBlockCount); + + const mediaBlocks = await zpattern.mediaBlocks.all(); + const mediaBlocksArray = await Promise.all(mediaBlocks.map(async (block) => block)); + + for (let i = 0; i < mediaBlocksArray.length; i++) { + const mediaBlock = mediaBlocksArray[i]; + await expect(await mediaBlock.locator('.detail-m')).toContainText(data.mediaBlocks[i].detailText); + await expect(await mediaBlock.locator('.heading-xs')).toContainText(data.mediaBlocks[i].h2Text); + await expect(await mediaBlock.locator('.body-s').nth(0)).toContainText(data.mediaBlocks[i].bodyText); + await expect(await mediaBlock.locator('.blue')).toContainText(data.mediaBlocks[i].blueButtonText); + + const image = await mediaBlock.locator('.image img').nth(0); + const classAttribute = await mediaBlock.getAttribute('class'); + const hasReversedClass = classAttribute.includes('media-reversed'); + + if (hasReversedClass) { + expect(await webUtil.verifyAttributes(mediaBlock, zpattern.attProperties['small-media-reversed'])).toBeTruthy(); + } + expect(await webUtil.verifyAttributes(image, zpattern.attProperties['media-image'])).toBeTruthy(); + } + }); + }); + + // Test 2 :Zpattern (large) block + test(`${features[2].name},${features[2].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + const { data } = features[2]; + + await test.step('step-1: Go to z-pattern (large) block test page', async () => { + await page.goto(`${baseURL}${features[2].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[2].path}${miloLibs}`); + }); + + await test.step('step-2: Verify z-pattern (large) block specs', async () => { + await expect(await zpattern.zPatternHeader).toContainText(data.headingText); + await expect(await zpattern.zPatternPText).toContainText(data.introText); + await expect(await zpattern.mediaBlocks).toHaveCount(data.mediaBlockCount); + + const mediaBlocks = await zpattern.mediaBlocks.all(); + const mediaBlocksArray = await Promise.all(mediaBlocks.map(async (block) => block)); + + for (let i = 0; i < mediaBlocksArray.length; i++) { + const mediaBlock = mediaBlocksArray[i]; + await expect(await mediaBlock.locator('.detail-l')).toContainText(data.mediaBlocks[i].detailText); + await expect(await mediaBlock.locator('.heading-xl')).toContainText(data.mediaBlocks[i].h2Text); + await expect(await mediaBlock.locator('.body-m').nth(0)).toContainText(data.mediaBlocks[i].bodyText); + await expect(await mediaBlock.locator('.blue')).toContainText(data.mediaBlocks[i].blueButtonText); + + const image = await mediaBlock.locator('.image img').nth(0); + const classAttribute = await mediaBlock.getAttribute('class'); + const hasReversedClass = classAttribute.includes('media-reversed'); + + if (hasReversedClass) { + expect(await webUtil.verifyAttributes(mediaBlock, zpattern.attProperties['large-media-reversed'])).toBeTruthy(); + } + expect(await webUtil.verifyAttributes(image, zpattern.attProperties['media-image'])).toBeTruthy(); + } + }); + }); + + // Test 3 :Zpattern (dark) block + test(`${features[3].name},${features[3].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[3].path}${miloLibs}`); + const { data } = features[3]; + + await test.step('step-1: Go to z-pattern (large) block test page', async () => { + await page.goto(`${baseURL}${features[3].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[3].path}${miloLibs}`); + }); + + await test.step('step-2: Verify z-pattern (dark) block specs', async () => { + await expect(await zpattern.zPatternHeader).toContainText(data.headingText); + await expect(await zpattern.zPatternPText).toContainText(data.introText); + await expect(await zpattern.mediaBlocks).toHaveCount(data.mediaBlockCount); + + const mediaBlocks = await zpattern.mediaBlocks.all(); + const mediaBlocksArray = await Promise.all(mediaBlocks.map(async (block) => block)); + + for (let i = 0; i < mediaBlocksArray.length; i++) { + const mediaBlock = mediaBlocksArray[i]; + await expect(await mediaBlock.locator('.detail-m')).toContainText(data.mediaBlocks[i].detailText); + await expect(await mediaBlock.locator('.heading-m')).toContainText(data.mediaBlocks[i].h2Text); + await expect(await mediaBlock.locator('.body-s').nth(0)).toContainText(data.mediaBlocks[i].bodyText); + await expect(await mediaBlock.locator('.blue')).toContainText(data.mediaBlocks[i].blueButtonText); + + const image = await mediaBlock.locator('.image img').nth(0); + const classAttribute = await mediaBlock.getAttribute('class'); + const hasReversedClass = classAttribute.includes('media-reversed'); + + if (hasReversedClass) { + expect(await webUtil.verifyAttributes(mediaBlock, zpattern.attProperties['dark-media-reversed'])).toBeTruthy(); + } + expect(await webUtil.verifyAttributes(image, zpattern.attProperties['media-image'])).toBeTruthy(); + } + }); + }); +}); diff --git a/nala/features/commerce/commerce.page.js b/nala/features/commerce/commerce.page.js new file mode 100644 index 0000000000..60edc95c9e --- /dev/null +++ b/nala/features/commerce/commerce.page.js @@ -0,0 +1,19 @@ +export default class CommercePage { + constructor(page) { + this.page = page; + + this.price = page.locator('//span[@data-template="price"]'); + this.priceOptical = page.locator('//span[@data-template="optical"]'); + this.priceStrikethrough = page.locator('//span[@data-template="strikethrough"]'); + this.buyNowCta = page.locator('//a[contains(@daa-ll, "Buy now")]'); + this.freeTrialCta = page.locator('//a[contains(@daa-ll, "Free trial")]'); + this.merchCard = page.locator('merch-card'); + // universal nav login account type + this.loginType = page.locator('div.feds-profile > div > div > ul > li:nth-child(5) > button'); + // entitlement block locators + this.ccAllAppsCTA = page.locator('//*[contains(@daa-ll,"CC All Apps")]'); + this.photoshopBuyCTA = page.locator('//*[contains(@daa-ll,"Buy now-1--Photoshop")]'); + this.photoshopFreeCTA = page.locator('//*[contains(@daa-ll,"Free trial-2--Photoshop")]'); + this.switchModalIframe = page.locator('#switch-modal > div > iframe'); + } +} diff --git a/nala/features/commerce/commerce.spec.js b/nala/features/commerce/commerce.spec.js new file mode 100644 index 0000000000..51b3a27dd9 --- /dev/null +++ b/nala/features/commerce/commerce.spec.js @@ -0,0 +1,89 @@ +module.exports = { + name: 'Commerce', + features: [ + { + tcid: '0', + name: '@Commerce-Price-Term', + path: '/drafts/nala/features/commerce/prices-with-term', + tags: '@commerce @smoke @regression', + }, + { + tcid: '1', + name: '@Commerce-Price-Unit-Term', + path: '/drafts/nala/features/commerce/prices-with-term-unit', + tags: '@commerce @smoke @regression', + + }, + { + tcid: '2', + name: '@Commerce-Price-Taxlabel-Unit-Term', + path: '/drafts/nala/features/commerce/prices-with-term-unit-taxlabel', + tags: '@commerce @smoke @regression', + }, + { + tcid: '3', + name: '@Commerce-Promo', + path: '/drafts/nala/features/commerce/promo-placeholders', + data: { + promo: 'UMRM2MUSPr501YOC', + workflow: 'recommendation', + }, + tags: '@commerce @smoke @regression', + }, + { + tcid: '4', + name: '@Commerce-Upgrade-Entitlement', + path: '/drafts/nala/features/commerce/checkout-links', + data: { UpgradeCTATitle: 'Upgrade now' }, + tags: '@commerce @entitlement @smoke @regression @nopr', + }, + { + tcid: '5', + name: '@Commerce-Download-Entitlement', + path: '/drafts/nala/features/commerce/checkout-links', + data: { + DownloadCTATitle: 'Download', + TrialCTATitle: 'Free trial', + DownloadUrl: 'download/photoshop', + }, + tags: '@commerce @entitlement @smoke @regression @nopr', + }, + { + tcid: '6', + name: '@Commerce-KitchenSink-Smoke', + path: '/docs/library/kitchen-sink/merch-card', + tags: '@commerce @kitchensink @smoke @regression', + }, + { + tcid: '7', + name: '@Commerce-DE', + path: '/de/drafts/nala/features/commerce/promo-placeholders', + data: { + promo: 'PEMAP50AASTE2', + CO: 'co=DE', + lang: 'lang=de', + workflow: 'recommendation', + }, + tags: '@commerce @smoke @regression', + }, + { + tcid: '8', + name: '@Commerce-Old-Promo', + path: '/drafts/nala/features/commerce/promo-old-price', + data: { promo: 'UMRM2MUSPr501YOC' }, + tags: '@commerce @smoke @regression', + }, + { + tcid: '9', + name: '@Commerce-GB', + path: '/uk/drafts/nala/features/commerce/promo-placeholders', + data: { + promo: 'PEMAP50AASTE2', + CO: 'co=GB', + lang: 'lang=en', + workflow: 'recommendation', + }, + tags: '@commerce @smoke @regression', + }, + ], +}; diff --git a/nala/features/commerce/commerce.test.js b/nala/features/commerce/commerce.test.js new file mode 100644 index 0000000000..6f2f404524 --- /dev/null +++ b/nala/features/commerce/commerce.test.js @@ -0,0 +1,474 @@ +import { expect, test } from '@playwright/test'; +import WebUtil from '../../libs/webutil.js'; +import { features } from './commerce.spec.js'; +import CommercePage from './commerce.page.js'; +import FedsLogin from '../feds/login/login.page.js'; +import FedsHeader from '../feds/header/header.page.js'; + +const miloLibs = process.env.MILO_LIBS || ''; + +let COMM; +test.beforeEach(async ({ page, baseURL, browserName }) => { + COMM = new CommercePage(page); + if (browserName === 'chromium') { + await page.setExtraHTTPHeaders({ 'sec-ch-ua': '"Chromium";v="123", "Not:A-Brand";v="8"' }); + } + + const skipOn = ['bacom', 'business']; + skipOn.some((skip) => { + if (baseURL.includes(skip)) test.skip(true, `Skipping the commerce tests for ${baseURL}`); + return null; + }); +}); + +test.describe('Commerce feature test suite', () => { + // @Commerce-Price-Term - Validate price with term display + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[0].path}${miloLibs}`; + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Validate regular price display', async () => { + await COMM.price.waitFor({ state: 'visible', timeout: 10000 }); + expect(await COMM.price.innerText()).toContain('US$263.88/yr'); + expect(await COMM.price.locator('.price-recurrence').innerText()).not.toBe(''); + expect(await COMM.price.locator('.price-unit-type').innerText()).toBe(''); + expect(await COMM.price.locator('.price-tax-inclusivity').innerText()).toBe(''); + }); + + await test.step('Validate optical price display', async () => { + await COMM.priceOptical.waitFor({ state: 'visible', timeout: 10000 }); + expect(await COMM.priceOptical.innerText()).toContain('US$21.99/mo'); + expect(await COMM.priceOptical.locator('.price-recurrence').innerText()).not.toBe(''); + expect(await COMM.priceOptical.locator('.price-unit-type').innerText()).toBe(''); + expect(await COMM.priceOptical.locator('.price-tax-inclusivity').innerText()).toBe(''); + }); + + await test.step('Validate strikethrough price display', async () => { + await COMM.priceStrikethrough.waitFor({ state: 'visible', timeout: 10000 }); + expect(await COMM.priceStrikethrough.innerText()).toContain('US$263.88/yr'); + expect(await COMM.priceStrikethrough.locator('.price-recurrence').innerText()).not.toBe(''); + expect(await COMM.priceStrikethrough.locator('.price-unit-type').innerText()).toBe(''); + expect(await COMM.priceStrikethrough.locator('.price-tax-inclusivity').innerText()).toBe(''); + const priceStyle = await COMM.priceStrikethrough.evaluate( + (e) => window.getComputedStyle(e).getPropertyValue('text-decoration'), + ); + expect(await priceStyle).toContain('line-through'); + }); + }); + + // @Commerce-Price-Unit-Term - Validate price with term and unit display + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[1].path}${miloLibs}`; + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Validate regular price display', async () => { + await COMM.price.waitFor({ state: 'visible', timeout: 10000 }); + expect(await COMM.price.innerText()).toContain('US$'); + expect(await COMM.price.locator('.price-recurrence').innerText()).not.toBe(''); + expect(await COMM.price.locator('.price-unit-type').innerText()).not.toBe(''); + expect(await COMM.price.locator('.price-tax-inclusivity').innerText()).toBe(''); + }); + + await test.step('Validate optical price display', async () => { + await COMM.priceOptical.waitFor({ state: 'visible', timeout: 10000 }); + expect(await COMM.priceOptical.innerText()).toContain('US$'); + expect(await COMM.priceOptical.locator('.price-recurrence').innerText()).not.toBe(''); + expect(await COMM.priceOptical.locator('.price-unit-type').innerText()).not.toBe(''); + expect(await COMM.priceOptical.locator('.price-tax-inclusivity').innerText()).toBe(''); + }); + + await test.step('Validate strikethrough price display', async () => { + await COMM.priceStrikethrough.waitFor({ state: 'visible', timeout: 10000 }); + expect(await COMM.priceStrikethrough.innerText()).toContain('US$'); + expect(await COMM.priceStrikethrough.locator('.price-recurrence').innerText()).not.toBe(''); + expect(await COMM.priceStrikethrough.locator('.price-unit-type').innerText()).not.toBe(''); + expect(await COMM.priceStrikethrough.locator('.price-tax-inclusivity').innerText()).toBe(''); + const priceStyle = await COMM.priceStrikethrough.evaluate( + (e) => window.getComputedStyle(e).getPropertyValue('text-decoration'), + ); + expect(await priceStyle).toContain('line-through'); + }); + }); + + // @Commerce-Price-Taxlabel-Unit-Term - Validate price with term, unit and tax label display + test(`${features[2].name},${features[2].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[2].path}${miloLibs}`; + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Validate regular price display', async () => { + await COMM.price.waitFor({ state: 'visible', timeout: 10000 }); + expect(await COMM.price.innerText()).toContain('US$'); + expect(await COMM.price.locator('.price-recurrence').innerText()).not.toBe(''); + expect(await COMM.price.locator('.price-unit-type').innerText()).not.toBe(''); + expect(await COMM.price.locator('.price-tax-inclusivity').innerText()).not.toBe(''); + }); + + await test.step('Validate optical price display', async () => { + await COMM.priceOptical.waitFor({ state: 'visible', timeout: 10000 }); + expect(await COMM.priceOptical.innerText()).toContain('US$'); + expect(await COMM.priceOptical.locator('.price-recurrence').innerText()).not.toBe(''); + expect(await COMM.priceOptical.locator('.price-unit-type').innerText()).not.toBe(''); + expect(await COMM.priceOptical.locator('.price-tax-inclusivity').innerText()).not.toBe(''); + }); + + await test.step('Validate strikethrough price display', async () => { + await COMM.priceStrikethrough.waitFor({ state: 'visible', timeout: 10000 }); + expect(await COMM.priceStrikethrough.innerText()).toContain('US$'); + expect(await COMM.priceStrikethrough.locator('.price-recurrence').innerText()).not.toBe(''); + expect(await COMM.priceStrikethrough.locator('.price-unit-type').innerText()).not.toBe(''); + expect(await COMM.priceStrikethrough.locator('.price-tax-inclusivity').innerText()).not.toBe(''); + const priceStyle = await COMM.priceStrikethrough.evaluate( + (e) => window.getComputedStyle(e).getPropertyValue('text-decoration'), + ); + expect(await priceStyle).toContain('line-through'); + }); + }); + + // @Commerce-Promo - Validate price and CTAs have promo code applied + test(`${features[3].name},${features[3].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[3].path}${miloLibs}`; + const { data } = features[3]; + + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Validate regular price has promo', async () => { + await COMM.price.waitFor({ state: 'visible', timeout: 10000 }); + await expect(COMM.price).toHaveAttribute('data-promotion-code', data.promo); + await expect(COMM.price).toHaveAttribute('data-display-old-price', 'true'); + await COMM.price.locator('.price').first().waitFor({ state: 'visible', timeout: 10000 }); + await COMM.price.locator('.price-strikethrough').waitFor({ state: 'visible', timeout: 10000 }); + }); + + await test.step('Validate optical price has promo', async () => { + await COMM.priceOptical.waitFor({ state: 'visible', timeout: 10000 }); + await expect(COMM.priceOptical).toHaveAttribute('data-promotion-code', data.promo); + }); + + await test.step('Validate strikethrough price has promo', async () => { + await COMM.priceStrikethrough.waitFor({ state: 'visible', timeout: 10000 }); + await expect(COMM.priceStrikethrough).toHaveAttribute('data-promotion-code', data.promo); + }); + + await test.step('Validate Buy now CTA has promo', async () => { + await COMM.buyNowCta.waitFor({ state: 'visible', timeout: 10000 }); + await expect(COMM.buyNowCta).toHaveAttribute('data-promotion-code', data.promo); + await expect(COMM.buyNowCta).toHaveAttribute('href', new RegExp(`${data.promo}`)); + }); + + await test.step('Validate Free Trial CTA has promo', async () => { + await COMM.freeTrialCta.waitFor({ state: 'visible', timeout: 10000 }); + await expect(COMM.freeTrialCta).toHaveAttribute('data-promotion-code', data.promo); + await expect(COMM.freeTrialCta).toHaveAttribute('href', new RegExp(`${data.promo}`)); + await expect(COMM.freeTrialCta).toHaveAttribute('href', new RegExp(`${data.workflow}`)); + }); + }); + + // @Commerce-Upgrade-Entitlement - Validate Upgrade commerce flow + test(`${features[4].name}, ${features[4].tags}`, async ({ page, baseURL }) => { + test.skip(); // Skipping due to missing login + + const testPage = `${baseURL}${features[4].path}${miloLibs}`; + console.info('[Test Page]: ', testPage); + + const { data } = features[4]; + const Login = new FedsLogin(page); + const Header = new FedsHeader(page); + + // Go to test example + await test.step('Go to test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + // Login with Adobe test account: + await test.step('Login with a valid Adobe account', async () => { + await Header.signInButton.click(); + if (COMM.loginType.isVisible()) { + await COMM.loginType.click(); + } + await Login.loginOnAppForm(process.env.IMS_EMAIL_PAID_PS, process.env.IMS_PASS_PAID_PS); + }); + + // Validate Upgrade eligibility check w.r.t Buy CTA + await test.step('Verify cc all apps card cta title', async () => { + await page.waitForLoadState('domcontentloaded'); + await COMM.ccAllAppsCTA.waitFor({ state: 'visible', timeout: 10000 }); + await expect(COMM.ccAllAppsCTA).toHaveText(data.UpgradeCTATitle); + }); + + // Validate Upgrade eligibility check w.r.t Switch modal + await test.step('Verify Switch modal launch for Upgrade', async () => { + await COMM.ccAllAppsCTA.click(); + await COMM.switchModalIframe.waitFor({ state: 'visible', timeout: 45000 }); + await expect(COMM.switchModalIframe).toBeVisible(); + }); + }); + + // @Commerce-Download-Entitlement - Validate Download commerce flow + test(`${features[5].name}, ${features[5].tags}`, async ({ page, baseURL }) => { + test.skip(); // Skipping due to missing login + + const testPage = `${baseURL}${features[5].path}${miloLibs}`; + console.info('[Test Page]: ', testPage); + const { data } = features[5]; + const Login = new FedsLogin(page); + const Header = new FedsHeader(page); + + // Go to test example + await test.step('Go to test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + // Login with Adobe test account: + await test.step('Login with a valid Adobe account', async () => { + await Header.signInButton.click(); + if (COMM.loginType.isVisible()) { + await COMM.loginType.click(); + } + await Login.loginOnAppForm(process.env.IMS_EMAIL_PAID_PS, process.env.IMS_PASS_PAID_PS); + }); + + // Validate Download eligibility check w.r.t Buy CTA + await test.step('Verify photoshop card cta title', async () => { + await page.waitForLoadState('domcontentloaded'); + await COMM.photoshopBuyCTA.waitFor({ state: 'visible', timeout: 10000 }); + await expect(COMM.photoshopBuyCTA).toHaveText(data.DownloadCTATitle); + await expect(COMM.photoshopFreeCTA).toHaveText(data.TrialCTATitle); + }); + + // Validate Download eligibility check w.r.t download link + await test.step('Verify download link for download', async () => { + await COMM.photoshopBuyCTA.click(); + await page.waitForLoadState('domcontentloaded'); + await expect(page.url()).toContain(data.DownloadUrl); + }); + }); + + // @Commerce-KitchenSink-Smoke - Validate commerce CTA and checkout placeholders + test(`${features[6].name}, ${features[6].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[6].path}${miloLibs}`; + const webUtil = new WebUtil(page); + + console.info('[Test Page]: ', testPage); + + // Go to test example + await test.step('Go to test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + // Validate there are no unresolved commerce placeholders + await test.step('Validate wcs placeholders', async () => { + await COMM.merchCard.first().waitFor({ state: 'visible', timeout: 45000 }); + await webUtil.scrollPage('down', 'slow'); + const unresolvedPlaceholders = await page.evaluate( + () => [...document.querySelectorAll('[data-wcs-osi]')].filter( + (el) => !el.classList.contains('placeholder-resolved'), + ), + ); + expect(unresolvedPlaceholders.length).toBe(0); + }); + + // Validate commerce checkout links are indeed commerce + await test.step('Validate checkout links', async () => { + const invalidCheckoutLinks = await page.evaluate( + () => [...document.querySelectorAll('[data-wcs-osi][is="checkout-link"]')].filter( + (el) => !el.getAttribute('href').includes('commerce'), + ), + ); + expect(invalidCheckoutLinks.length).toBe(0); + }); + }); + + // @Commerce-DE - Validate commerce CTA and checkout placeholders in DE locale + test(`${features[7].name}, ${features[7].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[7].path}${miloLibs}`; + const { data } = features[7]; + + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + // Validate there are no unresolved commerce placeholders + await test.step('Validate wcs placeholders', async () => { + const unresolvedPlaceholders = await page.evaluate( + () => [...document.querySelectorAll('[data-wcs-osi]')].filter( + (el) => !el.classList.contains('placeholder-resolved'), + ), + ); + expect(unresolvedPlaceholders.length).toBe(0); + }); + + await test.step('Validate Buy now CTA', async () => { + await COMM.buyNowCta.waitFor({ state: 'visible', timeout: 10000 }); + await expect(COMM.buyNowCta).toHaveAttribute('data-promotion-code', data.promo); + await expect(COMM.buyNowCta).toHaveAttribute('href', new RegExp(`${data.promo}`)); + await expect(COMM.buyNowCta).toHaveAttribute('href', new RegExp(`${data.CO}`)); + await expect(COMM.buyNowCta).toHaveAttribute('href', new RegExp(`${data.lang}`)); + }); + + await test.step('Validate Free Trial CTA', async () => { + await COMM.freeTrialCta.waitFor({ state: 'visible', timeout: 10000 }); + await expect(COMM.freeTrialCta).toHaveAttribute('data-promotion-code', data.promo); + await expect(COMM.freeTrialCta).toHaveAttribute('href', new RegExp(`${data.promo}`)); + await expect(COMM.freeTrialCta).toHaveAttribute('href', new RegExp(`${data.CO}`)); + await expect(COMM.freeTrialCta).toHaveAttribute('href', new RegExp(`${data.lang}`)); + await expect(COMM.freeTrialCta).toHaveAttribute('href', new RegExp(`${data.workflow}`)); + }); + + await test.step('Validate regular price display', async () => { + await COMM.price.waitFor({ state: 'visible', timeout: 10000 }); + expect(await COMM.price.innerText()).toContain('€/Jahr'); + expect(await COMM.price.locator('.price-recurrence').innerText()).not.toBe(''); + expect(await COMM.price.locator('.price-unit-type').innerText()).toBe(''); + expect(await COMM.price.locator('.price-tax-inclusivity').innerText()).toBe(''); + await expect(COMM.price).toHaveAttribute('data-promotion-code', data.promo); + }); + + await test.step('Validate optical price display', async () => { + await COMM.priceOptical.waitFor({ state: 'visible', timeout: 10000 }); + expect(await COMM.priceOptical.innerText()).toContain('€/Monat'); + expect(await COMM.priceOptical.locator('.price-recurrence').innerText()).not.toBe(''); + expect(await COMM.priceOptical.locator('.price-unit-type').innerText()).toBe(''); + expect(await COMM.priceOptical.locator('.price-tax-inclusivity').innerText()).toBe(''); + await expect(COMM.priceOptical).toHaveAttribute('data-promotion-code', data.promo); + }); + + await test.step('Validate strikethrough price display', async () => { + await COMM.priceStrikethrough.waitFor({ state: 'visible', timeout: 10000 }); + expect(await COMM.priceStrikethrough.innerText()).toContain('€/Jahr'); + expect(await COMM.priceStrikethrough.locator('.price-recurrence').innerText()).not.toBe(''); + expect(await COMM.priceStrikethrough.locator('.price-unit-type').innerText()).toBe(''); + expect(await COMM.priceStrikethrough.locator('.price-tax-inclusivity').innerText()).toBe(''); + const priceStyle = await COMM.priceStrikethrough.evaluate( + (e) => window.getComputedStyle(e).getPropertyValue('text-decoration'), + ); + expect(await priceStyle).toContain('line-through'); + await expect(COMM.priceStrikethrough).toHaveAttribute('data-promotion-code', data.promo); + }); + }); + + // @Commerce-Old-Promo - Validate promo price WITHOUT old price + test(`${features[8].name},${features[8].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[8].path}${miloLibs}`; + const { data } = features[8]; + + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Validate promo price does not show old price', async () => { + await COMM.price.waitFor({ state: 'visible', timeout: 10000 }); + await expect(COMM.price).toHaveAttribute('data-promotion-code', data.promo); + await expect(COMM.price).not.toHaveAttribute('data-display-old-price', 'true'); + // expect(await COMM.price.innerText()).toContain('US$17.24'); + // expect(await COMM.price.innerText()).not.toContain('US$34.49'); + await expect(await COMM.price.locator('.price').first()).toBeVisible(); + await expect(await COMM.price.locator('.price-strikethrough')).not.toBeVisible(); + }); + }); + + // @Commerce-GB - Validate commerce CTA and checkout placeholders in UK locale + test(`${features[9].name}, ${features[9].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[9].path}${miloLibs}`; + const { data } = features[9]; + + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + // Validate there are no unresolved commerce placeholders + await test.step('Validate wcs placeholders', async () => { + const unresolvedPlaceholders = await page.evaluate( + () => [...document.querySelectorAll('[data-wcs-osi]')].filter( + (el) => !el.classList.contains('placeholder-resolved'), + ), + ); + expect(unresolvedPlaceholders.length).toBe(0); + }); + + await test.step('Validate Buy now CTA', async () => { + await COMM.buyNowCta.waitFor({ state: 'visible', timeout: 10000 }); + await expect(COMM.buyNowCta).toHaveAttribute('data-promotion-code', data.promo); + await expect(COMM.buyNowCta).toHaveAttribute('href', new RegExp(`${data.promo}`)); + await expect(COMM.buyNowCta).toHaveAttribute('href', new RegExp(`${data.CO}`)); + await expect(COMM.buyNowCta).toHaveAttribute('href', new RegExp(`${data.lang}`)); + }); + + await test.step('Validate Free Trial CTA', async () => { + await COMM.freeTrialCta.waitFor({ state: 'visible', timeout: 10000 }); + await expect(COMM.freeTrialCta).toHaveAttribute('data-promotion-code', data.promo); + await expect(COMM.freeTrialCta).toHaveAttribute('href', new RegExp(`${data.promo}`)); + await expect(COMM.freeTrialCta).toHaveAttribute('href', new RegExp(`${data.CO}`)); + await expect(COMM.freeTrialCta).toHaveAttribute('href', new RegExp(`${data.lang}`)); + await expect(COMM.freeTrialCta).toHaveAttribute('href', new RegExp(`${data.workflow}`)); + }); + + await test.step('Validate regular price display', async () => { + await COMM.price.waitFor({ state: 'visible', timeout: 10000 }); + expect(await COMM.price.innerText()).toContain('£'); + expect(await COMM.price.innerText()).toContain('/yr'); + expect(await COMM.price.locator('.price-recurrence').first().innerText()).not.toBe(''); + expect(await COMM.price.locator('.price-unit-type').first().innerText()).toBe(''); + expect(await COMM.price.locator('.price-tax-inclusivity').first().innerText()).toBe(''); + await expect(COMM.price).toHaveAttribute('data-promotion-code', data.promo); + await expect(COMM.price).toHaveAttribute('data-display-old-price', 'true'); + await COMM.price.locator('.price').first().waitFor({ state: 'visible', timeout: 10000 }); + await COMM.price.locator('.price-strikethrough').waitFor({ state: 'visible', timeout: 10000 }); + }); + + await test.step('Validate optical price display', async () => { + await COMM.priceOptical.waitFor({ state: 'visible', timeout: 10000 }); + expect(await COMM.priceOptical.innerText()).toContain('£'); + expect(await COMM.priceOptical.innerText()).toContain('/mo'); + expect(await COMM.priceOptical.locator('.price-recurrence').innerText()).not.toBe(''); + expect(await COMM.priceOptical.locator('.price-unit-type').innerText()).toBe(''); + expect(await COMM.priceOptical.locator('.price-tax-inclusivity').innerText()).toBe(''); + await expect(COMM.priceOptical).toHaveAttribute('data-promotion-code', data.promo); + }); + + await test.step('Validate strikethrough price display', async () => { + await COMM.priceStrikethrough.waitFor({ state: 'visible', timeout: 10000 }); + expect(await COMM.priceStrikethrough.innerText()).toContain('£'); + expect(await COMM.priceStrikethrough.innerText()).toContain('/yr'); + expect(await COMM.priceStrikethrough.locator('.price-recurrence').innerText()).not.toBe(''); + expect(await COMM.priceStrikethrough.locator('.price-unit-type').innerText()).toBe(''); + expect(await COMM.priceStrikethrough.locator('.price-tax-inclusivity').innerText()).toBe(''); + const priceStyle = await COMM.priceStrikethrough.evaluate( + (e) => window.getComputedStyle(e).getPropertyValue('text-decoration'), + ); + expect(await priceStyle).toContain('line-through'); + await expect(COMM.priceStrikethrough).toHaveAttribute('data-promotion-code', data.promo); + }); + }); +}); diff --git a/nala/features/feds/footer/footer.page.js b/nala/features/feds/footer/footer.page.js new file mode 100644 index 0000000000..cb19320cb9 --- /dev/null +++ b/nala/features/feds/footer/footer.page.js @@ -0,0 +1,77 @@ +/* eslint-disable import/no-import-module-exports */ + +export default class FedsFooter { + constructor(page) { + this.page = page; + + // Container Selectors: + this.footerContainer = page.locator('footer.global-footer'); + this.footerSections = page.locator('footer div.feds-menu-section'); + this.footerColumns = page.locator('footer div.feds-menu-column'); + this.footerHeadings = page.locator('footer div.feds-menu-headline'); + + // Change Region Selectors: + this.changeRegionContainer = page.locator('div.feds-regionPicker-wrapper'); + this.changeRegionButton = page.locator('div.feds-regionPicker-wrapper a.feds-regionPicker'); + this.changeRegionModal = page.locator('div#langnav'); + this.changeRegionDropDown = page.locator('div.region-selector'); + this.changeRegionCloseButton = page.locator('button.dialog-close'); + + // Legal Selectors: + this.legalContainer = page.locator('div.feds-footer-legalWrapper'); + this.legalSections = page.locator('p.feds-footer-privacySection'); + this.legalLinks = page.locator('div.feds-footer-legalWrapper a'); + this.legalCopyright = page.locator('span.feds-footer-copyright'); + this.privacyLink = page.locator('a[href*="privacy.html"]'); + this.termsOfUseLink = page.locator('a[href*="terms.html"]'); + this.cookiePreferencesLink = page.locator('a[href*="#openPrivacy"]'); + this.doNotSellInformationLink = page.locator('a[href*="ca-rights.html"]'); + this.adChoicesLink = page.locator('a[href*="opt-out.html"]'); + this.adChoicesLogo = page.locator('svg.feds-adChoices-icon'); + + // Adobe Socials Selectors: + this.twitterIcon = page.locator('ul.feds-social a[aria-label="twitter"]'); + this.linkedInIcon = page.locator('ul.feds-social a[aria-label="linkedin"]'); + this.facebookIcon = page.locator('ul.feds-social a[aria-label="facebook"]'); + this.instagramIcon = page.locator('ul.feds-social a[aria-label="instagram"]'); + this.socialContainer = page.locator('ul.feds-social'); + this.socialIcons = page.locator('ul.feds-social li'); + + // Featured Products Selectors: + this.featuredProductsContainer = page.locator('div.feds-featuredProducts'); + this.featuredProducts = page.locator('div.feds-featuredProducts a'); + this.downloadAdobeExpress = page.locator('footer a[daa-ll="Adobe_Express"]'); + this.downloadAdobePhotoshop = page.locator('footer a[daa-ll="Photoshop"]'); + this.downloadAdobeIllustrator = page.locator('footer a[daa-ll="Illustrator"]'); + + // Footer Section Selectors: + this.footerCreativeCloud = page.locator(".feds-footer-wrapper a[href*='creativecloud.html']"); + this.footerViewAllProducts = page.locator(".feds-navLink[href*='/products/catalog.html?']"); + this.footerCreativeCloudForBusiness = page.locator(".feds-footer-wrapper [href$='cloud/business.html']").nth(0); + this.footerAcrobatForBusiness = page.locator(".feds-footer-wrapper a[href$='acrobat/business.html']"); + this.footerDiscountsForStudentsAndTeachers = page.locator(".feds-footer-wrapper a[href$='buy/students.html']"); + this.footerDigitalLearningSolutions = page.locator("a[href$='/elearning.html']"); + this.footerAppsforiOS = page.locator("a[href*='id852473028']"); + this.footerAppsforAndroid = page.locator("a[href*='id=com.adobe.cc']"); + this.footerWhatIsExperienceCloud = page.locator('.feds-footer-wrapper a[href*="business"]').nth(4); + this.footerTermsOfUse = page.locator('a[href*="experiencecloudterms"]'); + this.footerDownloadAndInstall = page.locator('.feds-footer-wrapper a[href*="download-install.html"]'); + this.footerGenuineSoftware = page.locator('a[href*="genuine.html"]'); + this.footerAdobeBlog = page.locator('.feds-navLink[href*="blog"]').nth(1); + this.footerAdobeDeveloper = page.locator('a[href*="developer"]'); + this.footerLogInToYourAccount = page.locator('.feds-footer-wrapper a[href*="account.adobe"]').nth(0); + this.footerAbout = page.locator('.feds-footer-wrapper [href*="about-adobe.html"]').nth(0); + this.footerIntegrity = page.locator('a[href*="integrity.html"]'); + this.footerAdobeBlogSecond = page.locator('.feds-navLink[href*="blog"]').nth(0); + this.protectMyPersonalData = page.locator('.feds-footer-legalWrapper a:nth-of-type(4)'); + this.termsOfUseLinkTwo = page.locator('a[href*="terms.html"]').nth(1); + + // Featured Product Selectors: + this.footerAdobeAcrobatReaderlogo = page.locator('a[href$="reader/"]'); + this.footerAdobeExpresslogo = page.locator('a[href$="Z2G1FSYV&mv=other"]:nth-of-type(2)'); + this.footerPhotoshoplogo = page.locator('a[href$="photoshop/free-trial-download.html"]'); + this.footerIllustratorlogo = page.locator('a[href$="illustrator/free-trial-download.html"]'); + } + + // >> FEDS Footer methods declared here << +} diff --git a/nala/features/feds/footer/footer.spec.js b/nala/features/feds/footer/footer.spec.js new file mode 100644 index 0000000000..47db482828 --- /dev/null +++ b/nala/features/feds/footer/footer.spec.js @@ -0,0 +1,26 @@ +module.exports = { + name: 'Footer Block', + features: [ + { + name: '@FEDS-Default-Footer', + path: [ + '/drafts/nala/blocks/footer/feds-default-footer', + ], + tags: '@milo @feds @footer @smoke @regression', + }, + { + name: '@FEDS-Skinny-Footer', + path: [ + '/drafts/nala/blocks/footer/feds-skinny-footer', + ], + tags: '@milo @feds @footer @smoke @regression', + }, + { + name: '@FEDS-Privacy-Footer', + path: [ + '/drafts/nala/blocks/footer/feds-privacy-footer', + ], + tags: '@milo @feds @footer @smoke @regression', + }, + ], +}; diff --git a/nala/features/feds/footer/footer.test.js b/nala/features/feds/footer/footer.test.js new file mode 100644 index 0000000000..ae67478fc3 --- /dev/null +++ b/nala/features/feds/footer/footer.test.js @@ -0,0 +1,164 @@ +/* eslint-disable no-await-in-loop, import/extensions */ +import { expect, test } from '@playwright/test'; +import { features } from './footer.spec.js'; +import FedsFooter from './footer.page.js'; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Footer Block Test Suite', () => { + // FEDS Default Footer Checks: + test(`${features[0].name}, ${features[0].tags}`, async ({ page, baseURL }) => { + const Footer = new FedsFooter(page); + console.info(`[FEDSInfo] Checking page: ${baseURL}${features[0].path}${miloLibs}`); + + await test.step('Navigate to FEDS Default Footer page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('Check FEDS Default Footer critical elements', async () => { + // Scroll FEDS Footer into viewport: + await Footer.legalContainer.scrollIntoViewIfNeeded(); + // Wait for FEDS Footer to be visible: + await Footer.footerContainer.waitFor({ state: 'visible', timeout: 5000 }); + // Check FEDS Footer critical elements: + await expect(Footer.legalContainer).toBeVisible(); + await expect(Footer.socialContainer).toBeVisible(); + await expect(Footer.footerContainer).toBeVisible(); + await expect(Footer.changeRegionContainer).toBeVisible(); + // !Note: Footer featuredProducts not appearing in NALA. Possible BUG! + // await expect(Footer.featuredProductsContainer).toBeVisible(); + await expect(Footer.footerColumns).toHaveCount(5); + + // updated the footer section and heading content as per consuming sites + // milo=6, cc=9 and so on + await expect([4, 6, 9].includes(await Footer.footerSections.count())).toBeTruthy(); + await expect([4, 6, 9].includes(await Footer.footerHeadings.count())).toBeTruthy(); + + await expect(Footer.socialIcons).toHaveCount(4); + await expect(Footer.legalLinks).toHaveCount(5); + }); + + await test.step('Check ChangeRegion functionality', async () => { + await Footer.changeRegionButton.click(); + await expect(Footer.changeRegionModal).toBeVisible(); + await Footer.changeRegionCloseButton.click(); + await expect(Footer.changeRegionModal).not.toBeVisible(); + }); + }); + + // FEDS Skinny Footer Checks: + test(`${features[1].name}, ${features[1].tags}`, async ({ page, baseURL }) => { + const Footer = new FedsFooter(page); + console.info(`[FEDSInfo] Checking page: ${baseURL}${features[1].path}${miloLibs}`); + + await test.step('Navigate to FEDS Skinny Footer page', async () => { + await page.goto(`${baseURL}${features[1].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}${miloLibs}`); + }); + + await test.step('Check FEDS Skinny Footer critical elements', async () => { + // Scroll FEDS Footer into viewport: + await Footer.legalContainer.scrollIntoViewIfNeeded(); + // Wait for FEDS Footer to be visible: + await Footer.footerContainer.waitFor({ state: 'visible', timeout: 5000 }); + // Check FEDS Footer critical elements: + await expect(Footer.legalContainer).toBeVisible(); + await expect(Footer.socialContainer).toBeVisible(); + await expect(Footer.footerContainer).toBeVisible(); + await expect(Footer.changeRegionContainer).toBeVisible(); + + // await expect(Footer.featuredProducts).toHaveCount(0); + // updated the featuredProducts count as per consuming sites + // milo=0, cc=4 and so on + expect([0, 4].includes(await Footer.featuredProducts.count())).toBeTruthy(); + + const featuredProductsCount = await Footer.featuredProducts.count(); + + if (featuredProductsCount === 0) { + await expect(Footer.featuredProductsContainer).not.toBeVisible(); + } else { + await expect(Footer.featuredProductsContainer).toBeVisible(); + } + + await expect(Footer.legalLinks).toHaveCount(5); + await expect(Footer.socialIcons).toHaveCount(4); + + // await expect(Footer.footerColumns).toHaveCount(0); + // await expect(Footer.footerSections).toHaveCount(0); + // await expect(Footer.footerHeadings).toHaveCount(0); + + const footerSectionsCount = await Footer.featuredProducts.count(); + + if (footerSectionsCount === 0) { + await expect(Footer.footerColumns).not.toBeVisible(); + await expect(Footer.footerSections).not.toBeVisible(); + await expect(Footer.footerHeadings).not.toBeVisible(); + } else { + expect([0, 5].includes(await Footer.footerColumns.count())).toBeTruthy(); + expect([4, 6, 9].includes(await Footer.footerSections.count())).toBeTruthy(); + expect([4, 6, 9].includes(await Footer.footerHeadings.count())).toBeTruthy(); + } + }); + + await test.step('Check ChangeRegion functionality', async () => { + await Footer.changeRegionButton.click(); + await expect(Footer.changeRegionModal).toBeVisible(); + await Footer.changeRegionCloseButton.click(); + await expect(Footer.changeRegionModal).not.toBeVisible(); + }); + }); + + // FEDS Privacy Footer Checks: + test(`${features[2].name}, ${features[2].tags}`, async ({ page, baseURL }) => { + const Footer = new FedsFooter(page); + console.info(`[FEDSInfo] Checking page: ${baseURL}${features[2].path}${miloLibs}`); + + await test.step('Navigate to FEDS Privacy Footer page', async () => { + await page.goto(`${baseURL}${features[2].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[2].path}${miloLibs}`); + }); + + await test.step('Check FEDS Privacy Footer critical elements', async () => { + // Scroll FEDS Footer into viewport: + await Footer.legalContainer.scrollIntoViewIfNeeded(); + // Wait for FEDS Footer to be visible: + await Footer.footerContainer.waitFor({ state: 'visible', timeout: 5000 }); + // Check FEDS Footer critical elements: + await expect(Footer.legalContainer).toBeVisible(); + await expect(Footer.socialContainer).toBeVisible(); + await expect(Footer.footerContainer).toBeVisible(); + await expect(Footer.changeRegionContainer).toBeVisible(); + await expect(Footer.featuredProductsContainer).toBeVisible(); + + await expect(Footer.footerColumns).toHaveCount(5); + + // await expect(Footer.footerSections).toHaveCount(9) + // await expect(Footer.footerHeadings).toHaveCount(9) + // await expect(Footer.featuredProducts).toHaveCount(3); + // await expect(Footer.legalSections).toHaveCount(2); + await expect(Footer.socialIcons).toHaveCount(4); + await expect(Footer.legalLinks).toHaveCount(5); + + // updated the footer section and heading content equal or greater + // than 6, to pass tests on cc pages. + expect([4, 6, 9].includes(await Footer.footerSections.count())).toBeTruthy(); + expect([4, 6, 9].includes(await Footer.footerHeadings.count())).toBeTruthy(); + expect([3, 4].includes(await Footer.featuredProducts.count())).toBeTruthy(); + expect([1, 2].includes(await Footer.legalSections.count())).toBeTruthy(); + expect([4].includes(await Footer.socialIcons.count())).toBeTruthy(); + expect([5].includes(await Footer.legalLinks.count())).toBeTruthy(); + }); + + await test.step('Check ChangeRegion functionality', async () => { + await Footer.changeRegionButton.click(); + await expect(Footer.changeRegionDropDown).toBeVisible(); + await expect(Footer.changeRegionModal).not.toBeVisible(); + await Footer.changeRegionButton.click(); + await expect(Footer.changeRegionDropDown).not.toBeVisible(); + }); + }); +}); diff --git a/nala/features/feds/header/header.page.js b/nala/features/feds/header/header.page.js new file mode 100644 index 0000000000..df43a70215 --- /dev/null +++ b/nala/features/feds/header/header.page.js @@ -0,0 +1,110 @@ +/* eslint-disable import/no-import-module-exports */ +import { expect } from '@playwright/test'; + +export default class FedsHeader { + constructor(page) { + this.page = page; + + // GNAV selectors: + this.gnavLogo = page.locator('a.gnav-logo'); + this.headerContainer = page.locator('header.global-navigation'); + this.mainNavLogo = page.locator('a.feds-brand, a.gnav-brand'); + this.mainNavContainer = page.locator('nav.feds-topnav, .gnav-wrapper'); + this.megaMenuToggle = page.locator('button.feds-navLink.feds-navLink--hoverCaret, .section-menu').first(); + this.megaMenuContainer = page.locator('section.feds-navItem--megaMenu div.feds-popup, .section-menu .gnav-menu-container'); + this.megaMenuColumn = page.locator('section.feds-navItem--megaMenu div.feds-menu-section'); + + // GNAV action selectors: + this.signInButton = page.locator('button.feds-signIn').first(); + this.searchIcon = page.locator('button.feds-search-trigger, button.gnav-search-button'); + this.searchInput = page.locator('input.feds-search-input, input.gnav-search-input'); + this.closeSearch = page.locator('span.feds-search-close, button.gnav-search-button[daa-lh="header|Close"]'); + this.searchResults = page.locator('#feds-search-results, .gnav-search-results'); + this.advancedSearchLink = page.locator('#feds-search-results li a, .gnav-search-results li a'); + + this.profileIcon = page.locator('button.feds-profile-button'); + this.profileModal = page.locator('div#feds-profile-menu'); + this.profileName = page.locator('p.feds-profile-name'); + this.profileEmail = page.locator('p.feds-profile-email'); + this.profileAccountLink = page.locator('p.feds-profile-account'); + this.profileDetails = page.locator('div.feds-profile-details'); + this.profileSignOut = page.locator('a.feds-profile-action'); + + // GNAV breadcrumb selectors: + this.breadcrumbList = page.locator('nav.feds-breadcrumbs ul'); + this.breadcrumbElems = page.locator('nav.feds-breadcrumbs li'); + this.breadcrumbContainer = page.locator('nav.feds-breadcrumbs'); + + // Promo-bar selectors: + this.promoBarContainer = page.locator('div.aside.promobar'); + this.promoBarBackground = this.promoBarContainer.locator('div.background'); + this.promoBarForeground = this.promoBarContainer.locator('div.foreground'); + this.promoBarContent = this.promoBarContainer.locator('div.desktop-up'); + this.promoBarText = this.promoBarContainer.locator('div.desktop-up p.content-area'); + this.promoBarBtn = this.promoBarContainer.locator('div.desktop-up p.action-area a'); + this.promoBarMobileContent = this.promoBarContainer.locator('div.mobile-up'); + this.promoBarMobileText = this.promoBarContainer.locator('div.mobile-up p.content-area'); + this.promoBarMobileBtn = this.promoBarContainer.locator('div.mobile-up p.action-area a'); + this.promoBarTabletContent = this.promoBarContainer.locator('div.tablet-up'); + this.promoBarTabletText = this.promoBarContainer.locator('div.tablet-up p.content-area'); + this.promoBarTabletBtn = this.promoBarContainer.locator('div.tablet-up p.action-area a'); + } + + /** + * Opens the User Profile via click on GNAV profile icon. + * !Note: Only use after user was logged in! + * @param {none} + * @return {Promise} PlayWright promise + */ + async openUserProfile() { + await this.profileIcon.waitFor({ state: 'visible', timeout: 10000 }); + await this.profileIcon.click(); + await expect(this.profileModal).toBeVisible(); + } + + /** + * Closes the User Profile via click on GNAV profile icon. + * !Note: Only use after user was logged in! + * @param {none} + * @return {Promise} PlayWright promise + */ + async closeUserProfile() { + await this.profileIcon.waitFor({ state: 'visible', timeout: 10000 }); + await this.profileIcon.click(); + await expect(this.profileModal).not.toBeVisible(); + } + + /** + * Checks the elements of the User Profile component. + * @param {none} + * @return {Promise} PlayWright promise + */ + async checkUserProfile() { + await expect(this.profileName).toBeVisible(); + await expect(this.profileEmail).toBeVisible(); + await expect(this.profileSignOut).toBeVisible(); + await expect(this.profileAccountLink).toBeVisible(); + } + + /** + * Opens the search bar via click fron GNAV search icon. + * @param {none} + * @return {Promise} PlayWright promise + */ + async openSearchBar() { + await this.searchIcon.waitFor({ state: 'visible', timeout: 10000 }); + await this.searchIcon.click(); + await expect(this.searchInput).toBeVisible(); + } + + /** + * Closes the search bar via click fron GNAV search icon. + * @param {none} + * @return {Promise} PlayWright promise + */ + async closeSearchBar() { + await this.closeSearch.waitFor({ state: 'visible', timeout: 10000 }); + await this.closeSearch.click(); + await expect(this.searchInput).not.toBeVisible(); + } +} diff --git a/nala/features/feds/header/header.spec.js b/nala/features/feds/header/header.spec.js new file mode 100644 index 0000000000..d0e754d39f --- /dev/null +++ b/nala/features/feds/header/header.spec.js @@ -0,0 +1,12 @@ +module.exports = { + name: 'Header Block', + features: [ + { + name: '@FEDS-Header-Checks', + path: [ + '/drafts/nala/blocks/header/feds-header-page', + ], + tags: '@milo @feds @header @nopr @smoke @regression', + }, + ], +}; diff --git a/nala/features/feds/header/header.test.js b/nala/features/feds/header/header.test.js new file mode 100644 index 0000000000..9969a551fc --- /dev/null +++ b/nala/features/feds/header/header.test.js @@ -0,0 +1,52 @@ +/* eslint-disable no-await-in-loop, import/extensions */ +import { expect, test } from '@playwright/test'; +import { features } from './header.spec.js'; +import FedsHeader from './header.page.js'; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Header Block Test Suite', () => { + // FEDS Default Header Checks: + test(`${features[0].name}, ${features[0].tags}`, async ({ page, baseURL }) => { + const Header = new FedsHeader(page); + console.info(`[FEDSInfo] Checking page: ${baseURL}${features[0].path}${miloLibs}`); + + await test.step('Navigate to FEDS HEADER page', async () => { + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + }); + + await test.step('Check HEADER block content', async () => { + // Wait for FEDS GNAV to be visible: + await Header.mainNavContainer.waitFor({ state: 'visible', timeout: 5000 }); + // Check HEADER block content: + await expect(Header.mainNavLogo).toBeVisible(); + + // skipping the step for PR branch runs + // working on better workaround soloution + // await expect(Header.signInButton).toBeVisible(); + }); + + await test.step('Check HEADER search component', async () => { + // adding the below check to accommodate testing on consuming sites + const isSearchIconVisible = await Header.searchIcon.isVisible(); + if (isSearchIconVisible) { + await test.step('Check HEADER search component', async () => { + await Header.openSearchBar(); + await Header.closeSearchBar(); + }); + } else { + console.info('Search icon is not visible, skipping the search component test.'); + } + }); + + await test.step('Check HEADER block mega menu component', async () => { + await Header.megaMenuToggle.waitFor({ state: 'visible', timeout: 5000 }); + await Header.megaMenuToggle.click(); + await expect(Header.megaMenuContainer).toBeVisible(); + await Header.megaMenuToggle.click(); + await expect(Header.megaMenuContainer).not.toBeVisible(); + }); + }); +}); diff --git a/nala/features/feds/login/login.page.js b/nala/features/feds/login/login.page.js new file mode 100644 index 0000000000..e2baaee3e4 --- /dev/null +++ b/nala/features/feds/login/login.page.js @@ -0,0 +1,118 @@ +// eslint-disable-next-line import/no-import-module-exports +import { expect } from '@playwright/test'; + +export default class FedsLogin { + constructor(page) { + this.page = page; + + this.loginButton = page.locator('button#sign_in'); + this.loginForm = page.locator('form#adobeid_signin'); + this.emailField = page.locator('input#adobeid_username'); + this.passwordField = page.locator('input#adobeid_password'); + + this.loggedInState = page.locator('img.feds-profile-img'); + this.loginWithEnterpriseId = page.locator('a#enterprise_signin_link'); + this.forgotPasswordLink = page.locator('a#adobeid_trouble_signing_in'); + + this.loginWithFacebook = page.locator('a.mod-facebook'); + this.loginWithGoogle = page.locator('a.mod-google'); + this.loginWithApple = page.locator('a.mod-apple'); + + this.appEmailForm = page.locator('form#EmailForm'); + this.appPasswordForm = page.locator('form#PasswordForm'); + this.appEmailField = page.locator('input#EmailPage-EmailField'); + this.appPasswordField = page.locator('input#PasswordPage-PasswordField'); + this.appVisibilityToggle = page.locator('button.PasswordField-VisibilityToggle'); + this.appPasswordContinue = page.locator('button[data-id^="EmailPage"]'); + this.appLoginContinue = page.locator('button[data-id^="PasswordPage"]'); + this.personalAccountLogo = page.locator('img[alt="Personal Account"]'); + this.selectAccountForm = page.locator('div[data-id="Profile"]'); + + this.appEmailFieldSelector = page.locator('input#EmailPage-EmailField'); + this.appPasswordFieldSelector = page.locator('input#PasswordPage-PasswordField'); + this.codePadChallenge = page.locator('div[data-id="ChallengeCodePage"]'); + } + + /** + * Login on the IMS APP login form with email & password. + * @param {string} email + * @param {string} password + * @return {Promise} PlayWright promise + */ + async loginOnAppForm(email, password) { + console.info('[EuroLogin] APP login form identified!'); + // Check EMAIL & PASSWWORD status: + expect(process.env.IMS_EMAIL, 'ERROR: No environment variable found for IMS_EMAIL').toBeTruthy(); + expect(process.env.IMS_PASS, 'ERROR: No environment variable found for IMS_PASS.').toBeTruthy(); + console.info(`[EuroLogin] Logging in with '${email}' account ...`); + // Wait for page to load & stabilize: + await this.page.waitForLoadState('domcontentloaded'); + // Wait for the SUSI login form to load: + await this.appEmailForm.waitFor({ state: 'visible', timeout: 15000 }); + // Insert account email & click 'Continue': + await this.appEmailField.waitFor({ state: 'visible', timeout: 15000 }); + await this.appEmailField.fill(email); + await this.appPasswordContinue.waitFor({ state: 'visible', timeout: 15000 }); + await expect(this.appPasswordContinue).toHaveText('Continue'); + await this.appPasswordContinue.click(); + // Wait for page to load & stabilize: + await this.page.waitForTimeout(5000); + // Insert account password & click 'Continue': + // await this.appPasswordForm.waitFor({state: 'visible', timeout: 15000}); + await this.appPasswordField.waitFor({ state: 'visible', timeout: 15000 }); + await this.appPasswordField.fill(password); + await this.appLoginContinue.waitFor({ state: 'visible', timeout: 15000 }); + await expect(this.appLoginContinue).toHaveText('Continue'); + await this.appLoginContinue.click(); + // Check if login process was successful: + await this.loggedInState.waitFor({ state: 'visible', timeout: 20000 }); + console.info(`[EuroLogin] Successfully logged-in as '${email}' (via APP login form).`); + } + + /** + * Login on the IMS SUSI login form with email & password. + * @param {string} email + * @param {string} password + * @return {Promise} PlayWright promise + */ + async loginOnSusiForm(email, password) { + console.info('[EuroLogin] SUSI login form identified!'); + // Check EMAIL & PASSWWORD status: + expect(process.env.IMS_EMAIL, 'ERROR: No environment variable found for IMS_EMAIL').toBeTruthy(); + expect(process.env.IMS_PASS, 'ERROR: No environment variable found for IMS_PASS.').toBeTruthy(); + console.info(`[EuroLogin] Logging in with '${email}' account ...`); + // Wait for page to load & stabilize: + await this.page.waitForLoadState('networkidle'); + // Wait for the SUSI login form to load: + await this.loginForm.waitFor({ state: 'visible', timeout: 15000 }); + await this.emailField.fill(email); + // !Note: Email field has short client-side validation (load). + // Password field is not interactable during that time. + await this.page.keyboard.press('Tab'); + // Wait for page to load & stabilize: + await this.page.waitForLoadState('domcontentloaded'); + // Set password & click 'Continue': + await this.appPasswordForm.waitFor({ state: 'visible', timeout: 15000 }); + await this.passwordField.waitFor({ state: 'visible', timeout: 15000 }); + await this.passwordField.fill(password); + // Complete the login flow: + await this.loginButton.waitFor({ state: 'visible', timeout: 15000 }); + await this.loginButton.click(); + // Check if login process was successful: + await this.loggedInState.waitFor({ state: 'visible', timeout: 20000 }); + console.info(`[EuroLogin] Successfully logged-in as '${email}' (via SUSI login form).`); + } + + /** + * Toggles the visibility of the IMS password field. + * @param {string} password + * @return {Promise} PlayWright promise + */ + async TogglePasswordVisibility(password) { + await this.appVisibilityToggle.waitFor({ state: 'visible', timeout: 15000 }); + await this.appVisibilityToggle.click(); + await expect(this.appPasswordField).toContain(password); + await this.appVisibilityToggle.click(); + await this.appVisibilityToggle.waitFor({ state: 'visible', timeout: 15000 }); + } +} diff --git a/nala/features/georouting/georouting.page.js b/nala/features/georouting/georouting.page.js index 8388b6bd71..0df953c8eb 100644 --- a/nala/features/georouting/georouting.page.js +++ b/nala/features/georouting/georouting.page.js @@ -66,7 +66,7 @@ export default class Georouting { await expect(this.geoModal.locator(`//a[text()="${data[tab].button}"]`)).toBeVisible({ timeout: 1000 }); await expect(this.geoModal.locator(`//a[text()="${data[tab].link}"]`).nth(index)).toBeVisible({ timeout: 1000 }); await expect(this.geoModal.locator(`//img[@alt="${data[tab].flag}"]`)).toBeVisible({ timeout: 1000 }); - index = +1; + index += 1; } return true; diff --git a/nala/features/georouting/georouting.test.js b/nala/features/georouting/georouting.test.js index 53b9cd1c61..b3524815ed 100644 --- a/nala/features/georouting/georouting.test.js +++ b/nala/features/georouting/georouting.test.js @@ -1,4 +1,3 @@ -/* eslint-disable import/no-extraneous-dependencies, max-len, no-console */ import { expect, test } from '@playwright/test'; import { features } from './georouting.spec.js'; import Georouting from './georouting.page.js'; diff --git a/nala/features/imslogin/imslogin.page.js b/nala/features/imslogin/imslogin.page.js new file mode 100644 index 0000000000..c5a4f2d9a0 --- /dev/null +++ b/nala/features/imslogin/imslogin.page.js @@ -0,0 +1,23 @@ +module.exports = { + '@gnav-signin': '.gnav-signin', + '@gnav-profile-button': '.gnav-profile-button', + '@gnav-signout': 'text=Sign Out', + '@gnav-viewaccount': '.gnav-profile-header', + '@gnav-manageTeam': 'text=Manage Team', + '@email': '#EmailPage-EmailField', + '@password': '#PasswordPage-PasswordField', + '@email-continue-btn': '[data-id=EmailPage-ContinueButton]', + '@verify-continue-btn': '[data-id=Page-PrimaryButton]', + '@password-reset': 'text=Reset your password', + '@password-continue-btn': '[data-id=PasswordPage-ContinueButton]', + '@apple-signin': '[data-id=EmailPage-AppleSignInButton]', + '@google-signin': '[data-id=EmailPage-GoogleSignInButton]', + '@facebook-signin': '[data-id=EmailPage-FacebookSignInButton]', + '@page-heading': '.spectrum-Heading1', + '@gnav-ec-signin': '[daa-ll=Experience_Cloud-1]', + '@gnav-comm-signin': '[daa-ll=Commerce__Magento-2]', + '@gnav-multi-signin': '[daa-ll=Adobe_Account-5]', + '@gnav-app-launcher': '.gnav-applications-button', + '@cc-app-launcher': '#navmenu-apps >> ul >> li:nth-child(1) >> a', + '@app-launcher-list': '.apps >> li', +}; diff --git a/nala/features/osttools/ost.page.js b/nala/features/osttools/ost.page.js new file mode 100644 index 0000000000..68d7586370 --- /dev/null +++ b/nala/features/osttools/ost.page.js @@ -0,0 +1,31 @@ +export default class OSTPage { + constructor(page) { + this.page = page; + + this.searchField = page.locator('//input[contains(@data-testid,"search")]'); + this.productList = page.locator('//span[contains(@class,"productName")]'); + this.planType = page.locator( + '//button/span[contains(@class, "spectrum-Dropdown-label") and (.//ancestor::div/span[contains(text(),"plan type")])]', + ); + this.offerType = page.locator( + '//button/span[contains(@class, "spectrum-Dropdown-label") and (.//ancestor::div/span[contains(text(),"offer type")])]', + ); + this.nextButton = page.locator('//button[contains(@data-testid, "nextButton")]/span'); + this.price = page.locator('//div[@data-type="price"]/span'); + this.priceOptical = page.locator('//div[contains(@data-type, "priceOptical")]/span'); + this.priceStrikethrough = page.locator('//div[contains(@data-type, "priceStrikethrough")]/span'); + this.termCheckbox = page.locator('//input[@value="displayRecurrence"]'); + this.unitCheckbox = page.locator('//input[@value="displayPerUnit"]'); + this.taxlabelCheckbox = page.locator('//input[@value="displayTax"]'); + this.taxInlcusivityCheckbox = page.locator('//input[@value="forceTaxExclusive"]'); + this.oldPrice = page.locator('//input[@value="displayOldPrice"]'); + this.priceUse = page.locator('button:near(h4:text("Price"))').first(); + this.priceOpticalUse = page.locator('button:near(:text("Optical price"))').first(); + this.priceStrikethroughUse = page.locator('button:near(:text("Strikethrough price"))').first(); + this.checkoutTab = page.locator('//div[@data-key="checkout"]'); + this.checkoutLink = page.locator('//a[@data-type="checkoutUrl"]'); + this.workflowMenu = page.locator('button:near(label:text("Workflow"))').first(); + this.promoField = page.locator('//input[contains(@class, "spectrum-Textfield-input")]'); + this.cancelPromo = page.locator('button:right-of(span:text("Promotion:"))').first(); + } +} diff --git a/nala/features/osttools/ost.spec.js b/nala/features/osttools/ost.spec.js new file mode 100644 index 0000000000..ec5e68f9c1 --- /dev/null +++ b/nala/features/osttools/ost.spec.js @@ -0,0 +1,128 @@ +module.exports = { + name: 'Offer Selector Tool', + features: [ + { + tcid: '0', + name: '@OST-Search-OfferID', + path: '/tools/ost', + data: { + offerID: '0ADF92A6C8514F2800BE9E87DB641D2A', + productName: 'Photoshop', + productNameShort: 'phsp', + planType: 'PUF', + offerType: 'TRIAL', + price: 'US$263.88', + opticalPrice: 'US$21.99', + term: '/yr', + opticalTerm: '/mo', + unit: 'per license', + taxLabel: 'excl. tax', + }, + browserParams: '?token=', + tags: '@ost @commerce @regression @nopr', + }, + { + tcid: '1', + name: '@OST-Offer-Entitlements', + path: '/tools/ost', + data: { + offerID: '0ADF92A6C8514F2800BE9E87DB641D2A', + planType: 'PUF', + offerType: 'TRIAL', + }, + browserParams: '?token=', + tags: '@ost @commerce @regression @nopr', + + }, + { + tcid: '2', + name: '@OST-Offer-Price', + path: '/tools/ost', + data: { + offerID: '0ADF92A6C8514F2800BE9E87DB641D2A', + price: 'US$263.88', + opticalPrice: 'US$21.99', + term: '/yr', + opticalTerm: '/mo', + unit: 'per license', + taxLabel: 'excl. tax', + }, + browserParams: '?token=', + tags: '@ost @commerce @regression @nopr', + }, + { + tcid: '3', + name: '@OST-Term', + path: '/tools/ost', + data: { + offerID: '0ADF92A6C8514F2800BE9E87DB641D2A', + term: '/yr', + opticalTerm: '/mo', + }, + browserParams: '?token=', + tags: '@ost @commerce @regression @nopr', + }, + { + tcid: '4', + name: '@OST-Unit', + path: '/tools/ost', + data: { + offerID: '0ADF92A6C8514F2800BE9E87DB641D2A', + unit: 'per license', + }, + browserParams: '?token=', + tags: '@ost @commerce @regression @nopr', + }, + { + tcid: '5', + name: '@OST-TaxLabel', + path: '/tools/ost', + data: { + offerID: '0ADF92A6C8514F2800BE9E87DB641D2A', + taxLabel: 'excl. tax', + }, + browserParams: '?token=', + tags: '@ost @commerce @regression @nopr', + }, + { + tcid: '6', + name: '@OST-TaxInclusivity', + path: '/tools/ost', + data: { offerID: '0ADF92A6C8514F2800BE9E87DB641D2A' }, + browserParams: '?token=', + tags: '@ost @commerce @regression @nopr', + }, + { + tcid: '7', + name: '@OST-Price-Promo', + path: '/tools/ost', + data: { + offerID: '0ADF92A6C8514F2800BE9E87DB641D2A', + promo: 'testpromo', + }, + browserParams: '?token=', + tags: '@ost @commerce @regression @nopr', + }, + { + tcid: '8', + name: '@OST-Checkout-Link', + path: '/tools/ost', + data: { + offerID: '0ADF92A6C8514F2800BE9E87DB641D2A', + workflowStep_1: 'email', + workflowStep_2: 'recommendation', + promo: 'testpromo', + }, + browserParams: '?token=', + tags: '@ost @commerce @regression @nopr', + }, + { + tcid: '9', + name: '@OST-OldPrice', + path: '/tools/ost', + data: { offerID: '0ADF92A6C8514F2800BE9E87DB641D2A' }, + browserParams: '?token=', + tags: '@ost @commerce @f1 S@regression @nopr', + }, + ], +}; diff --git a/nala/features/osttools/ost.test.js b/nala/features/osttools/ost.test.js new file mode 100644 index 0000000000..dc87a39aac --- /dev/null +++ b/nala/features/osttools/ost.test.js @@ -0,0 +1,695 @@ +import { expect, test } from '@playwright/test'; +import { features } from './ost.spec.js'; +import OSTPage from './ost.page.js'; +import ims from '../../libs/imslogin.js'; + +let authToken; +let adobeIMS; +let OST; + +test.beforeAll(async ({ browser }) => { + test.slow(); + // Skip tests on github actions and PRs, run only on Jenkins + if (process.env.GITHUB_ACTIONS) test.skip(); + + const page = await browser.newPage(); + await page.goto('https://www.adobe.com/creativecloud/plans.html?mboxDisable=1&adobe_authoring_enabled=true'); + const signinBtn = page.locator('#universal-nav button.profile-comp').first(); + await expect(signinBtn).toBeVisible(); + await signinBtn.click(); + await page.waitForURL('**/auth.services.adobe.com/en_US/index.html**/'); + features[0].url = 'https://www.adobe.com/creativecloud/plans.html?mboxDisable=1&adobe_authoring_enabled=true'; + await ims.fillOutSignInForm(features[0], page); + await expect(async () => { + const response = await page.request.get(features[0].url); + expect(response.status()).toBe(200); + }).toPass(); + authToken = await page.evaluate(() => adobeIMS.getAccessToken().token); +}); + +test.beforeEach(async ({ page }) => { + OST = new OSTPage(page); +}); + +test.describe('OST page test suite', () => { + // Verify OST search by offer ID + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[0].path}`); + + const testPage = `${baseURL}${features[0].path}${features[0].browserParams}${authToken}`; + const { data } = features[0]; + + await test.step('Open Offer Selector Tool', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Enter Offer ID in the search field', async () => { + await OST.searchField.fill(data.offerID); + await page.waitForTimeout(2000); + }); + + await test.step('Validate search results', async () => { + await OST.productList.first().waitFor({ state: 'visible', timeout: 10000 }); + const skus = OST.productList; + expect(await skus.count()).toBeLessThanOrEqual(2); + expect(await skus.nth(0).innerText()).toContain(data.productName); + expect(await skus.nth(1).innerText()).toContain(data.productNameShort); + }); + }); + + // Verify OST offer entitlements + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[1].path}`); + + const testPage = `${baseURL}${features[1].path}${features[1].browserParams}${authToken}`; + const { data } = features[1]; + + await test.step('Open Offer Selector Tool', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Enter Offer ID in the search field', async () => { + await OST.searchField.fill(data.offerID); + await page.waitForTimeout(2000); + }); + + await test.step('Validate entitlements', async () => { + await OST.planType.waitFor({ state: 'visible', timeout: 10000 }); + await OST.offerType.waitFor({ state: 'visible', timeout: 10000 }); + expect(await OST.planType.innerText()).toContain(data.planType); + expect(await OST.offerType.innerText()).toContain(data.offerType); + }); + }); + + // Verify OST offer price options display + test(`${features[2].name},${features[2].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[2].path}`); + + const testPage = `${baseURL}${features[2].path}${features[2].browserParams}${authToken}`; + const { data } = features[2]; + + let clipboardText; + + await test.step('Open Offer Selector Tool', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Enter Offer ID in the search field', async () => { + await OST.searchField.fill(data.offerID); + }); + + await test.step('Click Next button in OST', async () => { + await OST.nextButton.waitFor({ state: 'visible', timeout: 10000 }); + await OST.nextButton.click(); + }); + + await test.step('Validate Offer regular price option', async () => { + await OST.price.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceUse.waitFor({ state: 'visible', timeout: 10000 }); + + expect(await OST.price.innerText()).toContain(data.price); + expect(await OST.price.innerText()).toContain(data.term); + expect(await OST.price.innerText()).toContain(data.unit); + expect(await OST.price.innerText()).not.toContain(data.taxLabel); + + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('milo.adobe.com/tools/ost'); + expect(await clipboardText).toContain('type=price'); + }); + + await test.step('Validate Offer optical price option', async () => { + await OST.priceOptical.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceOpticalUse.waitFor({ state: 'visible', timeout: 10000 }); + + expect(await OST.priceOptical.innerText()).toContain(data.opticalPrice); + expect(await OST.priceOptical.innerText()).toContain(data.opticalTerm); + expect(await OST.priceOptical.innerText()).toContain(data.unit); + expect(await OST.priceOptical.innerText()).not.toContain(data.taxLabel); + + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('milo.adobe.com/tools/ost'); + expect(await clipboardText).toContain('type=priceOptical'); + }); + + await test.step('Validate Offer strikethrough price option', async () => { + await OST.priceStrikethrough.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceStrikethroughUse.waitFor({ state: 'visible', timeout: 10000 }); + + expect(await OST.priceStrikethrough.innerText()).toContain(data.price); + expect(await OST.priceStrikethrough.innerText()).toContain(data.term); + expect(await OST.priceStrikethrough.innerText()).toContain(data.unit); + expect(await OST.priceStrikethrough.innerText()).not.toContain(data.taxLabel); + + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('milo.adobe.com/tools/ost'); + expect(await clipboardText).toContain('type=priceStrikethrough'); + }); + }); + + // Verify OST enebalement for price term text + test(`${features[3].name},${features[3].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[3].path}`); + + const testPage = `${baseURL}${features[3].path}${features[3].browserParams}${authToken}`; + const { data } = features[3]; + + let clipboardText; + + await test.step('Open Offer Selector Tool', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Enter Offer ID in the search field', async () => { + await OST.searchField.fill(data.offerID); + }); + + await test.step('Click Next button in OST', async () => { + await OST.nextButton.waitFor({ state: 'visible', timeout: 10000 }); + await OST.nextButton.click(); + }); + + await test.step('Validate term enablement', async () => { + await OST.price.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceOptical.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceStrikethrough.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceUse.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceOpticalUse.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceStrikethroughUse.waitFor({ state: 'visible', timeout: 10000 }); + + expect(await OST.price.innerText()).toContain(data.term); + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('type=price'); + expect(await clipboardText).not.toContain('term='); + + expect(await OST.priceOptical.innerText()).toContain(data.opticalTerm); + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('type=priceOptical'); + expect(await clipboardText).not.toContain('term='); + + expect(await OST.priceStrikethrough.innerText()).toContain(data.term); + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('type=priceStrikethrough'); + expect(await clipboardText).not.toContain('term='); + + // Check term checkbox + await OST.termCheckbox.click(); + + expect(await OST.price.innerText()).not.toContain(data.term); + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('term=false'); + + expect(await OST.priceOptical.innerText()).not.toContain(data.opticalTerm); + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('term=false'); + + expect(await OST.priceStrikethrough.innerText()).not.toContain(data.term); + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('term=false'); + + // Uncheck term checkbox + await OST.termCheckbox.click(); + + expect(await OST.price.innerText()).toContain(data.term); + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('term='); + + expect(await OST.priceOptical.innerText()).toContain(data.opticalTerm); + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('term='); + + expect(await OST.priceStrikethrough.innerText()).toContain(data.term); + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('term='); + }); + }); + + // Verify OST enebalement for price unit text + test(`${features[4].name},${features[4].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[4].path}`); + + const testPage = `${baseURL}${features[4].path}${features[4].browserParams}${authToken}`; + const { data } = features[4]; + + let clipboardText; + + await test.step('Open Offer Selector Tool', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Enter Offer ID in the search field', async () => { + await OST.searchField.fill(data.offerID); + }); + + await test.step('Click Next button in OST', async () => { + await OST.nextButton.waitFor({ state: 'visible', timeout: 10000 }); + await OST.nextButton.click(); + }); + + await test.step('Validate unit enablement', async () => { + await OST.price.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceOptical.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceStrikethrough.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceUse.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceOpticalUse.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceStrikethroughUse.waitFor({ state: 'visible', timeout: 10000 }); + + expect(await OST.price.innerText()).toContain(data.unit); + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('type=price'); + expect(await clipboardText).not.toContain('seat='); + + expect(await OST.priceOptical.innerText()).toContain(data.unit); + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('type=priceOptical'); + expect(await clipboardText).not.toContain('seat='); + + expect(await OST.priceStrikethrough.innerText()).toContain(data.unit); + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('type=priceStrikethrough'); + expect(await clipboardText).not.toContain('seat='); + + // Check unit checkbox + await OST.unitCheckbox.click(); + + expect(await OST.price.innerText()).not.toContain(data.unit); + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('seat=false'); + + expect(await OST.priceOptical.innerText()).not.toContain(data.unit); + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('seat=false'); + + expect(await OST.priceStrikethrough.innerText()).not.toContain(data.unit); + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('seat=false'); + + // Uncheck unit checkbox + await OST.unitCheckbox.click(); + + expect(await OST.price.innerText()).toContain(data.unit); + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('seat='); + + expect(await OST.priceOptical.innerText()).toContain(data.unit); + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('seat='); + + expect(await OST.priceStrikethrough.innerText()).toContain(data.unit); + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('seat='); + }); + }); + + // Verify OST enebalement for price tax label + test(`${features[5].name},${features[5].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[5].path}`); + + const testPage = `${baseURL}${features[5].path}${features[5].browserParams}${authToken}`; + const { data } = features[5]; + + let clipboardText; + + await test.step('Open Offer Selector Tool', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Enter Offer ID in the search field', async () => { + await OST.searchField.fill(data.offerID); + }); + + await test.step('Click Next button in OST', async () => { + await OST.nextButton.waitFor({ state: 'visible', timeout: 10000 }); + await OST.nextButton.click(); + }); + + await test.step('Validate tax label enablement', async () => { + await OST.price.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceOptical.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceStrikethrough.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceUse.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceOpticalUse.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceStrikethroughUse.waitFor({ state: 'visible', timeout: 10000 }); + + expect(await OST.price.innerText()).not.toContain(data.taxLabel); + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('type=price'); + expect(await clipboardText).not.toContain('tax='); + + expect(await OST.priceOptical.innerText()).not.toContain(data.taxLabel); + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('type=priceOptical'); + expect(await clipboardText).not.toContain('tax='); + + expect(await OST.priceStrikethrough.innerText()).not.toContain(data.taxLabel); + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('type=priceStrikethrough'); + expect(await clipboardText).not.toContain('tax='); + + // Check tax label checkbox + await OST.taxlabelCheckbox.click(); + + expect(await OST.price.innerText()).toContain(data.taxLabel); + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('tax=true'); + + expect(await OST.priceOptical.innerText()).toContain(data.taxLabel); + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('tax=true'); + + expect(await OST.priceStrikethrough.innerText()).toContain(data.taxLabel); + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('tax=true'); + + // Uncheck tax label checkbox + await OST.taxlabelCheckbox.click(); + + expect(await OST.price.innerText()).not.toContain(data.taxLabel); + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('tax='); + + expect(await OST.priceOptical.innerText()).not.toContain(data.taxLabel); + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('tax='); + + expect(await OST.priceStrikethrough.innerText()).not.toContain(data.taxLabel); + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('tax='); + }); + }); + + // Verify OST enebalement for tax inclusivity in the price + test(`${features[6].name},${features[6].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[6].path}`); + + const testPage = `${baseURL}${features[6].path}${features[6].browserParams}${authToken}`; + const { data } = features[6]; + + let clipboardText; + + await test.step('Open Offer Selector Tool', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Enter Offer ID in the search field', async () => { + await OST.searchField.fill(data.offerID); + }); + + await test.step('Click Next button in OST', async () => { + await OST.nextButton.waitFor({ state: 'visible', timeout: 10000 }); + await OST.nextButton.click(); + }); + + await test.step('Validate tax inclusivity enablement', async () => { + await OST.price.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceOptical.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceStrikethrough.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceUse.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceOpticalUse.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceStrikethroughUse.waitFor({ state: 'visible', timeout: 10000 }); + + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('type=price'); + expect(await clipboardText).not.toContain('exclusive='); + + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('type=priceOptical'); + expect(await clipboardText).not.toContain('exclusive='); + + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('type=priceStrikethrough'); + expect(await clipboardText).not.toContain('exclusive='); + + // Check tax label checkbox + await OST.taxInlcusivityCheckbox.click(); + + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('exclusive=true'); + + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('exclusive=true'); + + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('exclusive=true'); + + // Uncheck tax label checkbox + await OST.taxInlcusivityCheckbox.click(); + + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('exclusive='); + + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('exclusive='); + + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('exclusive='); + }); + }); + + // Verify OST offer price promo + test(`${features[7].name},${features[7].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[7].path}`); + + const testPage = `${baseURL}${features[7].path}${features[7].browserParams}${authToken}`; + const { data } = features[7]; + + let clipboardText; + + await test.step('Open Offer Selector Tool', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Enter Offer ID in the search field', async () => { + await OST.searchField.fill(data.offerID); + }); + + await test.step('Click Next button in OST', async () => { + await OST.nextButton.waitFor({ state: 'visible', timeout: 10000 }); + await OST.nextButton.click(); + }); + + await test.step('Validate price with promo option', async () => { + await OST.price.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceUse.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceOptical.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceOpticalUse.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceStrikethrough.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceStrikethroughUse.waitFor({ state: 'visible', timeout: 10000 }); + await OST.promoField.waitFor({ state: 'visible', timeout: 10000 }); + await OST.cancelPromo.waitFor({ state: 'visible', timeout: 10000 }); + + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('promo='); + + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('promo='); + + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('promo='); + + // Add promo + await OST.promoField.fill(data.promo); + + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain(`promo=${data.promo}`); + + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain(`promo=${data.promo}`); + + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain(`promo=${data.promo}`); + + // Cancel promo + await OST.cancelPromo.click(); + + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('promo='); + + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('promo='); + + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('promo='); + }); + }); + + // Verify OST checkout link generation + test(`${features[8].name},${features[8].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[8].path}`); + + const testPage = `${baseURL}${features[7].path}${features[8].browserParams}${authToken}`; + const { data } = features[8]; + + await test.step('Open Offer Selector Tool', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Enter Offer ID in the search field', async () => { + await OST.searchField.fill(data.offerID); + }); + + await test.step('Click Next button in OST', async () => { + await OST.nextButton.waitFor({ state: 'visible', timeout: 10000 }); + await OST.nextButton.click(); + }); + + await test.step('Go to Checkout link tab', async () => { + await OST.checkoutTab.waitFor({ state: 'visible', timeout: 10000 }); + await OST.checkoutTab.click(); + }); + + await test.step('Validate Checkout Link', async () => { + await OST.checkoutLink.waitFor({ state: 'visible', timeout: 10000 }); + await OST.promoField.waitFor({ state: 'visible', timeout: 10000 }); + await OST.workflowMenu.waitFor({ state: 'visible', timeout: 10000 }); + + await expect(OST.checkoutLink).toHaveAttribute('href', new RegExp(`${data.offerID}`)); + await expect(OST.checkoutLink).toHaveAttribute('href', new RegExp(`${data.workflowStep_1}`)); + await expect(OST.checkoutLink).not.toHaveAttribute('href', /apc=/); + + // Add promo + await OST.promoField.fill(data.promo); + await expect(OST.checkoutLink).toHaveAttribute('href', new RegExp(`${data.promo}`)); + + // Change Forkflow step + await OST.workflowMenu.click(); + await page.locator(`div[data-key="${data.workflowStep_2}"]`).waitFor({ state: 'visible', timeout: 10000 }); + await page.locator(`div[data-key="${data.workflowStep_2}"]`).click(); + await expect(OST.checkoutLink).toHaveAttribute('href', new RegExp(`${data.workflowStep_2}`)); + }); + }); + + // Verify OST enebalement for old price in the promo price + test(`${features[9].name},${features[9].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[9].path}`); + + const testPage = `${baseURL}${features[9].path}${features[9].browserParams}${authToken}`; + const { data } = features[9]; + + let clipboardText; + + await test.step('Open Offer Selector Tool', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Enter Offer ID in the search field', async () => { + await OST.searchField.fill(data.offerID); + }); + + await test.step('Click Next button in OST', async () => { + await OST.nextButton.waitFor({ state: 'visible', timeout: 10000 }); + await OST.nextButton.click(); + }); + + await test.step('Validate tax inclusivity enablement', async () => { + await OST.price.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceOptical.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceStrikethrough.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceUse.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceOpticalUse.waitFor({ state: 'visible', timeout: 10000 }); + await OST.priceStrikethroughUse.waitFor({ state: 'visible', timeout: 10000 }); + + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('type=price'); + expect(await clipboardText).not.toContain('old='); + + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('type=priceOptical'); + expect(await clipboardText).not.toContain('old='); + + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('type=priceStrikethrough'); + expect(await clipboardText).not.toContain('old='); + + // Check tax label checkbox + await OST.oldPrice.click(); + + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('old=true'); + + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('old=true'); + + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).toContain('old=true'); + + // Uncheck tax label checkbox + await OST.oldPrice.click(); + + await OST.priceUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('old='); + + await OST.priceOpticalUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('old='); + + await OST.priceStrikethroughUse.click(); + clipboardText = await page.evaluate('navigator.clipboard.readText()'); + expect(await clipboardText).not.toContain('old='); + }); + }); +}); diff --git a/nala/features/promotions/promotions.page.js b/nala/features/promotions/promotions.page.js new file mode 100644 index 0000000000..484c813e6f --- /dev/null +++ b/nala/features/promotions/promotions.page.js @@ -0,0 +1,25 @@ +export default class CommercePage { + constructor(page) { + this.page = page; + + this.marqueeDefault = page.locator('.marquee #promo-test-page'); + this.marqueeReplace = page.locator('.marquee #marquee-promo-replace'); + this.marqueeFragment = page.locator('.marquee #fragment-marquee'); + this.textBlock = page.locator('.text-block'); + this.textDefault = page.locator('.text #default-text'); + this.textReplace = page.locator('.text #promo-text-replace'); + this.textInsertAfterMarquee = page.locator('.text #marquee-promo-text-insert'); + this.textInsertBeforeText = page.locator('.text #text-promo-text-insert'); + this.textInsertFuture = page.locator('.text #future-promo-text-insert'); + this.textInsertBeforeCommon = page.locator('.text #common-promo'); + this.textInsertBeforeCommonDE = page.locator('.text #german-promo'); + this.textInsertBeforeCommonFR = page.locator('.text #french-promo'); + this.mepMenuOpen = page.locator('.mep-open'); + this.mepPreviewButton = page.locator('//a[contains(text(),"Preview")]'); + this.mepManifestList = page.locator('.mep-manifest-list'); + this.mepInsertDefault = page.locator('//input[contains(@name,"promo-insert") and @value="default"]'); + this.mepInsertAll = page.locator('//input[contains(@name,"promo-insert") and @value="all"]'); + this.mepReplaceDefault = page.locator('//input[contains(@name,"promo-replace") and @value="default"]'); + this.mepReplaceAll = page.locator('//input[contains(@name,"promo-replace") and @value="all"]'); + } +} diff --git a/nala/features/promotions/promotions.spec.js b/nala/features/promotions/promotions.spec.js new file mode 100644 index 0000000000..44a30af081 --- /dev/null +++ b/nala/features/promotions/promotions.spec.js @@ -0,0 +1,163 @@ +module.exports = { + name: 'Promotions', + features: [ + { + tcid: '0', + name: '@Promo-insert', + path: '/drafts/nala/features/promotions/promo-insert', + data: { + textMarquee: 'Promo test page', + textAfterMarquee: 'Marquee promo text insert', + textBeforeText: 'Text promo text insert', + textDefault: 'Default text', + }, + tags: '@promo @commerce @regression', + }, + { + tcid: '1', + name: '@Promo-replace', + path: '/drafts/nala/features/promotions/promo-replace', + data: { + textReplaceMarquee: 'Marquee promo replace', + textReplace: 'Promo text replace', + }, + tags: '@promo @commerce @regression', + }, + { + tcid: '2', + name: '@Promo-remove', + path: '/drafts/nala/features/promotions/promo-remove', + data: { textMarquee: 'Promo test page' }, + tags: '@promo @commerce @regression', + }, + { + tcid: '3', + name: '@Promo-two-manifests', + path: '/drafts/nala/features/promotions/promo-default', + data: { + textReplaceMarquee: 'Marquee promo replace', + textReplace: 'Promo text replace', + textAfterMarquee: 'Marquee promo text insert', + textBeforeText: 'Text promo text insert', + }, + tags: '@promo @commerce @smoke @regression', + }, + { + tcid: '4', + name: '@Promo-replace-fragment', + path: '/drafts/nala/features/promotions/promo-with-fragments', + data: { textReplaceMarquee: 'Marquee promo replace' }, + tags: '@promo @commerce @regression', + }, + { + tcid: '5', + name: '@Promo-future', + path: '/drafts/nala/features/promotions/promo-future', + data: { + mepPath: '/drafts/nala/features/promotions/manifests/promo-insert-future.json--all', + textMarquee: 'Promo test page', + textDefault: 'Default text', + textFuture: 'Future promo text insert', + status: 'Scheduled - inactive', + manifestFile: 'promo-insert-future.json', + }, + tags: '@promo @commerce @regression', + }, + { + tcid: '6', + name: '@Promo-with-personalization', + path: '/drafts/nala/features/promotions/promo-with-personalization', + data: { + textMarquee: 'Promo test page', + textAfterMarquee: 'Marquee promo text insert', + textBeforeText: 'Text promo text insert', + }, + tags: '@promo @commerce @smoke @regression', + }, + { + tcid: '7', + name: '@Promo-with-personalization-and-target', + path: '/drafts/nala/features/promotions/promo-with-personalization-and-target', + data: { + textMarquee: 'Promo test page', + textAfterMarquee: 'Marquee promo text insert', + textBeforeText: 'Text promo text insert', + }, + tags: '@promo @commerce @smoke @regression', + }, + { + tcid: '8', + name: '@Promo-preview', + path: '/drafts/nala/features/promotions/promo-default', + data: { + mepInsertOn: '/drafts/nala/features/promotions/manifests/promo-insert.json--all', + mepReplaceOn: '/drafts/nala/features/promotions/manifests/promo-replace.json--all', + mepInsertOff: '/drafts/nala/features/promotions/manifests/promo-insert.json--default', + mepReplaceOff: '/drafts/nala/features/promotions/manifests/promo-replace.json--default', + textMarquee: 'Promo test page', + textDefault: 'Default text', + textAfterMarquee: 'Marquee promo text insert', + textBeforeText: 'Text promo text insert', + textReplaceMarquee: 'Marquee promo replace', + textReplace: 'Promo text replace', + inactiveStatus: 'Scheduled - inactive', + manifestInsertFile: 'promo-insert.json', + manifestReplaceFile: 'promo-replace.json', + }, + tags: '@promo @commerce @smoke @regression', + }, + { + tcid: '9', + name: '@Promo-page-filter-insert', + path: '/drafts/nala/features/promotions/promo-page-filter-insert', + data: { + textMarquee: 'Promo test page', + textDefault: 'Default text', + textAfterMarquee: 'Marquee promo text insert', + }, + tags: '@promo @commerce @regression', + }, + { + tcid: '10', + name: '@Promo-page-filter-replace', + path: '/drafts/nala/features/promotions/promo-page-filter-replace', + data: { + textReplaceMarquee: 'Marquee promo replace', + textDefault: 'Default text', + }, + tags: '@promo @commerce @regression', + }, + { + tcid: '11', + name: '@Promo-page-filter-geo', + path: '/drafts/nala/features/promotions/promo-page-filter', + data: { + textMarquee: 'Promo test page', + textDefault: 'Default text', + textBeforeText: 'Common Promo', + CO_DE: '/de', + textBeforeTextDE: 'German Promo', + CO_FR: '/fr', + textBeforeTextFR: 'French Promo', + }, + tags: '@promo @commerce @smoke @regression', + }, + { + tcid: '12', + name: '@Promo-remove-fragment', + path: '/drafts/nala/features/promotions/promo-with-fragments-remove', + tags: '@promo @commerce @regression', + }, + { + tcid: '13', + name: '@Promo-fragment-insert', + path: '/drafts/nala/features/promotions/promo-with-fragments-insert', + data: { + textMarquee: 'Fragment marquee', + textBeforeMarquee: 'Text promo text insert', + textAfterMarquee: 'Marquee promo text insert', + }, + tags: '@promo @commerce @regression', + }, + ], +}; diff --git a/nala/features/promotions/promotions.test.js b/nala/features/promotions/promotions.test.js new file mode 100644 index 0000000000..880704d762 --- /dev/null +++ b/nala/features/promotions/promotions.test.js @@ -0,0 +1,534 @@ +import { expect, test } from '@playwright/test'; +import { features } from './promotions.spec.js'; +import PromoPage from './promotions.page.js'; + +const miloLibs = process.env.MILO_LIBS || ''; + +let PROMO; +test.beforeEach(async ({ page, baseURL }) => { + PROMO = new PromoPage(page); + const skipOn = ['bacom', 'business']; + + skipOn.some((skip) => { + if (baseURL.includes(skip)) test.skip(true, `Skipping the promo tests for ${baseURL}`); + return null; + }); +}); + +test.describe('Promotions feature test suite', () => { + // @Promo-insert - Validate promo insert text after marquee and before text component + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[0].path}${miloLibs}`; + const { data } = features[0]; + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Verify default test page content', async () => { + await expect(await PROMO.marqueeDefault).toBeVisible(); + await expect(await PROMO.marqueeDefault).toContainText(data.textMarquee); + + await expect(await PROMO.textDefault).toBeVisible(); + await expect(await PROMO.textDefault).toContainText(data.textDefault); + }); + + await test.step('Validate content insert after marquee', async () => { + await expect(await PROMO.textInsertAfterMarquee).toBeVisible(); + await expect(await PROMO.textInsertAfterMarquee).toContainText(data.textAfterMarquee); + }); + + await test.step('Validate content insert before text component', async () => { + await expect(await PROMO.textInsertBeforeText).toBeVisible(); + await expect(await PROMO.textInsertBeforeText).toContainText(data.textBeforeText); + }); + }); + + // @Promo-replace - Validate promo replaces marquee and text component + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[1].path}${miloLibs}`; + const { data } = features[1]; + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Verify default test page content is not visible', async () => { + await expect(await PROMO.marqueeDefault).not.toBeVisible(); + await expect(await PROMO.textDefault).not.toBeVisible(); + }); + + await test.step('Validate marque replace', async () => { + await expect(await PROMO.marqueeReplace).toBeVisible(); + await expect(await PROMO.marqueeReplace).toContainText(data.textReplaceMarquee); + }); + + await test.step('Validate text component replace', async () => { + await expect(await PROMO.textReplace).toBeVisible(); + await expect(await PROMO.textReplace).toContainText(data.textReplace); + }); + }); + + // @Promo-remove - Validate promo removes text component + test(`${features[2].name},${features[2].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[2].path}${miloLibs}`; + const { data } = features[2]; + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Verify only default test page marquee is visible', async () => { + await expect(await PROMO.marqueeDefault).toBeVisible(); + await expect(await PROMO.marqueeDefault).toContainText(data.textMarquee); + await expect(await PROMO.textDefault).not.toBeVisible(); + }); + + await test.step('Validate text component removed', async () => { + await expect(await PROMO.textBlock).not.toBeVisible(); + }); + }); + + // @Promo-two-manifests - Validate 2 active manifests on the page + test(`${features[3].name},${features[3].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[3].path}${miloLibs}`; + const { data } = features[3]; + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Verify default test page content is not visible', async () => { + await expect(await PROMO.marqueeDefault).not.toBeVisible(); + await expect(await PROMO.textDefault).not.toBeVisible(); + }); + + await test.step('Validate marque replace', async () => { + await expect(await PROMO.marqueeReplace).toBeVisible(); + await expect(await PROMO.marqueeReplace).toContainText(data.textReplaceMarquee); + }); + + await test.step('Validate text component replace', async () => { + await expect(await PROMO.textReplace).toBeVisible(); + await expect(await PROMO.textReplace).toContainText(data.textReplace); + }); + + await test.step('Validate content insert after marquee', async () => { + await expect(await PROMO.textInsertAfterMarquee).toBeVisible(); + await expect(await PROMO.textInsertAfterMarquee).toContainText(data.textAfterMarquee); + }); + + await test.step('Validate content insert before text component', async () => { + await expect(await PROMO.textInsertBeforeText).toBeVisible(); + await expect(await PROMO.textInsertBeforeText).toContainText(data.textBeforeText); + }); + }); + + // @Promo-replace-fragment - Validate fragment marquee replace + test(`${features[4].name},${features[4].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[4].path}${miloLibs}`; + const { data } = features[4]; + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Verify default test page content is not visible', async () => { + await expect(await PROMO.marqueeFragment).not.toBeVisible(); + }); + + await test.step('Validate marque promo replace', async () => { + await expect(await PROMO.marqueeReplace).toBeVisible(); + await expect(await PROMO.marqueeReplace).toContainText(data.textReplaceMarquee); + }); + }); + + // @Promo-future - Validate active promo scheduled in the future + test(`${features[5].name},${features[5].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[5].path}${miloLibs}`; + const { data } = features[5]; + const previewPage = `${baseURL}${features[5].path}${'?mep='}${data.mepPath}&${miloLibs}`; + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Validate manifest is on served on the page but inactive', async () => { + await PROMO.mepMenuOpen.click(); + await expect(await PROMO.mepManifestList).toBeVisible(); + await expect(await PROMO.mepManifestList).toContainText(data.status); + await expect(await PROMO.mepManifestList).toContainText(data.manifestFile); + }); + + await test.step('Verify default test page content', async () => { + await expect(await PROMO.marqueeDefault).toBeVisible(); + await expect(await PROMO.marqueeDefault).toContainText(data.textMarquee); + + await expect(await PROMO.textDefault).toBeVisible(); + await expect(await PROMO.textDefault).toContainText(data.textDefault); + }); + + await test.step('Validate no future insert on the page', async () => { + await expect(await PROMO.textInsertFuture).not.toBeVisible(); + }); + + await test.step('Navigate to the page with applied future promo and validate content', async () => { + await page.goto(previewPage); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(previewPage); + console.info(`[Promo preview Page]: ${previewPage}`); + + await expect(await PROMO.marqueeDefault).toBeVisible(); + await expect(await PROMO.marqueeDefault).toContainText(data.textMarquee); + + await expect(await PROMO.textDefault).toBeVisible(); + await expect(await PROMO.textDefault).toContainText(data.textDefault); + + await expect(await PROMO.textInsertFuture).toBeVisible(); + await expect(await PROMO.textInsertFuture).toContainText(data.textFuture); + }); + }); + + // @Promo-with-personalization - Validate promo together with personalization and target OFF + test(`${features[6].name},${features[6].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[6].path}${miloLibs}`; + const { data } = features[6]; + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Verify only default test page marquee is visible', async () => { + await expect(await PROMO.marqueeDefault).toBeVisible(); + await expect(await PROMO.marqueeDefault).toContainText(data.textMarquee); + await expect(await PROMO.textDefault).not.toBeVisible(); + }); + + await test.step('Validate content insert after marquee', async () => { + await expect(await PROMO.textInsertAfterMarquee).toBeVisible(); + await expect(await PROMO.textInsertAfterMarquee).toContainText(data.textAfterMarquee); + }); + + await test.step('Validate content insert before text component', async () => { + await expect(await PROMO.textInsertBeforeText).toBeVisible(); + await expect(await PROMO.textInsertBeforeText).toContainText(data.textBeforeText); + }); + }); + + // @Promo-with-personalization-and-target - Validate promo together with personalization and target ON + test(`${features[7].name},${features[7].tags}`, async ({ page, baseURL, browserName }) => { + test.skip(browserName === 'chromium', 'Skipping test for Chromium browser'); + + const testPage = `${baseURL}${features[7].path}${miloLibs}`; + const { data } = features[7]; + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Verify only default test page marquee is visible', async () => { + await expect(await PROMO.marqueeDefault).toBeVisible(); + await expect(await PROMO.marqueeDefault).toContainText(data.textMarquee); + await expect(await PROMO.textDefault).not.toBeVisible(); + }); + + await test.step('Validate content insert after marquee', async () => { + await expect(await PROMO.textInsertAfterMarquee).toBeVisible(); + await expect(await PROMO.textInsertAfterMarquee).toContainText(data.textAfterMarquee); + }); + + await test.step('Validate content insert before text component', async () => { + await expect(await PROMO.textInsertBeforeText).toBeVisible(); + await expect(await PROMO.textInsertBeforeText).toContainText(data.textBeforeText); + }); + }); + + // @Promo-preview - Validate preview functionality + test(`${features[8].name},${features[8].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[8].path}${miloLibs}`; + const { data } = features[8]; + let previewPage; + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Validate all manifests are served and active on the page', async () => { + await PROMO.mepMenuOpen.click(); + await expect(await PROMO.mepManifestList).toBeVisible(); + await expect(await PROMO.mepManifestList).not.toContainText(data.inactiveStatus); + await expect(await PROMO.mepManifestList).toContainText(data.manifestInsertFile); + await expect(await PROMO.mepManifestList).toContainText(data.manifestReplaceFile); + }); + + await test.step('Verify promo page content', async () => { + await expect(await PROMO.marqueeDefault).not.toBeVisible(); + await expect(await PROMO.textDefault).not.toBeVisible(); + + await expect(await PROMO.marqueeReplace).toBeVisible(); + await expect(await PROMO.marqueeReplace).toContainText(data.textReplaceMarquee); + + await expect(await PROMO.textReplace).toBeVisible(); + await expect(await PROMO.textReplace).toContainText(data.textReplace); + + await expect(await PROMO.textInsertAfterMarquee).toBeVisible(); + await expect(await PROMO.textInsertAfterMarquee).toContainText(data.textAfterMarquee); + + await expect(await PROMO.textInsertBeforeText).toBeVisible(); + await expect(await PROMO.textInsertBeforeText).toContainText(data.textBeforeText); + }); + + await test.step('Disable insert manifest and preview', async () => { + await PROMO.mepInsertDefault.click(); + await PROMO.mepPreviewButton.click(); + + await page.waitForLoadState('domcontentloaded'); + previewPage = decodeURIComponent(page.url()); + console.info(`[Preview Page]: ${previewPage}`); + expect(previewPage).toContain(data.mepInsertOff); + expect(previewPage).toContain(data.mepReplaceOn); + + await expect(await PROMO.marqueeDefault).not.toBeVisible(); + await expect(await PROMO.textDefault).not.toBeVisible(); + + await expect(await PROMO.textInsertAfterMarquee).not.toBeVisible(); + await expect(await PROMO.textInsertBeforeText).not.toBeVisible(); + + await expect(await PROMO.marqueeReplace).toBeVisible(); + await expect(await PROMO.marqueeReplace).toContainText(data.textReplaceMarquee); + + await expect(await PROMO.textReplace).toBeVisible(); + await expect(await PROMO.textReplace).toContainText(data.textReplace); + }); + + await test.step('Enable insert and disable replace manifest and preview', async () => { + await PROMO.mepMenuOpen.click(); + await PROMO.mepInsertAll.click(); + await PROMO.mepReplaceDefault.click(); + await PROMO.mepPreviewButton.click(); + + await page.waitForLoadState('domcontentloaded'); + previewPage = decodeURIComponent(page.url()); + console.info(`[Preview Page]: ${previewPage}`); + expect(previewPage).toContain(data.mepInsertOn); + expect(previewPage).toContain(data.mepReplaceOff); + + await expect(await PROMO.marqueeDefault).toBeVisible(); + await expect(await PROMO.marqueeDefault).toContainText(data.textMarquee); + await expect(await PROMO.textDefault).toBeVisible(); + await expect(await PROMO.textDefault).toContainText(data.textDefault); + + await expect(await PROMO.textInsertAfterMarquee).toBeVisible(); + await expect(await PROMO.textInsertAfterMarquee).toContainText(data.textAfterMarquee); + + await expect(await PROMO.textInsertBeforeText).toBeVisible(); + await expect(await PROMO.textInsertBeforeText).toContainText(data.textBeforeText); + + await expect(await PROMO.marqueeReplace).not.toBeVisible(); + await expect(await PROMO.textReplace).not.toBeVisible(); + }); + + await test.step('Desable all manifests and preview', async () => { + await PROMO.mepMenuOpen.click(); + await PROMO.mepInsertDefault.click(); + await PROMO.mepReplaceDefault.click(); + await PROMO.mepPreviewButton.click(); + + await page.waitForLoadState('domcontentloaded'); + previewPage = decodeURIComponent(page.url()); + console.info(`[Preview Page]: ${previewPage}`); + expect(previewPage).toContain(data.mepInsertOff); + expect(previewPage).toContain(data.mepReplaceOff); + + await expect(await PROMO.marqueeDefault).toBeVisible(); + await expect(await PROMO.marqueeDefault).toContainText(data.textMarquee); + await expect(await PROMO.textDefault).toBeVisible(); + await expect(await PROMO.textDefault).toContainText(data.textDefault); + + await expect(await PROMO.textInsertAfterMarquee).not.toBeVisible(); + await expect(await PROMO.textInsertBeforeText).not.toBeVisible(); + await expect(await PROMO.marqueeReplace).not.toBeVisible(); + await expect(await PROMO.textReplace).not.toBeVisible(); + }); + }); + + // @Promo-page-filter-insert - Validate promo page filter with insert action + test(`${features[9].name},${features[9].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[9].path}${miloLibs}`; + const { data } = features[9]; + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Verify default test page content', async () => { + await expect(await PROMO.marqueeDefault).toBeVisible(); + await expect(await PROMO.marqueeDefault).toContainText(data.textMarquee); + + await expect(await PROMO.textDefault).toBeVisible(); + await expect(await PROMO.textDefault).toContainText(data.textDefault); + }); + + await test.step('Validate content insert after marquee', async () => { + await expect(await PROMO.textInsertAfterMarquee).toBeVisible(); + await expect(await PROMO.textInsertAfterMarquee).toContainText(data.textAfterMarquee); + }); + + await test.step('Validate other promo filter actions are not applied', async () => { + await expect(await PROMO.textInsertBeforeCommon).not.toBeVisible(); + await expect(await PROMO.marqueeReplace).not.toBeVisible(); + }); + }); + + // @Promo-page-filter-replace - Validate promo page filter with replace action + test(`${features[10].name},${features[10].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[10].path}${miloLibs}`; + const { data } = features[10]; + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Verify default content', async () => { + await expect(await PROMO.marqueeDefault).not.toBeVisible(); + await expect(await PROMO.textDefault).toBeVisible(); + await expect(await PROMO.textDefault).toContainText(data.textDefault); + }); + + await test.step('Validate marque replace', async () => { + await expect(await PROMO.marqueeReplace).toBeVisible(); + await expect(await PROMO.marqueeReplace).toContainText(data.textReplaceMarquee); + }); + + await test.step('Validate other promo filter actions are not applied', async () => { + await expect(await PROMO.textInsertBeforeCommon).not.toBeVisible(); + await expect(await PROMO.textInsertAfterMarquee).not.toBeVisible(); + }); + }); + + // @Promo-page-filter-geo - Validate promo page filter in default, de and fr locales + test(`${features[11].name},${features[11].tags}`, async ({ page, baseURL }) => { + let testPage = `${baseURL}${features[11].path}${miloLibs}`; + const { data } = features[11]; + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Verify default test page content', async () => { + await expect(await PROMO.marqueeDefault).toBeVisible(); + await expect(await PROMO.marqueeDefault).toContainText(data.textMarquee); + + await expect(await PROMO.textDefault).toBeVisible(); + await expect(await PROMO.textDefault).toContainText(data.textDefault); + }); + + await test.step('Validate content insert before text', async () => { + await expect(await PROMO.textInsertBeforeCommon).toBeVisible(); + await expect(await PROMO.textInsertBeforeCommon).toContainText(data.textBeforeText); + }); + + await test.step('Validate other promo filter actions are not applied', async () => { + await expect(await PROMO.textInsertAfterMarquee).not.toBeVisible(); + await expect(await PROMO.marqueeReplace).not.toBeVisible(); + }); + + await test.step('Go to the test page in DE locale', async () => { + testPage = `${baseURL}${data.CO_DE}${features[11].path}${miloLibs}`; + console.info('[Test Page][DE]: ', testPage); + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Verify page filter on DE page', async () => { + await expect(await PROMO.marqueeDefault).toBeVisible(); + await expect(await PROMO.textDefault).toBeVisible(); + await expect(await PROMO.textInsertBeforeCommonDE).toBeVisible(); + await expect(await PROMO.textInsertBeforeCommonDE).toContainText(data.textBeforeTextDE); + await expect(await PROMO.textInsertAfterMarquee).not.toBeVisible(); + await expect(await PROMO.marqueeReplace).not.toBeVisible(); + }); + + await test.step('Go to the test page in FR locale', async () => { + testPage = `${baseURL}${data.CO_FR}${features[11].path}${miloLibs}`; + console.info('[Test Page][FR]: ', testPage); + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Verify page filter on FR page', async () => { + await expect(await PROMO.marqueeDefault).toBeVisible(); + await expect(await PROMO.textDefault).toBeVisible(); + await expect(await PROMO.textInsertBeforeCommonFR).toBeVisible(); + await expect(await PROMO.textInsertBeforeCommonFR).toContainText(data.textBeforeTextFR); + await expect(await PROMO.textInsertAfterMarquee).not.toBeVisible(); + await expect(await PROMO.marqueeReplace).not.toBeVisible(); + }); + }); + + // @Promo-remove-fragment - Validate fragment marquee remove + test(`${features[12].name},${features[12].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[12].path}${miloLibs}`; + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Verify default test page content is not visible', async () => { + await expect(await PROMO.marqueeFragment).not.toBeVisible(); + }); + }); + + // @Promo-fragment-insert - Validate promo insert text after and before fragment + test(`${features[13].name},${features[13].tags}`, async ({ page, baseURL }) => { + const testPage = `${baseURL}${features[13].path}${miloLibs}`; + const { data } = features[13]; + console.info('[Test Page]: ', testPage); + + await test.step('Go to the test page', async () => { + await page.goto(testPage); + await page.waitForLoadState('domcontentloaded'); + }); + + await test.step('Verify default test page content', async () => { + await expect(await PROMO.marqueeFragment).toBeVisible(); + await expect(await PROMO.marqueeFragment).toContainText(data.textMarquee); + }); + + await test.step('Validate content insert after marquee', async () => { + await expect(await PROMO.textInsertAfterMarquee).toBeVisible(); + await expect(await PROMO.textInsertAfterMarquee).toContainText(data.textAfterMarquee); + }); + + await test.step('Validate content insert before text component', async () => { + await expect(await PROMO.textInsertBeforeText).toBeVisible(); + await expect(await PROMO.textInsertBeforeText).toContainText(data.textBeforeMarquee); + }); + }); +}); diff --git a/nala/libs/imslogin.js b/nala/libs/imslogin.js new file mode 100644 index 0000000000..7ccb781f77 --- /dev/null +++ b/nala/libs/imslogin.js @@ -0,0 +1,37 @@ +/* eslint-disable import/no-import-module-exports, import/no-extraneous-dependencies, max-len, no-console */ +import { expect } from '@playwright/test'; +import selectors from '../features/imslogin/imslogin.page.js'; + +async function clickSignin(page) { + const signinBtn = page.locator(selectors['@gnav-signin']); + await expect(signinBtn).toBeVisible(); + await signinBtn.click(); +} + +async function fillOutSignInForm(props, page) { + expect(process.env.IMS_EMAIL, 'ERROR: No environment variable for email provided for IMS Test.').toBeTruthy(); + expect(process.env.IMS_PASS, 'ERROR: No environment variable for password provided for IMS Test.').toBeTruthy(); + + await expect(page).toHaveTitle(/Adobe ID/); + let heading = await page.locator(selectors['@page-heading']).first().innerText(); + expect(heading).toBe('Sign in'); + + // Fill out Sign-in Form + await expect(async () => { + await page.locator(selectors['@email']).fill(process.env.IMS_EMAIL); + await page.locator(selectors['@email-continue-btn']).click(); + await expect(page.locator(selectors['@password-reset'])).toBeVisible({ timeout: 45000 }); // Timeout accounting for how long IMS Login page takes to switch form + }).toPass({ + intervals: [1_000], + timeout: 10_000, + }); + + heading = await page.locator(selectors['@page-heading'], { hasText: 'Enter your password' }).first().innerText(); + expect(heading).toBe('Enter your password'); + await page.locator(selectors['@password']).fill(process.env.IMS_PASS); + await page.locator(selectors['@password-continue-btn']).click(); + await page.waitForURL(`${props.url}#`); + await expect(page).toHaveURL(`${props.url}#`); +} + +module.exports = { clickSignin, fillOutSignInForm }; diff --git a/nala/utils/pr.run.sh b/nala/utils/pr.run.sh new file mode 100755 index 0000000000..0d72a8d66a --- /dev/null +++ b/nala/utils/pr.run.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +TAGS="" +REPORTER="" +EXCLUDE_TAGS="--grep-invert nopr" +EXIT_STATUS=0 +PR_NUMBER=$(echo "$GITHUB_REF" | awk -F'/' '{print $3}') +echo "PR Number: $PR_NUMBER" + +# Extract feature branch name from GITHUB_HEAD_REF +FEATURE_BRANCH="$GITHUB_HEAD_REF" +# Replace "/" characters in the feature branch name with "-" +FEATURE_BRANCH=$(echo "$FEATURE_BRANCH" | sed 's/\//-/g') +echo "Feature Branch Name: $FEATURE_BRANCH" + +PR_BRANCH_LIVE_URL_GH="https://$FEATURE_BRANCH--$prRepo--$prOrg.hlx.live" +# set pr branch url as env +export PR_BRANCH_LIVE_URL_GH +export PR_NUMBER + +echo "PR Branch live URL: $PR_BRANCH_LIVE_URL_GH" +echo "*******************************" + +# Convert GitHub Tag(@) labels that can be grepped +for label in ${labels}; do + if [[ "$label" = \@* ]]; then + label="${label:1}" + TAGS+="|$label" + fi +done + +# Remove the first pipe from tags if tags are not empty +[[ ! -z "$TAGS" ]] && TAGS="${TAGS:1}" && TAGS="-g $TAGS" + +# Retrieve GitHub reporter parameter if not empty +# Otherwise, use reporter settings in playwright.config.js +REPORTER=$reporter +[[ ! -z "$REPORTER" ]] && REPORTER="--reporter $REPORTER" + +echo "*** Running Nala on $FEATURE_BRANCH ***" +echo "Tags : $TAGS" +echo "npx playwright test ${TAGS} ${EXCLUDE_TAGS} ${REPORTER}" + +# Navigate to the GitHub Action path and install dependencies +cd "$GITHUB_ACTION_PATH" || exit +npm ci +npx playwright install --with-deps + +# Run Playwright tests on the specific projects using root-level playwright.config.js +# This will be changed later +echo "*** Running tests on specific projects ***" +npx playwright test --config=./playwright.config.js ${TAGS} ${EXCLUDE_TAGS} --project=milo-live-chromium --project=milo-live-firefox ${REPORTER} || EXIT_STATUS=$? + +# Check if tests passed or failed +if [ $EXIT_STATUS -ne 0 ]; then + echo "Some tests failed. Exiting with error." + exit $EXIT_STATUS +else + echo "All tests passed successfully." +fi diff --git a/test/blocks/article-header/article-header.test.js b/test/blocks/article-header/article-header.test.js index cbcf096c1f..3056c62da5 100644 --- a/test/blocks/article-header/article-header.test.js +++ b/test/blocks/article-header/article-header.test.js @@ -1,7 +1,6 @@ import { readFile } from '@web/test-runner-commands'; import { expect } from '@esm-bundle/chai'; -import sinon from 'sinon'; - +import sinon, { stub } from 'sinon'; import { setConfig, getConfig } from '../../../libs/utils/utils.js'; import { delay, waitForElement } from '../../helpers/waitfor.js'; @@ -9,9 +8,11 @@ const locales = { '': { ietf: 'en-US', tk: 'hah7vzn.css' } }; const conf = { locales, miloLibs: 'http://localhost:2000/libs' }; setConfig(conf); const config = getConfig(); +window.lana = { log: stub() }; document.body.innerHTML = await readFile({ path: './mocks/body.html' }); const { default: init } = await import('../../../libs/blocks/article-header/article-header.js'); +const { loadTaxonomy } = await import('../../../libs/blocks/article-feed/article-helpers.js'); const invalidDoc = await readFile({ path: './mocks/body-invalid.html' }); @@ -20,10 +21,20 @@ describe('article header', () => { const block = document.body.querySelector('.article-header'); config.locale.contentRoot = '/test/blocks/article-header/mocks'; config.taxonomyRoot = undefined; - await init(block); }); + it('should log unknown topic', async () => { + try { + const div = document.createElement('div'); + div.setAttribute('data-topic-link', ['abcd']); + document.body.append(div); + await loadTaxonomy(); + expect(window.lana.log.args[0][0]).to.equal('Trying to get a link for an unknown topic: abcd (current page)'); + } catch (e) { + console.log(e); + } + }); it('creates article header block', () => { expect(document.body.querySelector('.article-category')).to.exist; expect(document.body.querySelector('.article-title')).to.exist; @@ -36,16 +47,16 @@ describe('article header', () => { it('should open link popup when share links are clicked', () => { // first share link is twitter const shareLink = document.querySelector('.article-byline-sharing a'); - const stub = sinon.stub(window, 'open'); + const windowStub = sinon.stub(window, 'open'); shareLink.click(); const url = encodeURIComponent(window.location.href); const title = encodeURIComponent(document.querySelector('h1').textContent); - expect(stub.calledOnce).to.be.true; - expect(stub.firstCall.args[0]).to.equal(`https://www.twitter.com/share?&url=${url}&text=${title}`); - expect(stub.firstCall.args[2]).to.equal('popup,top=233,left=233,width=700,height=467'); + expect(windowStub.calledOnce).to.be.true; + expect(windowStub.firstCall.args[0]).to.equal(`https://www.twitter.com/share?&url=${url}&text=${title}`); + expect(windowStub.firstCall.args[2]).to.equal('popup,top=233,left=233,width=700,height=467'); - stub.restore(); + windowStub.restore(); }); it('updates share text after deferred event', async () => { @@ -94,6 +105,21 @@ describe('article header', () => { const categoryLink = document.querySelector('.article-category a'); expect(categoryLink.href.includes('/topics/')).to.be.true; }); + + it('adds an author image from a link', () => { + const img = document.querySelector('.article-author-image img'); + const link = document.querySelector('.article-author a'); + expect(img).to.exist; + expect(link).to.exist; + }); + + it('adds an author image from data attribute', async () => { + await init(document.querySelector('#article-header-no-author-link')); + const img = document.querySelector('.article-author-image img'); + const author = document.querySelector('.article-author [data-author-page]'); + expect(img).to.exist; + expect(author).to.exist; + }); }); describe('test the invalid article header', () => { @@ -111,7 +137,6 @@ describe('test the invalid article header', () => { it('adds invalid-date when invalid date is provided', async () => { await init(document.body.querySelector('.article-header')); - const date = await waitForElement('.article-date-invalid'); expect(date).to.exist; }); diff --git a/test/blocks/article-header/mocks/body.html b/test/blocks/article-header/mocks/body.html index ffe3737645..ec8471006d 100644 --- a/test/blocks/article-header/mocks/body.html +++ b/test/blocks/article-header/mocks/body.html @@ -39,3 +39,31 @@

+
+ +
+
+
+

+ Celebrating a special milestone: 10 things you might not know about Adobe’s 40 years of innovation

+
+
+
+
+

Adobe Communications Team

+

12-05-2022

+
+
+
+
+

+ + + Caption +

+
+
+ diff --git a/test/blocks/caas-config/expectedConfigs/defaultConfig.js b/test/blocks/caas-config/expectedConfigs/defaultConfig.js index 4d3f1b54e9..d510f9c5c1 100644 --- a/test/blocks/caas-config/expectedConfigs/defaultConfig.js +++ b/test/blocks/caas-config/expectedConfigs/defaultConfig.js @@ -204,6 +204,7 @@ const defaultConfig = { enabled: '', lastViewedSession: '', }, + linkTransformer: {}, customCard: ['card', 'return ``'], }; diff --git a/test/blocks/caas/utils.test.js b/test/blocks/caas/utils.test.js index 29884bb434..91c90ad4f7 100644 --- a/test/blocks/caas/utils.test.js +++ b/test/blocks/caas/utils.test.js @@ -397,6 +397,7 @@ describe('getConfig', () => { }, language: 'en', country: 'us', + linkTransformer: {}, customCard: [ 'card', 'return ``', @@ -670,6 +671,7 @@ describe('getConfig', () => { enabled: true, lastViewedSession: '', }, + linkTransformer: {}, }); }); }); @@ -995,6 +997,7 @@ describe('getFloodgateCaasConfig', () => { enabled: true, lastViewedSession: '', }, + linkTransformer: {}, }); }); }); diff --git a/test/blocks/editorial-card/mocks/body.html b/test/blocks/editorial-card/mocks/body.html index 9ed3c148d5..e27494c07b 100644 --- a/test/blocks/editorial-card/mocks/body.html +++ b/test/blocks/editorial-card/mocks/body.html @@ -25,8 +25,8 @@

2 Row - [media, copy]

-
@@ -45,10 +45,7 @@

3 Row - [bg, media, copy]

- - - - +
@@ -89,12 +86,12 @@

Variants: open, click w/ lockup content

- +
- +
@@ -102,7 +99,38 @@

Variants: open, click w/ lockup content

- + + Lockup +

+

per-breakpoint media - mobile image, tablet image, desktop video

+

Open
'no-border', 'l-rounded-corners-image', 'static-links-copy', 'underline-links-footer'

+ +

Editorial Card (static links copy) Static link

+

TODO: the footer should have its own footer-static-links variant

+
+
+ +
+
+ +
+
+

+ Lockup

per-breakpoint media - mobile image, tablet image, desktop video

@@ -125,7 +153,7 @@

Empty footer row and extra static row

- +
diff --git a/test/blocks/global-footer/global-footer.test.js b/test/blocks/global-footer/global-footer.test.js index d99e9868c5..3ab306b9ce 100644 --- a/test/blocks/global-footer/global-footer.test.js +++ b/test/blocks/global-footer/global-footer.test.js @@ -378,7 +378,7 @@ describe('global footer', () => { await createFullGlobalFooter({ waitForDecoration: false }); await clock.runAllAsync(); expect(window.lana.log.getCalls().find((c) => c.args[0].includes('Failed to fetch footer content'))); - expect(window.lana.log.getCalls().find((c) => c.args[1].tags.includes('errorType=warn,module=global-footer'))); + expect(window.lana.log.getCalls().find((c) => c.args[1].tags.includes('global-footer'))); }); it('should send log when could not create URL for region picker', async () => { @@ -393,7 +393,7 @@ describe('global footer', () => { // should throw error } expect(window.lana.log.getCalls().find((c) => c.args[0].includes('Could not create URL for region picker'))); - expect(window.lana.log.getCalls().find((c) => c.args[1].tags.includes('errorType=error,module=global-footer'))); + expect(window.lana.log.getCalls().find((c) => c.args[1].tags.includes('global-footer'))); }); it('should send log when footer cannot be instantiated ', async () => { @@ -401,7 +401,7 @@ describe('global footer', () => { await createFullGlobalFooter({ waitForDecoration: false }); await clock.runAllAsync(); expect(window.lana.log.getCalls().find((c) => c.args[0].includes('Footer could not be instantiated'))); - expect(window.lana.log.getCalls().find((c) => c.args[1].tags.includes('errorType=error,module=global-footer'))); + expect(window.lana.log.getCalls().find((c) => c.args[1].tags.includes('global-footer'))); }); it('should send LANA log when icons.svg has some network issue', async () => { diff --git a/test/blocks/global-navigation/global-navigation.test.js b/test/blocks/global-navigation/global-navigation.test.js index 4af0799b83..b260ee3f21 100644 --- a/test/blocks/global-navigation/global-navigation.test.js +++ b/test/blocks/global-navigation/global-navigation.test.js @@ -10,6 +10,7 @@ import { viewports, unavLocalesTestData, analyticsTestData, + unavVersion, } from './test-utilities.js'; import { setConfig, getLocale } from '../../../libs/utils/utils.js'; import initGnav, { getUniversalNavLocale, osMap } from '../../../libs/blocks/global-navigation/global-navigation.js'; @@ -27,7 +28,7 @@ describe('global navigation', () => { before(() => { document.head.innerHTML = ` - + `; }); @@ -366,8 +367,8 @@ describe('global navigation', () => { toFake: ['setTimeout'], shouldAdvanceTime: true, }); - window.UniversalNav = sinon.spy(); - window.UniversalNav.reload = sinon.spy(); + window.UniversalNav = sinon.spy(() => Promise.resolve()); + window.UniversalNav.reload = sinon.spy(() => Promise.resolve()); // eslint-disable-next-line no-underscore-dangle window._satellite = { track: sinon.spy() }; window.alloy = () => new Promise((resolve) => { @@ -578,7 +579,7 @@ describe('global navigation', () => { document.head.innerHTML = ` - + `; const gnav = await createFullGlobalNavigation({}); gnav.decorateAppPrompt(); @@ -592,7 +593,7 @@ describe('global navigation', () => { - + `; const gnav = await createFullGlobalNavigation({}); window.adobeIMS = { isSignedInUser: () => true }; @@ -620,4 +621,11 @@ describe('global navigation', () => { expect(document.querySelector(`${selectors.brandImage} img`).getAttribute('src')).to.equal('http://localhost:2000/test/blocks/global-navigation/mocks/adobe-dark-logo.svg'); }); }); + + describe('Client search feature in global navigation', () => { + it('should append the feds-client-search div when search is enabled', async () => { + await createFullGlobalNavigation({ customConfig: { searchEnabled: 'on' } }); + expect(document.querySelector(selectors.topNavWrapper).classList.contains('feds-client-search')).to.exist; + }); + }); }); diff --git a/test/blocks/global-navigation/gnav-brand.test.js b/test/blocks/global-navigation/gnav-brand.test.js index 3150a0fa7f..9b0917fbed 100644 --- a/test/blocks/global-navigation/gnav-brand.test.js +++ b/test/blocks/global-navigation/gnav-brand.test.js @@ -4,6 +4,7 @@ import { createFullGlobalNavigation, selectors, isElementVisible, + unavVersion, } from './test-utilities.js'; import logoOnlyNav from './mocks/global-navigation-only-logo.plain.js'; import brandOnlyNav from './mocks/global-navigation-only-brand.plain.js'; @@ -15,7 +16,7 @@ describe('brand', () => { before(() => { document.head.innerHTML = ` - + `; }); diff --git a/test/blocks/global-navigation/gnav-cross-cloud.test.js b/test/blocks/global-navigation/gnav-cross-cloud.test.js index 9bd2986ddd..000fc0372b 100644 --- a/test/blocks/global-navigation/gnav-cross-cloud.test.js +++ b/test/blocks/global-navigation/gnav-cross-cloud.test.js @@ -4,6 +4,7 @@ import { createFullGlobalNavigation, selectors, isElementVisible, + unavVersion, } from './test-utilities.js'; import globalNavigationCrossCloud from './mocks/global-navigation-cross-cloud.plain.js'; @@ -11,7 +12,7 @@ describe('Cross Cloud Menu', () => { before(() => { document.head.innerHTML = ` - + `; }); diff --git a/test/blocks/global-navigation/gnav-main-nav-popup.test.js b/test/blocks/global-navigation/gnav-main-nav-popup.test.js index 25e80c908b..3799a07514 100644 --- a/test/blocks/global-navigation/gnav-main-nav-popup.test.js +++ b/test/blocks/global-navigation/gnav-main-nav-popup.test.js @@ -5,6 +5,7 @@ import { createFullGlobalNavigation, selectors, isElementVisible, + unavVersion, } from './test-utilities.js'; import { toFragment } from '../../../libs/blocks/global-navigation/utilities/utilities.js'; import globalNavigationMock from './mocks/global-navigation.plain.js'; @@ -14,7 +15,7 @@ describe('main nav popups', () => { before(() => { document.head.innerHTML = ` - + `; }); diff --git a/test/blocks/global-navigation/gnav-main-nav.test.js b/test/blocks/global-navigation/gnav-main-nav.test.js index ef984078f2..5a09cb5518 100644 --- a/test/blocks/global-navigation/gnav-main-nav.test.js +++ b/test/blocks/global-navigation/gnav-main-nav.test.js @@ -7,6 +7,7 @@ import { selectors, isElementVisible, viewports, + unavVersion, } from './test-utilities.js'; import { isDesktop, setActiveLink, toFragment } from '../../../libs/blocks/global-navigation/utilities/utilities.js'; import globalNavigationActiveMock from './mocks/global-navigation-active.plain.js'; @@ -15,7 +16,7 @@ describe('main nav', () => { before(() => { document.head.innerHTML = ` - + `; }); diff --git a/test/blocks/global-navigation/gnav-profile.test.js b/test/blocks/global-navigation/gnav-profile.test.js index bc1210e4cd..0594568243 100644 --- a/test/blocks/global-navigation/gnav-profile.test.js +++ b/test/blocks/global-navigation/gnav-profile.test.js @@ -5,6 +5,7 @@ import { createFullGlobalNavigation, selectors, isElementVisible, + unavVersion, } from './test-utilities.js'; import globalNavigationMock from './mocks/global-navigation.plain.js'; @@ -12,7 +13,7 @@ describe('profile', () => { before(() => { document.head.innerHTML = ` - + `; }); diff --git a/test/blocks/global-navigation/gnav-promo.test.js b/test/blocks/global-navigation/gnav-promo.test.js index 972dd297db..eede5833c5 100644 --- a/test/blocks/global-navigation/gnav-promo.test.js +++ b/test/blocks/global-navigation/gnav-promo.test.js @@ -1,13 +1,13 @@ /* eslint-disable no-restricted-syntax */ import { expect } from '@esm-bundle/chai'; -import { createFullGlobalNavigation } from './test-utilities.js'; +import { createFullGlobalNavigation, unavVersion } from './test-utilities.js'; import { toFragment } from '../../../libs/blocks/global-navigation/utilities/utilities.js'; describe('Promo', () => { before(() => { document.head.innerHTML = ` - + `; }); diff --git a/test/blocks/global-navigation/gnav-search.test.js b/test/blocks/global-navigation/gnav-search.test.js index c25ef8b9f0..17f24b286c 100644 --- a/test/blocks/global-navigation/gnav-search.test.js +++ b/test/blocks/global-navigation/gnav-search.test.js @@ -7,6 +7,7 @@ import { selectors, isElementVisible, mockRes, + unavVersion, } from './test-utilities.js'; const ogFetch = window.fetch; @@ -19,7 +20,7 @@ describe('search', () => { before(() => { document.head.innerHTML = ` - + `; }); diff --git a/test/blocks/global-navigation/test-utilities.js b/test/blocks/global-navigation/test-utilities.js index 70be91e0ca..9e38a606b8 100644 --- a/test/blocks/global-navigation/test-utilities.js +++ b/test/blocks/global-navigation/test-utilities.js @@ -77,6 +77,8 @@ export const analyticsTestData = { 'unc|click|markUnread': 'Mark Notification as unread', }; +export const unavVersion = '1.3'; + export const unavLocalesTestData = Object.entries(LANGMAP).reduce((acc, curr) => { const result = []; const [locale, prefixes] = curr; diff --git a/test/blocks/instagram/mocks/embed-utils.js b/test/blocks/instagram/mocks/embed-utils.js index 8f69fa3854..88da37b5cf 100644 --- a/test/blocks/instagram/mocks/embed-utils.js +++ b/test/blocks/instagram/mocks/embed-utils.js @@ -25,6 +25,26 @@ export function createTag(tag, attributes, html) { return el; } +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); + link.onerror = (e) => callback(e.type); + } + document.head.appendChild(link); + } else if (callback) { + callback('noop'); + } + return link; +} + export const getConfig = () => ({}); export const customFetch = stub(); diff --git a/test/blocks/merch-twp/merch-twp.2cards.test.html b/test/blocks/merch-twp/merch-twp.2cards.test.html index c76061ab12..982b30039a 100644 --- a/test/blocks/merch-twp/merch-twp.2cards.test.html +++ b/test/blocks/merch-twp/merch-twp.2cards.test.html @@ -41,7 +41,7 @@ setConfig(config); runTests(async () => { - describe('Merch TWP', async () => { + describe.skip('Merch TWP', async () => { // not finished and on hold before(async () => { mockIms(); await mockFetch(); diff --git a/test/blocks/merch/merch.test.js b/test/blocks/merch/merch.test.js index 4a7641f9c2..cee0f412cc 100644 --- a/test/blocks/merch/merch.test.js +++ b/test/blocks/merch/merch.test.js @@ -12,14 +12,12 @@ import merch, { buildCta, getCheckoutContext, initService, - fetchLiterals, fetchCheckoutLinkConfigs, getCheckoutLinkConfig, getDownloadAction, fetchEntitlements, getModalAction, getCheckoutAction, - PRICE_LITERALS_URL, PRICE_TEMPLATE_REGULAR, getMasBase, appendTabName, @@ -151,7 +149,6 @@ describe('Merch Block', () => { document.head.innerHTML = await readMockText('/test/blocks/merch/mocks/head.html'); document.body.innerHTML = await readMockText('/test/blocks/merch/mocks/body.html'); ({ setCheckoutLinkConfigs, setSubscriptionsData } = await mockFetch()); - config.commerce = { priceLiteralsPromise: fetchLiterals(PRICE_LITERALS_URL) }; setCheckoutLinkConfigs(CHECKOUT_LINK_CONFIGS); }); diff --git a/test/blocks/merch/mocks/fetch.js b/test/blocks/merch/mocks/fetch.js index e67d70369d..08bef2ad7b 100644 --- a/test/blocks/merch/mocks/fetch.js +++ b/test/blocks/merch/mocks/fetch.js @@ -1,6 +1,4 @@ import sinon from 'sinon'; - -import { PRICE_LITERALS_URL } from '../../../../libs/blocks/merch/merch.js'; import { applyPlanType } from '../../../../libs/deps/mas/commerce.js'; const { fetch } = window; @@ -18,7 +16,6 @@ export const readMockText = async (path) => { export async function mockFetch() { // this path allows to import this mock from tests for other blocks (e.g. commerce) const basePath = '/test/blocks/merch/mocks/'; - const literals = await readMockJSON(`${basePath}literals.json`); const offers = await readMockJSON(`${basePath}offers.json`); const namedOffers = await readMockJSON(`${basePath}named-offers.json`); @@ -51,14 +48,8 @@ export async function mockFetch() { }; sinon.stub(window, 'fetch').callsFake((...args) => { - const { href, pathname, searchParams } = new URL(String(args[0])); - // literals mock - if (href === PRICE_LITERALS_URL) { - return Promise.resolve({ - ok: true, - json: () => Promise.resolve(literals), - }); - } + const { pathname, searchParams } = new URL(String(args[0])); + // wcs mock if (pathname.endsWith('/web_commerce_artifact')) { const osis = searchParams.get('offer_selector_ids').split(','); diff --git a/test/blocks/merch/mocks/ims.js b/test/blocks/merch/mocks/ims.js index a3ba0d18da..8e1e79ba86 100644 --- a/test/blocks/merch/mocks/ims.js +++ b/test/blocks/merch/mocks/ims.js @@ -1,7 +1,4 @@ -import { loadIms } from '../../../../libs/utils/utils.js'; - export async function mockIms(countryCode, userId = 1) { - loadIms(); window.adobeIMS = { initialized: true, isSignedInUser: () => !!countryCode, @@ -9,7 +6,6 @@ export async function mockIms(countryCode, userId = 1) { getAccessToken: () => ({ token: 'test_client_token' }), adobeIdData: { client_id: 'test_client_id' }, }; - window.adobeid.onReady(); } export function unmockIms() { diff --git a/test/blocks/merch/mocks/literals.json b/test/blocks/merch/mocks/literals.json deleted file mode 100644 index c29f836e35..0000000000 --- a/test/blocks/merch/mocks/literals.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "data": [{ - "lang": "en", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/mo} YEAR {/yr} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {per month} YEAR {per year} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {per license} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {per license} other {}}", - "freeLabel": "Free", - "freeAriaLabel": "Free", - "taxExclusiveLabel": "{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}", - "alternativePriceAriaLabel": "Alternatively at {alternativePrice}", - "strikethroughAriaLabel": "Regularly at {strikethroughPrice}" - }], - ":type": "sheet" -} diff --git a/test/blocks/quiz-results/mocks/body.html b/test/blocks/quiz-results/mocks/body.html index 7323de08ac..10823f4ee2 100644 --- a/test/blocks/quiz-results/mocks/body.html +++ b/test/blocks/quiz-results/mocks/body.html @@ -38,5 +38,15 @@
http://this-is-a-fake-redirect-url
+
+
+
nested-fragments
+
marquee-product
+
+
+
style
+
m-spacing
+
+
diff --git a/test/blocks/quiz-results/quiz-results.test.js b/test/blocks/quiz-results/quiz-results.test.js index 37bb6b77b9..7b0b887cb3 100644 --- a/test/blocks/quiz-results/quiz-results.test.js +++ b/test/blocks/quiz-results/quiz-results.test.js @@ -76,4 +76,11 @@ describe('Quiz Results', () => { expect(el.querySelector('.fragment > .section > .content').getAttribute('daa-lh')).to.equal('b1|content'); expect(el.querySelector('a').getAttribute('daa-ll')).to.equal('Fragment link-1--This is a basic frag'); }); + it('should return misconfigured block', async () => { + const el = document.body.querySelector('.nested-three'); + localStorage.setItem('misconf', JSON.stringify(mockData.mockTwo)); + el.classList.remove('nested'); + await init(el, 'quiz-results', 'misconf'); + expect(window.lana.log.args[2][0]).to.equal(`${LOADING_ERROR} The quiz-results block is misconfigured`); + }); }); diff --git a/test/blocks/text/mocks/body.html b/test/blocks/text/mocks/body.html index 2db780be10..bfb7a5a52f 100644 --- a/test/blocks/text/mocks/body.html +++ b/test/blocks/text/mocks/body.html @@ -144,7 +144,9 @@

How to...

- Adobe General Terms of Use Lorem ipsum dolor sit amet. Vel nobis quidem At dolores ratione qui vero molestiae est veritatis sint aut voluptates natus. Rem quidem quasi ut omnis cupiditate aut fugiat nulla. Sed nihil corporis ab sint dolor ut voluptates omnis sed aliquid voluptas nam vitae sunt in omnis assumenda ea voluptatem autem. Quo amet pariatur ut dolorum recusandae a perspiciatis iste quo repudiandae atque sit amet suscipit sed ipsam enim. Ex beatae dolores et eius architecto ut reiciendis sunt vel quaerat temporibus qui omnis optio et rerum magnam id praesentium recusandae. Qui perspiciatis doloribus qui tempora galisum non ipsam enim ad consequatur consequuntur et galisum commodi aut alias dolor. Sit reprehenderit illum id molestiae odio ex quae consectetur aut rerum officia et galisum iure rem perspiciatis maxime. Non natus autem qui officiis voluptatem ea quia facilis non autem sint ea facilis quos. Ex beatae dolores et eius architecto ut reiciendis sunt vel quaerat temporibus qui omnis optio et rerum magnam id praesentium recusandae. Qui perspiciatis doloribus qui tempora galisum non ipsam enim ad consequatur consequuntur et galisum commodi aut alias dolor. Sit reprehenderit illum id molestiae odio ex quae consectetur aut rerum officia et galisum. Qui perspiciatis doloribus qui tempora galisum non ipsam enim ad consequatur consequuntur et galisum commodi aut alias dolor. Sit reprehenderit illum id molestiae odio ex quae consectetur. +

+ Adobe General Terms of Use Lorem ipsum dolor sit amet. Vel nobis quidem At dolores ratione qui vero molestiae est veritatis sint aut voluptates natus. Rem quidem quasi ut omnis cupiditate aut fugiat nulla. Sed nihil corporis ab sint dolor ut voluptates omnis sed aliquid voluptas nam vitae sunt in omnis assumenda ea voluptatem autem. Quo amet pariatur ut dolorum recusandae a perspiciatis iste quo repudiandae atque sit amet suscipit sed ipsam enim. Ex beatae dolores et eius architecto ut reiciendis sunt vel quaerat temporibus qui omnis optio et rerum magnam id praesentium recusandae. Qui perspiciatis doloribus qui tempora galisum non ipsam enim ad consequatur consequuntur et galisum commodi aut alias dolor. Sit reprehenderit illum id molestiae odio ex quae consectetur aut rerum officia et galisum iure rem perspiciatis maxime. Non natus autem qui officiis voluptatem ea quia facilis non autem sint ea facilis quos. Ex beatae dolores et eius architecto ut reiciendis sunt vel quaerat temporibus qui omnis optio et rerum magnam id praesentium recusandae. Qui perspiciatis doloribus qui tempora galisum non ipsam enim ad consequatur consequuntur et galisum commodi aut alias dolor. Sit reprehenderit illum id molestiae odio ex quae consectetur aut rerum officia et galisum. Qui perspiciatis doloribus qui tempora galisum non ipsam enim ad consequatur consequuntur et galisum commodi aut alias dolor. Sit reprehenderit illum id molestiae odio ex quae consectetur. +

@@ -221,6 +223,103 @@

+
+
+

+ + + mock + This is a caption? +

+

Membership plans

+

Mobile Content

+

Start with a single app or a multi-app plan — the choice is yours.

+

Learn more Learn moreLearn more

+
+
+

+ + + mock + This is a caption? +

+

Membership plans

+

Tablet Content

+

Start with a single app or a multi-app plan — the choice is yours.

+

Learn more Learn moreLearn more

+
+
+

+ + + mock + This is a caption? +

+

Membership plans

+

Desktop Content

+

Start with a single app or a multi-app plan — the choice is yours. Start with a single app or a multi-app plan — the choice is yours.

+

Learn more Learn moreLearn more

+
+
+ +
+
+
red
+
blue
+
linear-gradient(90deg, #FA0F00 0%, #E9740A 15.42%, #FFCE2E 39.44%, #009C3B 67.99%, #2799F6 85.76%, #6349E0 95.42%, #9999FC 100%)
+
+
+ + +
+
+
+

+ + + + + + + + + + + + Lockup +

+

Membership plans

+

Get started with photo editing apps.

+

Start with a single app or a multi-app plan — the choice is yours.

+
+
+
+ +
+
+
+

+ + + + + + + + + + + + +

+

Membership plans

+

Get started with photo editing apps.

+

Start with a single app or a multi-app plan — the choice is yours.

+
+
+
diff --git a/test/blocks/vimeo/mocks/placeholders.js b/test/blocks/vimeo/mocks/placeholders.js new file mode 100644 index 0000000000..b3f9e9a45d --- /dev/null +++ b/test/blocks/vimeo/mocks/placeholders.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line +export const replaceKey = () => 'placeholder'; diff --git a/test/blocks/vimeo/vimeo.test.html b/test/blocks/vimeo/vimeo.test.html index ba1012672c..8eb108eb54 100644 --- a/test/blocks/vimeo/vimeo.test.html +++ b/test/blocks/vimeo/vimeo.test.html @@ -3,7 +3,8 @@ @@ -14,21 +15,42 @@ diff --git a/test/blocks/youtube/youtube.test.html b/test/blocks/youtube/youtube.test.html index ba852216f8..76f9891d5b 100644 --- a/test/blocks/youtube/youtube.test.html +++ b/test/blocks/youtube/youtube.test.html @@ -9,22 +9,62 @@ - Youtube Embed Link - Youtube Share Link