From adb35eb68d6519d9bcee04378b426d231834199c Mon Sep 17 00:00:00 2001 From: Elan Bartholomew <79870969+elan-tbx@users.noreply.github.com> Date: Thu, 29 Aug 2024 08:21:07 -0600 Subject: [PATCH 01/66] MWPW-151936 - Aside Tiger Team Enhancements (a new hope) (#2782) * MWPW-151936 Aside Tiger Team Enhancements * fix standard tests * update tests * simplify icon area * set approved desktop column spans * css updates based on feedback * account for notifications * additional fixes for notification asides * additional tweaks to account for promobar and notification edge cases * remove unnecessary async/await declarations * remove additional unnecessary async/awaits * adjustments to styles based on QA review * add base min-height for mobile, use variables --------- Co-authored-by: Megan Thomas --- libs/blocks/aside/aside.css | 330 +++++++++----- libs/blocks/aside/aside.js | 29 +- libs/styles/iconography.css | 71 ++- test/blocks/aside/aside-legacy.test.js | 116 +++++ test/blocks/aside/aside.test.js | 259 ++++++----- test/blocks/aside/mocks/body.html | 587 ------------------------- test/blocks/aside/mocks/split.html | 114 +++++ test/blocks/aside/mocks/standard.html | 97 ++++ 8 files changed, 766 insertions(+), 837 deletions(-) create mode 100644 test/blocks/aside/aside-legacy.test.js create mode 100644 test/blocks/aside/mocks/split.html create mode 100644 test/blocks/aside/mocks/standard.html diff --git a/libs/blocks/aside/aside.css b/libs/blocks/aside/aside.css index c0ad710b34..7feb805ada 100644 --- a/libs/blocks/aside/aside.css +++ b/libs/blocks/aside/aside.css @@ -1,4 +1,6 @@ .aside { + --min-height: 160px; + display: flex; width: 100%; position: relative; @@ -16,6 +18,40 @@ margin: 0; } +.aside [class^="heading-"]:only-of-type, +.aside [class^="heading-"]:last-of-type { + margin-bottom: var(--spacing-xs); +} + +.aside:is(.promobar, .notification.extra-small, .notification.small) [class^="heading-"]:is(:only-of-type, :last-of-type) { + margin-bottom: 0; +} + +.aside [class^="body-"] { + margin-bottom: var(--spacing-s); +} + +.aside:not(.notification, .promobar) { + min-height: var(--min-height); +} + +.aside.promobar [class^="body-"], +.aside.notification.extra-small [class^="body-"] { + margin-bottom: 0; +} + +.aside [class^="detail-"] { + margin-bottom: var(--spacing-xs); +} + +.aside .title-l { + font-size: var(--type-body-m-size); + line-height: var(--type-body-m-lh); + font-weight: bold; + text-transform: none; + margin-bottom: var(--spacing-xs); +} + .aside.split picture { display: flex; } @@ -38,15 +74,9 @@ align-items: center; justify-content: center; flex-direction: column; - gap: var(--spacing-l); -} - -.aside:not(.notification):not(.inline) .foreground > :first-child { - padding: var(--spacing-xxxl) 0 0; -} - -.aside:not(.notification):not(.inline) .foreground > :last-child { - margin-bottom: var(--spacing-xxxl); + gap: var(--spacing-m); + padding: var(--spacing-xl) 0; + box-sizing: border-box; } .aside .foreground.container .text { @@ -57,7 +87,16 @@ .aside.split .foreground.container .text { margin: 0; max-width: var(--grid-container-width); - padding: var(--spacing-xxxl) 0 var(--spacing-l) 0; +} + +.aside.media-top-mobile .foreground .image, +.aside.media-top-mobile .split-image { + order: -1; +} + +.aside.media-bottom-mobile .foreground .image, +.aside.media-bottom-mobile .split-image { + order: 1; } .aside.simple .foreground.container .text { @@ -85,13 +124,9 @@ flex-basis: 100%; } -.aside.inline .foreground.container .image, -.aside.inline .foreground.container .text { - padding: 0; -} - -.aside.split.large .foreground.container .text { - padding: var(--spacing-l) 0 var(--spacing-xxxl) 0; +.aside.split .icon-stack-area li { + width: fit-content; + min-width: calc(50% - 6px); } .aside.split .icon-stack-area li, @@ -110,18 +145,11 @@ width: 100%; } -.aside .foreground.container .text .heading-xl { - margin: 0 0 var(--spacing-xs); -} - -.aside .foreground.container .text .body-m, -.aside .foreground.container .text .body-s { - margin-bottom: var(--spacing-s); -} - .aside .foreground.container .text .supplemental-text { - padding-top: var(--spacing-s); + margin-top: var(--spacing-s); margin-bottom: 0; + font-size: var(--type-body-s-size); + line-height: var(--type-body-s-lh); } .aside.promobar .promo-text .action-area { @@ -143,10 +171,14 @@ align-items: center; } -.aside .foreground.container .icon-area { - height: 56px; - max-width: 234px; +.aside:not(.notification) .foreground.container .icon-area { + max-height: 80px; + display: flex; + align-items: center; + gap: var(--spacing-xs); + line-height: 0; margin-bottom: var(--spacing-s); + font-weight: bold; } .aside .foreground.container a.icon-area { @@ -154,6 +186,15 @@ margin-bottom: 0; } +.aside.center .foreground.container .icon-area { + justify-content: center; +} + +.aside .avatar-area, +.aside .lockup-area { + margin-bottom: var(--spacing-s); +} + .aside.split .split-image img, .aside.split .split-image video { position: relative; @@ -196,9 +237,16 @@ align-items: center; } +.aside.split .icon-stack-area li picture { + flex-shrink: 0; +} + +.aside:not(.notification) .foreground.container .icon-area picture { + height: 100%; +} + .aside .foreground.container .icon-area img { - max-width: 234px; - height: var(--icon-size-l); + max-height: 100%; width: auto; object-fit: cover; object-position: left top; @@ -216,6 +264,11 @@ flex-direction: column-reverse; } +.aside.split.large.aside.media-top-mobile, +.aside.split.large.aside.media-bottom-mobile { + flex-direction: column; +} + .aside.split .foreground.container { width: 100%; max-width: 100%; @@ -236,10 +289,6 @@ display: none; } -.aside.split .icon-stack-area li picture { - flex-shrink: 0; -} - .aside.notification .text [class^="heading-"] + .action-area { margin-top: var(--spacing-xs); } @@ -248,6 +297,11 @@ display: block; } +.aside.rounded-corners .foreground .image img, +.aside.rounded-corners .foreground .image video { + border-radius: 16px; +} + .aside .foreground.container .image video, .aside .foreground.container .image picture, .aside .foreground.container .image picture img { @@ -322,6 +376,7 @@ .aside.promobar .foreground.container .icon-area img { height: var(--icon-size-m); + max-width: 234px; } .aside.notification.extra-small .foreground.container a:last-child { @@ -359,6 +414,7 @@ .aside.notification .foreground.container .icon-area img { max-height: 40px; + max-width: 234px; height: auto; } @@ -443,6 +499,10 @@ padding: 0; } +.aside.no-media:not(.notification) .foreground.container { + gap: 0; +} + .aside.notification.large.center .foreground.container, .aside.notification.large .foreground.container.no-image { max-width: 800px; @@ -466,7 +526,6 @@ .aside.split .icon-stack-area { display: flex; flex-flow: row wrap; - flex-direction: column; gap: 12px; margin: -8px 0 var(--spacing-s); width: 100%; @@ -490,10 +549,6 @@ justify-content: center; } -.aside:not(.notification) .foreground.container .text .detail-m { - margin-bottom: var(--spacing-xs); -} - .aside.split .image.format { display: flex; } @@ -502,16 +557,6 @@ display: none; } -.aside.center:not(.notification) .foreground.container .text .icon-area { - height: var(--icon-size-xxl); -} - -.aside.center:not(.notification) .foreground.container .text .icon-area img { - height: var(--icon-size-xxl); - max-width: 300px; - width: auto; -} - .aside.promobar .foreground.container > :first-child { padding: var(--spacing-xs) 0; } @@ -611,47 +656,47 @@ stroke: var(--color-white); } -@media screen and (max-width: 600px) { - .aside.no-media:not(.notification) .foreground.container { - gap: 0; +.aside.promobar.popup .mobile-up.hide-block ~ .promo-close { + display: none; +} + +@media screen and (min-width: 600px) { + .aside { + --min-height-small: 420px; + --min-height-medium: 560px; + --min-height-large: 700px; } - .aside.split.no-media:not(.notification) .foreground.container .text { - padding: var(--spacing-xxxl) 0; + .aside.small:not(.notification, .promobar) { + min-height: var(--min-height-small); } - .aside.promobar.popup .mobile-up.hide-block ~ .promo-close { - display: none; + .aside.medium:not(.notification, .promobar) { + min-height: var(--min-height-medium); } -} -@media screen and (min-width: 600px) { - .aside.small { - min-height: 420px; + .aside.large:not(.notification, .promobar) { + min-height: var(--min-height-large); } - .aside.medium { - min-height: 560px; + .aside.media-top-mobile .foreground .image, + .aside.media-top-mobile .split-image { + order: unset; } - .aside.large { - min-height: 700px; + .aside.media-bottom-mobile .foreground .image, + .aside.media-bottom-mobile .split-image { + order: unset; + } + + .aside.promobar.popup .mobile-up.hide-block ~ .promo-close { + display: unset; } .aside .foreground.container { align-items: center; flex-direction: row; margin: 0 auto; - padding: var(--spacing-m) 0; - gap: var(--spacing-xl); - } - - .aside:not(.notification):not(.inline) .foreground.container > :first-child { - padding: 0; - } - - .aside:not(.notification):not(.inline):not(.center) .foreground.container > :last-child { - margin-bottom: 0; } .aside .foreground.container .image { @@ -674,7 +719,6 @@ .aside .foreground.container .text { margin-bottom: 0; - padding-right: var(--spacing-s); } .aside.split-right .foreground.container .text { @@ -687,15 +731,6 @@ padding-right: 0; } - .aside.split .split-image { - overflow: hidden; - position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 0; - } - .aside.split .tablet-wide img, .aside.split .tablet-wide video { aspect-ratio: var(--aspect-ratio-wide); @@ -718,8 +753,7 @@ .aside .split-image .modal-img-link, .aside.split .split-image img, .aside.split .split-image video { - width: 60.5vw; - max-width: 56%; + width: 50vw; position: absolute; right: 0; object-fit: cover; @@ -767,8 +801,7 @@ } .aside.split .foreground.container .text { - flex: 0 0 31vw; - padding: var(--spacing-xxl) 0; + flex: 0 0 41.67%; max-width: 100%; margin: 0; } @@ -795,7 +828,7 @@ flex-grow: 1; } - .aside.notification.small .foreground.container .text .body-m { + .aside.notification.small [class^="body-"] { margin: 0; } @@ -879,10 +912,6 @@ height: 40px; } - .aside.center:not(.notification) .foreground.container .text .icon-area img { - max-width: 234px; - } - .aside.medium.split.bio .foreground.container .text .icon-area, .aside.large.split.bio .foreground.container .text .icon-area { height: var(--icon-size-xxl); @@ -922,16 +951,84 @@ } } -@media (min-width: 600px) and (max-width: 1200px) { +@media (min-width: 600px) and (max-width: 1199px) { .aside.promobar.popup .tablet-up.hide-block ~ .promo-close { display: none; } + + .aside.media-top-tablet .foreground.container, + .aside.media-bottom-tablet .foreground.container { + justify-content: center; + flex-direction: column; + gap: var(--spacing-l); + padding: var(--spacing-xl) 0; + } + + .aside.media-top-tablet .foreground.container .text, + .aside.media-bottom-tablet .foreground.container .text { + padding-right: 0; + } + + .aside.media-top-tablet .foreground .image, + .aside.split.media-top-tablet .split-image { + order: -1; + } + + .aside.media-bottom-tablet .foreground .image, + .aside.split.media-bottom-tablet .split-image { + order: 100; + } + + .aside.large.media-top-tablet, + .aside.large.media-bottom-tablet { + min-height: auto; + } + + .aside.split.large.media-top-tablet .foreground.container .text, + .aside.split.large.media-bottom-tablet .foreground.container .text { + padding: 0; + flex: 0 0 100%; + } + + .aside.split.media-top-tablet .split-image, + .aside.split.media-bottom-tablet .split-image { + position: relative; + } + + .aside.split.media-top-tablet .split-image img, + .aside.split.media-bottom-tablet .split-image img { + position: relative; + width: 100%; + max-width: 100%; + } + + .aside.split.media-top-tablet .text .icon-stack-area, + .aside.split.media-bottom-tablet .text .icon-stack-area { + width: 83.33%; + } + + .aside.split.media-top-tablet .icon-stack-area li, + .aside.split.media-bottom-tablet .icon-stack-area li { + width: calc(50% - 6px); + } } @media screen and (min-width: 1200px) { - .aside .foreground.container { + .aside.small { min-height: 420px; + } + + .aside.medium { + min-height: 560px; + } + + .aside.large { + min-height: 700px; + } + + .aside .foreground.container { width: var(--grid-container-width); + gap: var(--grid-column-width); } .aside .foreground.container > div { @@ -939,16 +1036,12 @@ padding-left: 0; } - .aside .foreground.container .text { - padding: var(--spacing-xxl) 0; + .aside:not(.notification) .foreground.container .text { + flex: 1 0 calc(var(--grid-column-width) * 6); } - .aside .foreground.container .icon-area { - max-width: 400px; - } - - .aside .foreground.container .text .heading-xl { - margin: 0 0 var(--spacing-xs); + .aside:not(.notification) .foreground.container .image { + flex: 1 0 calc(var(--grid-column-width) * 5); } .aside.inline .foreground.container .text { @@ -961,7 +1054,7 @@ } .aside.split .foreground.container .text { - flex: 0 0 300px; + flex: 0 0 29%; } .aside.split .foreground.container .image { @@ -970,6 +1063,12 @@ object-fit: cover; } + .aside .split-image .modal-img-link, + .aside.split .split-image img, + .aside.split .split-image video { + width: 60.5vw; + } + .aside.split .split-image img, .aside.split .split-image video { max-width: 1396px; @@ -999,14 +1098,8 @@ flex: 0 0 calc(36% - var(--spacing-s)); } - .aside.split.icon-stack .foreground.container .text, - .aside.split.bio .foreground.container .text { - padding: var(--spacing-xxl) 0; - } - .aside.split .icon-stack-area li { - max-width: calc(50% - 6px); - min-width: calc(50% - 6px); + width: calc(50% - 6px); } .aside.split .icon-stack-area { @@ -1023,10 +1116,6 @@ height: var(--icon-size-m); } - .aside.center:not(.notification) .foreground.container .text .icon-area img { - max-width: 400px; - } - .aside.center:not(.notification) .foreground.container .text { max-width: 50%; } @@ -1092,6 +1181,7 @@ .aside.promobar .promo-text .action-area { gap: var(--spacing-s); + flex-wrap: nowrap; } .aside.promobar .promo-text.mobile-up, @@ -1139,3 +1229,9 @@ margin-top: 0; } } + +@media screen and (min-width: 1440px) { + .aside.split .foreground.container .text { + flex: 0 0 calc(500px - 10.5vw); + } +} diff --git a/libs/blocks/aside/aside.js b/libs/blocks/aside/aside.js index dde3602f8c..59975b58f6 100644 --- a/libs/blocks/aside/aside.js +++ b/libs/blocks/aside/aside.js @@ -14,8 +14,8 @@ * Aside - v5.1 */ -import { decorateBlockText, decorateIconStack, applyHoverPlay, decorateBlockBg } from '../../utils/decorate.js'; -import { createTag } from '../../utils/utils.js'; +import { decorateBlockText, decorateIconStack, applyHoverPlay, decorateBlockBg, decorateTextOverrides } from '../../utils/decorate.js'; +import { createTag, getConfig, loadStyle } from '../../utils/utils.js'; // standard/default aside uses same text sizes as the split const variants = ['split', 'inline', 'notification']; @@ -163,6 +163,12 @@ function decoratePromobar(el) { return foreground; } +function loadIconography() { + const { miloLibs, codeRoot } = getConfig(); + const base = miloLibs || codeRoot; + return new Promise((resolve) => { loadStyle(`${base}/styles/iconography.css`, resolve); }); +} + function decorateLayout(el) { const elems = el.querySelectorAll(':scope > div'); if (elems.length > 1) { @@ -182,11 +188,16 @@ function decorateLayout(el) { } const picture = text?.querySelector('p picture'); const iconArea = picture ? (picture.closest('p') || createTag('p', null, picture)) : null; - iconArea?.classList.add('icon-area'); + if (iconArea) { + const iconVariant = el.className.match(/-(avatar|lockup)/); + const iconClass = iconVariant ? `${iconVariant[1]}-area` : 'icon-area'; + if (iconVariant) loadIconography(); + iconArea.classList.add(iconClass); + } const foregroundImage = foreground.querySelector(':scope > div:not(.text) img')?.closest('div'); const bgImage = el.querySelector(':scope > div:not(.text):not(.foreground) img')?.closest('div'); - const foregroundMedia = foreground.querySelector(':scope > div:not(.text) video, :scope > div:not(.text) a[href*=".mp4"]')?.closest('div'); - const bgMedia = el.querySelector(':scope > div:not(.text):not(.foreground) video, :scope > div:not(.text):not(.foreground) a[href*=".mp4"]')?.closest('div'); + const foregroundMedia = foreground.querySelector(':scope > div:not(.text) video, :scope > div:not(.text) a:is([href*=".mp4"], [href*="tv.adobe.com"])')?.closest('div'); + const bgMedia = el.querySelector(':scope > div:not(.text):not(.foreground) video, :scope > div:not(.text):not(.foreground) a:is([href*=".mp4"], [href*="tv.adobe.com"])')?.closest('div'); const image = foregroundImage ?? bgImage; const asideMedia = foregroundMedia ?? bgMedia ?? image; const isSplit = el.classList.contains('split'); @@ -202,10 +213,7 @@ function decorateLayout(el) { } else if (!iconArea) { foreground?.classList.add('no-image'); } - if (el.classList.contains('split') - && (el.classList.contains('medium') || el.classList.contains('large'))) { - decorateIconStack(el); - } + if (el.classList.contains('split')) decorateIconStack(el); return foreground; } @@ -216,4 +224,7 @@ export default function init(el) { decorateBlockText(blockText, blockData); decorateStaticLinks(el); formatPromoButton(el); + decorateTextOverrides(el); + // Override Detail with Title L style if class exists - Temporary solution until Spectrum 2 + if (el.classList.contains('l-title')) el.querySelector('[class*="detail-"]')?.classList.add('title-l'); } diff --git a/libs/styles/iconography.css b/libs/styles/iconography.css index acee064c00..83ca16ac01 100644 --- a/libs/styles/iconography.css +++ b/libs/styles/iconography.css @@ -1,28 +1,40 @@ -/* iconograph.css +/* iconography.css This is consonant shared design patterns dealing w/ groups of media and associated text - lockup -(coming soon...) - avatar */ :root { - /* lockup sizes */ - --type-lockup-all-weight: 700; - --type-lockup-xxl-size: 55px; - --type-lockup-xxl-ls: -0.01em; - --type-lockup-xl-size: 44px; - --type-lockup-xl-ls: -0.01em; - --type-lockup-l-size: 38px; - --type-lockup-l-ls: -0.01em; - --type-lockup-m-size: 27px; - --type-lockup-m-ls: -0.02em; - --type-lockup-s-size: 22px; - --type-lockup-s-ls: -0.02em; - --type-lockup-xs-size: 16px; - --type-lockup-xs-ls: 0; - --type-lockup-xxs-size: 11px; - --type-lockup-xxs-ls: -0.01em; + /* lockup sizes */ + --type-lockup-all-weight: 700; + --type-lockup-xxl-size: 55px; + --type-lockup-xxl-ls: -0.01em; + --type-lockup-xl-size: 44px; + --type-lockup-xl-ls: -0.01em; + --type-lockup-l-size: 38px; + --type-lockup-l-ls: -0.01em; + --type-lockup-m-size: 27px; + --type-lockup-m-ls: -0.02em; + --type-lockup-s-size: 22px; + --type-lockup-s-ls: -0.02em; + --type-lockup-xs-size: 16px; + --type-lockup-xs-ls: 0; + --type-lockup-xxs-size: 11px; + --type-lockup-xxs-ls: -0.01em; + + /* avatar sizes */ + --avatar-size-xxl: 64px; + --avatar-size-xl: 56px; + --avatar-size-l: 40px; +} + +@media (min-width: 1200px) { + :root { + --avatar-size-xxl: 80px; + --avatar-size-xl: 64px; + --avatar-size-l: 56px; + } } /* Lockup */ @@ -90,3 +102,26 @@ dealing w/ groups of media and associated text .l-lockup .lockup-area img { height: var(--icon-size-l); } .xl-lockup .lockup-area img { height: var(--icon-size-xl); } .xxl-lockup .lockup-area img { height: var(--icon-size-xxl); } + +.avatar-area { + line-height: 0; + margin-bottom: var(--spacing-s); +} + +.l-avatar .avatar-area img { + border-radius: 50%; + height: var(--avatar-size-l); + width: var(--avatar-size-l); +} + +.xl-avatar .avatar-area img { + border-radius: 50%; + height: var(--avatar-size-xl); + width: var(--avatar-size-xl); +} + +.xxl-avatar .avatar-area img { + border-radius: 50%; + height: var(--avatar-size-xxl); + width: var(--avatar-size-xxl); +} diff --git a/test/blocks/aside/aside-legacy.test.js b/test/blocks/aside/aside-legacy.test.js new file mode 100644 index 0000000000..66e017582b --- /dev/null +++ b/test/blocks/aside/aside-legacy.test.js @@ -0,0 +1,116 @@ +import { readFile } from '@web/test-runner-commands'; +import { expect } from '@esm-bundle/chai'; + +document.body.innerHTML = await readFile({ path: './mocks/body.html' }); +const { default: init } = await import('../../../libs/blocks/aside/aside.js'); + +const types = ['simple', 'split', 'inline', 'notification', 'promobar']; + +describe('aside', () => { + const asides = document.querySelectorAll('.aside'); + asides.forEach((aside) => { + init(aside); + + const typeIndex = types.findIndex((v) => aside.classList.contains(v)); + const type = typeIndex >= 0 ? types[typeIndex] : 'default'; + + describe(`aside ${type}`, () => { + const isInline = type === 'inline'; + + if (type !== 'notification') { + it('has a heading', () => { + const heading = aside.querySelector('[class^=heading-]'); + expect(heading).to.exist; + }); + + it('icon has a wrapper', () => { + const icon = aside.querySelector('.text picture, .promo-text picture'); + if (icon) { + expect(icon.closest('.icon-area')).to.exist; + } + }); + + it('has a body', () => { + const body = aside.querySelector('[class^=body-]'); + expect(body).to.exist; + }); + + it('button has a wrapper', () => { + const button = aside.querySelector('.text .con-button, .promo-text .con-button'); + if (button) { + expect(button.closest('p')).to.exist; + } + }); + + if (aside.classList.contains('icon-stack')) { + it('Has icon stack area', () => { + const iconStack = aside.querySelector('ul.icon-stack-area'); + expect(iconStack).to.exist; + }); + } + + if (aside.classList.contains('aspect-ratio')) { + it('Has aspect ratio set', () => { + let aspectRatios = ''; + if (aside.classList.contains('aspect-ratio-three')) { + aspectRatios = aside.querySelector('.mobile-square.tablet-standard.desktop-wide'); + expect(aspectRatios).to.exist; + } else if (aside.classList.contains('aspect-ratio-two')) { + aspectRatios = aside.querySelector('.mobile-standard.tablet-wide'); + expect(aspectRatios).to.exist; + } else if (aside.classList.contains('aspect-ratio-one')) { + aspectRatios = aside.querySelector('.mobile-standard'); + expect(aspectRatios).to.exist; + } + }); + } + } + + if (type === 'default' || type === isInline) { + it('has an image', () => { + const image = aside.querySelector('.image'); + expect(image).to.exist; + }); + } + + if (type === types[1]) { + it('has a background image or video', () => { + const body = aside.querySelector('.split-image'); + expect(body).to.exist; + }); + } + + if (type === 'promobar') { + it('has viewport content', () => { + const viewportContent = aside.querySelectorAll('.promo-text'); + expect(viewportContent.length).to.equal(3); + }); + + if (aside.classList.contains('popup')) { + it('has promo close button', () => { + const closeBtn = aside.querySelector('.promo-close'); + expect(closeBtn).to.exist; + }); + + if (aside.classList.contains('mobile-promo-only')) { + it('has empty tablet block hidden', () => { + const tabletBlock = aside.querySelector('.tablet-up.hide-block'); + expect(tabletBlock).to.exist; + }); + + it('has empty desktop block hidden', () => { + const desktopBlock = aside.querySelector('.tablet-up.hide-block'); + expect(desktopBlock).to.exist; + }); + } + + it('close button click closes the popup', () => { + const closeBtn = aside.querySelector('.promo-close'); + closeBtn.click(); + expect(aside.closest('.section').classList.contains('close-sticky-section')).to.be.true; + }); + } + } + }); + }); +}); diff --git a/test/blocks/aside/aside.test.js b/test/blocks/aside/aside.test.js index 66e017582b..d0793c430b 100644 --- a/test/blocks/aside/aside.test.js +++ b/test/blocks/aside/aside.test.js @@ -1,116 +1,163 @@ import { readFile } from '@web/test-runner-commands'; import { expect } from '@esm-bundle/chai'; +import { waitForElement } from '../../helpers/waitfor.js'; +import { setConfig } from '../../../libs/utils/utils.js'; -document.body.innerHTML = await readFile({ path: './mocks/body.html' }); const { default: init } = await import('../../../libs/blocks/aside/aside.js'); +const standardBody = await readFile({ path: './mocks/standard.html' }); +const splitBody = await readFile({ path: './mocks/split.html' }); +const conf = { miloLibs: 'http://localhost:2000/libs' }; -const types = ['simple', 'split', 'inline', 'notification', 'promobar']; +setConfig(conf); describe('aside', () => { - const asides = document.querySelectorAll('.aside'); - asides.forEach((aside) => { - init(aside); - - const typeIndex = types.findIndex((v) => aside.classList.contains(v)); - const type = typeIndex >= 0 ? types[typeIndex] : 'default'; - - describe(`aside ${type}`, () => { - const isInline = type === 'inline'; - - if (type !== 'notification') { - it('has a heading', () => { - const heading = aside.querySelector('[class^=heading-]'); - expect(heading).to.exist; - }); - - it('icon has a wrapper', () => { - const icon = aside.querySelector('.text picture, .promo-text picture'); - if (icon) { - expect(icon.closest('.icon-area')).to.exist; - } - }); - - it('has a body', () => { - const body = aside.querySelector('[class^=body-]'); - expect(body).to.exist; - }); - - it('button has a wrapper', () => { - const button = aside.querySelector('.text .con-button, .promo-text .con-button'); - if (button) { - expect(button.closest('p')).to.exist; - } - }); - - if (aside.classList.contains('icon-stack')) { - it('Has icon stack area', () => { - const iconStack = aside.querySelector('ul.icon-stack-area'); - expect(iconStack).to.exist; - }); - } - - if (aside.classList.contains('aspect-ratio')) { - it('Has aspect ratio set', () => { - let aspectRatios = ''; - if (aside.classList.contains('aspect-ratio-three')) { - aspectRatios = aside.querySelector('.mobile-square.tablet-standard.desktop-wide'); - expect(aspectRatios).to.exist; - } else if (aside.classList.contains('aspect-ratio-two')) { - aspectRatios = aside.querySelector('.mobile-standard.tablet-wide'); - expect(aspectRatios).to.exist; - } else if (aside.classList.contains('aspect-ratio-one')) { - aspectRatios = aside.querySelector('.mobile-standard'); - expect(aspectRatios).to.exist; - } - }); - } - } - - if (type === 'default' || type === isInline) { - it('has an image', () => { - const image = aside.querySelector('.image'); - expect(image).to.exist; - }); - } - - if (type === types[1]) { - it('has a background image or video', () => { - const body = aside.querySelector('.split-image'); - expect(body).to.exist; - }); - } - - if (type === 'promobar') { - it('has viewport content', () => { - const viewportContent = aside.querySelectorAll('.promo-text'); - expect(viewportContent.length).to.equal(3); - }); - - if (aside.classList.contains('popup')) { - it('has promo close button', () => { - const closeBtn = aside.querySelector('.promo-close'); - expect(closeBtn).to.exist; - }); - - if (aside.classList.contains('mobile-promo-only')) { - it('has empty tablet block hidden', () => { - const tabletBlock = aside.querySelector('.tablet-up.hide-block'); - expect(tabletBlock).to.exist; - }); - - it('has empty desktop block hidden', () => { - const desktopBlock = aside.querySelector('.tablet-up.hide-block'); - expect(desktopBlock).to.exist; - }); - } - - it('close button click closes the popup', () => { - const closeBtn = aside.querySelector('.promo-close'); - closeBtn.click(); - expect(aside.closest('.section').classList.contains('close-sticky-section')).to.be.true; - }); - } - } + describe('standard', () => { + before(() => { + document.body.innerHTML = standardBody; + const blocks = document.querySelectorAll('.aside'); + blocks.forEach((el) => init(el)); + }); + + it('allows a background color', async () => { + const el = await waitForElement('#test-default'); + expect(window.getComputedStyle(el)?.backgroundColor).to.equal('rgb(238, 238, 238)'); + }); + + it('allows a background image', async () => { + const el = await waitForElement('#test-default-2 .background img'); + expect(el).to.exist; + }); + + it('allows an icon image', async () => { + const el = await waitForElement('#test-default .icon-area img'); + expect(el).to.exist; + }); + + it('has Detail M by default', async () => { + const el = await waitForElement('#test-default .detail-m'); + expect(el).to.exist; + }); + + it('has Heading XL by default', async () => { + const el = await waitForElement('#test-default .heading-xl'); + expect(el).to.exist; + }); + + it('has Body S by default', async () => { + const el = await waitForElement('#test-default p.body-s'); + expect(el).to.exist; + }); + + it('allows a cta', async () => { + const el = await waitForElement('#test-default .action-area .con-button'); + expect(el).to.exist; + }); + + it('allows supplemental text', async () => { + const el = await waitForElement('#test-default .supplemental-text'); + expect(el).to.exist; + }); + + it('allows a foreground image', async () => { + const el = await waitForElement('#test-default .foreground .image img'); + expect(el).to.exist; + }); + + it('allows text overrides', async () => { + const el = await waitForElement('#test-text-overrides'); + expect(el.querySelector('.detail-l')).to.exist; + expect(el.querySelector('.heading-l')).to.exist; + expect(el.querySelector('p.body-m')).to.exist; + }); + + it('allows Title L to override Detail', async () => { + const el = await waitForElement('#test-title .detail-m.title-l'); + expect(el).to.exist; + }); + + it('allows an avatar', async () => { + const el = await waitForElement('#test-avatar .avatar-area img'); + expect(el).to.exist; + }); + + it('allows a product lockup', async () => { + const el = await waitForElement('#test-lockup .lockup-area img'); + expect(el).to.exist; + }); + }); + + describe('split', () => { + before(() => { + document.body.innerHTML = splitBody; + const blocks = document.querySelectorAll('.aside'); + blocks.forEach((el) => init(el)); + }); + + it('allows a background color', async () => { + const el = await waitForElement('#test-default'); + expect(window.getComputedStyle(el)?.backgroundColor).to.equal('rgb(30, 30, 30)'); + }); + + it('allows an icon image', async () => { + const el = await waitForElement('#test-default .icon-area img'); + expect(el).to.exist; + }); + + it('has Detail M by default', async () => { + const el = await waitForElement('#test-default .detail-m'); + expect(el).to.exist; + }); + + it('has Heading XL by default', async () => { + const el = await waitForElement('#test-default .heading-xl'); + expect(el).to.exist; + }); + + it('has Body S by default', async () => { + const el = await waitForElement('#test-default p.body-s'); + expect(el).to.exist; + }); + + it('allows icon stack', async () => { + const el = await waitForElement('#test-default .icon-stack-area'); + expect(el).to.exist; + }); + + it('allows a cta', async () => { + const el = await waitForElement('#test-default .action-area .con-button'); + expect(el).to.exist; + }); + + it('allows supplemental text', async () => { + const el = await waitForElement('#test-default .supplemental-text'); + expect(el).to.exist; + }); + + it('allows a split image', async () => { + const el = await waitForElement('#test-default .split-image img'); + expect(el).to.exist; + }); + + it('allows text overrides', async () => { + const el = await waitForElement('#test-text-overrides'); + expect(el.querySelector('.detail-l')).to.exist; + expect(el.querySelector('.heading-l')).to.exist; + expect(el.querySelector('p.body-m')).to.exist; + }); + + it('allows Title L to override Detail', async () => { + const el = await waitForElement('#test-title .detail-m.title-l'); + expect(el).to.exist; + }); + + it('allows an avatar', async () => { + const el = await waitForElement('#test-avatar .avatar-area img'); + expect(el).to.exist; + }); + + it('allows a product lockup', async () => { + const el = await waitForElement('#test-lockup .lockup-area img'); + expect(el).to.exist; }); }); }); diff --git a/test/blocks/aside/mocks/body.html b/test/blocks/aside/mocks/body.html index 3ac00c2d9b..b41014fd38 100644 --- a/test/blocks/aside/mocks/body.html +++ b/test/blocks/aside/mocks/body.html @@ -3,88 +3,6 @@ data-section-status="loaded">

Aside

-

Standard - Small

-
-
-
#f9f9f9
-
#f9f9f9
-
-
-
-

{{icon-illustrator}}

-

Detail M Bold

-

Heading XL Aside standard small two lines

-

Body S Regular. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor - incididunt ut labore et dolore magna.

-

Learn More

-
-
- - - - -
-
-
-

Standard - Medium

-
-
-
Vital Ocean
-
-
-
-

{{icon-illustrator}}

-

Detail M Bold

-

Heading XL Aside - standard medium three lines Lorem ipsum dolor sit amet.

-

Body S Regular. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor - incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation - ullamco laboris nisi ut.

-

Learn More Sign Up

-
-
- - - - -
-
-
-

Standard - Large

-
-
-
- - - - -
-
-
-
-

{{icon-illustrator}}

-

Detail M Bold

-

- Heading XL Aside standard large five lines Lorem ipsum dolor sit amet, consectetur adipiscing - elit, sed do eismod tempor incididunt.

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut - labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco - laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in - voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat. -

-

Learn More Sign Up

-
-
- - - - -
-
-

Simple - Small

@@ -100,511 +18,6 @@

Heading XL Aside Standard S

-

Split - Small

-
-
-
- - - - -
-
-
-
-

Detail M Bold

-

Heading XL Aside split ipsum.

-

Body S Regular. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor - incididunt ut labore et dolore magna.

-

Learn More

-
-
-
-

Split – Medium - Half

-
-
-
- - - - -
-
-
-
-

Detail M Bold

-

Heading XL Lorem ipsum dolor - sit, consectetur adipiscing elit.

-

Body S Regular. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor - incididunt ut labore et dolore magna. At vero eos et accusamus et iusto odio dignissimos ducimus - qui blanditiis.

-

Learn More

-
-
-
-

Split – Large - Right

-
-
-
- - - - -
-
-
-
-

Detail M Bold

-

- Heading XL Lorem ipsum dolor sit, consectetur adipiscing elit, sed do eiusmod incididunt ut. -

-

Body S Regular. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor - incididunt ut labore et dolore magna. At vero eos et accusamus et iusto odio dignissimos ducimus - qui blanditiis praesentium voluptatum deleniti atque.

-

Learn More

-
-
-
-
-
-
#eeeeee
-
-
-
-

- - - - - - -

-

DETAIL M BOLD

-

Heading XL Aside Standard Small

-

Body S Regular Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut - labore et dolore magna.

- -

Learn More Learn More

-
-
- - - - - - -
-
-
-
-
-
#d3d3d3
-
-
-
-

- - - - - - -

-

DETAIL M BOLD

-

Heading XL Aside Standard Small

-

Body S Regular Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut - labore et dolore magna.

-

Learn More Learn More

-
-
- -
-
-
-
-
-
- -
-
- -
-
-
-
-

Detail M Bold

-

Heading XL Lorem ipsum dolor sit, - consectetur adipiscing elit.

-

Body S Regular. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt - ut labore et dolore magna. At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis.

-

Learn More Learn More

-
-
-
-
-
-
-
- -
-
- -
-
- -
-
-
-
-

Detail M Bold

-

Heading XL Lorem ipsum dolor sit, - consectetur adipiscing elit.

-

Body S Regular. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt - ut labore et dolore magna. At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis.

-

Learn More Learn More

-
-
-
-
-
-
-
- -
-
-
-
-

Detail M Bold

-

Heading XL Lorem ipsum dolor sit, - consectetur adipiscing elit.

-

Body S Regular. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt - ut labore et dolore magna. At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis.

-

Learn More Learn More

-
-
-
-
-
-
-
#d3d3d3
-
-
-
-

- - - -

-
-

Detail M Bold

-

Heading XL - Lorem ipsum dolor sit, consectetur adipiscing elit, sed do eiusmod incididunt ut.

-

Body S Regular. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt - ut labore et dolore magna. At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis - praesentium voluptatum deleniti atque.

-

Learn More Learn More

-
-
-

- - - - - - -

-

format: square standard wide

-
-
-
-
-
-
#d3d3d3
-
-
-
-

- - - -

-
-

Detail M Bold

-

Heading XL - Lorem ipsum dolor sit, consectetur adipiscing elit, sed do eiusmod incididunt ut.

-

Body S Regular. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt - ut labore et dolore magna. At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis - praesentium voluptatum deleniti atque.

-

Learn More Learn More

-
-
-

- - - - - - -

-

format: standard wide

-
-
-
-
-
-
#d3d3d3
-
-
-
-

- - - -

-
-

Detail M Bold

-

Heading XL - Lorem ipsum dolor sit, consectetur adipiscing elit, sed do eiusmod incididunt ut.

-

Body S Regular. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt - ut labore et dolore magna. At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis - praesentium voluptatum deleniti atque.

-

Learn More Learn More

-
-
-

- - - - - - -

-

format: standard

-
-
-
-
-
-
#d3d3d3
-
-
-
-

- - - -

-
-

Detail M Bold

-

Heading XL - Lorem ipsum dolor sit, consectetur adipiscing elit, sed do eiusmod incididunt ut.

-

Body S Regular. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt - ut labore et dolore magna. At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis - praesentium voluptatum deleniti atque.

-

Learn More Learn More

-
-
-

- - - - - - -

-

format:

-
-
-
-
-
-
#eeeeee
-
-
-
-

- - - - - - -

-

DETAIL M BOLD

-

- Heading XL Aside standard large five lines Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do - eismod tempor incididunt.

-

Body S Regular. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt - ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut - aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum - dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat.

-

Learn More Text - Link

-

Supplemental text for large

-
-
- - - - - - -
-
-

Inline

diff --git a/test/blocks/aside/mocks/split.html b/test/blocks/aside/mocks/split.html new file mode 100644 index 0000000000..11f3e609d4 --- /dev/null +++ b/test/blocks/aside/mocks/split.html @@ -0,0 +1,114 @@ +
+
+
#1e1e1e
+
+
+
+

+ + + +

+

Detail

+

Heading – Aside (split, medium)

+

Body Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna.

+ +

Learn More Learn More Text Link

+

Body CTA supplemental text (optional)

+
+
+ + + +
+
+
+
+
+
#1e1e1e
+
+
+
+

+ + + +

+

Detail

+

Heading – Aside (split, l-heading, m-body, l-detail)

+

Body Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna.

+

Learn More Learn More Text Link

+

Body CTA supplemental text (optional)

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

Title

+

Heading – Aside (split, l-title)

+

Body Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna.

+

Learn More Learn More Text Link

+

Body CTA supplemental text (optional)

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

+ + + +

+

Detail

+

Heading – Aside (split, l-avatar)

+

Body Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna.

+

Learn More Learn More Text Link

+

Body CTA supplemental text (optional)

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

Adobe

+

Detail

+

Heading – Aside (split, l-avatar)

+

Body Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna.

+

Learn More Learn More Text Link

+

Body CTA supplemental text (optional)

+
+
+ + + +
+
+
diff --git a/test/blocks/aside/mocks/standard.html b/test/blocks/aside/mocks/standard.html new file mode 100644 index 0000000000..51b171413e --- /dev/null +++ b/test/blocks/aside/mocks/standard.html @@ -0,0 +1,97 @@ +
+
+
#eeeeee
+
+
+
+

+ + + +

+

Detail

+

Heading – Aside

+

Body Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna.

+

Learn More Text Link

+

Body CTA supplemental text (optional)

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

Detail

+

Heading – Aside

+

Body Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna.

+

Learn More Text Link

+
+
+
+
+
+
+

Detail

+

Heading – Aside (center, l-heading, m-body, l-detail)

+

Body Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna.

+

Learn More Text Link

+
+
+
+
+
+
+

Title

+

Heading – Aside (center, l-title)

+

Body Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna.

+

Learn More Text Link

+
+
+
+
+
+
+

+ + + +

+

Detail

+

Heading – Aside (l-avatar)

+

Body Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna.

+

Learn More Text Link

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

Adobe

+

Detail

+

Heading – Aside

+

Body Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna.

+

Learn More Text Link

+
+
+ + + +
+
+
From 6012fcb07adebad2730004308f36d41a86d5c37e Mon Sep 17 00:00:00 2001 From: Elan Bartholomew <79870969+elan-tbx@users.noreply.github.com> Date: Thu, 29 Aug 2024 08:21:30 -0600 Subject: [PATCH 02/66] MWPW-155621 - Adding functionality to the Pill Notification (#2783) * add multiviewport content control * minor style tweaks, leaving text.js alone for now * Revert "MWPW-151936 - Aside Tiger Team Enhancements (redux)" (#2777) Revert "MWPW-151936 - Aside Tiger Team Enhancements (redux) (#2767)" This reverts commit 88cb1010512d247531a75bfef6bdc174a2b957b7. * per-viewport dom replacing, and popup behavior support * account for flexible box shadow * update mock * improvements based on PR feedback * handle resize multi-viewport edge case * accounting for ribbon edge cases * sticky top pill edge case, default bg * handle body-only copy area --- libs/blocks/notification/notification.css | 65 ++++++++++++++++--- libs/blocks/notification/notification.js | 50 +++++++++----- .../section-metadata/section-metadata.css | 4 ++ .../blocks/section-metadata/sticky-section.js | 6 +- libs/utils/decorate.js | 20 ++++++ test/blocks/notification/mocks/body.html | 47 ++++++++++++++ 6 files changed, 163 insertions(+), 29 deletions(-) diff --git a/libs/blocks/notification/notification.css b/libs/blocks/notification/notification.css index 0dff835631..182ca31ced 100644 --- a/libs/blocks/notification/notification.css +++ b/libs/blocks/notification/notification.css @@ -16,6 +16,7 @@ --icon-size: 56px; --icon-size-xl: 64px; --pill-radius: 16px; + --pill-shadow: 0 3px 6px #707070; display: flex; inline-size: 100%; @@ -25,6 +26,7 @@ flex-wrap: wrap; overflow: hidden; min-block-size: var(--min-block-size); + background: var(--color-white); } .notification.ribbon { @@ -151,6 +153,14 @@ inline-size: 100%; } +.notification.ribbon [class*="heading-"]:only-child { + margin-block-end: var(--spacing-s); +} + +.notification.ribbon.space-between [class*="heading-"]:only-child { + margin-block-end: 0; +} + .notification.ribbon.space-between .foreground .text { flex-wrap: nowrap; inline-size: 100%; @@ -158,6 +168,9 @@ .notification.ribbon.space-between .foreground .copy-wrap { margin-inline-end: var(--spacing-s); + display: flex; + flex-direction: column; + justify-content: center; } .notification .foreground .image { @@ -197,6 +210,12 @@ flex-wrap: nowrap; } +.notification .foreground .image :is(picture, video), +.notification .foreground .image picture img { + inline-size: 100%; + display: flex; +} + .notification:is(.ribbon.m-icon, .pill) .icon-area img { max-block-size: var(--icon-size-m); } @@ -250,12 +269,6 @@ margin-block-end: 0; } -.notification .foreground .image :is(picture, video), -.notification .foreground .image picture img { - inline-size: 100%; - display: flex; -} - .notification .foreground .text a:not(.con-button) { inline-size: auto; font-weight: normal; @@ -283,6 +296,7 @@ border-radius: var(--pill-radius); inline-size: calc(100% - var(--spacing-m)); margin-inline: auto; + box-shadow: var(--pill-shadow); } .notification.pill .foreground .action-area { @@ -298,6 +312,11 @@ .notification .flexible-inner { inline-size: 100%; + box-shadow: var(--pill-shadow); +} + +.notification .foreground > :is(.tablet-up, .desktop-up) { + display: none; } .notification.pill .foreground .text > :not(.action-area) { @@ -305,6 +324,10 @@ inline-size: calc(100% - var(--spacing-xxs)); } +.notification.pill .copy-wrap p:first-child:not(:only-child) { + margin-block-end: var(--spacing-xxs); +} + @media screen and (min-width: 600px) { .notification { --max-inline-size-image: 188px; @@ -329,6 +352,14 @@ gap: var(--spacing-s); } + .notification .foreground > :is(.mobile-up, .desktop-up) { + display: none; + } + + .notification .foreground > .tablet-up { + display: flex; + } + .notification:is(.max-width-12-desktop, .ribbon) .foreground { max-inline-size: var(--full-width); margin-inline: var(--grid-margins-width); @@ -418,6 +449,8 @@ .notification.pill.flexible { pointer-events: none; + box-shadow: none; + padding-block-end: var(--spacing-xxs); } .notification .flexible-inner { @@ -444,7 +477,8 @@ --max-inline-size-image-10: 300px; --inline-size-image-full: 33.333%; --max-inline-size-image-full: 400px; - --pill-radius: 36px; + --max-inline-size-pill: 1600px; + --pill-radius: 100px; } .notification:not(.pill, .ribbon) .foreground { @@ -472,6 +506,14 @@ max-inline-size: calc(var(--grid-column-width) * 10); } + .notification .foreground > :is(.mobile-up, .tablet-up) { + display: none; + } + + .notification .foreground > .desktop-up { + display: flex; + } + .notification .foreground > div { object-fit: cover; padding-inline-start: 0; @@ -511,6 +553,7 @@ .notification.pill { min-block-size: var(--min-block-size-pill); inline-size: var(--inline-size-pill); + max-inline-size: var(--max-inline-size-pill); margin-inline: auto; } @@ -525,7 +568,6 @@ } .notification.pill .foreground .text [class*="heading-"] { - margin-inline-end: var(--spacing-xxs); margin-block-end: 0; } @@ -549,7 +591,7 @@ } .notification.pill .foreground .icon-area { - margin-inline-end: var(--spacing-xs); + margin-inline-end: var(--spacing-s); margin-block-end: 0; } @@ -576,6 +618,7 @@ flex-wrap: wrap; align-items: baseline; text-align: start; + gap: var(--spacing-xxs); } .notification.pill .copy-wrap > * { @@ -585,4 +628,8 @@ .notification.ribbon.space-between .foreground .icon-area { margin-inline-end: var(--spacing-s); } + + .notification.pill .copy-wrap p:first-child:not(:only-child) { + margin-block-end: 0; + } } diff --git a/libs/blocks/notification/notification.js b/libs/blocks/notification/notification.js index 52787c2521..4a2cab725d 100644 --- a/libs/blocks/notification/notification.js +++ b/libs/blocks/notification/notification.js @@ -11,10 +11,10 @@ */ /* -* Notification - v1.1 +* Notification - v1.2 */ -import { decorateBlockText, decorateBlockBg, decorateTextOverrides } from '../../utils/decorate.js'; +import { decorateBlockText, decorateBlockBg, decorateTextOverrides, decorateMultiViewport } from '../../utils/decorate.js'; import { createTag, getConfig, loadStyle } from '../../utils/utils.js'; const { miloLibs, codeRoot } = getConfig(); @@ -54,6 +54,8 @@ const closeSvg = ` `; +let iconographyLoaded = false; + function getOpts(el) { const optRows = [...el.querySelectorAll(':scope > div:nth-of-type(n+3)')]; if (!optRows.length) return {}; @@ -73,13 +75,15 @@ function getBlockData(el) { } function wrapCopy(foreground) { - const text = foreground.querySelector('.text'); - if (!text) return; - const heading = text?.querySelector('h1, h2, h3, h4, h5, h6, p:not(.icon-area, .action-area)'); - const icon = heading?.previousElementSibling; - const body = heading?.nextElementSibling?.classList.contains('action-area') ? '' : heading?.nextElementSibling; - const copy = createTag('div', { class: 'copy-wrap' }, [heading, body].filter(Boolean)); - text?.insertBefore(copy, icon?.nextSibling || text.children[0]); + const texts = foreground.querySelectorAll('.text'); + if (!texts) return; + texts.forEach((text) => { + const heading = text?.querySelector('h1, h2, h3, h4, h5, h6, p:not(.icon-area, .action-area)'); + const icon = heading?.previousElementSibling; + const body = heading?.nextElementSibling?.classList.contains('action-area') ? '' : heading?.nextElementSibling; + const copy = createTag('div', { class: 'copy-wrap' }, [heading, body].filter(Boolean)); + text?.insertBefore(copy, icon?.nextSibling || text.children[0]); + }); } function decorateClose(el) { @@ -104,10 +108,11 @@ function decorateFlexible(el) { async function loadIconography() { await new Promise((resolve) => { loadStyle(`${base}/styles/iconography.css`, resolve); }); + iconographyLoaded = true; } async function decorateLockup(lockupArea, el) { - await loadIconography(); + if (!iconographyLoaded) await loadIconography(); const icon = lockupArea.querySelector('picture'); const content = icon.nextElementSibling || icon.nextSibling; const label = createTag('span', { class: 'lockup-label' }, content.nodeValue || content); @@ -122,21 +127,29 @@ async function decorateLockup(lockupArea, el) { if (pre && pre[2] === 'icon') el.classList.replace(pre[0], `${pre[1]}-lockup`); } +async function decorateForegroundText(el, container) { + const text = container?.querySelector('h1, h2, h3, h4, h5, h6, p')?.closest('div'); + text?.classList.add('text'); + const iconArea = text?.querySelector('p:has(picture)'); + iconArea?.classList.add('icon-area'); + if (iconArea?.textContent.trim()) await decorateLockup(iconArea, el); +} + async function decorateLayout(el) { const [background, ...rest] = el.querySelectorAll(':scope > div'); const foreground = rest.pop(); if (background) decorateBlockBg(el, background); foreground?.classList.add('foreground', 'container'); - const text = foreground?.querySelector('h1, h2, h3, h4, h5, h6, p')?.closest('div'); - text?.classList.add('text'); - const iconArea = text?.querySelector('p picture')?.closest('p'); - iconArea?.classList.add('icon-area'); - if (iconArea?.textContent.trim()) await decorateLockup(iconArea, el); + if (el.matches(`:is(.${pill}, .${ribbon})`)) { + foreground.querySelectorAll(':scope > div').forEach((div) => decorateForegroundText(el, div)); + } else { + await decorateForegroundText(el, foreground); + } const fgMedia = foreground?.querySelector(':scope > div:not(.text) :is(img, video, a[href*=".mp4"])')?.closest('div'); const bgMedia = el.querySelector(':scope > div:not(.foreground) :is(img, video, a[href*=".mp4"])')?.closest('div'); const media = fgMedia ?? bgMedia; media?.classList.toggle('image', media && !media.classList.contains('text')); - foreground?.classList.toggle('no-image', !media && !iconArea); + foreground?.classList.toggle('no-image', !media && !el.querySelector('.icon-area')); if (el.matches(`:is(.${pill}, .${ribbon}):not(.no-closure)`)) decorateClose(el); if (el.matches(`.${pill}.flexible`)) decorateFlexible(el); return foreground; @@ -152,5 +165,8 @@ export default async function init(el) { } decorateTextOverrides(el); el.querySelectorAll('a:not([class])').forEach((staticLink) => staticLink.classList.add('static')); - if (el.matches(`:is(.${ribbon}, .${pill})`)) wrapCopy(blockText); + if (el.matches(`:is(.${ribbon}, .${pill})`)) { + wrapCopy(blockText); + decorateMultiViewport(el); + } } diff --git a/libs/blocks/section-metadata/section-metadata.css b/libs/blocks/section-metadata/section-metadata.css index 82d18fbb26..d053a42980 100644 --- a/libs/blocks/section-metadata/section-metadata.css +++ b/libs/blocks/section-metadata/section-metadata.css @@ -192,6 +192,10 @@ background-color: var(--color-white); } +.section.sticky-top:has(.notification.pill) { + height: 0; +} + .section.sticky-bottom { position: sticky; bottom: 0; diff --git a/libs/blocks/section-metadata/sticky-section.js b/libs/blocks/section-metadata/sticky-section.js index 7308042421..db2a757554 100644 --- a/libs/blocks/section-metadata/sticky-section.js +++ b/libs/blocks/section-metadata/sticky-section.js @@ -27,10 +27,10 @@ function promoIntersectObserve(el, stickySectionEl, options = {}) { function handleStickyPromobar(section, delay) { const main = document.querySelector('main'); section.classList.add('promo-sticky-section', 'hide-sticky-section'); - if (section.querySelector('.promobar.popup')) section.classList.add('popup'); + if (section.querySelector('.popup:is(.promobar, .notification.pill)')) section.classList.add('popup'); let stickySectionEl = null; let hasScrollControl; - if ((section.querySelector('.promobar').classList.contains('no-delay')) || (delay && section.classList.contains('popup'))) { + if ((section.querySelector(':is(.promobar, .notification.pill)').classList.contains('no-delay')) || (delay && section.classList.contains('popup'))) { hasScrollControl = true; } if (!hasScrollControl && main.children[0] !== section) { @@ -52,7 +52,7 @@ export default async function handleStickySection(sticky, section) { break; } case 'sticky-bottom': { - if (section.querySelector('.promobar')) { + if (section.querySelector(':is(.promobar, .notification.pill.popup)')) { const metadata = getMetadata(section.querySelector('.section-metadata')); const delay = getDelayTime(metadata.delay?.text); if (delay) setTimeout(() => { handleStickyPromobar(section, delay); }, delay); diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index ab3363489f..25a0e8203c 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -278,3 +278,23 @@ export function applyInViewPortPlay(video) { observer.observe(video); } } + +export function decorateMultiViewport(el) { + const viewports = [ + '(max-width: 599px)', + '(min-width: 600px) and (max-width: 1199px)', + '(min-width: 1200px)', + ]; + const foreground = el.querySelector('.foreground'); + if (foreground.childElementCount === 2 || foreground.childElementCount === 3) { + [...foreground.children].forEach((child, index) => { + const mq = window.matchMedia(viewports[index]); + const setContent = () => { + if (mq.matches) foreground.replaceChildren(child); + }; + setContent(); + mq.addEventListener('change', setContent); + }); + } + return foreground; +} diff --git a/test/blocks/notification/mocks/body.html b/test/blocks/notification/mocks/body.html index 8dedf20078..9a13c85a93 100644 --- a/test/blocks/notification/mocks/body.html +++ b/test/blocks/notification/mocks/body.html @@ -449,4 +449,51 @@

Default heading M 24/30

+ + +
From 6362c19f2721eb04f517bba2f8882a4616462d7b Mon Sep 17 00:00:00 2001 From: Denys Fedotov Date: Mon, 2 Sep 2024 02:17:03 -0600 Subject: [PATCH 03/66] [MWPW-155566] [MEP] Allow relevant actions in MEP to use text and HTML instead of just fragments (#2683) * getSelectedElement - V2 * getSelectedElement - V2 * getSelectedElement - V2 * getSelectedElement - V2 * getSelectedElement - V2 * added test coverage for action * cleanup * added comments * comments * comments * fixes erroring but still has failure * rooEl fix * fixed tests * added modifier * eslint * lint * removed reduncant try catch * Add target analytics for update action * removed test-results and added to .gitignore * reverted a deletion of a prependtosection validation code * removed comments * updated validation for appendtosdection * removed comments and eslint escapes * change modifier to an array that is not a separate column * add highight for update * add timeout to merch-card highlight * remove setTimeout and move preview.js call from loadPostLCP to loadDeferred * update unit test * remove update to git ignore * add temp console message for easy QA * MWPW-155566 [MEP] Changing from a new action to just expanding power of existing actions (#2764) * stash * use replace instead of update, allow html for create commands, update simplified selectors * streamline * remove export on MILO_BLOCKS * change prependToSection and appendToSection to just prepend and append * remove unused function param * change from includes to === * remove use of .parentElement * move spoof param test so you can reload the other tests * account for number used after dot notation * add test to insertbefore and insert after href * more coverage * more coverage * hopefully final coverage * remove console * rename checkSelectorType to getSelectorType * move parts of createContent into sub functions * small big fix * update modifySelectorTerm function per suggestion * update modifyNonFragmentSelector per suggestion * update to getSelectedElement * change equal to not equal * fix end number update * codecov * no longer require fragments be in URL for fragments because of gnav * call getModifiers in modifyNonFragmentSelector instead * require space before first flag * update unit tests to need space before flags * handle adding flags to fragment selectors * revert for demo * put back * remove utils update so it does not conflict --------- Co-authored-by: Denys Fedotov Co-authored-by: vgoodric Co-authored-by: markpadbe Co-authored-by: Vivian A Goodrich <101133187+vgoodric@users.noreply.github.com> --- .../personalization/personalization.js | 300 ++++++++++-------- libs/features/personalization/preview.js | 1 + test/features/personalization/actions.test.js | 36 ++- .../actionsTargetManifestId.test.js | 24 ++ .../mocks/actions/manifestInsertAfter.json | 22 ++ .../mocks/actions/manifestInsertBefore.json | 11 + .../mocks/actions/manifestTargetUpdate.json | 65 ++++ .../mocks/actions/manifestUpdate.json | 74 +++++ .../mocks/manifestSectionBlock.json | 11 +- .../mocks/personalization.html | 14 +- .../modifyNonFragmentSelector.test.js | 119 +++++++ .../personalization-params.test.js | 50 +++ .../personalization/personalization.test.js | 43 +-- 13 files changed, 602 insertions(+), 168 deletions(-) create mode 100644 test/features/personalization/mocks/actions/manifestTargetUpdate.json create mode 100644 test/features/personalization/mocks/actions/manifestUpdate.json create mode 100644 test/features/personalization/modifyNonFragmentSelector.test.js create mode 100644 test/features/personalization/personalization-params.test.js diff --git a/libs/features/personalization/personalization.js b/libs/features/personalization/personalization.js index 4586cc158b..df0f282ce8 100644 --- a/libs/features/personalization/personalization.js +++ b/libs/features/personalization/personalization.js @@ -93,46 +93,90 @@ const isInLcpSection = (el) => { return lcpSection === el || lcpSection?.contains(el); }; -export const createFrag = (el, url, manifestId, targetManifestId) => { - let href = url; +const GLOBAL_CMDS = [ + 'insertscript', + 'replacepage', + 'updatemetadata', + 'useblockcode', +]; + +const CREATE_CMDS = { + insertafter: 'afterend', + insertbefore: 'beforebegin', + prepend: 'afterbegin', + append: 'beforeend', +}; + +const COMMANDS_KEYS = { + remove: 'remove', + replace: 'replace', +}; + +function addIds(el, manifestId, targetManifestId) { + if (manifestId) el.dataset.manifestId = manifestId; + if (targetManifestId) el.dataset.adobeTargetTestid = targetManifestId; +} + +function getSelectorType(selector) { + const sel = selector.toLowerCase().trim(); + if (sel.startsWith('/') || sel.startsWith('http')) return 'fragment'; + return 'other'; +} + +const getUpdatedHref = (el, content, action) => { + const href = el.getAttribute('href'); + if (action === 'insertafter' || action === 'append') return `${href}${content}`; + if (action === 'insertbefore' || action === 'prepend') return `${content}${href}`; + return content; +}; + +const createFrag = (el, action, content, manifestId, targetManifestId) => { + if (action === 'replace') el.classList.add(CLASS_EL_DELETE, CLASS_EL_REPLACE); + let href = content; try { - const { pathname, search, hash } = new URL(url); + const { pathname, search, hash } = new URL(content); href = `${pathname}${search}${hash}`; } catch { // ignore } - const a = createTag('a', { href }, url); - if (manifestId) a.dataset.manifestId = manifestId; - if (targetManifestId) a.dataset.adobeTargetTestid = targetManifestId; - let frag = createTag('p', undefined, a); + const a = createTag('a', { href }, content); + addIds(a, manifestId, targetManifestId); + const frag = createTag('p', undefined, a); const isDelayedModalAnchor = /#.*delay=/.test(href); if (isDelayedModalAnchor) frag.classList.add('hide-block'); - const isSection = el.parentElement.nodeName === 'MAIN'; - if (isSection) { - frag = createTag('div', undefined, frag); - } if (isInLcpSection(el)) { loadLink(`${localizeLink(a.href)}.plain.html`, { as: 'fetch', crossorigin: 'anonymous', rel: 'preload' }); } return frag; }; -const GLOBAL_CMDS = [ - 'insertscript', - 'replacepage', - 'updatemetadata', - 'useblockcode', -]; +export const createContent = (el, content, manifestId, targetManifestId, action, modifiers) => { + if (action === 'replace') { + addIds(el, manifestId, targetManifestId); + } + if (el?.nodeName === 'A' && modifiers?.includes('href')) { + el.setAttribute('href', getUpdatedHref(el, content, action)); + addIds(el, manifestId, targetManifestId); + return el; + } + if (getSelectorType(content) !== 'fragment') { + if (action === 'replace') { + el.innerHTML = content; + return el; + } + const container = createTag('div', {}, content); + addIds(container, manifestId, targetManifestId); + return container; + } -const CREATE_CMDS = { - insertafter: 'afterend', - insertbefore: 'beforebegin', - prependtosection: 'afterbegin', - appendtosection: 'beforeend', + const frag = createFrag(el, action, content, manifestId, targetManifestId); + addIds(frag, manifestId, targetManifestId); + if (el?.parentElement.nodeName !== 'MAIN') return frag; + return createTag('div', undefined, frag); }; const COMMANDS = { - remove: (el, target, manifestId) => { + [COMMANDS_KEYS.remove]: ({ el, target, manifestId }) => { if (target === 'false') return; if (manifestId) { el.dataset.removedManifestId = manifestId; @@ -140,17 +184,15 @@ const COMMANDS = { } el.classList.add(CLASS_EL_DELETE); }, - replace: (el, target, manifestId, targetManifestId) => { + [COMMANDS_KEYS.replace]: ({ el, target, modifiers, manifestId, targetManifestId }) => { if (!el || el.classList.contains(CLASS_EL_REPLACE)) return; - el.insertAdjacentElement('beforebegin', createFrag(el, target, manifestId, targetManifestId)); - el.classList.add(CLASS_EL_DELETE, CLASS_EL_REPLACE); + el.insertAdjacentElement( + 'beforebegin', + createContent(el, target, manifestId, targetManifestId, 'replace', modifiers), + ); }, }; -function checkSelectorType(selector) { - return selector?.startsWith('/') || selector?.startsWith('http') ? 'fragment' : 'css'; -} - const fetchData = async (url, type = DATA_TYPE.JSON) => { try { const resp = await fetch(normalizePath(url)); @@ -240,8 +282,7 @@ const setMetadata = (metadata) => { const propName = selector.startsWith('og:') ? 'property' : 'name'; let metaEl = document.querySelector(`meta[${propName}="${selector}"]`); if (!metaEl) { - metaEl = document.createElement('meta'); - metaEl.setAttribute(propName, selector); + metaEl = createTag('meta', { [propName]: selector }); document.head.append(metaEl); } metaEl.setAttribute('content', val); @@ -259,13 +300,6 @@ function normalizeKeys(obj) { }, {}); } -const getDivInTargetedCell = (tableCell) => { - tableCell.replaceChildren(); - const div = document.createElement('div'); - tableCell.appendChild(div); - return div; -}; - const querySelector = (el, selector, all = false) => { try { return all ? el.querySelectorAll(selector) : el.querySelector(selector); @@ -275,18 +309,6 @@ const querySelector = (el, selector, all = false) => { return null; } }; - -function getTrailingNumber(s) { - const match = s.match(/\d+$/); - return match ? parseInt(match[0], 10) : null; -} - -function getSection(rootEl, idx) { - return rootEl === document - ? document.querySelector(`body > main > div:nth-child(${idx})`) - : rootEl.querySelector(`:scope > div:nth-child(${idx})`); -} - function registerInBlockActions(cmd, manifestId, targetManifestId) { const { action, target, selector } = cmd; const command = { action, target, manifestId, targetManifestId }; @@ -302,7 +324,7 @@ function registerInBlockActions(cmd, manifestId, targetManifestId) { if (blockAndSelector.length > 1) { blockSelector = blockAndSelector.slice(1).join(' '); command.selector = blockSelector; - if (checkSelectorType(blockSelector) === 'fragment') { + if (getSelectorType(blockSelector) === 'fragment') { config.mep.inBlock[blockName].fragments ??= {}; const { fragments } = config.mep.inBlock[blockName]; delete command.selector; @@ -327,70 +349,88 @@ function registerInBlockActions(cmd, manifestId, targetManifestId) { config.mep.inBlock[blockName].commands.push(command); } -function getSelectedElement(selector, action, rootEl) { - if (!selector) return null; - if ((action.includes('appendtosection') || action.includes('prependtosection'))) { - if (!selector.includes('section')) return null; - const section = selector.trim().replace('section', ''); - if (section !== '' && Number.isNaN(section)) return null; +const updateEndNumber = (endNumber, term) => (endNumber + ? term.replace(endNumber, `:nth-child(${endNumber})`) + : term); +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)', + }; + const otherSelectors = ['row', 'col']; + const htmlEls = ['main', 'div', 'a', 'p', 'strong', 'em', 'picture', 'source', 'img', 'h']; + const startTextMatch = term.match(/^[a-zA-Z/./-]*/); + const startText = startTextMatch ? startTextMatch[0].toLowerCase() : ''; + const endNumberMatch = term.match(/[0-9]*$/); + const endNumber = endNumberMatch ? 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 (checkSelectorType(selector) === 'fragment') { - try { - const fragment = document.querySelector(`a[href*="${normalizePath(selector, false)}"], a[href*="${normalizePath(selector, true)}"]`); - if (fragment) return fragment.parentNode; - return null; - } catch (e) { - return null; - } + if (Object.keys(specificSelectors).includes(startText)) { + term = term.replace(startText, specificSelectors[startText]); + term = updateEndNumber(endNumber, term); + return term; } - let selectedEl; - if (selector.includes('.') || !['section', 'block', 'row'].some((s) => selector.includes(s))) { - selectedEl = querySelector(rootEl, selector); - if (selectedEl) return selectedEl; + if (!startText.startsWith('.')) term = `.${term}`; + if (endNumber) { + term = term.replace(endNumber, ''); + term = `${term}:nth-child(${endNumber} of ${term})`; } - - const terms = selector.split(/\s+/); - - let section = null; - if (terms[0]?.startsWith('section')) { - const sectionIdx = getTrailingNumber(terms[0]) || 1; - section = getSection(rootEl, sectionIdx); - terms.shift(); + return term; +} +function getModifiers(selector) { + let sel = selector; + const modifiers = []; + const flags = sel.split(/\s+#_/); + if (flags.length) { + sel = flags.shift(); + flags.forEach((flag) => { + flag.split(/_|#_/).forEach((mod) => modifiers.push(mod.toLowerCase().trim())); + }); } + return { sel, modifiers }; +} +export function modifyNonFragmentSelector(selector) { + const { sel, modifiers } = getModifiers(selector); + return { + modifiedSelector: sel + .split('>').join(' > ') + .split(',').join(' , ') + .replaceAll(/main\s*>?\s*(section\d*)/gi, '$1') + .split(/\s+/) + .map(modifySelectorTerm) + .join(' ') + .trim(), + modifiers, + }; +} - if (terms.length) { - const blockStr = terms.shift(); - if (blockStr.includes('.')) { - selectedEl = querySelector(section || rootEl, blockStr); - } else { - const blockIndex = getTrailingNumber(blockStr) || 1; - const blockName = blockStr.replace(`${blockIndex}`, ''); - if (blockName === 'block') { - if (!section) section = getSection(rootEl, 1); - selectedEl = querySelector(section, `:scope > div:nth-of-type(${blockIndex})`); - } else { - selectedEl = querySelector(section || rootEl, `.${blockName}`, true)?.[blockIndex - 1]; - } - } - } else if (section) { - return section; - } - - if (terms.length) { - // find targeted table cell in rowX colY format - const rowColMatch = /row(?\d+)\s+col(?\d+)/gm.exec(terms.join(' ')); - if (rowColMatch) { - const { row, col } = rowColMatch.groups; - const tableCell = querySelector(selectedEl, `:nth-child(${row}) > :nth-child(${col})`); - if (!tableCell) return null; - return getDivInTargetedCell(tableCell); +function getSelectedElement({ selector: sel, rootEl }) { + const selector = sel.trim(); + if (!selector) return {}; + + if (getSelectorType(selector) === 'fragment') { + try { + const fragment = document.querySelector( + `a[href*="${normalizePath(selector, false)}"], a[href*="${normalizePath(selector, true)}"]`, + ); + if (fragment) return { el: fragment.parentNode }; + return {}; + } catch (e) { + /* c8 ignore next */ + return {}; } } - - return selectedEl; + const { modifiedSelector, modifiers } = modifyNonFragmentSelector(selector); + return { el: querySelector(rootEl || document, modifiedSelector), modifiers }; } - const addHash = (url, newHash) => { if (!newHash) return url; try { @@ -413,10 +453,10 @@ export const updateFragDataProps = (a, inline, sections, fragment) => { if (manifestId) setDataIdOnChildren(sections, 'manifestId', manifestId); if (adobeTargetTestid) setDataIdOnChildren(sections, 'adobeTargetTestid', adobeTargetTestid); } else { - if (manifestId) fragment.dataset.manifestId = manifestId; - if (adobeTargetTestid) fragment.dataset.adobeTargetTestid = adobeTargetTestid; + addIds(fragment, manifestId, adobeTargetTestid); } }; + export function handleCommands(commands, rootEl = document, forceInline = false) { commands.forEach((cmd) => { const { manifestId, targetManifestId, action, selector, target: trgt } = cmd; @@ -425,20 +465,20 @@ export function handleCommands(commands, rootEl = document, forceInline = false) registerInBlockActions(cmd, manifestId, targetManifestId); return; } + const { el, modifiers } = getSelectedElement({ selector, rootEl }); + + if (!el || (!(action in COMMANDS) && !(action in CREATE_CMDS))) return; if (action in COMMANDS) { - const el = getSelectedElement(selector, action, rootEl); - COMMANDS[action](el, target, manifestId, targetManifestId); - } else if (action in CREATE_CMDS) { - const el = getSelectedElement(selector, action, rootEl); - el?.insertAdjacentElement( - CREATE_CMDS[action], - createFrag(el, target, manifestId, targetManifestId), - ); - } else { - /* c8 ignore next 2 */ - console.log('Invalid command found: ', cmd); + COMMANDS[action]({ + el, target, manifestId, targetManifestId, action, modifiers, + }); + return; } + el?.insertAdjacentElement( + CREATE_CMDS[action], + createContent(el, target, manifestId, targetManifestId, action, modifiers), + ); }); } @@ -448,9 +488,11 @@ const getVariantInfo = (line, variantNames, variants, manifestPath, fTargetId) = let targetId = manifestId.replace('.json', ''); if (fTargetId) targetId = fTargetId; if (!config.mep?.preview) manifestId = false; - const action = line.action?.toLowerCase().replace('content', '').replace('fragment', ''); - const { selector } = line; + // retro support + const action = line.action?.toLowerCase() + .replace('content', '').replace('fragment', '').replace('tosection', ''); const pageFilter = line['page filter'] || line['page filter optional']; + const { selector } = line; if (pageFilter && !matchGlob(pageFilter, new URL(window.location).pathname)) return; @@ -465,14 +507,14 @@ const getVariantInfo = (line, variantNames, variants, manifestPath, fTargetId) = selector, pageFilter, target: line[vn], - selectorType: checkSelectorType(selector), + selectorType: getSelectorType(selector), manifestId, targetManifestId, }; if (action in COMMANDS && variantInfo.selectorType === 'fragment') { variants[vn].fragments.push({ - selector: normalizePath(variantInfo.selector), + selector: normalizePath(variantInfo.selector.split(' #_')[0]), val: normalizePath(line[vn]), action, manifestId, @@ -842,13 +884,12 @@ export function cleanAndSortManifestList(manifests) { export function handleFragmentCommand(command, a) { const { action, fragment, manifestId, targetManifestId } = command; - if (action === 'replace') { + if (action === COMMANDS_KEYS.replace) { a.href = fragment; - if (manifestId) a.dataset.manifestId = manifestId; - if (targetManifestId) a.dataset.adobeTargetTestid = targetManifestId; + addIds(a, manifestId, targetManifestId); return fragment; } - if (action === 'remove') { + if (action === COMMANDS_KEYS.remove) { if (manifestId) { a.parentElement.dataset.removedManifestId = manifestId; } else { @@ -885,8 +926,7 @@ export async function applyPers(manifests, postLCP = false) { if (config.mep.replacepage && !postLCP && main) { await replaceInner(config.mep.replacepage.val, main); const { manifestId, targetManifestId } = config.mep.replacepage; - if (manifestId) main.dataset.manifestId = manifestId; - if (targetManifestId) main.dataset.adobeTargetTestid = targetManifestId; + addIds(main, manifestId, targetManifestId); } if (!postLCP) handleCommands(config.mep.commands); diff --git a/libs/features/personalization/preview.js b/libs/features/personalization/preview.js index f88c0d4889..5c1d662324 100644 --- a/libs/features/personalization/preview.js +++ b/libs/features/personalization/preview.js @@ -290,6 +290,7 @@ function addHighlightData(manifests) { selectedVariant?.updatemetadata?.forEach(({ selector }) => { if (selector === 'gnav-source') updateManifestId('header, footer'); }); + // eslint-disable-next-line max-len document.querySelectorAll(`.section[class*="merch-cards"] .fragment[data-manifest-id="${manifestName}"] merch-card`) .forEach((el) => (el.dataset.manifestId = manifestName)); diff --git a/test/features/personalization/actions.test.js b/test/features/personalization/actions.test.js index fe9e935202..19057fd2eb 100644 --- a/test/features/personalization/actions.test.js +++ b/test/features/personalization/actions.test.js @@ -86,6 +86,8 @@ describe('insertAfter action', async () => { expect(document.querySelector('a[href="/fragments/insertafter"]')).to.be.null; expect(document.querySelector('a[href="/fragments/insertafterfragment"]')).to.be.null; + expect(document.querySelector('#insertafter').getAttribute('href')).to.equal('/my-page.html'); + expect(document.querySelector('#inserted-html')).to.be.null; await init(mepSettings); expect(getConfig().mep.commands[0].targetManifestId).to.equal(false); @@ -98,6 +100,8 @@ describe('insertAfter action', async () => { expect(fragment).to.not.be.null; expect(fragment.parentElement.previousElementSibling.querySelector('a[href="/fragments/insertaround"]')).to.exist; + expect(document.querySelector('#insertafter').getAttribute('href')).to.equal('/my-page.html#modal'); + expect(document.querySelector('#inserted-html')).to.not.be.null; }); }); @@ -109,6 +113,7 @@ describe('insertBefore action', async () => { setFetchResponse(manifestJson); expect(document.querySelector('a[href="/fragments/insertbefore"]')).to.be.null; + expect(document.querySelector('#insertbefore').getAttribute('href')).to.equal('/my-page.html'); await init(mepSettings); expect(getConfig().mep.commands[0].targetManifestId).to.equal(false); @@ -121,11 +126,12 @@ describe('insertBefore action', async () => { expect(fragment).to.not.be.null; expect(fragment.parentElement.nextElementSibling.querySelector('a[href="/fragments/insertaround"]')).to.exist; + expect(document.querySelector('#insertbefore').getAttribute('href')).to.equal('/de/my-page.html'); }); }); describe('prependToSection action', async () => { - it('appendToSection should add fragment to beginning of section', async () => { + it('prependToSection should add fragment to beginning of section', async () => { let manifestJson = await readFile({ path: './mocks/actions/manifestPrependToSection.json' }); manifestJson = JSON.parse(manifestJson); @@ -157,6 +163,34 @@ describe('appendToSection action', async () => { }); }); +describe('replace action with html/text instead of fragment', () => { + it('should replace marquee content', async () => { + document.body.innerHTML = await readFile({ path: './mocks/personalization.html' }); + let manifestJson = await readFile({ path: './mocks/actions/manifestUpdate.json' }); + manifestJson = JSON.parse(manifestJson); + setFetchResponse(manifestJson); + + const primaryCTA = document.querySelector('.marquee p strong a'); + const secondaryCTA = document.querySelector('.marquee p a'); + const header = document.querySelector('.marquee h2'); + const actionArea = document.querySelector('main div:nth-child(5) .marquee p:has(em a, strong a)'); + + expect(header.innerText).to.not.equal('updated text'); + expect(primaryCTA.innerText).to.not.equal('updated text'); + expect(primaryCTA.href).to.not.equal('updated text'); + expect(secondaryCTA.innerText).to.not.equal('updated text'); + expect(actionArea.innerHTML).to.not.equal('

updated text

'); + + await init(mepSettings); + + expect(header.innerText).to.equal('updated text'); + expect(primaryCTA.innerText).to.equal('updated text'); + expect(primaryCTA.href).to.equal('https://test.com/updated_href'); + expect(secondaryCTA.innerText).to.equal('updated text'); + expect(actionArea.innerHTML).to.equal('

updated text

'); + }); +}); + describe('remove action', () => { before(async () => { let manifestJson = await readFile({ path: './mocks/actions/manifestRemove.json' }); diff --git a/test/features/personalization/actionsTargetManifestId.test.js b/test/features/personalization/actionsTargetManifestId.test.js index 7345cabb8b..9f649f2e58 100644 --- a/test/features/personalization/actionsTargetManifestId.test.js +++ b/test/features/personalization/actionsTargetManifestId.test.js @@ -123,6 +123,30 @@ describe('appendToSection action', async () => { }); }); +describe('replace action with html/text instead of fragment', () => { + it('should replace marquee content', async () => { + document.body.innerHTML = await readFile({ path: './mocks/personalization.html' }); + let manifestJson = await readFile({ path: './mocks/actions/manifestTargetUpdate.json' }); + manifestJson = JSON.parse(manifestJson); + setFetchResponse(manifestJson); + + const primaryCTA = document.querySelector('.marquee p strong a'); + const secondaryCTA = document.querySelector('.marquee p a'); + const header = document.querySelector('.marquee h2'); + const actionArea = document.querySelector('main div:nth-child(5) .marquee p:has(em a, strong a)'); + + expect(header.dataset.adobeTargetTestid).not.to.exist; + expect(primaryCTA.dataset.adobeTargetTestid).not.to.exist; + expect(secondaryCTA.dataset.adobeTargetTestid).not.to.exist; + expect(actionArea.dataset.adobeTargetTestid).not.to.exist; + await init(mepSettings); + expect(header.dataset.adobeTargetTestid).to.equal('manifest'); + expect(primaryCTA.dataset.adobeTargetTestid).to.equal('manifest'); + expect(secondaryCTA.dataset.adobeTargetTestid).to.equal('manifest'); + expect(actionArea.dataset.adobeTargetTestid).to.equal('manifest'); + }); +}); + describe('useBlockCode action', async () => { it('useBlockCode should override a current block with the custom block code provided', async () => { let manifestJson = await readFile({ path: './mocks/actions/manifestUseBlockCode.json' }); diff --git a/test/features/personalization/mocks/actions/manifestInsertAfter.json b/test/features/personalization/mocks/actions/manifestInsertAfter.json index 0e40395cf9..09f24811c8 100644 --- a/test/features/personalization/mocks/actions/manifestInsertAfter.json +++ b/test/features/personalization/mocks/actions/manifestInsertAfter.json @@ -35,6 +35,28 @@ "firefox": "", "android": "", "ios": "" + }, + { + "action": "insertAfter", + "selector": "#insertafter", + "page filter (optional)": "", + "param-newoffer=123": "", + "chrome": "
Hello World
", + "target-var1": "/test/features/personalization/mocks/fragments/insertafter", + "firefox": "", + "android": "", + "ios": "" + }, + { + "action": "insertAfter", + "selector": "#insertafter #_href", + "page filter (optional)": "", + "param-newoffer=123": "", + "chrome": "#modal", + "target-var1": "", + "firefox": "", + "android": "", + "ios": "" } ], ":type": "sheet" diff --git a/test/features/personalization/mocks/actions/manifestInsertBefore.json b/test/features/personalization/mocks/actions/manifestInsertBefore.json index 057cfaacf4..dd0e845941 100644 --- a/test/features/personalization/mocks/actions/manifestInsertBefore.json +++ b/test/features/personalization/mocks/actions/manifestInsertBefore.json @@ -24,6 +24,17 @@ "firefox": "", "android": "", "ios": "" + }, + { + "action": "insertBefore", + "selector": "#insertbefore #_href", + "page filter (optional)": "", + "param-newoffer=123": "", + "chrome": "/de", + "target-var1": "", + "firefox": "", + "android": "", + "ios": "" } ], ":type": "sheet" diff --git a/test/features/personalization/mocks/actions/manifestTargetUpdate.json b/test/features/personalization/mocks/actions/manifestTargetUpdate.json new file mode 100644 index 0000000000..6e2d97793b --- /dev/null +++ b/test/features/personalization/mocks/actions/manifestTargetUpdate.json @@ -0,0 +1,65 @@ +{ + "total": 5, + "offset": 0, + "limit": 5, + "data": [ + { + "action": "replace", + "selector": ".marquee h2", + "page filter (optional)": "", + "param-newoffer=123": "", + "chrome": "", + "target-var1": "updated text", + "firefox": "", + "android": "", + "ios": "" + }, + { + "action": "replace", + "selector": "marquee primary-cta", + "page filter (optional)": "", + "param-newoffer=123": "", + "chrome": "", + "target-var1": "updated text", + "firefox": "", + "android": "", + "ios": "" + }, + { + "action": "replace", + "selector": ".marquee primary-cta", + "page filter (optional)": "", + "param-newoffer=123": "", + "chrome": "", + "'modifier": "href", + "target-var1": "https://test.com/updated_href", + "firefox": "", + "android": "", + "ios": "" + }, + { + "action": "replace", + "selector": "marquee secondary-cta", + "page filter (optional)": "", + "param-newoffer=123": "", + "chrome": "", + "target-var1": "updated text", + "firefox": "", + "android": "", + "ios": "" + }, + { + "action": "replace", + "selector": "section5 marquee action-area", + "'modifier": "html", + "page filter (optional)": "", + "param-newoffer=123": "", + "chrome": "", + "target-var1": "

updated text

", + "firefox": "", + "android": "", + "ios": "" + } + ], + ":type": "sheet" +} diff --git a/test/features/personalization/mocks/actions/manifestUpdate.json b/test/features/personalization/mocks/actions/manifestUpdate.json new file mode 100644 index 0000000000..261ef3649b --- /dev/null +++ b/test/features/personalization/mocks/actions/manifestUpdate.json @@ -0,0 +1,74 @@ +{ + "total": 5, + "offset": 0, + "limit": 5, + "data": [ + { + "action": "replace", + "selector": ".marquee h2", + "page filter (optional)": "", + "param-newoffer=123": "", + "chrome": "updated text", + "target-var1": "", + "firefox": "", + "android": "", + "ios": "" + }, + { + "action": "replace", + "selector": "marquee primary-cta", + "page filter (optional)": "", + "param-newoffer=123": "", + "chrome": "updated text", + "target-var1": "", + "firefox": "", + "android": "", + "ios": "" + }, + { + "action": "replace", + "selector": ".marquee primary-cta #_href", + "page filter (optional)": "", + "param-newoffer=123": "", + "chrome": "https://test.com/updated_href", + "target-var1": "", + "firefox": "", + "android": "", + "ios": "" + }, + { + "action": "replace", + "selector": "marquee secondary-cta", + "page filter (optional)": "", + "param-newoffer=123": "", + "chrome": "updated text", + "target-var1": "", + "firefox": "", + "android": "", + "ios": "" + }, + { + "action": "replace", + "selector": "section5 marquee action-area", + "page filter (optional)": "", + "param-newoffer=123": "", + "chrome": "

updated text

", + "target-var1": "", + "firefox": "", + "android": "", + "ios": "" + }, + { + "action": "replace", + "selector": "section5...%$@#", + "page filter (optional)": "", + "param-newoffer=123": "", + "chrome": "

updated text

", + "target-var1": "", + "firefox": "", + "android": "", + "ios": "" + } + ], + ":type": "sheet" +} diff --git a/test/features/personalization/mocks/manifestSectionBlock.json b/test/features/personalization/mocks/manifestSectionBlock.json index 77088f07f5..1a5809b040 100644 --- a/test/features/personalization/mocks/manifestSectionBlock.json +++ b/test/features/personalization/mocks/manifestSectionBlock.json @@ -5,21 +5,14 @@ "data": [ { "action": "removeContent", - "selector": "section2 block2", + "selector": ".custom-block1", "page filter (optional)": "", "param-newoffer=123": "", "all": "on" }, { "action": "removeContent", - "selector": "custom-block2", - "page filter (optional)": "", - "param-newoffer=123": "", - "all": "on" - }, - { - "action": "removeContent", - "selector": "section5 custom-block", + "selector": "section2 .custom-block", "page filter (optional)": "", "param-newoffer=123": "", "all": "on" diff --git a/test/features/personalization/mocks/personalization.html b/test/features/personalization/mocks/personalization.html index f2ed9f2cb9..d9aac62f61 100644 --- a/test/features/personalization/mocks/personalization.html +++ b/test/features/personalization/mocks/personalization.html @@ -14,8 +14,14 @@

Milo Experimentation Platform

Leverage the Milo Experimentation Platform (MEP) for all your personalization needs on Milo!

-

Review - Docs

+

+ + Review Docs + + + Secondary CTA + +

@@ -168,6 +174,10 @@

The second marquee

Custom block 4
+ + + +
diff --git a/test/features/personalization/modifyNonFragmentSelector.test.js b/test/features/personalization/modifyNonFragmentSelector.test.js new file mode 100644 index 0000000000..56db63e300 --- /dev/null +++ b/test/features/personalization/modifyNonFragmentSelector.test.js @@ -0,0 +1,119 @@ +import { expect } from '@esm-bundle/chai'; +import { modifyNonFragmentSelector } from '../../../libs/features/personalization/personalization.js'; + +const values = [ + { + b: 'main section1 marquee action-area', + a: 'main > div:nth-child(1) .marquee p:has(em a, strong a)', + }, + { + b: 'main > section1 .marquee h2', + a: 'main > div:nth-child(1) .marquee h2', + }, + { + b: '#some-random-id, section1 marquee row2 col1 p:nth-of-type(2)', + a: '#some-random-id , main > div:nth-child(1) .marquee > div:nth-child(2) > div:nth-child(1) p:nth-of-type(2)', + }, + { + b: 'marquee.light:nth-child(2) h2', + a: '.marquee.light:nth-child(2) h2', + }, + { + b: 'section2 text3', + a: 'main > div:nth-child(2) .text:nth-child(3 of .text)', + }, + { + b: 'section2 .text.light strong', + a: 'main > div:nth-child(2) .text.light strong', + }, + { + b: 'section3 table row2 col2 primary-cta', + a: 'main > div:nth-child(3) .table > div:nth-child(2) > div:nth-child(2) p strong a', + }, + { + b: 'marquee primary-cta #_href', + a: '.marquee p strong a', + m: ['href'], + }, + { + b: 'marquee primary-cta #_HREF', + a: '.marquee p strong a', + m: ['href'], + }, + { + b: 'marquee primary-cta#_href', + a: '.marquee p strong a#_href', + }, + { + b: 'marquee primary-cta #_href_all', + a: '.marquee p strong a', + m: ['href', 'all'], + }, + { + b: 'marquee primary-cta #_href#_all', + a: '.marquee p strong a', + m: ['href', 'all'], + }, + { + b: 'marquee primary-cta #_href #_all', + a: '.marquee p strong a', + m: ['href', 'all'], + }, + { + b: 'section3 table row5 col2', + a: 'main > div:nth-child(3) .table > div:nth-child(5) > div:nth-child(2)', + }, + { + b: 'section3 table row2 col4 secondary-cta', + a: 'main > div:nth-child(3) .table > div:nth-child(2) > div:nth-child(4) p em a', + }, + { + b: 'section4 merch-card', + a: 'main > div:nth-child(4) .merch-card', + }, + { + b: 'section5 tabs col2 row2', + a: 'main > div:nth-child(5) .tabs > div:nth-child(2) > div:nth-child(2)', + }, + { + b: 'section8 table row4 col2', + a: 'main > div:nth-child(8) .table > div:nth-child(4) > div:nth-child(2)', + }, + { + b: 'section5', + a: 'main > div:nth-child(5)', + }, + { + b: '.text:has(#im-a-unique-text-block) secondary-cta', + a: '.text:has(#im-a-unique-text-block) p em a', + }, + { + b: 'section1 .random-block2', + a: 'main > div:nth-child(1) .random-block:nth-child(2 of .random-block)', + }, + { + b: 'main > section30', + a: 'main > div:nth-child(30)', + }, + { + b: 'main>section30', + a: 'main > div:nth-child(30)', + }, + { + b: 'main>div:nth-child(30)', + a: 'main > div:nth-child(30)', + }, + { + b: 'custom-block3', + a: '.custom-block:nth-child(3 of .custom-block)', + }, +]; +describe('test different values', () => { + values.forEach((value) => { + it(`should return the expected value for ${value.b}`, () => { + const { modifiedSelector, modifiers } = modifyNonFragmentSelector(value.b); + expect(modifiedSelector).to.equal(value.a); + expect(modifiers).to.deep.equal(value.m || []); + }); + }); +}); diff --git a/test/features/personalization/personalization-params.test.js b/test/features/personalization/personalization-params.test.js new file mode 100644 index 0000000000..ab23c98ec5 --- /dev/null +++ b/test/features/personalization/personalization-params.test.js @@ -0,0 +1,50 @@ +import { expect } from '@esm-bundle/chai'; +import { readFile } from '@web/test-runner-commands'; +import { stub } from 'sinon'; +import { getConfig } from '../../../libs/utils/utils.js'; +import { init } from '../../../libs/features/personalization/personalization.js'; +import spoofParams from './spoofParams.js'; +import mepSettings from './mepSettings.js'; + +document.head.innerHTML = await readFile({ path: './mocks/metadata.html' }); +document.body.innerHTML = await readFile({ path: './mocks/personalization.html' }); + +const getFetchPromise = (data, type = 'json') => new Promise((resolve) => { + resolve({ + ok: true, + [type]: () => data, + }); +}); + +const setFetchResponse = (data, type = 'json') => { + window.fetch = stub().returns(getFetchPromise(data, type)); +}; + +async function loadManifestAndSetResponse(manifestPath) { + let manifestJson = await readFile({ path: manifestPath }); + manifestJson = JSON.parse(manifestJson); + setFetchResponse(manifestJson); +} + +// Note that the manifestPath doesn't matter as we stub the fetch +describe('Functional Test', () => { + before(() => { + // Add custom keys so tests doesn't rely on real data + const config = getConfig(); + config.env = { name: 'prod' }; + config.consumerEntitlements = { + '11111111-aaaa-bbbb-6666-cccccccccccc': 'my-special-app', + '22222222-xxxx-bbbb-7777-cccccccccccc': 'fireflies', + }; + }); + + it('should override to param-newoffer=123', async () => { + spoofParams({ newoffer: '123' }); + const config = getConfig(); + await loadManifestAndSetResponse('./mocks/actions/manifestAppendToSection.json'); + setTimeout(async () => { + await init(mepSettings); + expect(config.mep.experiments[0].selectedVariantName).to.equal('param-newoffer=123'); + }, 100); + }); +}); diff --git a/test/features/personalization/personalization.test.js b/test/features/personalization/personalization.test.js index b396befa72..0c25dc04ab 100644 --- a/test/features/personalization/personalization.test.js +++ b/test/features/personalization/personalization.test.js @@ -4,9 +4,8 @@ import { assert, stub } from 'sinon'; import { getConfig, setConfig } from '../../../libs/utils/utils.js'; import { handleFragmentCommand, applyPers, - init, matchGlob, createFrag, combineMepSources, buildVariantInfo, + init, matchGlob, createContent, combineMepSources, buildVariantInfo, } from '../../../libs/features/personalization/personalization.js'; -import spoofParams from './spoofParams.js'; import mepSettings from './mepSettings.js'; document.head.innerHTML = await readFile({ path: './mocks/metadata.html' }); @@ -54,37 +53,39 @@ describe('Functional Test', () => { }); it('Can select elements using block-#', async () => { + document.body.innerHTML = await readFile({ path: './mocks/personalization.html' }); await loadManifestAndSetResponse('./mocks/manifestBlockNumber.json'); - expect(document.querySelector('.marquee')).to.not.be.null; - expect(document.querySelector('a[href="/fragments/replace/marquee/r2c1"]')).to.be.null; - expect(document.querySelector('a[href="/fragments/replace/marquee-2/r3c2"]')).to.be.null; + const firstMarquee = document.getElementsByClassName('marquee')[0]; const secondMarquee = document.getElementsByClassName('marquee')[1]; + expect(firstMarquee).to.not.be.null; expect(secondMarquee).to.not.be.null; + expect(document.querySelector('a[href="/fragments/replace/marquee/r2c1"]')).to.be.null; + expect(document.querySelector('a[href="/fragments/replace/marquee-2/r3c2"]')).to.be.null; await init(mepSettings); const fragment = document.querySelector('a[href="/fragments/replace/marquee/r2c1"]'); - expect(fragment).to.not.be.null; - const replacedCell = document.querySelector('.marquee > div:nth-child(2) > div:nth-child(1)'); - expect(replacedCell.firstChild.firstChild).to.equal(fragment); const secondFrag = document.querySelector('a[href="/fragments/replace/marquee-2/r2c2"]'); - expect(secondMarquee.lastElementChild.lastElementChild.firstChild.firstChild) - .to.equal(secondFrag); + expect(fragment).to.not.be.null; + expect(secondFrag).to.not.be.null; + + const firstMarqueeReplacedCell = firstMarquee.querySelector('p > a'); + const secondMarqueeReplacedCell = secondMarquee.querySelector('p > a'); + expect(firstMarqueeReplacedCell.href).to.equal(fragment.href); + expect(secondMarqueeReplacedCell.href).to.equal(secondFrag.href); }); it('Can select blocks using section and block indexs', async () => { await loadManifestAndSetResponse('./mocks/manifestSectionBlock.json'); - expect(document.querySelector('.special-block')).to.not.be.null; + expect(document.querySelector('.custom-block-1')).to.not.be.null; expect(document.querySelector('.custom-block-2')).to.not.be.null; - expect(document.querySelector('.custom-block-3')).to.not.be.null; await init(mepSettings); - expect(document.querySelector('.special-block')).to.be.null; + expect(document.querySelector('.custom-block-1')).to.be.null; expect(document.querySelector('.custom-block-2')).to.be.null; - expect(document.querySelector('.custom-block-3')).to.be.null; }); it('scheduled manifest should apply changes if active (bts)', async () => { @@ -285,7 +286,7 @@ describe('Functional Test', () => { await init(mepSettings); - assert.calledWith(window.console.log, 'Invalid selector: ', '.bad...selector'); + assert.calledWith(window.console.log, 'Invalid selector: '); window.console.log.reset(); }); @@ -308,16 +309,6 @@ describe('Functional Test', () => { expect(document.querySelector('meta[property="og:title"]').content).to.equal('New Title'); expect(document.querySelector('meta[property="og:image"]').content).to.equal('https://adobe.com/path/to/image.jpg'); }); - - it('should override to param-newoffer=123', async () => { - spoofParams({ newoffer: '123' }); - const config = getConfig(); - await loadManifestAndSetResponse('./mocks/actions/manifestAppendToSection.json'); - setTimeout(async () => { - await init(mepSettings); - expect(config.mep.experiments[0].selectedVariantName).to.equal('param-newoffer=123'); - }, 100); - }); }); describe('matchGlob function', () => { @@ -367,7 +358,7 @@ describe('matchGlob function', () => { const parent = document.createElement('div'); const el = document.createElement('div'); parent.appendChild(el); - const wrapper = createFrag(el, '/fragments/promos/path-to-promo/#modal-hash:delay=1'); + const wrapper = createContent(el, '/fragments/promos/path-to-promo/#modal-hash:delay=1'); expect(wrapper.tagName).to.equal('P'); expect(wrapper.classList.contains('hide-block')).to.be.true; }); From 42d51a0d190a84a269becb50a4f31072cd4823b3 Mon Sep 17 00:00:00 2001 From: Mark Perry <124626043+markpadbe@users.noreply.github.com> Date: Mon, 2 Sep 2024 01:17:10 -0700 Subject: [PATCH 04/66] MWPW-156793 [MILO][Analytics] Change char limit for headers based on MD (#2765) * Add charlimit for headers analytics * Fix header for integer value only * Update libs/martech/attributes.js set header length to false for clarity Co-authored-by: Vivian A Goodrich <101133187+vgoodric@users.noreply.github.com> * Add tests * Refacter str null check per suggestion * Fix test coverage --------- Co-authored-by: Vivian A Goodrich <101133187+vgoodric@users.noreply.github.com> --- libs/martech/attributes.js | 16 ++++++++-- test/martech/attributes.test.js | 52 +++++++++++++++++++++++++++++++++ test/martech/mocks/body.html | 5 ++-- 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/libs/martech/attributes.js b/libs/martech/attributes.js index 9bb2b3dc5f..1f0af83c53 100644 --- a/libs/martech/attributes.js +++ b/libs/martech/attributes.js @@ -1,3 +1,5 @@ +import { getMetadata } from '../utils/utils.js'; + const INVALID_CHARACTERS = /[^\u00C0-\u1FFF\u2C00-\uD7FF\w]+/g; const LEAD_UNDERSCORES = /^_+|_+$/g; @@ -11,6 +13,13 @@ export function processTrackingLabels(text, config, charLimit) { return analyticsValue; } +function getHeaderCharLimit(str) { + const defaultLimit = 20; + if (!str) return defaultLimit; + if (str === 'off') return false; + if (!Number.isNaN(Number(str))) return parseInt(str, 10); + return defaultLimit; +} export function decorateDefaultLinkAnalytics(block, config) { if (block.classList.length && !block.className.includes('metadata') @@ -20,6 +29,7 @@ export function decorateDefaultLinkAnalytics(block, config) { let header = ''; let linkCount = 1; + const headerCharLimit = getHeaderCharLimit(getMetadata('analytics-header-limit')); const headerSelector = 'h1, h2, h3, h4, h5, h6'; let analyticsSelector = `${headerSelector}, .tracking-header`; const headers = block.querySelectorAll(analyticsSelector); @@ -27,13 +37,13 @@ export function decorateDefaultLinkAnalytics(block, config) { block.querySelectorAll(`${analyticsSelector}, a:not(.video.link-block), button`).forEach((item) => { if (item.nodeName === 'A' || item.nodeName === 'BUTTON') { if (item.classList.contains('tracking-header')) { - header = processTrackingLabels(item.textContent, config, 20); + header = processTrackingLabels(item.textContent, config, headerCharLimit); } else if (!header) { const section = block.closest('.section'); if (section?.className.includes('-up') || section?.classList.contains('milo-card-section')) { const previousHeader = section?.previousElementSibling?.querySelector(headerSelector); if (previousHeader) { - header = processTrackingLabels(previousHeader.textContent, config, 20); + header = processTrackingLabels(previousHeader.textContent, config, headerCharLimit); } } } @@ -59,7 +69,7 @@ export function decorateDefaultLinkAnalytics(block, config) { if (item.nodeName === 'STRONG' || item.nodeName === 'B') { item.classList.add('tracking-header'); } - header = processTrackingLabels(item.textContent, config, 20); + header = processTrackingLabels(item.textContent, config, headerCharLimit); } }); } diff --git a/test/martech/attributes.test.js b/test/martech/attributes.test.js index 0e6e89e748..97593f80d3 100644 --- a/test/martech/attributes.test.js +++ b/test/martech/attributes.test.js @@ -1,11 +1,13 @@ import { readFile } from '@web/test-runner-commands'; import { expect } from '@esm-bundle/chai'; import { decorateSectionAnalytics, processTrackingLabels } from '../../libs/martech/attributes.js'; +import { createTag } from '../../libs/utils/utils.js'; const expectedLinkValues = [ 'already-has-1', 'Traditional Link-2--Second Traditional H', 'Title Only Link-3--Tracking Header', + 'Traditional Link-5--Button Tracking Head', 'After bold-1--Bold text', 'After strong-2--Strong text', ]; @@ -44,6 +46,56 @@ describe('Analytics', async () => { expect(link.getAttribute('daa-ll')).to.equal(expectedLinkValues[idx]); }); }); + it('should limit analytics header length to analytics-header-limit when this metadata value is set to a number', async () => { + document.body.outerHTML = await readFile({ path: './mocks/body.html' }); + const analyticsHeaderLimit = 2; + const meta = createTag('meta', { name: 'analytics-header-limit', content: analyticsHeaderLimit }); + document.querySelector('head')?.append(meta); + document.querySelectorAll('main > div').forEach((section, idx) => decorateSectionAnalytics(section, idx, { mep: { martech: '|smb|hp' } })); + const section = document.querySelector('main > div'); + section.querySelectorAll('a').forEach((link) => { + const daall = link.getAttribute('daa-ll'); + const linkHeaderValue = daall.includes('--') && daall.split('--')[1]; + if (linkHeaderValue) { + expect(linkHeaderValue.length).to.be.at.most(analyticsHeaderLimit); + } + }); + }); + it('should not limit analytics header length when metadata analytics-header-limit value set to off', async () => { + document.body.outerHTML = await readFile({ path: './mocks/body.html' }); + const analyticsHeaderLimit = 'off'; + document.querySelector('meta[name="analytics-header-limit"]')?.setAttribute('content', analyticsHeaderLimit); + document.querySelectorAll('main > div').forEach((section, idx) => decorateSectionAnalytics(section, idx, { mep: { martech: '|smb|hp' } })); + const headerText = document.querySelector('#block-with-header h3:nth-of-type(2)')?.textContent.trim(); + const daall = document.querySelector('#block-with-header p:nth-of-type(2) a')?.getAttribute('daa-ll')?.split('--')[1]; + expect(headerText).to.equal(daall); + }); + it('should limit analytics header length to default when analytics-header-limit metadata value is empty', async () => { + const defaultValue = 20; + document.body.outerHTML = await readFile({ path: './mocks/body.html' }); + const analyticsHeaderLimit = ''; + document.querySelector('meta[name="analytics-header-limit"]')?.setAttribute('content', analyticsHeaderLimit); + document.querySelectorAll('main > div').forEach((section, idx) => decorateSectionAnalytics(section, idx, { mep: { martech: '|smb|hp' } })); + const daall = document.querySelector('#block-with-header p:nth-of-type(2) a')?.getAttribute('daa-ll')?.split('--')[1]; + expect(daall.length).to.be.at.most(defaultValue); + }); + it('should limit analytics header length to default when analytics-header-limit metadata value is incorrect', async () => { + const defaultValue = 20; + document.body.outerHTML = await readFile({ path: './mocks/body.html' }); + const analyticsHeaderLimit = 'offs'; + document.querySelector('meta[name="analytics-header-limit"]')?.setAttribute('content', analyticsHeaderLimit); + document.querySelectorAll('main > div').forEach((section, idx) => decorateSectionAnalytics(section, idx, { mep: { martech: '|smb|hp' } })); + const daall = document.querySelector('#block-with-header p:nth-of-type(2) a')?.getAttribute('daa-ll')?.split('--')[1]; + expect(daall.length).to.be.at.most(defaultValue); + }); + it('should limit analytics header length to default when no metadata', async () => { + const defaultValue = 20; + document.body.outerHTML = await readFile({ path: './mocks/body.html' }); + document.querySelector('meta[name="analytics-header-limit"]').remove(); + document.querySelectorAll('main > div').forEach((section, idx) => decorateSectionAnalytics(section, idx, { mep: { martech: '|smb|hp' } })); + const daall = document.querySelector('#block-with-header p:nth-of-type(2) a')?.getAttribute('daa-ll')?.split('--')[1]; + expect(daall.length).to.be.at.most(defaultValue); + }); it('should process tracking labels', () => { const longString = 'This is a long string that should be truncated'; const processedString = processTrackingLabels(longString, { locale: { ietf: 'en-US' } }, 20); diff --git a/test/martech/mocks/body.html b/test/martech/mocks/body.html index ee224e65be..be6320b7ff 100644 --- a/test/martech/mocks/body.html +++ b/test/martech/mocks/body.html @@ -9,6 +9,8 @@

Second Traditional Header

Traditional Link

Tracking Header

+ +

Traditional Link

@@ -56,6 +58,5 @@

AI for

- + - From 8467b03b315068ea0e5bd8b31e15fbd27e7d7bc3 Mon Sep 17 00:00:00 2001 From: Mariia Lukianets Date: Mon, 2 Sep 2024 10:17:17 +0200 Subject: [PATCH 05/66] MWPW-157540: fix external modals (#2806) --- libs/blocks/merch/merch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/blocks/merch/merch.js b/libs/blocks/merch/merch.js index 4dd85521cf..98631793cc 100644 --- a/libs/blocks/merch/merch.js +++ b/libs/blocks/merch/merch.js @@ -374,7 +374,7 @@ export async function openModal(e, url, offerType) { if (/\/fragments\//.test(url)) { const fragmentPath = url.split(/hlx.(page|live)/).pop(); modal = await openFragmentModal(fragmentPath, getModal); - } else if (/^https?:/.test(url)) { + } else { modal = await openExternalModal(url, getModal); } if (modal) { From d405ac1b49ffb69a631f3ea8541f1ff005fc4d14 Mon Sep 17 00:00:00 2001 From: Axel Cureno Basurto Date: Mon, 2 Sep 2024 01:17:24 -0700 Subject: [PATCH 06/66] Mwpw 142267: What's included style fixes (#2674) --- libs/deps/mas/merch-mnemonic-list.js | 5 +++-- libs/deps/mas/merch-whats-included.js | 1 - libs/features/mas/web-components/src/merch-mnemonic-list.js | 5 +++-- libs/features/mas/web-components/src/merch-whats-included.js | 1 - 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libs/deps/mas/merch-mnemonic-list.js b/libs/deps/mas/merch-mnemonic-list.js index c5cda0820a..946d0d034c 100644 --- a/libs/deps/mas/merch-mnemonic-list.js +++ b/libs/deps/mas/merch-mnemonic-list.js @@ -2,8 +2,9 @@ import{html as e,css as i,LitElement as s}from"/libs/deps/lit-all.min.js";var t= :host { display: flex; flex-direction: row; - gap: 10px; - margin-bottom: 10px; + gap: 5px; + margin-bottom: 5px; + margin-right: 10px; align-items: flex-end; } diff --git a/libs/deps/mas/merch-whats-included.js b/libs/deps/mas/merch-whats-included.js index 24065954d7..bee34475b6 100644 --- a/libs/deps/mas/merch-whats-included.js +++ b/libs/deps/mas/merch-whats-included.js @@ -8,7 +8,6 @@ import{html as e,css as o,LitElement as l}from"/libs/deps/lit-all.min.js";var t= place-content: stretch start; box-sizing: border-box; align-self: baseline; - margin-top: 16px; margin-bottom: 16px; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); grid-auto-rows: unset; diff --git a/libs/features/mas/web-components/src/merch-mnemonic-list.js b/libs/features/mas/web-components/src/merch-mnemonic-list.js index b2d66a4e3e..2981e13d07 100644 --- a/libs/features/mas/web-components/src/merch-mnemonic-list.js +++ b/libs/features/mas/web-components/src/merch-mnemonic-list.js @@ -5,8 +5,9 @@ export class MerchMnemonicList extends LitElement { :host { display: flex; flex-direction: row; - gap: 10px; - margin-bottom: 10px; + gap: 5px; + margin-bottom: 5px; + margin-right: 10px; align-items: flex-end; } diff --git a/libs/features/mas/web-components/src/merch-whats-included.js b/libs/features/mas/web-components/src/merch-whats-included.js index c0ea4fb163..6ceb950a85 100644 --- a/libs/features/mas/web-components/src/merch-whats-included.js +++ b/libs/features/mas/web-components/src/merch-whats-included.js @@ -11,7 +11,6 @@ export class MerchWhatsIncluded extends LitElement { place-content: stretch start; box-sizing: border-box; align-self: baseline; - margin-top: 16px; margin-bottom: 16px; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); grid-auto-rows: unset; From 1c4001b0fedf139dda5160ba02dc8af3cd587773 Mon Sep 17 00:00:00 2001 From: Sheridan Sunier Date: Mon, 2 Sep 2024 01:17:30 -0700 Subject: [PATCH 07/66] MWPW-156810: adding caas config change for live CTAs (#2707) * MWPW-145727: add configurator changes for alt cta on live events * MWPW-14572: get the alt cta from secondaryCTA so it can be localized by caas * lint * left something in * left something in * unit tests * unit tests * MWPW-145727: unit tests * MWPW-145727: unit tests * MWPW-145727: unit tests * MWPW-145727: unit tests * unit tests hopefully * ok finally the unit tests probably * ok this will be the one * variable name * config hash test --------- Co-authored-by: milo-pr-merge[bot] <169241390+milo-pr-merge[bot]@users.noreply.github.com> Co-authored-by: Sheridan Sunier --- libs/blocks/caas-config/caas-config.js | 1 + libs/blocks/caas/utils.js | 2 ++ test/blocks/caas-config/caas-config.test.html | 2 +- test/blocks/caas-config/expectedConfigs/defaultConfig.js | 1 + test/blocks/caas/utils.test.js | 3 +++ 5 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libs/blocks/caas-config/caas-config.js b/libs/blocks/caas-config/caas-config.js index c1bec7e45d..db1859b792 100644 --- a/libs/blocks/caas-config/caas-config.js +++ b/libs/blocks/caas-config/caas-config.js @@ -366,6 +366,7 @@ const UiPanel = () => html` <${Input} label="Show total card count at top" prop="showTotalResults" type="checkbox" /> <${Input} label="Hide date for on-demand content" prop="hideDateInterval" type="checkbox" /> <${Input} label="Enable showing card badges (by default hidden)" prop="showCardBadges" type="checkbox" /> + <${Input} label="Show a different CTA for live events" prop="dynamicCTAForLiveEvents" type="checkbox" /> <${Select} label="Card Style" prop="cardStyle" options=${defaultOptions.cardStyle} /> <${Select} options=${defaultOptions.cardTitleAccessibilityLevel} prop="cardTitleAccessibilityLevel" label="Card Accessibility Title Level" /> <${Select} label="Layout" prop="container" options=${defaultOptions.container} /> diff --git a/libs/blocks/caas/utils.js b/libs/blocks/caas/utils.js index 7ea5fcbf41..e83d9b3e36 100644 --- a/libs/blocks/caas/utils.js +++ b/libs/blocks/caas/utils.js @@ -599,6 +599,7 @@ export const getConfig = async (originalState, strs = {}) => { }, detailsTextOption: state.detailsTextOption, hideDateInterval: state.hideDateInterval, + dynamicCTAForLiveEvents: state.dynamicCTAForLiveEvents, setCardBorders: state.setCardBorders, showFooterDivider: state.showFooterDivider, useOverlayLinks: state.useOverlayLinks, @@ -764,6 +765,7 @@ export const initCaas = async (state, caasStrs, el) => { export const defaultState = { additionalRequestParams: [], + dynamicCTAForLiveEvents: false, analyticsCollectionName: '', analyticsTrackImpression: false, andLogicTags: [], diff --git a/test/blocks/caas-config/caas-config.test.html b/test/blocks/caas-config/caas-config.test.html index fa0456dff4..612a76c827 100644 --- a/test/blocks/caas-config/caas-config.test.html +++ b/test/blocks/caas-config/caas-config.test.html @@ -312,7 +312,7 @@ // The header is the part before the first / in the hash // Due to this we only compare the characters after the header value - const hashWithoutHeader = 'bNhD9Fz47Wafb9kFva+8uYsCbdWMv+lAExZgcSYQpjkoO7VWK/ntB6u44b9I55FzOXPivAKU0a7JgvuI/AT3vwEHlRfbXt4UAC6ZhLf2ajEEZz32BCkUmxIQ8OJCnTVU79F6TFVkOxmM8oLZUaHmAorcXmNYULLtmC7YYTh6JThW400aS3WP01LqY4m/WTxgJTu25MTGWEkz+oURdlNwxB80GH6SMAR210dxs8YxGZL8vhBxSWbHtTdROV+AaMaXHTEcsGb4G9/r7gFkGbdGJTHz6Zbl8gfc/teKyo9Dyoalx1EO2WohMSACfdb93wccLwTNVa3Cqs83wkNyJTPzt0eRiIRR9Id7C92ZLoAY1lfZwNLgCa9H5EXaQ8+Nx+EeratI2Cnq5XD6CoiN+lFTdyVJX6OAD1PpuzFLEG2ftyFZouyrguzRBoYpRdil10JhjDsYcQZ6eRndiIXIEDm52M9eG0T2dB+stsAraqB3YWL7UQBWwlgO9JQmdKgZznuCFliIT5AZo5sevk74zaF/S5amquRk0KgJzquav72IhSgQ1mCm1wjXDRs3/x8Qj8AiMG8vozmAGo9peSWTAFgEK7Pug/7/DqLqBhkJqnBhHqCNEoF7I4c0OtsRXY0fWNDG3VTdOg+xdQOSuLtRQaJtkfbC6Sh+DHyhQiemRJxu7bWy/kfkjgGXNybe9wXc5dUCq1Ei+eTyUWOH9eNGAxJKMQvfmTNsjDn0w7HfodknA3xbCIzhZPms0fWk8SrIKXDNmOEB7Ck5iD3IUZkVOTQfHl3RJOKgC5/AzEaN71GetYptMmF5r/5x66zb3aueFSCb7Xp1gm6sz+5TiDDoQxxWe1BgJchxb8MHLH7BHvAG+kNK5nhQzEZhDMHEoVX+vJdrK76gO9exCi38Fq6jaQ1UbPdn16UAc8j05nqHP3U6YgX1E1yn0+A9ptG7nEHp0Z9JuR2RE9mm5XF4RKUoU2X2Lt+/HleEEXrnrekeUcDl58W0hGArfNufPd2o8I+JRVyDHnX7WceWIAbueKI5zEBdcfOLQxyWnkEEbf8B3fq27/ae6Oi0Ex1A/Iyhti+7lE+V9JGKXpJ47UJzKKMZCBI/baDpaG5wGj69ndAaarbansacm72dgphsLKHh0G5tTHKr//gcEWd70YggAAA=='; + const hashWithoutHeader = 'bNhD9Lzw7Wafb9qCb7V0jBrxZN/aihyIoxuRIIkxxVHJor1L0vxek9ek4N+kNOR9v3gz/FaCUZk0WzFf8J6DnHTiovMj++jYTqrFQabk6LNbktvqMz2e07EWWg/E4E2DBNKylX5ExKKOfL1ChyIQYGQ8O5GlT1Q6912RHt9WWCi0PULTxIDCtKFh2zRZs0Z88Ep0qcKeNJLvHGOkaYoy/WT+ySHBqz42JuZRg8g8l6qLk1nLQbHAhZUzoqI3mZotnNCL7fSZkX8qSbeeidroC14ixeah0wJLjW3Cvv/eYZdAWncjEp1/m8xd4/1MrLlsTWj40NQ58yCsXIhMSwGft70Pw8ULwTNUKnGp9MyxSOJGJvz2aXMyEoi/EW/jebAlUz6bSHo4Gl2AtuqGZykHOT8f+H62qSdtI6OVy+QiKjvhRUvUgS12hgw9Q64ehShFvnLUjW6Ftu4Dv0gSFKmbZltRCQ405GHMEeXoewomZyBE4uMnNXBtGlxTYnknAMmijdmBj+5KAKmAte/OWJLSsGMx5hBdaikyQ66FJHL9K/E6gfUmX56rmpueoCMypm7++i5koEVTvptQKVwwbNf0fCo/AEzBuLKM7g+mdantDkQFbBCiw00H3/4CRdQMNhSScmEeoI0SgXsjhXQVb4puxI2uaWNuyHaee9jYhcjcXaii0TbQurK7SRx8HClRifOTZRrUN8hssfwSwrDnFtnfsbU0tkDo1GN88Hkqs8HG4aEBiSUahe3PmqhGHPhj2O3S7ROBvM+ERnCzXGk3XGo+SrALXDBX20J6Ck9iBHIlZklPjwfElXRIOqsApvCZidE/6rFWUycjSce3XSVv3ba922ojkstPqCNvcnNmnEifQgTiu+MTGYCDHUYILL3/AnvAO+EJK53rUzGTAHIKJQ6m6e1fDtfM7qkM9uXDFv4JVVO2hqo0e7fp0IA75nhxP0HW7EyZgl9FtCR3+QxnXsFMIPbozabcjMiL7NJ/PbwwpSxTZ4xW/vh83jhN4E67VjijhcvLi20wwFP4qzp/v1HhGxKOuQI47/azjyhE9djtRHOcgLrj4xKGPS04hgzb+gO/8Wrf7T7V9mgmOqX5GUNoW7csnysdoiCpJmjtQnMpIxkwEj9voOnrrgwaPr2d0BpqttqdBU6P3MzDTnQUUPLqNzSkO1X//AxGj3OyCCAAA'; const hash = copyTextArea.value.split('#')[1].trim(); diff --git a/test/blocks/caas-config/expectedConfigs/defaultConfig.js b/test/blocks/caas-config/expectedConfigs/defaultConfig.js index 0e9b72de15..4d3f1b54e9 100644 --- a/test/blocks/caas-config/expectedConfigs/defaultConfig.js +++ b/test/blocks/caas-config/expectedConfigs/defaultConfig.js @@ -40,6 +40,7 @@ const defaultConfig = { reservoir: { sample: 3, pool: 1000 }, ctaAction: '_self', additionalRequestParams: {}, + dynamicCTAForLiveEvents: false, }, headers: [], hideCtaIds: [ diff --git a/test/blocks/caas/utils.test.js b/test/blocks/caas/utils.test.js index be7df8253f..29884bb434 100644 --- a/test/blocks/caas/utils.test.js +++ b/test/blocks/caas/utils.test.js @@ -190,6 +190,7 @@ describe('getConfig', () => { showFooterDivider: false, useOverlayLinks: false, additionalRequestParams: {}, + dynamicCTAForLiveEvents: false, banner: { register: { description: 'Sign Up', url: '#registration' }, upcoming: { description: 'Upcoming' }, @@ -453,6 +454,7 @@ describe('getConfig', () => { showFooterDivider: false, useOverlayLinks: false, additionalRequestParams: {}, + dynamicCTAForLiveEvents: false, banner: { register: { description: 'Sign Up', url: '#registration' }, upcoming: { description: 'Upcoming' }, @@ -777,6 +779,7 @@ describe('getFloodgateCaasConfig', () => { showFooterDivider: false, useOverlayLinks: false, additionalRequestParams: {}, + dynamicCTAForLiveEvents: false, banner: { register: { description: 'Sign Up', url: '#registration' }, upcoming: { description: 'Upcoming' }, From 541776292e928ce01d767c1c5a4045db3824bdcb Mon Sep 17 00:00:00 2001 From: Mariia Lukianets Date: Mon, 2 Sep 2024 10:17:37 +0200 Subject: [PATCH 08/66] MWPW-156672: refactor commerce & optimize pandora dependencies (#2723) * add sitemap * remove pandora wcs client * remove obsolete dependencies * fix request&settings test * remove domainSwitch * fix tests * update deps * fix review comment * use usual error message * fix language param * commit packag-lock * bring back transitive dependencies * bring back www.stage.adobe.com * omit cookies for wcs requests * revert special logic for stage --- libs/deps/commerce.js | 6 - libs/deps/mas/commerce.js | 4 +- libs/deps/mas/mas.js | 4 +- .../internal/data-source-wcs-0.2.8.tgz | Bin 5782 -> 0 bytes .../internal/eslint-config-tacocat-1.1.2.tgz | Bin 806 -> 0 bytes .../internal/react-auth-provider-1.2.1.tgz | Bin 5309 -> 0 bytes .../internal/react-env-provider-1.2.2.tgz | Bin 3312 -> 0 bytes .../internal/react-fetch-provider-1.2.2.tgz | Bin 3179 -> 0 bytes .../commerce/internal/tacocat-core-1.12.2.tgz | Bin 18104 -> 0 bytes .../commerce/internal/tacocat-core-1.13.0.tgz | Bin 0 -> 19169 bytes .../internal/tacocat-wcs-client-1.17.0.tgz | Bin 7374 -> 0 bytes libs/features/mas/commerce/libs/commerce.d.ts | 13 +- libs/features/mas/commerce/package.json | 9 +- libs/features/mas/commerce/src/constants.js | 8 + libs/features/mas/commerce/src/defaults.js | 6 +- libs/features/mas/commerce/src/external.js | 22 +- libs/features/mas/commerce/src/index.d.ts | 13 +- libs/features/mas/commerce/src/index.js | 5 +- libs/features/mas/commerce/src/settings.js | 27 +- libs/features/mas/commerce/src/wcs.js | 106 +++-- .../mas/commerce/test/settings.test.js | 144 +++++-- libs/features/mas/commerce/test/wcs.test.js | 8 +- libs/features/mas/package-lock.json | 408 +++++------------- 23 files changed, 295 insertions(+), 488 deletions(-) delete mode 100644 libs/deps/commerce.js delete mode 100644 libs/features/mas/commerce/internal/data-source-wcs-0.2.8.tgz delete mode 100644 libs/features/mas/commerce/internal/eslint-config-tacocat-1.1.2.tgz delete mode 100644 libs/features/mas/commerce/internal/react-auth-provider-1.2.1.tgz delete mode 100644 libs/features/mas/commerce/internal/react-env-provider-1.2.2.tgz delete mode 100644 libs/features/mas/commerce/internal/react-fetch-provider-1.2.2.tgz delete mode 100644 libs/features/mas/commerce/internal/tacocat-core-1.12.2.tgz create mode 100644 libs/features/mas/commerce/internal/tacocat-core-1.13.0.tgz delete mode 100644 libs/features/mas/commerce/internal/tacocat-wcs-client-1.17.0.tgz diff --git a/libs/deps/commerce.js b/libs/deps/commerce.js deleted file mode 100644 index c3a2dc1903..0000000000 --- a/libs/deps/commerce.js +++ /dev/null @@ -1,6 +0,0 @@ -// branch: main commit: c9d55dcfbb025978f8378c6aad1a2d2a773bbdd3 Tue, 23 Jul 2024 17:39:22 GMT -var kr=Object.defineProperty;var ui=(e,t,r)=>t in e?kr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var li=(e,t)=>{for(var r in t)kr(e,r,{get:t[r],enumerable:!0})};var ne=(e,t,r)=>(ui(e,typeof t!="symbol"?t+"":t,r),r),Gr=(e,t,r)=>{if(!t.has(e))throw TypeError("Cannot "+r)};var Lt=(e,t,r)=>(Gr(e,t,"read from private field"),r?r.call(e):t.get(e)),Fr=(e,t,r)=>{if(t.has(e))throw TypeError("Cannot add the same private member more than once");t instanceof WeakSet?t.add(e):t.set(e,r)},Nt=(e,t,r,n)=>(Gr(e,t,"write to private field"),n?n.call(e,r):t.set(e,r),r);var Ie;(function(e){e.ServerError="ServerError",e.ClientError="ClientError",e.UnexpectedError="UnexpectedError"})(Ie||(Ie={}));var Vr=(e,t,r)=>({type:(i=>i>=500?Ie.ServerError:i<400?Ie.UnexpectedError:Ie.ClientError)(e),message:t,originatingRequest:r,status:e});var fi=function(e,t,r,n){function i(o){return o instanceof r?o:new r(function(s){s(o)})}return new(r||(r=Promise))(function(o,s){function a(u){try{l(n.next(u))}catch(p){s(p)}}function c(u){try{l(n.throw(u))}catch(p){s(p)}}function l(u){u.done?o(u.value):i(u.value).then(a,c)}l((n=n.apply(e,t||[])).next())})},Be;(function(e){e.AUTHORIZATION="Authorization",e.X_API_KEY="X-Api-Key"})(Be||(Be={}));var Ct=class{constructor(t){this.fetchOptions=t}commonHeaders(){let t={};return this.fetchOptions.apiKey&&(t[Be.X_API_KEY]=this.fetchOptions.apiKey),this.fetchOptions.accessToken&&(t[Be.AUTHORIZATION]=`Bearer ${this.fetchOptions.accessToken}`),t}transformData(t,r){return r?t.map(n=>r(n)):t.map(n=>this.identifyTransform(n))}transformDatum(t,r){return r?r(t):this.identifyTransform(t)}identifyTransform(t){return t}failOnBadStatusOrParseBody(t,r){return fi(this,void 0,void 0,function*(){if(t.ok)return t.json().then(i=>({headers:t.headers,status:t.status,statusText:t.statusText,data:i}));let n=yield t.text();return Promise.reject(Vr(t.status,n,r))})}buildUrl(t,r,n,i,o){var s;let a=(s=this.fetchOptions.baseUrl)!==null&&s!==void 0?s:i(this.fetchOptions.env),c=o(r,n);return this.generateUrl(a,t,c)}generateUrl(t,r,n){let i=new URL(r,t);return n&&(i.search=this.convertToSearchParams(n).toString()),i.toString()}convertToSearchParams(t){return new URLSearchParams(t)}setParams(t,r,n){n!=null&&typeof n=="boolean"?t[r]=String(n):n&&(t[r]=n)}},It=Ct;var Y;(function(e){e.STAGE="STAGE",e.PRODUCTION="PRODUCTION",e.LOCAL="LOCAL"})(Y||(Y={}));var Re;(function(e){e.STAGE="STAGE",e.PRODUCTION="PROD",e.LOCAL="LOCAL"})(Re||(Re={}));var G;(function(e){e.DRAFT="DRAFT",e.PUBLISHED="PUBLISHED"})(G||(G={}));var fe;(function(e){e.V2="UCv2",e.V3="UCv3"})(fe||(fe={}));var $;(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"})($||($={}));var Rt=function(e){var t;return(t=pi.get(e))!==null&&t!==void 0?t:e},pi=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 jr=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.")},Wr=function(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),i,o=[],s;try{for(;(t===void 0||t-- >0)&&!(i=n.next()).done;)o.push(i.value)}catch(a){s={error:a}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return o};function ve(e,t,r){var n,i;try{for(var o=jr(Object.entries(e)),s=o.next();!s.done;s=o.next()){var a=Wr(s.value,2),c=a[0],l=a[1],u=Rt(c);l!=null&&r.has(u)&&t.set(u,l)}}catch(p){n={error:p}}finally{try{s&&!s.done&&(i=o.return)&&i.call(o)}finally{if(n)throw n.error}}}function Ye(e){switch(e){case Y.PRODUCTION:return"https://commerce.adobe.com";default:return"https://commerce-stg.adobe.com"}}function $e(e,t){var r,n;for(var i in e){var o=e[i];try{for(var s=(r=void 0,jr(Object.entries(o))),a=s.next();!a.done;a=s.next()){var c=Wr(a.value,2),l=c[0],u=c[1];if(u!=null){var p=Rt(l);t.set("items["+i+"]["+p+"]",u)}}}catch(f){r={error:f}}finally{try{a&&!a.done&&(n=s.return)&&n.call(s)}finally{if(r)throw r.error}}}}var mi=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 i=0,n=Object.getOwnPropertySymbols(e);i=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 Hr(e){gi(e);var t=e.env,r=e.items,n=e.workflowStep,i=mi(e,["env","items","workflowStep"]),o=new URL(Ye(t));return o.pathname=n+"/",$e(r,o.searchParams),ve(i,o.searchParams,di),o.toString()}var di=new Set(["cli","co","lang","ctx","cUrl","mv","nglwfdata","otac","promoid","rUrl","sdid","spint","trackingid","code","campaignid","appctxid"]),Ei=["env","workflowStep","clientId","country","items"];function gi(e){var t,r;try{for(var n=hi(Ei),i=n.next();!i.done;i=n.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&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}return!0}var xi=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 i=0,n=Object.getOwnPropertySymbols(e);i=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},_i="p_draft_landscape",vi="/store/";function Dt(e){bi(e);var t=e.env,r=e.items,n=e.workflowStep,i=e.ms,o=e.marketSegment,s=e.ot,a=e.offerType,c=e.pa,l=e.productArrangementCode,u=e.landscape,p=xi(e,["env","items","workflowStep","ms","marketSegment","ot","offerType","pa","productArrangementCode","landscape"]),f={marketSegment:o??i,offerType:a??s,productArrangementCode:l??c},d=new URL(Ye(t));return d.pathname=""+vi+n,n!==$.SEGMENTATION&&n!==$.CHANGE_PLAN_TEAM_PLANS&&$e(r,d.searchParams),n===$.SEGMENTATION&&ve(f,d.searchParams,Ut),ve(p,d.searchParams,Ut),u===G.DRAFT&&ve({af:_i},d.searchParams,Ut),d.toString()}var Ut=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"]),Ti=["env","workflowStep","clientId","country"];function bi(e){var t,r;try{for(var n=yi(Ti),i=n.next();!i.done;i=n.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&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}if(e.workflowStep!==$.SEGMENTATION&&e.workflowStep!==$.CHANGE_PLAN_TEAM_PLANS&&!e.items)throw new Error('Argument "checkoutData" is not valid, missing: items');return!0}function Mt(e,t){switch(e){case fe.V2:return Hr(t);case fe.V3:return Dt(t);default:return console.warn("Unsupported CheckoutType, will use UCv3 as default. Given type: "+e),Dt(t)}}var kt;(function(e){e.BASE="BASE",e.TRIAL="TRIAL",e.PROMOTION="PROMOTION"})(kt||(kt={}));var I;(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"})(I||(I={}));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 Gt;(function(e){e.INDIVIDUAL="INDIVIDUAL",e.TEAM="TEAM",e.ENTERPRISE="ENTERPRISE"})(Gt||(Gt={}));var Ft;(function(e){e.COM="COM",e.EDU="EDU",e.GOV="GOV"})(Ft||(Ft={}));var Vt;(function(e){e.DIRECT="DIRECT",e.INDIRECT="INDIRECT"})(Vt||(Vt={}));var jt;(function(e){e.ENTERPRISE_PRODUCT="ENTERPRISE_PRODUCT",e.ETLA="ETLA",e.RETAIL="RETAIL",e.VIP="VIP",e.VIPMP="VIPMP",e.FREE="FREE"})(jt||(jt={}));var Wt=()=>{};Wt.createContext=Wt;var Si=G.PUBLISHED,Xr=e=>{switch(e){case Y.PRODUCTION:return"https://wcs.adobe.io";case Y.STAGE:return"https://wcs-stage.adobe.io";case Y.LOCAL:return"http://localhost:3002";default:return"https://wcs-stage.adobe.io"}},Br=(e,t)=>{var r;return e.api_key=t.apiKey,e.landscape=(r=t.landscape)!==null&&r!==void 0?r:Si,e};var Pi=function(e,t,r,n){function i(o){return o instanceof r?o:new r(function(s){s(o)})}return new(r||(r=Promise))(function(o,s){function a(u){try{l(n.next(u))}catch(p){s(p)}}function c(u){try{l(n.throw(u))}catch(p){s(p)}}function l(u){u.done?o(u.value):i(u.value).then(a,c)}l((n=n.apply(e,t||[])).next())})},Ht=class extends It{constructor(t){super(t),this.apiPaths={getWebCommerceArtifact:"web_commerce_artifact"},this.getWebCommerceArtifact=(r,n,i,o)=>Pi(this,void 0,void 0,function*(){let s=this.buildUrl(this.apiPaths.getWebCommerceArtifact,n,r,a=>Xr(a),(a,c)=>this.evaluateGetWebCommerceArtifactParams(a,c));return this.fetchOptions.fetch(s,{signal:o,headers:Object.assign({},this.commonHeaders()),mode:"cors"}).then(a=>this.failOnBadStatusOrParseBody(a,`GET ${s}`)).then(a=>{let l=a.data;return{data:this.transformDatum(l,i)}})})}evaluateGetWebCommerceArtifactParams(t,r){let n={};return this.setParams(n,"offer_selector_ids",r.offerSelectorIds.join(",")),this.setParams(n,"country",r.country),this.setParams(n,"language",r.language),this.setParams(n,"currency",r.currency),this.setParams(n,"locale",r.locale),this.setParams(n,"promotion_code",r.promotionCode),Br(n,t)}},Yr=Ht;var qe=e=>new Yr(e).getWebCommerceArtifact;var $r="tacocat.js";var ze=(e,t)=>String(e??"").toLowerCase()==String(t??"").toLowerCase(),qr=e=>`${e??""}`.replace(/[&<>'"]/g,t=>({"&":"&","<":"<",">":">","'":"'",'"':"""})[t]??t)??"";function w(e,t={},{metadata:r=!0,search:n=!0,storage:i=!0}={}){let o;if(n&&o==null){let s=new URLSearchParams(window.location.search),a=Te(n)?n:e;o=s.get(a)}if(i&&o==null){let s=Te(i)?i:e;o=window.sessionStorage.getItem(s)??window.localStorage.getItem(s)}if(r&&o==null){let s=Qr(Te(r)?r:e);o=document.documentElement.querySelector(`meta[name="${s}"]`)?.content}return o??t[e]}var be=()=>{};var zr=e=>typeof e=="boolean",ce=e=>typeof e=="function",Ze=e=>typeof e=="number",Zr=e=>e!=null&&typeof e=="object";var Te=e=>typeof e=="string",Xt=e=>Te(e)&&e,Se=e=>Ze(e)&&Number.isFinite(e)&&e>0;function Pe(e,t=r=>r==null||r===""){return e!=null&&Object.entries(e).forEach(([r,n])=>{t(n)&&delete e[r]}),e}function v(e,t){if(zr(e))return e;let r=String(e);return r==="1"||r==="true"?!0:r==="0"||r==="false"?!1:t}function ie(e,t,r){let n=Object.values(t);return n.find(i=>ze(i,e))??r??n[0]}function Qr(e=""){return String(e).replace(/(\p{Lowercase_Letter})(\p{Uppercase_Letter})/gu,(t,r,n)=>`${r}-${n}`).replace(/\W+/gu,"-").toLowerCase()}function Ae(e,t=1){return Ze(e)||(e=Number.parseInt(e,10)),!Number.isNaN(e)&&e>0&&Number.isFinite(e)?e:t}var Ai=Date.now(),Bt=()=>`(+${Date.now()-Ai}ms)`,Qe=new Set,wi=v(w("tacocat.debug",{},{metadata:!1}),typeof process<"u"&&process.env?.DEBUG);function Jr(e){let t=`[${$r}/${e}]`,r=(s,a,...c)=>s?!0:(i(a,...c),!1),n=wi?(s,...a)=>{console.debug(`${t} ${s}`,...a,Bt())}:()=>{},i=(s,...a)=>{let c=`${t} ${s}`;Qe.forEach(([l])=>l(c,...a))};return{assert:r,debug:n,error:i,warn:(s,...a)=>{let c=`${t} ${s}`;Qe.forEach(([,l])=>l(c,...a))}}}function Oi(e,t){let r=[e,t];return Qe.add(r),()=>{Qe.delete(r)}}Oi((e,...t)=>{console.error(e,...t,Bt())},(e,...t)=>{console.warn(e,...t,Bt())});var Li="no promo",Kr="promo-tag",Ni="yellow",Ci="neutral",Ii=(e,t,r)=>{let n=o=>o||Li,i=r?` (was "${n(t)}")`:"";return`${n(e)}${i}`},Ri="cancel-context",Ue=(e,t)=>{let r=e===Ri,n=!r&&e?.length>0,i=(n||r)&&(t&&t!=e||!t&&!r),o=i&&n||!i&&!!t,s=o?e||t:void 0;return{effectivePromoCode:s,overridenPromoCode:e,className:o?Kr:`${Kr} no-promo`,text:Ii(s,t,i),variant:o?Ni:Ci,isOverriden:i}};var Yt=function(e,t){return Yt=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,n){r.__proto__=n}||function(r,n){for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(r[i]=n[i])},Yt(e,t)};function De(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 r(){this.constructor=e}e.prototype=t===null?Object.create(t):(r.prototype=t.prototype,new r)}var T=function(){return T=Object.assign||function(t){for(var r,n=1,i=arguments.length;n0}),r=[],n=0,i=t;n1)throw new RangeError("integer-width stems only accept a single optional option");i.options[0].replace(Mi,function(a,c,l,u,p,f){if(c)t.minimumIntegerDigits=l.length;else{if(u&&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(ln.test(i.stem)){t.minimumIntegerDigits=i.stem.length;continue}if(on.test(i.stem)){if(i.options.length>1)throw new RangeError("Fraction-precision stems only accept a single optional option");i.stem.replace(on,function(a,c,l,u,p,f){return l==="*"?t.minimumFractionDigits=c.length:u&&u[0]==="#"?t.maximumFractionDigits=u.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=T(T({},t),sn(i.options[0])));continue}if(un.test(i.stem)){t=T(T({},t),sn(i.stem));continue}var o=fn(i.stem);o&&(t=T(T({},t),o));var s=ki(i.stem);s&&(t=T(T({},t),s))}return t}var zt,Gi=new RegExp("^"+qt.source+"*"),Fi=new RegExp(qt.source+"*$");function x(e,t){return{start:e,end:t}}var Vi=!!String.prototype.startsWith,ji=!!String.fromCodePoint,Wi=!!Object.fromEntries,Hi=!!String.prototype.codePointAt,Xi=!!String.prototype.trimStart,Bi=!!String.prototype.trimEnd,Yi=!!Number.isSafeInteger,$i=Yi?Number.isSafeInteger:function(e){return typeof e=="number"&&isFinite(e)&&Math.floor(e)===e&&Math.abs(e)<=9007199254740991},Qt=!0;try{mn=gn("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu"),Qt=((zt=mn.exec("a"))===null||zt===void 0?void 0:zt[0])==="a"}catch{Qt=!1}var mn,hn=Vi?function(t,r,n){return t.startsWith(r,n)}:function(t,r,n){return t.slice(n,n+r.length)===r},Jt=ji?String.fromCodePoint:function(){for(var t=[],r=0;ro;){if(s=t[o++],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},dn=Wi?Object.fromEntries:function(t){for(var r={},n=0,i=t;n=n)){var i=t.charCodeAt(r),o;return i<55296||i>56319||r+1===n||(o=t.charCodeAt(r+1))<56320||o>57343?i:(i-55296<<10)+(o-56320)+65536}},qi=Xi?function(t){return t.trimStart()}:function(t){return t.replace(Gi,"")},zi=Bi?function(t){return t.trimEnd()}:function(t){return t.replace(Fi,"")};function gn(e,t){return new RegExp(e,t)}var Kt;Qt?(Zt=gn("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu"),Kt=function(t,r){var n;Zt.lastIndex=r;var i=Zt.exec(t);return(n=i[1])!==null&&n!==void 0?n:""}):Kt=function(t,r){for(var n=[];;){var i=En(t,r);if(i===void 0||yn(i)||Ji(i))break;n.push(i),r+=i>=65536?2:1}return Jt.apply(void 0,n)};var Zt,xn=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 i=[];!this.isEOF();){var o=this.char();if(o===123){var s=this.parseArgument(t,n);if(s.err)return s;i.push(s.val)}else{if(o===125&&t>0)break;if(o===35&&(r==="plural"||r==="selectordinal")){var a=this.clonePosition();this.bump(),i.push({type:A.pound,location:x(a,this.clonePosition())})}else if(o===60&&!this.ignoreTag&&this.peek()===47){if(n)break;return this.error(g.UNMATCHED_CLOSING_TAG,x(this.clonePosition(),this.clonePosition()))}else if(o===60&&!this.ignoreTag&&er(this.peek()||0)){var s=this.parseTag(t,r);if(s.err)return s;i.push(s.val)}else{var s=this.parseLiteral(t,r);if(s.err)return s;i.push(s.val)}}}return{val:i,err:null}},e.prototype.parseTag=function(t,r){var n=this.clonePosition();this.bump();var i=this.parseTagName();if(this.bumpSpace(),this.bumpIf("/>"))return{val:{type:A.literal,value:"<"+i+"/>",location:x(n,this.clonePosition())},err:null};if(this.bumpIf(">")){var o=this.parseMessage(t+1,r,!0);if(o.err)return o;var s=o.val,a=this.clonePosition();if(this.bumpIf("")?{val:{type:A.tag,value:i,children:s,location:x(n,this.clonePosition())},err:null}:this.error(g.INVALID_TAG,x(a,this.clonePosition())))}else return this.error(g.UNCLOSED_TAG,x(n,this.clonePosition()))}else return this.error(g.INVALID_TAG,x(n,this.clonePosition()))},e.prototype.parseTagName=function(){var t=this.offset();for(this.bump();!this.isEOF()&&Qi(this.char());)this.bump();return this.message.slice(t,this.offset())},e.prototype.parseLiteral=function(t,r){for(var n=this.clonePosition(),i="";;){var o=this.tryParseQuote(r);if(o){i+=o;continue}var s=this.tryParseUnquoted(t,r);if(s){i+=s;continue}var a=this.tryParseLeftAngleBracket();if(a){i+=a;continue}break}var c=x(n,this.clonePosition());return{val:{type:A.literal,value:i,location:c},err:null}},e.prototype.tryParseLeftAngleBracket=function(){return!this.isEOF()&&this.char()===60&&(this.ignoreTag||!Zi(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 Jt.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(),Jt(n))},e.prototype.parseArgument=function(t,r){var n=this.clonePosition();if(this.bump(),this.bumpSpace(),this.isEOF())return this.error(g.EXPECT_ARGUMENT_CLOSING_BRACE,x(n,this.clonePosition()));if(this.char()===125)return this.bump(),this.error(g.EMPTY_ARGUMENT,x(n,this.clonePosition()));var i=this.parseIdentifierIfPossible().value;if(!i)return this.error(g.MALFORMED_ARGUMENT,x(n,this.clonePosition()));if(this.bumpSpace(),this.isEOF())return this.error(g.EXPECT_ARGUMENT_CLOSING_BRACE,x(n,this.clonePosition()));switch(this.char()){case 125:return this.bump(),{val:{type:A.argument,value:i,location:x(n,this.clonePosition())},err:null};case 44:return this.bump(),this.bumpSpace(),this.isEOF()?this.error(g.EXPECT_ARGUMENT_CLOSING_BRACE,x(n,this.clonePosition())):this.parseArgumentOptions(t,r,i,n);default:return this.error(g.MALFORMED_ARGUMENT,x(n,this.clonePosition()))}},e.prototype.parseIdentifierIfPossible=function(){var t=this.clonePosition(),r=this.offset(),n=Kt(this.message,r),i=r+n.length;this.bumpTo(i);var o=this.clonePosition(),s=x(t,o);return{value:n,location:s}},e.prototype.parseArgumentOptions=function(t,r,n,i){var o,s=this.clonePosition(),a=this.parseIdentifierIfPossible().value,c=this.clonePosition();switch(a){case"":return this.error(g.EXPECT_ARGUMENT_TYPE,x(s,c));case"number":case"date":case"time":{this.bumpSpace();var l=null;if(this.bumpIf(",")){this.bumpSpace();var u=this.clonePosition(),p=this.parseSimpleArgStyleIfPossible();if(p.err)return p;var f=zi(p.val);if(f.length===0)return this.error(g.EXPECT_ARGUMENT_STYLE,x(this.clonePosition(),this.clonePosition()));var d=x(u,this.clonePosition());l={style:f,styleLocation:d}}var h=this.tryParseArgumentClose(i);if(h.err)return h;var E=x(i,this.clonePosition());if(l&&hn(l?.style,"::",0)){var S=qi(l.style.slice(2));if(a==="number"){var p=this.parseNumberSkeletonFromString(S,l.styleLocation);return p.err?p:{val:{type:A.number,value:n,location:E,style:p.val},err:null}}else{if(S.length===0)return this.error(g.EXPECT_DATE_TIME_SKELETON,E);var f={type:pe.dateTime,pattern:S,location:l.styleLocation,parsedOptions:this.shouldParseSkeletons?rn(S):{}},N=a==="date"?A.date:A.time;return{val:{type:N,value:n,location:E,style:f},err:null}}}return{val:{type:a==="number"?A.number:a==="date"?A.date:A.time,value:n,location:E,style:(o=l?.style)!==null&&o!==void 0?o:null},err:null}}case"plural":case"selectordinal":case"select":{var P=this.clonePosition();if(this.bumpSpace(),!this.bumpIf(","))return this.error(g.EXPECT_SELECT_ARGUMENT_OPTIONS,x(P,T({},P)));this.bumpSpace();var b=this.parseIdentifierIfPossible(),O=0;if(a!=="select"&&b.value==="offset"){if(!this.bumpIf(":"))return this.error(g.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE,x(this.clonePosition(),this.clonePosition()));this.bumpSpace();var p=this.tryParseDecimalInteger(g.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE,g.INVALID_PLURAL_ARGUMENT_OFFSET_VALUE);if(p.err)return p;this.bumpSpace(),b=this.parseIdentifierIfPossible(),O=p.val}var y=this.tryParsePluralOrSelectOptions(t,a,r,b);if(y.err)return y;var h=this.tryParseArgumentClose(i);if(h.err)return h;var C=x(i,this.clonePosition());return a==="select"?{val:{type:A.select,value:n,options:dn(y.val),location:C},err:null}:{val:{type:A.plural,value:n,options:dn(y.val),offset:O,pluralType:a==="plural"?"cardinal":"ordinal",location:C},err:null}}default:return this.error(g.INVALID_ARGUMENT_TYPE,x(s,c))}},e.prototype.tryParseArgumentClose=function(t){return this.isEOF()||this.char()!==125?this.error(g.EXPECT_ARGUMENT_CLOSING_BRACE,x(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 i=this.clonePosition();if(!this.bumpUntil("'"))return this.error(g.UNCLOSED_QUOTE_IN_ARGUMENT_STYLE,x(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(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=cn(t)}catch{return this.error(g.INVALID_NUMBER_SKELETON,r)}return{val:{type:pe.number,tokens:n,location:r,parsedOptions:this.shouldParseSkeletons?pn(n):{}},err:null}},e.prototype.tryParsePluralOrSelectOptions=function(t,r,n,i){for(var o,s=!1,a=[],c=new Set,l=i.value,u=i.location;;){if(l.length===0){var p=this.clonePosition();if(r!=="select"&&this.bumpIf("=")){var f=this.tryParseDecimalInteger(g.EXPECT_PLURAL_ARGUMENT_SELECTOR,g.INVALID_PLURAL_ARGUMENT_SELECTOR);if(f.err)return f;u=x(p,this.clonePosition()),l=this.message.slice(p.offset,this.offset())}else break}if(c.has(l))return this.error(r==="select"?g.DUPLICATE_SELECT_ARGUMENT_SELECTOR:g.DUPLICATE_PLURAL_ARGUMENT_SELECTOR,u);l==="other"&&(s=!0),this.bumpSpace();var d=this.clonePosition();if(!this.bumpIf("{"))return this.error(r==="select"?g.EXPECT_SELECT_ARGUMENT_SELECTOR_FRAGMENT:g.EXPECT_PLURAL_ARGUMENT_SELECTOR_FRAGMENT,x(this.clonePosition(),this.clonePosition()));var h=this.parseMessage(t+1,r,n);if(h.err)return h;var E=this.tryParseArgumentClose(d);if(E.err)return E;a.push([l,{value:h.val,location:x(d,this.clonePosition())}]),c.add(l),this.bumpSpace(),o=this.parseIdentifierIfPossible(),l=o.value,u=o.location}return a.length===0?this.error(r==="select"?g.EXPECT_SELECT_ARGUMENT_SELECTOR:g.EXPECT_PLURAL_ARGUMENT_SELECTOR,x(this.clonePosition(),this.clonePosition())):this.requiresOtherClause&&!s?this.error(g.MISSING_OTHER_CLAUSE,x(this.clonePosition(),this.clonePosition())):{val:a,err:null}},e.prototype.tryParseDecimalInteger=function(t,r){var n=1,i=this.clonePosition();this.bumpIf("+")||this.bumpIf("-")&&(n=-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 c=x(i,this.clonePosition());return o?(s*=n,$i(s)?{val:s,err:null}:this.error(r,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 r=En(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(hn(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()&&yn(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 er(e){return e>=97&&e<=122||e>=65&&e<=90}function Zi(e){return er(e)||e===47}function Qi(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 yn(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,rt(t)||nt(t))for(var r in t.options)delete t.options[r].location,tr(t.options[r].value);else Ke(t)&&ot(t.style)||(et(t)||tt(t))&&Me(t.style)?delete t.style.location:it(t)&&tr(t.children)})}function _n(e,t){t===void 0&&(t={}),t=T({shouldParseSkeletons:!0,requiresOtherClause:!0},t);var r=new xn(e,t).parse();if(r.err){var n=SyntaxError(g[r.err.kind]);throw n.location=r.err.location,n.originalMessage=r.err.message,n}return t?.captureLocation||tr(r.val),r.val}function ke(e,t){var r=t&&t.cache?t.cache:io,n=t&&t.serializer?t.serializer:no,i=t&&t.strategy?t.strategy:eo;return i(e,{cache:r,serializer:n})}function Ki(e){return e==null||typeof e=="number"||typeof e=="boolean"}function vn(e,t,r,n){var i=Ki(n)?n:r(n),o=t.get(i);return typeof o>"u"&&(o=e.call(this,n),t.set(i,o)),o}function Tn(e,t,r){var n=Array.prototype.slice.call(arguments,3),i=r(n),o=t.get(i);return typeof o>"u"&&(o=e.apply(this,n),t.set(i,o)),o}function rr(e,t,r,n,i){return r.bind(t,e,n,i)}function eo(e,t){var r=e.length===1?vn:Tn;return rr(e,this,r,t.cache.create(),t.serializer)}function to(e,t){return rr(e,this,Tn,t.cache.create(),t.serializer)}function ro(e,t){return rr(e,this,vn,t.cache.create(),t.serializer)}var no=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 io={create:function(){return new nr}},st={variadic:to,monadic:ro};var me;(function(e){e.MISSING_VALUE="MISSING_VALUE",e.INVALID_VALUE="INVALID_VALUE",e.MISSING_INTL_API="MISSING_INTL_API"})(me||(me={}));var Ge=function(e){De(t,e);function t(r,n,i){var o=e.call(this,r)||this;return o.code=n,o.originalMessage=i,o}return t.prototype.toString=function(){return"[formatjs Error: "+this.code+"] "+this.message},t}(Error);var ir=function(e){De(t,e);function t(r,n,i,o){return e.call(this,'Invalid values for "'+r+'": "'+n+'". Options are "'+Object.keys(i).join('", "')+'"',me.INVALID_VALUE,o)||this}return t}(Ge);var bn=function(e){De(t,e);function t(r,n,i){return e.call(this,'Value for "'+r+'" must be of type '+n,me.INVALID_VALUE,i)||this}return t}(Ge);var Sn=function(e){De(t,e);function t(r,n){return e.call(this,'The intl string context variable "'+r+'" was not provided to the string "'+n+'"',me.MISSING_VALUE,n)||this}return t}(Ge);var U;(function(e){e[e.literal=0]="literal",e[e.object=1]="object"})(U||(U={}));function oo(e){return e.length<2?e:e.reduce(function(t,r){var n=t[t.length-1];return!n||n.type!==U.literal||r.type!==U.literal?t.push(r):n.value+=r.value,t},[])}function so(e){return typeof e=="function"}function Fe(e,t,r,n,i,o,s){if(e.length===1&&$t(e[0]))return[{type:U.literal,value:e[0].value}];for(var a=[],c=0,l=e;c0?e.substring(0,n):"";let i=wn(e.split("").reverse().join("")),o=r-i,s=e.substring(o,o+1),a=o+(s==="."||s===","?1:0);t.suffix=i>0?e.substring(a,r):"",t.mask=e.substring(n,a),t.maskHasNegativeSign=t.mask.charAt(0)==="-",t.maskHasPositiveSign=t.mask.charAt(0)==="+";let c=t.mask.match(fo);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 mo(e,t,r){let n=!1,i={value:e};e<0&&(n=!0,i.value=-i.value),i.sign=n?"-":"",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,ho(i,t),(i.result==="0"||i.result==="")&&(n=!1,i.sign=""),!n&&t.maskHasPositiveSign?i.sign="+":n&&t.maskHasPositiveSign?i.sign="-":n&&(i.sign=r&&r.enforceMaskSign&&!t.maskHasNegativeSign?"":"-"),i}function ho(e,t){e.result="";let r=t.integer.split(t.separator),n=r.join(""),i=n&&n.indexOf("0");if(i>-1)for(;e.integer.lengthMath.round(e*20)/20},sr=(e,t)=>({accept:e,round:t}),_o=[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={[I.YEAR]:{[L.MONTHLY]:Ve.MONTH,[L.ANNUAL]:Ve.YEAR},[I.MONTH]:{[L.MONTHLY]:Ve.MONTH}},vo=(e,t)=>e.indexOf(`'${t}'`)===0,To=(e,t=!0)=>{let r=e.replace(/'.*?'/,"").trim(),n=Un(r);return!!n?t||(r=r.replace(/[,\.]0+/,n)):r=r.replace(/\s?(#.*0)(?!\s)?/,"$&"+So(e)),r},bo=e=>{let t=Po(e),r=vo(e,t),n=e.replace(/'.*?'/,""),i=Cn.test(n)||In.test(n);return{currencySymbol:t,isCurrencyFirst:r,hasCurrencySpace:i}},Rn=e=>e.replace(Cn,Nn).replace(In,Nn),So=e=>e.match(/#(.?)#/)?.[1]===Ln?go:Ln,Po=e=>e.match(/'(.*?)'/)?.[1]??"",Un=e=>e.match(/0(.?)0/)?.[1]??"";function at({formatString:e,price:t,usePrecision:r,isIndianPrice:n=!1},i,o=s=>s){let{currencySymbol:s,isCurrencyFirst:a,hasCurrencySpace:c}=bo(e),l=r?Un(e):"",u=To(e,r),p=r?2:0,f=o(t,{currencySymbol:s}),d=n?f.toLocaleString("hi-IN",{minimumFractionDigits:p,maximumFractionDigits:p}):On(u,f),h=r?d.lastIndexOf(l):d.length,E=d.substring(0,h),S=d.substring(h+1);return{accessiblePrice:e.replace(/'.*?'/,"SYMBOL").replace(/#.*0/,d).replace(/SYMBOL/,s),currencySymbol:s,decimals:S,decimalsDelimiter:l,hasCurrencySpace:c,integer:E,isCurrencyFirst:a,recurrenceTerm:i}}var Dn=e=>{let{commitment:t,term:r,usePrecision:n}=e,i=xo[r]??1;return at(e,i>1?Ve.MONTH:ar[t]?.[r],(o,{currencySymbol:s})=>{let a={divisor:i,price:o,usePrecision:n},{round:c}=_o.find(({accept:u})=>u(a));if(!c)throw new Error(`Missing rounding rule for: ${JSON.stringify(a)}`);return(yo[s]??(u=>u))(c(a))})},Mn=({commitment:e,term:t,...r})=>at(r,ar[e]?.[t]),kn=e=>{let{commitment:t,term:r}=e;return t===I.YEAR&&r===L.MONTHLY?at(e,Ve.YEAR,n=>n*12):at(e,ar[t]?.[r])};var Ao={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}"},wo=Jr("ConsonantTemplates/price"),Oo=/<.+?>/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"},he={perUnitLabel:"perUnitLabel",perUnitAriaLabel:"perUnitAriaLabel",recurrenceLabel:"recurrenceLabel",recurrenceAriaLabel:"recurrenceAriaLabel",taxExclusiveLabel:"taxExclusiveLabel",taxInclusiveLabel:"taxInclusiveLabel",strikethroughAriaLabel:"strikethroughAriaLabel"},Lo="TAX_EXCLUSIVE",No=e=>Zr(e)?Object.entries(e).filter(([,t])=>Te(t)||Ze(t)||t===!0).reduce((t,[r,n])=>t+` ${r}${n===!0?"":'="'+qr(n)+'"'}`,""):"",z=(e,t,r,n=!1)=>`${n?Rn(t):t??""}`;function Co(e,{accessibleLabel:t,currencySymbol:r,decimals:n,decimalsDelimiter:i,hasCurrencySpace:o,integer:s,isCurrencyFirst:a,recurrenceLabel:c,perUnitLabel:l,taxInclusivityLabel:u},p={}){let f=z(W.currencySymbol,r),d=z(W.currencySpace,o?" ":""),h="";return a&&(h+=f+d),h+=z(W.integer,s),h+=z(W.decimalsDelimiter,i),h+=z(W.decimals,n),a||(h+=d+f),h+=z(W.recurrence,c,null,!0),h+=z(W.unitType,l,null,!0),h+=z(W.taxInclusivity,u,!0),z(e,h,{...p,"aria-label":t})}var de=({displayOptical:e=!1,displayStrikethrough:t=!1,displayAnnual:r=!1}={})=>({country:n,displayFormatted:i=!0,displayRecurrence:o=!0,displayPerUnit:s=!1,displayTax:a=!1,language:c,literals:l={}}={},{commitment:u,formatString:p,price:f,priceWithoutDiscount:d,taxDisplay:h,taxTerm:E,term:S,usePrecision:N}={},P={})=>{Object.entries({country:n,formatString:p,language:c,price:f}).forEach(([re,wt])=>{if(wt==null)throw new Error(`Argument "${re}" is missing`)});let b={...Ao,...l},O=`${c.toLowerCase()}-${n.toUpperCase()}`;function y(re,wt){let Ot=b[re];if(Ot==null)return"";try{return new An(Ot.replace(Oo,""),O).format(wt)}catch{return wo.error("Failed to format literal:",Ot),""}}let C=t&&d?d:f,M=e?Dn:Mn;r&&(M=kn);let{accessiblePrice:X,recurrenceTerm:B,...R}=M({commitment:u,formatString:p,term:S,price:e?f:C,usePrecision:N,isIndianPrice:n==="IN"}),j=X,ae="";if(v(o)&&B){let re=y(he.recurrenceAriaLabel,{recurrenceTerm:B});re&&(j+=" "+re),ae=y(he.recurrenceLabel,{recurrenceTerm:B})}let le="";if(v(s)){le=y(he.perUnitLabel,{perUnit:"LICENSE"});let re=y(he.perUnitAriaLabel,{perUnit:"LICENSE"});re&&(j+=" "+re)}let te="";v(a)&&E&&(te=y(h===Lo?he.taxExclusiveLabel:he.taxInclusiveLabel,{taxTerm:E}),te&&(j+=" "+te)),t&&(j=y(he.strikethroughAriaLabel,{strikethroughPrice:j}));let q=W.container;if(e&&(q+=" "+W.containerOptical),t&&(q+=" "+W.containerStrikethrough),r&&(q+=" "+W.containerAnnual),v(i))return Co(q,{...R,accessibleLabel:j,recurrenceLabel:ae,perUnitLabel:le,taxInclusivityLabel:te},P);let{currencySymbol:ye,decimals:He,decimalsDelimiter:Xe,hasCurrencySpace:Ce,integer:At,isCurrencyFirst:ai}=R,_e=[At,Xe,He];ai?(_e.unshift(Ce?"\xA0":""),_e.unshift(ye)):(_e.push(Ce?"\xA0":""),_e.push(ye)),_e.push(ae,le,te);let ci=_e.join("");return z(q,ci,P)},Gn=()=>(e,t,r)=>{let i=(e.displayOldPrice===void 0||v(e.displayOldPrice))&&t.priceWithoutDiscount&&t.priceWithoutDiscount!=t.price;return`${de()(e,t,r)}${i?" "+de({displayStrikethrough:!0})(e,t,r):""}`};var cr=de(),ur=Gn(),lr=de({displayOptical:!0}),fr=de({displayStrikethrough:!0}),pr=de({displayAnnual:!0});var Io=(e,t)=>{if(!(!Se(e)||!Se(t)))return Math.floor((t-e)/t*100)},Fn=()=>(e,t,r)=>{let{price:n,priceWithoutDiscount:i}=t,o=Io(n,i);return o===void 0?'':`${o}%`};var mr=Fn();var hr="ABM",dr="PUF",Er="M2M",gr="PERPETUAL",Vn="P3Y",Ro="TAX_INCLUSIVE_DETAILS",Uo="TAX_EXCLUSIVE",jn={ABM:hr,PUF:dr,M2M:Er,PERPETUAL:gr,P3Y:Vn},tc={[hr]:{commitment:I.YEAR,term:L.MONTHLY},[dr]:{commitment:I.YEAR,term:L.ANNUAL},[Er]:{commitment:I.MONTH,term:L.MONTHLY},[gr]:{commitment:I.PERPETUAL,term:void 0},[Vn]:{commitment:I.THREE_MONTHS,term:L.P3Y}},Wn="Value is not an offer",ct=e=>{if(typeof e!="object")return Wn;let{commitment:t,term:r}=e,n=Do(t,r);return{...e,planType:n}};var Do=(e,t)=>{if(e===void 0)return Wn;if(e===""&&t==="")return"";let r="";return e===I.YEAR?t===L.MONTHLY?r=hr:t===L.ANNUAL&&(r=dr):e===I.MONTH?t===L.MONTHLY&&(r=Er):e===I.PERPETUAL&&(r=gr),r};function xr(e){let{priceDetails:t}=e,{price:r,priceWithoutDiscount:n,priceWithoutTax:i,priceWithoutDiscountAndTax:o,taxDisplay:s}=t;if(s!==Ro)return e;let a={...e,priceDetails:{...t,price:i??r,priceWithoutDiscount:o??n,taxDisplay:Uo}};return a.offerType==="TRIAL"&&a.priceDetails.price===0&&(a.priceDetails.price=a.priceDetails.priceWithoutDiscount),a}var{freeze:Ee}=Object,Z=Ee({...fe}),Q=Ee({...$}),H=Ee({...Y}),yr=Ee({...I}),we=Ee({...Re}),_r=Ee({...jn}),vr=Ee({...L});var Or={};li(Or,{CLASS_NAME_FAILED:()=>ut,CLASS_NAME_PENDING:()=>lt,CLASS_NAME_RESOLVED:()=>ft,ERROR_MESSAGE_BAD_REQUEST:()=>Tr,ERROR_MESSAGE_MISSING_LITERALS_URL:()=>Sr,ERROR_MESSAGE_OFFER_NOT_FOUND:()=>br,EVENT_TYPE_ERROR:()=>Mo,EVENT_TYPE_FAILED:()=>pt,EVENT_TYPE_PENDING:()=>mt,EVENT_TYPE_READY:()=>ge,EVENT_TYPE_RESOLVED:()=>ht,LOG_NAMESPACE:()=>Pr,PARAM_AOS_API_KEY:()=>ko,PARAM_ENV:()=>Ar,PARAM_LANDSCAPE:()=>wr,PARAM_WCS_API_KEY:()=>Go,STATE_FAILED:()=>J,STATE_PENDING:()=>K,STATE_RESOLVED:()=>ee,TAG_NAME_SERVICE:()=>oe});var ut="placeholder-failed",lt="placeholder-pending",ft="placeholder-resolved",Tr="Bad WCS request",br="Commerce offer not found",Sr="Literals URL not provided",Mo="wcms:commerce:error",pt="wcms:placeholder:failed",mt="wcms:placeholder:pending",ge="wcms:commerce:ready",ht="wcms:placeholder:resolved",Pr="wcms/commerce",Ar="commerce.env",wr="commerce.landscape",ko="commerce.aosKey",Go="commerce.wcsKey",J="failed",K="pending",ee="resolved",oe="wcms-commerce";var Lr={clientId:"merch-at-scale",delimiter:"\xB6",ignoredProperties:["analytics","literals"],serializableTypes:["Array","Object"],sampleRate:30,tags:"consumer=milo/commerce"},Hn=new Set,Fo=e=>e instanceof Error||typeof e.originatingRequest=="string";function Xn(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:i,status:o}=e;return[n,o,i].filter(s=>s).join(" ")}let r=e[Symbol.toStringTag]??Object.getPrototypeOf(e).constructor.name;if(!Lr.serializableTypes.includes(r))return r}return e}function Vo(e,t){if(!Lr.ignoredProperties.includes(e))return Xn(t)}var Nr={append(e){let{delimiter:t,sampleRate:r,tags:n,clientId:i}=Lr,{message:o,params:s}=e,a=[],c=o,l=[];s.forEach(f=>{f!=null&&(Fo(f)?a:l).push(f)}),a.length&&(c+=" ",c+=a.map(Xn).join(" "));let{pathname:u,search:p}=window.location;c+=`${t}page=`,c+=u+p,l.length&&(c+=`${t}facts=`,c+=JSON.stringify(l,Vo)),Hn.has(c)||(Hn.add(c),window.lana?.log(c,{sampleRate:r,tags:n,clientId:i}))}};var _=Object.freeze({checkoutClientId:"adobe_com",checkoutWorkflow:Z.V3,checkoutWorkflowStep:Q.EMAIL,country:"US",displayOldPrice:!0,displayPerUnit:!1,displayRecurrence:!0,displayTax:!1,domainSwitch:!1,env:H.PRODUCTION,forceTaxExclusive:!1,language:"en",entitlement:!1,extraOptions:{},modal:!1,promotionCode:"",quantity:1,wcsApiKey:"wcms-commerce-ims-ro-user-milo",wcsBufferDelay:1,wcsEnv:we.PRODUCTION,landscape:G.PUBLISHED,wcsBufferLimit:1});function Bn(e,{once:t=!1}={}){let r=null;function n(){let i=document.querySelector(oe);i!==r&&(r=i,i&&e(i))}return document.addEventListener(ge,n,{once:t}),ue(n),()=>document.removeEventListener(ge,n)}function je(e,{country:t,forceTaxExclusive:r,perpetual:n}){let i;if(e.length<2)i=e;else{let o=t==="GB"||n?"EN":"MULT",[s,a]=e;i=[s.language===o?s:a]}return r&&(i=i.map(xr)),i}var ue=e=>window.setTimeout(e);function Oe(e,t=1){if(e==null)return[t];let r=(Array.isArray(e)?e:String(e).split(",")).map(Ae).filter(Se);return r.length||(r=[t]),r}function dt(e){return e==null?[]:(Array.isArray(e)?e:String(e).split(",")).filter(Xt)}function F(){return window.customElements.get(oe)?.instance}var jo="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"},se=Object.freeze({LOCAL:"local",PROD:"prod",STAGE:"stage"});function Yn({locale:e={}}={}){if(!e.prefix)return{country:_.country,language:_.language,locale:jo};let t=e.prefix.replace("/","")??"",[r=_.country,n=_.language]=(m[t]??t).split("_",2);return r=r.toUpperCase(),n=n.toLowerCase(),{country:r,language:n,locale:`${n}_${r}`}}function Cr(e={}){let{commerce:t={},locale:r=void 0}=e,i=(e.env?.name===se.PROD?se.PROD:ie(w(Ar,t,{metadata:!1}),se,se.PROD))===se.STAGE?H.STAGE:H.PRODUCTION,o=w("checkoutClientId",t)??_.checkoutClientId,s=ie(w("checkoutWorkflow",t),Z,_.checkoutWorkflow),a=Q.CHECKOUT;s===Z.V3&&(a=ie(w("checkoutWorkflowStep",t),Q,_.checkoutWorkflowStep));let c=v(w("displayOldPrice",t),_.displayOldPrice),l=v(w("displayPerUnit",t),_.displayPerUnit),u=v(w("displayRecurrence",t),_.displayRecurrence),p=v(w("displayTax",t),_.displayTax),f=v(w("entitlement",t),_.entitlement),d=v(w("modal",t),_.modal),h=v(w("forceTaxExclusive",t),_.forceTaxExclusive),E=w("promotionCode",t)??_.promotionCode,S=Oe(w("quantity",t)),N=w("wcsApiKey",t)??_.wcsApiKey,P=e.env?.name===se.PROD?G.PUBLISHED:ie(w(wr,t),G,_.landscape),b=Ae(w("wcsBufferDelay",t),_.wcsBufferDelay),O=Ae(w("wcsBufferLimit",t),_.wcsBufferLimit),y=v(w("domain.switch",t),!1);return{...Yn({locale:r}),displayOldPrice:c,checkoutClientId:o,checkoutWorkflow:s,checkoutWorkflowStep:a,displayPerUnit:l,displayRecurrence:u,displayTax:p,entitlement:f,extraOptions:_.extraOptions,modal:d,env:i,forceTaxExclusive:h,priceLiteralsURL:t.priceLiteralsURL,priceLiteralsPromise:t.priceLiteralsPromise,promotionCode:E,quantity:S,wcsApiKey:N,wcsBufferDelay:b,wcsBufferLimit:O,wcsEnv:i===H.STAGE?we.STAGE:we.PRODUCTION,landscape:P,domainSwitch:y}}var qn="debug",Wo="error",Ho="info",Xo="warn",Bo=Date.now(),Ir=new Set,Rr=new Set,$n=new Map,We=Object.freeze({DEBUG:qn,ERROR:Wo,INFO:Ho,WARN:Xo}),zn={append({level:e,message:t,params:r,timestamp:n,source:i}){console[e](`${n}ms [${i}] %c${t}`,"font-weight: bold;",...r)}},Zn={filter:({level:e})=>e!==qn},Yo={filter:()=>!1};function $o(e,t,r,n,i){return{level:e,message:t,namespace:r,get params(){if(n.length===1){let[o]=n;ce(o)&&(n=o(),Array.isArray(n)||(n=[n]))}return n},source:i,timestamp:Date.now()-Bo}}function qo(e){[...Rr].every(t=>t(e))&&Ir.forEach(t=>t(e))}function Qn(e){let t=($n.get(e)??0)+1;$n.set(e,t);let r=`${e} #${t}`,n=o=>(s,...a)=>qo($o(o,s,e,a,r)),i=Object.seal({id:r,namespace:e,module(o){return Qn(`${i.namespace}/${o}`)},debug:n(We.DEBUG),error:n(We.ERROR),info:n(We.INFO),warn:n(We.WARN)});return i}function Et(...e){e.forEach(t=>{let{append:r,filter:n}=t;ce(n)?Rr.add(n):ce(r)&&Ir.add(r)})}function zo(e={}){let{name:t}=e,r=v(w("commerce.debug",{search:!0,storage:!0}),t===se.LOCAL);return Et(r?zn:Zn),t===se.PROD&&Et(Nr),D}function Zo(){Ir.clear(),Rr.clear()}var D={...Qn(Pr),Level:We,Plugins:{consoleAppender:zn,debugFilter:Zn,quietFilter:Yo,lanaAppender:Nr},init:zo,reset:Zo,use:Et};var Qo={CLASS_NAME_FAILED:ut,CLASS_NAME_PENDING:lt,CLASS_NAME_RESOLVED:ft,EVENT_TYPE_FAILED:pt,EVENT_TYPE_PENDING:mt,EVENT_TYPE_RESOLVED:ht,STATE_FAILED:J,STATE_PENDING:K,STATE_RESOLVED:ee},Jo={[J]:ut,[K]:lt,[ee]:ft},Ko={[J]:pt,[K]:mt,[ee]:ht},yt=new WeakMap;function V(e){if(!yt.has(e)){let t=D.module(e.constructor.is);yt.set(e,{changes:new Map,connected:!1,dispose:be,error:void 0,log:t,options:void 0,promises:[],state:K,timer:null,value:void 0,version:0})}return yt.get(e)}function gt(e){let t=V(e),{error:r,promises:n,state:i}=t;(i===ee||i===J)&&(t.promises=[],i===ee?n.forEach(({resolve:o})=>o(e)):i===J&&n.forEach(({reject:o})=>o(r))),e.dispatchEvent(new CustomEvent(Ko[i],{bubbles:!0}))}function xt(e){let t=yt.get(e);[J,K,ee].forEach(r=>{e.classList.toggle(Jo[r],r===t.state)})}var es={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=Bn(()=>this.requestUpdate(!0))},disconnectedCallback(){let e=V(this);e.connected&&(e.connected=!1,e.log.debug("Disconnected:",{element:this})),e.dispose(),e.dispose=be},onceSettled(){let{error:e,promises:t,state:r}=V(this);return ee===r?Promise.resolve(this):J===r?Promise.reject(e):new Promise((n,i)=>{t.push({resolve:n,reject:i})})},toggleResolved(e,t,r){let n=V(this);return e!==n.version?!1:(r!==void 0&&(n.options=r),n.state=ee,n.value=t,xt(this),this.log.debug("Resolved:",{element:this,value:t}),ue(()=>gt(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=J,xt(this),n.log.error("Failed:",{element:this,error:t}),ue(()=>gt(this)),!0)},togglePending(e){let t=V(this);return t.version++,e&&(t.options=e),t.state=K,xt(this),ue(()=>gt(this)),t.version},requestUpdate(e=!1){if(!this.isConnected||!F())return;let t=V(this);if(t.timer)return;let{error:r,options:n,state:i,value:o,version:s}=t;t.state=K,t.timer=ue(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===K&&t.version===s&&(t.state=i,t.error=r,t.value=o,xt(this),gt(this))}catch(c){this.toggleFailed(t.version,c,n)}})}};function Jn(e={}){return Object.entries(e).forEach(([t,r])=>{(r==null||r===""||r?.length===0)&&delete e[t]}),e}function _t(e,t={}){let{tag:r,is:n}=e,i=document.createElement(r,{is:n});return i.setAttribute("is",n),Object.assign(i.dataset,Jn(t)),i}function vt(e){let{tag:t,is:r,prototype:n}=e,i=window.customElements.get(r);return i||(Object.defineProperties(n,Object.getOwnPropertyDescriptors(es)),i=Object.defineProperties(e,Object.getOwnPropertyDescriptors(Qo)),window.customElements.define(r,i,{extends:t})),i}function Tt(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,Jn(t)),e):null}var ts="download",rs="upgrade",xe,Le=class Le extends HTMLAnchorElement{constructor(){super();Fr(this,xe,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(r={},n=""){let i=F();if(!i)return null;let{checkoutMarketSegment:o,checkoutWorkflow:s,checkoutWorkflowStep:a,entitlement:c,upgrade:l,modal:u,perpetual:p,promotionCode:f,quantity:d,wcsOsi:h,extraOptions:E}=i.collectCheckoutOptions(r),S=_t(Le,{checkoutMarketSegment:o,checkoutWorkflow:s,checkoutWorkflowStep:a,entitlement:c,upgrade:l,modal:u,perpetual:p,promotionCode:f,quantity:d,wcsOsi:h,extraOptions:E});return n&&(S.innerHTML=`${n}`),S}static getCheckoutLinks(r){return Tt(Le,r)}get isCheckoutLink(){return!0}get placeholder(){return this}clickHandler(r){var n;(n=Lt(this,xe))==null||n.call(this,r)}async render(r={}){if(!this.isConnected)return!1;let n=F();if(!n)return!1;this.dataset.imsCountry||n.imsCountryPromise.then(u=>{u&&(this.dataset.imsCountry=u)},be);let i=n.collectCheckoutOptions(r,this.placeholder);if(!i.wcsOsi.length)return!1;let o;try{o=JSON.parse(i.extraOptions??"{}")}catch(u){this.placeholder.log.error("cannot parse exta checkout options",u)}let s=this.placeholder.togglePending(i);this.href="";let a=n.resolveOfferSelectors(i),c=await Promise.all(a);c=c.map(u=>je(u,i));let l=await n.buildCheckoutAction(c.flat(),{...o,...i});return this.renderOffers(c.flat(),i,{},l,s)}renderOffers(r,n,i={},o=void 0,s=void 0){if(!this.isConnected)return!1;let a=F();if(!a)return!1;if(n={...JSON.parse(this.placeholder.dataset.extraOptions??"null"),...n,...i},s??(s=this.placeholder.togglePending(n)),Lt(this,xe)&&Nt(this,xe,void 0),o){this.classList.remove(ts,rs),this.placeholder.toggleResolved(s,r,n);let{url:l,text:u,className:p,handler:f}=o;return l&&(this.href=l),u&&(this.firstElementChild.innerHTML=u),p&&this.classList.add(...p.split(" ")),f&&(this.setAttribute("href","#"),Nt(this,xe,f.bind(this))),!0}else if(r.length){if(this.placeholder.toggleResolved(s,r,n)){let l=a.buildCheckoutURL(r,n);return this.setAttribute("href",l),!0}}else{let l=new Error(`Not provided: ${n?.wcsOsi??"-"}`);if(this.placeholder.toggleFailed(s,l,n))return this.setAttribute("href","#"),!0}return!1}updateOptions(r={}){let n=F();if(!n)return!1;let{checkoutMarketSegment:i,checkoutWorkflow:o,checkoutWorkflowStep:s,entitlement:a,upgrade:c,modal:l,perpetual:u,promotionCode:p,quantity:f,wcsOsi:d}=n.collectCheckoutOptions(r);return bt(this,{checkoutMarketSegment:i,checkoutWorkflow:o,checkoutWorkflowStep:s,entitlement:a,upgrade:c,modal:l,perpetual:u,promotionCode:p,quantity:f,wcsOsi:d}),!0}};xe=new WeakMap,ne(Le,"is","checkout-link"),ne(Le,"tag","a");var Ur=Le,St=vt(Ur);var Kn=[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],ns={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]},Ne=class Ne 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=F();if(!r)return null;let{displayOldPrice:n,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:c,promotionCode:l,quantity:u,template:p,wcsOsi:f}=r.collectPriceOptions(t);return _t(Ne,{displayOldPrice:n,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:c,promotionCode:l,quantity:u,template:p,wcsOsi:f})}static getInlinePrices(t){return Tt(Ne,t)}get isInlinePrice(){return!0}get placeholder(){return this}resolveDisplayTaxForGeoAndSegment(t,r,n,i){let o=`${t}_${r}`;if(Kn.includes(t)||Kn.includes(o))return!0;let s=ns[`${n}_${i}`];return s?!!(s.includes(t)||s.includes(o)):!1}async resolveDisplayTax(t,r){let[n]=await t.resolveOfferSelectors(r),i=je(await n,r);if(i?.length){let{country:o,language:s}=r,a=i[0],[c=""]=a.marketSegments;return this.resolveDisplayTaxForGeoAndSegment(o,s,a.customerSegment,c)}}async render(t={}){if(!this.isConnected)return!1;let r=F();if(!r)return!1;let n=r.collectPriceOptions(t,this.placeholder);if(!n.wcsOsi.length)return!1;let i=this.placeholder.togglePending(n);this.innerHTML="";let[o]=r.resolveOfferSelectors(n);return this.renderOffers(je(await o,n),n,i)}renderOffers(t,r={},n=void 0){if(!this.isConnected)return;let i=F();if(!i)return!1;let o=i.collectPriceOptions({...this.dataset,...r});if(n??(n=this.placeholder.togglePending(o)),t.length){if(this.placeholder.toggleResolved(n,t,o))return this.innerHTML=i.buildPriceHTML(t,o),!0}else{let s=new Error(`Not provided: ${o?.wcsOsi??"-"}`);if(this.placeholder.toggleFailed(n,s,o))return this.innerHTML="",!0}return!1}updateOptions(t){let r=F();if(!r)return!1;let{displayOldPrice:n,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:c,promotionCode:l,quantity:u,template:p,wcsOsi:f}=r.collectPriceOptions(t);return bt(this,{displayOldPrice:n,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:c,promotionCode:l,quantity:u,template:p,wcsOsi:f}),!0}};ne(Ne,"is","inline-price"),ne(Ne,"tag","span");var Dr=Ne,Pt=vt(Dr);function ei({providers:e,settings:t},r){let n=D.module("checkout");function i(l,u){let{checkoutClientId:p,checkoutWorkflow:f,checkoutWorkflowStep:d,country:h,language:E,promotionCode:S,quantity:N}=t,{checkoutMarketSegment:P,checkoutWorkflow:b=f,checkoutWorkflowStep:O=d,imsCountry:y,country:C=y??h,language:M=E,quantity:X=N,entitlement:B,upgrade:R,modal:j,perpetual:ae,promotionCode:le=S,wcsOsi:te,extraOptions:q,...ye}=Object.assign({},u?.dataset??{},l??{}),He=ie(b,Z,_.checkoutWorkflow),Xe=Q.CHECKOUT;He===Z.V3&&(Xe=ie(O,Q,_.checkoutWorkflowStep));let Ce=Pe({...ye,extraOptions:q,checkoutClientId:p,checkoutMarketSegment:P,country:C,quantity:Oe(X,_.quantity),checkoutWorkflow:He,checkoutWorkflowStep:Xe,language:M,entitlement:v(B),upgrade:v(R),modal:v(j),perpetual:v(ae),promotionCode:Ue(le).effectivePromoCode,wcsOsi:dt(te)});if(u)for(let At of e.checkout)At(u,Ce);return Ce}async function o(l,u){let p=F(),f=await r.getCheckoutAction?.(l,u,p.imsSignedInPromise);return f||null}function s(l,u){if(!Array.isArray(l)||!l.length||!u)return"";let{env:p,landscape:f}=t,{checkoutClientId:d,checkoutMarketSegment:h,checkoutWorkflow:E,checkoutWorkflowStep:S,country:N,promotionCode:P,quantity:b,...O}=i(u),y=window.frameElement?"if":"fp",C={checkoutPromoCode:P,clientId:d,context:y,country:N,env:p,items:[],marketSegment:h,workflowStep:S,landscape:f,...O};if(l.length===1){let[{offerId:M,offerType:X,productArrangementCode:B}]=l,{marketSegments:[R]}=l[0];Object.assign(C,{marketSegment:R,offerType:X,productArrangementCode:B}),C.items.push(b[0]===1?{id:M}:{id:M,quantity:b[0]})}else C.items.push(...l.map(({offerId:M},X)=>({id:M,quantity:b[X]??_.quantity})));return Mt(E,C)}let{createCheckoutLink:a,getCheckoutLinks:c}=St;return{CheckoutLink:St,CheckoutWorkflow:Z,CheckoutWorkflowStep:Q,buildCheckoutAction:o,buildCheckoutURL:s,collectCheckoutOptions:i,createCheckoutLink:a,getCheckoutLinks:c}}function is({interval:e=200,maxAttempts:t=25}={}){let r=D.module("ims");return new Promise(n=>{r.debug("Waing for IMS to be ready");let i=0;function o(){window.adobeIMS?.initialized?n():++i>t?(r.debug("Timeout"),n()):setTimeout(o,e)}o()})}function os(e){return e.then(()=>window.adobeIMS?.isSignedInUser()??!1)}function ss(e){let t=D.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 ti({}){let e=is(),t=os(e),r=ss(t);return{imsReadyPromise:e,imsSignedInPromise:t,imsCountryPromise:r}}function as(e){if(!e.priceLiteralsURL)throw new Error(Sr);return new Promise(t=>{window.fetch(e.priceLiteralsURL).then(r=>{r.json().then(({data:n})=>{t(n)})})})}async function ri(e){let r=await(e.priceLiteralsPromise||as(e));if(Array.isArray(r)){let n=o=>r.find(s=>ze(s.lang,o)),i=n(e.language)??n(_.language);if(i)return Object.freeze(i)}return{}}function ni({literals:e,providers:t,settings:r}){function n(a,c){let{country:l,displayOldPrice:u,displayPerUnit:p,displayRecurrence:f,displayTax:d,forceTaxExclusive:h,language:E,promotionCode:S,quantity:N}=r,{displayOldPrice:P=u,displayPerUnit:b=p,displayRecurrence:O=f,displayTax:y=d,forceTaxExclusive:C=h,country:M=l,language:X=E,perpetual:B,promotionCode:R=S,quantity:j=N,template:ae,wcsOsi:le,...te}=Object.assign({},c?.dataset??{},a??{}),q=Pe({...te,country:M,displayOldPrice:v(P),displayPerUnit:v(b),displayRecurrence:v(O),displayTax:v(y),forceTaxExclusive:v(C),language:X,perpetual:v(B),promotionCode:Ue(R).effectivePromoCode,quantity:Oe(j,_.quantity),template:ae,wcsOsi:dt(le)});if(c)for(let ye of t.price)ye(c,q);return q}function i(a,c){if(!Array.isArray(a)||!a.length||!c)return"";let{template:l}=c,u;switch(l){case"discount":u=mr;break;case"strikethrough":u=fr;break;case"optical":u=lr;break;case"annual":u=pr;break;default:u=c.promotionCode?ur:cr}let p=n(c);p.literals=Object.assign({},e.price,Pe(c.literals??{}));let[f]=a;return f={...f,...f.priceDetails},u(p,f)}let{createInlinePrice:o,getInlinePrices:s}=Pt;return{InlinePrice:Pt,buildPriceHTML:i,collectPriceOptions:n,createInlinePrice:o,getInlinePrices:s}}var Mr="_acom",ii={[H.PRODUCTION]:"https://wcs.adobe.com",[H.STAGE]:"https://wcs.stage.adobe.com",[H.PRODUCTION+Mr]:"https://www.adobe.com",[H.STAGE+Mr]:"https://www.stage.adobe.com"};function oi({settings:e}){let t=D.module("wcs"),{env:r,domainSwitch:n,wcsApiKey:i}=e,o=n?ii[r+Mr]:ii[r],s={apiKey:i,baseUrl:o,fetch:window.fetch.bind(window)},a=qe(s),c=new Map,l=new Map,u;async function p(h,E,S=!0){let N=br;try{t.debug("Fetching:",h),h.offerSelectorIds=h.offerSelectorIds.sort();let{data:P}=await a(h,{apiKey:i,environment:e.wcsEnv,landscape:r===H.STAGE?"ALL":e.landscape},({resolvedOffers:O})=>({offers:O.map(ct)}));t.debug("Fetched:",h,P);let{offers:b}=P??{};E.forEach(({resolve:O},y)=>{let C=b.filter(({offerSelectorIds:M})=>M.includes(y)).flat();C.length&&(E.delete(y),O(C))})}catch(P){P.status===404&&h.offerSelectorIds.length>1?(t.debug("Multi-osi 404, fallback to fetch-by-one strategy"),await Promise.allSettled(h.offerSelectorIds.map(b=>p({...h,offerSelectorIds:[b]},E,!1)))):(t.error("Failed:",h,P),N=Tr)}S&&E.size&&(t.debug("Missing:",{offerSelectorIds:[...E.keys()]}),E.forEach(P=>{P.reject(new Error(N))}))}function f(){clearTimeout(u);let h=[...l.values()];l.clear(),h.forEach(({options:E,promises:S})=>p(E,S))}function d({country:h,language:E,perpetual:S=!1,promotionCode:N="",wcsOsi:P=[]}){let b=`${E}_${h}`;h!=="GB"&&(E=S?"EN":"MULT");let O=[h,E,N].filter(y=>y).join("-").toLowerCase();return P.map(y=>{let C=`${y}-${O}`;if(!c.has(C)){let M=new Promise((X,B)=>{let R=l.get(O);if(!R){let j={country:h,locale:b,offerSelectorIds:[]};h!=="GB"&&(j.language=E),R={options:j,promises:new Map},l.set(O,R)}N&&(R.options.promotionCode=N),R.options.offerSelectorIds.push(y),R.promises.set(y,{resolve:X,reject:B}),R.options.offerSelectorIds.length>=e.wcsBufferLimit?f():(t.debug("Queued:",R.options),u||(u=setTimeout(f,e.wcsBufferDelay)))});c.set(C,M)}return c.get(C)})}return{WcsCommitment:yr,WcsPlanType:_r,WcsTerm:vr,resolveOfferSelectors:d}}var k=class extends HTMLElement{get isWcmsCommerce(){return!0}};ne(k,"instance"),ne(k,"promise",null);window.customElements.define(oe,k);async function cs(e,t){let r=D.init(e.env).module("service");r.debug("Activating:",e);let n={price:{}},i=Object.freeze(Cr(e));try{n.price=await ri(i)}catch(c){r.warn("Price literals were not fetched:",c)}let o={checkout:new Set,price:new Set},s=document.createElement(oe),a={literals:n,providers:o,settings:i};return k.instance=Object.defineProperties(s,Object.getOwnPropertyDescriptors({...ei(a,t),...ti(a),...ni(a),...oi(a),...Or,Log:D,get defaults(){return _},get literals(){return n},get log(){return D},get providers(){return{checkout(c){return o.checkout.add(c),()=>o.checkout.delete(c)},price(c){return o.price.add(c),()=>o.price.delete(c)}}},get settings(){return i}})),r.debug("Activated:",{literals:n,settings:i,element:s}),document.head.append(s),ue(()=>{let c=new CustomEvent(ge,{bubbles:!0,cancelable:!1,detail:k.instance});k.instance.dispatchEvent(c)}),k.instance}function si(){document.head.querySelector(oe)?.remove(),k.promise=null,D.reset()}function us(e,t){if(ce(e)){let r=ce(t)?t():{};return r.force&&si(),k.promise??(k.promise=cs(e(),r))}return k.promise?k.promise:new Promise(r=>{let n=i=>{r(i.detail)};document.head.addEventListener(ge,n,{once:!0})})}export{St as CheckoutLink,Z as CheckoutWorkflow,Q as CheckoutWorkflowStep,_ as Defaults,Pt as InlinePrice,G as Landscape,D as Log,oe as TAG_NAME_SERVICE,yr as WcsCommitment,we as WcsEnv,_r as WcsPlanType,vr as WcsTerm,ct as applyPlanType,Yn as getLocaleSettings,Cr as getSettings,us as init,si as reset}; - //# sourceMappingURL=commerce.js.map - \ No newline at end of file diff --git a/libs/deps/mas/commerce.js b/libs/deps/mas/commerce.js index 36c618ec66..cf25cfbf96 100644 --- a/libs/deps/mas/commerce.js +++ b/libs/deps/mas/commerce.js @@ -1,4 +1,4 @@ -var kr=Object.defineProperty;var ui=(e,t,r)=>t in e?kr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var li=(e,t)=>{for(var r in t)kr(e,r,{get:t[r],enumerable:!0})};var ne=(e,t,r)=>(ui(e,typeof t!="symbol"?t+"":t,r),r),Gr=(e,t,r)=>{if(!t.has(e))throw TypeError("Cannot "+r)};var Lt=(e,t,r)=>(Gr(e,t,"read from private field"),r?r.call(e):t.get(e)),Fr=(e,t,r)=>{if(t.has(e))throw TypeError("Cannot add the same private member more than once");t instanceof WeakSet?t.add(e):t.set(e,r)},Nt=(e,t,r,n)=>(Gr(e,t,"write to private field"),n?n.call(e,r):t.set(e,r),r);var Ie;(function(e){e.ServerError="ServerError",e.ClientError="ClientError",e.UnexpectedError="UnexpectedError"})(Ie||(Ie={}));var Vr=(e,t,r)=>({type:(i=>i>=500?Ie.ServerError:i<400?Ie.UnexpectedError:Ie.ClientError)(e),message:t,originatingRequest:r,status:e});var fi=function(e,t,r,n){function i(o){return o instanceof r?o:new r(function(s){s(o)})}return new(r||(r=Promise))(function(o,s){function a(u){try{l(n.next(u))}catch(p){s(p)}}function c(u){try{l(n.throw(u))}catch(p){s(p)}}function l(u){u.done?o(u.value):i(u.value).then(a,c)}l((n=n.apply(e,t||[])).next())})},Be;(function(e){e.AUTHORIZATION="Authorization",e.X_API_KEY="X-Api-Key"})(Be||(Be={}));var Ct=class{constructor(t){this.fetchOptions=t}commonHeaders(){let t={};return this.fetchOptions.apiKey&&(t[Be.X_API_KEY]=this.fetchOptions.apiKey),this.fetchOptions.accessToken&&(t[Be.AUTHORIZATION]=`Bearer ${this.fetchOptions.accessToken}`),t}transformData(t,r){return r?t.map(n=>r(n)):t.map(n=>this.identifyTransform(n))}transformDatum(t,r){return r?r(t):this.identifyTransform(t)}identifyTransform(t){return t}failOnBadStatusOrParseBody(t,r){return fi(this,void 0,void 0,function*(){if(t.ok)return t.json().then(i=>({headers:t.headers,status:t.status,statusText:t.statusText,data:i}));let n=yield t.text();return Promise.reject(Vr(t.status,n,r))})}buildUrl(t,r,n,i,o){var s;let a=(s=this.fetchOptions.baseUrl)!==null&&s!==void 0?s:i(this.fetchOptions.env),c=o(r,n);return this.generateUrl(a,t,c)}generateUrl(t,r,n){let i=new URL(r,t);return n&&(i.search=this.convertToSearchParams(n).toString()),i.toString()}convertToSearchParams(t){return new URLSearchParams(t)}setParams(t,r,n){n!=null&&typeof n=="boolean"?t[r]=String(n):n&&(t[r]=n)}},It=Ct;var Y;(function(e){e.STAGE="STAGE",e.PRODUCTION="PRODUCTION",e.LOCAL="LOCAL"})(Y||(Y={}));var Re;(function(e){e.STAGE="STAGE",e.PRODUCTION="PROD",e.LOCAL="LOCAL"})(Re||(Re={}));var G;(function(e){e.DRAFT="DRAFT",e.PUBLISHED="PUBLISHED"})(G||(G={}));var fe;(function(e){e.V2="UCv2",e.V3="UCv3"})(fe||(fe={}));var $;(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"})($||($={}));var Rt=function(e){var t;return(t=pi.get(e))!==null&&t!==void 0?t:e},pi=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 jr=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.")},Wr=function(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),i,o=[],s;try{for(;(t===void 0||t-- >0)&&!(i=n.next()).done;)o.push(i.value)}catch(a){s={error:a}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return o};function ve(e,t,r){var n,i;try{for(var o=jr(Object.entries(e)),s=o.next();!s.done;s=o.next()){var a=Wr(s.value,2),c=a[0],l=a[1],u=Rt(c);l!=null&&r.has(u)&&t.set(u,l)}}catch(p){n={error:p}}finally{try{s&&!s.done&&(i=o.return)&&i.call(o)}finally{if(n)throw n.error}}}function Ye(e){switch(e){case Y.PRODUCTION:return"https://commerce.adobe.com";default:return"https://commerce-stg.adobe.com"}}function $e(e,t){var r,n;for(var i in e){var o=e[i];try{for(var s=(r=void 0,jr(Object.entries(o))),a=s.next();!a.done;a=s.next()){var c=Wr(a.value,2),l=c[0],u=c[1];if(u!=null){var p=Rt(l);t.set("items["+i+"]["+p+"]",u)}}}catch(f){r={error:f}}finally{try{a&&!a.done&&(n=s.return)&&n.call(s)}finally{if(r)throw r.error}}}}var mi=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 i=0,n=Object.getOwnPropertySymbols(e);i=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 Hr(e){gi(e);var t=e.env,r=e.items,n=e.workflowStep,i=mi(e,["env","items","workflowStep"]),o=new URL(Ye(t));return o.pathname=n+"/",$e(r,o.searchParams),ve(i,o.searchParams,di),o.toString()}var di=new Set(["cli","co","lang","ctx","cUrl","mv","nglwfdata","otac","promoid","rUrl","sdid","spint","trackingid","code","campaignid","appctxid"]),Ei=["env","workflowStep","clientId","country","items"];function gi(e){var t,r;try{for(var n=hi(Ei),i=n.next();!i.done;i=n.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&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}return!0}var xi=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 i=0,n=Object.getOwnPropertySymbols(e);i=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},_i="p_draft_landscape",vi="/store/";function Dt(e){bi(e);var t=e.env,r=e.items,n=e.workflowStep,i=e.ms,o=e.marketSegment,s=e.ot,a=e.offerType,c=e.pa,l=e.productArrangementCode,u=e.landscape,p=xi(e,["env","items","workflowStep","ms","marketSegment","ot","offerType","pa","productArrangementCode","landscape"]),f={marketSegment:o??i,offerType:a??s,productArrangementCode:l??c},d=new URL(Ye(t));return d.pathname=""+vi+n,n!==$.SEGMENTATION&&n!==$.CHANGE_PLAN_TEAM_PLANS&&$e(r,d.searchParams),n===$.SEGMENTATION&&ve(f,d.searchParams,Ut),ve(p,d.searchParams,Ut),u===G.DRAFT&&ve({af:_i},d.searchParams,Ut),d.toString()}var Ut=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"]),Ti=["env","workflowStep","clientId","country"];function bi(e){var t,r;try{for(var n=yi(Ti),i=n.next();!i.done;i=n.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&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}if(e.workflowStep!==$.SEGMENTATION&&e.workflowStep!==$.CHANGE_PLAN_TEAM_PLANS&&!e.items)throw new Error('Argument "checkoutData" is not valid, missing: items');return!0}function Mt(e,t){switch(e){case fe.V2:return Hr(t);case fe.V3:return Dt(t);default:return console.warn("Unsupported CheckoutType, will use UCv3 as default. Given type: "+e),Dt(t)}}var kt;(function(e){e.BASE="BASE",e.TRIAL="TRIAL",e.PROMOTION="PROMOTION"})(kt||(kt={}));var I;(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"})(I||(I={}));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 Gt;(function(e){e.INDIVIDUAL="INDIVIDUAL",e.TEAM="TEAM",e.ENTERPRISE="ENTERPRISE"})(Gt||(Gt={}));var Ft;(function(e){e.COM="COM",e.EDU="EDU",e.GOV="GOV"})(Ft||(Ft={}));var Vt;(function(e){e.DIRECT="DIRECT",e.INDIRECT="INDIRECT"})(Vt||(Vt={}));var jt;(function(e){e.ENTERPRISE_PRODUCT="ENTERPRISE_PRODUCT",e.ETLA="ETLA",e.RETAIL="RETAIL",e.VIP="VIP",e.VIPMP="VIPMP",e.FREE="FREE"})(jt||(jt={}));var Wt=()=>{};Wt.createContext=Wt;var Si=G.PUBLISHED,Xr=e=>{switch(e){case Y.PRODUCTION:return"https://wcs.adobe.io";case Y.STAGE:return"https://wcs-stage.adobe.io";case Y.LOCAL:return"http://localhost:3002";default:return"https://wcs-stage.adobe.io"}},Br=(e,t)=>{var r;return e.api_key=t.apiKey,e.landscape=(r=t.landscape)!==null&&r!==void 0?r:Si,e};var Pi=function(e,t,r,n){function i(o){return o instanceof r?o:new r(function(s){s(o)})}return new(r||(r=Promise))(function(o,s){function a(u){try{l(n.next(u))}catch(p){s(p)}}function c(u){try{l(n.throw(u))}catch(p){s(p)}}function l(u){u.done?o(u.value):i(u.value).then(a,c)}l((n=n.apply(e,t||[])).next())})},Ht=class extends It{constructor(t){super(t),this.apiPaths={getWebCommerceArtifact:"web_commerce_artifact"},this.getWebCommerceArtifact=(r,n,i,o)=>Pi(this,void 0,void 0,function*(){let s=this.buildUrl(this.apiPaths.getWebCommerceArtifact,n,r,a=>Xr(a),(a,c)=>this.evaluateGetWebCommerceArtifactParams(a,c));return this.fetchOptions.fetch(s,{signal:o,headers:Object.assign({},this.commonHeaders()),mode:"cors"}).then(a=>this.failOnBadStatusOrParseBody(a,`GET ${s}`)).then(a=>{let l=a.data;return{data:this.transformDatum(l,i)}})})}evaluateGetWebCommerceArtifactParams(t,r){let n={};return this.setParams(n,"offer_selector_ids",r.offerSelectorIds.join(",")),this.setParams(n,"country",r.country),this.setParams(n,"language",r.language),this.setParams(n,"currency",r.currency),this.setParams(n,"locale",r.locale),this.setParams(n,"promotion_code",r.promotionCode),Br(n,t)}},Yr=Ht;var qe=e=>new Yr(e).getWebCommerceArtifact;var $r="tacocat.js";var ze=(e,t)=>String(e??"").toLowerCase()==String(t??"").toLowerCase(),qr=e=>`${e??""}`.replace(/[&<>'"]/g,t=>({"&":"&","<":"<",">":">","'":"'",'"':"""})[t]??t)??"";function w(e,t={},{metadata:r=!0,search:n=!0,storage:i=!0}={}){let o;if(n&&o==null){let s=new URLSearchParams(window.location.search),a=Te(n)?n:e;o=s.get(a)}if(i&&o==null){let s=Te(i)?i:e;o=window.sessionStorage.getItem(s)??window.localStorage.getItem(s)}if(r&&o==null){let s=Qr(Te(r)?r:e);o=document.documentElement.querySelector(`meta[name="${s}"]`)?.content}return o??t[e]}var be=()=>{};var zr=e=>typeof e=="boolean",ce=e=>typeof e=="function",Ze=e=>typeof e=="number",Zr=e=>e!=null&&typeof e=="object";var Te=e=>typeof e=="string",Xt=e=>Te(e)&&e,Se=e=>Ze(e)&&Number.isFinite(e)&&e>0;function Pe(e,t=r=>r==null||r===""){return e!=null&&Object.entries(e).forEach(([r,n])=>{t(n)&&delete e[r]}),e}function v(e,t){if(zr(e))return e;let r=String(e);return r==="1"||r==="true"?!0:r==="0"||r==="false"?!1:t}function ie(e,t,r){let n=Object.values(t);return n.find(i=>ze(i,e))??r??n[0]}function Qr(e=""){return String(e).replace(/(\p{Lowercase_Letter})(\p{Uppercase_Letter})/gu,(t,r,n)=>`${r}-${n}`).replace(/\W+/gu,"-").toLowerCase()}function Ae(e,t=1){return Ze(e)||(e=Number.parseInt(e,10)),!Number.isNaN(e)&&e>0&&Number.isFinite(e)?e:t}var Ai=Date.now(),Bt=()=>`(+${Date.now()-Ai}ms)`,Qe=new Set,wi=v(w("tacocat.debug",{},{metadata:!1}),typeof process<"u"&&process.env?.DEBUG);function Jr(e){let t=`[${$r}/${e}]`,r=(s,a,...c)=>s?!0:(i(a,...c),!1),n=wi?(s,...a)=>{console.debug(`${t} ${s}`,...a,Bt())}:()=>{},i=(s,...a)=>{let c=`${t} ${s}`;Qe.forEach(([l])=>l(c,...a))};return{assert:r,debug:n,error:i,warn:(s,...a)=>{let c=`${t} ${s}`;Qe.forEach(([,l])=>l(c,...a))}}}function Oi(e,t){let r=[e,t];return Qe.add(r),()=>{Qe.delete(r)}}Oi((e,...t)=>{console.error(e,...t,Bt())},(e,...t)=>{console.warn(e,...t,Bt())});var Li="no promo",Kr="promo-tag",Ni="yellow",Ci="neutral",Ii=(e,t,r)=>{let n=o=>o||Li,i=r?` (was "${n(t)}")`:"";return`${n(e)}${i}`},Ri="cancel-context",Ue=(e,t)=>{let r=e===Ri,n=!r&&e?.length>0,i=(n||r)&&(t&&t!=e||!t&&!r),o=i&&n||!i&&!!t,s=o?e||t:void 0;return{effectivePromoCode:s,overridenPromoCode:e,className:o?Kr:`${Kr} no-promo`,text:Ii(s,t,i),variant:o?Ni:Ci,isOverriden:i}};var Yt=function(e,t){return Yt=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,n){r.__proto__=n}||function(r,n){for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(r[i]=n[i])},Yt(e,t)};function De(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 r(){this.constructor=e}e.prototype=t===null?Object.create(t):(r.prototype=t.prototype,new r)}var T=function(){return T=Object.assign||function(t){for(var r,n=1,i=arguments.length;n0}),r=[],n=0,i=t;n1)throw new RangeError("integer-width stems only accept a single optional option");i.options[0].replace(Mi,function(a,c,l,u,p,f){if(c)t.minimumIntegerDigits=l.length;else{if(u&&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(ln.test(i.stem)){t.minimumIntegerDigits=i.stem.length;continue}if(on.test(i.stem)){if(i.options.length>1)throw new RangeError("Fraction-precision stems only accept a single optional option");i.stem.replace(on,function(a,c,l,u,p,f){return l==="*"?t.minimumFractionDigits=c.length:u&&u[0]==="#"?t.maximumFractionDigits=u.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=T(T({},t),sn(i.options[0])));continue}if(un.test(i.stem)){t=T(T({},t),sn(i.stem));continue}var o=fn(i.stem);o&&(t=T(T({},t),o));var s=ki(i.stem);s&&(t=T(T({},t),s))}return t}var zt,Gi=new RegExp("^"+qt.source+"*"),Fi=new RegExp(qt.source+"*$");function x(e,t){return{start:e,end:t}}var Vi=!!String.prototype.startsWith,ji=!!String.fromCodePoint,Wi=!!Object.fromEntries,Hi=!!String.prototype.codePointAt,Xi=!!String.prototype.trimStart,Bi=!!String.prototype.trimEnd,Yi=!!Number.isSafeInteger,$i=Yi?Number.isSafeInteger:function(e){return typeof e=="number"&&isFinite(e)&&Math.floor(e)===e&&Math.abs(e)<=9007199254740991},Qt=!0;try{mn=gn("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu"),Qt=((zt=mn.exec("a"))===null||zt===void 0?void 0:zt[0])==="a"}catch{Qt=!1}var mn,hn=Vi?function(t,r,n){return t.startsWith(r,n)}:function(t,r,n){return t.slice(n,n+r.length)===r},Jt=ji?String.fromCodePoint:function(){for(var t=[],r=0;ro;){if(s=t[o++],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},dn=Wi?Object.fromEntries:function(t){for(var r={},n=0,i=t;n=n)){var i=t.charCodeAt(r),o;return i<55296||i>56319||r+1===n||(o=t.charCodeAt(r+1))<56320||o>57343?i:(i-55296<<10)+(o-56320)+65536}},qi=Xi?function(t){return t.trimStart()}:function(t){return t.replace(Gi,"")},zi=Bi?function(t){return t.trimEnd()}:function(t){return t.replace(Fi,"")};function gn(e,t){return new RegExp(e,t)}var Kt;Qt?(Zt=gn("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu"),Kt=function(t,r){var n;Zt.lastIndex=r;var i=Zt.exec(t);return(n=i[1])!==null&&n!==void 0?n:""}):Kt=function(t,r){for(var n=[];;){var i=En(t,r);if(i===void 0||yn(i)||Ji(i))break;n.push(i),r+=i>=65536?2:1}return Jt.apply(void 0,n)};var Zt,xn=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 i=[];!this.isEOF();){var o=this.char();if(o===123){var s=this.parseArgument(t,n);if(s.err)return s;i.push(s.val)}else{if(o===125&&t>0)break;if(o===35&&(r==="plural"||r==="selectordinal")){var a=this.clonePosition();this.bump(),i.push({type:A.pound,location:x(a,this.clonePosition())})}else if(o===60&&!this.ignoreTag&&this.peek()===47){if(n)break;return this.error(g.UNMATCHED_CLOSING_TAG,x(this.clonePosition(),this.clonePosition()))}else if(o===60&&!this.ignoreTag&&er(this.peek()||0)){var s=this.parseTag(t,r);if(s.err)return s;i.push(s.val)}else{var s=this.parseLiteral(t,r);if(s.err)return s;i.push(s.val)}}}return{val:i,err:null}},e.prototype.parseTag=function(t,r){var n=this.clonePosition();this.bump();var i=this.parseTagName();if(this.bumpSpace(),this.bumpIf("/>"))return{val:{type:A.literal,value:"<"+i+"/>",location:x(n,this.clonePosition())},err:null};if(this.bumpIf(">")){var o=this.parseMessage(t+1,r,!0);if(o.err)return o;var s=o.val,a=this.clonePosition();if(this.bumpIf("")?{val:{type:A.tag,value:i,children:s,location:x(n,this.clonePosition())},err:null}:this.error(g.INVALID_TAG,x(a,this.clonePosition())))}else return this.error(g.UNCLOSED_TAG,x(n,this.clonePosition()))}else return this.error(g.INVALID_TAG,x(n,this.clonePosition()))},e.prototype.parseTagName=function(){var t=this.offset();for(this.bump();!this.isEOF()&&Qi(this.char());)this.bump();return this.message.slice(t,this.offset())},e.prototype.parseLiteral=function(t,r){for(var n=this.clonePosition(),i="";;){var o=this.tryParseQuote(r);if(o){i+=o;continue}var s=this.tryParseUnquoted(t,r);if(s){i+=s;continue}var a=this.tryParseLeftAngleBracket();if(a){i+=a;continue}break}var c=x(n,this.clonePosition());return{val:{type:A.literal,value:i,location:c},err:null}},e.prototype.tryParseLeftAngleBracket=function(){return!this.isEOF()&&this.char()===60&&(this.ignoreTag||!Zi(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 Jt.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(),Jt(n))},e.prototype.parseArgument=function(t,r){var n=this.clonePosition();if(this.bump(),this.bumpSpace(),this.isEOF())return this.error(g.EXPECT_ARGUMENT_CLOSING_BRACE,x(n,this.clonePosition()));if(this.char()===125)return this.bump(),this.error(g.EMPTY_ARGUMENT,x(n,this.clonePosition()));var i=this.parseIdentifierIfPossible().value;if(!i)return this.error(g.MALFORMED_ARGUMENT,x(n,this.clonePosition()));if(this.bumpSpace(),this.isEOF())return this.error(g.EXPECT_ARGUMENT_CLOSING_BRACE,x(n,this.clonePosition()));switch(this.char()){case 125:return this.bump(),{val:{type:A.argument,value:i,location:x(n,this.clonePosition())},err:null};case 44:return this.bump(),this.bumpSpace(),this.isEOF()?this.error(g.EXPECT_ARGUMENT_CLOSING_BRACE,x(n,this.clonePosition())):this.parseArgumentOptions(t,r,i,n);default:return this.error(g.MALFORMED_ARGUMENT,x(n,this.clonePosition()))}},e.prototype.parseIdentifierIfPossible=function(){var t=this.clonePosition(),r=this.offset(),n=Kt(this.message,r),i=r+n.length;this.bumpTo(i);var o=this.clonePosition(),s=x(t,o);return{value:n,location:s}},e.prototype.parseArgumentOptions=function(t,r,n,i){var o,s=this.clonePosition(),a=this.parseIdentifierIfPossible().value,c=this.clonePosition();switch(a){case"":return this.error(g.EXPECT_ARGUMENT_TYPE,x(s,c));case"number":case"date":case"time":{this.bumpSpace();var l=null;if(this.bumpIf(",")){this.bumpSpace();var u=this.clonePosition(),p=this.parseSimpleArgStyleIfPossible();if(p.err)return p;var f=zi(p.val);if(f.length===0)return this.error(g.EXPECT_ARGUMENT_STYLE,x(this.clonePosition(),this.clonePosition()));var d=x(u,this.clonePosition());l={style:f,styleLocation:d}}var h=this.tryParseArgumentClose(i);if(h.err)return h;var E=x(i,this.clonePosition());if(l&&hn(l?.style,"::",0)){var S=qi(l.style.slice(2));if(a==="number"){var p=this.parseNumberSkeletonFromString(S,l.styleLocation);return p.err?p:{val:{type:A.number,value:n,location:E,style:p.val},err:null}}else{if(S.length===0)return this.error(g.EXPECT_DATE_TIME_SKELETON,E);var f={type:pe.dateTime,pattern:S,location:l.styleLocation,parsedOptions:this.shouldParseSkeletons?rn(S):{}},N=a==="date"?A.date:A.time;return{val:{type:N,value:n,location:E,style:f},err:null}}}return{val:{type:a==="number"?A.number:a==="date"?A.date:A.time,value:n,location:E,style:(o=l?.style)!==null&&o!==void 0?o:null},err:null}}case"plural":case"selectordinal":case"select":{var P=this.clonePosition();if(this.bumpSpace(),!this.bumpIf(","))return this.error(g.EXPECT_SELECT_ARGUMENT_OPTIONS,x(P,T({},P)));this.bumpSpace();var b=this.parseIdentifierIfPossible(),O=0;if(a!=="select"&&b.value==="offset"){if(!this.bumpIf(":"))return this.error(g.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE,x(this.clonePosition(),this.clonePosition()));this.bumpSpace();var p=this.tryParseDecimalInteger(g.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE,g.INVALID_PLURAL_ARGUMENT_OFFSET_VALUE);if(p.err)return p;this.bumpSpace(),b=this.parseIdentifierIfPossible(),O=p.val}var y=this.tryParsePluralOrSelectOptions(t,a,r,b);if(y.err)return y;var h=this.tryParseArgumentClose(i);if(h.err)return h;var C=x(i,this.clonePosition());return a==="select"?{val:{type:A.select,value:n,options:dn(y.val),location:C},err:null}:{val:{type:A.plural,value:n,options:dn(y.val),offset:O,pluralType:a==="plural"?"cardinal":"ordinal",location:C},err:null}}default:return this.error(g.INVALID_ARGUMENT_TYPE,x(s,c))}},e.prototype.tryParseArgumentClose=function(t){return this.isEOF()||this.char()!==125?this.error(g.EXPECT_ARGUMENT_CLOSING_BRACE,x(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 i=this.clonePosition();if(!this.bumpUntil("'"))return this.error(g.UNCLOSED_QUOTE_IN_ARGUMENT_STYLE,x(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(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=cn(t)}catch{return this.error(g.INVALID_NUMBER_SKELETON,r)}return{val:{type:pe.number,tokens:n,location:r,parsedOptions:this.shouldParseSkeletons?pn(n):{}},err:null}},e.prototype.tryParsePluralOrSelectOptions=function(t,r,n,i){for(var o,s=!1,a=[],c=new Set,l=i.value,u=i.location;;){if(l.length===0){var p=this.clonePosition();if(r!=="select"&&this.bumpIf("=")){var f=this.tryParseDecimalInteger(g.EXPECT_PLURAL_ARGUMENT_SELECTOR,g.INVALID_PLURAL_ARGUMENT_SELECTOR);if(f.err)return f;u=x(p,this.clonePosition()),l=this.message.slice(p.offset,this.offset())}else break}if(c.has(l))return this.error(r==="select"?g.DUPLICATE_SELECT_ARGUMENT_SELECTOR:g.DUPLICATE_PLURAL_ARGUMENT_SELECTOR,u);l==="other"&&(s=!0),this.bumpSpace();var d=this.clonePosition();if(!this.bumpIf("{"))return this.error(r==="select"?g.EXPECT_SELECT_ARGUMENT_SELECTOR_FRAGMENT:g.EXPECT_PLURAL_ARGUMENT_SELECTOR_FRAGMENT,x(this.clonePosition(),this.clonePosition()));var h=this.parseMessage(t+1,r,n);if(h.err)return h;var E=this.tryParseArgumentClose(d);if(E.err)return E;a.push([l,{value:h.val,location:x(d,this.clonePosition())}]),c.add(l),this.bumpSpace(),o=this.parseIdentifierIfPossible(),l=o.value,u=o.location}return a.length===0?this.error(r==="select"?g.EXPECT_SELECT_ARGUMENT_SELECTOR:g.EXPECT_PLURAL_ARGUMENT_SELECTOR,x(this.clonePosition(),this.clonePosition())):this.requiresOtherClause&&!s?this.error(g.MISSING_OTHER_CLAUSE,x(this.clonePosition(),this.clonePosition())):{val:a,err:null}},e.prototype.tryParseDecimalInteger=function(t,r){var n=1,i=this.clonePosition();this.bumpIf("+")||this.bumpIf("-")&&(n=-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 c=x(i,this.clonePosition());return o?(s*=n,$i(s)?{val:s,err:null}:this.error(r,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 r=En(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(hn(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()&&yn(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 er(e){return e>=97&&e<=122||e>=65&&e<=90}function Zi(e){return er(e)||e===47}function Qi(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 yn(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,rt(t)||nt(t))for(var r in t.options)delete t.options[r].location,tr(t.options[r].value);else Ke(t)&&ot(t.style)||(et(t)||tt(t))&&Me(t.style)?delete t.style.location:it(t)&&tr(t.children)})}function _n(e,t){t===void 0&&(t={}),t=T({shouldParseSkeletons:!0,requiresOtherClause:!0},t);var r=new xn(e,t).parse();if(r.err){var n=SyntaxError(g[r.err.kind]);throw n.location=r.err.location,n.originalMessage=r.err.message,n}return t?.captureLocation||tr(r.val),r.val}function ke(e,t){var r=t&&t.cache?t.cache:io,n=t&&t.serializer?t.serializer:no,i=t&&t.strategy?t.strategy:eo;return i(e,{cache:r,serializer:n})}function Ki(e){return e==null||typeof e=="number"||typeof e=="boolean"}function vn(e,t,r,n){var i=Ki(n)?n:r(n),o=t.get(i);return typeof o>"u"&&(o=e.call(this,n),t.set(i,o)),o}function Tn(e,t,r){var n=Array.prototype.slice.call(arguments,3),i=r(n),o=t.get(i);return typeof o>"u"&&(o=e.apply(this,n),t.set(i,o)),o}function rr(e,t,r,n,i){return r.bind(t,e,n,i)}function eo(e,t){var r=e.length===1?vn:Tn;return rr(e,this,r,t.cache.create(),t.serializer)}function to(e,t){return rr(e,this,Tn,t.cache.create(),t.serializer)}function ro(e,t){return rr(e,this,vn,t.cache.create(),t.serializer)}var no=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 io={create:function(){return new nr}},st={variadic:to,monadic:ro};var me;(function(e){e.MISSING_VALUE="MISSING_VALUE",e.INVALID_VALUE="INVALID_VALUE",e.MISSING_INTL_API="MISSING_INTL_API"})(me||(me={}));var Ge=function(e){De(t,e);function t(r,n,i){var o=e.call(this,r)||this;return o.code=n,o.originalMessage=i,o}return t.prototype.toString=function(){return"[formatjs Error: "+this.code+"] "+this.message},t}(Error);var ir=function(e){De(t,e);function t(r,n,i,o){return e.call(this,'Invalid values for "'+r+'": "'+n+'". Options are "'+Object.keys(i).join('", "')+'"',me.INVALID_VALUE,o)||this}return t}(Ge);var bn=function(e){De(t,e);function t(r,n,i){return e.call(this,'Value for "'+r+'" must be of type '+n,me.INVALID_VALUE,i)||this}return t}(Ge);var Sn=function(e){De(t,e);function t(r,n){return e.call(this,'The intl string context variable "'+r+'" was not provided to the string "'+n+'"',me.MISSING_VALUE,n)||this}return t}(Ge);var U;(function(e){e[e.literal=0]="literal",e[e.object=1]="object"})(U||(U={}));function oo(e){return e.length<2?e:e.reduce(function(t,r){var n=t[t.length-1];return!n||n.type!==U.literal||r.type!==U.literal?t.push(r):n.value+=r.value,t},[])}function so(e){return typeof e=="function"}function Fe(e,t,r,n,i,o,s){if(e.length===1&&$t(e[0]))return[{type:U.literal,value:e[0].value}];for(var a=[],c=0,l=e;ct 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;l0?e.substring(0,n):"";let i=wn(e.split("").reverse().join("")),o=r-i,s=e.substring(o,o+1),a=o+(s==="."||s===","?1:0);t.suffix=i>0?e.substring(a,r):"",t.mask=e.substring(n,a),t.maskHasNegativeSign=t.mask.charAt(0)==="-",t.maskHasPositiveSign=t.mask.charAt(0)==="+";let c=t.mask.match(fo);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 mo(e,t,r){let n=!1,i={value:e};e<0&&(n=!0,i.value=-i.value),i.sign=n?"-":"",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,ho(i,t),(i.result==="0"||i.result==="")&&(n=!1,i.sign=""),!n&&t.maskHasPositiveSign?i.sign="+":n&&t.maskHasPositiveSign?i.sign="-":n&&(i.sign=r&&r.enforceMaskSign&&!t.maskHasNegativeSign?"":"-"),i}function ho(e,t){e.result="";let r=t.integer.split(t.separator),n=r.join(""),i=n&&n.indexOf("0");if(i>-1)for(;e.integer.lengthMath.round(e*20)/20},sr=(e,t)=>({accept:e,round:t}),_o=[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={[I.YEAR]:{[L.MONTHLY]:Ve.MONTH,[L.ANNUAL]:Ve.YEAR},[I.MONTH]:{[L.MONTHLY]:Ve.MONTH}},vo=(e,t)=>e.indexOf(`'${t}'`)===0,To=(e,t=!0)=>{let r=e.replace(/'.*?'/,"").trim(),n=Un(r);return!!n?t||(r=r.replace(/[,\.]0+/,n)):r=r.replace(/\s?(#.*0)(?!\s)?/,"$&"+So(e)),r},bo=e=>{let t=Po(e),r=vo(e,t),n=e.replace(/'.*?'/,""),i=Cn.test(n)||In.test(n);return{currencySymbol:t,isCurrencyFirst:r,hasCurrencySpace:i}},Rn=e=>e.replace(Cn,Nn).replace(In,Nn),So=e=>e.match(/#(.?)#/)?.[1]===Ln?go:Ln,Po=e=>e.match(/'(.*?)'/)?.[1]??"",Un=e=>e.match(/0(.?)0/)?.[1]??"";function at({formatString:e,price:t,usePrecision:r,isIndianPrice:n=!1},i,o=s=>s){let{currencySymbol:s,isCurrencyFirst:a,hasCurrencySpace:c}=bo(e),l=r?Un(e):"",u=To(e,r),p=r?2:0,f=o(t,{currencySymbol:s}),d=n?f.toLocaleString("hi-IN",{minimumFractionDigits:p,maximumFractionDigits:p}):On(u,f),h=r?d.lastIndexOf(l):d.length,E=d.substring(0,h),S=d.substring(h+1);return{accessiblePrice:e.replace(/'.*?'/,"SYMBOL").replace(/#.*0/,d).replace(/SYMBOL/,s),currencySymbol:s,decimals:S,decimalsDelimiter:l,hasCurrencySpace:c,integer:E,isCurrencyFirst:a,recurrenceTerm:i}}var Dn=e=>{let{commitment:t,term:r,usePrecision:n}=e,i=xo[r]??1;return at(e,i>1?Ve.MONTH:ar[t]?.[r],(o,{currencySymbol:s})=>{let a={divisor:i,price:o,usePrecision:n},{round:c}=_o.find(({accept:u})=>u(a));if(!c)throw new Error(`Missing rounding rule for: ${JSON.stringify(a)}`);return(yo[s]??(u=>u))(c(a))})},Mn=({commitment:e,term:t,...r})=>at(r,ar[e]?.[t]),kn=e=>{let{commitment:t,term:r}=e;return t===I.YEAR&&r===L.MONTHLY?at(e,Ve.YEAR,n=>n*12):at(e,ar[t]?.[r])};var Ao={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}"},wo=Jr("ConsonantTemplates/price"),Oo=/<.+?>/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"},he={perUnitLabel:"perUnitLabel",perUnitAriaLabel:"perUnitAriaLabel",recurrenceLabel:"recurrenceLabel",recurrenceAriaLabel:"recurrenceAriaLabel",taxExclusiveLabel:"taxExclusiveLabel",taxInclusiveLabel:"taxInclusiveLabel",strikethroughAriaLabel:"strikethroughAriaLabel"},Lo="TAX_EXCLUSIVE",No=e=>Zr(e)?Object.entries(e).filter(([,t])=>Te(t)||Ze(t)||t===!0).reduce((t,[r,n])=>t+` ${r}${n===!0?"":'="'+qr(n)+'"'}`,""):"",z=(e,t,r,n=!1)=>`${n?Rn(t):t??""}`;function Co(e,{accessibleLabel:t,currencySymbol:r,decimals:n,decimalsDelimiter:i,hasCurrencySpace:o,integer:s,isCurrencyFirst:a,recurrenceLabel:c,perUnitLabel:l,taxInclusivityLabel:u},p={}){let f=z(W.currencySymbol,r),d=z(W.currencySpace,o?" ":""),h="";return a&&(h+=f+d),h+=z(W.integer,s),h+=z(W.decimalsDelimiter,i),h+=z(W.decimals,n),a||(h+=d+f),h+=z(W.recurrence,c,null,!0),h+=z(W.unitType,l,null,!0),h+=z(W.taxInclusivity,u,!0),z(e,h,{...p,"aria-label":t})}var de=({displayOptical:e=!1,displayStrikethrough:t=!1,displayAnnual:r=!1}={})=>({country:n,displayFormatted:i=!0,displayRecurrence:o=!0,displayPerUnit:s=!1,displayTax:a=!1,language:c,literals:l={}}={},{commitment:u,formatString:p,price:f,priceWithoutDiscount:d,taxDisplay:h,taxTerm:E,term:S,usePrecision:N}={},P={})=>{Object.entries({country:n,formatString:p,language:c,price:f}).forEach(([re,wt])=>{if(wt==null)throw new Error(`Argument "${re}" is missing`)});let b={...Ao,...l},O=`${c.toLowerCase()}-${n.toUpperCase()}`;function y(re,wt){let Ot=b[re];if(Ot==null)return"";try{return new An(Ot.replace(Oo,""),O).format(wt)}catch{return wo.error("Failed to format literal:",Ot),""}}let C=t&&d?d:f,M=e?Dn:Mn;r&&(M=kn);let{accessiblePrice:X,recurrenceTerm:B,...R}=M({commitment:u,formatString:p,term:S,price:e?f:C,usePrecision:N,isIndianPrice:n==="IN"}),j=X,ae="";if(v(o)&&B){let re=y(he.recurrenceAriaLabel,{recurrenceTerm:B});re&&(j+=" "+re),ae=y(he.recurrenceLabel,{recurrenceTerm:B})}let le="";if(v(s)){le=y(he.perUnitLabel,{perUnit:"LICENSE"});let re=y(he.perUnitAriaLabel,{perUnit:"LICENSE"});re&&(j+=" "+re)}let te="";v(a)&&E&&(te=y(h===Lo?he.taxExclusiveLabel:he.taxInclusiveLabel,{taxTerm:E}),te&&(j+=" "+te)),t&&(j=y(he.strikethroughAriaLabel,{strikethroughPrice:j}));let q=W.container;if(e&&(q+=" "+W.containerOptical),t&&(q+=" "+W.containerStrikethrough),r&&(q+=" "+W.containerAnnual),v(i))return Co(q,{...R,accessibleLabel:j,recurrenceLabel:ae,perUnitLabel:le,taxInclusivityLabel:te},P);let{currencySymbol:ye,decimals:He,decimalsDelimiter:Xe,hasCurrencySpace:Ce,integer:At,isCurrencyFirst:ai}=R,_e=[At,Xe,He];ai?(_e.unshift(Ce?"\xA0":""),_e.unshift(ye)):(_e.push(Ce?"\xA0":""),_e.push(ye)),_e.push(ae,le,te);let ci=_e.join("");return z(q,ci,P)},Gn=()=>(e,t,r)=>{let i=(e.displayOldPrice===void 0||v(e.displayOldPrice))&&t.priceWithoutDiscount&&t.priceWithoutDiscount!=t.price;return`${de()(e,t,r)}${i?" "+de({displayStrikethrough:!0})(e,t,r):""}`};var cr=de(),ur=Gn(),lr=de({displayOptical:!0}),fr=de({displayStrikethrough:!0}),pr=de({displayAnnual:!0});var Io=(e,t)=>{if(!(!Se(e)||!Se(t)))return Math.floor((t-e)/t*100)},Fn=()=>(e,t,r)=>{let{price:n,priceWithoutDiscount:i}=t,o=Io(n,i);return o===void 0?'':`${o}%`};var mr=Fn();var hr="ABM",dr="PUF",Er="M2M",gr="PERPETUAL",Vn="P3Y",Ro="TAX_INCLUSIVE_DETAILS",Uo="TAX_EXCLUSIVE",jn={ABM:hr,PUF:dr,M2M:Er,PERPETUAL:gr,P3Y:Vn},tc={[hr]:{commitment:I.YEAR,term:L.MONTHLY},[dr]:{commitment:I.YEAR,term:L.ANNUAL},[Er]:{commitment:I.MONTH,term:L.MONTHLY},[gr]:{commitment:I.PERPETUAL,term:void 0},[Vn]:{commitment:I.THREE_MONTHS,term:L.P3Y}},Wn="Value is not an offer",ct=e=>{if(typeof e!="object")return Wn;let{commitment:t,term:r}=e,n=Do(t,r);return{...e,planType:n}};var Do=(e,t)=>{if(e===void 0)return Wn;if(e===""&&t==="")return"";let r="";return e===I.YEAR?t===L.MONTHLY?r=hr:t===L.ANNUAL&&(r=dr):e===I.MONTH?t===L.MONTHLY&&(r=Er):e===I.PERPETUAL&&(r=gr),r};function xr(e){let{priceDetails:t}=e,{price:r,priceWithoutDiscount:n,priceWithoutTax:i,priceWithoutDiscountAndTax:o,taxDisplay:s}=t;if(s!==Ro)return e;let a={...e,priceDetails:{...t,price:i??r,priceWithoutDiscount:o??n,taxDisplay:Uo}};return a.offerType==="TRIAL"&&a.priceDetails.price===0&&(a.priceDetails.price=a.priceDetails.priceWithoutDiscount),a}var{freeze:Ee}=Object,Z=Ee({...fe}),Q=Ee({...$}),H=Ee({...Y}),yr=Ee({...I}),we=Ee({...Re}),_r=Ee({...jn}),vr=Ee({...L});var Or={};li(Or,{CLASS_NAME_FAILED:()=>ut,CLASS_NAME_PENDING:()=>lt,CLASS_NAME_RESOLVED:()=>ft,ERROR_MESSAGE_BAD_REQUEST:()=>Tr,ERROR_MESSAGE_MISSING_LITERALS_URL:()=>Sr,ERROR_MESSAGE_OFFER_NOT_FOUND:()=>br,EVENT_TYPE_ERROR:()=>Mo,EVENT_TYPE_FAILED:()=>pt,EVENT_TYPE_PENDING:()=>mt,EVENT_TYPE_READY:()=>ge,EVENT_TYPE_RESOLVED:()=>ht,LOG_NAMESPACE:()=>Pr,PARAM_AOS_API_KEY:()=>ko,PARAM_ENV:()=>Ar,PARAM_LANDSCAPE:()=>wr,PARAM_WCS_API_KEY:()=>Go,STATE_FAILED:()=>J,STATE_PENDING:()=>K,STATE_RESOLVED:()=>ee,TAG_NAME_SERVICE:()=>oe});var ut="placeholder-failed",lt="placeholder-pending",ft="placeholder-resolved",Tr="Bad WCS request",br="Commerce offer not found",Sr="Literals URL not provided",Mo="wcms:commerce:error",pt="wcms:placeholder:failed",mt="wcms:placeholder:pending",ge="wcms:commerce:ready",ht="wcms:placeholder:resolved",Pr="wcms/commerce",Ar="commerce.env",wr="commerce.landscape",ko="commerce.aosKey",Go="commerce.wcsKey",J="failed",K="pending",ee="resolved",oe="wcms-commerce";var Lr={clientId:"merch-at-scale",delimiter:"\xB6",ignoredProperties:["analytics","literals"],serializableTypes:["Array","Object"],sampleRate:30,tags:"consumer=milo/commerce"},Hn=new Set,Fo=e=>e instanceof Error||typeof e.originatingRequest=="string";function Xn(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:i,status:o}=e;return[n,o,i].filter(s=>s).join(" ")}let r=e[Symbol.toStringTag]??Object.getPrototypeOf(e).constructor.name;if(!Lr.serializableTypes.includes(r))return r}return e}function Vo(e,t){if(!Lr.ignoredProperties.includes(e))return Xn(t)}var Nr={append(e){let{delimiter:t,sampleRate:r,tags:n,clientId:i}=Lr,{message:o,params:s}=e,a=[],c=o,l=[];s.forEach(f=>{f!=null&&(Fo(f)?a:l).push(f)}),a.length&&(c+=" ",c+=a.map(Xn).join(" "));let{pathname:u,search:p}=window.location;c+=`${t}page=`,c+=u+p,l.length&&(c+=`${t}facts=`,c+=JSON.stringify(l,Vo)),Hn.has(c)||(Hn.add(c),window.lana?.log(c,{sampleRate:r,tags:n,clientId:i}))}};var _=Object.freeze({checkoutClientId:"adobe_com",checkoutWorkflow:Z.V3,checkoutWorkflowStep:Q.EMAIL,country:"US",displayOldPrice:!0,displayPerUnit:!1,displayRecurrence:!0,displayTax:!1,domainSwitch:!1,env:H.PRODUCTION,forceTaxExclusive:!1,language:"en",entitlement:!1,extraOptions:{},modal:!1,promotionCode:"",quantity:1,wcsApiKey:"wcms-commerce-ims-ro-user-milo",wcsBufferDelay:1,wcsEnv:we.PRODUCTION,landscape:G.PUBLISHED,wcsBufferLimit:1});function Bn(e,{once:t=!1}={}){let r=null;function n(){let i=document.querySelector(oe);i!==r&&(r=i,i&&e(i))}return document.addEventListener(ge,n,{once:t}),ue(n),()=>document.removeEventListener(ge,n)}function je(e,{country:t,forceTaxExclusive:r,perpetual:n}){let i;if(e.length<2)i=e;else{let o=t==="GB"||n?"EN":"MULT",[s,a]=e;i=[s.language===o?s:a]}return r&&(i=i.map(xr)),i}var ue=e=>window.setTimeout(e);function Oe(e,t=1){if(e==null)return[t];let r=(Array.isArray(e)?e:String(e).split(",")).map(Ae).filter(Se);return r.length||(r=[t]),r}function dt(e){return e==null?[]:(Array.isArray(e)?e:String(e).split(",")).filter(Xt)}function F(){return window.customElements.get(oe)?.instance}var jo="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"},se=Object.freeze({LOCAL:"local",PROD:"prod",STAGE:"stage"});function Yn({locale:e={}}={}){if(!e.prefix)return{country:_.country,language:_.language,locale:jo};let t=e.prefix.replace("/","")??"",[r=_.country,n=_.language]=(m[t]??t).split("_",2);return r=r.toUpperCase(),n=n.toLowerCase(),{country:r,language:n,locale:`${n}_${r}`}}function Cr(e={}){let{commerce:t={},locale:r=void 0}=e,i=(e.env?.name===se.PROD?se.PROD:ie(w(Ar,t,{metadata:!1}),se,se.PROD))===se.STAGE?H.STAGE:H.PRODUCTION,o=w("checkoutClientId",t)??_.checkoutClientId,s=ie(w("checkoutWorkflow",t),Z,_.checkoutWorkflow),a=Q.CHECKOUT;s===Z.V3&&(a=ie(w("checkoutWorkflowStep",t),Q,_.checkoutWorkflowStep));let c=v(w("displayOldPrice",t),_.displayOldPrice),l=v(w("displayPerUnit",t),_.displayPerUnit),u=v(w("displayRecurrence",t),_.displayRecurrence),p=v(w("displayTax",t),_.displayTax),f=v(w("entitlement",t),_.entitlement),d=v(w("modal",t),_.modal),h=v(w("forceTaxExclusive",t),_.forceTaxExclusive),E=w("promotionCode",t)??_.promotionCode,S=Oe(w("quantity",t)),N=w("wcsApiKey",t)??_.wcsApiKey,P=e.env?.name===se.PROD?G.PUBLISHED:ie(w(wr,t),G,_.landscape),b=Ae(w("wcsBufferDelay",t),_.wcsBufferDelay),O=Ae(w("wcsBufferLimit",t),_.wcsBufferLimit),y=v(w("domain.switch",t),!1);return{...Yn({locale:r}),displayOldPrice:c,checkoutClientId:o,checkoutWorkflow:s,checkoutWorkflowStep:a,displayPerUnit:l,displayRecurrence:u,displayTax:p,entitlement:f,extraOptions:_.extraOptions,modal:d,env:i,forceTaxExclusive:h,priceLiteralsURL:t.priceLiteralsURL,priceLiteralsPromise:t.priceLiteralsPromise,promotionCode:E,quantity:S,wcsApiKey:N,wcsBufferDelay:b,wcsBufferLimit:O,wcsEnv:i===H.STAGE?we.STAGE:we.PRODUCTION,landscape:P,domainSwitch:y}}var qn="debug",Wo="error",Ho="info",Xo="warn",Bo=Date.now(),Ir=new Set,Rr=new Set,$n=new Map,We=Object.freeze({DEBUG:qn,ERROR:Wo,INFO:Ho,WARN:Xo}),zn={append({level:e,message:t,params:r,timestamp:n,source:i}){console[e](`${n}ms [${i}] %c${t}`,"font-weight: bold;",...r)}},Zn={filter:({level:e})=>e!==qn},Yo={filter:()=>!1};function $o(e,t,r,n,i){return{level:e,message:t,namespace:r,get params(){if(n.length===1){let[o]=n;ce(o)&&(n=o(),Array.isArray(n)||(n=[n]))}return n},source:i,timestamp:Date.now()-Bo}}function qo(e){[...Rr].every(t=>t(e))&&Ir.forEach(t=>t(e))}function Qn(e){let t=($n.get(e)??0)+1;$n.set(e,t);let r=`${e} #${t}`,n=o=>(s,...a)=>qo($o(o,s,e,a,r)),i=Object.seal({id:r,namespace:e,module(o){return Qn(`${i.namespace}/${o}`)},debug:n(We.DEBUG),error:n(We.ERROR),info:n(We.INFO),warn:n(We.WARN)});return i}function Et(...e){e.forEach(t=>{let{append:r,filter:n}=t;ce(n)?Rr.add(n):ce(r)&&Ir.add(r)})}function zo(e={}){let{name:t}=e,r=v(w("commerce.debug",{search:!0,storage:!0}),t===se.LOCAL);return Et(r?zn:Zn),t===se.PROD&&Et(Nr),D}function Zo(){Ir.clear(),Rr.clear()}var D={...Qn(Pr),Level:We,Plugins:{consoleAppender:zn,debugFilter:Zn,quietFilter:Yo,lanaAppender:Nr},init:zo,reset:Zo,use:Et};var Qo={CLASS_NAME_FAILED:ut,CLASS_NAME_PENDING:lt,CLASS_NAME_RESOLVED:ft,EVENT_TYPE_FAILED:pt,EVENT_TYPE_PENDING:mt,EVENT_TYPE_RESOLVED:ht,STATE_FAILED:J,STATE_PENDING:K,STATE_RESOLVED:ee},Jo={[J]:ut,[K]:lt,[ee]:ft},Ko={[J]:pt,[K]:mt,[ee]:ht},yt=new WeakMap;function V(e){if(!yt.has(e)){let t=D.module(e.constructor.is);yt.set(e,{changes:new Map,connected:!1,dispose:be,error:void 0,log:t,options:void 0,promises:[],state:K,timer:null,value:void 0,version:0})}return yt.get(e)}function gt(e){let t=V(e),{error:r,promises:n,state:i}=t;(i===ee||i===J)&&(t.promises=[],i===ee?n.forEach(({resolve:o})=>o(e)):i===J&&n.forEach(({reject:o})=>o(r))),e.dispatchEvent(new CustomEvent(Ko[i],{bubbles:!0}))}function xt(e){let t=yt.get(e);[J,K,ee].forEach(r=>{e.classList.toggle(Jo[r],r===t.state)})}var es={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=Bn(()=>this.requestUpdate(!0))},disconnectedCallback(){let e=V(this);e.connected&&(e.connected=!1,e.log.debug("Disconnected:",{element:this})),e.dispose(),e.dispose=be},onceSettled(){let{error:e,promises:t,state:r}=V(this);return ee===r?Promise.resolve(this):J===r?Promise.reject(e):new Promise((n,i)=>{t.push({resolve:n,reject:i})})},toggleResolved(e,t,r){let n=V(this);return e!==n.version?!1:(r!==void 0&&(n.options=r),n.state=ee,n.value=t,xt(this),this.log.debug("Resolved:",{element:this,value:t}),ue(()=>gt(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=J,xt(this),n.log.error("Failed:",{element:this,error:t}),ue(()=>gt(this)),!0)},togglePending(e){let t=V(this);return t.version++,e&&(t.options=e),t.state=K,xt(this),ue(()=>gt(this)),t.version},requestUpdate(e=!1){if(!this.isConnected||!F())return;let t=V(this);if(t.timer)return;let{error:r,options:n,state:i,value:o,version:s}=t;t.state=K,t.timer=ue(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===K&&t.version===s&&(t.state=i,t.error=r,t.value=o,xt(this),gt(this))}catch(c){this.toggleFailed(t.version,c,n)}})}};function Jn(e={}){return Object.entries(e).forEach(([t,r])=>{(r==null||r===""||r?.length===0)&&delete e[t]}),e}function _t(e,t={}){let{tag:r,is:n}=e,i=document.createElement(r,{is:n});return i.setAttribute("is",n),Object.assign(i.dataset,Jn(t)),i}function vt(e){let{tag:t,is:r,prototype:n}=e,i=window.customElements.get(r);return i||(Object.defineProperties(n,Object.getOwnPropertyDescriptors(es)),i=Object.defineProperties(e,Object.getOwnPropertyDescriptors(Qo)),window.customElements.define(r,i,{extends:t})),i}function Tt(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,Jn(t)),e):null}var ts="download",rs="upgrade",xe,Le=class Le extends HTMLAnchorElement{constructor(){super();Fr(this,xe,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(r={},n=""){let i=F();if(!i)return null;let{checkoutMarketSegment:o,checkoutWorkflow:s,checkoutWorkflowStep:a,entitlement:c,upgrade:l,modal:u,perpetual:p,promotionCode:f,quantity:d,wcsOsi:h,extraOptions:E}=i.collectCheckoutOptions(r),S=_t(Le,{checkoutMarketSegment:o,checkoutWorkflow:s,checkoutWorkflowStep:a,entitlement:c,upgrade:l,modal:u,perpetual:p,promotionCode:f,quantity:d,wcsOsi:h,extraOptions:E});return n&&(S.innerHTML=`${n}`),S}static getCheckoutLinks(r){return Tt(Le,r)}get isCheckoutLink(){return!0}get placeholder(){return this}clickHandler(r){var n;(n=Lt(this,xe))==null||n.call(this,r)}async render(r={}){if(!this.isConnected)return!1;let n=F();if(!n)return!1;this.dataset.imsCountry||n.imsCountryPromise.then(u=>{u&&(this.dataset.imsCountry=u)},be);let i=n.collectCheckoutOptions(r,this.placeholder);if(!i.wcsOsi.length)return!1;let o;try{o=JSON.parse(i.extraOptions??"{}")}catch(u){this.placeholder.log.error("cannot parse exta checkout options",u)}let s=this.placeholder.togglePending(i);this.href="";let a=n.resolveOfferSelectors(i),c=await Promise.all(a);c=c.map(u=>je(u,i));let l=await n.buildCheckoutAction(c.flat(),{...o,...i});return this.renderOffers(c.flat(),i,{},l,s)}renderOffers(r,n,i={},o=void 0,s=void 0){if(!this.isConnected)return!1;let a=F();if(!a)return!1;if(n={...JSON.parse(this.placeholder.dataset.extraOptions??"null"),...n,...i},s??(s=this.placeholder.togglePending(n)),Lt(this,xe)&&Nt(this,xe,void 0),o){this.classList.remove(ts,rs),this.placeholder.toggleResolved(s,r,n);let{url:l,text:u,className:p,handler:f}=o;return l&&(this.href=l),u&&(this.firstElementChild.innerHTML=u),p&&this.classList.add(...p.split(" ")),f&&(this.setAttribute("href","#"),Nt(this,xe,f.bind(this))),!0}else if(r.length){if(this.placeholder.toggleResolved(s,r,n)){let l=a.buildCheckoutURL(r,n);return this.setAttribute("href",l),!0}}else{let l=new Error(`Not provided: ${n?.wcsOsi??"-"}`);if(this.placeholder.toggleFailed(s,l,n))return this.setAttribute("href","#"),!0}return!1}updateOptions(r={}){let n=F();if(!n)return!1;let{checkoutMarketSegment:i,checkoutWorkflow:o,checkoutWorkflowStep:s,entitlement:a,upgrade:c,modal:l,perpetual:u,promotionCode:p,quantity:f,wcsOsi:d}=n.collectCheckoutOptions(r);return bt(this,{checkoutMarketSegment:i,checkoutWorkflow:o,checkoutWorkflowStep:s,entitlement:a,upgrade:c,modal:l,perpetual:u,promotionCode:p,quantity:f,wcsOsi:d}),!0}};xe=new WeakMap,ne(Le,"is","checkout-link"),ne(Le,"tag","a");var Ur=Le,St=vt(Ur);var Kn=[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],ns={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]},Ne=class Ne 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=F();if(!r)return null;let{displayOldPrice:n,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:c,promotionCode:l,quantity:u,template:p,wcsOsi:f}=r.collectPriceOptions(t);return _t(Ne,{displayOldPrice:n,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:c,promotionCode:l,quantity:u,template:p,wcsOsi:f})}static getInlinePrices(t){return Tt(Ne,t)}get isInlinePrice(){return!0}get placeholder(){return this}resolveDisplayTaxForGeoAndSegment(t,r,n,i){let o=`${t}_${r}`;if(Kn.includes(t)||Kn.includes(o))return!0;let s=ns[`${n}_${i}`];return s?!!(s.includes(t)||s.includes(o)):!1}async resolveDisplayTax(t,r){let[n]=await t.resolveOfferSelectors(r),i=je(await n,r);if(i?.length){let{country:o,language:s}=r,a=i[0],[c=""]=a.marketSegments;return this.resolveDisplayTaxForGeoAndSegment(o,s,a.customerSegment,c)}}async render(t={}){if(!this.isConnected)return!1;let r=F();if(!r)return!1;let n=r.collectPriceOptions(t,this.placeholder);if(!n.wcsOsi.length)return!1;let i=this.placeholder.togglePending(n);this.innerHTML="";let[o]=r.resolveOfferSelectors(n);return this.renderOffers(je(await o,n),n,i)}renderOffers(t,r={},n=void 0){if(!this.isConnected)return;let i=F();if(!i)return!1;let o=i.collectPriceOptions({...this.dataset,...r});if(n??(n=this.placeholder.togglePending(o)),t.length){if(this.placeholder.toggleResolved(n,t,o))return this.innerHTML=i.buildPriceHTML(t,o),!0}else{let s=new Error(`Not provided: ${o?.wcsOsi??"-"}`);if(this.placeholder.toggleFailed(n,s,o))return this.innerHTML="",!0}return!1}updateOptions(t){let r=F();if(!r)return!1;let{displayOldPrice:n,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:c,promotionCode:l,quantity:u,template:p,wcsOsi:f}=r.collectPriceOptions(t);return bt(this,{displayOldPrice:n,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:c,promotionCode:l,quantity:u,template:p,wcsOsi:f}),!0}};ne(Ne,"is","inline-price"),ne(Ne,"tag","span");var Dr=Ne,Pt=vt(Dr);function ei({providers:e,settings:t},r){let n=D.module("checkout");function i(l,u){let{checkoutClientId:p,checkoutWorkflow:f,checkoutWorkflowStep:d,country:h,language:E,promotionCode:S,quantity:N}=t,{checkoutMarketSegment:P,checkoutWorkflow:b=f,checkoutWorkflowStep:O=d,imsCountry:y,country:C=y??h,language:M=E,quantity:X=N,entitlement:B,upgrade:R,modal:j,perpetual:ae,promotionCode:le=S,wcsOsi:te,extraOptions:q,...ye}=Object.assign({},u?.dataset??{},l??{}),He=ie(b,Z,_.checkoutWorkflow),Xe=Q.CHECKOUT;He===Z.V3&&(Xe=ie(O,Q,_.checkoutWorkflowStep));let Ce=Pe({...ye,extraOptions:q,checkoutClientId:p,checkoutMarketSegment:P,country:C,quantity:Oe(X,_.quantity),checkoutWorkflow:He,checkoutWorkflowStep:Xe,language:M,entitlement:v(B),upgrade:v(R),modal:v(j),perpetual:v(ae),promotionCode:Ue(le).effectivePromoCode,wcsOsi:dt(te)});if(u)for(let At of e.checkout)At(u,Ce);return Ce}async function o(l,u){let p=F(),f=await r.getCheckoutAction?.(l,u,p.imsSignedInPromise);return f||null}function s(l,u){if(!Array.isArray(l)||!l.length||!u)return"";let{env:p,landscape:f}=t,{checkoutClientId:d,checkoutMarketSegment:h,checkoutWorkflow:E,checkoutWorkflowStep:S,country:N,promotionCode:P,quantity:b,...O}=i(u),y=window.frameElement?"if":"fp",C={checkoutPromoCode:P,clientId:d,context:y,country:N,env:p,items:[],marketSegment:h,workflowStep:S,landscape:f,...O};if(l.length===1){let[{offerId:M,offerType:X,productArrangementCode:B}]=l,{marketSegments:[R]}=l[0];Object.assign(C,{marketSegment:R,offerType:X,productArrangementCode:B}),C.items.push(b[0]===1?{id:M}:{id:M,quantity:b[0]})}else C.items.push(...l.map(({offerId:M},X)=>({id:M,quantity:b[X]??_.quantity})));return Mt(E,C)}let{createCheckoutLink:a,getCheckoutLinks:c}=St;return{CheckoutLink:St,CheckoutWorkflow:Z,CheckoutWorkflowStep:Q,buildCheckoutAction:o,buildCheckoutURL:s,collectCheckoutOptions:i,createCheckoutLink:a,getCheckoutLinks:c}}function is({interval:e=200,maxAttempts:t=25}={}){let r=D.module("ims");return new Promise(n=>{r.debug("Waing for IMS to be ready");let i=0;function o(){window.adobeIMS?.initialized?n():++i>t?(r.debug("Timeout"),n()):setTimeout(o,e)}o()})}function os(e){return e.then(()=>window.adobeIMS?.isSignedInUser()??!1)}function ss(e){let t=D.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 ti({}){let e=is(),t=os(e),r=ss(t);return{imsReadyPromise:e,imsSignedInPromise:t,imsCountryPromise:r}}function as(e){if(!e.priceLiteralsURL)throw new Error(Sr);return new Promise(t=>{window.fetch(e.priceLiteralsURL).then(r=>{r.json().then(({data:n})=>{t(n)})})})}async function ri(e){let r=await(e.priceLiteralsPromise||as(e));if(Array.isArray(r)){let n=o=>r.find(s=>ze(s.lang,o)),i=n(e.language)??n(_.language);if(i)return Object.freeze(i)}return{}}function ni({literals:e,providers:t,settings:r}){function n(a,c){let{country:l,displayOldPrice:u,displayPerUnit:p,displayRecurrence:f,displayTax:d,forceTaxExclusive:h,language:E,promotionCode:S,quantity:N}=r,{displayOldPrice:P=u,displayPerUnit:b=p,displayRecurrence:O=f,displayTax:y=d,forceTaxExclusive:C=h,country:M=l,language:X=E,perpetual:B,promotionCode:R=S,quantity:j=N,template:ae,wcsOsi:le,...te}=Object.assign({},c?.dataset??{},a??{}),q=Pe({...te,country:M,displayOldPrice:v(P),displayPerUnit:v(b),displayRecurrence:v(O),displayTax:v(y),forceTaxExclusive:v(C),language:X,perpetual:v(B),promotionCode:Ue(R).effectivePromoCode,quantity:Oe(j,_.quantity),template:ae,wcsOsi:dt(le)});if(c)for(let ye of t.price)ye(c,q);return q}function i(a,c){if(!Array.isArray(a)||!a.length||!c)return"";let{template:l}=c,u;switch(l){case"discount":u=mr;break;case"strikethrough":u=fr;break;case"optical":u=lr;break;case"annual":u=pr;break;default:u=c.promotionCode?ur:cr}let p=n(c);p.literals=Object.assign({},e.price,Pe(c.literals??{}));let[f]=a;return f={...f,...f.priceDetails},u(p,f)}let{createInlinePrice:o,getInlinePrices:s}=Pt;return{InlinePrice:Pt,buildPriceHTML:i,collectPriceOptions:n,createInlinePrice:o,getInlinePrices:s}}var Mr="_acom",ii={[H.PRODUCTION]:"https://www.adobe.com",[H.STAGE]:"https://www.stage.adobe.com",[H.PRODUCTION+Mr]:"https://www.adobe.com",[H.STAGE+Mr]:"https://www.stage.adobe.com"};function oi({settings:e}){let t=D.module("wcs"),{env:r,domainSwitch:n,wcsApiKey:i}=e,o=n?ii[r+Mr]:ii[r],s={apiKey:i,baseUrl:o,fetch:window.fetch.bind(window)},a=qe(s),c=new Map,l=new Map,u;async function p(h,E,S=!0){let N=br;try{t.debug("Fetching:",h),h.offerSelectorIds=h.offerSelectorIds.sort();let{data:P}=await a(h,{apiKey:i,environment:e.wcsEnv,landscape:r===H.STAGE?"ALL":e.landscape},({resolvedOffers:O})=>({offers:O.map(ct)}));t.debug("Fetched:",h,P);let{offers:b}=P??{};E.forEach(({resolve:O},y)=>{let C=b.filter(({offerSelectorIds:M})=>M.includes(y)).flat();C.length&&(E.delete(y),O(C))})}catch(P){P.status===404&&h.offerSelectorIds.length>1?(t.debug("Multi-osi 404, fallback to fetch-by-one strategy"),await Promise.allSettled(h.offerSelectorIds.map(b=>p({...h,offerSelectorIds:[b]},E,!1)))):(t.error("Failed:",h,P),N=Tr)}S&&E.size&&(t.debug("Missing:",{offerSelectorIds:[...E.keys()]}),E.forEach(P=>{P.reject(new Error(N))}))}function f(){clearTimeout(u);let h=[...l.values()];l.clear(),h.forEach(({options:E,promises:S})=>p(E,S))}function d({country:h,language:E,perpetual:S=!1,promotionCode:N="",wcsOsi:P=[]}){let b=`${E}_${h}`;h!=="GB"&&(E=S?"EN":"MULT");let O=[h,E,N].filter(y=>y).join("-").toLowerCase();return P.map(y=>{let C=`${y}-${O}`;if(!c.has(C)){let M=new Promise((X,B)=>{let R=l.get(O);if(!R){let j={country:h,locale:b,offerSelectorIds:[]};h!=="GB"&&(j.language=E),R={options:j,promises:new Map},l.set(O,R)}N&&(R.options.promotionCode=N),R.options.offerSelectorIds.push(y),R.promises.set(y,{resolve:X,reject:B}),R.options.offerSelectorIds.length>=e.wcsBufferLimit?f():(t.debug("Queued:",R.options),u||(u=setTimeout(f,e.wcsBufferDelay)))});c.set(C,M)}return c.get(C)})}return{WcsCommitment:yr,WcsPlanType:_r,WcsTerm:vr,resolveOfferSelectors:d}}var k=class extends HTMLElement{get isWcmsCommerce(){return!0}};ne(k,"instance"),ne(k,"promise",null);window.customElements.define(oe,k);async function cs(e,t){let r=D.init(e.env).module("service");r.debug("Activating:",e);let n={price:{}},i=Object.freeze(Cr(e));try{n.price=await ri(i)}catch(c){r.warn("Price literals were not fetched:",c)}let o={checkout:new Set,price:new Set},s=document.createElement(oe),a={literals:n,providers:o,settings:i};return k.instance=Object.defineProperties(s,Object.getOwnPropertyDescriptors({...ei(a,t),...ti(a),...ni(a),...oi(a),...Or,Log:D,get defaults(){return _},get literals(){return n},get log(){return D},get providers(){return{checkout(c){return o.checkout.add(c),()=>o.checkout.delete(c)},price(c){return o.price.add(c),()=>o.price.delete(c)}}},get settings(){return i}})),r.debug("Activated:",{literals:n,settings:i,element:s}),document.head.append(s),ue(()=>{let c=new CustomEvent(ge,{bubbles:!0,cancelable:!1,detail:k.instance});k.instance.dispatchEvent(c)}),k.instance}function si(){document.head.querySelector(oe)?.remove(),k.promise=null,D.reset()}function us(e,t){if(ce(e)){let r=ce(t)?t():{};return r.force&&si(),k.promise??(k.promise=cs(e(),r))}return k.promise?k.promise:new Promise(r=>{let n=i=>{r(i.detail)};document.head.addEventListener(ge,n,{once:!0})})}export{St as CheckoutLink,Z as CheckoutWorkflow,Q as CheckoutWorkflowStep,_ as Defaults,Pt as InlinePrice,G as Landscape,D as Log,oe as TAG_NAME_SERVICE,yr as WcsCommitment,we as WcsEnv,_r as WcsPlanType,vr as WcsTerm,ct as applyPlanType,Yn as getLocaleSettings,Cr as getSettings,us as init,si as reset}; +`,le.MISSING_INTL_API,s);var C=n.getPluralRules(t,{type:c.pluralType}).select(f-(c.offset||0));T=c.options[C]||c.options.other}if(!T)throw new ir(c.value,f,Object.keys(c.options),s);a.push.apply(a,De(T.value,t,n,r,i,f-(c.offset||0)));continue}}return qi(a)}function Zi(e,t){return t?y(y(y({},e||{}),t||{}),Object.keys(e).reduce(function(n,r){return n[r]=y(y({},e[r]),t[r]||{}),n},{})):e}function Ji(e,t){return t?Object.keys(e).reduce(function(n,r){return n[r]=Zi(e[r],t[r]),n},y({},e)):e}function or(e){return{create:function(){return{get:function(t){return e[t]},set:function(t,n){e[t]=n}}}}}function Qi(e){return e===void 0&&(e={number:{},dateTime:{},pluralRules:{}}),{getNumberFormat:Ie(function(){for(var t,n=[],r=0;r0?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 diff --git a/libs/deps/mas/mas.js b/libs/deps/mas/mas.js index 83267703e1..6e761a8a42 100644 --- a/libs/deps/mas/mas.js +++ b/libs/deps/mas/mas.js @@ -1,4 +1,4 @@ -var Dr=Object.defineProperty;var Mr=e=>{throw TypeError(e)};var pi=(e,t,r)=>t in e?Dr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var mi=(e,t)=>{for(var r in t)Dr(e,r,{get:t[r],enumerable:!0})};var te=(e,t,r)=>pi(e,typeof t!="symbol"?t+"":t,r),kr=(e,t,r)=>t.has(e)||Mr("Cannot "+r);var wt=(e,t,r)=>(kr(e,t,"read from private field"),r?r.call(e):t.get(e)),Gr=(e,t,r)=>t.has(e)?Mr("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,r),Ot=(e,t,r,n)=>(kr(e,t,"write to private field"),n?n.call(e,r):t.set(e,r),r);var Ce;(function(e){e.ServerError="ServerError",e.ClientError="ClientError",e.UnexpectedError="UnexpectedError"})(Ce||(Ce={}));var Fr=(e,t,r)=>({type:(i=>i>=500?Ce.ServerError:i<400?Ce.UnexpectedError:Ce.ClientError)(e),message:t,originatingRequest:r,status:e});var hi=function(e,t,r,n){function i(o){return o instanceof r?o:new r(function(s){s(o)})}return new(r||(r=Promise))(function(o,s){function a(l){try{u(n.next(l))}catch(p){s(p)}}function c(l){try{u(n.throw(l))}catch(p){s(p)}}function u(l){l.done?o(l.value):i(l.value).then(a,c)}u((n=n.apply(e,t||[])).next())})},Be;(function(e){e.AUTHORIZATION="Authorization",e.X_API_KEY="X-Api-Key"})(Be||(Be={}));var Lt=class{constructor(t){this.fetchOptions=t}commonHeaders(){let t={};return this.fetchOptions.apiKey&&(t[Be.X_API_KEY]=this.fetchOptions.apiKey),this.fetchOptions.accessToken&&(t[Be.AUTHORIZATION]=`Bearer ${this.fetchOptions.accessToken}`),t}transformData(t,r){return r?t.map(n=>r(n)):t.map(n=>this.identifyTransform(n))}transformDatum(t,r){return r?r(t):this.identifyTransform(t)}identifyTransform(t){return t}failOnBadStatusOrParseBody(t,r){return hi(this,void 0,void 0,function*(){if(t.ok)return t.json().then(i=>({headers:t.headers,status:t.status,statusText:t.statusText,data:i}));let n=yield t.text();return Promise.reject(Fr(t.status,n,r))})}buildUrl(t,r,n,i,o){var s;let a=(s=this.fetchOptions.baseUrl)!==null&&s!==void 0?s:i(this.fetchOptions.env),c=o(r,n);return this.generateUrl(a,t,c)}generateUrl(t,r,n){let i=new URL(r,t);return n&&(i.search=this.convertToSearchParams(n).toString()),i.toString()}convertToSearchParams(t){return new URLSearchParams(t)}setParams(t,r,n){n!=null&&typeof n=="boolean"?t[r]=String(n):n&&(t[r]=n)}},Nt=Lt;var Y;(function(e){e.STAGE="STAGE",e.PRODUCTION="PRODUCTION",e.LOCAL="LOCAL"})(Y||(Y={}));var Ie;(function(e){e.STAGE="STAGE",e.PRODUCTION="PROD",e.LOCAL="LOCAL"})(Ie||(Ie={}));var j;(function(e){e.DRAFT="DRAFT",e.PUBLISHED="PUBLISHED"})(j||(j={}));var fe;(function(e){e.V2="UCv2",e.V3="UCv3"})(fe||(fe={}));var $;(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"})($||($={}));var Ct=function(e){var t;return(t=di.get(e))!==null&&t!==void 0?t:e},di=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 Vr=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.")},jr=function(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),i,o=[],s;try{for(;(t===void 0||t-- >0)&&!(i=n.next()).done;)o.push(i.value)}catch(a){s={error:a}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return o};function ve(e,t,r){var n,i;try{for(var o=Vr(Object.entries(e)),s=o.next();!s.done;s=o.next()){var a=jr(s.value,2),c=a[0],u=a[1],l=Ct(c);u!=null&&r.has(l)&&t.set(l,u)}}catch(p){n={error:p}}finally{try{s&&!s.done&&(i=o.return)&&i.call(o)}finally{if(n)throw n.error}}}function Ye(e){switch(e){case Y.PRODUCTION:return"https://commerce.adobe.com";default:return"https://commerce-stg.adobe.com"}}function $e(e,t){var r,n;for(var i in e){var o=e[i];try{for(var s=(r=void 0,Vr(Object.entries(o))),a=s.next();!a.done;a=s.next()){var c=jr(a.value,2),u=c[0],l=c[1];if(l!=null){var p=Ct(u);t.set("items["+i+"]["+p+"]",l)}}}catch(f){r={error:f}}finally{try{a&&!a.done&&(n=s.return)&&n.call(s)}finally{if(r)throw r.error}}}}var Ei=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 i=0,n=Object.getOwnPropertySymbols(e);i=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 Wr(e){_i(e);var t=e.env,r=e.items,n=e.workflowStep,i=Ei(e,["env","items","workflowStep"]),o=new URL(Ye(t));return o.pathname=n+"/",$e(r,o.searchParams),ve(i,o.searchParams,xi),o.toString()}var xi=new Set(["cli","co","lang","ctx","cUrl","mv","nglwfdata","otac","promoid","rUrl","sdid","spint","trackingid","code","campaignid","appctxid"]),yi=["env","workflowStep","clientId","country","items"];function _i(e){var t,r;try{for(var n=gi(yi),i=n.next();!i.done;i=n.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&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}return!0}var vi=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 i=0,n=Object.getOwnPropertySymbols(e);i=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},Si="p_draft_landscape",bi="/store/";function Rt(e){Ai(e);var t=e.env,r=e.items,n=e.workflowStep,i=e.ms,o=e.marketSegment,s=e.ot,a=e.offerType,c=e.pa,u=e.productArrangementCode,l=e.landscape,p=vi(e,["env","items","workflowStep","ms","marketSegment","ot","offerType","pa","productArrangementCode","landscape"]),f={marketSegment:o??i,offerType:a??s,productArrangementCode:u??c},d=new URL(Ye(t));return d.pathname=""+bi+n,n!==$.SEGMENTATION&&n!==$.CHANGE_PLAN_TEAM_PLANS&&$e(r,d.searchParams),n===$.SEGMENTATION&&ve(f,d.searchParams,It),ve(p,d.searchParams,It),l===j.DRAFT&&ve({af:Si},d.searchParams,It),d.toString()}var It=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 Ai(e){var t,r;try{for(var n=Ti(Pi),i=n.next();!i.done;i=n.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&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}if(e.workflowStep!==$.SEGMENTATION&&e.workflowStep!==$.CHANGE_PLAN_TEAM_PLANS&&!e.items)throw new Error('Argument "checkoutData" is not valid, missing: items');return!0}function Ut(e,t){switch(e){case fe.V2:return Wr(t);case fe.V3:return Rt(t);default:return console.warn("Unsupported CheckoutType, will use UCv3 as default. Given type: "+e),Rt(t)}}var Dt;(function(e){e.BASE="BASE",e.TRIAL="TRIAL",e.PROMOTION="PROMOTION"})(Dt||(Dt={}));var I;(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"})(I||(I={}));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 Mt;(function(e){e.INDIVIDUAL="INDIVIDUAL",e.TEAM="TEAM",e.ENTERPRISE="ENTERPRISE"})(Mt||(Mt={}));var kt;(function(e){e.COM="COM",e.EDU="EDU",e.GOV="GOV"})(kt||(kt={}));var Gt;(function(e){e.DIRECT="DIRECT",e.INDIRECT="INDIRECT"})(Gt||(Gt={}));var Ft;(function(e){e.ENTERPRISE_PRODUCT="ENTERPRISE_PRODUCT",e.ETLA="ETLA",e.RETAIL="RETAIL",e.VIP="VIP",e.VIPMP="VIPMP",e.FREE="FREE"})(Ft||(Ft={}));var Vt=()=>{};Vt.createContext=Vt;var wi=j.PUBLISHED,Hr=e=>{switch(e){case Y.PRODUCTION:return"https://wcs.adobe.io";case Y.STAGE:return"https://wcs-stage.adobe.io";case Y.LOCAL:return"http://localhost:3002";default:return"https://wcs-stage.adobe.io"}},Xr=(e,t)=>{var r;return e.api_key=t.apiKey,e.landscape=(r=t.landscape)!==null&&r!==void 0?r:wi,e};var Oi=function(e,t,r,n){function i(o){return o instanceof r?o:new r(function(s){s(o)})}return new(r||(r=Promise))(function(o,s){function a(l){try{u(n.next(l))}catch(p){s(p)}}function c(l){try{u(n.throw(l))}catch(p){s(p)}}function u(l){l.done?o(l.value):i(l.value).then(a,c)}u((n=n.apply(e,t||[])).next())})},jt=class extends Nt{constructor(t){super(t),this.apiPaths={getWebCommerceArtifact:"web_commerce_artifact"},this.getWebCommerceArtifact=(r,n,i,o)=>Oi(this,void 0,void 0,function*(){let s=this.buildUrl(this.apiPaths.getWebCommerceArtifact,n,r,a=>Hr(a),(a,c)=>this.evaluateGetWebCommerceArtifactParams(a,c));return this.fetchOptions.fetch(s,{signal:o,headers:Object.assign({},this.commonHeaders()),mode:"cors"}).then(a=>this.failOnBadStatusOrParseBody(a,`GET ${s}`)).then(a=>{let u=a.data;return{data:this.transformDatum(u,i)}})})}evaluateGetWebCommerceArtifactParams(t,r){let n={};return this.setParams(n,"offer_selector_ids",r.offerSelectorIds.join(",")),this.setParams(n,"country",r.country),this.setParams(n,"language",r.language),this.setParams(n,"currency",r.currency),this.setParams(n,"locale",r.locale),this.setParams(n,"promotion_code",r.promotionCode),Xr(n,t)}},Br=jt;var qe=e=>new Br(e).getWebCommerceArtifact;var Yr="tacocat.js";var ze=(e,t)=>String(e??"").toLowerCase()==String(t??"").toLowerCase(),$r=e=>`${e??""}`.replace(/[&<>'"]/g,t=>({"&":"&","<":"<",">":">","'":"'",'"':"""})[t]??t)??"";function w(e,t={},{metadata:r=!0,search:n=!0,storage:i=!0}={}){let o;if(n&&o==null){let s=new URLSearchParams(window.location.search),a=Te(n)?n:e;o=s.get(a)}if(i&&o==null){let s=Te(i)?i:e;o=window.sessionStorage.getItem(s)??window.localStorage.getItem(s)}if(r&&o==null){let s=Zr(Te(r)?r:e);o=document.documentElement.querySelector(`meta[name="${s}"]`)?.content}return o??t[e]}var Se=()=>{};var qr=e=>typeof e=="boolean",ae=e=>typeof e=="function",Ze=e=>typeof e=="number",zr=e=>e!=null&&typeof e=="object";var Te=e=>typeof e=="string",Wt=e=>Te(e)&&e,be=e=>Ze(e)&&Number.isFinite(e)&&e>0;function Pe(e,t=r=>r==null||r===""){return e!=null&&Object.entries(e).forEach(([r,n])=>{t(n)&&delete e[r]}),e}function _(e,t){if(qr(e))return e;let r=String(e);return r==="1"||r==="true"?!0:r==="0"||r==="false"?!1:t}function re(e,t,r){let n=Object.values(t);return n.find(i=>ze(i,e))??r??n[0]}function Zr(e=""){return String(e).replace(/(\p{Lowercase_Letter})(\p{Uppercase_Letter})/gu,(t,r,n)=>`${r}-${n}`).replace(/\W+/gu,"-").toLowerCase()}function Ae(e,t=1){return Ze(e)||(e=Number.parseInt(e,10)),!Number.isNaN(e)&&e>0&&Number.isFinite(e)?e:t}var Li=Date.now(),Ht=()=>`(+${Date.now()-Li}ms)`,Qe=new Set,Ni=_(w("tacocat.debug",{},{metadata:!1}),typeof process<"u"&&process.env?.DEBUG);function Qr(e){let t=`[${Yr}/${e}]`,r=(s,a,...c)=>s?!0:(i(a,...c),!1),n=Ni?(s,...a)=>{console.debug(`${t} ${s}`,...a,Ht())}:()=>{},i=(s,...a)=>{let c=`${t} ${s}`;Qe.forEach(([u])=>u(c,...a))};return{assert:r,debug:n,error:i,warn:(s,...a)=>{let c=`${t} ${s}`;Qe.forEach(([,u])=>u(c,...a))}}}function Ci(e,t){let r=[e,t];return Qe.add(r),()=>{Qe.delete(r)}}Ci((e,...t)=>{console.error(e,...t,Ht())},(e,...t)=>{console.warn(e,...t,Ht())});var Ii="no promo",Jr="promo-tag",Ri="yellow",Ui="neutral",Di=(e,t,r)=>{let n=o=>o||Ii,i=r?` (was "${n(t)}")`:"";return`${n(e)}${i}`},Mi="cancel-context",Re=(e,t)=>{let r=e===Mi,n=!r&&e?.length>0,i=(n||r)&&(t&&t!=e||!t&&!r),o=i&&n||!i&&!!t,s=o?e||t:void 0;return{effectivePromoCode:s,overridenPromoCode:e,className:o?Jr:`${Jr} no-promo`,text:Di(s,t,i),variant:o?Ri:Ui,isOverriden:i}};var Xt=function(e,t){return Xt=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,n){r.__proto__=n}||function(r,n){for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(r[i]=n[i])},Xt(e,t)};function Ue(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");Xt(e,t);function r(){this.constructor=e}e.prototype=t===null?Object.create(t):(r.prototype=t.prototype,new r)}var v=function(){return v=Object.assign||function(t){for(var r,n=1,i=arguments.length;n0}),r=[],n=0,i=t;n1)throw new RangeError("integer-width stems only accept a single optional option");i.options[0].replace(Fi,function(a,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(ln.test(i.stem)){t.minimumIntegerDigits=i.stem.length;continue}if(nn.test(i.stem)){if(i.options.length>1)throw new RangeError("Fraction-precision stems only accept a single optional option");i.stem.replace(nn,function(a,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=v(v({},t),on(i.options[0])));continue}if(cn.test(i.stem)){t=v(v({},t),on(i.stem));continue}var o=un(i.stem);o&&(t=v(v({},t),o));var s=Vi(i.stem);s&&(t=v(v({},t),s))}return t}var $t,ji=new RegExp("^"+Yt.source+"*"),Wi=new RegExp(Yt.source+"*$");function x(e,t){return{start:e,end:t}}var Hi=!!String.prototype.startsWith,Xi=!!String.fromCodePoint,Bi=!!Object.fromEntries,Yi=!!String.prototype.codePointAt,$i=!!String.prototype.trimStart,qi=!!String.prototype.trimEnd,zi=!!Number.isSafeInteger,Zi=zi?Number.isSafeInteger:function(e){return typeof e=="number"&&isFinite(e)&&Math.floor(e)===e&&Math.abs(e)<=9007199254740991},zt=!0;try{pn=En("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu"),zt=(($t=pn.exec("a"))===null||$t===void 0?void 0:$t[0])==="a"}catch{zt=!1}var pn,mn=Hi?function(t,r,n){return t.startsWith(r,n)}:function(t,r,n){return t.slice(n,n+r.length)===r},Zt=Xi?String.fromCodePoint:function(){for(var t=[],r=0;ro;){if(s=t[o++],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},hn=Bi?Object.fromEntries:function(t){for(var r={},n=0,i=t;n=n)){var i=t.charCodeAt(r),o;return i<55296||i>56319||r+1===n||(o=t.charCodeAt(r+1))<56320||o>57343?i:(i-55296<<10)+(o-56320)+65536}},Qi=$i?function(t){return t.trimStart()}:function(t){return t.replace(ji,"")},Ji=qi?function(t){return t.trimEnd()}:function(t){return t.replace(Wi,"")};function En(e,t){return new RegExp(e,t)}var Qt;zt?(qt=En("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu"),Qt=function(t,r){var n;qt.lastIndex=r;var i=qt.exec(t);return(n=i[1])!==null&&n!==void 0?n:""}):Qt=function(t,r){for(var n=[];;){var i=dn(t,r);if(i===void 0||xn(i)||to(i))break;n.push(i),r+=i>=65536?2:1}return Zt.apply(void 0,n)};var qt,gn=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 i=[];!this.isEOF();){var o=this.char();if(o===123){var s=this.parseArgument(t,n);if(s.err)return s;i.push(s.val)}else{if(o===125&&t>0)break;if(o===35&&(r==="plural"||r==="selectordinal")){var a=this.clonePosition();this.bump(),i.push({type:A.pound,location:x(a,this.clonePosition())})}else if(o===60&&!this.ignoreTag&&this.peek()===47){if(n)break;return this.error(g.UNMATCHED_CLOSING_TAG,x(this.clonePosition(),this.clonePosition()))}else if(o===60&&!this.ignoreTag&&Jt(this.peek()||0)){var s=this.parseTag(t,r);if(s.err)return s;i.push(s.val)}else{var s=this.parseLiteral(t,r);if(s.err)return s;i.push(s.val)}}}return{val:i,err:null}},e.prototype.parseTag=function(t,r){var n=this.clonePosition();this.bump();var i=this.parseTagName();if(this.bumpSpace(),this.bumpIf("/>"))return{val:{type:A.literal,value:"<"+i+"/>",location:x(n,this.clonePosition())},err:null};if(this.bumpIf(">")){var o=this.parseMessage(t+1,r,!0);if(o.err)return o;var s=o.val,a=this.clonePosition();if(this.bumpIf("")?{val:{type:A.tag,value:i,children:s,location:x(n,this.clonePosition())},err:null}:this.error(g.INVALID_TAG,x(a,this.clonePosition())))}else return this.error(g.UNCLOSED_TAG,x(n,this.clonePosition()))}else return this.error(g.INVALID_TAG,x(n,this.clonePosition()))},e.prototype.parseTagName=function(){var t=this.offset();for(this.bump();!this.isEOF()&&eo(this.char());)this.bump();return this.message.slice(t,this.offset())},e.prototype.parseLiteral=function(t,r){for(var n=this.clonePosition(),i="";;){var o=this.tryParseQuote(r);if(o){i+=o;continue}var s=this.tryParseUnquoted(t,r);if(s){i+=s;continue}var a=this.tryParseLeftAngleBracket();if(a){i+=a;continue}break}var c=x(n,this.clonePosition());return{val:{type:A.literal,value:i,location:c},err:null}},e.prototype.tryParseLeftAngleBracket=function(){return!this.isEOF()&&this.char()===60&&(this.ignoreTag||!Ki(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 Zt.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(),Zt(n))},e.prototype.parseArgument=function(t,r){var n=this.clonePosition();if(this.bump(),this.bumpSpace(),this.isEOF())return this.error(g.EXPECT_ARGUMENT_CLOSING_BRACE,x(n,this.clonePosition()));if(this.char()===125)return this.bump(),this.error(g.EMPTY_ARGUMENT,x(n,this.clonePosition()));var i=this.parseIdentifierIfPossible().value;if(!i)return this.error(g.MALFORMED_ARGUMENT,x(n,this.clonePosition()));if(this.bumpSpace(),this.isEOF())return this.error(g.EXPECT_ARGUMENT_CLOSING_BRACE,x(n,this.clonePosition()));switch(this.char()){case 125:return this.bump(),{val:{type:A.argument,value:i,location:x(n,this.clonePosition())},err:null};case 44:return this.bump(),this.bumpSpace(),this.isEOF()?this.error(g.EXPECT_ARGUMENT_CLOSING_BRACE,x(n,this.clonePosition())):this.parseArgumentOptions(t,r,i,n);default:return this.error(g.MALFORMED_ARGUMENT,x(n,this.clonePosition()))}},e.prototype.parseIdentifierIfPossible=function(){var t=this.clonePosition(),r=this.offset(),n=Qt(this.message,r),i=r+n.length;this.bumpTo(i);var o=this.clonePosition(),s=x(t,o);return{value:n,location:s}},e.prototype.parseArgumentOptions=function(t,r,n,i){var o,s=this.clonePosition(),a=this.parseIdentifierIfPossible().value,c=this.clonePosition();switch(a){case"":return this.error(g.EXPECT_ARGUMENT_TYPE,x(s,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=Ji(p.val);if(f.length===0)return this.error(g.EXPECT_ARGUMENT_STYLE,x(this.clonePosition(),this.clonePosition()));var d=x(l,this.clonePosition());u={style:f,styleLocation:d}}var h=this.tryParseArgumentClose(i);if(h.err)return h;var E=x(i,this.clonePosition());if(u&&mn(u?.style,"::",0)){var b=Qi(u.style.slice(2));if(a==="number"){var p=this.parseNumberSkeletonFromString(b,u.styleLocation);return p.err?p:{val:{type:A.number,value:n,location:E,style:p.val},err:null}}else{if(b.length===0)return this.error(g.EXPECT_DATE_TIME_SKELETON,E);var f={type:pe.dateTime,pattern:b,location:u.styleLocation,parsedOptions:this.shouldParseSkeletons?tn(b):{}},N=a==="date"?A.date:A.time;return{val:{type:N,value:n,location:E,style:f},err:null}}}return{val:{type:a==="number"?A.number:a==="date"?A.date:A.time,value:n,location:E,style:(o=u?.style)!==null&&o!==void 0?o:null},err:null}}case"plural":case"selectordinal":case"select":{var P=this.clonePosition();if(this.bumpSpace(),!this.bumpIf(","))return this.error(g.EXPECT_SELECT_ARGUMENT_OPTIONS,x(P,v({},P)));this.bumpSpace();var S=this.parseIdentifierIfPossible(),O=0;if(a!=="select"&&S.value==="offset"){if(!this.bumpIf(":"))return this.error(g.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE,x(this.clonePosition(),this.clonePosition()));this.bumpSpace();var p=this.tryParseDecimalInteger(g.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE,g.INVALID_PLURAL_ARGUMENT_OFFSET_VALUE);if(p.err)return p;this.bumpSpace(),S=this.parseIdentifierIfPossible(),O=p.val}var y=this.tryParsePluralOrSelectOptions(t,a,r,S);if(y.err)return y;var h=this.tryParseArgumentClose(i);if(h.err)return h;var C=x(i,this.clonePosition());return a==="select"?{val:{type:A.select,value:n,options:hn(y.val),location:C},err:null}:{val:{type:A.plural,value:n,options:hn(y.val),offset:O,pluralType:a==="plural"?"cardinal":"ordinal",location:C},err:null}}default:return this.error(g.INVALID_ARGUMENT_TYPE,x(s,c))}},e.prototype.tryParseArgumentClose=function(t){return this.isEOF()||this.char()!==125?this.error(g.EXPECT_ARGUMENT_CLOSING_BRACE,x(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 i=this.clonePosition();if(!this.bumpUntil("'"))return this.error(g.UNCLOSED_QUOTE_IN_ARGUMENT_STYLE,x(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(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=an(t)}catch{return this.error(g.INVALID_NUMBER_SKELETON,r)}return{val:{type:pe.number,tokens:n,location:r,parsedOptions:this.shouldParseSkeletons?fn(n):{}},err:null}},e.prototype.tryParsePluralOrSelectOptions=function(t,r,n,i){for(var o,s=!1,a=[],c=new Set,u=i.value,l=i.location;;){if(u.length===0){var p=this.clonePosition();if(r!=="select"&&this.bumpIf("=")){var f=this.tryParseDecimalInteger(g.EXPECT_PLURAL_ARGUMENT_SELECTOR,g.INVALID_PLURAL_ARGUMENT_SELECTOR);if(f.err)return f;l=x(p,this.clonePosition()),u=this.message.slice(p.offset,this.offset())}else break}if(c.has(u))return this.error(r==="select"?g.DUPLICATE_SELECT_ARGUMENT_SELECTOR:g.DUPLICATE_PLURAL_ARGUMENT_SELECTOR,l);u==="other"&&(s=!0),this.bumpSpace();var d=this.clonePosition();if(!this.bumpIf("{"))return this.error(r==="select"?g.EXPECT_SELECT_ARGUMENT_SELECTOR_FRAGMENT:g.EXPECT_PLURAL_ARGUMENT_SELECTOR_FRAGMENT,x(this.clonePosition(),this.clonePosition()));var h=this.parseMessage(t+1,r,n);if(h.err)return h;var E=this.tryParseArgumentClose(d);if(E.err)return E;a.push([u,{value:h.val,location:x(d,this.clonePosition())}]),c.add(u),this.bumpSpace(),o=this.parseIdentifierIfPossible(),u=o.value,l=o.location}return a.length===0?this.error(r==="select"?g.EXPECT_SELECT_ARGUMENT_SELECTOR:g.EXPECT_PLURAL_ARGUMENT_SELECTOR,x(this.clonePosition(),this.clonePosition())):this.requiresOtherClause&&!s?this.error(g.MISSING_OTHER_CLAUSE,x(this.clonePosition(),this.clonePosition())):{val:a,err:null}},e.prototype.tryParseDecimalInteger=function(t,r){var n=1,i=this.clonePosition();this.bumpIf("+")||this.bumpIf("-")&&(n=-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 c=x(i,this.clonePosition());return o?(s*=n,Zi(s)?{val:s,err:null}:this.error(r,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 r=dn(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(mn(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()&&xn(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 Jt(e){return e>=97&&e<=122||e>=65&&e<=90}function Ki(e){return Jt(e)||e===47}function eo(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 xn(e){return e>=9&&e<=13||e===32||e===133||e>=8206&&e<=8207||e===8232||e===8233}function to(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 Kt(e){e.forEach(function(t){if(delete t.location,rt(t)||nt(t))for(var r in t.options)delete t.options[r].location,Kt(t.options[r].value);else Ke(t)&&ot(t.style)||(et(t)||tt(t))&&De(t.style)?delete t.style.location:it(t)&&Kt(t.children)})}function yn(e,t){t===void 0&&(t={}),t=v({shouldParseSkeletons:!0,requiresOtherClause:!0},t);var r=new gn(e,t).parse();if(r.err){var n=SyntaxError(g[r.err.kind]);throw n.location=r.err.location,n.originalMessage=r.err.message,n}return t?.captureLocation||Kt(r.val),r.val}function Me(e,t){var r=t&&t.cache?t.cache:ao,n=t&&t.serializer?t.serializer:so,i=t&&t.strategy?t.strategy:no;return i(e,{cache:r,serializer:n})}function ro(e){return e==null||typeof e=="number"||typeof e=="boolean"}function _n(e,t,r,n){var i=ro(n)?n:r(n),o=t.get(i);return typeof o>"u"&&(o=e.call(this,n),t.set(i,o)),o}function vn(e,t,r){var n=Array.prototype.slice.call(arguments,3),i=r(n),o=t.get(i);return typeof o>"u"&&(o=e.apply(this,n),t.set(i,o)),o}function er(e,t,r,n,i){return r.bind(t,e,n,i)}function no(e,t){var r=e.length===1?_n:vn;return er(e,this,r,t.cache.create(),t.serializer)}function io(e,t){return er(e,this,vn,t.cache.create(),t.serializer)}function oo(e,t){return er(e,this,_n,t.cache.create(),t.serializer)}var so=function(){return JSON.stringify(arguments)};function tr(){this.cache=Object.create(null)}tr.prototype.get=function(e){return this.cache[e]};tr.prototype.set=function(e,t){this.cache[e]=t};var ao={create:function(){return new tr}},st={variadic:io,monadic:oo};var me;(function(e){e.MISSING_VALUE="MISSING_VALUE",e.INVALID_VALUE="INVALID_VALUE",e.MISSING_INTL_API="MISSING_INTL_API"})(me||(me={}));var ke=function(e){Ue(t,e);function t(r,n,i){var o=e.call(this,r)||this;return o.code=n,o.originalMessage=i,o}return t.prototype.toString=function(){return"[formatjs Error: "+this.code+"] "+this.message},t}(Error);var rr=function(e){Ue(t,e);function t(r,n,i,o){return e.call(this,'Invalid values for "'+r+'": "'+n+'". Options are "'+Object.keys(i).join('", "')+'"',me.INVALID_VALUE,o)||this}return t}(ke);var Tn=function(e){Ue(t,e);function t(r,n,i){return e.call(this,'Value for "'+r+'" must be of type '+n,me.INVALID_VALUE,i)||this}return t}(ke);var Sn=function(e){Ue(t,e);function t(r,n){return e.call(this,'The intl string context variable "'+r+'" was not provided to the string "'+n+'"',me.MISSING_VALUE,n)||this}return t}(ke);var U;(function(e){e[e.literal=0]="literal",e[e.object=1]="object"})(U||(U={}));function co(e){return e.length<2?e:e.reduce(function(t,r){var n=t[t.length-1];return!n||n.type!==U.literal||r.type!==U.literal?t.push(r):n.value+=r.value,t},[])}function lo(e){return typeof e=="function"}function Ge(e,t,r,n,i,o,s){if(e.length===1&&Bt(e[0]))return[{type:U.literal,value:e[0].value}];for(var a=[],c=0,u=e;c{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;l0?e.substring(0,n):"";let i=An(e.split("").reverse().join("")),o=r-i,s=e.substring(o,o+1),a=o+(s==="."||s===","?1:0);t.suffix=i>0?e.substring(a,r):"",t.mask=e.substring(n,a),t.maskHasNegativeSign=t.mask.charAt(0)==="-",t.maskHasPositiveSign=t.mask.charAt(0)==="+";let c=t.mask.match(ho);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 go(e,t,r){let n=!1,i={value:e};e<0&&(n=!0,i.value=-i.value),i.sign=n?"-":"",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,xo(i,t),(i.result==="0"||i.result==="")&&(n=!1,i.sign=""),!n&&t.maskHasPositiveSign?i.sign="+":n&&t.maskHasPositiveSign?i.sign="-":n&&(i.sign=r&&r.enforceMaskSign&&!t.maskHasNegativeSign?"":"-"),i}function xo(e,t){e.result="";let r=t.integer.split(t.separator),n=r.join(""),i=n&&n.indexOf("0");if(i>-1)for(;e.integer.lengthMath.round(e*20)/20},ir=(e,t)=>({accept:e,round:t}),So=[ir(({divisor:e,price:t})=>t%e==0,({divisor:e,price:t})=>t/e),ir(({usePrecision:e})=>e,({divisor:e,price:t})=>Math.ceil(Math.floor(t*1e4/e)/100)/100),ir(()=>!0,({divisor:e,price:t})=>Math.ceil(Math.floor(t*100/e)/100))],or={[I.YEAR]:{[L.MONTHLY]:Fe.MONTH,[L.ANNUAL]:Fe.YEAR},[I.MONTH]:{[L.MONTHLY]:Fe.MONTH}},bo=(e,t)=>e.indexOf(`'${t}'`)===0,Po=(e,t=!0)=>{let r=e.replace(/'.*?'/,"").trim(),n=Rn(r);return!!n?t||(r=r.replace(/[,\.]0+/,n)):r=r.replace(/\s?(#.*0)(?!\s)?/,"$&"+wo(e)),r},Ao=e=>{let t=Oo(e),r=bo(e,t),n=e.replace(/'.*?'/,""),i=Nn.test(n)||Cn.test(n);return{currencySymbol:t,isCurrencyFirst:r,hasCurrencySpace:i}},In=e=>e.replace(Nn,Ln).replace(Cn,Ln),wo=e=>e.match(/#(.?)#/)?.[1]===On?_o:On,Oo=e=>e.match(/'(.*?)'/)?.[1]??"",Rn=e=>e.match(/0(.?)0/)?.[1]??"";function at({formatString:e,price:t,usePrecision:r,isIndianPrice:n=!1},i,o=s=>s){let{currencySymbol:s,isCurrencyFirst:a,hasCurrencySpace:c}=Ao(e),u=r?Rn(e):"",l=Po(e,r),p=r?2:0,f=o(t,{currencySymbol:s}),d=n?f.toLocaleString("hi-IN",{minimumFractionDigits:p,maximumFractionDigits:p}):wn(l,f),h=r?d.lastIndexOf(u):d.length,E=d.substring(0,h),b=d.substring(h+1);return{accessiblePrice:e.replace(/'.*?'/,"SYMBOL").replace(/#.*0/,d).replace(/SYMBOL/,s),currencySymbol:s,decimals:b,decimalsDelimiter:u,hasCurrencySpace:c,integer:E,isCurrencyFirst:a,recurrenceTerm:i}}var Un=e=>{let{commitment:t,term:r,usePrecision:n}=e,i=vo[r]??1;return at(e,i>1?Fe.MONTH:or[t]?.[r],(o,{currencySymbol:s})=>{let a={divisor:i,price:o,usePrecision:n},{round:c}=So.find(({accept:l})=>l(a));if(!c)throw new Error(`Missing rounding rule for: ${JSON.stringify(a)}`);return(To[s]??(l=>l))(c(a))})},Dn=({commitment:e,term:t,...r})=>at(r,or[e]?.[t]),Mn=e=>{let{commitment:t,term:r}=e;return t===I.YEAR&&r===L.MONTHLY?at(e,Fe.YEAR,n=>n*12):at(e,or[t]?.[r])};var Lo={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}"},No=Qr("ConsonantTemplates/price"),Co=/<.+?>/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"},he={perUnitLabel:"perUnitLabel",perUnitAriaLabel:"perUnitAriaLabel",recurrenceLabel:"recurrenceLabel",recurrenceAriaLabel:"recurrenceAriaLabel",taxExclusiveLabel:"taxExclusiveLabel",taxInclusiveLabel:"taxInclusiveLabel",strikethroughAriaLabel:"strikethroughAriaLabel"},Io="TAX_EXCLUSIVE",Ro=e=>zr(e)?Object.entries(e).filter(([,t])=>Te(t)||Ze(t)||t===!0).reduce((t,[r,n])=>t+` ${r}${n===!0?"":'="'+$r(n)+'"'}`,""):"",z=(e,t,r,n=!1)=>`${n?In(t):t??""}`;function Uo(e,{accessibleLabel:t,currencySymbol:r,decimals:n,decimalsDelimiter:i,hasCurrencySpace:o,integer:s,isCurrencyFirst:a,recurrenceLabel:c,perUnitLabel:u,taxInclusivityLabel:l},p={}){let f=z(W.currencySymbol,r),d=z(W.currencySpace,o?" ":""),h="";return a&&(h+=f+d),h+=z(W.integer,s),h+=z(W.decimalsDelimiter,i),h+=z(W.decimals,n),a||(h+=d+f),h+=z(W.recurrence,c,null,!0),h+=z(W.unitType,u,null,!0),h+=z(W.taxInclusivity,l,!0),z(e,h,{...p,"aria-label":t})}var de=({displayOptical:e=!1,displayStrikethrough:t=!1,displayAnnual:r=!1}={})=>({country:n,displayFormatted:i=!0,displayRecurrence:o=!0,displayPerUnit:s=!1,displayTax:a=!1,language:c,literals:u={}}={},{commitment:l,formatString:p,price:f,priceWithoutDiscount:d,taxDisplay:h,taxTerm:E,term:b,usePrecision:N}={},P={})=>{Object.entries({country:n,formatString:p,language:c,price:f}).forEach(([ee,Pt])=>{if(Pt==null)throw new Error(`Argument "${ee}" is missing`)});let S={...Lo,...u},O=`${c.toLowerCase()}-${n.toUpperCase()}`;function y(ee,Pt){let At=S[ee];if(At==null)return"";try{return new Pn(At.replace(Co,""),O).format(Pt)}catch{return No.error("Failed to format literal:",At),""}}let C=t&&d?d:f,D=e?Un:Dn;r&&(D=Mn);let{accessiblePrice:X,recurrenceTerm:B,...R}=D({commitment:l,formatString:p,term:b,price:e?f:C,usePrecision:N,isIndianPrice:n==="IN"}),V=X,se="";if(_(o)&&B){let ee=y(he.recurrenceAriaLabel,{recurrenceTerm:B});ee&&(V+=" "+ee),se=y(he.recurrenceLabel,{recurrenceTerm:B})}let ue="";if(_(s)){ue=y(he.perUnitLabel,{perUnit:"LICENSE"});let ee=y(he.perUnitAriaLabel,{perUnit:"LICENSE"});ee&&(V+=" "+ee)}let K="";_(a)&&E&&(K=y(h===Io?he.taxExclusiveLabel:he.taxInclusiveLabel,{taxTerm:E}),K&&(V+=" "+K)),t&&(V=y(he.strikethroughAriaLabel,{strikethroughPrice:V}));let q=W.container;if(e&&(q+=" "+W.containerOptical),t&&(q+=" "+W.containerStrikethrough),r&&(q+=" "+W.containerAnnual),_(i))return Uo(q,{...R,accessibleLabel:V,recurrenceLabel:se,perUnitLabel:ue,taxInclusivityLabel:K},P);let{currencySymbol:ye,decimals:He,decimalsDelimiter:Xe,hasCurrencySpace:Ne,integer:bt,isCurrencyFirst:ui}=R,_e=[bt,Xe,He];ui?(_e.unshift(Ne?"\xA0":""),_e.unshift(ye)):(_e.push(Ne?"\xA0":""),_e.push(ye)),_e.push(se,ue,K);let fi=_e.join("");return z(q,fi,P)},kn=()=>(e,t,r)=>{let i=(e.displayOldPrice===void 0||_(e.displayOldPrice))&&t.priceWithoutDiscount&&t.priceWithoutDiscount!=t.price;return`${de()(e,t,r)}${i?" "+de({displayStrikethrough:!0})(e,t,r):""}`};var sr=de(),ar=kn(),cr=de({displayOptical:!0}),lr=de({displayStrikethrough:!0}),ur=de({displayAnnual:!0});var Do=(e,t)=>{if(!(!be(e)||!be(t)))return Math.floor((t-e)/t*100)},Gn=()=>(e,t,r)=>{let{price:n,priceWithoutDiscount:i}=t,o=Do(n,i);return o===void 0?'':`${o}%`};var fr=Gn();var pr="ABM",mr="PUF",hr="M2M",dr="PERPETUAL",Fn="P3Y",Mo="TAX_INCLUSIVE_DETAILS",ko="TAX_EXCLUSIVE",Vn={ABM:pr,PUF:mr,M2M:hr,PERPETUAL:dr,P3Y:Fn},uc={[pr]:{commitment:I.YEAR,term:L.MONTHLY},[mr]:{commitment:I.YEAR,term:L.ANNUAL},[hr]:{commitment:I.MONTH,term:L.MONTHLY},[dr]:{commitment:I.PERPETUAL,term:void 0},[Fn]:{commitment:I.THREE_MONTHS,term:L.P3Y}},jn="Value is not an offer",Er=e=>{if(typeof e!="object")return jn;let{commitment:t,term:r}=e,n=Go(t,r);return{...e,planType:n}};var Go=(e,t)=>{if(e===void 0)return jn;if(e===""&&t==="")return"";let r="";return e===I.YEAR?t===L.MONTHLY?r=pr:t===L.ANNUAL&&(r=mr):e===I.MONTH?t===L.MONTHLY&&(r=hr):e===I.PERPETUAL&&(r=dr),r};function gr(e){let{priceDetails:t}=e,{price:r,priceWithoutDiscount:n,priceWithoutTax:i,priceWithoutDiscountAndTax:o,taxDisplay:s}=t;if(s!==Mo)return e;let a={...e,priceDetails:{...t,price:i??r,priceWithoutDiscount:o??n,taxDisplay:ko}};return a.offerType==="TRIAL"&&a.priceDetails.price===0&&(a.priceDetails.price=a.priceDetails.priceWithoutDiscount),a}var{freeze:Ee}=Object,ne=Ee({...fe}),ie=Ee({...$}),H=Ee({...Y}),Wn=Ee({...I}),Ve=Ee({...Ie}),Hn=Ee({...Vn}),Xn=Ee({...L});var br={};mi(br,{CLASS_NAME_FAILED:()=>ct,CLASS_NAME_PENDING:()=>lt,CLASS_NAME_RESOLVED:()=>ut,ERROR_MESSAGE_BAD_REQUEST:()=>xr,ERROR_MESSAGE_MISSING_LITERALS_URL:()=>_r,ERROR_MESSAGE_OFFER_NOT_FOUND:()=>yr,EVENT_TYPE_ERROR:()=>Fo,EVENT_TYPE_FAILED:()=>ft,EVENT_TYPE_PENDING:()=>pt,EVENT_TYPE_READY:()=>ge,EVENT_TYPE_RESOLVED:()=>mt,LOG_NAMESPACE:()=>vr,PARAM_AOS_API_KEY:()=>Vo,PARAM_ENV:()=>Tr,PARAM_LANDSCAPE:()=>Sr,PARAM_WCS_API_KEY:()=>jo,STATE_FAILED:()=>Z,STATE_PENDING:()=>Q,STATE_RESOLVED:()=>J,TAG_NAME_SERVICE:()=>ce});var ct="placeholder-failed",lt="placeholder-pending",ut="placeholder-resolved",xr="Bad WCS request",yr="Commerce offer not found",_r="Literals URL not provided",Fo="wcms:commerce:error",ft="wcms:placeholder:failed",pt="wcms:placeholder:pending",ge="wcms:commerce:ready",mt="wcms:placeholder:resolved",vr="wcms/commerce",Tr="commerce.env",Sr="commerce.landscape",Vo="commerce.aosKey",jo="commerce.wcsKey",Z="failed",Q="pending",J="resolved",ce="wcms-commerce";var Pr={clientId:"merch-at-scale",delimiter:"\xB6",ignoredProperties:["analytics","literals"],serializableTypes:["Array","Object"],sampleRate:30,tags:"consumer=milo/commerce"},Bn=new Set,Wo=e=>e instanceof Error||typeof e.originatingRequest=="string";function Yn(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:i,status:o}=e;return[n,o,i].filter(s=>s).join(" ")}let r=e[Symbol.toStringTag]??Object.getPrototypeOf(e).constructor.name;if(!Pr.serializableTypes.includes(r))return r}return e}function Ho(e,t){if(!Pr.ignoredProperties.includes(e))return Yn(t)}var Ar={append(e){let{delimiter:t,sampleRate:r,tags:n,clientId:i}=Pr,{message:o,params:s}=e,a=[],c=o,u=[];s.forEach(f=>{f!=null&&(Wo(f)?a:u).push(f)}),a.length&&(c+=" ",c+=a.map(Yn).join(" "));let{pathname:l,search:p}=window.location;c+=`${t}page=`,c+=l+p,u.length&&(c+=`${t}facts=`,c+=JSON.stringify(u,Ho)),Bn.has(c)||(Bn.add(c),window.lana?.log(c,{sampleRate:r,tags:n,clientId:i}))}};var T=Object.freeze({checkoutClientId:"adobe_com",checkoutWorkflow:ne.V3,checkoutWorkflowStep:ie.EMAIL,country:"US",displayOldPrice:!0,displayPerUnit:!1,displayRecurrence:!0,displayTax:!1,domainSwitch:!1,env:H.PRODUCTION,forceTaxExclusive:!1,language:"en",entitlement:!1,extraOptions:{},modal:!1,promotionCode:"",quantity:1,wcsApiKey:"wcms-commerce-ims-ro-user-milo",wcsBufferDelay:1,wcsEnv:Ve.PRODUCTION,landscape:j.PUBLISHED,wcsBufferLimit:1});function $n(e,{once:t=!1}={}){let r=null;function n(){let i=document.querySelector(ce);i!==r&&(r=i,i&&e(i))}return document.addEventListener(ge,n,{once:t}),le(n),()=>document.removeEventListener(ge,n)}function je(e,{country:t,forceTaxExclusive:r,perpetual:n}){let i;if(e.length<2)i=e;else{let o=t==="GB"||n?"EN":"MULT",[s,a]=e;i=[s.language===o?s:a]}return r&&(i=i.map(gr)),i}var le=e=>window.setTimeout(e);function we(e,t=1){if(e==null)return[t];let r=(Array.isArray(e)?e:String(e).split(",")).map(Ae).filter(be);return r.length||(r=[t]),r}function ht(e){return e==null?[]:(Array.isArray(e)?e:String(e).split(",")).filter(Wt)}function G(){return window.customElements.get(ce)?.instance}var Xo="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"},oe=Object.freeze({LOCAL:"local",PROD:"prod",STAGE:"stage"});function Bo({locale:e={}}={}){if(!e.prefix)return{country:T.country,language:T.language,locale:Xo};let t=e.prefix.replace("/","")??"",[r=T.country,n=T.language]=(m[t]??t).split("_",2);return r=r.toUpperCase(),n=n.toLowerCase(),{country:r,language:n,locale:`${n}_${r}`}}function qn(e={}){let{commerce:t={},locale:r=void 0}=e,i=(e.env?.name===oe.PROD?oe.PROD:re(w(Tr,t,{metadata:!1}),oe,oe.PROD))===oe.STAGE?H.STAGE:H.PRODUCTION,o=w("checkoutClientId",t)??T.checkoutClientId,s=re(w("checkoutWorkflow",t),ne,T.checkoutWorkflow),a=ie.CHECKOUT;s===ne.V3&&(a=re(w("checkoutWorkflowStep",t),ie,T.checkoutWorkflowStep));let c=_(w("displayOldPrice",t),T.displayOldPrice),u=_(w("displayPerUnit",t),T.displayPerUnit),l=_(w("displayRecurrence",t),T.displayRecurrence),p=_(w("displayTax",t),T.displayTax),f=_(w("entitlement",t),T.entitlement),d=_(w("modal",t),T.modal),h=_(w("forceTaxExclusive",t),T.forceTaxExclusive),E=w("promotionCode",t)??T.promotionCode,b=we(w("quantity",t)),N=w("wcsApiKey",t)??T.wcsApiKey,P=e.env?.name===oe.PROD?j.PUBLISHED:re(w(Sr,t),j,T.landscape),S=Ae(w("wcsBufferDelay",t),T.wcsBufferDelay),O=Ae(w("wcsBufferLimit",t),T.wcsBufferLimit),y=_(w("domain.switch",t),!1);return{...Bo({locale:r}),displayOldPrice:c,checkoutClientId:o,checkoutWorkflow:s,checkoutWorkflowStep:a,displayPerUnit:u,displayRecurrence:l,displayTax:p,entitlement:f,extraOptions:T.extraOptions,modal:d,env:i,forceTaxExclusive:h,priceLiteralsURL:t.priceLiteralsURL,priceLiteralsPromise:t.priceLiteralsPromise,promotionCode:E,quantity:b,wcsApiKey:N,wcsBufferDelay:S,wcsBufferLimit:O,wcsEnv:i===H.STAGE?Ve.STAGE:Ve.PRODUCTION,landscape:P,domainSwitch:y}}var Zn="debug",Yo="error",$o="info",qo="warn",zo=Date.now(),wr=new Set,Or=new Set,zn=new Map,We=Object.freeze({DEBUG:Zn,ERROR:Yo,INFO:$o,WARN:qo}),Qn={append({level:e,message:t,params:r,timestamp:n,source:i}){console[e](`${n}ms [${i}] %c${t}`,"font-weight: bold;",...r)}},Jn={filter:({level:e})=>e!==Zn},Zo={filter:()=>!1};function Qo(e,t,r,n,i){return{level:e,message:t,namespace:r,get params(){if(n.length===1){let[o]=n;ae(o)&&(n=o(),Array.isArray(n)||(n=[n]))}return n},source:i,timestamp:Date.now()-zo}}function Jo(e){[...Or].every(t=>t(e))&&wr.forEach(t=>t(e))}function Kn(e){let t=(zn.get(e)??0)+1;zn.set(e,t);let r=`${e} #${t}`,n=o=>(s,...a)=>Jo(Qo(o,s,e,a,r)),i=Object.seal({id:r,namespace:e,module(o){return Kn(`${i.namespace}/${o}`)},debug:n(We.DEBUG),error:n(We.ERROR),info:n(We.INFO),warn:n(We.WARN)});return i}function dt(...e){e.forEach(t=>{let{append:r,filter:n}=t;ae(n)?Or.add(n):ae(r)&&wr.add(r)})}function Ko(e={}){let{name:t}=e,r=_(w("commerce.debug",{search:!0,storage:!0}),t===oe.LOCAL);return dt(r?Qn:Jn),t===oe.PROD&&dt(Ar),M}function es(){wr.clear(),Or.clear()}var M={...Kn(vr),Level:We,Plugins:{consoleAppender:Qn,debugFilter:Jn,quietFilter:Zo,lanaAppender:Ar},init:Ko,reset:es,use:dt};var ts={CLASS_NAME_FAILED:ct,CLASS_NAME_PENDING:lt,CLASS_NAME_RESOLVED:ut,EVENT_TYPE_FAILED:ft,EVENT_TYPE_PENDING:pt,EVENT_TYPE_RESOLVED:mt,STATE_FAILED:Z,STATE_PENDING:Q,STATE_RESOLVED:J},rs={[Z]:ct,[Q]:lt,[J]:ut},ns={[Z]:ft,[Q]:pt,[J]:mt},xt=new WeakMap;function F(e){if(!xt.has(e)){let t=M.module(e.constructor.is);xt.set(e,{changes:new Map,connected:!1,dispose:Se,error:void 0,log:t,options:void 0,promises:[],state:Q,timer:null,value:void 0,version:0})}return xt.get(e)}function Et(e){let t=F(e),{error:r,promises:n,state:i}=t;(i===J||i===Z)&&(t.promises=[],i===J?n.forEach(({resolve:o})=>o(e)):i===Z&&n.forEach(({reject:o})=>o(r))),e.dispatchEvent(new CustomEvent(ns[i],{bubbles:!0}))}function gt(e){let t=xt.get(e);[Z,Q,J].forEach(r=>{e.classList.toggle(rs[r],r===t.state)})}var is={get error(){return F(this).error},get log(){return F(this).log},get options(){return F(this).options},get state(){return F(this).state},get value(){return F(this).value},attributeChangedCallback(e,t,r){F(this).changes.set(e,r),this.requestUpdate()},connectedCallback(){F(this).dispose=$n(()=>this.requestUpdate(!0))},disconnectedCallback(){let e=F(this);e.connected&&(e.connected=!1,e.log.debug("Disconnected:",{element:this})),e.dispose(),e.dispose=Se},onceSettled(){let{error:e,promises:t,state:r}=F(this);return J===r?Promise.resolve(this):Z===r?Promise.reject(e):new Promise((n,i)=>{t.push({resolve:n,reject:i})})},toggleResolved(e,t,r){let n=F(this);return e!==n.version?!1:(r!==void 0&&(n.options=r),n.state=J,n.value=t,gt(this),this.log.debug("Resolved:",{element:this,value:t}),le(()=>Et(this)),!0)},toggleFailed(e,t,r){let n=F(this);return e!==n.version?!1:(r!==void 0&&(n.options=r),n.error=t,n.state=Z,gt(this),n.log.error("Failed:",{element:this,error:t}),le(()=>Et(this)),!0)},togglePending(e){let t=F(this);return t.version++,e&&(t.options=e),t.state=Q,gt(this),le(()=>Et(this)),t.version},requestUpdate(e=!1){if(!this.isConnected||!G())return;let t=F(this);if(t.timer)return;let{error:r,options:n,state:i,value:o,version:s}=t;t.state=Q,t.timer=le(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=r,t.value=o,gt(this),Et(this))}catch(c){this.toggleFailed(t.version,c,n)}})}};function ei(e={}){return Object.entries(e).forEach(([t,r])=>{(r==null||r===""||r?.length===0)&&delete e[t]}),e}function yt(e,t={}){let{tag:r,is:n}=e,i=document.createElement(r,{is:n});return i.setAttribute("is",n),Object.assign(i.dataset,ei(t)),i}function _t(e){let{tag:t,is:r,prototype:n}=e,i=window.customElements.get(r);return i||(Object.defineProperties(n,Object.getOwnPropertyDescriptors(is)),i=Object.defineProperties(e,Object.getOwnPropertyDescriptors(ts)),window.customElements.define(r,i,{extends:t})),i}function vt(e,t=document.body){return Array.from(t?.querySelectorAll(`${e.tag}[is="${e.is}"]`)??[])}function Tt(e,t={}){return e instanceof HTMLElement?(Object.assign(e.dataset,ei(t)),e):null}var os="download",ss="upgrade",xe,Oe=class Oe extends HTMLAnchorElement{constructor(){super();Gr(this,xe);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 i=G();if(!i)return null;let{checkoutMarketSegment:o,checkoutWorkflow:s,checkoutWorkflowStep:a,entitlement:c,upgrade:u,modal:l,perpetual:p,promotionCode:f,quantity:d,wcsOsi:h,extraOptions:E}=i.collectCheckoutOptions(r),b=yt(Oe,{checkoutMarketSegment:o,checkoutWorkflow:s,checkoutWorkflowStep:a,entitlement:c,upgrade:u,modal:l,perpetual:p,promotionCode:f,quantity:d,wcsOsi:h,extraOptions:E});return n&&(b.innerHTML=`${n}`),b}static getCheckoutLinks(r){return vt(Oe,r)}get isCheckoutLink(){return!0}get placeholder(){return this}clickHandler(r){var n;(n=wt(this,xe))==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)},Se);let i=n.collectCheckoutOptions(r,this.placeholder);if(!i.wcsOsi.length)return!1;let o;try{o=JSON.parse(i.extraOptions??"{}")}catch(l){this.placeholder.log.error("cannot parse exta checkout options",l)}let s=this.placeholder.togglePending(i);this.href="";let a=n.resolveOfferSelectors(i),c=await Promise.all(a);c=c.map(l=>je(l,i));let u=await n.buildCheckoutAction(c.flat(),{...o,...i});return this.renderOffers(c.flat(),i,{},u,s)}renderOffers(r,n,i={},o=void 0,s=void 0){if(!this.isConnected)return!1;let a=G();if(!a)return!1;if(n={...JSON.parse(this.placeholder.dataset.extraOptions??"null"),...n,...i},s??(s=this.placeholder.togglePending(n)),wt(this,xe)&&Ot(this,xe,void 0),o){this.classList.remove(os,ss),this.placeholder.toggleResolved(s,r,n);let{url:u,text:l,className:p,handler:f}=o;return u&&(this.href=u),l&&(this.firstElementChild.innerHTML=l),p&&this.classList.add(...p.split(" ")),f&&(this.setAttribute("href","#"),Ot(this,xe,f.bind(this))),!0}else if(r.length){if(this.placeholder.toggleResolved(s,r,n)){let u=a.buildCheckoutURL(r,n);return this.setAttribute("href",u),!0}}else{let u=new Error(`Not provided: ${n?.wcsOsi??"-"}`);if(this.placeholder.toggleFailed(s,u,n))return this.setAttribute("href","#"),!0}return!1}updateOptions(r={}){let n=G();if(!n)return!1;let{checkoutMarketSegment:i,checkoutWorkflow:o,checkoutWorkflowStep:s,entitlement:a,upgrade:c,modal:u,perpetual:l,promotionCode:p,quantity:f,wcsOsi:d}=n.collectCheckoutOptions(r);return Tt(this,{checkoutMarketSegment:i,checkoutWorkflow:o,checkoutWorkflowStep:s,entitlement:a,upgrade:c,modal:u,perpetual:l,promotionCode:p,quantity:f,wcsOsi:d}),!0}};xe=new WeakMap,te(Oe,"is","checkout-link"),te(Oe,"tag","a");var Lr=Oe,Nr=_t(Lr);var ti=[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],as={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]},Le=class Le 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:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:c,promotionCode:u,quantity:l,template:p,wcsOsi:f}=r.collectPriceOptions(t);return yt(Le,{displayOldPrice:n,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:c,promotionCode:u,quantity:l,template:p,wcsOsi:f})}static getInlinePrices(t){return vt(Le,t)}get isInlinePrice(){return!0}get placeholder(){return this}resolveDisplayTaxForGeoAndSegment(t,r,n,i){let o=`${t}_${r}`;if(ti.includes(t)||ti.includes(o))return!0;let s=as[`${n}_${i}`];return s?!!(s.includes(t)||s.includes(o)):!1}async resolveDisplayTax(t,r){let[n]=await t.resolveOfferSelectors(r),i=je(await n,r);if(i?.length){let{country:o,language:s}=r,a=i[0],[c=""]=a.marketSegments;return this.resolveDisplayTaxForGeoAndSegment(o,s,a.customerSegment,c)}}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 i=this.placeholder.togglePending(n);this.innerHTML="";let[o]=r.resolveOfferSelectors(n);return this.renderOffers(je(await o,n),n,i)}renderOffers(t,r={},n=void 0){if(!this.isConnected)return;let i=G();if(!i)return!1;let o=i.collectPriceOptions({...this.dataset,...r});if(n??(n=this.placeholder.togglePending(o)),t.length){if(this.placeholder.toggleResolved(n,t,o))return this.innerHTML=i.buildPriceHTML(t,o),!0}else{let s=new Error(`Not provided: ${o?.wcsOsi??"-"}`);if(this.placeholder.toggleFailed(n,s,o))return this.innerHTML="",!0}return!1}updateOptions(t){let r=G();if(!r)return!1;let{displayOldPrice:n,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:c,promotionCode:u,quantity:l,template:p,wcsOsi:f}=r.collectPriceOptions(t);return Tt(this,{displayOldPrice:n,displayPerUnit:i,displayRecurrence:o,displayTax:s,forceTaxExclusive:a,perpetual:c,promotionCode:u,quantity:l,template:p,wcsOsi:f}),!0}};te(Le,"is","inline-price"),te(Le,"tag","span");var Cr=Le,Ir=_t(Cr);function ri({providers:e,settings:t},r){let n=M.module("checkout");function i(u,l){let{checkoutClientId:p,checkoutWorkflow:f,checkoutWorkflowStep:d,country:h,language:E,promotionCode:b,quantity:N}=t,{checkoutMarketSegment:P,checkoutWorkflow:S=f,checkoutWorkflowStep:O=d,imsCountry:y,country:C=y??h,language:D=E,quantity:X=N,entitlement:B,upgrade:R,modal:V,perpetual:se,promotionCode:ue=b,wcsOsi:K,extraOptions:q,...ye}=Object.assign({},l?.dataset??{},u??{}),He=re(S,ne,T.checkoutWorkflow),Xe=ie.CHECKOUT;He===ne.V3&&(Xe=re(O,ie,T.checkoutWorkflowStep));let Ne=Pe({...ye,extraOptions:q,checkoutClientId:p,checkoutMarketSegment:P,country:C,quantity:we(X,T.quantity),checkoutWorkflow:He,checkoutWorkflowStep:Xe,language:D,entitlement:_(B),upgrade:_(R),modal:_(V),perpetual:_(se),promotionCode:Re(ue).effectivePromoCode,wcsOsi:ht(K)});if(l)for(let bt of e.checkout)bt(l,Ne);return Ne}async function o(u,l){let p=G(),f=await r.getCheckoutAction?.(u,l,p.imsSignedInPromise);return f||null}function s(u,l){if(!Array.isArray(u)||!u.length||!l)return"";let{env:p,landscape:f}=t,{checkoutClientId:d,checkoutMarketSegment:h,checkoutWorkflow:E,checkoutWorkflowStep:b,country:N,promotionCode:P,quantity:S,...O}=i(l),y=window.frameElement?"if":"fp",C={checkoutPromoCode:P,clientId:d,context:y,country:N,env:p,items:[],marketSegment:h,workflowStep:b,landscape:f,...O};if(u.length===1){let[{offerId:D,offerType:X,productArrangementCode:B}]=u,{marketSegments:[R]}=u[0];Object.assign(C,{marketSegment:R,offerType:X,productArrangementCode:B}),C.items.push(S[0]===1?{id:D}:{id:D,quantity:S[0]})}else C.items.push(...u.map(({offerId:D},X)=>({id:D,quantity:S[X]??T.quantity})));return Ut(E,C)}let{createCheckoutLink:a,getCheckoutLinks:c}=Nr;return{CheckoutLink:Nr,CheckoutWorkflow:ne,CheckoutWorkflowStep:ie,buildCheckoutAction:o,buildCheckoutURL:s,collectCheckoutOptions:i,createCheckoutLink:a,getCheckoutLinks:c}}function cs({interval:e=200,maxAttempts:t=25}={}){let r=M.module("ims");return new Promise(n=>{r.debug("Waing for IMS to be ready");let i=0;function o(){window.adobeIMS?.initialized?n():++i>t?(r.debug("Timeout"),n()):setTimeout(o,e)}o()})}function ls(e){return e.then(()=>window.adobeIMS?.isSignedInUser()??!1)}function us(e){let t=M.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 ni({}){let e=cs(),t=ls(e),r=us(t);return{imsReadyPromise:e,imsSignedInPromise:t,imsCountryPromise:r}}function fs(e){if(!e.priceLiteralsURL)throw new Error(_r);return new Promise(t=>{window.fetch(e.priceLiteralsURL).then(r=>{r.json().then(({data:n})=>{t(n)})})})}async function ii(e){let r=await(e.priceLiteralsPromise||fs(e));if(Array.isArray(r)){let n=o=>r.find(s=>ze(s.lang,o)),i=n(e.language)??n(T.language);if(i)return Object.freeze(i)}return{}}function oi({literals:e,providers:t,settings:r}){function n(a,c){let{country:u,displayOldPrice:l,displayPerUnit:p,displayRecurrence:f,displayTax:d,forceTaxExclusive:h,language:E,promotionCode:b,quantity:N}=r,{displayOldPrice:P=l,displayPerUnit:S=p,displayRecurrence:O=f,displayTax:y=d,forceTaxExclusive:C=h,country:D=u,language:X=E,perpetual:B,promotionCode:R=b,quantity:V=N,template:se,wcsOsi:ue,...K}=Object.assign({},c?.dataset??{},a??{}),q=Pe({...K,country:D,displayOldPrice:_(P),displayPerUnit:_(S),displayRecurrence:_(O),displayTax:_(y),forceTaxExclusive:_(C),language:X,perpetual:_(B),promotionCode:Re(R).effectivePromoCode,quantity:we(V,T.quantity),template:se,wcsOsi:ht(ue)});if(c)for(let ye of t.price)ye(c,q);return q}function i(a,c){if(!Array.isArray(a)||!a.length||!c)return"";let{template:u}=c,l;switch(u){case"discount":l=fr;break;case"strikethrough":l=lr;break;case"optical":l=cr;break;case"annual":l=ur;break;default:l=c.promotionCode?ar:sr}let p=n(c);p.literals=Object.assign({},e.price,Pe(c.literals??{}));let[f]=a;return f={...f,...f.priceDetails},l(p,f)}let{createInlinePrice:o,getInlinePrices:s}=Ir;return{InlinePrice:Ir,buildPriceHTML:i,collectPriceOptions:n,createInlinePrice:o,getInlinePrices:s}}var Rr="_acom",si={[H.PRODUCTION]:"https://www.adobe.com",[H.STAGE]:"https://www.stage.adobe.com",[H.PRODUCTION+Rr]:"https://www.adobe.com",[H.STAGE+Rr]:"https://www.stage.adobe.com"};function ai({settings:e}){let t=M.module("wcs"),{env:r,domainSwitch:n,wcsApiKey:i}=e,o=n?si[r+Rr]:si[r],s={apiKey:i,baseUrl:o,fetch:window.fetch.bind(window)},a=qe(s),c=new Map,u=new Map,l;async function p(h,E,b=!0){let N=yr;try{t.debug("Fetching:",h),h.offerSelectorIds=h.offerSelectorIds.sort();let{data:P}=await a(h,{apiKey:i,environment:e.wcsEnv,landscape:r===H.STAGE?"ALL":e.landscape},({resolvedOffers:O})=>({offers:O.map(Er)}));t.debug("Fetched:",h,P);let{offers:S}=P??{};E.forEach(({resolve:O},y)=>{let C=S.filter(({offerSelectorIds:D})=>D.includes(y)).flat();C.length&&(E.delete(y),O(C))})}catch(P){P.status===404&&h.offerSelectorIds.length>1?(t.debug("Multi-osi 404, fallback to fetch-by-one strategy"),await Promise.allSettled(h.offerSelectorIds.map(S=>p({...h,offerSelectorIds:[S]},E,!1)))):(t.error("Failed:",h,P),N=xr)}b&&E.size&&(t.debug("Missing:",{offerSelectorIds:[...E.keys()]}),E.forEach(P=>{P.reject(new Error(N))}))}function f(){clearTimeout(l);let h=[...u.values()];u.clear(),h.forEach(({options:E,promises:b})=>p(E,b))}function d({country:h,language:E,perpetual:b=!1,promotionCode:N="",wcsOsi:P=[]}){let S=`${E}_${h}`;h!=="GB"&&(E=b?"EN":"MULT");let O=[h,E,N].filter(y=>y).join("-").toLowerCase();return P.map(y=>{let C=`${y}-${O}`;if(!c.has(C)){let D=new Promise((X,B)=>{let R=u.get(O);if(!R){let V={country:h,locale:S,offerSelectorIds:[]};h!=="GB"&&(V.language=E),R={options:V,promises:new Map},u.set(O,R)}N&&(R.options.promotionCode=N),R.options.offerSelectorIds.push(y),R.promises.set(y,{resolve:X,reject:B}),R.options.offerSelectorIds.length>=e.wcsBufferLimit?f():(t.debug("Queued:",R.options),l||(l=setTimeout(f,e.wcsBufferDelay)))});c.set(C,D)}return c.get(C)})}return{WcsCommitment:Wn,WcsPlanType:Hn,WcsTerm:Xn,resolveOfferSelectors:d}}var k=class extends HTMLElement{get isWcmsCommerce(){return!0}};te(k,"instance"),te(k,"promise",null);window.customElements.define(ce,k);async function ps(e,t){let r=M.init(e.env).module("service");r.debug("Activating:",e);let n={price:{}},i=Object.freeze(qn(e));try{n.price=await ii(i)}catch(c){r.warn("Price literals were not fetched:",c)}let o={checkout:new Set,price:new Set},s=document.createElement(ce),a={literals:n,providers:o,settings:i};return k.instance=Object.defineProperties(s,Object.getOwnPropertyDescriptors({...ri(a,t),...ni(a),...oi(a),...ai(a),...br,Log:M,get defaults(){return T},get literals(){return n},get log(){return M},get providers(){return{checkout(c){return o.checkout.add(c),()=>o.checkout.delete(c)},price(c){return o.price.add(c),()=>o.price.delete(c)}}},get settings(){return i}})),r.debug("Activated:",{literals:n,settings:i,element:s}),document.head.append(s),le(()=>{let c=new CustomEvent(ge,{bubbles:!0,cancelable:!1,detail:k.instance});k.instance.dispatchEvent(c)}),k.instance}function ci(){document.head.querySelector(ce)?.remove(),k.promise=null,M.reset()}function Ur(e,t){if(ae(e)){let r=ae(t)?t():{};return r.force&&ci(),k.promise??(k.promise=ps(e(),r))}return k.promise?k.promise:new Promise(r=>{let n=i=>{r(i.detail)};document.head.addEventListener(ge,n,{once:!0})})}var{origin:ms,searchParams:St}=new URL(import.meta.url),hs=St.get("locale")??"US_en",Dl=St.get("lang")??"en",li=St.get("env")==="stage",ds=St.get("features"),Es=li?"stage":"prod",gs=li?"STAGE":"PROD",xs=()=>({env:{name:Es},commerce:{"commerce.env":gs},locale:{prefix:hs}});Ur(xs);ds.includes("merch-card")&&import(`${ms}/libs/deps/merch-card-all.js`); +`,le.MISSING_INTL_API,s);var C=n.getPluralRules(t,{type:c.pluralType}).select(f-(c.offset||0));P=c.options[C]||c.options.other}if(!P)throw new nr(c.value,f,Object.keys(c.options),s);a.push.apply(a,De(P.value,t,n,r,i,f-(c.offset||0)));continue}}return Ji(a)}function Ki(e,t){return t?x(x(x({},e||{}),t||{}),Object.keys(e).reduce(function(n,r){return n[r]=x(x({},e[r]),t[r]||{}),n},{})):e}function eo(e,t){return t?Object.keys(e).reduce(function(n,r){return n[r]=Ki(e[r],t[r]),n},x({},e)):e}function ir(e){return{create:function(){return{get:function(t){return e[t]},set:function(t,n){e[t]=n}}}}}function to(e){return e===void 0&&(e={number:{},dateTime:{},pluralRules:{}}),{getNumberFormat:Ie(function(){for(var t,n=[],r=0;r0?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 diff --git a/libs/features/mas/commerce/internal/data-source-wcs-0.2.8.tgz b/libs/features/mas/commerce/internal/data-source-wcs-0.2.8.tgz deleted file mode 100644 index edddf7e0d1a26a52db75f0ad6985a65f0487d6f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5782 zcmV;H7HR1piwFP!00000|Lt9SSKCOk-@pASddZ%_=E%Y}24>7883)J=_cDPCReUEiT! z&Ck)_<{TQ0#zwnMisB(O+Z*fTZ)@utjb?LWqXGHgVZFHtf9K6eQ5Z$B6#^?yrNbLT zlE42UVRdDtLRQGid;*f{?i{?^CEEud-tX<~emL3d?qlqEK0q|4YzM<}=w4pMq}6D( zNY@E^l+p1#a)j{_Iie8_uc>2V{D*^+z3tr&OLzT#5Du)^4SZq;er&lub;uPBsp}Ir zGKl3nB%}j~kFacfMaeJ>hBS=FB>0JF*$SVt73R7%M;vj8Yu1XdoN&xW~$n?gp~9q2*8#Q9GnDqiiRgj9FPf?zmAz2d>Y14>}{ha-&R-1|jqv z)ZvWmOdd$4q03o-pHUpTcI=G_l>ne<5KB)`eKXOIwx7LGLq&7t^7ASeu7Jyj1xj z0tvHpeomu5K_GZ^$so5CLar@uL^~jGMznHQ+q#!P@iG*|s6T>e6m^`U0dbc!?vVb- z=OUkn#FZ&LAkq`cr2hfUj_@}Qtqy;u&@+)4K)8fazk2ODilmh zwXKJb|4`BY;_;A1i(mhZ=2~9=Yd7@zUqq1jc=uhQnhvuD75`so{WsCpn_T}bjsFV? zbH@LdZjKHQ3@ZMQLhs$k^&A?`R|rgB|7&Zl=H&Wstv6eG{VyVD|Bv?ne9!(LcOd*f z`_K)MZ4IeG0N1WvkaD~JwHpThfcmM+=eHr`3qotvv0|$p1tT!9>Z928qUEg$3~>~b zo!$4{kNYR*``r&a$J^b*U9ij&>gM6ccl&$Cf9&pTRm9rbp)t1Hr)>K5EC5`Pv^X6Q zdoSH<=o`xi+cM$C6iWk`39SL9U{(i8>tm^j(K==u=8>IWbfcZC&{eXxXV{@Jd8T4t2cIJT5K5IV0r6)y2mHo z-*@jNSqBMyNgqUb|6sejUrre4dI2ont00OyYmG)rp$V#wHS*#|as?)QGVWc+A#4C~ z$RajY4uQ#!1NZL{4aWyVTvibds}M}s5Qlg!j2|pERydlN3_MbaP)X&df#o>=qhvS< z4q17e6P%KfA)rhW=7+F&vv}6iWozi3f1%^$GbpbXGa=!>={TF&lWT%yubx|zg^~<4 z^7q%Ti9Z5q_tQ^MDf@XHxDIKMxA?o0UvyilmxWikRnb=LLxXDny^#L5v7X!iYppjn zwEnkm?7^+1YXtgSq{%jwM6jFPk+$LtX5{fA!T-o z0y?>9%x^*?El@?VsL(MA)KI3;<=QfvPCA1&Iy?D{YC5y}H#_}|hB}LGI-!@&$kMl? zk4`{B`a_ykkJVIDJ@o-v>f&_NyoP!|8kVFyhj&%=|G^yE2Kf!MbG~~3)AhfNR*LYiC^>^HzPi~%SfAYu+-E%C?>@#zgYtfrNQGzz?HQJg&r zdRycU|EUHcmaQHl)A0Y-=pz8MrnOq0oB>5p=T#jcNv8hD>${+&I4S2bC_lHLCeUFu zX|3tgud;E-3E%(+HqI-OuQE0TlbJer`h8WLF2z@2aIj(lJKDe=-Ej!-Tb^+%thMI}3HdanTgQ;W|C5)p&P86S+&J zwxxjB{M0F}+iTta8`A9)M>VQN9EN9eN+$(Nv<5@fztfYAsj~+i+nxoz@JZr780^pw zPNec8^mA0zLg?lotoA8LpF|r=r6)`kI+2>XR~f8;9(HKOSE%aTB6kI+wK`<^hW5^F z5e-5@oW3M?6U2(kBa}gkU?8hH8aVCz2nuau=L=_#+o z6U|iE5mRj$S3T@o=<#Z%!6nR2vZpehxbD+YWKobJ3!(UM92W~%9{y(eS;Z8EHmri* z3+v?*WC|T+;1qCWl-2RgWWvBxtI!|DL`Sg+!n)kZ#a~en24U18!V3j+5^`2=?+jig zPKLn$gXaQ#$Ws^u4uzRx2Vu0FO}(q-nPfd)YRq0`z`$~pE=AwXln7f9OQ)sZ_-d&KDU#rJED@5Gy<1{ zexHWt5k-6n!gJSwv6iGwmUt}E_MFK49JqdU*;uYk3ZVkZb};fm`BKPJ4~p?En0 zOG+s$qh_gj6o%BdGgXVIS?WfIL8fLFF+)iJ%|U==K+-xYg(P7c;uS(-N|j(uv8A4D zjmfPk;mb$?M{nD_R8aSSUdaAmYv<1Yw$@tf+Wucec&_~)xy0wSwZlrMI&1bYd>Wr* zRa_dvN%zxFq{<&_sbAw!ZjBPBhU$NKq+=s-?q=K@i5bkH_sK4fDeeuy0P{d(jrkI1 zhsc!iMJQk)eG)R8qCb>7KxSYf?+%#*BUu+oIu>(xlqi+Y%~7J=B-%~#1Gq^_T@|@q zw+Y|HeV&U_uSL#dAq2w%{T6)9D44aBsWlm;aZgj%@=0sRNuhDb~c#ea~uJ7hlnPSUiH+5azi zRe2tsfP2n=wi>zjf7)vez5l8oWW|D=N;Dai7t8B&uex!gv^EN^9B8Y#X&15J~x z<&>zKyckP_T(SvOp#q5Web7%kT+0+anoVbBgE~y7yaTJCwX>RX@=$bL@f9P}N7&{Q=rwabCKIa2Y8IWq z3zQg<`hA_351IX+7t#OP&CPa!{-^JMSx9)U{hwm#B2!BNW{#Uh4E_*i4 zr2q&t0|6Dyv*npEo`FIZ*RwR#Vk{y_P9Pi&UQ`cZ$c}i{9d7|$B>NsE z1%uva<)|+FLIj0BW;zJ~I58gaQof(SBJS`ks5ULMY4JUq7L|LH<%~)x6sfGD%$#C* zDPc+ly2dr+oc^4aQ{|f})apzru2ttYM^rUCrKZR_lPlk~LhbvRb#JCX&ZvL$qiCS~ z@~vE?R1scg)$_=jX9|_P_U-B9Qy{VIHp6LJeEHPNXz(d~NWW=bm7iIWpN{-RS028! z?xRDh|BJsSZUD`@j(hySjiUSC+WP*tg#?rgOTIOrOC3_IRfCLJLg~764Y~w~1Ru1)GgPhyzmz~z6nAnf|93aECwZg5F(|( z8Ppwm{TqLu)eZ){2q-Mnj~KhhySro`6t)k?yJYVJ5h-T;HRf4SH^Y4D-Nu4xP;+r# z&!t#32OP{MaxZZ-S&C^CW7PRMK1Ju}ONcFuLf`dR+0(SFC`6+HX5-Bna9gRvq9>S$ z0B3jmeP9F$^(`-A_sN2WKMW!lxErUu;nj>t<qGmc-JJa-*I!mXEN~RkC9~!(Wp04yo6NRup}5!LEk5`go;F!0f25&3P8T5ME9cMw{A81){3z9qpZ zCUGRl#}O-y8YfvXzYaWE+5XBz1s&xweKBqI^3u zPqV8O4;Xk+9Yz_c{HtNEn{CqD4B5e>f^&;?3$KvMm*eOxnHMSFNu-t(Aus8YG` zhXa0k-t$TsykG_2S&?glkqm4>xr+-xaJl0NXs`odu+6c6t6F07vjiyA?bPnj-TjLG zH{i%1>S3h^?jIMK^~DrYET7odEj)o)d`@mA9-;T32H`^3mMRu1ZU=bmja|A@>yZCC z{-?R?QMPBE@gk^)vpU(nkq2b?T`L{33_3o)D+tYj?%^J>VNm(&nRlr(DUigbw@Ol< zJe>!M_%fnlbJ-y5p|yNwh>c5j_zoEPcq}VTYQ3Z}e@CRakI|8q(qh$irik6Y^poyc?Nqq%}4w;x`t8C;~`4lou6FqDo-71y;4Mt=DphX7O z7fPa0h~sF*WDF~tB-YZZH5{TV{3e74?(oOa?gyS1++a1rK9rI?j5?J{olMo#^XaPE zkHYvBJM+S)*mYDmaVnKhlz{PxQvwiU#HQ&*QO*l;1M@({V_znxcZV9aolriMvIW zi=?T*s0%dw0N^eNzi`Z(SVpjf1I8rF+M~py{TMlphSYZZV|LxzU>J|d#p&5a%K4aG zzGd>x-G*TYkNHZl=z2f~ceSpmH#XRK z+AwfHo=usR6@0n@O-H`|LFDTnD=X=1X59IL1bU-5ofJOh_vwiR#VB5=oL%7dx#!w0 zlr^7y+^2mPZ4QGzrvWY6!YLcg6Ztj*3KQhKQh7I)`_=~Q?}%CYn z5u1yOGstzZ&Gsi=^9x;nYp z`A8jD_A-E8_@0q2(H#s@l}oKts8g|*Y-CH5yzJr*u5aL&RT4cx{*s8Vj3~^cUEdi7 zVBDtBtidlmtq*;YQHQdeBw2>C=6442cLa`?fnd4#`A zOp@w=BRmkh$X*X@4J)UVBvK&;+ zQ8WRnT%k$@Z9Z7Cf*I%yo(oT>Fi&wpmG`89GZ6HuSR#Mgg|44;6YCs&x5}hG9%FFC zNhA>|UfVMHmT`SM081e7>RqH%KM37R*YA*jAnKe4>=$`Ojs+k)0d60lHXsEvXd>)5 z0yki0s^d=@Uj=BL{Jwi)kb^_KuJm}Ol7GlI&M+GKqkb~RPZw~e4T5Ec&`DjkUE=1jVEu%?AMJnT zdcL_T0r%iPK!48u-(1)Czb_7jmz`D1C(D{`&!hl)z#Sd=$H`DY2*6NaS`m(WnQ0@P33IEsh z`JaUZjsF_|HU4Y-*ZBWqhT`wP&vy^t9{k^E=FWe$);IP3|3ZSse~te?1pcR=gE9(T z`06`qPkP<|9+&n^hrXWr-c$m#_kY*h#rIVCSLI5#AYdG?**ml1qfp8X>;+%c%D(TH zz71Zu%kUq(A)xlmEZ;8L`W8QcTm1dkm&gBgz5l zfqU@3v7S5s*IsXH{9j1W_^F^xkzVCm%6#lpL`M-q(jsF_|HU4Y-*ZBWqh7|vkkEhJNj(hHZ zX{@d1-v4X2TN?it5em?{2&yZEC|wBAl}7{g)6x0K&^!lRemp480g;~$$0`=T^yJh* U2OV_K;n~9f0}$rt&;ZT=07SxMJ^%m! diff --git a/libs/features/mas/commerce/internal/eslint-config-tacocat-1.1.2.tgz b/libs/features/mas/commerce/internal/eslint-config-tacocat-1.1.2.tgz deleted file mode 100644 index ea3f1a8869a3bc526529c98df0ace53164e24d2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 806 zcmV+>1KIo^iwFP!00002|Ls`aZ`v>r=Q)2xls z(`Vv(daLVtmTh~UrEr#Q9XU#8we}#1keI8fJVBC5^!4?HNXYyo00hgJaVhk7I)~p0 zB3iYx>GR z=l|mR4DA0X|GRd#&i}UCYtDcBOz_QTL}K0THCj_7$$Qn(}JoZMTZGO%;S*e{X0 zRgP;t4}d@RY_ifFBYVr3L(Bw~jNewxWYJ{jw9+{f9u(SB^+Sa57-64HLEVP^8xjh$ zSl((?p_;L;-h@aI$|xvOR5wR4xtH3+R{X!-di#I>cU))d{on02{J$4D&?h@Nf1+vc z_3>yh>g(#jNI1>T(3*B|pnnr;;b_`qGSQTcka}S<%=~=F+>}2k?2MCsUC>Ok2sId< kk3N4LU!0AH`q`IHS&Zha)<`3bH1d@47wSCl zX>2RVl24K$zJ&jN-7^|#^zb9W*jX}N3ASf?x<@_Jk9lgS8+Uq-R;WLy817)`I@Ae# z?W6zG;-FfsZf|XoymWy2*5)SpX|u6it<|=-tAJOn)f+oC_*`HeCc?-MbPuZXR66`& zNc8y^fqUH}?Y8dwR?i{(q!jcmpZx7_1V1&Au};2xA>EN<1eWWN(mMHEAOKRi^UgoZq=YQ}GO?gotxT}A?)63k*6(cU^n(5_WbP3MKK}l9xj2F^?U#}218;w#9t>?mz2WF4Lvt-gYl4ReceBQa4tM|NWEaJ8M^#n15;QjCe zaP;9L=JXhS^Jp;g91`%nNB$8e3h`+AaA=R2WXo~Y*LMqmUU|JJD3I6W=;Zt#EpmK* z_WS8c>+JgU=nbZ}vjEAMbLhMi)}Y8o7o7mJ?9hhV~ojHWESffEsnDO+AZO0e<>{XBmM##;)J7Op`ifg=5vi`rJ{Qpv*IPz&|LyEhFLg*?@ z+6C(%9At?rA=tQLyG{MKV3KTFEHj0>*X%D84q`F&R;YO(V zknGX_jx3Lsijg%au7^m>002rG%`Q~rlB1r`u0FDZQdE-R6;Wj)gD|0p+xZAaLKq16 zg>epiXz@dlLzdnIG?SNVvV_N7@cF4wi9Zt3DDkiBQM{BFF3l4xkvLjwZ>LG6aQ6rsw)0l1}OKd-0zEZzw^&497jVEdEy{UE%CUzM}s57 zfCsyF@2JyEM)<;mj~qUBGGW9}W+S3iYxs0Xj{jF-|Le7_?D)Uh*i!a?8StFr|5&LK z!~gGZWUri{a)K|`2^O+GE?-G=2cNPFoazAOiJ;gC;)D>|g;kq51rtCX|Mgw>&aYU| zc%K#;1I!rzZ8fs%|J$28%Kt9|llaFF+zK#+^RPFj41lnot{;-MhJx^$uD zDKrn$*1q?F+&6-3{}*rhIn1*E+nMoStx;F&ze@pS|CRlJ`Sw4t^d;{=5*uDpZUFJi zdH#Tf$S=tk$n$@ziT|tH^{s6DUsvltOMykt|IH#0Dpy#VI->#g(716DWj@O=Won@v z641iWXog`P&P;H=4ziL+h zYg=mlcR8?Z{g*xQw`-!bQ#G-Ta%>ULJTgv_~1;nW5M>+4HAPI8(w<7(o)$->5)cVg- zV8QXHUF>X|6lC<|MUaMbDlu1@UG3X z&_rTO!XTX(BhP~&H9=!ZOg+$fL9vS#3RUE7#_{an#KAq^dklzT*OgT7XF8Ldo&R69 z{jb-z8(I6mrQ*M(z;o^YjeGmdqrZQJv%ktsDmVFJ-Q?3;+;rcTf3Q_@ae1d&ljl}r z;$pge)kgCLLH_uUtwb&?0GRIoH>=gG|KHkDBph? z(u52REP6{f`lVPfNFaT28P>sNj4@Y(uexLXkNJ?~9cFkb$Ir>yz6b0NX@UqjXsIVl z-d8rE%zIWPM;}lVbC3o`bkdDy>+$z&Bl9Lr5j{0Opx{JZ9;2p)cD>d;_zSpLB+;dG z6P)d3N_^?&44ul;8qKo7KWE!a5Kl^k(TVDY!DFTo847!D2pHG3fj*o;JYYiDjb!^6 zYlG#ZY(bDm$TD}xD9PdHpG!Gy0(H!$YUh`Kw2w~So}Nu&dIhl-DE$ewX%8ZpMe@G7 zxwtsLyq?bgf=5zeH5UH-7nkQJH^0N6pIO1> z32!EQs(g$7VROGbn$$Nu zYZ!z%Y#uQ>ZWp*Dq`{XFJZkH>aD&SUW#W&FKGA(*4Sc}7qYmO5eaptxm}rwfXLCo# z53XnWz~lV~mVyLb4|%?$&c?q-0aa*heCMIkH?-?&6PI&^&tIE`aI zxUUnb_N+jZ8F_Y?@*DgI33)>;5?1qqcY~YL3S>}CDkq);sTV_=;pcXjsN%e9NRQna zLF@AJjLk-fL0HB{)a-(%VN;zog=Y;sy$em%A@KOb9R(!t4cF;fJuUEAX z8(k7Go+YRjk< zp{~5aWg!|C7IKCI;*CJ#QI-&v$-kMLqdqjG)H|U=3cBVP2_52F0D#zkYTH_^u2nI% z|4(g4tBS9`;8($&_f*pz%uyub&h|woZGj0Lj%z%&@|El8o?Iu4r!Jsru#i@Pj#?Qx zR#0J`FRlt={cdqnf>|3piF&O6M(jcT{-iq77<02r8+G%Sovoc;&0l}%P_w($=^B(8 zo3vVO{8FzOzt(E?Zn5w<=cJzS|CcR@cw5@Qd?HRwx`v;xa&{5Hq>NWx*t?5EDv5 zSVEYIJ|%6*(a5{L1s$=k^T+7Aw(UM(Rl==6;6ci4*O1$V$46A$Q8kS}aXjLm1@h+ky0 z;r{_9qnMMG*E9_r3{y4H8INs?9Xmxqf|U5)EYx$?fiXkC+F@XQf<8e}>b|4j(YB%c zd|#OXwYHD(SKLAb`U6uZ3`?<3OV&eYd-#9}d4S*xFrlC1(SZJTWetWl_7!+`zNowCthA-J>8Ot5HZol1^NwWKT@o#KRVk_M;|$ zjCxH*5h@2^o6F_n#t*&zE5fW+p&-@G54|exGzGhcCVoqJah+R9H z;tkSH&UTm9VIUrM5TC6e;ldSGSN5yu#&xn5tw^jfN5$B(jkdC6V;-q#@~>i*SOl1l zUC#XaSFy}pf0)X;@F(?HztyF|c7VYg1Yr+8gc4ow{5%nw#U~x`jV5|k7V+^;n^PN& z9QpoefGueSYkn%?y_FU4HdKaOtsc0>-I1)>dyzU{Lj{ie)VT9`EkL`|n$6T9w<}_d z*kQq+38C2?WugfyGLI8qfS;c9AkFXu&+o^3BL_9jIrvr=E|A%BA!HRnXKMA$twu4* zVnV^7URtl=tLEj&%iI&9gUIrZZ1EL-*w#U6eG0r`#8yiyGdUz{W9qMwCRuabwa2{* z5XhsJ(#;&A8SR~$c8((E(9c!ZX*~8SLNcki1H0X-2k}pUVgCD{aeixIJZ7B#&pZDG ziE93588F*ovJfXV_K|KxWRs)uUU8EeHjV+qXk4=Cl5rHfHrUpo1Yy3zLB#Hdq73_c z2B9vf+=1G!xImcj{@0c3e@)&0vlLMJ|6@bm`~McI{6Ed{{=eR_`v z*!*G-Hb1{}3lA&{KNbGmOVInS9ai4^|Go$NUsL=4mIBKDEBmkPzq0@7_rERD2h6bl zIq!eltnO?o`@alO_Fvim7i<63``>0l_Wf^*j{axg|60%9|GuN{|62|y`>*W(%eVgv zfB#!dIojk^PHztlQ*S~lQjWaiCOgJ;{NE|u@KT|il#|xKq|~$XGtN>v*!Cqp2$oGr9Swt=tLyX2qd#O;IXH646-&Cg zYF)PfxIDkP2=R6kk_ykpi1#IlQ=`zCpy5vfze$|Ypu^v`Oo&N!J9#>ske=s`hE^&O ze6JHElWe_1?PSuxH8U(+Jjj;j;XdHy0xc)7f^jkj?S*B~^wIh#;|;w}a|Is|jQnJY z$7xb|$8T|ngoA2w9DIt;1SioCGUVhlqf<_ePChq^JVE2Fp`LmcepO^%8b5Rvo=lT7 zlGhcaYI?<`cEoQsi0k|e#6~d%+O;ZQE zlYKcab>Q6v7hw33sg4_1-7(GF#Spe@A7auo*8kHDKL1?~tV@1mo+Q~7MUmY89M)jM zMG#Q~ioS3ta8iBbKy;8|;oy`8fMor!SvlN)?99+CM{iHWV=boYqSKEbAO5FysYVo{y$bbLe z>h-^>?*CW{DEw_D`}nB=G1W7!z5x^pT}pg@6w PXM_I(@NDmY0N?-sX7$LV diff --git a/libs/features/mas/commerce/internal/react-env-provider-1.2.2.tgz b/libs/features/mas/commerce/internal/react-env-provider-1.2.2.tgz deleted file mode 100644 index 0b66ec771f221a7caa07d7c58f0fe99e1ddf6ca4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3312 zcmVC6T?U>+e!7n-!WgznAUZjx!!_Cu#j zj4eQ|v3+GZDU;>D-*Y5C1UQejENOdvrVU1q^OAHPvP59npR6&h({Nh1{b}HP)Dz~% z@Rv10qtV!Hw@F?(!1i{#MSj`d*=;nNySoi&*J!rdja~R$Clfb86bg$2E8mJo2}9D) z9|Y=;*98W7Ob(Au|J5hG(~}p+NBxuYlL#+sfV%s3lQ1Zg!|4RYxCLn`sO zE_sFJA$dhZ$}g#7#$8TM&yRckj%>obk7$3Q1WK_W7c^Y;90C7}zr| zU^qE5%$bc+a;<8);{IU;9aJhuDyMXPt--wt66edc7dkbfW zH{MFOIQ@B zr@-RY6xW2MBqQL1aUs-o?qTZo<>)L-nRHwL&#{+@mre9?CU&K(D^c&!HRjzVRh@DF zk~x$wP#jn~O<*>nP(QFt`B)|Pgt-o=Kout67Rc&~@4HkgIb+dG$l_|1NINkZ7~*@4 z51D6dRHR%F?P0%f2;%t9Wy1%G|8{mT=39#Yw)a{^{I>}xfvJdHc&Td`r^AmBUziRZ zfpb2KExw#F8Olm z=o*k1a&?p8x+XB!tjHA|Xg*i^kpp#;(s@Nu7c$x~Csqg|rsxDt87VU&LPvBB=?4bY z{$S9NV}KsQf{NU!j!5GQKy-TLB^5_Bv^fie4_bvlmz1Sb2KSD#$T<+pfntIK)(}bR zC^#~!p)?_ybW!n9r6S!OCk*(qVEFD}L3JXXcup}SR%X~7M}nCl6_pIj(qi_YFC+`H z`}^nvE=Bl&Ns>-fGZqD_aB50Lhp5*o@nEugIHQWhu}B<$+QiXV$^&z}8alt##Ben& zyeTQXUruO{ug(gQ!2Qy}++Mn~o$T%r>g^Fid6#_u(Ni0PA2N!F7M?IYwt4jO1>L z-j^q3^VY#ek`*MBwOgk=54pRoFUZ{0L*U;nM$ zy`1=OXS-Pcn*dzHB@Z&#QiqhD2M{dy+^WlWE;Yz)YeAZK81rpNxpDG z3?t^szVB5_Af0r={_v!90RfpGGN3fe=%Cw1tYapUJ`s20lIQRrG{gflRaWOrjq;mg z$iO)@wSI{lh`kh>gfMS5qA*2cO6mM52$)>yKgeWB9oWUR1ybn%Vglso4Zj z;Xl=w+kgkiu(1at1N6mYv4N@}wGm0zs;a1|xE&(%T3nH^GllfSv4qv34innIkfegh zisja0>QN4nz8o4lW)LIH(y`1obZRv<;JOlRhMl}%ic6hf5grB5ze_!*b4bRWi;K~L zAy-Q-pp+lcfI_inFRU+hR1doS)!a3kEwh2IdH-tenT_P}2|gyQsafp!(|dGDS;J9& z?Rys2$zm{c21A30%sNcedgL)tmy<7}hOJ&OWl6BAriv(+^EagctMA{`k}Jm99?_ka z^JK5R_q+4^lOc6R?cvC#)ZV6z#?F&g)Be5LY>i6Bd~Nwm;{RXu50C!rn^R|9h&8z5 z{uhY0?Ef1(+r|FRM&J>-#pxTy0r^^Qj&0;Yip*GqreQpKM2>o%IqKc_@hNi3^ym{)d%L7g6V0WPH{AnG_ z<$;t~e2_nfX*}5k!44DdY?c4q*P!dwfvYLTqi_9OIoY@XbV)6YU|ij`&%J| ziWX;nHQ9TUyI&rqPKc8_T*7k+ZRPgdMSSw^0!6x5IE;6J9dY;Z&9Sb%DjE79HJ&H7 zU53bTvrg`Jf>J;RTrsx{;y*rgIl8}xwn5VZw;}Vi6zbASmO$ew)^OdK+z?eudN@-N zBWTWdLsY3g8Y2#)2YJG$j$t%S@{SJCk&zmCuPvQ3HyShVLwO=Z5O(VIE7b}@EYs)X zdLXOAy2fTrmDMXo%OodwtPI03^4`P8G-Av!VtI_s@$Il~1q|H$h=O)(T3_t7Kqpm4 z#n?7UPabL`>XHJ8i6jplxs}@k{{$nfTj94!-;57+Dz2tR9}t)hy^J}MXlk}ZQ@eqM zhju~*JQxJI_x~~cULTD+_J3M?tM|Wli}!z2FFeakB=snd=>_3b3px+I6mNb=rPke_$ta(tp z(_IJT-~WDi|6jcS-3%1|zwrNs|1bQ1;s5_XVfFssdVN2_9p`^{TH7nPM^MpK2q2CqL1KPuj1t{b1Jn+hO(o-}hku zn|r&(@BeND3j1H!|HA$k_Fuaow{{wL*#E}%%KeXKyIt7-jX;jY&NJ6WuBFa3)5cd@ u=x;aA-)x)LV3@z&DzCvLf4e=_jqyWI&H@T3pnwAYG58-eDqz9@asU7~BZ3J4 diff --git a/libs/features/mas/commerce/internal/react-fetch-provider-1.2.2.tgz b/libs/features/mas/commerce/internal/react-fetch-provider-1.2.2.tgz deleted file mode 100644 index 632564fda63dfb5be90d3f91d9e2c83e96f1b5f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3179 zcmV-x43zU9iwFP!00000|Lt3CQyaGy?q~lB)xA^fjJ)ov+10L%Ce$SCs`(m5GBrde+YT9^LIxo_Z^qix!BBJhRI${kbCk;28L}9=J z<$RJqtg$qk&FxNytg467-rQ`GA2zqPo2}ONb`$8Dt#+r`hTnB|;;u54iVEn;x5_bt zA<6GQq%@wbtCYwya`5u>zenWo^yJm?%cGOG#|N*mw!d7U8e0y-XeRh*tVp}rZjl2o z955}%_sKcdOLER66IaZ0VwRKBx5tM^T}{G+VJIe4@h~867%0jE=8-WIj0c3v8lizl z1e-v;L|!#!Bobl7gqo3XX!L0!4w~dNihRxl@bMwk&K&X<7uscT{m5UxPXU`8m6ks^gl2mW$TlA-wY8ao}h z{?y~ah$ullMkI3uQ(Cf{*cnODl;kCsl1+GEtOv$WV=i+R4F%W^ba+!YM+41FaMJ~N zl}d0|`7;8X1MYb&Ak)BSQbPYY$ha0R zCKS0PL#Es@nHxj`%Jz8kKnU%P2)zq z3ze9%(mX*r>11HeRxN^1u0DmktB`x7*()J*^euJ>_Q#Y9Rw?JPyIhUwG(00lp^t=a zpBwu300}M?UM1nzgykymB^cNrF||N{NZm23Fn~r31uA7rTP8p55jBe#_`>&$ettuv zss*oL>iazncsRI#U0aGuwMNupRnM|j?oywFY2B5WgOOiAUYCd-nOC6&elml(!vFx~ z!Vc$0L4N)j#Na4IS*-FQuLR94|gjx)g>$y7C!vaneL;DumNvwxU=#Ek|&ut<`H>cm6ACS01@&=T!h zBLOVd09RCTFenZN-|fMmv?6}L!yOsdVz z7w6|(u6^v49v1ztjQw?Fes}zTZ}k75*gqcDN-N)Y1c*-<=`jE*mJR}UMIX8M-cyHR zPgbH#{*MP9yIFJWkLAnpf2-5(EXMz>tyYo$dz4bR`NGYAw{AYpfZgW7zLxrEo85EL z+{ebhD|eL5|2z%;H@9}Sm+`+@jAgK3I$;7K&IrQD6VAHb6$x@1 zE~crn@n;Dh>MAz&R{NTY45UFW0{f^7aOgEZyp-yrO?`#=7L-5&<))^X4G-<_TI z^8Vl2F82Rp6kOBgfKFJsOVZ@3K8@A&nW`Qon-xi~AXvd8+HhoSRnO~E{=}KYX}%mE{@3Y35i=Ab)yU^#)e3j^?z}TfjMZ)o{dbK zgkc;Fhk!8H#Ub@2Gj@PI?y3+%KCCx*fuds#Wpk!3D<&0I_51iF`~9*$xuS$2_ciYa zLkX%a6Y|58F$V-@q2!=xmg>P2BuX7|WqGE;k86H~|G`2cauQ>8&&+JUI|fV=%*pWC zaiH{a3=MMisz`cnRzPOlgDITDjR38u9#atAP)~6%R_gjNmq;)8M9?A3)queGNjOzR zNrypxiL;9&zzfyzi87>@JkZIl6pO&QoiSEd=kFJI;;_Jrcis1(?DV;mK@J* zEUGJ|NW?u{2ykTt`4F+21%yvz17gx=&g~~z`y_8_;D!|ZAmGk7NZlnM3jZ_2!UZ{? zhK~Z68_=)Dc>>cw>O+$3TGLQB5j@1`^|--yX95Qik1E<_9#@%(A+~|&hDGWT3&1rh zy|x4!aflXX$y}yRu3k42o;9M&ktv8KL`(yMRhVqfzH2)tYY3OH%x_1}v7q3wh(T50 z=62V@2O;479f4^p6&f)nUasUdAA>Z@w$o}mP0UdJ-r8}R_VGDB+98?s?}d{Guq4`X zbzFo26FTJ2%EG>=a?*8iU$9lU&V%Ft&ega~dL@97{gX;JSaEaOt?EoLZ^ zKm*vrYfP??gDApYnezY{hu8Qvp`YPdsYu7?8rbOwlSPVP(g|V(X>ve-^a_TXNH)0| zJsfxjN!ZUMRh~vOCTGI315YwN2WQ8iEeNIJuA>JJ?jXIgbYIHii&jbRG{LE6#9|gd z^r##+bQMsP(AbI`-B6so!^~*~&N_Sh8m$b;2k>=MU%dwCuc0l@lh=wf|B&h!Uk zo%uIhuF2T1`ZxeI@MHQh$z`uf^ork5DEG$fzMf~^Yza#k}0DR<9&xcKnVn8w}=(Cxb(zar>|>*xZww`W{I8a?gDVs6t( zLMOY-XWC_X|3cf3nEps`OTS&5o;a8U2u6H3tE7UebF$k2LHl#l7yS&6=F>`k0pSjV zrE{C^gLz_u{D^W(+jf6Me5#1XN*0(&Mlk1uE9N1^MiE@=F`R#w>ZXt7%=~)W=*iW;>Z?zD(_MNhs!e z-Ent$pKFmATD}fl>YJM%c8|+&!s;$~sBlt`9rZ?!G#X^LUSG*L0ot^8x*Hp>%ojrl z8-~v+1;3?F{|gJ|+*bne^L=JF%6lg=+cn7~oxjxhvOtX41ZKjX!nJ zmnqT8zyH8P*ZOqalmBbBmhXRVcbY}~{}`qC{#$(i{cgYi<_}Y;i+#`^f~@|-50AM% z&Xe(u*_%n-_|F&O*WJ9%{W~PsQ&BfQbmh6eq}I9?tM}F6ZstbRFN<+SFL#~m7t5vp zJqbN%pYQ*>K7AMEhr9m%ySV@LD5dcKh5s-7f8qZN|NrkS%lE(6>-({M#r^M{ zPI3R|QA*+e3;+M!`v2no_q}EL{`cPl|2K>L@1v9g{|o#t@V~(S%*E)n>$nI1o107b z|5}|+k^g&?vI4PJ!F6dRQm=&R(pMw&x5M){WAhq-`Rh@64Uqipc$@{}r(T>zDN0d_ RQk1`}{2yyYwVVK0007E0L7o5r diff --git a/libs/features/mas/commerce/internal/tacocat-core-1.12.2.tgz b/libs/features/mas/commerce/internal/tacocat-core-1.12.2.tgz deleted file mode 100644 index 453ba1fa8af5a914e7a0bb05b3c352a003a5e9c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18104 zcmV)FK)=5qiwFP!00002|LuM4cH20zVE^`0ptXG>DlOBJ<0RX1oJ=}#_vDTfXJe;( zc71&#nuKI_C{jaEwl$7k=RU%HfO)b#g>Mkxhd<(;>9NnriA?~7LZJW@3iUA$!q34J zZ;(9Pi0}mGd64+Ok#BDEXD}E%+1f&`?}Nc$b8~BezIpWc$zV8q@?@}uz8MTRhfkiO zZwBAo!OwyeLH^BPa0`PR329yd+A;ZQk%mQ_rCQBJ5hrmGW2m_nW+{Q1NjB}HFvmfGUlM}zqK_gxiBl{)+9~zz zbzgHv?Diy}fVO>$i_wW9W+~z0-azKpb4)! z>aIao8xJ3@p@-;C1)j~5pukA9CG7KZj$!PS2wvCUfDfeG^BEQb>u0jnbF^AR4>#6C zH^{8$F@$>Pl3_kU-nXuCih80V{N%V;^Z!nM_|khQ4{;#`cDL88vLu^2KL=^x`aVyB5T9mA zg!5NfTHuSKq1O96JBuTnH&okAr*VoKDjo7OyrJ4b5lpGTVpYmN1es zZ=TIE*l4@|G#i_~{$U=!!t$?Q|HG}plTB;=KiYhBzy9yyXXD{R^zb%+))1AUU%uY| zaTmSXfA`ay*Sqfy-@M#|ub-?RULC#4=F2>uo)&0xu=xnRjItAq4wj_AGlJfvVV2Lc zJScEPYrjkq^Z{xU^Z^r`pW(>oklyVdzInAfqE+H_lI632iQZXS1aXQZbc%Bvrzj?U z6r>T#@hpf_D&ZHW7|ru+j`L!PvPlgVv}IfnL>BXT662io0YE%KLAvzO;VDLOT3{-y zErNtrFK~F8#$k{EE+L*5L?H!@IbK1|kjP_P1o;vbSqnU%y+nn6bB-7%|&4d~wH4_wmZ z-8RS5m=rkQ%cfJDYxi4r{P7EEgp+s{7xaeE{m1`wxB2%EyFc&#_0#^r8?=KqI@x4` z^8*ZQkmYZp&Zy%Z{a5Gs`(9_mZ1D2k>ks>HUVr-a7ijk~jq)sx{DZI^W+Pe!=bqmOWfjVn&mip&saIe1THAuAPtgb5r?GPN8JQa4-(Qn7Vrt3 z!T9gNNrDgIVoMvn%=2Ig4faod!(joxy|<6`W7##qIDH>1lPrh;K#I@N0WLgsk&nqP z+%3=!^3H-}f#LE+?+o-W6sM#J(hz48#Oi(dg5a8+O;`m#%i}3!(RBI&|80Rufp&Iw zP#1VE)#hi4i+vJ9H*REVzBC}&&Ip6L)-R5KZ}%InxbtDQ>u8_G3c-@;jls=f#|xc-~EIh zCJkmx*`hVq6$w!M0*$}B1b8d--6eckjnRm9D=z4Ery1jmvf{u!n{t?JE@zmKV2aIq z`&DM!(oFEda=?9Lx9=lT1jT~5Xra;LNP=M1U7(Jw>#;wHlLF_ScSgDGtY_EwEsImH zi@H6RZiL}a=VHfbN)bIeSk6we#4oY~I{d?6dW>GYFeQ8a;KF^8_h`B%E%;!_^ z`2zMLA37;1OspNikJB(&M3{I`yVuj1Mdt{8Qt5@>UIbE?^%*bw$XX26j-|T(jF-Kt z<-xx?mzs$P-*(Y5DAeC zfZ-rKWlAU%1`7~UK!V{_!Ysn*JWdi&;u28p78yF2CyVqm?QWK(ago6aLGwipDkmbO zLlDe3VsbGM6d<;rp!3r>JXN3z;7LG^1CLWQPO|AIzBi1~B*qC9Mqu;|8jb`oxL6bf zoyUb33U7x5ZzF=w;1s3{Dgyej?OVlJ9Q|oG*DtR0_i= zT;w?rV-8(- z?!^ebzIgqrk6yj%qf?yBFZu|0=yW|O)=3y7*mw7c(=)U#<_)K3ah|0!&MFJSTD|4( z00h&|o%bL1Uw?dc_-6lIr;oDY6zAtL!62FfuYiojMty=&=iu<=&$}J7^UD?bg19z$ zg;wY&EG~}GI^}zQ1Hl&sWzir>7#~KA!lVgOmXgH`=ZC?R0LUVkf-p_hC6q@3B&z7t z_1D+}6{{Dg<;ANu$TTkYf;3=KuP0`#55()Eu(;?W9SN}^Zfu}~U^Y)M0>PXL|BRZ$ z!2r#0kiv9mtC_B?DK5n5<>JydH_$`u&jBkCAvIBk<1~`L_0@X-MdCwH;1PN>P~R5> zzt6L1L>Yo^x@wlYtlDZ@D6Mt5-;*FbDGJDU2o|TnG^w?I25}QxNE}EFc)=KvaK{<-0+6>cLx1#fuD}9OP5bdVho{ zW1IxxXPia|Ule%&CtD8MTsVc{3JV+SDGnl>(^Iow21K$MhDR@cB=a+h9w+9x)7Pr5 z11ma(a3SmT5y3$oo>GasBoLWn(+D&E?o!^cSL`6GtSg18?|Dy-NehdMI=0a`R9&>7 zjd`&`FW{rsLnHWq+sU8|i0q5;oWDrPX*?;UY+5s=5=cuPGe|XoX)n{N)BsLxU!49+ z-dEq2)f+jFDTdf#L^+I07Fn*I6pKuWR|LZWSEv(fHVf>T(oUJWDcjgUK`}#Fnk?1X zM^T}wFL+3&r!{QHuEueSCW~T`V|0#D7^EnOA~ef#tc?dYCV&yepvS>!5yK&Of{SyE zmB`8#vDF8Ca0gI3&Rt3{nLb>L+n_yTH?5kR?+T4xAjot0g@o(4sT=63Q`cvW#AWg` zUJ?qv=WIx?wXlQHs~YUwcoA+ba$&^tw57YNZ zWsmCsipe{C{$3tc(C!8LR@YehrvdRY5(CBU0yk`Sjw;A5`Wm2uD1tsfv*m^eKbkDz zC)rf=-Pfn7S9QFv)YdM9Ncy3-Z9LCwpv-4@Qx@!Ny3YF-`9JXc|EEu&G9aHmv6n#j z<5r#n8{Yr622acHe_NYdoA>X3cWVDv_39y>J(THR_2!_pVI~1pj@FW}VBx5}K}PWbIpw5NYK)YbGm?@Y&v;9`#p(d?0f$-o+IBb?v@ zmnH5s{d7?P;GyQ@hdl)cP6XDIR4MNM_{*E!Uq5|3*!}S7<XcdgvFN!&~9TmOntRPV;PrH-^I> z{DJ>uu=Ut~@&vujPT~ZkgJ2TmF^41{=6>Ga|9Nls(~tXyR~W$)f8YTdk3yIpT(o zBSiP3N$T~KgtRJwr)u-=0WP>6TUQ>&DFXFTX(&_*G}UKaQ%OmmO_koMw_U$kdEK{c zS7=f5Cw2#8mYe9ZlLYB>5lnGs)Y*O4={sJa^H2Zr*OzJX)0?Lkub=#M@#|lb&HdMB z|1XdC{_l_3!QRus@GsHu^uzKL|L@L9#{gf(?1(is2-iff0v!CikxXySS0mY3^kDB z3?<#zXu%AJjuUzDM5@4eErP7gvG?`q@h&Um!_DusU*DY0@q`}!0hKUft}M&5ldK>c z)Fuh%#6e13F!7x7nC`=I%XG)*68&`~k^EiAC8SI7cn^wos%EFAbmG^dtDF* z$!KFE05dr%3SP;apy!^JYH9zj<|g3OImahwf;t*iZa8DjWaN}Frq+iiOz%iSI5KYy zzfuecdUuG11AnlMevQ*7TifX3$=3C52?Kvny2c#$uhIE7%FjkW{NN9Ite1_=!Ei7b z4u;Le>4?p-n{7|}h?M72s?Emn?4ahwlY}U}5mI~;{J+Hf$ z!HY%ZRsowy)$Z!L*IDbiZ1(fjmv0w+k6fL$6{0G(R!S$)3o~$*HYx1k3f_Cbwu0Fc zbZ=L16wpe7@)T$Be1y6m54zfove!s61BjH7Oo323e$E($G_cpH~Y`8aw+u+37-C`fYcss$HQtVXCCQ9H-3&9Bs$Y=pY>|5EwN#d8%qt+1hjt1{5wcJ8Pp zsP^Rl4F$Bp!mR$BEmud~{cZ{)!gR<${reyI z|7Fv!;r`d)|GPC9+Wx=8C-?7vcl-Va9s^)~ieZ6KgJd!fLfl71_G6YMIB1CUf#(_Z zrgm#P{V8AIFEjP%AT2*=M!b`o$SS!N!)!NKuQ z72ZX!@yTMkOYL02$U|b!_B?%*UY8s2_z|?}_F=Q9&Lc$t_QzcFiB?>Y;RZGHJPW~< z?OU*^kz!F7Om29QkJGak{_EWzKmM$T75PA4O$aq1mqysk;+!jl2v>#@D#tETW@k;e z=M9P@9ebQkrHfpE#w1(hAs(X{i>E^7IArZkmefNLrjoh$0R(6bP?(hze{`jFBRmNf zNkL_6Fo!z92&b}?-~wJH(O4TJ8{!xYq+qLsKC+zZa0zB!ePBTkcNv61+CgK3;I!cw zT8^dVxEW%l!$YieM2MBWZ0ZiFvX@OgMu?u_<2BDQW4-Ya^Jt<>z1sNhlGR=vk9D^S z7JUUcHO$f|rcq>2gZVEt{gu_r);}!Nh6Tdub41zAxJ| zGp69Y@pqSucHj!e+poqFLZ2^cuV)NJp*)gzk!?LN4HmwC4Jhfeha(}qQd>s{S_%cR zT}Wf%BibVqqmHGop)osLTr72_FtcO^V?Mz zhV@D+C^5F5BSc-W2ZM{!iKeWz*X;|7(i> zw`I%!TSF>J-^>4ZC;!)M4qWX26T5wHa3O9z%yNjXLrrQHsbS;?+m5mW+G9d%Nn?}H zFcj3@E%}ulqWbQ(ZB-I+1B{)u*(OK_iguI*(6)Q4wyQ|(6g63D(}zB+>TZm2ITc~y zf)v?I9DG!2=e|rJq(f(M>ePhE15<`b1Od6r6uX*tj3$)riPN=$K!tYDnzc&Rc$7D& z)bfY-RTdQ4WbWw9(Xgwyv2n%;OX5HBK1S%(a>|UiG?jS2?)4IWQaj zn`;2|^WTowb{FUWqpc@T9QOZ@@6Z1`J^!y1|CdMFrNM#v{12{Y>pP?H#^l{%c7k*9 z7xOF=f3a{T+ys6W7b*-0*SuxCr<=tZ?nAH(8FiQ$)|Vm=J7OD^V-W8P7XUlPxy$f?GoG+%QSU27%-Nocg^;W3&*OZO3f6>Sq@+GfvZ7MzNp({@uYV-5P! zltSoRGHD*cSDvWa^CwAAcx>kVw=e(t>EPv0yPw{?JKX(w_d`$DN{VO~J4yuzN^K7$ zdhple5JfoTbu`>YF?tSRkx{(9uIo|hB|xk?-tH(qHlr756VIyjtkBQS;L0VyULfho zM^N^%Y>cw3iA0=L+Q(P6!g@QLJ59~AzA34ZJK?CI8hfm0we13|FM$T|d^rUK%nm(H zy*@Ia*Tjcb(E=Q2J^hi$MOAt!2bi7<@R<&jElkvttPOR;(KU^zlZ$EGFC36t<#Mm7;O=Yh9* zc;xqbWvfPz9Y$zt)BMgtgp6cpN81*1-^<2jx2mKR1Fyi{c)ci4SLAmUN9Z)q&Pg*%yRx~^wz|2mP=tR? z{^rtROd#@hN_eUjh;#|D8c&0`dLGGQ1|#_d)<_~I)c~!y0lKh$W+mUFp^=BB3I8Lq zn88Ew?6~LiD{qnQ{%sK?-sZhZ^j`m~r`Wh9YtUNxe{=BEmjAZ~_xT@wm-kkU#^4`euQeby`TBJ9&lEYNCV~JzjNQ?9{?#5bjnyV?` z4aG==Cr;gnLrb_wKd0Gw`W&s0U3wLOg4_aPAGtw`dX2ElBtZ@_OPOU`gaBVbxQ^tB!^c$%d00|B4(h9rtr>sm{v zDBPLU%Y}JAw?QynxRKD2qFsxN@u=e9Fynp2F+1jCidw5=t#s67g7P=Tv zc^k|dP+=+zM=y1qM6}A+>O^coSLoyt9g%{CtG70-<577!#cF^((uII{_%g4iM|`(D z9;sQ@Jg)`TmBnm_wTs1*6THPRgZAz#%B+#JF{#acYH+$QYiyTSQcirD4b@e|n5MFv zPla7BDJ6+hS4Wblz7i*tUCCq&yIiB-Yeohcf`LEg=&U0G*dIApW1E$Dd~^(NY$DI8 z!AWw;==5mGP_>zp*nG7eLPMyuH9FRneD`G4j^?ss@tf>Xd|+fiG&ZnffbxNkb;%;r z@}@o4dnB(%aN()mC7Qju81cO6RBPHZ0J)Jja@(wxfC*0qa3osfxS5w)&k$O%J|x#D z81C!DTnee$oCai>hKkiVnjVL&Mp}NM=Hd|H$$)u%7CgCkY^d7wdg4#5MsOa)1$v)n zvzTB%ND|XxP$1y*U&PBP>9HuxbJoyu4dHDm(Bc%Qo|j{?NDAVt$@c~*SI#GwNIvT_ z?bE6vQooTLKxYkz)XXUZT;+aaFSEMITs7TEHp7u{=Vg)q4J)k9^=_cLB1D}H)& z5BjVLDwIwEAO?ibt_MQb{gtTU>e_uhZaEIe2}Uqa>r}M~IH|sN^%4WVznH^Qizf~+ zkyXmw?aB^6Zhmowe$aPr$@xU?&w|y!yAcd2tnHVWh>H85e3B|5t67d1VZy*QRT|V( zCf#=Joi6hQ0ZCRC%J)lTU!>jVZ)xYT^UdWfdM%E0%L~^7EC*na;f0Bz^eC*ckM*Dw1bC4WpDTQgbbh zY(8C88)dmS+9{(woT^^kX3T+zQ=M%F-c?a?M6r>3&EkX`T4X0QC2n4aj;PaOS8|L( z`SxA`!9}W2>{&1NrDwRxY!4+ya4yJFozi$(tVg>OS0bSLMd|9Q;^eKzx8lULaqOGb z9AM0AEeTIC)flbS9k%_t(#TK_)?#g+ztt9wWzTm5P{V}*U4os3=aTrpxYTz&wBoT7 zi2JY;Ix^4qeO^(YYfVPrAsDiW0LaW`H+#t@8TlBF4)Y_EsgiR za%LxSsYPW^Hrp!20juOwJdy95|h8O(Jc zYQJF{&9Og;)5!C}AdTXPazcfMLe}tu5Mmp8WoiiWHJv^(it++ku|brPi}cFUKsDzV$**-BY= z?W$z1QUchC9jnC+1HQ{~L7X~Q_Ea!l`y7r6yW&N!m7fBu-FJd$>7~$U|IaOfZW0Zm z-u~zD*5>1~{m+x9Tle<=zlZ&gQ%BD+`^AWzg4r>JfP+^Zy_cLA&yL1;c+HSUNyQ#OOJ1+Fv zWxsWef=yYlsfIbvF=S$h)2W=ro$49pb|6v`-+7JJn(zmX1ch2K?eY#YwfifX(vV1^qW zR(<>4{DIjpzj+K*G=(kQV- zk3^bOn1lK$pnEhvi?anumf9Yyon@>EWIXxGQ?+@csp>n_uwQffTuD&r6yDX;fnk&} zcl;HD59@Yi{u`6-R@dv-n!lUPoxS4;-G;H@$q=K`Q|0Ds$$s$Zqb~2gX(`!luSIru zt<7898>@~zN>J_8J5mRkz~Z>_&D6S@9i@#+iL zzKPb(`?ZCm^2L&3v%@E+@)pU5;|3+Ki2zH55UVy1v@I4Ys~VOJ{R(%!ZKt{ESDYi| zU7@Y-uRCAu)|hLp-E%-&4gl;c89>(%@~YRG@rDPbk|F&fDYma+d|Wq~n+=mGqnh*S zthaH7G@(13U{|r*Xe?t}2>o8ud-KKJa;n=2qE}hk_srACJe3yU|Gt4QE8;&2 z?!A==koEE3A3q&zTJayZ22bwozyG!3zc++*yi$H4J%5nO79_HfpxaCXRF0s>!o)dj zP3=@WZ@3z;A(Wn(2}z4Ur)f;B+&^}xJdVe8J5#!!7JZ44v6ST%UFkWTx^KCmL1|3f zP)ueqsvOn_aWFIl04!(!ar<1d)IY*3xP+ofiDitFNRu0+aIS;~f=&baMTI;R*Tkyq zKKmlI&M$z;6+;5b7@H=P=EOeIELzNhytB|8!P6u=2@>hNtT~XDIavarVHLl1GN5ok zH=KH{#4t4pU$hkJB9?^1*|u7A;qFtkZ2`Iv3C10$fsLLg9IBL1UQ|0ks1Pt5*0D+w zR0(h7^z^OqK$HXG5i)?uU8_=7>{SU|oaoLKA{*BbLe!}nTNSM&{66mVoz z(gNhT(<`mk8WSz8tVo$EwpKahigLwg>q0u>%U6@K#OdZ@j)+LKW%bL0dNsSks>mW- zc$Y0$w6Drp6e4wfBrjchZwwG6Mk9pvNu0pl$g$%$M2WJ9WhyBb}uue1YH(#xbrQ-Zbf zFjnO~V0E25WXdJ6%D-20YHH=-t6sD!I=bm)wbB1q-UP4j0bGCod-`}_<$rzpX!HL5 z_s7WpRo}sTBgl}G$E&)~9DLh3PKgHfZapFcP2jku3746=OgvTjSEOv-oI0i&6?t{J z6--zI>ap1xrz&5eD&lI0m~n|nr8}n~NT-WclM7Q7F>Jeh z$y=3%`8-dSVnT>(I)yvt95QVhw^Dpj;56bX=un5>QaZK9-CCYjiz<^W8=>wb%Sz(N z%4L_qdvgLh3Gxx@o&>o=t~m+*PRst@rV~MiYNJ>)aC@`!u)&n#+Pqf9ICcpicAfc- z9W&7#YE`(?iM#jJtc)z<+R9)*P2qj`kX!+Mu3}%C;XH(-W{!+z4sI?oXF^AsT%neM zuQ%1gIepcup6t3gt;;HD&$m0BWCh`@Txc$8Rb@lF9lBJ+K59y%R&$BE0!`iIvks`1 zo==VLn}XbUHZPXk_z|cs&)T~py8GC=oe8NUyBStZkMSy$5l5G+0be5vP8nf4Ho~t81(BqbqWz8^q?R7rt-oeB& zZ4g0_B$tPnZ<;cZQ`gqOS7ibg2S?KsGXx-Xg-onSB_6l@!D??hg&g$`U6WO~!ne~Y z9j&&17nWq|!y`@@pa*Ap%l3&ZL(ZQ7^}xt`}GtkUVS;vM~++Krnpaz1+^NTt&J z+7SD$aCNu7)l{$l;T}z=R zvmn0n+-(w0mR|OsCq^~&uF0U5U;mgE($U}E~7xuG@tM#$LS zY{L3Hz8jCvtzXyV!SSt&KX`j^j3`$$r8=|`* z+`wX&k+05RR|-vyX1g?!b%W(DGhEUG#$AaFJd+=bce+ynX?v~4Q2i$nIIuKGT^8<( z9%}N9vKlv_JVWG$lxH>uw6q1`iP}i6C2Fa(Dpk^!mH?rZz$NPUE5NM&t=xUDZvQPM z!LMci{b=xb%eMa>+`s?bt^9ZG+(>T2Zn^AiVM7%)=1yv7*ex2=8+KP&F3Z+%_=nWa zQbdW!bFf?#=GWvprp;zbky_K{>Z-_*&c$8ct#BNwy#L?O61v7}T_Yt}?zEs{HAO1y z)0^zDs~E$!668{RqmiIYr6J}1t5^pXt&{iMok^yPW7=_x+ki%=wICCBB{4f+ZCcz5@@q}mO(urpTZ*+!r{@q*vJv?=4vX^d8UH2E zW*MBZw`KUzPW~G{wdB9et*ybm{C5{WYzKU||LOgQ{kQv{4tM{02nie0j6M%#GMD44 zy_W|EP(nYi7r|5(v6jEQ{P5=GJKA`OlO#Kr9~>q?lhUXAs>`tFi}UX8ccRwjvF?Y{#wKD~PR?$z$zr(a&~ecT0V zgdmnC>!Gx(k(XwkzW(6~WfDpCn_RCb%p$Bept;AfT+0m-8^2%@+lxvv+K+n-Y2colqif`bVu)lmMqYSV7vi8PCoT4xwSSLA# zC}l(<&`0OAm{1$;G?N9YPfu(8;DHY7Ta7@5UeW9wLdA(_wWq-uga*c`2B!}KtfF+0 zA;y9I61+xKDZ1a9=$5H3@|jCj4qciWiFL|9cwo*Z4L{?3TdMM{)};X*PbN4l;xopL zuYh59WS1|rA%j(b!g{2#${5RAyP#Nr6|E#xSvFGNRJAY(2zeLG@JPdm?W!Z#Na(LC zlxFL6$&LG(uuD^>>szGfK**e6ZO>GY__Ee9Ft_GWd9zk+HHcilNB1{P^JZ-X{knZ` zbuUl;6YKw^BzTj&?=9m$JbGl=|37~8_|d)mcc=Qlt1e%qx{K2YUzo}|;2J}8U=}${ zyl`siRAsSuqDc}=5jo8kNd(W2aFWLpL^4nhfXU-&K-kF^r}IV8r_q{Yf}(gb!PHEW zV%U_*KE zz0bvjwaW4?`ra667xBAPxp^EJ&4Fn&Onq$VUwz=GOfA+=jw)@+>_V_k$p}(t)ueQIn+t@Iz<$kj@ z@s)70X;#9i2C)0tRmmJo#Fm?r(l}+guI-2?pcn!P1gAo(KvLg(GclR^GqG_f74r3W z2pydGcm;|3YH)64#(g8wZK z?&$FTvGUsbpBxwY@|NHJ+Sva;w*0?`kDuJzf8DA6$7y^XllK{k;RyXHPU8ZHWbgDuHg0<{yUXT&u$)0GYn-LlUDcP4Ip5bg!jL;MIZRN5w zKdiSjrTqQu@VQ}JNb{rJJRP(bw|nT>WdasbwmqW4Tn#bs|Q|G)dw%$He4V%*_nXwp2vMs zhl3aJL?JLML1G0+zlI|tAD>tayoZ`V`_%PNv+2?OL!hb4Yopt1^n4R}o%kH19W>mo zuvk}weGTS4X?E`Q(DxSM?dHlnJdJR0mgQd6FkOVC1Qc$NY^)xihh9_Qc`+9^kGO8Q zZB+eeba^hxReD5q{e1tuApdj6s%!y((c^_5oMd@{qxvZ94I|aqt;%A*wRfh0BQ6dZ zNj$*^{nEbB)YVnv!na|5#gpaiU5abE0!>Z7DPy^r!HDFptdu>*8>VYd>hmMwkNIHf ze*a^V;BCD9ebXZT&)~70|Lf_!|L5Jl|JAa-dhDYgsx0$qVZ9Z8n~3EUj5Hh;XK=N> zu#6p91G-g6QI(HZ4@U`K#W7xMN6hNzp{aSYVt_^672U?ZT+A^?FJ%r9h=8y=F+XE_ zH;SFA50phY5Ce6EP`x<;SRc1V0;i#J3j)AN00dy>`%Elr6G*=rL5dm;CM&I|uaB0@ z?OwKKzbXCS%;pIOX*8g$Q<=`XjbZw#cH&&FjQO%661jEjGy4&*&BUhpqr!+WLQX3XAY_`xz^CVW~EUa7~Ch68|^2c-fy-6pa|5{d8l%!m*OfxSD zWxGmVmFxZq9jo;FpN#Z!r~W^iPqr-k&&@}V@9%$iZ2w6;ANR7U7M3T;re^3Kk%&1B zW|+)_5ZA}>AYIfrr>tYo@0^3%)cmooD7bj^dDp zCz)YFXe6T2^M&X8evpIC(vkQTqP?(~LJE{@AYsz%&+L;X?5O}kBjmABISygl2D2f>ZV&EweEU zd4#J?i7MM&cqw}Ef|)s~&f(IsW7OJo){>;bBn;;G&&3P`9q+W5sTb(+cbD)tYrh)% zIi>*|)akr&^x)a^Zs&MIJ1x8-nA(Scy%TmHbVsQBAeha!9U(lr&uD2c z)9pav|9bR;vuTGm{o5k58n2FuV0sLLg%R+0c68{6im|H3! z{B42rrI7rhEM(8d#(tuv$S;s&u0#Wd&OG~yOv?VDum0skW;$NgWXG#2<;^Mu)i7kWA zcqwdtxIO{wZ`h|xNiej-&UsE>^(W^7_BRa#dbwH?5()^4lmto^$P@28PNVGH7fkCjsG2k(6hZL$FL+7akolg%>!KH8EF++SYRd8A z2vC_;!ZCzS7QmW~zj-{oW*Hgwm14s|xzRCJIv+k?iU)k*O>yz2z%x(v1k2ymkS^Go zX5~>zE@&R6sLIA!WPiaY!3o{5<(Nek7_@{;N3Bhub{GBg;oH5pxCnOHBMQBInw?@+ z0!B@I!}7&1ypd10wF8{suqdZJ7y}YVa0KskzPr>0u<9I-UG}tsLwL6@SVGsdaNI}? zYM~c$ksW=;%VRVWt79b&vv`_jIXynLtH8!X6w!1H)a~-&hG~3P9oLa2XDdZ=WZ#Tr zN%WJbu2Lhl=s;|NW*6eC(FrulCj1csVoP-S7Taytl?qyH^d&zxbS7UH+x5l_>Pmg_ zj}1Mgv{uW0*hQt(5C3TNBXTD&K6>!L(J|A`wCwl;p6*`EOC9r%^&NAMMy0D^t=e_V zc7Rg1{9^-M*}>L!{<4B5!gTgmDm40)n-u>f1}vpbw`r#Vgq!Dbqf7;eeE?pjEz4c7 zh$vP!F0OL`*FkUsy{734sAwXxb;ck$q-b0O`4ksp@z!!0oT-Nc?i$N>9Gc`Djp4;% zOeKvs6aJTYxfhQ{eH6L-Mvo8IT6){Cl7wz~bv_gelrqM-6Z7cdj zVhTp+JqTYx@{GZ<0#;|UxOgwIW+w#etuM`JTuVdj?O(p=+}d5!BU&>!F8vKx$>0=( zM#3xhWWEv)&Ix{nI<8O-(bph`RgcdBN1Prqj#m%;fMPYZ6R5}>M}%jd4(J6R>j)ij z+}fS*Dg{#&Rr=A=Eq0 zsoX0TFtlF~`cxVaydG;8agJL^FYPhOQlDP~Sb5e*36SH#w z6k(A7E1RJ6)1W}$9VGSy-{l^_*KLq4R}8TJ^>Br>E{qc3=4^rvk5}kO$Me`+=UTlo zlkFCBzmQfu8aCfQpvbfCna#A)PtVUR{N*Z^HrPR)s`BVwH!LcETm z{+)GmG&r_j3SMRD8O{sBXR*l8_%jIQ>mgP3Fb476Q-ju3tB}Dd6IBWLA{$e;tDZ|J zHH;-FQMR=)RMz$WbACzHJb>P(JzNwxU-jVY$N9YUbz{0PRUqD{KAI2vXioJ_G(OyL zwf^1Zd~>Bq@-_+^|M~0p01S24HLuaK!fCap58RXk-lPSdiWAvnCD7Fw>F7$ww|*W7 z8gf}-9^}9sg#bGoSa-8;rC9MUc;^xbsznb+wgYV+UDfVdLSwGnh1g6unVJ_#MIHNF z*F|ujCVKl3{!R465r6P~zhB`eWy*AYLj)6l@yF9^cvi8mOwbj7={-sHE&J0HCAj#I zX~U7{o%mDRnZzf#uEYl?zfni5doRj=efxj@zyBM_(!2Hl91b=g+5VqfoA>WOck#nE z&<^Y%oe}E%DZ&>8&NuiQ(0Z8VxWjgU4rGG^Q_T_T4E^DzzbOhPae_%_gpTBH-XVF| zQAcCv!|u!1Z+HD!)S=&wdBa%{Q*iM3NTZ1PzYzhHy^``$@e!h_?9#9f&>t0UMM;>) zbNW@CDIK_)00b})MeFP5)S_a2J;DVJi~TuH{~V+dSOKiB>)1lGQ5cJs3}>#A?nL

8pgQPp&%b7Nt^=p;Gue*6BFS5*U%sM0~4aMG$>)eoYV17<3h)7kth7?pQdr) zhgm-NgD5+}ewfWh=i!WOs24oH)HsTBs)2;AI(*HO4LZxh@Z^3w|9AEOr}tz(@(Xf@ z`~T*ntw)>I`~P5g@Be=%KU|KcI+OAv|BDe~+GKi};qUv;&ayaquAC-m%d%d71n;Dp zk}3wv>gCb#cBxfaRqtrWtCU+>ig!m-QK#ItLILZ5aKIm5OYKdSsG}XLQEpdKtUKDV z8s&B+y||;D(1G(S_aW0hZj|7(A8)ClBa8eF*D)waYbGeW%`wF|n@-~t*P&-s zu7UQR>45SpVJaE{s_d=5riTNbB)Ssaa<-0oSVh~R)4DaVvj-&amL$|+N$cZ?spFv^HmNq;=jaR1 z9%<7V?7T0_MBB@zB9#s|9HrC|s?9N#x5uFMp^9K|)QXuJn7_po`Jy;o3Ob?v;uPoS zF`>~mI1JEbk4;K)`&9`}GLf`j9U)diTeVKaZ)STfV^N=vY$7=7Bib z*q#v+I7{?+6X0Mi4&!lxr>5j>p|8ySVlSIc+3s5lKytBeBQ}k}>JPjL6AA^i!| z^VS8+zydAOT($w$RISV-P}EYmDJrsWFGd{kz9c}YfUP!9$J<`yd(?p4V-`|k;IzqJ zHg&B>X778zt$(HZI*76~S;BZmtYv8mKUiM%{Ph%`P2F1`_r@GcbMdhN1`MAlh8y#F zQwv@-K}J3_xw5zdU)Gbmu|kiBJC5ODi6W`tR))h75MOtVovFxRWzw0ZF%8)+0$ogk zIMHBnRK?$im9D4~ZPgZE1mHU@{?;=rGXoPeJ8UJwh{*2t36ed^iUE{aJUQ;%(C?|bJsU%46T!8Ul&^uPT8g^HVyXJI%X~Dj&UBu1v3zh z^*6o~6P~sb=7budF8@s?j^Y(WCnMAqzuHyUAZQi#YnR3=;&b{kU6zuu zTT?qyQ`XlB3ER6IX7d2S8nrxT{>*KCKQ8mSeGin}EXW41zzCaxQK21m=}_V|H>DocuEsBQrAJ(kEf(YpwIZ36P&f8!+1zm-{ea0L(HcwJ0G1o5 zrl0N74Y0hGIXi<5R+}XX;!6v>T30+e7FD@YsTqx0;6_bQGr9J?xzGqOGx&H;Z-+69 zJE5;4ZG+-cK7cw|atNBK+JwewF7d_Wd@+IC|D9zA)~}Z4I(C1>>k>)v6^d{N^vy3yFa%tgs)C<_?fUHR_`0G zMf?vGaO_>Akg-*`SrKPgL*kT?LtK#;$l?UjzJTSAXe(+d3%|aSKC$my&22;aqK$ zrx{%1=JO=x{yXKPLYNFP?T}C#z(vzOEzV+m4(-{+n@|@IxRFbt|f0A5#O;Rq#JK##OI zrFY6K@AE2BKpdGWC{@nI$q{17q~V;B2G#XvkW@FDywgW->Cl?bB1scCi&*#Ou-V~L zI(SNoN0y1tg!ltubE&Cb(mF$-qXj!>7u+)}yQx(1wKsX3I<0cqGks4XJp z8Jwnc0IVM+E?u^gIm|JXZ*SB{#~$Co;)KO+BWRpa(&2opY02p^m}Ui&4gq<5##T@P zgh*%tIw3u0iuN(#2X|DdQ2m$^*7=-iU!_A!&Ky&Og3f^!=<0$52)1OI9Eli)3z^b_ z7F9g~cvK-{fTmLu8%mUu=mYJl5T)MJj99D>PD>^}R4bcl62&?SfVTqZtkl5GQ7+Oe zjZ@QC)~+rldx>|)x@E_@s_N4H#91liwPAZ}KVcwRZ5rHypyr8K+2Q;?6H{zjoSe!mq|6z- zq?$F%tQ=CLL~nzOc(#~v>n18Oi*Wu$z7+mS)RQep#p}P68f65+lw0`-u&=58)Cn%m zVK3w1Nm)Dw(ub?VEKZV`MK~qCdJLhl3d_JC4=H#uihwH+;Gy8Vw2fUgcp2ePbg-(R z6C_4qB=V%?0nc18LN6h8!GX-LfeuBsjZ&F9><;-MC88&e%-JaK95OeHmWkzsyv#l` z+CHZ!2&7pQnZy0p`y&@*>Fo)a%{DAbY(YlK=iU`H3e}k`?G0f&WbpRix@-Am5`)4)J({@i^2>C1>VVXoE2RZ;ZdAo+0k~X zZ?FB1PsDad0t#r`eO#QAXeS%bCO9q7wgK2%K?rG>b?M)2S-Ul%ty^d^%L{adQGiCg z>Y%*>U9CNRx`LjfKNom1O@aa=(KctF=Ti)0r$q4D{u+G9+Z~@_A+UZaTfIVy74&p% zMRbGAiXKC#gRU9oBjo+y8mFiyD#A~Oi&>VUbe1Gr^y6ZM{==x1hvyhb8KG@-e8MUu zxIjfb!5P{{y)9N-A{NE@l;qgcy8wJ>1N|fIBf&v-6i+aj6}-llQ9Mcr0iJG48`$0C&bvzW5=`26@Xz`YT`VUUr1!_4i#0b0&+J054Rxt`m0ZQeN zBnb`+MrGgkB~B-Hi|SDuU1oC!BJjhefG4}`*o_w`42tj^c{t06HE{8hkg-oIh-HQ& zMR8g%Xm{B!u0D(}i%yxroP~UL>I>bw7L%+ii9rD@R#xy8-&V}^ffidUe|r4+u0K=l z{EyQJU)}fN-+2D_HrJn*&;S1O{^R-oAU}NRJ(Y*J5CYp(3t7|3mz&J$;Iv-sjH> zqB8W(yMs5o=??ha^`I31B}5-`!5q(u;?I6~(* z!*Pn@yo-V~LK&U}aY`lp;vA!CMy5C`=7@}Hu%IpDB1idbI!$7ngXP$(&$1eZWP^e)bGJc(0AFfu~Ld7K+WBpJ%^G$YY0H0n%(0%viM zP#WYYFS0l+k~tcY>@1EVoT6Ep;51B#!*2mlvga zpAX&~934WPGg5pDv*0rMmQUf(T#F@c@i-+Jjy|!e9OE1=DD5B(l6et_dAp0+37{S% zdHY1b&nXAve+6d=K7xxaZM2hR!5kVKoc#lb1^o8OKGsiU*Ez=Nr(m9tAOZj>zC?$( z@YF><&UfK%fwqx%5hODVmoIu}pnst_&5IxnF&QCN@7p&7*K9Ik6?~G#V>(6C@n`(c z8O{r|y}gavFy~T@KVw`R{c_wt zL9f-X1AY%*Y^|ufzMv=m74Q~=RabpB-tBGLwj=tPrR>raZE2WN#lyBihb;+*6#@xF z*KPgoC-g9BFk#9Tt+}p9fa2F^_~SLeTc97W;mcx(2DDpoLAN_4Y+jTV2X1c4VKQD$ zaGnQaY~I^%!fi`4!3WC$cahz`i}E5UX1R+N8a<9B2v*$%>cqMp`=dB1aOQazbhce| z>>B?daq6{EyW`T0F#IVmwvDC~(c{DUJ_t9wQc{>$JAfajVKR$w?m_KNN1rVEjL?}%FZA{zkh-j|c-}?UVz72B z)%90A?^G=h{?)nEOhov$vp*|v6u;qf-eKrM6`rxCgI6B5z`)Sm{$Ib$!(;x(%8h%a!MkXf`A;XEFvYxruKkPNR) zD4;ONBLBRYz=cfjh942X;j>?3e2KQLbEJAO2JH-lpFFW3Q!=hSML195v{;Se9Hs}F z1XrsGPT}C>TGO{A9mV5WhI6#H^Kl0hO>lgUNQU0wD>x`aGMV5k#9ZuRr)-Ym0>vpR zf)I#=ast3`5S}w76bgeG2q_@J@G2pRFuIJB1eCZrsCEm24yVa1{Ytx=kTfm`tPnJv zWuS7(g>(pl8AnVmh8zWm?I-B+JPywl=mO>>Ajg5nDH;J$n+rv(mH64cRupT0|Wpgy+QH&!X;Qu^4_(+?D#g%Hs z5D$b#n>MoAfcABJ*bzkj^EAkU3A`yV{1zxoVw@HqBD6|55P+Z>a=Dxqt;P`oMJ`l_ zu|z=;_z3M@4bZ!*cW=As?b|Lo$I0}ni(n3&uLi|x9tH{a-96&;0<8+Z;q)TTNIKzD zWtOv6`}`ds$MkdS)8~VCU)~;lIQZD=B2t{=>@vrfaqKZyke}yejv3hY@Uc739OygoNNCPJIdV*tJAYK=R#Z?#SNQez_Z4Dg;lWBqx z2Ra|bfYKtwQwAST*PlE8IC?MYa*oLbLmc zLkx>SdM!{Na11)~$!Mtu4vT1f!LoN87Y>3NKW(EnYH!t*b6AsL>X|}J$0aCgFD%-m zIy!iFFhF!-oCV=moJI&=64kDbaDM=yRP^D~Mb zC+4}+)vB(-RCEgALe}Y`90yr=P9^S=KxB?hBh2u}Yk9+7u!F3!t`w?%;ypQ-78X}^ z(?*-2>Y@d0%!?Iz4IjM@8o<9>&I!tZ$iCQ|^Ji&(9*+tso7S*Y0$Fm78KfEk+sm{n zHGosw7pMQ6_tmv!^~RaU6hrJVqBD$379>+oiUm>PRgU3+E7XZKnFKbcv{R;zWov6F zC?<%c$y}X%6cwuaf`@c^TElkiY8BF^V8?w;xhRa&vOdC<7`N;wXlQHs~YUw!ym7|;`w6x$7^VH{1wkn7Q>pxY^&?GoA+ba z$wa7{hv|Eyvd47*#ra2k`AHsC(C#(*LDyLM=Rxj~JO+x}1#Z~v9958A^ff>QQ3QQ} zX3Gr`el%IaPsmvG-PPIDsXE>lYHJrlB>m9YGM?u(Q06ndDGPQrUFYMA{2%!J|MV18 z2KnhJdkKU;?&UeK;r(x;_oDp%x3Rvl{`mg)p!R=NuO8yrLz(_nZw^`;W)iUS512y} z5`Ogrq%P9!ts3q``FRkLOVcW>bWLO098*81SMa&IXPfJN9C}N8@LMYj^jx|)9Ho#|NRxY*-DG<)P= zGO$M52q(C}Wr=%5KV1|6c&PdEc~8NC6M^+4Rf@ZBe*Lif+v%6X-Or~xzwCZIf~T*2 z@~=2ag0<&<4|#iWI=k9JT!V=E&$iIv{_6V|{r(s9W)>&W+R>-}j=#R%+g#u9d;N_q zba~N1JJV@`f5T^gjf=JC&tCY?Hj($&zZ~uFbx{(3#po9tekC3BE6(7paBaiyp||H5 znc%g4|7XAFZ}v8x` z<>jS6rbT{8Ch!hO^~0UEddVxk2xTwK4W;>2lSAo+PfwX1;TK#KI6FP{+OmGTE0>+; z(16Q&d`)%e3s{8nFpJNy*EV;G(#j7;1%wPOmAA|~B2LGiEmBLx9=yYJOIjQGXXj?jrCQxvahJ7R znz7XE?0#uWL4FSckAft>A%$C?SL2;1DUnLU#f;&~<#=VHrdhD%9& z?+ohSFLV00W{iH{YnayW2xf}mgp5HUj^%?BB`*D{}Zfvw*hC|1Rym%y4V7yk2tj)3W{pshC6?5c<6~ zh=ciHZ7l#ZIVuX?$ef_#W=plS|4_UMICU=Z*@d8vMwL5qteK3QG6rjXc*69KC4^)1 z*6~l(--FOHha(gzTbad zdwuI~_Q|K|NOK6^UtM=*KRbH#s1Gf-+|oy-g*taX{msI((&4R z1YRsEw+h%ys&-e`z0O+CWwW2}zI@y0C*O zLHm9MM**!QD339Trvudfa@f{xl$}PJ89=0rWJ-kakW>1CFPR;s*QWpU+a0rl(Ds{O zZ3ULM(c9><4p5t`71S{T1TRCDhIi)Yh=i00+y!|8JuUm8mv<9s&hpk1oCDbLYZVPsycku-$NvnI^;^S=%;8!|c*^Ukf3gRel4USv= zR=2g@I$11E+V^=4VZ-gCjot=wvYl0UX5UuqH|8I&>A#EnzsDF$onBqW@dZxJSKekx z-DFjHVVk9vQIMRqizQ|;#cF`s5w&w{+w+wglMPUt{#zP_=5D6pTBOU>mcpN&TG6}&_+>k#j!AFow4FfxX=i%H%YsvsP{B;+jMMzYL2c8!DM zpDMhI-r=*^c$eC_f{};Bp6z-1D7`i};PE49)9%7%Pn}1K0PK&s<`XTrAj1u6rWpyr zmF)+xsgYt)8%%C^k&n}h*Z#ZRH(!3y!-{;SuO>M)A(uwj%;KCYgmSJ7&8ZwaOPQTD z-JaJdj&$sCI+iYS0UG9HmW6nTCM=#xKE)wxcQ&UUioi97O^XdZ&dbrCV4AKr78U&{e zhtP5;Eyv9eD=i*kr6oeF?2)lMq{dgBr|#sp+q*Ubg;Wp*AcKPM;$>-CS-#w#SK%G@BXQrMWK~zh26K zS=kv=aNh98YeqY81>@}(LkXeF7q!1$|khl`7)&J<>r%wUWN%(5SJKwa8p)reHtjRs^`Lj*ET)7fmy zW5KXqsbz%N(CQa#*`&f;wIw4CjSu?p%(W~HIgTr;i$F)5WMF5K6`jJ zo3Qb^JOh?tI4mdk24Jlpe$7}GDs={E21<{gQgod?2*_m{ygZu1J=*^%`#&=4y=w=JuZh#O$+tj#t-I#9H&EP%G$TeV$9YNx2l(lUMM!>aDa7?)EK z7S8g5OvJ%QrFQPi1VTD=7N<^4m^?6Lh(r*O+f1>mdB0z+j8>dG#t#{A)AEkO=rd|WA}b-av1tbHu*DgVf+R`di}$heet zLItNrcE`@-nDpMt-5Jx$?h&FK=3d`MQ=hYS&6HstXRO!AFshkbifg>;ZFyHYxH=w~ zjsC+mfcp7w$7_3t^Z(h#=BC5`|M}zj|DfmpjpG0ENV_yRP?!I~^=w^d6y7-hIGdc| zO#H<>%fw$SoC!C9pTva<1Hv_Lg!go}Si@Zib|Iq*yV7L0lS;Trr+gMJOWl z?VHkVan`6YXhn>p&LW|6waYv{YednLCp^**M8cSR_82VenG(9 z)c{h8mBajBMU0$kJWBkUs_0r18Aw8-Jr9r36k58A_^aqLA*Su=`O<<@5p>#a>JiqU zD@`eczJ*Bh2)^<})s8<(g2H2*_xE@Hc6zw;e)sgl$D`d}c0YG?t)z%{v8_~qpw#w2 zq6dFX4pD?dUPb*a6r)!V78%8>tGXVQUIN6b)6Ek{|#&}kxV}*Wp23IZt_5w*y zK7z8BWn+|OO(fz|W%>BZR#Y%3g z-)9?T`R_&ldH+%Vdr0|jMHz}Y76O;kTqT;*r!(HOrU#PSV5_R5R!Nt^)F2suI1N%6 zP!J@bR&#spM(D!ct2`R77o85=9N*xhY=$dL5(QpVYKlkH#|1M_7BVZ8fnqgMmHqS1 zKhBb*qH`IarNuVbmXH%QxkMO7>hegXwn&~1*QMAzSuh`<;}g@8=8Q>rE+d{uytBT2UVIzDWtj=k8$pKxZF<#uf$mK;0@nB}HB@8rHA2Udc%C zm+_yOURX4$B*jz@_=u=jV&E0HJFgc7>W1@O#SuEs$YtI!LVY6-OB4Rb z`D_9Y#gmhc&#$}%+5P7%NWAq&mFT1XS5L8VPu8Hd^8b48g)RSY^d9p+{4VdmD)o{p zAv=mVQmiW%>#&kX?EcfP<Vi&nVi+YW)%Oph$ivu^oPjI^Y=_W7) zcbdj1>QSwee1w?dG=kR*l;MBSSg}&utJ)7j7hUq-fXTVmzuiW|;B5;^2@{7Ha`1!r6Yxx=JU+7%znldfVsgaS{R2~uA<#d*` zn9Goq5b&djafpkT4ywKq8ttcRhT%3VEt&aOfZh+LF3{a_fmH04w})5tSp2^;%t99f zD*M5-0TrggaP(5wNkprBuTI1kbc0T=(Q#g|aP`)vbuuWkQ>+HqBV7oHhcEMLddzpr zlY!#0=6Nl!t}JFdtX(XgoZu~n8ML?G(#aZ08B`4xu4b+8Q0}O1^utYR6OAvG`5)C_XSUAQ~IkF+ll1$GT*Z zX?fF*>pha!%W>hU-X)ryx)|}i=~!#pF#x%dH*(volz<6O25=;rWw?1REzKdcV13A0 zqhPqN6H_UqYCH|{c^ayz#?kaRWHr+A3pE#qa-IyB*Jr_#J12&!O|K{Z)M^BmL0q6u z8JWa6_JbrbEd~VwKL16$oaP-Cg?Y*vTCO3yEd^SfgMFC=}t~F90_-h<{lOZgPUJHRJMG-;yJS7 zr#JVYb4^g8bP515AbfT`5W4PfL=9Kh?(6ZA<8Yi{Ip%4dsx|>9)mLs_V!-!jQ+R6e z!~rI`FV4^p`pzw9elGWCG1b7k5ezA;?Kd|O7572;BvnFIvm7zPgn?_S zG^nXey6xIKUFHh{lB_J0@7Kt_NW0J9(#~V&o6A`AS{&(?7p@0b4!|J8YZF81QCO+d zwr8$p!BV>KdS%qg<}B`!@XCL6Z2%U;trf@q7uBqMs}tQ2T3_^$bJ6R&tXzcx8*_@$ z89kL3+L01uomnPKPzw3Xk0VHjP(OOd9xg%2%noN64!$-f#IP`_P7I-~B6-%+FiK>W znrmrb&(lSn79W&a)sp{2j#vF(^)!Am?-4qo^6dSqM%+9!>1v#TBakDyfM4cse zCC4a~Z|@BdoTUoIj`d<+dWNga_E4G#&IMVjQyR|_>(TDSl?bSQQM$USIC<;wtvGRQ z9J^*U2N?5On}_F^YK&Iu4%=>BX=Eq|Yq7S^-)alTvgf-2sNuqZuEEa2b4mQ)Tdy95|h z8O(JcYQJF{&51vX)5!C}AdTXP&V&jLg{o7A<)yxM}5fB5+O* zaqr`76p($+-6~A0q(1IV`>Lf}{?3OQMRbsa_ zXDemhm79{eN(o>mcB~dR4EQd`1##-!*i*rH?Q=LP?1~q?rTi3F?XDBVl3ogp_W#@x z=q}M9>g|7?Z>&Er+y897*m$)6|2^z~oN`jVfq%VG5TBy9Re>mQgXgvjbv8Y=Sx}>0 zP>pF%gKdx1yKUdoXy6m!B$(^ne)I+UuhBiRNs}c%luUf5Q=oKYaS8u)QtWM?c8>0b*Vc9et#pjdI@_V5bh*^?O2g z{Z`1@^wp82#@hEfGnC9?8G;{ahTsbg3BC%Cl$=_*19)0b${EHpAIeIkppOV+JW#7$ zJg2eN{IkJAwi|qRjMTPcq9nds(@NpgUjiw*iSTDb*UY~muEx`khAaDCgX{X;a9zJK zt`(d%P+iJZjvbA+_LOrsci5CwnxI*}2CND5h~WMnRh5J(4XZ3YwkxX(-P`i@DLLKM zgN4k{<-DU1<9W$iT-o6w1NY>bOmGB=!IL>nhI#>3i!Hw1 zTWAtwUuRPwzkGI!bE#Cut~5Uyo>KX23mncI*Wz8r?e3-lE%T}g1o3>ggn}-IdOMJu;WZHLT2@2r=IldJe$+6f=M^x&cDG*_+I?s;*2}rNxzOc z4VxGTeV>~cgdmi9TnHw94_jKdEO_6Iv!yqj-(!!fN{3P!r%(z@OglGNiEiH2?TP&C zZ{*m&3%%=&6z=bopp#mwC+Bzh#J>Gm(zUK~4?f2q`gEyU17$+oX%XD#aYUzqe4`Mh zVlv$Eu1^@Et{2+DChqmarUB%#KC7-GpaZ~Q+(|N^CTmdql&V7gg{TV~b z4?{`dAblj#qyi7>SwQz_d=Zmbp3Jp9SUbyD6Ucb-omsVhCsuVGYS{0|K35V{%EE_Y z9T-L#yyLGJd|3A@^WT|t_qtxc7ys_YJA20yx(#E)lOaZ>r^?;elKtS*MQz@D(^9hA z-iqw*TI=_?H&z{cl%U$FcdQOFfyGJX$#tJgw9M|(3gv{p`2+2+ityhZZixIxKlBEV81#H!5$ZHtA?U@*e9ABg+LW;^gnlpf-hFW|$?D|<(VHyo$H~(;c`7Zy|8oOh zR>Xf4v-e&iK-R~9fBvGkZpDAx=xsjQfB*N2|K1SN@kaTD^!!08Tad^`g6=a7P&tAg z3lrzCHMLXieBf%phERHDCL}Eaou)Cha{t(&@;Dy1?M&%@mgq~2jHN8E=t|Gw)ZXWY z2Bk4^Lou1fsB&1J#lg@J0I;0>$L({;QvV3E;1Y@^CFU_sB28|P!nqO}a&#WhFDm4r zxF%L*_c;)$b$$g*ZWt0!#@IBWG$;0vX3>HR^3FnY1dkJP79`SnS#uyQ&twUJhE@E* zIRS+Oy5ZDoC5EX<_@bpy7qKK9&bHN}3wNKQZ41zaNHFd|4Q%v0;ZUW7^19jqLWO|g zuufExph|cHr>Ad?2cjGhkB|YA+_frY#ZHyL#fk1*A+m7|Aw-?Ju~pGZ!f*7%yGcGJ zK>3-7W8i}qE?MIlnxNAl98_r?HGVl+ZnAH@mWjT}3ELzF0s%%x~Zc~>B{Xn>?# zJ1cKUHL*)5o%jrGB$Jx3jf4`TvL|$7Q+=j2*QQ`=Yb{5=7P)RS7 z9!&|>%EMTc_khK1@{lQ)#47(@&8cZA4`217RngI1FRP9Izw#z{dk^6H``?S_JuCm~ zi)ZVP_rE_z{;&EDJ{duVoO8UY3(diIImao{px&)VWS|Kg*EHcWQsfDh=~_o-8#9A+G5d?wC`^v}xQ*@l}D-h^L@KEq+UB)f#ted0H*1j0hQ^ z_K1*@II?irW$@mdfX;$!fZAt4=8$X7f`8Gne=XCAAValLtQoky*?HKQl;YaFQZ;ex z58aHkV@@0(c}S;n=M!G4;;`|u&T0{Yyxir7a@Y1C>iQ8%Ee zn|#&*wWQ}$qx+^HH=az3IX8X;s>`$Xp@{B2w(f@^b!0cgs_8Lag)-v!dT~OKHtnKmd#kR+Ffm~Ud4$f;{<;HxqLi-V(y#S8%mT_F=IQi;bcf3Vu?P9aCVL)T;# zuJG-2N=J*W-(^ZN_2Drmj8L#Om?-~SPfTWB$VDTrl>n4kCm9MpW_AZx4 z^|qEmO=dxS=Xuy9oGiWUzflfOEnB6I(E<1OzO8wY@E2dszKGI%`O&i(Rqm3y^y~di zFyy$WNlmP|htt{Q);T(5E}UBH8#9ZNZDdo9(jLm-?9KKwTvt2GGDfiLQdl{0S1>~d zl#*wf0q#>mbt|r9A-1K0Oc%mY@OiXl@n<>ea{Zfb%>#M+eGn($8bvsQvTN6yxE5wl zhD&fLpX|r)svJs^I`!F9fUE9xqg8+M1pOd6LDTffvQ7ngSq_AXpbce)ZMh6u*kw*n zwc@D1Xq<174o*n+gjECJY1c}Z65LC+c$HL(xU2! z`UVsZW2>?ngUI38)f7=g0zJmUhU^wgnaqt<#z8CX5N{cl^FL`Tqb_Tam=pL?C zTPT9pROlqFz$GOHS1K~iX!G_xRNM{fT?@>NO4TvtRkD)$KAU6S3uTETFJf)quH-KE ztZ^@;8V!+qF}R1v500wQEwv*xW)ZmQv9Uz3el>^X#-u)JyOs8l?lpHX>(P&Y_x-O} zI%Ges+;st1{4Q#wiu<1o$@KmGKb~#Cov)<-dA7Onc>jCQ``=A(UABH?x!c|H_qF?V zgEjNxG>LY*Ki;wjFU*g*Hp2o4dZBHx;*6(-0y6N@G?F#`Jw;`6~4X*Wp6-4O0zvCGI;XRs@Urbe?}8p*oBa+et{=>g+zLZ@7B4I+=kt9+1bK|Dr(G~w47nLXi#t1U1hl} zTf^ZWQaei#B_hkfa#5IH=eIF!HdBh!nl@KgMUHeX?(%kp<51=O|BjZ>HCF2yDZz4Q z2`W}oq{2SE$qu_}V%S!KT#9cr5|pVlq}+cK>%d~^1>f;-=hE$#*Ry;yb^#&CQ~v9>9DjsPVak^kVZ zDF2!9UotWwaK_%3;m30FU;l+A|E+Iq^d9BEhxlPT;Kzg0PoEF=4^EGE|8@ik8&g7` zhltGO_;zpS@DNJq=hY$@%OckD*PYKFc0SU^bDSjPQkH)_uxg}uR%AgUtBhtz0&h*^ zh`Azpw3jX-NkrqnT;MDNZvZPH)YNLk?62TAc>6O3razJAv(YHNqL)Ys9lb_FB{Xq>*?`S^Bs@ATK5 zy)U~!jS$4rWHpp_HS*HT)7L*;AR>`ezsdEALK0!U0nKecipR5z1@jfZI$B7{2E?|! z2#Q$_mTXxZqy_q^)B>L6XE`XV+RD1)^g#GPX8D)6om$lc{;uM8J zj&+h_h*FkI1iI*w7U$H4J0-F}_33G?pFGiF{h$%Z&?}m~L#Q|rt@b>)fY87=)!=kN zfK`+(2x2p^TY}e!Dn<7P6WucPMLu(>%AreBBe72TCr?an((p6h52Y$UXk8l6@o0p@ zBEDd=@hwc)ZQ12(ZOC91ps*gOtTM*()-EU(U_~nlRhA9ZH&rc6f;|5iOz=R%i0!Ha z*huKF3zU*oy5xpkP1vO=)AcRVb0B0gSlcrdB)+V549u;0P~NOnTMZ)D@6r8D)4W+5 zLAP$-TRh5>|Hk@1DGA;s@B5PRAD%t4?EjxXd;aWE{(DgU-%XdVQr*RAgs)6x9n2a- zbYK=aOuTSv=~QL0ccM`ej8T40W=RCkk8qO5Ba{x5@(>gi#8oeO*@*t9_{-;Y5V#j zZa7Qs#C$Wi;ob_I%Ji(w@G98mz(xHP2gh!gR>G+Uu>09n$sA0?mYb8(IFU@(cElr441olKQz2C#sqej;m`wed z*f^95`Sv@64o-Z$fy8|?IQPPF--z^ECIwe6v&yYb`&NR#HW=4N!ut?iwmP@p$=VOu znUu$t>ylM>bolsKd29VohKp=|&u@Rr*#AGb{J;CpHy`c49#sG1G(L~>Pb81w2z?)? zae+V1zyiEnCcf61^Yg3eXP9D#xG3UutU?8Xwd`bCKhhcnC7123xTgBNcZE|8q; zOhEX=<36dw!3%hz5a3FXSOL;+;mF9xCsqURp(fBibv@Kx!`Nz`Q3VmtF_`WD(wOuFS*J2nQD=^QwmFA|xfCaD!xH^*A3oO@ZeH zFYX?3-EiBe`qAj}N|LMei0b+ss!g{li~Xg& zGYuSZal}aCiFwd1?F&s^-83%zGLv7;$#V8CHEY@eO-;WkW4W2Zh~%!Uls(29rfW~? z@+0Dp`C#dO|6`KieZ2ksX^Hqhz2|oRuNRO0pAY-~S3C99V;}ucWtmS4>#gvYiC9j- zNW)=q23Ok)%h-`MpqC0Ms`Byb;V9v&IL2%3h*=#yG&N6F46vxXqTAS)i#g`#rOY7$ z5#;Pn%+J`)onoiz17%ST#6VpkRPRmz*2jI3z-g$w1OebA00J=ceI}MI6G*=rL5dm; zCM&I|Z;zJD?OwKKzbpMeOr{A2X*8fyr!t-OGKT4kTE@9t8MApsBy#K4XZ9nwUwSbr z)BCQIp+t3=2KKwI2i6B$4;I1yC=|ZY#)?3JeGXP0AxR^F`SR4YRqa4-`>RF&gEVyU zpsM8!cd}cuYu$Y(5^9kfF?0EyQ=OZ*?rkR(7vRx%_I_v%o9jo;FpN#bKp#DGWn;Vw>=lZkfkN3X^w*RD_k9%aSh2=@e z*bLnx5;3R21n1Kr#PuQ}EEn6>E<&+zCwm_dRwfFnM^krUv z^|!9_lOF<_UU^&g1-;;d7$tusyA+ za1bXW=)Dw{Qv(RX!hSH-C4IXOv~A#K1A=XZn%Mw2K0FM~|e zxekwYdY_;#X>gWMTc!Lw$gmEPjL?L})5r&!_koI?xnw+#av&))E>9^;lS~s^@(UVE zU)()1E+>-(#&b*!GW%m}C7z{R=9P7hs`du`D>)^=c16pMBPfpo53#FOOizCnd}&}h z&rCBM#UTw(GQoLHBN3IJFFfD(gA8<*j>NAJ?S;h@QlMl536o}jW}h@+PX!PfAdij8 zabN=hS)oWs>{3y4qR?Q09F#4HxZiOmJen8ZBMNdzm;F&N?pBCT!k7hS@}MRp0#^Y` z);Xl+B~k=Wc#Ii`Teb*5W71D1DHq?KJdyN)D6ssq@}kic(B;MR-DLy){FgiUL!STr zXZ@bT|F{1b|NQ~|f7N-ujoL*Jk}v?xzy5sB9)>;j%-HYs2cyU|=;}aMxjDW|pqeEA=8gN63@deHi^;E{d zTq-~UZ>fOr&l%3mpx1cTF%T<=JD{FWn|b_iVX+lM#os`eE57R9`c1Z#>IyMPdwEV zEPq!+x?pRXl}9OOLGv(0RW{Co{1u-CXLQGwV-{6l&=N8owZ=g0F8Y_F{k?r$1iS1J zg~ul+zvz0f}Qcg11{gUTXtbv`&UDds;C=c(<=v zLf5o#+(-;+q1SSe9e>616EqO3V<8T+c$|`q9-rD(VC^Z2XgUV!cKLM8G`_2j>qwKc zl_EK^Z$`2t`bkt*sgaiGKx~0#7vihY2{a-j{s;lFCE9$8Z7x~)ImHOfz8+uA!{G-v2$eqCE(UT{Rj+u65$&Nqc@$S{M)G_~9-!b=S zRJt10YPoLN4p8cre{7&DJJ^=xA1P=eOlNFFoiRubDH;|*HpazJytSMM7wX}FyT-B| zhbB2kLwIo*Qc2^(i2o&C?!}`~7e(&=IPf7%pdpBhLn)jLS(kpvikKp&i+21ITA;!% z)950kwgr76F$E*^352g8dC6c|0jtR*Eq;{k*U}Js`?qiUY;CXT5v>^< zm;Q#UWN->XW6mpfWWEv)&Ix{nI;l_&(bph`RfqF{BTf$)$E$~aK(U(I2~-fr5#gDq z1A5KJIzY!9w`JC4LCga~(q0yLJUBByCV$n#M#EU;;UWjjhoNPF5#knK+{Sv=bZnc!&rh6Wm_9VWnJ%oO|Pk%2hcm+!$pC!MF+lqnNCYz*Tyqb1>&7{(X`)1 zQ>t&G@!|T5)gP~?>kCbiw^3O8U%&kXz)*Wt^BOHHoE9tkz)d;eLt5amIFZe%1iCsS z9bM`8*3Sb$LoO>!gA8UzA;9)~*4^v}DOP+8KDq>gYSF`y?LgZ{7qz>V(3mTCA;t;k zq~=9ZQOEw)brIaBiQayMe-k}%#2{PFY_o>lBC6LiI2dQVb) z%lmxS+}`VcGHLX^=)F3)Vm@wK^dYPV!X}P1@}F=IG$v!9azH zoDh23nkGTYHN^!%90Hf{ci!y7DZTS%UxkqU@*X~Y`tn|W+Fz%&_SdyqyPrSp9(~!_ zqjkj}s`%O8X~DC$+N!?;D5}?RVF*9 z9Vwf_!1-Tg1TAnAND+YoO%3#xMK#EowYZq};yR?36GW$j-svyFbee!}A})k=H0U{S z#GvzAm0u143}wb;L$A#Y*@F}XQ53^L9wc(sfNpzDsyc)w3Xqs*)tg&nK^#Hre!hu+A4oe)>p5%JrrS50K0FUtYPfdCj37*c4P(0pM{-)q^7?70~z$ zn1XW%5jX+SkfiKoE8y2&$-Oe=1#E-(%2b$V3$a-(-_8OS=FRLu@Bv><6B1#a6QV;F z%v!AdWAWPw-1@I8Myci?4G078==3`qqXW&-U{#G$fvhViVD_YqNaG-k#$ux8Tz?r; zD>qfec+!Dr#&X3EoObpVvrjf}x1H@oByU--wDma~QKo{LMc>e@C-T>D09aoKrPgda zb=obxhBL!iGN{FyBD6f>=ruiL>tU*6$^}=JRs%TN{k(s=_u=jC$HUzvaD!d>H9g8r zy!ZvMmX`~C;eVl7U)hpiyVo>}LriBq{p~lFnC4xahh!$Ag%-gT!dGE3%URI3IK+9= zHf(AnMkEXIQE;^@pnL)#VsE`Rwnhs79WH`6$=%zcS_`yp*Zi|I1b69Ca8=)ICyk6c zMR4^l&ZqK94T$NOa&G{pSQb_3+29na-8lcwkDJ-LjD(_vTnzLq!%-la3M2au(E` z2EvUenEzQ2n$){E$FZ2|2_auG_)?)+8W*QL6VqwLkrJTKi(;A&*48fLudyGJZ0ZLQ zIm3QPCTmw~!T*TA;<23^Q|BFH7TfMYL^%5VVP~(chsHN*>&#+*&}Ka=1!iL_gRBj; zODu6HnREcQl1Z29r9fYj<@uld>|=i!Viwm#nJ2zdM;Ra7JIOt3T*UpK|LgxFC+Wk+ zf9m(vpV{%BHr5{jtqM~0@Ba_+!{&AiG>EMMYW+FFR|U@2xDDuPNHW}F)3F8F;K5aM zfLeXO|IF`+f>E5{yfr|_ayhp0ENrRWto3j?eP$1PD3X7QAMRoh1ko+beTIEbRv)l2G8vAP=J0*A%H6sLa) z(g?f&R#$awq1hmeMN5V=-;rApzIaC;op2h)xYVH_AI_6F1?qvJ`YI9lKeU@UMR!$3V2l?S54!y;fjr?B^5VIzu7j|yn|MG&w z(JK`+iMA};^$oyIS~F6KL0vLGKG`a@Dx2y#+VLvomX_h2qp7GG3%qX5}f77TWYA~!+gubm~Zo|fP%DQ zg0kDZ1l#$oYS6psI8JdLdRFBcXrGu3NOmJkMI%6!+4|S?Kr?XGp_%#`3Km0sJJ7GAR%o3^r64pQ}1u=9;P(ZgV&AyNa!u>`Po6@Qyt8O{R z65+GiSOAe|y@Rh~*a;J;1YXcloP#qdjY=aTmcY=%AcdHnIF+R$$i;A)kr3y(kJAhP z-R_$&zYOV9PBz2l=qL_e^FW-_*ye}{oEF^)ICzW0c%0y=EBUg}7v_GkN5*5e`_=-G zvsi!37{}oC2hoHQ3I$$EG>xkktqVK@LA{m9HZV0+EAt2xwG?iOitO9#0Y|(m2~a9v ztIgB#wio#xbzt|Hhm;sNZL&wkuJy?LeGj?!uQXo^B9bO^7|(#UEV1yj6;+R0Pt)f_ z=hnyH8;81+SVQBOkh4SzJpl>&e|%p~ur5#b8*<-#jI5WjGuG z@paeOnF<0clX9BoG+?_3)xcAopM)i~^z84W%1~4iZPgZE1rR$;{LQu2v?u0Zq`US@ z52G&9;{Sfdb3OPkcOwwqJWmxgkJ3^WuoY;)*OcKL(*g2It5G{^)wRF00-2t*wyibO z-qABVM?e8XaYwk)ts9lZU>PnBf9$PHP z*J?#FDWPuc)v~$cKKdEwvqWnwpOIN^;F^B6N;kmrR_5#sHdu{I6vVeB@LIa!@rkI) zl}gQM)B-nZf||*-?@fh9fVsiPQ+hj$Q(-kDD{31Qm+}GB*_=brT-8Q2POI4({FpIH zf`7%ZnO?*J8cwO}88w}xyB9?q-cq>Z3fsOw5n1FcFB1TTMG=G{p2~2fajpgt=T_`q z+Ox5mYbTYO(e&>A%Dxc3J;&kKoE@=x-*7GBe=yUg8FrFF##ZHSMSQ{<5~poWrA1!M zXJ?T16+C}LTTx4S_;r;8Lbq@YQ~N5aZlcs5(7f-kNI@elx5P3_Oo&sCD&;fzRkZ|9 zl@Tz|5iY{>p_G{6Y|OBhzpEgKRuK@ismSSoBZCWIg=UVElgAonUOP^{XknyvtG2Yz z>2h3X&OmvQ%|aS#Q!yW6EC`q0@|mXbvUE&YCqU)Q7JI&6@BrP0#T8q+#pJF|WJg!a zk-5}c#b(F)@_nC$pwo{t*=@jJ>#g>V^U+99DffQzPmT3p2V z656wicTPh*z>QqG9R^d_tFjn=hgGUYVJR(Bri_$A?yn-^ihwj_Bjtl*YhI>NwlH!pbuHG^mKv&E`=A2orcYIG#$sM;7 zNgQ^^C?kf^LN8LcV-xT}?`n@(i~ zabsAO2ffrOm81@hunfoB5tdDWO8tW=X;DRxF&%_ueL+gijET>~z#)Evs}05oj?)E1HQjhUu&0IVM+E?qArbC_c&-`=Q^jy=A^*%^!7 zo}(e5q{G=z(~{F;FeL?(4gq<5!B$WKgh*%tdLca%Mf*7C2X|DdQ2mnTtn(?;zRG|W z84FAy6m$->Kvx$eK(IMF!;p?v48w&?X+evso&Y?kkTF2hsfi6G%1QLkxUskkQED5` zh{gKgv}EE#wX*p=sC4Tn0MQCSXQc*ij&hdXXq=k9vUYVb*-N}T)-5~MRaKYnC(cSC zZ<%Rt?I#SxQkw?1AgFmF7B`<|Ffp%miZVh?$ArSN^y}$H>>{3WDLdV_WyI*}78N_J zYSGcIYPQLQmU{1wbhyjei77TqoSe!mq&zctN%hn)vvNw2672_9@nkmP)=gAm7U}$o zY%b!J&`37mP{&^XrPL@h5TxA7M}U1t?WfLgaS3}FPfx<)G3H&kI!xjuiCKn|+*gkw z)DfTz?BD`t5XndcT!jFS1>dD@?5e@b3uY(LeQC6PMNtq)>u$(23Z{O0qs~5TeNhEJ wb(Nsu66odWj0W;xZ}ik60A7x=7^fkAG=qKodHi|&`Gf!b|7RSY%>bYQ058?IrT_o{ literal 0 HcmV?d00001 diff --git a/libs/features/mas/commerce/internal/tacocat-wcs-client-1.17.0.tgz b/libs/features/mas/commerce/internal/tacocat-wcs-client-1.17.0.tgz deleted file mode 100644 index f7a959da63466a2d9aa7e91d77d200894fbd92f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7374 zcmV;<95Le`iwFP!00002|Lr|#ciXnI{j6Vs`u3Dkj!ao{oc79fQpa(cyyxwCX>N~? zUjmbmjF=)-0`l@){r7hU8$lAJWIJ}cSRZVO1O~utm>DcH#=$urk`7CQ4vj-{<$q@1 zeBsa1($b6P&ynjne0see`eu3M#ZtHX;>FT)^vzPYyV6}k-z^u^{I9&|{!oLq|dyZyE?8 zMA_R(9B`V%ZS{LEVU*Jg@|MPwlby-vl%(=qGNODhO~xbzxs}gYfCqyl3b&JR5|MUe zs$~4 z0a=`I8ZjH(B{|(lMkA62WIg3{fP)zjE(56F2$GmV{U{l>QIHbM$vR^sqeNI!WNEvI_&KxPV3z#?dK37!7ne zL z#`&1_JD@#%#_^E&I8087A0#8sl$Je7IB{m?IK=}zHpbPON;^@Q10Rk;Lj&MwTyX?=55TyA%Bo`!td4l;tl8!Up z@8E=W!X#jw;e>{ylU$JWf|5&r6n6e)05co#>j_7cBg%9qNQh3ak<#fXqNgcNuc4!# z_Ksd}y+3%j`Q{VgFiyD92Gw01QeN!io&!THqvW(R!VG3KWj?XobOPf7 zduozKuO}4DU!SvoZM|5w8{Wf=(RhgBBwi#}lyUfRfujjQgCs?9a@n>B_Zbu%9mYvY z7@|Bkgu~r8yL~hnhnN$C3Z5VwMad3YdQ(PSWX_{Oz(%Js7 z_hGTSy!_*jOH&I%$9y;N(HipBx3)aZ#PC<@-9aX346POHhi1mWCFNCAV3umi9+3DV z`yrZ=Kc}r#s}H|Vh!({9J`|d>G+etpW>-te>_+OxFi(?i<(PFxD$``>hvalJY5nu6K%Shk<`(qu##P}gBxHEq%=EGT_u+fCNc-HkL=muz)r!4jIY*)+4X z{i*G>hTG23zm)@`x-BqgAy-v27@cY>g0gH%UNZ*aYD2rXH^r&B?V3c}IXKg=;QSW+ zKZnbHe`m1P|Ld--EUy&(zm=sQp5i}`@Bc}bwRm77i8;Aa8m07BbBJ~pcM`ri8uRM| zp3->u<=w=btT+Q!rsyC^19FJ3Hm`zc!srFrOKCvzwd|nNyuKfJd_`|hdL$p*@vcVE z0hE1RPlpp_FT1>*OlwHsruaf29$OwyjA+a_jst>{0WuYp%OkA8x!em!=VAJGIvb9% z%|K~6-}i@6a*88nTixHf2CwS0T6ycYcGl9(s5Y|M%hnK=@e*D6x5~~O$&YheMyR&g4_nTz>Z zh5$eq{LbKy(C6%}#z@E%mEVdWuNINL5Ev)td^YlHmL3SbaTd*WtNe9T=TkXlKs`c~-1`KSOnw zU4#;^RaYvKt&ffik4~y9&zLj};T)b(QMFF1Z4O1=5gs?2MmMu|UY8Wu!t&6?8XAp< z<-oGT7E;nOBLwX8GZI5*z?5{h@9Kk&5K=^UH9b8Vlb}lB=ajz|>Z(3EhDO;>Bdgd`%IR1J)+&e?~mAZUfktTs_x;SSwSTP^if z3~5@99Tcq$p&UvKTzFEDXwzy92Lj$Ta0fVZbtIK-tX0Hdl^9iwyKztCOk;Dl2q(R!NVYaim!)zVz3 zRm-`4O2SD%nlmN7iG!ex+{`euvfRxO1pXOj%?c57`Vy`Wz!?%=3C9;bn+ue{fHCa^ zRfU5*cjZ^2H>|oh?HSsuQkS%ESxpT3HkGEWxeNDUl(S*Ss&nfOKfGf*MC2L zFz9&jfR8;DLo}k&&0A9>L|WoC?yF6%C70jxrB+uDljC?0zB4B@>DJm z!D<66vS(Yr^WemaYee{25~2k? z(=9mq=omJ{^0j|-N%>hafsF{#kX}GZTG$f)K6Js&0-0kiVrw&Y6cqM0w+gm{!lo+> zFQ+TV!o72)8P>pD;gfvWu0)nE)sF)5`p8Qdy=#YZ4p5$!W*z4AGYKRm(TK`jsACg{ zL$F<<-u)R@Wty!~S!QFNiGIX!|llY=P*KYhO!}k`{j+1nRBMQsl znf!u7yP$jdaFY8_^kx-A>QSR-&_a|dJcmL<=yVHf&+*h_*6h*6zmM|gG7Q3b-3c+B72}~aU zFmZH*Q(}&Scx{B)bsRhbpGs)DPiqKYV#d z+lIkTT6Mck$`go`<>OLYM!NebYs8lGu6he=t5>XbQm-x4=0aOLNV}8a5y`xm zHd@e|KpY;;S5YRqKjQtHzk#-Sm;ueFj=q%G-C`Qd$&_1+oRLZps(1K;ye1^xz)?iP z50sykh562rjG1)EHarOn{$nL1i(qm)^*pXkrtjOxsh{l`O1TgF3q?5EfnBb&3AGO zYVJk^8tY@V&5(S2vxyd8qL8%whalhb?aAj<#PgGHh7v70(3P$I8SPcu99ph7hj!tL zQ-|0pfevsKv37O}NgqIZ;N1)lvr0Hy#eSi6#91wuH$^?(I^zQvtxom#An|25uX=nu zpef^R#7L0D;mN%hH0QDB*Z0<&e>Z)pcV`KPy)FB9@5ugwPW4Q^PsHV342|KO zwhFv(e1(WP{;l570m9>PL<4*pk-sb#i^04h9Mg#PD=b@4rjMTg+yD2E?dlRAF>Bdf?*P{P58kh6S<$!rJ1_SJZ{W z)p<+`(m{fjvFzMj=*uqTofCV2wl)$-e01zV+ivV><|1$~1~GAgCAx%*bj7W~*@)r( zS{$)!u{*GGpj1J65EMQ-+PuQNR-OOhzFL3Y{0~b%bW8a^S9-nYPx}8+_5UekD%vU&6Hmc%&bi`<%+@}0@J`T$ZYv5JHin&oGvq%i3s#v~3Q*9A%m zoV6rj+xRx(Lf=x$rY;DZto)9TB1b1&&WnC0&049BGS5N&_LRe+x@IMyxD437>gJbj z)AxN(=VOyC$n+B(b^5YqqPK4pH#cW>I&x|gwm!lam_`t5a87d&g|()-EX`luo>?>Z z+U1@LZCX&H`X&-rdAR7lRO6L@ymTTi;P)+GH6;YsslUvX1wCv+?PpuK8TXbcIn9hCu1umhKS+V z_hgsuMJrwGN=Iy?_$-&kp?tgdC`Zg9=z}o{=zv;RpZz|;F{j*W08jW?{@T}y+hFBU zcXQ(_t4;-m)@%~mdBJXElmM=k6JHLm$E2+{HNn+@@lY6kqUHSHn1Cy2V?#Vk$pEdD z^mo(H$u?LVG*Lm14E5-bKgyITBdJ8qBB6?THYg_}i1jq;7nEt(H#n+_(Ui$rP2NJ> z>ZFdD+?<@FLfOM<=2@^nv{oUZiy*koX)eNNNLBG-=F3Eq^Lo_s1@A(>x1?n^BEqY&VbBo#oGcdRK6R%Unw zBe!#9a|ZfcHKgfYSrlT}@{SUp#5~O|fxoMV(31BZA9FFOYAtb+*>m+$Ia`tOobqlq zX0lS5i456XliO%SdESn>&0>|*sdn}~CzSb@nr5vo)h;5rUrrd=OG!YP$>lvmBzOr_ z#_OeIM4$!xcIAGUGl%Z3Z_Ka&&l{#jq=UH^IUV!0ImTkbAB#s3~R{wMSZ z+Aw;r;_ls15|W55CgI4F@g13c9Bd1*!()_Oi2EcX9E;hK$t?2%g0eu?UvG;n;jg#7 zRrP4^=q)_jJ9=v#ZTCc}?VeR?bANC1@MwKYl+{17*UP_&7t6n85jyAQ2Poy<6|rhn zGi$`Uc(!P4!0$ykJ`~1*QeW$@xBKu%%IV(G+dlk}zqWhZefXo^>Hhb#ujZ;{XcZF7CUEi`;z(AMAH-8p=>^_y1j$9qR_Z&iPN zX9rNPt8e#en-{Isx35QC2PQ*}0+pr0GjNuFtAlX(Zhv$0qiFmf*B@Y)sf}Ziu1o~Z zb`rj;?ho_C1AWOaR%sxtDBngh8c#UzIV397Y>y`5TGgyiVvPw}7hlBgDcDFQu38l} zAwh1a^F&IcxRKA5DBg)@dWFj_Ew^jo7~wFa;0%JVtJ(9MTz90`V0p37iEE34Wttjm zi3{>(z+jSrYUx|>PGxhRN!nXRTRz@pnNkt~5XDN0<;Gb*hJ@>0HH(#tu?$ET1EAU& z0Eld4T`0fTe!dx z4f8E%I*_Yzl!U~lgl2{Q*u6l+Jd0Uf0w2pr_)HEHxyl?6JP$G%3kt}e29l?C2f&tfi{tcT2dsid zET;Z6jo2!#m{Z1TY5n!KwZ|wMK^~4&wKZV&j@}kCh)yffQPj%!9m95S`%aA;U2em+ z2c_TJ$xU?vRaGn}+ehONYt1Val<=%%iXMlLrIw2vJ6dO|+R_?|E3=W(pSfj|oa63R zeHJqEK4ZO~SlCBeUrlZr&B_!VOR-jI^;B)B5bR-!3jOaioQ!~}!C9ljBZ#eryQTX$ zjp}a96XWpxns}PprCY!Vtj9ULaOZ~I#{4^P4cJ-_-rjI%79UuXko=Or8hZAG+r%Ha!&j{7L9wo^+WVPmK5>x(BUC;k9RGreG&p02mey4Lu2I z>Db3%a!ULl8Fj8Y_(yt9hKAzVqK`#8->hCprFyvketpZclWFEkmp6inYD?1Zw}?Bl zR{pZ4`qBlGI24HuAeYQXoA1VQH%5EGUROVrLXy97rj&eCnAMHro`2xhB=_*=@9O`n z|Ndu|#9z9NdjG%s;(5vc@2))U|9qT}td-b>5vBqhgrH~~k}Ll+(?CZe zdzX5vA3~&Ib1)zQhxV*{?-yV*>R3F@6wJbSavD)~CV-F;EDr~ZDTxSX1T8Lx>Gfhd ziS2#@M{p>9Nlrm75F_aYu}i>4Lnr|0chKVECET`-78i>}`T^CY}~>(|J(JK4C*T?rlg|eTpPMn-4(y*tXl{MLFpxb#eZI1I7^8n3-|mVXBHMI zo@DW5zgxuN#haOEBrKw_z~X=o7Zq+SwB#VBWSlU{lk_?d3|t(jGo;+^@g$AZ)1QZw z7iQJ|WiVoz82)GG7b=G|6~{!NsRgBcS;rDLHFZrJkE8!V_(Z(U4}H!aQ~xh_pLdJ; zzqj-h|9#~6@4kH`tBpR)oy{#55zcM9e1kbY5Q4m~4oOP^5UHAXRhD1zF?*|gx5Q6xx5;tTy>=Z6x%%`Q86j_oCQGzt|V2_i^!BDklR8F9@dMDCY*BcgNW zVVMmb%(~{_;GdwVk_`ptPoTo_=5gdyERW-);vXjt`r*hn7v}oO8Y3Rnwd^5GVEZ3Az zFHH-J0hb#OBmk@c0BVC} A-2eap diff --git a/libs/features/mas/commerce/libs/commerce.d.ts b/libs/features/mas/commerce/libs/commerce.d.ts index 4f909701e9..20c92d59e8 100644 --- a/libs/features/mas/commerce/libs/commerce.d.ts +++ b/libs/features/mas/commerce/libs/commerce.d.ts @@ -2,11 +2,6 @@ import { CheckoutType, WorkflowStep, } from '@pandora/commerce-checkout-url-builder'; -import { - ProviderEnvironment, - Landscape, - Environment, -} from '@pandora/data-source-utils'; import { PriceDetails, ResolvedOffer, @@ -131,8 +126,8 @@ declare global { interface Settings { country: string; - env: ProviderEnvironment; - landscape: Landscape; + env: string; + landscape: string; // TODO: ideally, this setting should be processed by price template and belong to price settings forceTaxExclusive: boolean; language: string; @@ -334,7 +329,6 @@ declare global { checkoutWorkflow: CheckoutType; checkoutWorkflowStep: WorkflowStep; entitlement: boolean; - upgrade: boolean; modal: boolean; extraOptions: Partial>; } @@ -575,8 +569,7 @@ declare global { wcsApiKey: string; wcsBufferDelay: number; wcsBufferLimit: number; - wcsEnv: Environment; - domainSwitch: boolean; + wcsURL: string; } } } diff --git a/libs/features/mas/commerce/package.json b/libs/features/mas/commerce/package.json index 4b27bd07eb..7a1964d597 100644 --- a/libs/features/mas/commerce/package.json +++ b/libs/features/mas/commerce/package.json @@ -11,7 +11,7 @@ "types": "src/index.d.ts", "sideEffects": false, "scripts": { - "build": "npm run build:bundle && npm run build:types", + "build": "npm run test:ci && npm run build:bundle && npm run build:types", "build:bundle": "node ./build.mjs", "build:types": "cp ./src/index.d.ts ./libs/commerce.d.ts", "build:watch": "npm run build:bundle --watch", @@ -20,16 +20,11 @@ }, "dependencies": { "@dexter/tacocat-consonant-templates": "file:./internal/tacocat-consonant-templates-1.13.0.tgz", - "@dexter/tacocat-core": "file:./internal/tacocat-core-1.12.2.tgz", - "@dexter/tacocat-wcs-client": "file:./internal/tacocat-wcs-client-1.17.0.tgz", + "@dexter/tacocat-core": "file:./internal/tacocat-core-1.13.0.tgz", "@pandora/commerce-checkout-url-builder": "file:./internal/commerce-checkout-url-builder-1.6.0.tgz", "@pandora/data-models-odm": "file:./internal/data-models-odm-0.5.4.tgz", - "@pandora/data-source-wcs": "file:./internal/data-source-wcs-0.2.8.tgz", "@pandora/data-source-utils": "file:./internal/data-source-utils-0.3.1.tgz", "@pandora/fetch": "file:./internal/fetch-1.3.4.tgz", - "@pandora/react-auth-provider": "file:./internal/react-auth-provider-1.2.1.tgz", - "@pandora/react-env-provider": "file:./internal/react-env-provider-1.2.2.tgz", - "@pandora/react-fetch-provider": "file:./internal/react-fetch-provider-1.2.2.tgz", "@pandora/logger": "file:./internal/logger-1.3.0.tgz" }, "devDependencies": { diff --git a/libs/features/mas/commerce/src/constants.js b/libs/features/mas/commerce/src/constants.js index 29be2fafa6..77cb8387b9 100644 --- a/libs/features/mas/commerce/src/constants.js +++ b/libs/features/mas/commerce/src/constants.js @@ -19,8 +19,16 @@ export const PARAM_LANDSCAPE = 'commerce.landscape'; export const PARAM_AOS_API_KEY = 'commerce.aosKey'; export const PARAM_WCS_API_KEY = 'commerce.wcsKey'; +export const WCS_PROD_URL = 'https://www.adobe.com/web_commerce_artifact'; +export const WCS_STAGE_URL = 'https://www.stage.adobe.com/web_commerce_artifact_stage'; + export const STATE_FAILED = 'failed'; export const STATE_PENDING = 'pending'; export const STATE_RESOLVED = 'resolved'; export const TAG_NAME_SERVICE = 'wcms-commerce'; + +export const Landscape = { + DRAFT: "DRAFT", + PUBLISHED: "PUBLISHED" +} diff --git a/libs/features/mas/commerce/src/defaults.js b/libs/features/mas/commerce/src/defaults.js index 46921d00fb..da7ac92f14 100644 --- a/libs/features/mas/commerce/src/defaults.js +++ b/libs/features/mas/commerce/src/defaults.js @@ -2,9 +2,8 @@ import { CheckoutWorkflow, CheckoutWorkflowStep, Env, - Landscape, - WcsEnv, } from './external.js'; +import { Landscape } from './constants.js'; /** @type {Commerce.Defaults} */ export const Defaults = Object.freeze({ @@ -16,7 +15,6 @@ export const Defaults = Object.freeze({ displayPerUnit: false, displayRecurrence: true, displayTax: false, - domainSwitch: false, env: Env.PRODUCTION, forceTaxExclusive: false, language: 'en', @@ -27,7 +25,7 @@ export const Defaults = Object.freeze({ quantity: 1, wcsApiKey: 'wcms-commerce-ims-ro-user-milo', wcsBufferDelay: 1, - wcsEnv: WcsEnv.PRODUCTION, + wcsURL: 'https://www.adobe.com/web_commerce_artifact', landscape: Landscape.PUBLISHED, wcsBufferLimit: 1, }); diff --git a/libs/features/mas/commerce/src/external.js b/libs/features/mas/commerce/src/external.js index 8680e7f772..c611e50e60 100644 --- a/libs/features/mas/commerce/src/external.js +++ b/libs/features/mas/commerce/src/external.js @@ -5,12 +5,6 @@ import { buildCheckoutUrl, } from '@pandora/commerce-checkout-url-builder'; import { Term, Commitment } from '@pandora/data-models-odm'; -import { - Environment, - Landscape, - ProviderEnvironment, -} from '@pandora/data-source-utils'; -import { webCommerceArtifact } from '@pandora/data-source-wcs'; import { price, @@ -33,14 +27,11 @@ import { omitProperties, toBoolean, toEnumeration, - toKebabCase, toPositiveFiniteInteger, -} from '@dexter/tacocat-core'; -import { applyPlanType, forceTaxExclusivePrice, PlanType, -} from '@dexter/tacocat-wcs-client'; +} from '@dexter/tacocat-core'; const { freeze } = Object; @@ -48,10 +39,13 @@ const { freeze } = Object; const CheckoutWorkflow = freeze({ ...CheckoutType }); /** @type {Commerce.Checkout.CheckoutWorkflowStep} */ const CheckoutWorkflowStep = freeze({ ...WorkflowStep }); -const Env = freeze({ ...ProviderEnvironment }); +const Env = { + STAGE: "STAGE", + PRODUCTION: "PRODUCTION", + LOCAL: "LOCAL" +}; /** @type {Commerce.Wcs.WcsCommitment} */ const WcsCommitment = freeze({ ...Commitment }); -const WcsEnv = freeze({ ...Environment }); /** @type {Commerce.Wcs.WcsPlanType} */ const WcsPlanType = freeze({ ...PlanType }); /** @type {Commerce.Wcs.WcsTerm} */ @@ -62,8 +56,6 @@ export { CheckoutWorkflowStep, Env, WcsCommitment, - WcsEnv, - Landscape, WcsTerm, WcsPlanType, applyPlanType, @@ -87,7 +79,5 @@ export { discount, toBoolean, toEnumeration, - toKebabCase, toPositiveFiniteInteger, - webCommerceArtifact, }; diff --git a/libs/features/mas/commerce/src/index.d.ts b/libs/features/mas/commerce/src/index.d.ts index 4f909701e9..20c92d59e8 100644 --- a/libs/features/mas/commerce/src/index.d.ts +++ b/libs/features/mas/commerce/src/index.d.ts @@ -2,11 +2,6 @@ import { CheckoutType, WorkflowStep, } from '@pandora/commerce-checkout-url-builder'; -import { - ProviderEnvironment, - Landscape, - Environment, -} from '@pandora/data-source-utils'; import { PriceDetails, ResolvedOffer, @@ -131,8 +126,8 @@ declare global { interface Settings { country: string; - env: ProviderEnvironment; - landscape: Landscape; + env: string; + landscape: string; // TODO: ideally, this setting should be processed by price template and belong to price settings forceTaxExclusive: boolean; language: string; @@ -334,7 +329,6 @@ declare global { checkoutWorkflow: CheckoutType; checkoutWorkflowStep: WorkflowStep; entitlement: boolean; - upgrade: boolean; modal: boolean; extraOptions: Partial>; } @@ -575,8 +569,7 @@ declare global { wcsApiKey: string; wcsBufferDelay: number; wcsBufferLimit: number; - wcsEnv: Environment; - domainSwitch: boolean; + wcsURL: string; } } } diff --git a/libs/features/mas/commerce/src/index.js b/libs/features/mas/commerce/src/index.js index 762a9906ab..2a46453a1e 100644 --- a/libs/features/mas/commerce/src/index.js +++ b/libs/features/mas/commerce/src/index.js @@ -1,12 +1,10 @@ import { CheckoutLink } from './checkout-link.js'; -import { TAG_NAME_SERVICE } from './constants.js'; +import { TAG_NAME_SERVICE, Landscape } from './constants.js'; import { Defaults } from './defaults.js'; import { CheckoutWorkflow, CheckoutWorkflowStep, WcsCommitment, - WcsEnv, - Landscape, WcsTerm, WcsPlanType, applyPlanType, @@ -25,7 +23,6 @@ export { InlinePrice, Log, WcsCommitment, - WcsEnv, Landscape, WcsTerm, WcsPlanType, diff --git a/libs/features/mas/commerce/src/settings.js b/libs/features/mas/commerce/src/settings.js index b780e36c2b..1c47fdde58 100644 --- a/libs/features/mas/commerce/src/settings.js +++ b/libs/features/mas/commerce/src/settings.js @@ -1,11 +1,9 @@ -import { PARAM_ENV, PARAM_LANDSCAPE } from './constants.js'; +import { PARAM_ENV, PARAM_LANDSCAPE, Landscape, WCS_PROD_URL, WCS_STAGE_URL } from './constants.js'; import { Defaults } from './defaults.js'; import { CheckoutWorkflow, CheckoutWorkflowStep, Env, - WcsEnv, - Landscape, getParameter, toBoolean, toEnumeration, @@ -135,15 +133,16 @@ function getSettings(config = {}) { // TODO: add alias names for meta, search and storage // See https://git.corp.adobe.com/wcms/tacocat.js/pull/348#discussion_r6557570 const { commerce = {}, locale = undefined } = config; - const hostEnv = - config.env?.name === HostEnv.PROD - ? HostEnv.PROD - : toEnumeration( - getParameter(PARAM_ENV, commerce, { metadata: false }), - HostEnv, - HostEnv.PROD, - ); - const env = hostEnv === HostEnv.STAGE ? Env.STAGE : Env.PRODUCTION; + let env = Env.PRODUCTION; + let wcsURL = WCS_PROD_URL; + + const lowHostEnv = ['local', 'stage'].includes(config.env?.name); + const forceWcsStage = getParameter(PARAM_ENV, commerce, { metadata: false })?.toLowerCase() === 'stage'; + if (lowHostEnv && forceWcsStage) { + env = Env.STAGE; + wcsURL = WCS_STAGE_URL; + } + const checkoutClientId = getParameter('checkoutClientId', commerce) ?? Defaults.checkoutClientId; const checkoutWorkflow = toEnumeration( @@ -204,7 +203,6 @@ function getSettings(config = {}) { getParameter('wcsBufferLimit', commerce), Defaults.wcsBufferLimit, ); - const domainSwitch = toBoolean(getParameter('domain.switch', commerce), false); return { ...getLocaleSettings({ locale }), @@ -227,9 +225,8 @@ function getSettings(config = {}) { wcsApiKey, wcsBufferDelay, wcsBufferLimit, - wcsEnv: env === Env.STAGE ? WcsEnv.STAGE : WcsEnv.PRODUCTION, + wcsURL, landscape, - domainSwitch, }; } diff --git a/libs/features/mas/commerce/src/wcs.js b/libs/features/mas/commerce/src/wcs.js index 3c3088bab1..1773378891 100644 --- a/libs/features/mas/commerce/src/wcs.js +++ b/libs/features/mas/commerce/src/wcs.js @@ -8,42 +8,22 @@ import { WcsPlanType, WcsTerm, applyPlanType, - webCommerceArtifact, } from './external.js'; import { Log } from './log.js'; -/** @typedef {import('@pandora/data-source-wcs').GetWebCommerceArtifactOptions} WcsOptions */ -/** @typedef {import('@pandora/data-source-wcs').getWebCommerceArtifactPromise} getWcsOffers */ /** * @typedef {Map void, * reject: (reason: Error) => void * }>} WcsPromises */ -const ACOM = '_acom'; -const WcsBaseUrl = { - [Env.PRODUCTION]: 'https://www.adobe.com', - [Env.STAGE]: 'https://www.stage.adobe.com', - [Env.PRODUCTION + ACOM]: 'https://www.adobe.com', - [Env.STAGE + ACOM]: 'https://www.stage.adobe.com', -}; - /** * @param {{ settings: Commerce.Wcs.Settings }} params * @returns {Commerce.Wcs.Client} */ export function Wcs({ settings }) { const log = Log.module('wcs'); - const { env, domainSwitch, wcsApiKey: apiKey } = settings; - const baseUrl = domainSwitch ? WcsBaseUrl[env + ACOM] : WcsBaseUrl[env]; - // Create @pandora Wcs client. - const fetchOptions = { - apiKey, - baseUrl, - fetch: window.fetch.bind(window), - }; - const getWcsOffers = webCommerceArtifact(fetchOptions); - + const { env, wcsApiKey: apiKey } = settings; /** * Cache of promises resolving to arrays of Wcs offers grouped by osi-based keys. * @type {Map>} @@ -51,7 +31,7 @@ export function Wcs({ settings }) { const cache = new Map(); /** * Queue of pending requests to Wcs grouped by locale and promo. - * @type {Map} + * @type {Map} */ const queue = new Map(); let timer; @@ -63,29 +43,40 @@ export function Wcs({ settings }) { * If WCS does not provide an offer for particular osi, * its pending promise will be rejected with "not found". * In case of any other Wcs/Network error, promises are rejected with "bad request". - * @param {WcsOptions} options + * @param options * @param {WcsPromises} promises * @param reject - used for recursion, prevents rejection of promises with missing offers */ async function resolveWcsOffers(options, promises, reject = true) { let message = ERROR_MESSAGE_OFFER_NOT_FOUND; + log.debug('Fetching:', options); try { - log.debug('Fetching:', options); - options.offerSelectorIds = options.offerSelectorIds.sort(); - const { data } = await getWcsOffers( - options, - { - apiKey, - environment: settings.wcsEnv, - // @ts-ignore - landscape: env === Env.STAGE ? 'ALL' : settings.landscape, - }, - ({ resolvedOffers }) => ({ - offers: resolvedOffers.map(applyPlanType), - }), - ); + options.offerSelectorIds = options.offerSelectorIds.sort(); + const url = new URL(settings.wcsURL); + url.searchParams.set('offer_selector_ids', options.offerSelectorIds.join(',')); + url.searchParams.set('country', options.country); + url.searchParams.set('locale', options.locale); + url.searchParams.set('landscape', env === Env.STAGE ? 'ALL' : settings.landscape); + url.searchParams.set('api_key', apiKey); + // language can be undefined if its a UK offer + if (options.language) { + url.searchParams.set('language', options.language); + } + if (options.promotionCode) { + url.searchParams.set('promotion_code', options.promotionCode); + } + if (options.currency) { + url.searchParams.set('currency', options.currency); + } + + const response = await fetch(url.toString(), { + credentials: 'omit' + }); + if (response.ok) { + const data = await response.json(); log.debug('Fetched:', options, data); - const { offers } = data ?? {}; + let offers = data.resolvedOffers ?? []; + offers = offers.map(applyPlanType); // resolve all promises that have offers promises.forEach(({ resolve }, offerSelectorId) => { // select offers with current OSI @@ -99,25 +90,28 @@ export function Wcs({ settings }) { promises.delete(offerSelectorId); resolve(resolved); } - }); - } catch (error) { - // in case of 404 WCS error caused by a request with multiple osis, - // fallback to `fetch-by-one` strategy - if (error.status === 404 && options.offerSelectorIds.length > 1) { - log.debug('Multi-osi 404, fallback to fetch-by-one strategy'); - await Promise.allSettled( - options.offerSelectorIds.map((offerSelectorId) => - resolveWcsOffers( - { ...options, offerSelectorIds: [offerSelectorId] }, - promises, - false, // do not reject promises for missing offers, this will be done below - ), + }); + } + // in case of 404 WCS error caused by a request with multiple osis, + // fallback to `fetch-by-one` strategy + else if (response.status === 404 && options.offerSelectorIds.length > 1) { + log.debug('Multi-osi 404, fallback to fetch-by-one strategy'); + await Promise.allSettled( + options.offerSelectorIds.map((offerSelectorId) => + resolveWcsOffers( + { ...options, offerSelectorIds: [offerSelectorId] }, + promises, + false, // do not reject promises for missing offers, this will be done below ), - ); - } else { - log.error('Failed:', options, error); - message = ERROR_MESSAGE_BAD_REQUEST; - } + ), + ); + } else { + message = ERROR_MESSAGE_BAD_REQUEST; + log.error(message, options); + } + } catch (e) { + message = ERROR_MESSAGE_BAD_REQUEST; + log.error(message, options, e); } if (reject && promises.size) { diff --git a/libs/features/mas/commerce/test/settings.test.js b/libs/features/mas/commerce/test/settings.test.js index 65aefb560b..4ab9f2da9c 100644 --- a/libs/features/mas/commerce/test/settings.test.js +++ b/libs/features/mas/commerce/test/settings.test.js @@ -1,4 +1,5 @@ -import { Env, WcsEnv, Landscape } from '../src/external.js'; +import { Env } from '../src/external.js'; +import { Landscape, WCS_PROD_URL, WCS_STAGE_URL } from '../src/constants.js'; import { Defaults } from '../src/defaults.js'; import { getSettings } from '../src/settings.js'; @@ -11,7 +12,11 @@ describe('getSettings', () => { after(() => { document.head.querySelectorAll('meta').forEach((meta) => meta.remove()); window.history.replaceState({}, '', href); + }); + + afterEach(() => { window.sessionStorage.clear(); + window.history.replaceState({}, '', href); }); before(() => { @@ -28,10 +33,58 @@ describe('getSettings', () => { }); }); - it('uses location search, document metadata and storage', () => { - const checkoutClientId = 'checkout-client-id'; - const url = `${window.location.href}&checkoutClientId=${checkoutClientId}`; - window.history.replaceState({}, '', url); + it('overrides with search parameters', () => { + const checkoutClientId = 'adobecom'; + const checkoutWorkflowStep = 'segmentation'; + const promotionCode = 'nicopromo'; + + const url = new URL(window.location.href); + url.searchParams.set('checkoutClientId', checkoutClientId); + url.searchParams.set('checkoutWorkflowStep', checkoutWorkflowStep); + url.searchParams.set('promotionCode', promotionCode); + url.searchParams.set('displayOldPrice', 'false'); + url.searchParams.set('displayPerUnit', 'true'); + url.searchParams.set('displayRecurrence', 'false'); + url.searchParams.set('displayTax', 'true'); + url.searchParams.set('entitlement', 'true'); + url.searchParams.set('modal', 'true'); + url.searchParams.set('commerce.landscape', 'DRAFT'); + url.searchParams.set('commerce.env', 'STAGE'); + url.searchParams.set('wcsBufferDelay', '30'); + url.searchParams.set('wcsBufferLimit', '5'); + url.searchParams.set('quantity', '2'); + url.searchParams.set('wcsApiKey', 'testapikey'); + window.history.replaceState({}, '', url.toString()); + + const config = { commerce: {}, env: { name: 'stage' }, }; + expect( + getSettings(config), + ).to.deep.equal({ + ...Defaults, + checkoutClientId, + checkoutWorkflowStep, + promotionCode, + displayOldPrice: false, + displayPerUnit: true, + displayRecurrence: false, + displayTax: true, + entitlement: true, + modal: true, + landscape: 'DRAFT', + wcsBufferDelay: 30, + wcsBufferLimit: 5, + quantity: [2], + wcsApiKey: 'testapikey', + locale: "en_US", + priceLiteralsURL: undefined, + priceLiteralsPromise: undefined, + env: "STAGE", + wcsURL: WCS_STAGE_URL + }); + }); + + + it('uses document metadata and storage', () => { const wcsApiKey = 'wcs-api-key'; const meta = document.createElement('meta'); meta.content = wcsApiKey; @@ -54,7 +107,6 @@ describe('getSettings', () => { ...Defaults, forceTaxExclusive: true, promotionCode: 'promo1', - checkoutClientId, country: 'NO', env: Env.STAGE, language: 'nb', @@ -63,53 +115,61 @@ describe('getSettings', () => { priceLiteralsPromise: undefined, quantity: [Defaults.quantity], wcsApiKey, - wcsEnv: WcsEnv.STAGE, + wcsURL: WCS_STAGE_URL, landscape: Landscape.DRAFT, }); window.sessionStorage.removeItem(PARAM_ENV); }); - it('if host env is "dev" - override commerce landscape', () => { - window.sessionStorage.setItem(PARAM_LANDSCAPE, 'DRAFT'); + it('host env "local" -> WCS prod origin + prod akamai', () => { + const config = { commerce: {}, env: { name: 'local' }, }; + const settings = getSettings(config); + expect(settings.wcsURL).to.equal(WCS_PROD_URL); + expect(settings.env).to.equal(Env.PRODUCTION); + }); - const config = { - commerce: {}, - env: { name: 'local' }, - }; - expect(getSettings(config)).to.deep.equal({ - ...Defaults, - checkoutClientId: 'checkout-client-id', - wcsApiKey: 'wcs-api-key', - env: Env.PRODUCTION, - locale: 'en_US', - priceLiteralsURL: undefined, - priceLiteralsPromise: undefined, - quantity: [Defaults.quantity], - wcsEnv: WcsEnv.PRODUCTION, - landscape: Landscape.DRAFT, - }); + it('host env "stage" -> WCS prod origin + prod akamai', () => { + const config = { commerce: {}, env: { name: 'stage' }, }; + const settings = getSettings(config); + expect(settings.wcsURL).to.equal(WCS_PROD_URL); + expect(settings.env).to.equal(Env.PRODUCTION); + }); + + it('host env "prod" -> WCS prod origin + prod akamai', () => { + const config = { commerce: {}, env: { name: 'prod' }, }; + const settings = getSettings(config); + expect(settings.wcsURL).to.equal(WCS_PROD_URL); + expect(settings.env).to.equal(Env.PRODUCTION); }); - it('if host env is "prod" - doesnt override commerce env and landscape', () => { + it('host env "local" - override landscape and WCS origin (_stage)', () => { window.sessionStorage.setItem(PARAM_ENV, 'stage'); window.sessionStorage.setItem(PARAM_LANDSCAPE, 'DRAFT'); + const config = { commerce: {}, env: { name: 'local' }, }; + const settings = getSettings(config); + expect(settings.wcsURL).to.equal(WCS_STAGE_URL); + expect(settings.landscape).to.equal(Landscape.DRAFT); + expect(settings.env).to.equal(Env.STAGE); + }); - const config = { - commerce: {}, - env: { name: 'prod' }, - }; - expect(getSettings(config)).to.deep.equal({ - ...Defaults, - checkoutClientId: 'checkout-client-id', - wcsApiKey: 'wcs-api-key', - env: Env.PRODUCTION, - locale: 'en_US', - priceLiteralsURL: undefined, - priceLiteralsPromise: undefined, - quantity: [Defaults.quantity], - wcsEnv: WcsEnv.PRODUCTION, - landscape: Landscape.PUBLISHED, - }); + it('host env "stage" - override landscape and WCS origin (_stage)', () => { + window.sessionStorage.setItem(PARAM_ENV, 'stage'); + window.sessionStorage.setItem(PARAM_LANDSCAPE, 'DRAFT'); + const config = { commerce: {}, env: { name: 'stage' }, }; + const settings = getSettings(config); + expect(settings.wcsURL).to.equal(WCS_STAGE_URL); + expect(settings.landscape).to.equal(Landscape.DRAFT); + expect(settings.env).to.equal(Env.STAGE); + }); + + it('if host env is "prod" - cant override landscape or WCS origin', () => { + window.sessionStorage.setItem(PARAM_ENV, 'stage'); + window.sessionStorage.setItem(PARAM_LANDSCAPE, 'DRAFT'); + const config = { commerce: {}, env: { name: 'prod' }, }; + const settings = getSettings(config); + expect(settings.wcsURL).to.equal(WCS_PROD_URL); + expect(settings.landscape).to.equal(Landscape.PUBLISHED); + expect(settings.env).to.equal(Env.PRODUCTION); }); [ diff --git a/libs/features/mas/commerce/test/wcs.test.js b/libs/features/mas/commerce/test/wcs.test.js index da6c5bad2d..f8f26fdfb5 100644 --- a/libs/features/mas/commerce/test/wcs.test.js +++ b/libs/features/mas/commerce/test/wcs.test.js @@ -1,4 +1,5 @@ import { ERROR_MESSAGE_OFFER_NOT_FOUND } from '../src/constants.js'; +import { Defaults } from '../src/defaults.js'; import { Wcs } from '../src/wcs.js'; import { mockFetch } from './mocks/fetch.js'; @@ -12,9 +13,9 @@ describe('resolveOfferSelectors', () => { const client = Wcs({ // @ts-ignore settings: { - country: 'US', - language: 'en', + ...Defaults, locale: 'en_US', + wcsBufferLimit: 4, }, }); const results = await Promise.allSettled( @@ -41,8 +42,7 @@ describe('resolveOfferSelectors', () => { const client = Wcs({ // @ts-ignore settings: { - country: 'US', - language: 'en', + ...Defaults, locale: 'en_US', wcsBufferLimit: 2, }, diff --git a/libs/features/mas/package-lock.json b/libs/features/mas/package-lock.json index 30161f54af..50c44c2a05 100644 --- a/libs/features/mas/package-lock.json +++ b/libs/features/mas/package-lock.json @@ -69,17 +69,12 @@ "version": "2.0.0", "dependencies": { "@dexter/tacocat-consonant-templates": "file:./internal/tacocat-consonant-templates-1.13.0.tgz", - "@dexter/tacocat-core": "file:./internal/tacocat-core-1.12.2.tgz", - "@dexter/tacocat-wcs-client": "file:./internal/tacocat-wcs-client-1.17.0.tgz", + "@dexter/tacocat-core": "file:./internal/tacocat-core-1.13.0.tgz", "@pandora/commerce-checkout-url-builder": "file:./internal/commerce-checkout-url-builder-1.6.0.tgz", "@pandora/data-models-odm": "file:./internal/data-models-odm-0.5.4.tgz", "@pandora/data-source-utils": "file:./internal/data-source-utils-0.3.1.tgz", - "@pandora/data-source-wcs": "file:./internal/data-source-wcs-0.2.8.tgz", "@pandora/fetch": "file:./internal/fetch-1.3.4.tgz", - "@pandora/logger": "file:./internal/logger-1.3.0.tgz", - "@pandora/react-auth-provider": "file:./internal/react-auth-provider-1.2.1.tgz", - "@pandora/react-env-provider": "file:./internal/react-env-provider-1.2.2.tgz", - "@pandora/react-fetch-provider": "file:./internal/react-fetch-provider-1.2.2.tgz" + "@pandora/logger": "file:./internal/logger-1.3.0.tgz" }, "devDependencies": { "@web/dev-server-import-maps": "^0.1.1", @@ -89,44 +84,6 @@ "esbuild": "0.18.11" } }, - "commons/node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "extraneous": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, "mas": { "name": "@adobe/mas", "version": "0.0.1", @@ -176,28 +133,6 @@ "@esbuild/win32-x64": "0.21.5" } }, - "node_modules/@75lb/deep-merge": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@75lb/deep-merge/-/deep-merge-1.1.1.tgz", - "integrity": "sha512-xvgv6pkMGBA6GwdyJbNAnDmfAIR/DfWhrj9jgWh3TY7gRm3KO46x/GPjRg6wJ0nOepwqrNxFfojebh0Df4h4Tw==", - "dev": true, - "dependencies": { - "lodash.assignwith": "^4.2.0", - "typical": "^7.1.1" - }, - "engines": { - "node": ">=12.17" - } - }, - "node_modules/@75lb/deep-merge/node_modules/typical": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/typical/-/typical-7.1.1.tgz", - "integrity": "sha512-T+tKVNs6Wu7IWiAce5BgMd7OZfNYUndHwc5MknN+UHOudi7sGZzuHdCadllRuqJ3fPtgFtIH9+lt9qRv6lmpfA==", - "dev": true, - "engines": { - "node": ">=12.17" - } - }, "node_modules/@adobe/mas": { "resolved": "mas", "link": true @@ -358,22 +293,9 @@ } }, "node_modules/@dexter/tacocat-core": { - "version": "1.12.2", - "resolved": "file:commerce/internal/tacocat-core-1.12.2.tgz", - "integrity": "sha512-ydBx5I1uxsQY5KjlN7VVs+J7kg0js/x2fBjuQ0DY9cKgZY7nYnzZfz/E7q8uGTVuqk5hW9cVzDFGLazUVqAmYg==" - }, - "node_modules/@dexter/tacocat-wcs-client": { - "version": "1.17.0", - "resolved": "file:commerce/internal/tacocat-wcs-client-1.17.0.tgz", - "integrity": "sha512-UVuXQaVvLhhFSYfq6bIYVOhEDMXAh95xCBR7qizH8ug0+DouDl12qjm+nWZGNcnw3iXke9tvYH0aGj+dYVQ6Uw==", - "dependencies": { - "@dexter/tacocat-consonant-templates": "^1.0.0", - "@dexter/tacocat-core": "^1.0.0", - "@pandora/commerce-checkout-url-builder": "^1.5.2", - "@pandora/data-models-odm": "0.5.4", - "@pandora/data-source-utils": "0.3.1", - "@pandora/data-source-wcs": "0.2.8" - } + "version": "1.13.0", + "resolved": "file:commerce/internal/tacocat-core-1.13.0.tgz", + "integrity": "sha512-3XWBVZk8/pvFxE+qcwcDHgpSYVrhK1Sm8I4PfMELpCxP2l0MbcxiBSMXAyLJZXbjSVT5gCzG45aHZEIAerBjBw==" }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", @@ -780,9 +702,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.0.tgz", - "integrity": "sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==", + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.1.tgz", + "integrity": "sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==", "dev": true, "dependencies": { "@eslint/object-schema": "^2.1.4", @@ -817,9 +739,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.6.0.tgz", - "integrity": "sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A==", + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.0.tgz", + "integrity": "sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -850,26 +772,26 @@ "dev": true }, "node_modules/@floating-ui/core": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.6.tgz", - "integrity": "sha512-Vkvsw6EcpMHjvZZdMkSY+djMGFbt7CRssW99Ne8tar2WLnZ/l3dbxeTShbLQj+/s35h+Qb4cmnob+EzwtjrXGQ==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz", + "integrity": "sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==", "dependencies": { - "@floating-ui/utils": "^0.2.6" + "@floating-ui/utils": "^0.2.7" } }, "node_modules/@floating-ui/dom": { - "version": "1.6.9", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.9.tgz", - "integrity": "sha512-zB1PcI350t4tkm3rvUhSRKa9sT7vH5CrAbQxW+VaPYJXKAO0gsg4CTueL+6Ajp7XzAQC8CW4Jj1Wgqc0sB6oUQ==", + "version": "1.6.10", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.10.tgz", + "integrity": "sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==", "dependencies": { "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.6" + "@floating-ui/utils": "^0.2.7" } }, "node_modules/@floating-ui/utils": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.6.tgz", - "integrity": "sha512-0KI3zGxIUs1KDR/pjQPdJH4Z8nGBm0yJ5WRoRfdw1Kzeh45jkIfA0rmD0kBF6fKHH+xaH7g8y4jIXyAV5MGK3g==" + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.7.tgz", + "integrity": "sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==" }, "node_modules/@formatjs/ecma402-abstract": { "version": "1.9.7", @@ -1045,22 +967,6 @@ "@pandora/fetch": "^1.0.1" } }, - "node_modules/@pandora/data-source-wcs": { - "version": "0.2.8", - "resolved": "file:commerce/internal/data-source-wcs-0.2.8.tgz", - "integrity": "sha512-eetfLZRhgiIKDmtWbb3oG8B+Zy+X5oqtwztmBzdw4FolB5y/A2Tgz0YzLX3Wn68wPq1STNPlt+Hffl4o320/Lg==", - "dependencies": { - "@pandora/data-models-odm": "^0.5.4", - "@pandora/data-source-utils": "^0.3.1", - "@pandora/react-auth-provider": "^1.0.1", - "@pandora/react-env-provider": "^1.1.0", - "@pandora/react-fetch-provider": "^1.0.1" - }, - "peerDependencies": { - "react": "^16.12.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.12.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/@pandora/fetch": { "version": "1.3.4", "resolved": "file:commerce/internal/fetch-1.3.4.tgz", @@ -1083,36 +989,6 @@ "ts-log": "^2.1.4" } }, - "node_modules/@pandora/react-auth-provider": { - "version": "1.2.1", - "resolved": "file:commerce/internal/react-auth-provider-1.2.1.tgz", - "integrity": "sha512-ulYG/WKP8oRvC7GMJTjmiklS925LenGmLZJitEQHkUYuik576HZn3tF7DbSCnA7L1IzaRMmw6cictRaCZ2skAg==", - "peerDependencies": { - "react": "^16.12.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.12.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@pandora/react-env-provider": { - "version": "1.2.2", - "resolved": "file:commerce/internal/react-env-provider-1.2.2.tgz", - "integrity": "sha512-uuj1d+Idn1nZ3YDZVCduO/kp/ZyBSGu1ThBErrIqPpopUk+uCfaYCNqS2kgznoEdAJ/9Lu0mMfq3asOvSURVLA==", - "peerDependencies": { - "react": "^16.12.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.12.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@pandora/react-fetch-provider": { - "version": "1.2.2", - "resolved": "file:commerce/internal/react-fetch-provider-1.2.2.tgz", - "integrity": "sha512-6qXrPeIuNxisEUfI2JhUuidIUIr4/gRy1Oq84CfuGhsj5rsaWMtJPLMSB5b1ezyNBsKHaPrG/tvp1lNsCk4Ltw==", - "dependencies": { - "@pandora/fetch": "^1.2.5" - }, - "peerDependencies": { - "react": "^16.12.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.12.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/@pkgr/core": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", @@ -1202,9 +1078,9 @@ } }, "node_modules/@sinonjs/text-encoding": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", - "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==", "dev": true }, "node_modules/@spectrum-web-components/action-button": { @@ -1691,9 +1567,9 @@ } }, "node_modules/@types/chai": { - "version": "4.3.16", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.16.tgz", - "integrity": "sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==", + "version": "4.3.17", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.17.tgz", + "integrity": "sha512-zmZ21EWzR71B4Sscphjief5djsLre50M6lI622OSySTmn9DB3j+C3kWroHfBQWXbOBwbgg/M8CG/hUxDLIloow==", "dev": true }, "node_modules/@types/chai-as-promised": { @@ -1879,12 +1755,12 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.14.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", - "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", + "version": "22.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.0.tgz", + "integrity": "sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==", "dev": true, "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.2" } }, "node_modules/@types/parse5": { @@ -2972,14 +2848,14 @@ } }, "node_modules/@web/test-runner/node_modules/@web/dev-server/node_modules/command-line-usage": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.2.tgz", - "integrity": "sha512-MwNFB8nxi3IVnzir+nkSIbDTU4H6ne26zqicO2eTt1wPrvdOAphPhnYqWOjxXKWYLNYDu4Z/r2ESEziEqKuOVg==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", + "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", "dev": true, "dependencies": { "array-back": "^6.2.2", "chalk-template": "^0.4.0", - "table-layout": "^3.0.2", + "table-layout": "^4.1.0", "typical": "^7.1.1" }, "engines": { @@ -3707,14 +3583,14 @@ } }, "node_modules/command-line-usage": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.2.tgz", - "integrity": "sha512-MwNFB8nxi3IVnzir+nkSIbDTU4H6ne26zqicO2eTt1wPrvdOAphPhnYqWOjxXKWYLNYDu4Z/r2ESEziEqKuOVg==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", + "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", "dev": true, "dependencies": { "array-back": "^6.2.2", "chalk-template": "^0.4.0", - "table-layout": "^3.0.2", + "table-layout": "^4.1.0", "typical": "^7.1.1" }, "engines": { @@ -3814,9 +3690,9 @@ "dev": true }, "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -4459,16 +4335,16 @@ } }, "node_modules/eslint": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.6.0.tgz", - "integrity": "sha512-ElQkdLMEEqQNM9Njff+2Y4q2afHk7JpkPvrd7Xh7xefwgQynqPxwf55J7di9+MEibWUGdNjFF9ITG9Pck5M84w==", + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.0.tgz", + "integrity": "sha512-JfiKJrbx0506OEerjK2Y1QlldtBxkAlLxT5OEcRF8uaQ86noDe2k31Vw9rnSWv+MXZHj7OOUV/dA0AhdLFcyvA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/config-array": "^0.17.0", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.17.1", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.6.0", + "@eslint/js": "9.9.0", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", @@ -4477,7 +4353,7 @@ "cross-spawn": "^7.0.2", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.0.1", + "eslint-scope": "^8.0.2", "eslint-visitor-keys": "^4.0.0", "espree": "^10.1.0", "esquery": "^1.5.0", @@ -4507,6 +4383,14 @@ }, "funding": { "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-config-prettier": { @@ -4522,13 +4406,13 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", - "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", "dev": true, "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.6" + "synckit": "^0.9.1" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -4552,9 +4436,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", - "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", + "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -4853,9 +4737,9 @@ "dev": true }, "node_modules/focus-visible": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/focus-visible/-/focus-visible-5.2.0.tgz", - "integrity": "sha512-Rwix9pBtC1Nuy5wysTmKy+UjbDJpIfg8eHjw0rjZ1mX4GNLz1Bmd16uDpI3Gk1i70Fgcs8Csg2lPm8HULFg9DQ==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/focus-visible/-/focus-visible-5.2.1.tgz", + "integrity": "sha512-8Bx950VD1bWTQJEH/AM6SpEk+SU55aVnp4Ujhuuxy3eMEBCRwBnTBnVXr9YAPvZL3/CNjCa8u4IWfNmEO53whA==" }, "node_modules/fresh": { "version": "0.5.2", @@ -5144,12 +5028,12 @@ } }, "node_modules/husky": { - "version": "9.0.11", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", - "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.5.tgz", + "integrity": "sha512-rowAVRUBfI0b4+niA4SJMhfQwc107VLkBUgEYYAOQAbqDCnra1nYh83hF/MDmhYs9t9n1E3DuKOrs2LYNC+0Ag==", "dev": true, "bin": { - "husky": "bin.mjs" + "husky": "bin.js" }, "engines": { "node": ">=18" @@ -5191,9 +5075,9 @@ ] }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { "node": ">= 4" @@ -5330,9 +5214,9 @@ } }, "node_modules/is-core-module": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz", - "integrity": "sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, "dependencies": { "hasown": "^2.0.2" @@ -5521,7 +5405,8 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, "node_modules/js-yaml": { "version": "4.1.0", @@ -5797,12 +5682,6 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "node_modules/lodash.assignwith": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assignwith/-/lodash.assignwith-4.2.0.tgz", - "integrity": "sha512-ZznplvbvtjK2gMvnQ1BR/zqPFZmS6jbK4p+6Up4xcRYA7yMIwxHCfbTcrYxXKzzqLsQ05eJPVznEW3tuwV7k1g==", - "dev": true - }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -5844,18 +5723,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "peer": true, - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, "node_modules/lru-cache": { "version": "8.0.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", @@ -6586,9 +6453,9 @@ } }, "node_modules/prettier": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", - "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -6796,9 +6663,9 @@ } }, "node_modules/qs": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.3.tgz", - "integrity": "sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dev": true, "dependencies": { "side-channel": "^1.0.6" @@ -6870,31 +6737,6 @@ "node": ">= 0.8" } }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -7112,19 +6954,10 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0" - } - }, "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -7264,15 +7097,6 @@ "node": ">= 0.6" } }, - "node_modules/stream-read-all": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/stream-read-all/-/stream-read-all-3.0.1.tgz", - "integrity": "sha512-EWZT9XOceBPlVJRrYcykW8jyRSZYbkb/0ZK36uLEmoWVO5gxBOnntNTseNzfREsqxqdfEGQrD8SXQ3QWbBmq8A==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -7354,9 +7178,9 @@ } }, "node_modules/synckit": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", - "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", + "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", "dev": true, "dependencies": { "@pkgr/core": "^0.1.0", @@ -7370,22 +7194,14 @@ } }, "node_modules/table-layout": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-3.0.2.tgz", - "integrity": "sha512-rpyNZYRw+/C+dYkcQ3Pr+rLxW4CfHpXjPDnG7lYhdRoUcZTUt+KEsX+94RGp/aVp/MQU35JCITv2T/beY4m+hw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", + "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", "dev": true, "dependencies": { - "@75lb/deep-merge": "^1.1.1", "array-back": "^6.2.2", - "command-line-args": "^5.2.1", - "command-line-usage": "^7.0.0", - "stream-read-all": "^3.0.1", - "typical": "^7.1.1", "wordwrapjs": "^5.1.0" }, - "bin": { - "table-layout": "bin/cli.js" - }, "engines": { "node": ">=12.17" } @@ -7399,15 +7215,6 @@ "node": ">=12.17" } }, - "node_modules/table-layout/node_modules/typical": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/typical/-/typical-7.1.1.tgz", - "integrity": "sha512-T+tKVNs6Wu7IWiAce5BgMd7OZfNYUndHwc5MknN+UHOudi7sGZzuHdCadllRuqJ3fPtgFtIH9+lt9qRv6lmpfA==", - "dev": true, - "engines": { - "node": ">=12.17" - } - }, "node_modules/tar-fs": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", @@ -7589,9 +7396,9 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true }, "node_modules/unpipe": { @@ -7774,25 +7581,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "studio": { - "name": "@adobe/mas-studio", - "version": "0.0.1", - "extraneous": true, - "license": "ISC", - "dependencies": { - "@adobe/lit-mobx": "^2.2.2", - "@adobe/mas": "^0.0.1", - "@adobe/mas-web-components": "^0.0.1", - "mobx": "^6.12.4" - }, - "devDependencies": { - "@web/dev-server-import-maps": "^0.2.1", - "@web/test-runner": "^0.13.27", - "@web/test-runner-commands": "^0.6.1", - "esbuild": "^0.21.5", - "esbuild-node-externals": "^1.13.1" - } - }, "web-components": { "name": "@adobe/mas-web-components", "version": "0.0.1", From 3551ffd610e157923303fafa82489cb6c7f04a44 Mon Sep 17 00:00:00 2001 From: Ryan Parrish Date: Mon, 2 Sep 2024 02:17:44 -0600 Subject: [PATCH 09/66] MWPW-154209 - [hero-marquee] bugs (#2727) * hero bugz updates * added wrapInnerHTMLInPTag for single line entries * changed order to wrapInnerinP so it happens before decorateButtons * PR feedback * PR feedback - minor logic fix for potential null value * minor fix, missing scope for flex dir col for .bg-bottom-tablet * missing selector for bg-bottom-tablet btm padding * Fixed scope so row-lockup also gets iconography.css * PR feedback, shorten some code logic * await loadIconography --- libs/blocks/hero-marquee/hero-marquee.css | 74 +++++------------------ libs/blocks/hero-marquee/hero-marquee.js | 68 +++++++++++++++------ 2 files changed, 64 insertions(+), 78 deletions(-) diff --git a/libs/blocks/hero-marquee/hero-marquee.css b/libs/blocks/hero-marquee/hero-marquee.css index 67a3d5eade..34c9175a3f 100644 --- a/libs/blocks/hero-marquee/hero-marquee.css +++ b/libs/blocks/hero-marquee/hero-marquee.css @@ -62,22 +62,8 @@ justify-content: center; } -/* Lockup Area */ -.hero-marquee .lockup-area { - font-weight: 700; - display: flex; - align-items: center; - gap: var(--spacing-xs); - margin: 0 0 var(--spacing-xxs); - font-size: var(--type-body-m-size); - line-height: var(--type-body-m-lh); - text-transform: initial; - white-space: nowrap; -} - -.hero-marquee .lockup-area picture { - line-height: 0; - display: block; +.hero-marquee .row-lockup .lockup-area { + margin: 0; } .hero-marquee .lockup-area a, @@ -102,45 +88,6 @@ display: block; } -/* Lockup Area sizes - default large */ -.hero-marquee .lockup-area picture img, -.hero-marquee .l-icon .lockup-area, -.hero-marquee.l-icon .lockup-area { - font-size: var(--type-body-m-size); - line-height: var(--type-body-m-lh); -} - -.hero-marquee .lockup-area picture img, -.hero-marquee .l-icon .lockup-area picture img, -.hero-marquee.l-icon .lockup-area picture img { - min-width: var(--icon-size-l); - height: var(--icon-size-l); -} - -.hero-marquee .m-icon .lockup-area, -.hero-marquee.m-icon .lockup-area { - font-size: var(--type-body-s-size); - line-height: var(--type-body-s-lh); -} - -.hero-marquee .m-icon .lockup-area picture img, -.hero-marquee.m-icon .lockup-area picture img { - min-width: var(--icon-size-m); - height: var(--icon-size-m); -} - -.hero-marquee .xl-icon .lockup-area, -.hero-marquee.xl-icon .lockup-area { - font-size: var(--type-body-xl-size); - line-height: var(--type-body-xl-lh); -} - -.hero-marquee .xl-icon .lockup-area picture img, -.hero-marquee.xl-icon .lockup-area picture img { - min-width: var(--icon-size-xl); - height: var(--icon-size-xl); -} - .hero-marquee.center { text-align: center; align-items: center; @@ -158,7 +105,6 @@ .hero-marquee .main-copy [class^="heading"], .hero-marquee .norm p:only-child { margin: 0; } .hero-marquee .norm :is(h1, h2, h3, h4, h5, h6) { margin: 0 0 var(--spacing-xs) 0; } -.hero-marquee .norm .action-area { margin-top: var(--spacing-s); } .hero-marquee .norm div > *:last-child { margin-bottom: 0; } .hero-marquee .norm div *:first-child { margin-top: 0; } @@ -322,8 +268,10 @@ html[dir="rtl"] .hero-marquee li.icon-item span.icon { } /* con-vars support */ - .hero-marquee:is(.bg-top-mobile, .bg-top-mobile, .bg-bottom-mobile) .background { + .hero-marquee:is(.bg-top-mobile, .bg-bottom-mobile, .bg-top-tablet, .bg-bottom-tablet) .background, + .hero-marquee:is(.bg-top-mobile, .bg-bottom-mobile, .bg-top-tablet, .bg-bottom-tablet) .background video { position: relative; + width: 100%; } .hero-marquee.bg-top-mobile { @@ -375,14 +323,19 @@ html[dir="rtl"] .hero-marquee li.icon-item span.icon { } /* con-vars support */ - .hero-marquee.bg-top-tablet .background, - .hero-marquee.bg-bottom-tablet .background { + .hero-marquee:is(.bg-top-tablet, .bg-bottom-tablet) .background, + .hero-marquee:is(.bg-top-tablet, .bg-bottom-tablet) .background video { position: relative; + width: 100%; } .hero-marquee.bg-top-tablet { padding-top: 0; } + + .hero-marquee.bg-bottom-tablet { + padding-bottom: 0; + } .hero-marquee.bg-bottom-tablet .background { order: 2; @@ -413,7 +366,8 @@ html[dir="rtl"] .hero-marquee li.icon-item span.icon { justify-content: center; } - .hero-marquee.bg-top-tablet { + .hero-marquee.bg-top-tablet, + .hero-marquee.bg-bottom-tablet { flex-direction: column; } diff --git a/libs/blocks/hero-marquee/hero-marquee.js b/libs/blocks/hero-marquee/hero-marquee.js index 230742aa5a..550c5aa2c9 100644 --- a/libs/blocks/hero-marquee/hero-marquee.js +++ b/libs/blocks/hero-marquee/hero-marquee.js @@ -11,9 +11,13 @@ import { createTag, loadStyle, getConfig } from '../../utils/utils.js'; const contentTypes = ['list', 'qrcode', 'lockup', 'text', 'bgcolor', 'supplemental']; const rowTypeKeyword = 'con-block-row-'; const breakpointThemeClasses = ['dark-mobile', 'light-mobile', 'dark-tablet', 'light-tablet', 'dark-desktop', 'light-desktop']; +const textDefault = ['xxl', 'm', 'l']; // heading, body, detail + +const { miloLibs, codeRoot } = getConfig(); +const base = miloLibs || codeRoot; function distillClasses(el, classes) { - const taps = ['-heading', '-body', '-detail']; + const taps = ['-heading', '-body', '-detail', '-button']; classes?.forEach((elClass) => { const elTaps = taps.filter((tap) => elClass.endsWith(tap)); if (!elTaps.length) return; @@ -48,10 +52,31 @@ function decorateQr(el) { }); } -function decorateLockupFromContent(el) { +async function loadIconography() { + await new Promise((resolve) => { loadStyle(`${base}/styles/iconography.css`, resolve); }); +} + +async function decorateLockupFromContent(el) { const rows = el.querySelectorAll(':scope > p'); const firstRowImg = rows[0]?.querySelector('img'); - if (firstRowImg) rows[0].classList.add('lockup-area'); + if (!firstRowImg) return; + await loadIconography(); + rows[0].classList.add('lockup-area'); + rows[0].childNodes.forEach((node) => { + if (node.nodeType === 3 && node.nodeValue?.trim()) { + const newSpan = createTag('span', { class: 'lockup-label' }, node.nodeValue); + node.parentElement.replaceChild(newSpan, node); + } + }); +} + +async function decorateLockupRow(el, classes) { + const child = el.querySelector(':scope > div'); + await loadIconography(); + child?.classList.add('lockup-area'); + const iconSizeClass = classes?.find((c) => c.endsWith('-icon')); + if (iconSizeClass) el.classList.remove(iconSizeClass); + el.classList.add(`${iconSizeClass?.split('-')[0] || 'l'}-lockup`); } function decorateBg(el) { @@ -60,22 +85,29 @@ function decorateBg(el) { el.remove(); } +function wrapInnerHTMLInPTag(el) { + const innerDiv = el.querySelector(':scope > div'); + const containsPTag = [...innerDiv.childNodes].some((node) => node.nodeName === 'P'); + if (!containsPTag) { + const pTag = createTag('p'); + while (innerDiv.firstChild) pTag.appendChild(innerDiv.firstChild); + innerDiv.appendChild(pTag); + } +} + function decorateText(el, classes) { el.classList.add('norm'); + wrapInnerHTMLInPTag(el); const btnClass = classes?.find((c) => c.endsWith('-button')); if (btnClass) { const [theme, size] = btnClass.split('-').reverse(); el.classList.remove(btnClass); - decorateButtons(el, `${size}-${theme}`); + decorateButtons(el, `${theme}-${size}`); } else { decorateButtons(el, 'button-xl'); } - distillClasses(el, classes); -} - -function decorateLockupRow(el) { - const child = el.querySelector(':scope > div'); - if (child) child.classList.add('lockup-area'); + decorateBlockText(el, textDefault); + decorateTextOverrides(el, ['-heading', '-body', '-detail']); } function decorateSup(el, classes) { @@ -107,7 +139,7 @@ function loadContentType(el, key, classes) { decorateBg(el); break; case 'lockup': - decorateLockupRow(el); + decorateLockupRow(el, classes); break; case 'qrcode': decorateQr(el); @@ -126,8 +158,7 @@ function loadContentType(el, key, classes) { } function loadBreakpointThemes() { - const { miloLibs, codeRoot } = getConfig(); - loadStyle(`${miloLibs || codeRoot}/styles/breakpoint-theme.css`); + loadStyle(`${base}/styles/breakpoint-theme.css`); } export default async function init(el) { @@ -175,8 +206,8 @@ export default async function init(el) { : null; if (assetUnknown) assetUnknown.classList.add('asset-unknown'); - decorateBlockText(copy, ['xxl', 'm', 'l']); // heading, body, detail - decorateLockupFromContent(copy); + decorateBlockText(copy, textDefault); + await decorateLockupFromContent(copy); extendButtonsClass(copy); /* c8 ignore next 2 */ @@ -187,15 +218,16 @@ export default async function init(el) { const assetRow = allRows[0].classList.contains('asset'); if (assetRow) el.classList.add('asset-left'); - const mainCopy = createTag('div', { class: 'main-copy' }, copy.innerHTML); + const mainCopy = createTag('div', { class: 'main-copy' }); + while (copy.childNodes.length > 0) { + mainCopy.appendChild(copy.childNodes[0]); + } rows.splice(mainRowIndex, 1); if (mainRowIndex > 0) { for (let i = 0; i < mainRowIndex; i += 1) { rows[i].classList.add('prepend'); } } - - copy.innerHTML = ''; copy.append(mainCopy); [...rows].forEach((row) => { if (row.classList.contains('prepend')) { From 10cfc4b4750a252585f727e19b7b380e31ac5baa Mon Sep 17 00:00:00 2001 From: nishantka <126539566+nishantka@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:47:51 +0530 Subject: [PATCH 10/66] Dark nav for standalone Gnav (#2730) * gnav dark theme init * Dark gnav icon changes * Dark gnav changes * add theme in standalone gnav config * allow 2 logo for dark mode * UTs for gnav dark theme * UTs for gnav dark theme * Dark gnav color changes * gnav dark mode refactor css * gnav dark mode refactor css * load darknav after base to override variables * update darknav promo css * lightmode promobar link color * update region nav link color * update nav background color for darkmode * fix outline for windows * update darknav css --- libs/blocks/global-footer/global-footer.css | 34 +- libs/blocks/global-footer/global-footer.js | 2 + libs/blocks/global-navigation/base.css | 51 +-- libs/blocks/global-navigation/dark-nav.css | 256 ++++++++++++++ .../features/profile/dropdown.css | 6 +- .../features/search/gnav-search.css | 4 +- .../global-navigation/global-navigation.css | 52 +-- .../global-navigation/global-navigation.js | 15 +- .../global-navigation/utilities/menu/menu.css | 16 +- .../global-navigation/utilities/utilities.js | 19 +- libs/navigation/navigation.css | 11 +- libs/navigation/navigation.js | 5 +- libs/styles/styles.css | 2 +- .../global-footer/global-footer.test.js | 11 + test/blocks/global-footer/test-utilities.js | 4 +- .../global-navigation.test.js | 20 ++ .../mocks/adobe-dark-logo.svg | 9 + .../mocks/dark-global-navigation.plain.js | 326 ++++++++++++++++++ 18 files changed, 755 insertions(+), 88 deletions(-) create mode 100644 libs/blocks/global-navigation/dark-nav.css create mode 100644 test/blocks/global-navigation/mocks/adobe-dark-logo.svg create mode 100644 test/blocks/global-navigation/mocks/dark-global-navigation.plain.js diff --git a/libs/blocks/global-footer/global-footer.css b/libs/blocks/global-footer/global-footer.css index 1a240cf2c0..9d70ef3eb7 100644 --- a/libs/blocks/global-footer/global-footer.css +++ b/libs/blocks/global-footer/global-footer.css @@ -4,7 +4,7 @@ display: flex; justify-content: center; font-size: 12px; - background: var(--feds-background-footer--light); + background: var(--feds-background-footer); } .global-footer ul { @@ -26,7 +26,7 @@ display: flex; flex-direction: column; row-gap: 30px; - color: var(--feds-color-headline--light); + color: var(--feds-color-headline); } .feds-footer-wrapper ul { @@ -65,7 +65,7 @@ margin: 0 var(--feds-gutter-footer); border-width: 1px 0 1px 0; border-style: solid; - border-color: var(--feds-borderColor-featuredProducts--light); + border-color: var(--feds-borderColor-featuredProducts); } .feds-featuredProducts-label { @@ -99,15 +99,15 @@ /* Region picker */ .feds-regionPicker { column-gap: 10px; - color: var(--feds-color-link--light); - border-color: var(--feds-color-link--light); - fill: var(--feds-color-link--light); + color: var(--feds-color-link); + border-color: var(--feds-color-link); + fill: var(--feds-color-link); } .feds-regionPicker:hover { - color: var(--feds-color-link--hover--light); - border-color: var(--feds-color-link--hover--light); - fill: var(--feds-color-link--hover--light); + color: var(--feds-color-link--hover); + border-color: var(--feds-color-link--hover); + fill: var(--feds-color-link--hover); } .feds-regionPicker:after { @@ -145,8 +145,8 @@ min-width: 130px; max-height: 300px; overflow-y: auto; - background: var(--feds-background-nav--light); - border: 1px solid var(--feds-color-border--light); + background: var(--feds-background-nav); + border: 1px solid var(--feds-borderColor); border-radius: 4px; } @@ -186,11 +186,11 @@ padding: 0 20px; display: block; line-height: 2; - color: var(--feds-color-link--hover--light); + color: var(--feds-color-link--hover); } .feds-regionPicker-wrapper > .fragment a:hover { - background: var(--feds-background-link--hover--light); + background: var(--feds-background-link--hover); } /* Social */ @@ -234,13 +234,13 @@ display: flex; column-gap: 5px; align-items: center; - color: var(--feds-color-link--light); - fill: var(--feds-color-link--light); + color: var(--feds-color-link); + fill: var(--feds-color-link); } .feds-footer-privacyLink:hover { - color: var(--feds-color-link--hover--light); - fill: var(--feds-color-link--hover--light); + color: var(--feds-color-link--hover); + fill: var(--feds-color-link--hover); } .feds-adChoices-icon { diff --git a/libs/blocks/global-footer/global-footer.js b/libs/blocks/global-footer/global-footer.js index 29a6a4b3c2..2cd9f35559 100644 --- a/libs/blocks/global-footer/global-footer.js +++ b/libs/blocks/global-footer/global-footer.js @@ -20,6 +20,7 @@ import { logErrorFor, toFragment, federatePictureSources, + isDarkMode, } from '../global-navigation/utilities/utilities.js'; import { getFederatedUrl } from '../../utils/federated.js'; @@ -370,6 +371,7 @@ class Footer { export default function init(block) { try { const footer = new Footer({ block }); + if (isDarkMode()) block.classList.add('feds--dark'); return footer; } catch (e) { lanaLog({ message: 'Could not create footer', e }); diff --git a/libs/blocks/global-navigation/base.css b/libs/blocks/global-navigation/base.css index 1ebe34e97a..9bbffc30c4 100644 --- a/libs/blocks/global-navigation/base.css +++ b/libs/blocks/global-navigation/base.css @@ -7,26 +7,31 @@ --feds-height-breadcrumbs: 33px; --feds-gutter: 8px; /* Top navigation - backgrounds */ - --feds-background-nav--light: #fff; - --feds-background-popup--light: #fafafa; + --feds-background-nav: #fff; + --feds-background-popup: #fafafa; --feds-background-promo--dark: #000; /* Top navigation - borders */ - --feds-borderColor--light: #eaeaea; - --feds-borderColor-menu--light: #e1e1e1; + --feds-borderColor: #eaeaea; + --feds-borderColor-menu: #e1e1e1; + --feds-borderColor-navLink: #2c2c2c; /* Top navigation - colors */ --feds-color-adobeBrand: #EB1000; - --feds-color-headline--light: #505050; + --feds-color-headline: #505050; + --feds-color-hamburger: #2d2d2d; + --feds-color-breadcrumbs--current: #2c2c2c; + --feds-color-signIn: #4B4B4B; /* Top navigation - misc */ --feds-radius-utilityIcon: 4px; /* Links */ - --feds-background-link--hover--light: #f5f5f5; - --feds-borderColor-link--light: #f3f3f3; - --feds-color-link--light: #2c2c2c; - --feds-color-link--hover--light: #1473e6; - --feds-color-navLink-description--light: #656565; + --feds-background-link--hover: #f5f5f5; + --feds-borderColor-link: #f3f3f3; + --feds-color-link: #2c2c2c; + --feds-color-link--hover: #1473e6; + --feds-color-navLink-description: #656565; + --feds-color-link-breadcrumbs: #707070; /* Footer */ - --feds-background-footer--light: #fafafa; - --feds-borderColor-featuredProducts--light: #999; + --feds-background-footer: #fafafa; + --feds-borderColor-featuredProducts: #999; --feds-gutter-footer: 32px; } @@ -36,7 +41,7 @@ align-items: center; padding: 12px; border: none; - color: var(--feds-color-link--light); + color: var(--feds-color-link); font: inherit; white-space: nowrap; flex-shrink: 0; @@ -44,7 +49,7 @@ .feds-navLink:hover, .feds-navLink:focus { - color: var(--feds-color-link--hover--light); + color: var(--feds-color-link--hover); } .feds-navLink:not(.feds-navLink--hoverCaret) { @@ -52,7 +57,7 @@ } .feds-navLink--blue { - color: var(--feds-color-link--hover--light); + color: var(--feds-color-link--hover); } .feds-navLink--hoverCaret { @@ -62,7 +67,7 @@ .feds-navLink--hoverCaret:hover, .feds-navLink--hoverCaret:focus { - color: var(--feds-color-link--light); + color: var(--feds-color-link); } .feds-navLink--hoverCaret:after, @@ -70,7 +75,7 @@ display: flex; border-width: 0 1px 1px 0; border-style: solid; - border-color: var(--feds-color-link--light); + border-color: var(--feds-color-link); transform-origin: 75% 75%; transition: transform 0.1s ease; box-sizing: content-box; @@ -134,7 +139,7 @@ header.global-navigation { .feds-navItem--active > .feds-navLink:only-child:hover, .feds-navItem--active > .feds-navLink:only-child:focus { - color: var(--feds-color-link--light); + color: var(--feds-color-link); } .feds-navItem--active > .feds-navLink:before { @@ -142,7 +147,7 @@ header.global-navigation { bottom: 0; left: 0; right: 0; - border-bottom: 2px solid #2c2c2c; + border-bottom: 2px solid var(--feds-borderColor-navLink); content: ""; } @@ -172,7 +177,7 @@ header.global-navigation { .feds-navLink--hoverCaret:hover, .feds-navLink--hoverCaret:focus { - background-color: var(--feds-background-popup--light); + background-color: var(--feds-background-popup); } .feds-navLink--hoverCaret:after { @@ -214,12 +219,12 @@ header.global-navigation { .feds-navLink-description { display: flex; font-size: 12px; - color: var(--feds-color-navLink-description--light); + color: var(--feds-color-navLink-description); } .feds-navLink:hover .feds-navLink-description, .feds-navLink:focus .feds-navLink-description { - color: var(--feds-color-navLink-description--light); + color: var(--feds-color-navLink-description); } /* Nav Link special styles for A/B test */ @@ -260,7 +265,7 @@ header.global-navigation { .feds-navLink[class *= '-gradient']:hover .feds-navLink-title:after, .feds-navLink[class *= '-gradient']:focus .feds-navLink-title:after { - border-color: var(--feds-color-link--hover--light); + border-color: var(--feds-color-link--hover); } .feds-navLink--photo-gradient { diff --git a/libs/blocks/global-navigation/dark-nav.css b/libs/blocks/global-navigation/dark-nav.css new file mode 100644 index 0000000000..9f9caa2819 --- /dev/null +++ b/libs/blocks/global-navigation/dark-nav.css @@ -0,0 +1,256 @@ +:root { + --text-color: #F2F2F2; + --background-color: #111; + --link-color: #1473e6; + --link-hover-color: #1473e6; + /* Top navigation - backgrounds */ + --feds-background-popup: #111; + --feds-background-nav: #222; + --feds-background-nav--desktop: #1B1B1B; + /* Top navigation - borders */ + --feds-borderColor: #303030; + --feds-borderColor-menu: #4B4B4B; + --feds-borderColor-navLink: #DBDBDB; + /* Top navigation - colors */ + --feds-color-adobeBrand: #FFF; + --feds-color-headline--mobile: #F2F2F2; + --feds-color-headline: #EBEBEB; + --feds-color-popup: #EBEBEB; + --feds-color-hamburger: #FFF; + --feds-color-breadcrumbs--current: #DBDBDB; + --feds-color-signIn: #DBDBDB; + --feds-color-search: #DBDBDB; + /* Links */ + --feds-color-link: #F2F2F2; + /* --feds-color-link--mobile: #F2F2F2; */ + --feds-color-link--desktop: #DBDBDB; + --feds-color-link--hover: #1473e6; + --feds-color-blue-link: #5eaaf7; + --feds-color-navLink-description: #B0B0B0; + --feds-color-link-breadcrumbs: #B0B0B0; + --feds-background-link--hover: #1B1B1B; + --feds-borderColor-link: #323232; + /* Footer */ + --feds-background-footer: #222; + --feds-background-footer--desktop: #1B1B1B; + --feds-borderColor-featuredProducts: #999; + /* Dropdown */ + --feds-color-profile-heading--dark: #909090; + --feds-color-profile--dark: #DBDBDB; + --feds-color-profile--emphasis--dark: #F2F2F2; + --feds-border-profile--dark: 1px solid var(--feds-borderColor); +} + +.feds--dark .feds-navLink--hoverCaret:hover, +.feds--dark .feds-navLink--hoverCaret:focus { + background-color: #151515; +} + +/* Desktop styles */ +@media (min-width: 900px) { + .feds--dark .feds-navLink { + color: var(--feds-color-link--desktop); + } + + .feds--dark .feds-navLink:hover { + color: var(--feds-color-link--hover); + } + + .feds--dark .feds-navLink--hoverCaret:hover, + .feds--dark .feds-navLink--hoverCaret:focus { + color: var(--feds-color-link--desktop); + background-color: var(--background-color); + } +} + +/* global-navigation.css */ +.feds-cta--primary { + background-color: #066ce7; + border-color: #066ce7; + color: #fff; +} + +.feds--dark .feds-cta--secondary { + background-color: var(--background-color); + border-color: #393939; + color: var(--feds-color-link--dark); +} + +.feds--dark .feds-cta--secondary:hover, +.feds--dark .feds-cta--secondary:focus { + border-color: #444; + background-color: #2C2C2C; + color: var(--feds-color-link--dark); +} + +/* Popup */ +.feds--dark .feds-popup { + background-color: #151515; +} + +.feds--dark .feds-popup .feds-navLink { + color: var(--feds-color-popup); +} + +.feds--dark .feds-popup .feds-navLink:hover, +.feds--dark .feds-popup .feds-navLink:focus { + color: var(--feds-color-link--hover); +} + +.feds--dark .feds-popup .feds-navLink--blue { + color: var(--feds-color-blue-link); +} + +.feds--dark .feds-popup .feds-navLink--blue:hover, +.feds--dark .feds-popup .feds-navLink--blue:focus { + color: var(--feds-color-link--hover); +} + +.feds--dark .feds-brand-label, +.feds--dark .feds-brand-image, +.feds--dark .feds-logo, +.feds--dark .feds-logo-label { + color: var(--feds-color-adobeBrand); +} + +/* Desktop styles */ +@media (min-width: 900px) { + header.global-navigation.feds--dark { + background-color: var(--feds-background-nav--desktop); + } + + .feds--dark .feds-topnav-wrapper { + background-color: var(--feds-background-nav--desktop); + } + + /* Popup */ + .feds--dark .feds-popup { + background-color: var(--feds-background-popup); + box-shadow: 0 3px 3px 0 rgb(255 255 255 / 20%); + } +} + +/* gnav-search.css */ +.feds--dark .feds-search-input { + border: 1px solid var(--feds-borderColor); + background-color: transparent; + color: var(--feds-color-search); +} + +.feds--dark .feds-search-icons svg path { + fill: var(--feds-color-search); +} + +.feds--dark .feds-search-clear { + border: none; + color: var(--feds-color-search); +} + +.feds--dark .feds-search-clear:hover { + color: var(--feds-color-link--hover); +} + +.feds--dark .feds-search-results:not(:empty) { + border-color: var(--feds-borderColor); +} + +.feds--dark .feds-search-result { + color: var(--feds-color-link); +} + +.feds--dark .feds-search-result:hover, +.feds--dark .feds-search-result:focus { + background: var(--feds-background-nav); +} + +@media (min-width: 900px) { + .feds--dark .feds-promo { + border: 1px solid var(--feds-borderColor); + background: #1D1D1D; + } + + .feds--dark .feds-promo--dark, + .feds--dark .feds-promo--dark a:not(.feds-cta) { + color: var(--feds-color-headline); + } + + .feds--dark .feds-promo-link { + color: var(--feds-color-blue-link); + } + + .feds--dark .feds-promo-link:hover { + color: var(--feds-color-link--hover); + } +} + +@media (min-width: 1200px) { + .feds--dark .feds-crossCloudMenu-wrapper { + border-top: solid 1px var(--feds-borderColor); + background-color: var(--feds-background-popup); + } +} + +/* dropdown.css */ +.feds--dark .feds-profile-name { + color: var(--feds-color-profile--emphasis--dark); +} + +.feds--dark .feds-profile-email { + color: var(--feds-color-profile-heading--dark); +} + +.feds--dark .feds-profile-account { + color: var(--feds-color-profile--dark); +} + +.feds--dark .feds-profile-menu { + border: var(--feds-border-profile--dark); + background: var(--feds-background-nav); +} + +.feds--dark .feds-local-menu { + border-top: var(--feds-border-profile--dark); +} + +.feds--dark .feds-local-menu h5 { + color: var(--feds-color-profile-heading--dark); +} + +.feds--dark .feds-profile-action { + border-top: var(--feds-border-profile--dark); +} + +.feds--dark .feds-regionPicker-wrapper > .fragment { + background: var(--feds-background-popup); +} + +.feds--dark .feds-social-icon { + color: #AAA; +} + +.feds-social-link:hover .feds-social-icon { + color: #CCC; +} + +.dialog-modal .region-nav { + color: var(--feds-color-link); + background-color: var(--feds-background-popup); +} + +.dialog-modal .region-nav a { + color: var(--feds-color-blue-link); +} + +.dialog-modal .region-nav a:hover { + text-decoration: underline; +} + +header.global-navigation.feds--dark { + visibility: visible; +} + +@media (min-width: 900px) { + .global-footer.feds--dark { + background: var(--feds-background-footer--desktop); + } +} diff --git a/libs/blocks/global-navigation/features/profile/dropdown.css b/libs/blocks/global-navigation/features/profile/dropdown.css index 3475affa70..dabc767618 100644 --- a/libs/blocks/global-navigation/features/profile/dropdown.css +++ b/libs/blocks/global-navigation/features/profile/dropdown.css @@ -97,13 +97,13 @@ .feds-local-menu a, .feds-profile-actions a { display: block; - color: var(--feds-color-link--light); + color: var(--feds-color-link); } .feds-local-menu a:hover, .feds-profile-actions a:hover { - color: var(--feds-color-link--hover--light); - background-color: var(--feds-background-link--hover--light); + color: var(--feds-color-link--hover); + background-color: var(--feds-background-link--hover); } .feds-local-menu a { diff --git a/libs/blocks/global-navigation/features/search/gnav-search.css b/libs/blocks/global-navigation/features/search/gnav-search.css index 6d4d4f7f0e..db93014727 100644 --- a/libs/blocks/global-navigation/features/search/gnav-search.css +++ b/libs/blocks/global-navigation/features/search/gnav-search.css @@ -3,8 +3,8 @@ } .feds-search-dropdown { - border-bottom: 1px solid var(--feds-borderColor--light); - background-color: var(--feds-background-nav--light); + border-bottom: 1px solid var(--feds-borderColor); + background-color: var(--feds-background-nav); } .feds-search-bar { diff --git a/libs/blocks/global-navigation/global-navigation.css b/libs/blocks/global-navigation/global-navigation.css index 86eb13d5c7..60f84165b3 100644 --- a/libs/blocks/global-navigation/global-navigation.css +++ b/libs/blocks/global-navigation/global-navigation.css @@ -32,7 +32,7 @@ header.global-navigation { position: sticky; top: 0; z-index: 10; - background-color: var(--feds-background-nav--light); + background-color: var(--feds-background-nav); box-sizing: content-box; overflow-x: clip; } @@ -43,7 +43,7 @@ header.global-navigation { display: flex; justify-content: center; height: var(--feds-height-nav); - background-color: var(--feds-background-nav--light); + background-color: var(--feds-background-nav); } .feds-topnav { @@ -62,8 +62,8 @@ header.global-navigation { display: none; flex-direction: column; height: 100vh; - border-top: 1px solid var(--feds-borderColor--light); - background-color: var(--feds-background-nav--light); + border-top: 1px solid var(--feds-borderColor); + background-color: var(--feds-background-nav); } [dir = "rtl"] .feds-nav-wrapper { @@ -94,7 +94,7 @@ header.global-navigation { border: none; background: transparent; box-shadow: none; - color: #2d2d2d; + color: var(--feds-color-hamburger); cursor: pointer; font-size: 20px; font-weight: 300; @@ -153,7 +153,7 @@ header.global-navigation { /* Popup */ .feds-popup { display: none; - background-color: var(--feds-background-popup--light); + background-color: var(--feds-background-popup); } .feds-popup p { @@ -174,7 +174,7 @@ header.global-navigation { font-size: 14px; font-weight: 400; line-height: 1.4; - color: var(--feds-color-link--light); + color: var(--feds-color-link); white-space: nowrap; } @@ -187,7 +187,7 @@ header.global-navigation { } .feds-navItem:not(:last-child) > .feds-navLink { - border-bottom: 1px solid var(--feds-borderColor-link--light); + border-bottom: 1px solid var(--feds-borderColor-link); } /* Item with active dropdown */ @@ -201,7 +201,7 @@ header.global-navigation { bottom: 0; left: 0; width: 2px; - background: var(--feds-color-link--light); + background: var(--feds-color-link); content: ""; z-index: 1; } @@ -213,7 +213,7 @@ header.global-navigation { .feds-popup .feds-navLink:hover, .feds-popup .feds-navLink:focus { - background-color: var(--feds-background-link--hover--light); + background-color: var(--feds-background-link--hover); } .feds-cta-wrapper { @@ -287,11 +287,11 @@ header.global-navigation { display: none; font-size: 20px; line-height: 1; - color: var(--feds-color-link--light); + color: var(--feds-color-link); } .feds-search-close:hover { - color: var(--feds-color-link--hover--light); + color: var(--feds-color-link--hover); } .feds-search-close:before { @@ -302,7 +302,7 @@ header.global-navigation { .feds-breadcrumbs-wrapper { display: flex; order: -1; - border-bottom: 1px solid var(--feds-borderColor--light); + border-bottom: 1px solid var(--feds-borderColor); } .feds-breadcrumbs { @@ -355,11 +355,11 @@ header.global-navigation { .feds-breadcrumbs a { display: block; - color: #707070; + color: var(--feds-color-link-breadcrumbs); } .feds-breadcrumbs [aria-current] { - color: #2c2c2c; + color: var(--feds-color-breadcrumbs--current); } .feds-breadcrumbs li:not(:first-of-type)::before { @@ -408,7 +408,7 @@ header.global-navigation { .feds-signIn { padding: 11px var(--feds-gutter); border-radius: var(--feds-radius-utilityIcon); - color: #4B4B4B; + color: var(--feds-color-signIn); white-space: nowrap; border: none; font: inherit; @@ -425,7 +425,7 @@ header.global-navigation { display: none; right: 0; top: 100%; - background-color: var(--feds-background-popup--light); + background-color: var(--feds-background-popup); overflow: hidden; box-shadow: 0 3px 3px 0 rgb(0 0 0 / 20%); line-height: 1.4; @@ -451,14 +451,14 @@ header.global-navigation { .feds-signIn-dropdown li > a, .feds-signIn-dropdown .feds-signIn { display: block; - color: var(--feds-color-link--light); + color: var(--feds-color-link); padding: 6px 32px; } .feds-signIn-dropdown li > a:hover, .feds-signIn-dropdown .feds-signIn:hover { - color: var(--feds-color-link--hover--light); - background-color: var(--feds-background-link--hover--light); + color: var(--feds-color-link--hover); + background-color: var(--feds-background-link--hover); } #feds-googleLogin { @@ -482,7 +482,7 @@ header.global-navigation { } .feds-topnav-wrapper { - border-bottom: 1px solid var(--feds-borderColor--light); + border-bottom: 1px solid var(--feds-borderColor); box-sizing: content-box; } @@ -588,8 +588,8 @@ header.global-navigation { } .feds-navItem--section:only-of-type { - border-left: 1px solid var(--feds-borderColor--light); - border-right: 1px solid var(--feds-borderColor--light); + border-left: 1px solid var(--feds-borderColor); + border-right: 1px solid var(--feds-borderColor); } .feds-navItem--section > .feds-navLink { @@ -642,11 +642,11 @@ header.global-navigation { } .feds-search-trigger svg path { - fill: var(--feds-color-link--light); + fill: var(--feds-color-link); } .feds-search-trigger:hover svg path { - fill: var(--feds-color-link--hover--light); + fill: var(--feds-color-link--hover); } /* Breadcrumbs */ @@ -658,7 +658,7 @@ header.global-navigation { justify-content: center; border-bottom: unset; box-shadow: 0 3px 2px rgb(142 142 142 / 30%); - background: var(--feds-background-nav--light); + background: var(--feds-background-nav); transform: translate3d(0,0,0); /* Fix Safari issues w/ position: sticky */ } diff --git a/libs/blocks/global-navigation/global-navigation.js b/libs/blocks/global-navigation/global-navigation.js index a83ba5d6bc..1440b0b94c 100644 --- a/libs/blocks/global-navigation/global-navigation.js +++ b/libs/blocks/global-navigation/global-navigation.js @@ -34,12 +34,14 @@ import { trigger, yieldToMain, addMepHighlightAndTargetId, + isDarkMode, + darkIcons, } from './utilities/utilities.js'; import { replaceKey, replaceKeyArray } from '../../features/placeholders.js'; export const CONFIG = { - icons, + icons: isDarkMode() ? darkIcons : icons, delays: { mainNavDropdowns: 800, loadDelayed: 3000, @@ -605,7 +607,7 @@ class Gnav { env: environment, locale, imsClientId: window.adobeid?.client_id, - theme: 'light', + theme: isDarkMode() ? 'dark' : 'light', onReady: () => { this.decorateAppPrompt({ getAnchorState: () => window.UniversalNav.getComponent?.('app-switcher') }); }, @@ -746,6 +748,14 @@ class Gnav { // Create image element const getImageEl = () => { + if (isDarkMode()) { + const allSvgImgs = rawBlock.querySelectorAll('picture img[src$=".svg"]'); + if (allSvgImgs.length === 2) return allSvgImgs[1]; + + const images = blockLinks.filter((blockLink) => imgRegex.test(blockLink.href) + || imgRegex.test(blockLink.textContent)); + if (images.length === 2) return getBrandImage(images[1]); + } const svgImg = rawBlock.querySelector('picture img[src$=".svg"]'); if (svgImg) return svgImg; @@ -1027,6 +1037,7 @@ export default async function init(block) { block.setAttribute('daa-im', 'true'); const mepMartech = mep?.martech || ''; block.setAttribute('daa-lh', `gnav|${getExperienceName()}${mepMartech}`); + if (isDarkMode()) block.classList.add('feds--dark'); return gnav; } catch (e) { lanaLog({ message: 'Could not create global navigation.', e, tags: 'errorType=error,module=gnav' }); diff --git a/libs/blocks/global-navigation/utilities/menu/menu.css b/libs/blocks/global-navigation/utilities/menu/menu.css index c99630ddb8..f9add4aab1 100644 --- a/libs/blocks/global-navigation/utilities/menu/menu.css +++ b/libs/blocks/global-navigation/utilities/menu/menu.css @@ -35,14 +35,14 @@ .feds-menu-column--group .feds-menu-column:not(:last-child) { padding-bottom: 12px; margin-bottom: 12px; - border-bottom: 1px solid var(--feds-borderColor-menu--light); + border-bottom: 1px solid var(--feds-borderColor-menu); } .feds-menu-headline { position: relative; padding: 12px 44px 12px 32px; - border-bottom: 1px solid var(--feds-borderColor-menu--light); - color: var(--feds-color-headline--light); + border-bottom: 1px solid var(--feds-borderColor-menu); + color: var(--feds-color-headline); font-weight: 600; white-space: nowrap; cursor: pointer; @@ -63,7 +63,7 @@ margin-top: -3px; border-width: 0 1px 1px 0; border-style: solid; - border-color: var(--feds-color-link--light); + border-color: var(--feds-color-link); transform-origin: 75% 75%; transform: rotateZ(45deg); transition: transform 0.1s ease; @@ -77,7 +77,7 @@ } .feds-menu-items { - border-bottom: solid 1px var(--feds-borderColor-menu--light); + border-bottom: solid 1px var(--feds-borderColor-menu); } .feds-promo { @@ -164,8 +164,8 @@ display: flex; flex-direction: column; width: 100%; - border: 1px solid var(--feds-borderColor--light); - background: var(--feds-background-nav--light); + border: 1px solid var(--feds-borderColor); + background: var(--feds-background-nav); white-space: normal; box-sizing: content-box; } @@ -209,7 +209,7 @@ .feds-promo--dark, .feds-promo--dark a:not(.feds-cta) { - color: var(--feds-background-nav--light); + color: var(--feds-background-nav); } .feds-promo--dark a, diff --git a/libs/blocks/global-navigation/utilities/utilities.js b/libs/blocks/global-navigation/utilities/utilities.js index a4c73212f7..3c1e9b7ee6 100644 --- a/libs/blocks/global-navigation/utilities/utilities.js +++ b/libs/blocks/global-navigation/utilities/utilities.js @@ -30,6 +30,11 @@ export const icons = { home: '', }; +export const darkIcons = { + ...icons, + company: '', +}; + export const lanaLog = ({ message, e = '', tags = 'errorType=default' }) => { const url = getMetadata('gnav-source'); window.lana.log(`${message} | gnav-source: ${url} | href: ${window.location.href} | ${e.reason || e.error || e.message || e}`, { @@ -156,12 +161,22 @@ export function loadStyles(url) { }); } +export function isDarkMode() { + const { theme } = getConfig(); + return theme === 'dark'; +} + // Base styles are shared between top navigation and footer, // since they can be independent of each other. // CSS imports were not used due to duplication of file include export async function loadBaseStyles() { - const url = rootPath('base.css'); - await loadStyles(url); + if (isDarkMode()) { + new Promise((resolve) => { loadStyle(rootPath('base.css'), resolve); }) + .then(() => loadStyles(rootPath('dark-nav.css'))); + } else { + const url = rootPath('base.css'); + await loadStyles(url); + } } export function loadBlock(path) { diff --git a/libs/navigation/navigation.css b/libs/navigation/navigation.css index 96ac72d5de..e7fbbdc1bc 100644 --- a/libs/navigation/navigation.css +++ b/libs/navigation/navigation.css @@ -24,7 +24,16 @@ font-size: 18px; } -header.global-navigation { +header.global-navigation, header.global-navigation.feds--dark { height: 64px; visibility: hidden; } + +@media (min-width: 900px) { + .feds-promo-link { + color: #035FE6; + } + .feds-promo-link:hover { + color: #136FF6; + } +} diff --git a/libs/navigation/navigation.js b/libs/navigation/navigation.js index c74bcaed2e..e31dd77195 100644 --- a/libs/navigation/navigation.js +++ b/libs/navigation/navigation.js @@ -34,7 +34,9 @@ function getParamsConfigs(configs) { } export default async function loadBlock(configs, customLib) { - const { header, footer, authoringPath, env = 'prod', locale = '' } = configs || {}; + const { + header, footer, authoringPath, env = 'prod', locale = '', theme, + } = configs || {}; const branch = new URLSearchParams(window.location.search).get('navbranch'); const miloLibs = branch ? `https://${branch}--milo--adobecom.hlx.page` : customLib || envMap[env]; if (!header && !footer) { @@ -55,6 +57,7 @@ export default async function loadBlock(configs, customLib) { pathname: `/${locale}`, locales: configs.locales || locales, contentRoot: authoringPath || footer.authoringPath, + theme, ...paramConfigs, }; setConfig(clientConfig); diff --git a/libs/styles/styles.css b/libs/styles/styles.css index 7413807321..53eca9bd3c 100644 --- a/libs/styles/styles.css +++ b/libs/styles/styles.css @@ -703,7 +703,7 @@ header:not(.global-navigation) ~ main { the 'global-navigation' class should also be removed from the 'header' selector all of the ':not(.global-navigation)' rules can be entirely removed. */ -header.global-navigation { +header.global-navigation, header.global-navigation.feds--dark { height: var(--global-height-nav); visibility: hidden; } diff --git a/test/blocks/global-footer/global-footer.test.js b/test/blocks/global-footer/global-footer.test.js index 0010dfdc56..d99e9868c5 100644 --- a/test/blocks/global-footer/global-footer.test.js +++ b/test/blocks/global-footer/global-footer.test.js @@ -422,4 +422,15 @@ describe('global footer', () => { expect(window.lana.log.getCalls().find((c) => c.args[0].includes('Issue with loadIcons'))); }); }); + + describe('dark mode footer', async () => { + it('should not contain dark theme class if dark theme is not configured', async () => { + await createFullGlobalFooter({ waitForDecoration: true }); + expect(document.querySelector('footer').classList.contains('feds--dark')).to.be.false; + }); + it('should contain dark theme class if dark theme is configured', async () => { + await createFullGlobalFooter({ waitForDecoration: true, customConfig: { theme: 'dark' } }); + expect(document.querySelector('footer').classList.contains('feds--dark')).to.be.true; + }); + }); }); diff --git a/test/blocks/global-footer/test-utilities.js b/test/blocks/global-footer/test-utilities.js index 6bb9a9e1ba..24e65614f6 100644 --- a/test/blocks/global-footer/test-utilities.js +++ b/test/blocks/global-footer/test-utilities.js @@ -74,9 +74,9 @@ export const waitForFooterToDecorate = (targetedSelectors = allSelectors) => Pro .map((key) => waitForElement(allSelectors[key])), ); -export const createFullGlobalFooter = async ({ waitForDecoration, viewport = 'desktop' }) => { +export const createFullGlobalFooter = async ({ waitForDecoration, viewport = 'desktop', customConfig = {} }) => { await setViewport(viewports[viewport]); - setConfig(config); + setConfig({ ...config, ...customConfig }); // we need to import the footer class in here so it can use the config we have set above // if we import it at the top of the file, an empty config will be defined and used by the footer diff --git a/test/blocks/global-navigation/global-navigation.test.js b/test/blocks/global-navigation/global-navigation.test.js index 61f6c91a23..4af0799b83 100644 --- a/test/blocks/global-navigation/global-navigation.test.js +++ b/test/blocks/global-navigation/global-navigation.test.js @@ -16,6 +16,7 @@ import initGnav, { getUniversalNavLocale, osMap } from '../../../libs/blocks/glo import { isDesktop, isTangentToViewport, toFragment } from '../../../libs/blocks/global-navigation/utilities/utilities.js'; import logoOnlyNav from './mocks/global-navigation-only-logo.plain.js'; import longNav from './mocks/global-navigation-long.plain.js'; +import darkNav from './mocks/dark-global-navigation.plain.js'; import globalNavigationMock from './mocks/global-navigation.plain.js'; import { getConfig } from '../../../tools/send-to-caas/send-utils.js'; @@ -600,4 +601,23 @@ describe('global navigation', () => { expect(!!weAppPrompt).to.be.true; }); }); + + describe('GNav Dark theme', () => { + it('should not contain dark theme class if dark theme is not configured', async () => { + await createFullGlobalNavigation(); + expect(document.querySelector(selectors.globalNav).classList.contains('feds--dark')).to.be.false; + }); + it('should contain dark theme class if dark theme is configured', async () => { + await createFullGlobalNavigation({ customConfig: { theme: 'dark' } }); + expect(document.querySelector(selectors.globalNav).classList.contains('feds--dark')).to.be.true; + }); + it('should use first image if not dark theme', async () => { + await createFullGlobalNavigation({ globalNavigation: darkNav }); + expect(document.querySelector(`${selectors.brandImage} img`).getAttribute('src')).to.equal('http://localhost:2000/test/blocks/global-navigation/mocks/adobe-logo.svg'); + }); + it('should use second image for dark theme', async () => { + await createFullGlobalNavigation({ globalNavigation: darkNav, customConfig: { theme: 'dark' } }); + expect(document.querySelector(`${selectors.brandImage} img`).getAttribute('src')).to.equal('http://localhost:2000/test/blocks/global-navigation/mocks/adobe-dark-logo.svg'); + }); + }); }); diff --git a/test/blocks/global-navigation/mocks/adobe-dark-logo.svg b/test/blocks/global-navigation/mocks/adobe-dark-logo.svg new file mode 100644 index 0000000000..f799476f23 --- /dev/null +++ b/test/blocks/global-navigation/mocks/adobe-dark-logo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/test/blocks/global-navigation/mocks/dark-global-navigation.plain.js b/test/blocks/global-navigation/mocks/dark-global-navigation.plain.js new file mode 100644 index 0000000000..a2124ee177 --- /dev/null +++ b/test/blocks/global-navigation/mocks/dark-global-navigation.plain.js @@ -0,0 +1,326 @@ +// Uses the franklin structure without any customizations +export default `

+ +
+
+
+
+
+

+ Cloud Menu +

+
+
+
+
+
+
+
+
+

+ FEDS Menu +

+
+
+
+
+
+

w/ Promo

+ +
+
+
+

Business Resilience: Leading Through Change

+

+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas + porttitor congue massa. +

+

Check it out

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

2 Col

+
Column 1 heading
+ + +
Column 2 heading
+ +

+ Events +

+
+ +
+

+ Primary +

+
+
+

+ Secondary +

+
+
+

Random text

+
+ +
+ +
+
+
+
Local Menu Title
+

+ Creativity +

+

+ Digital Transformation +

+

+ Trends & Research +

+
+
+ +
+
+ +
+ +
+ `; From ad50e4b21bb0b54e7a1892ee1ba7619647317600 Mon Sep 17 00:00:00 2001 From: Vivian A Goodrich <101133187+vgoodric@users.noreply.github.com> Date: Mon, 2 Sep 2024 02:17:57 -0600 Subject: [PATCH 11/66] MWPW-157034 [MEP] move call for preview from loadPostLCP to loadDeferred (#2770) * move call for preview from loadPostLCP to loadDeferred * change function name to getSelectorType * revert --- libs/utils/utils.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/utils/utils.js b/libs/utils/utils.js index 52d3b0e190..6931da2a23 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -1013,10 +1013,6 @@ async function loadPostLCP(config) { import('../features/personalization/personalization.js') .then(({ addMepAnalytics }) => addMepAnalytics(config, header)); } - if (config.mep?.preview) { - import('../features/personalization/preview.js') - .then(({ default: decoratePreviewMode }) => decoratePreviewMode()); - } } export function scrollToHashedElement(hash) { @@ -1072,6 +1068,10 @@ export async function loadDeferred(area, blocks, config) { sampleRate: parseInt(getMetadata('pageperf-rate'), 10), })); } + if (config.mep?.preview) { + import('../features/personalization/preview.js') + .then(({ default: decoratePreviewMode }) => decoratePreviewMode()); + } } function initSidekick() { From 77268c0e81be855f03c13fcc3ffcce6354cfbdef Mon Sep 17 00:00:00 2001 From: Drashti Modasara Date: Mon, 2 Sep 2024 13:48:04 +0530 Subject: [PATCH 12/66] [MWPW-152968] mWeb - Passing ECID to Branch.io banner - Implementation (#2772) * branch banner ecid * review changes * passing ecid value --------- Co-authored-by: Drashti Modasara --- .../mobile-app-banner/mobile-app-banner.js | 21 +++++++++++- .../mobile-app-banner.test.js | 34 ++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/libs/blocks/mobile-app-banner/mobile-app-banner.js b/libs/blocks/mobile-app-banner/mobile-app-banner.js index 0e2e546b07..9c5988ee44 100644 --- a/libs/blocks/mobile-app-banner/mobile-app-banner.js +++ b/libs/blocks/mobile-app-banner/mobile-app-banner.js @@ -13,9 +13,20 @@ async function getKey(product) { return keyMatch[0]?.key; } +async function getECID() { + let ecid = null; + 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' })); + } + return ecid; +} + /* eslint-disable */ -function branchInit(key) { +async function branchInit(key) { let initValue = false; + const ecid = await getECID(); function initBranch() { if (initValue) { return; @@ -48,8 +59,16 @@ function branchInit(key) { 0 ); const privacyConsent = window.adobePrivacy?.hasUserProvidedConsent(); + const isAndroid = navigator.userAgent.includes('Android'); + + const cookieGrp = window.adobePrivacy?.activeCookieGroups(); + const performanceCookieConsent = cookieGrp.includes('C0002'); + const advertisingCookieConsent = cookieGrp.includes('C0004'); + + if (performanceCookieConsent && advertisingCookieConsent && isAndroid) branch.setBranchViewData({ data: { ecid }}); branch.init(key, { tracking_disabled: !privacyConsent }); } + ['adobePrivacy:PrivacyConsent', 'adobePrivacy:PrivacyReject', 'adobePrivacy:PrivacyCustom'] .forEach((event) => { window.addEventListener(event, initBranch); diff --git a/test/blocks/mobile-app-banner/mobile-app-banner.test.js b/test/blocks/mobile-app-banner/mobile-app-banner.test.js index b8656f95f1..81b65f8e19 100644 --- a/test/blocks/mobile-app-banner/mobile-app-banner.test.js +++ b/test/blocks/mobile-app-banner/mobile-app-banner.test.js @@ -51,7 +51,11 @@ describe('mobile-app-banner', () => { }); it('should init by adding branchio script', async () => { - window.adobePrivacy = { hasUserProvidedConsent: () => true }; + window.adobePrivacy = { + hasUserProvidedConsent: () => true, + activeCookieGroups: () => ['C0002', 'C0004'], + }; + const userAgentStub = sinon.stub(navigator, 'userAgent').get(() => 'Android'); const module = await import('../../../libs/blocks/mobile-app-banner/mobile-app-banner.js'); const banner = document.body.querySelector('.mobile-app-banner.product-test'); await module.default(banner); @@ -63,5 +67,33 @@ describe('mobile-app-banner', () => { if (scriptTag.getAttribute('src') !== null) scriptSrcs.push(scriptTag.getAttribute('src')); }); expect(scriptSrcs).to.include('https://cdn.branch.io/branch-latest.min.js'); + userAgentStub.restore(); + }); + + it('should fetch ecid from alloy and return if event is dispatched twice', async () => { + window.adobePrivacy = { + hasUserProvidedConsent: () => true, + activeCookieGroups: () => ['C0002', 'C0004'], + }; + window.alloy = () => {}; + const alloyStub = sinon.stub(window, 'alloy').callsFake((command) => { + if (command === 'getIdentity') { + return Promise.resolve({ identity: { ECID: 'test-ecid' } }); + } + return 'test-ecid'; + }); + const module = await import('../../../libs/blocks/mobile-app-banner/mobile-app-banner.js'); + const banner = document.body.querySelector('.mobile-app-banner.product-test'); + await module.default(banner); + window.dispatchEvent(new CustomEvent('adobePrivacy:PrivacyConsent')); + await delay(0); + const scriptTags = document.querySelectorAll('head > script'); + const scriptSrcs = []; + scriptTags.forEach((scriptTag) => { + if (scriptTag.getAttribute('src') !== null) scriptSrcs.push(scriptTag.getAttribute('src')); + }); + window.dispatchEvent(new CustomEvent('adobePrivacy:PrivacyConsent')); + expect(scriptSrcs).to.include('https://cdn.branch.io/branch-latest.min.js'); + alloyStub.restore(); }); }); From 4e85f88a6bda5a37482e8c2b6bf5bc02c338e22f Mon Sep 17 00:00:00 2001 From: Elan Bartholomew <79870969+elan-tbx@users.noreply.github.com> Date: Mon, 2 Sep 2024 02:18:11 -0600 Subject: [PATCH 13/66] MWPW-153372 - Add ability to further customize buttons (#2776) * update button decorator to handle custom classes * add dash character to regex * add coverage * add safeguard for href-less buttons * account for modals * Revert "MWPW-151936 - Aside Tiger Team Enhancements (redux)" (#2777) Revert "MWPW-151936 - Aside Tiger Team Enhancements (redux) (#2767)" This reverts commit 88cb1010512d247531a75bfef6bdc174a2b957b7. * add modal coverage * Revert "Revert "MWPW-151936 - Aside Tiger Team Enhancements (redux)" (#2777)" This reverts commit b1a39486ec5141de57018960a7c8515a93e78340. --- libs/utils/decorate.js | 18 ++++++++++++++---- test/blocks/text/mocks/body.html | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 25a0e8203c..06b84a971b 100644 --- a/libs/utils/decorate.js +++ b/libs/utils/decorate.js @@ -5,17 +5,27 @@ export function decorateButtons(el, size) { if (buttons.length === 0) return; const buttonTypeMap = { STRONG: 'blue', EM: 'outline', A: 'blue' }; buttons.forEach((button) => { + let target = button; const parent = button.parentElement; const buttonType = buttonTypeMap[parent.nodeName] || 'outline'; if (button.nodeName === 'STRONG') { - parent.classList.add('con-button', buttonType); - if (size) parent.classList.add(size); /* button-l, button-xl */ + target = parent; } else { - button.classList.add('con-button', buttonType); - if (size) button.classList.add(size); /* button-l, button-xl */ parent.insertAdjacentElement('afterend', button); parent.remove(); } + target.classList.add('con-button', buttonType); + if (size) target.classList.add(size); /* button-l, button-xl */ + const customClasses = target.href && [...target.href.matchAll(/#_button-([a-zA-Z-]+)/g)]; + if (customClasses) { + customClasses.forEach((match) => { + target.href = target.href.replace(match[0], ''); + if (target.dataset.modalHash) { + target.setAttribute('data-modal-hash', target.dataset.modalHash.replace(match[0], '')); + } + target.classList.add(match[1]); + }); + } const actionArea = button.closest('p, div'); if (actionArea) { actionArea.classList.add('action-area'); diff --git a/test/blocks/text/mocks/body.html b/test/blocks/text/mocks/body.html index 3c10028ad6..2db780be10 100644 --- a/test/blocks/text/mocks/body.html +++ b/test/blocks/text/mocks/body.html @@ -220,3 +220,18 @@

+
+
#ffffff
+
+
+
+

Text – Full-Width, Medium

+

Featuring over 600,000 hand-picked stock photos and graphics, curated from the world’s leading photographers, illustrators, and agencies. Our Premium collection is perfect for organizations looking for authentic, high-quality commercial content, and easy licensing plans.

+

Milo

+
+
+ + From e675bf41c2a97f5c52119d3f32944d38acf3260b Mon Sep 17 00:00:00 2001 From: Cody Lloyd <119891065+colloyd@users.noreply.github.com> Date: Mon, 2 Sep 2024 02:18:17 -0600 Subject: [PATCH 14/66] MWPW-156126 - quiz-entry contrast and accessibility (#2785) * adjustment to colors for better contrast * aria-label for carousel arrows * css clean up Resolves: [MWPW-156126](https://jira.corp.adobe.com/browse/MWPW-156126) --- libs/blocks/quiz-entry/quiz-entry.css | 58 +++++++-------------------- libs/blocks/quiz-entry/quizoption.js | 4 +- 2 files changed, 16 insertions(+), 46 deletions(-) diff --git a/libs/blocks/quiz-entry/quiz-entry.css b/libs/blocks/quiz-entry/quiz-entry.css index 858604ce0d..049f39a0b9 100644 --- a/libs/blocks/quiz-entry/quiz-entry.css +++ b/libs/blocks/quiz-entry/quiz-entry.css @@ -1,3 +1,8 @@ +.quiz-entry { + --quiz-button-disabled-bg: #757575; + --quiz-button-disabled-text: #FFF6F6; +} + .quiz-container { align-items: center; color: var(--color-black); @@ -17,15 +22,15 @@ } .quiz-title { - font-size: 28px; - line-height: 36px; + font-size: var(--type-heading-xl-size); + line-height: var(--type-heading-xl-lh); font-weight: var(--type-heading-all-weight); margin-bottom: 8px; } .quiz-subtitle { - font-size: 20px; - line-height: 30px; + font-size: var(--type-heading-l-size); + line-height: var(--type-heading-l-lh); } .quiz-question-container { @@ -134,7 +139,7 @@ .quiz-directions { align-items: center; - background: linear-gradient(90deg, #E200D9 1.24%, #E84601 100%); + background: linear-gradient(90deg, #A900A2 1.24%, #E84601 100%); border-radius: 8px; color: #FFF; display: flex; @@ -230,25 +235,6 @@ position: relative; } -.quiz-option-icon { - align-items: center; - background-color: var(--quiz-icon-bg); - border-radius: 0.5rem; - display: flex; - line-height: 0; - padding: 0 24px; -} - -.quiz-option-icon img { - height: var(--icon-size-l); - width: var(--icon-size-l); - max-width: var(--icon-size-l); -} - -.quiz-option.has-icon .no-icon-default { - display: none; -} - .quiz-option-image { display: flex; align-items: center; @@ -256,7 +242,7 @@ justify-content: center; margin: 0; height: 100%; - filter: brightness(33%); + filter: brightness(35%); } .quiz-option-text-container { @@ -289,14 +275,6 @@ text-align: start; } -.quiz-option:hover .quiz-option-icon { - background-color: color-mix(in srgb, var(--quiz-icon-bg) 70%, var(--color-white)); -} - -.quiz-option:focus-visible .quiz-option-icon { - background-color: color-mix(in srgb, var(--quiz-icon-bg) 80%, var(--color-white)); -} - .quiz-option.selected { border: 5px solid #FC00F2; border-radius: 13px; @@ -314,14 +292,10 @@ z-index: 1; } -.quiz-option.selected .quiz-option-icon { - background-color: color-mix(in srgb, var(--quiz-icon-bg) 70%, var(--color-white)); -} - .quiz-option:hover .quiz-option-image, .quiz-option.selected .quiz-option-image, .quiz-option:focus-visible .quiz-option-image { - filter: brightness(33%); + filter: brightness(20%); } .quiz-option:focus-visible::after { @@ -382,12 +356,12 @@ } .quiz-button[disabled] { - background: var(--color-gray-400); + background: var(--quiz-button-disabled-bg); cursor: not-allowed; } .quiz-button[disabled] .quiz-button-label{ - color: var(--color-white); + color: var(--quiz-button-disabled-text); } /* Tablet up */ @@ -477,10 +451,6 @@ min-height: 232px; } - .quiz-option-image { - filter: brightness(55%); - } - .quiz-option-title { font-size: 24px; line-height: 30px; diff --git a/libs/blocks/quiz-entry/quizoption.js b/libs/blocks/quiz-entry/quizoption.js index 4681a86e54..bb3f88bf15 100644 --- a/libs/blocks/quiz-entry/quizoption.js +++ b/libs/blocks/quiz-entry/quizoption.js @@ -144,7 +144,7 @@ export const GetQuizOption = ({ return html`
- ${index > 0 && html``} + ${index > 0 && html``} - ${(index + visibleCount < options.data.length) && html``} + ${(index + visibleCount < options.data.length) && html``}
`; }; From 76a13443e73af38f3ceee17c901dcc5d8673161a Mon Sep 17 00:00:00 2001 From: Santoshkumar Nateekar Date: Mon, 2 Sep 2024 01:18:25 -0700 Subject: [PATCH 15/66] MWPW-157346:[NALA] Move Nala tests to Milo repository (#2795) * nala initial commit * gitignore * updated the tests * update to the packages * updating util file * remove new line * update base-reporter.js * update geo pom * update nala run * update git ignore * fixing eslint errors * eslint fixes --------- Co-authored-by: milo-pr-merge[bot] <169241390+milo-pr-merge[bot]@users.noreply.github.com> Co-authored-by: Santoshkumar Sharanappa Nateekar Co-authored-by: Santoshkumar Sharanappa Nateekar --- .gitignore | 2 + .../.nala-snippets/spec-snippet.code-snippets | 25 ++ nala/blocks/accordion/accordion.page.js | 22 ++ nala/blocks/accordion/accordion.spec.js | 56 +++ nala/blocks/accordion/accordion.test.js | 136 ++++++++ nala/features/georouting/georouting.page.js | 78 +++++ nala/features/georouting/georouting.spec.js | 104 ++++++ nala/features/georouting/georouting.test.js | 137 ++++++++ nala/libs/baseurl.js | 17 + nala/libs/webutil.js | 329 ++++++++++++++++++ nala/utils/base-reporter.js | 223 ++++++++++++ nala/utils/global.setup.js | 132 +++++++ nala/utils/nala.run.js | 181 ++++++++++ nala/utils/slack.js | 21 ++ package-lock.json | 103 +++++- package.json | 3 + playwright.config.js | 71 ++++ 17 files changed, 1633 insertions(+), 7 deletions(-) create mode 100644 nala/.nala-snippets/spec-snippet.code-snippets create mode 100644 nala/blocks/accordion/accordion.page.js create mode 100644 nala/blocks/accordion/accordion.spec.js create mode 100644 nala/blocks/accordion/accordion.test.js create mode 100644 nala/features/georouting/georouting.page.js create mode 100644 nala/features/georouting/georouting.spec.js create mode 100644 nala/features/georouting/georouting.test.js create mode 100644 nala/libs/baseurl.js create mode 100644 nala/libs/webutil.js create mode 100644 nala/utils/base-reporter.js create mode 100644 nala/utils/global.setup.js create mode 100644 nala/utils/nala.run.js create mode 100644 nala/utils/slack.js create mode 100644 playwright.config.js diff --git a/.gitignore b/.gitignore index b2dfb8c210..5fba7ff881 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ logs/* .iml .env **/mas/*/stats.json +test-html-results/ +test-results/ diff --git a/nala/.nala-snippets/spec-snippet.code-snippets b/nala/.nala-snippets/spec-snippet.code-snippets new file mode 100644 index 0000000000..1c5e123bf3 --- /dev/null +++ b/nala/.nala-snippets/spec-snippet.code-snippets @@ -0,0 +1,25 @@ +{ + "Create Nala Spec": { + "prefix": "create nala spec", + "body": [ + "module.exports = {", + " FeatureName: '${1:Block or Feature Name}',", + " features: [", + " {", + " tcid: '0',", + " name: '@${2:spec-name}',", + " path: '/drafts/nala/[${3:test-page-path}]',", + " data: {", + " attribute-1: '${4:value}',", + " attribute-2: '${5:value}',", + " attribute-3: '${6:value}',", + " },", + " tags: '@Block @smoke @regression @milo',", + " },", + " ],", + "};" + ], + "description": "Create a Nala spec with block name or feature name" + } +} + diff --git a/nala/blocks/accordion/accordion.page.js b/nala/blocks/accordion/accordion.page.js new file mode 100644 index 0000000000..4d49b6fb94 --- /dev/null +++ b/nala/blocks/accordion/accordion.page.js @@ -0,0 +1,22 @@ +export default class Accordion { + constructor(page, nth = 0) { + this.page = page; + // accordion locators + this.section = this.page.locator('.section').nth(nth); + this.accordion = this.page.locator('.accordion-container').nth(nth); + this.accordionForeground = this.accordion.locator('.foreground'); + this.accordionHeaders = this.accordion.locator('dt[role=heading]'); + this.accordionButtons = this.accordion.locator('dt button'); + this.accordionButtonIcons = this.accordion.locator('.accordion-icon'); + this.outlineButton = this.accordion.locator('.con-button.outline').nth(nth); + this.blueButton = this.accordion.locator('.con-button.blue').nth(nth); + this.textLink = this.accordion.locator('//a[contains(text(), "Text link")]').nth(nth); + + // accordion blocks attributes + this.attributes = { + 'accordion-container': { class: 'accordion-container con-block max-width-10-desktop' }, + 'accordion-container.seo': { class: 'accordion-container seo con-block max-width-10-desktop' }, + 'accordion-container-quiet-large': { class: 'accordion-container quiet max-width-12-desktop-large con-block' }, + }; + } +} diff --git a/nala/blocks/accordion/accordion.spec.js b/nala/blocks/accordion/accordion.spec.js new file mode 100644 index 0000000000..a40d8740e3 --- /dev/null +++ b/nala/blocks/accordion/accordion.spec.js @@ -0,0 +1,56 @@ +module.exports = { + FeatureName: 'Accordion Block', + features: [ + { + tcid: '0', + name: '@accordion-container', + path: '/drafts/nala/blocks/accordion/accordion#', + data: { + headers: 3, + heading0: 'How do I compress a PDF without losing quality?', + heading1: 'What size PDFs can I compress?', + heading2: 'How do I check my PDF file size?', + }, + tags: '@accordion @t1 @smoke @regression @milo', + }, + { + tcid: '1', + name: '@accordion(seo)', + path: '/drafts/nala/blocks/accordion/accordion-seo#', + data: { + headers: 3, + heading0: 'How do I compress a PDF without losing quality?', + heading1: 'What size PDFs can I compress?', + heading2: 'How do I check my PDF file size?', + }, + tags: '@accordion @accordion-seo @smoke @regression @milo', + }, + { + tcid: '2', + name: '@accordion (quiet, max-width-12-desktop-large)', + path: '/drafts/nala/blocks/accordion/accordion-quiet-max-width-12-desktop-large#', + data: { + headers: 3, + heading0: 'How do I compress a PDF without losing quality?', + heading1: 'What size PDFs can I compress?', + heading2: 'How do I check my PDF file size?', + }, + tags: '@accordion @accordion-quiet-max @smoke @regression @milo', + }, + { + tcid: '3', + name: '@accordion-seo-editorial', + path: '/drafts/nala/blocks/accordion/accordion-seo-editorial', + data: { + headers: 3, + heading0: 'How do I compress a PDF without losing quality?', + heading1: 'What size PDFs can I compress?', + heading2: 'How do I check my PDF file size?', + outlineButtonText: 'Lorem ipsum', + blueButtonText: 'Learn more', + }, + tags: '@accordion @t3 @smoke @regression @milo', + }, + + ], +}; diff --git a/nala/blocks/accordion/accordion.test.js b/nala/blocks/accordion/accordion.test.js new file mode 100644 index 0000000000..86dfa7d275 --- /dev/null +++ b/nala/blocks/accordion/accordion.test.js @@ -0,0 +1,136 @@ +import { expect, test } from '@playwright/test'; +import WebUtil from '../../libs/webutil.js'; +import { features } from './accordion.spec.js'; +import AccordionBlock from './accordion.page.js'; + +let webUtil; +let accordion; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo Accordion Block test suite', () => { + test.beforeEach(async ({ page }) => { + webUtil = new WebUtil(page); + accordion = new AccordionBlock(page); + }); + + // Test 0 : Accordion Container + 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 Accordion 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 Accrodion block content/specs', async () => { + await expect(await accordion.accordion).toBeVisible(); + + // verify accordion headers, buttons, and icons count + await expect(await accordion.accordionHeaders).toHaveCount(data.headers); + await expect(await accordion.accordionButtons).toHaveCount(data.headers); + await expect(await accordion.accordionButtonIcons).toHaveCount(data.headers); + + // verify accordion headers text content + await expect(await accordion.accordionHeaders.nth(0)).toContainText(data.heading0); + await expect(await accordion.accordionHeaders.nth(1)).toContainText(data.heading1); + await expect(await accordion.accordionHeaders.nth(2)).toContainText(data.heading2); + + // verify accordion buttons open close clicks + await expect(await accordion.accordionButtons.nth(0)).toHaveAttribute('aria-expanded', 'false'); + await accordion.accordionButtonIcons.nth(0).click(); + await expect(await accordion.accordionButtons.nth(0)).toHaveAttribute('aria-expanded', 'true'); + await accordion.accordionButtonIcons.nth(0).click(); + await expect(await accordion.accordionButtons.nth(0)).toHaveAttribute('aria-expanded', 'false'); + }); + + await test.step('step-3: Verify analytics attributes', async () => { + await expect(await accordion.section).toHaveAttribute('daa-lh', await webUtil.getSectionDaalh(1)); + await expect(await accordion.accordion).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('accordion-container', 1)); + }); + }); + + // Test 1 : Accordion (seo) + 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 Accordion 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 Accrodion seo block specs', async () => { + await expect(await accordion.accordion).toBeVisible(); + + const scriptContent = await page.evaluate(() => { + const scriptElement = document.querySelector('script[type="application/ld+json"]'); + return scriptElement ? scriptElement.textContent : null; + }); + expect(scriptContent).toBeTruthy(); + console.log('[SEO Script content]:', scriptContent); + + expect(await webUtil.verifyAttributes(accordion.accordion, accordion.attributes['accordion-container.seo'])).toBeTruthy(); + }); + + await test.step('step-3: Verify analytics attributes', async () => { + await expect(await accordion.accordion).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('accordion-container', 1)); + }); + }); + + // Test 2 : Accordion (quiet, max-width-12-desktop-large) + 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 Accordion 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 Accrodion block content/specs', async () => { + await expect(await accordion.accordion).toBeVisible(); + + // verify accordion headers, buttons, and icons count + await expect(await accordion.accordionHeaders).toHaveCount(data.headers); + await expect(await accordion.accordionButtons).toHaveCount(data.headers); + await expect(await accordion.accordionButtonIcons).toHaveCount(data.headers); + + // verify accordion headers text content + await expect(await accordion.accordionHeaders.nth(0)).toContainText(data.heading0); + await expect(await accordion.accordionHeaders.nth(1)).toContainText(data.heading1); + await expect(await accordion.accordionHeaders.nth(2)).toContainText(data.heading2); + + expect(await webUtil.verifyAttributes(accordion.accordion, accordion.attributes['accordion-container-quiet-large'])).toBeTruthy(); + }); + + await test.step('step-3: Verify analytics attributes', async () => { + await expect(await accordion.accordion).toHaveAttribute('daa-lh', await webUtil.getBlockDaalh('accordion-container', 1)); + }); + }); + + // Test 3 : Accordion seo editorial + 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 accordion 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 accordion content/specs', async () => { + // verify action area buttons, links and text visibility and content + await expect(await accordion.outlineButton).toBeVisible(); + await expect(await accordion.blueButton).toBeVisible(); + await expect(await accordion.textLink).toBeVisible(); + + await expect(await accordion.outlineButton).toContainText(data.outlineButtonText); + await expect(await accordion.blueButton).toContainText(data.blueButtonText); + }); + }); +}); diff --git a/nala/features/georouting/georouting.page.js b/nala/features/georouting/georouting.page.js new file mode 100644 index 0000000000..8388b6bd71 --- /dev/null +++ b/nala/features/georouting/georouting.page.js @@ -0,0 +1,78 @@ +/* eslint-disable import/no-extraneous-dependencies, max-len, no-console */ +import { expect } from '@playwright/test'; + +export default class Georouting { + constructor(page) { + this.page = page; + + // global footer locators + this.footer = this.page.locator('.global-footer'); + this.changeRegionLink = this.footer.locator('.modal.link-block'); + + // change region modal locators + this.changeRegionModal = this.page.locator('.dialog-modal'); + this.fragment = this.changeRegionModal.locator('.fragment'); + this.regionNav = this.changeRegionModal.locator('.region-nav'); + this.deLink = this.changeRegionModal.locator('//a[text()="Deutschland"]'); + this.usLink = this.changeRegionModal.locator('//a[text()="United States"]/@href'); + this.modalClose = this.changeRegionModal.locator('.dialog-close'); + + // georouting modal locators + this.geoModal = this.page.locator('#locale-modal-v2'); + this.geoModalTitle = this.geoModal.locator('h3'); + this.geoModalText = this.geoModal.locator('.locale-text'); + this.geoModalClose = this.geoModal.locator('.dialog-close'); + this.usButton = this.geoModal.locator('//a[text()="United States"][@lang="en-US"]'); + } + + /** + * Verifies georouting modal content. + * @param {data} - data object from spec. + * @returns {Promise} - Returns true if modal content matches the expected data. + */ + async verifyGeoModal(data) { + try { + await expect(this.geoModal).toBeVisible(); + await expect(this.geoModalClose).toBeVisible(); + await expect(this.geoModalTitle).toContainText(data.title); + await expect(this.geoModalText).toContainText(data.text); + await expect(this.geoModal.locator(`//a[text()="${data.button}"]`)).toBeVisible({ timeout: 1000 }); + await expect(this.geoModal.locator(`//a[text()="${data.link}"]`)).toBeVisible({ timeout: 1000 }); + await expect(this.geoModal.locator(`//img[@alt="${data.flag}"]`)).toBeVisible({ timeout: 1000 }); + + return true; + } catch (error) { + console.error('Georouting modal verification is failed:', error); + return false; + } + } + + /** + * Verifies multi tab georouting modal. + * @param {data} - data object from spec. + * @returns {Promise} - Returns true if modal content is as expected. + */ + async verifyMultiTabGeoModal(data) { + /* eslint-disable no-restricted-syntax, guard-for-in */ + try { + await expect(this.geoModal).toBeVisible(); + await expect(this.geoModalClose).toBeVisible(); + let index = 0; + for (const tab in data) { + console.info(`[Tab]: "${data[tab].name}"`); + await this.geoModal.locator(`//button[text()="${data[tab].name}"]`).click(); + await expect(this.geoModalTitle.nth(index)).toContainText(data[tab].title); + await expect(this.geoModalText.nth(index)).toContainText(data[tab].text); + 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; + } + + return true; + } catch (error) { + console.log('Georouting multi tab modal verification is failed:', error); + return false; + } + } +} diff --git a/nala/features/georouting/georouting.spec.js b/nala/features/georouting/georouting.spec.js new file mode 100644 index 0000000000..6235b4e1c0 --- /dev/null +++ b/nala/features/georouting/georouting.spec.js @@ -0,0 +1,104 @@ +module.exports = { + name: 'Georouting Feature', + features: [ + { + tcid: '0', + name: '@Georouting modal', + desc: 'User accessing DE page from US locale.', + path: '/de/drafts/nala/features/georouting/georouting', + data: { + title: /This Adobe site doesn[' ’]t match your location./, + text: 'Based on your location, we think you may prefer the United States website', + button: 'United States', + link: 'Deutschland', + flag: 'United States', + cookieName: 'international', + cookieValue: 'de', + }, + tags: '@georouting @smoke @regression @milo @nopr', + }, + { + tcid: '1', + name: '@Georouting with query param', + desc: 'User is accessing the US page with a query parameter (akamaiLocale=DE).', + path: '/drafts/nala/features/georouting/georouting?akamaiLocale=DE', + data: { + title: 'Diese Adobe-Site passt nicht zu deinem Standort.', + text: 'Basierend auf deiner IP-Adresse könnte die Website für Deutschland passender sein.', + button: 'Deutschland', + link: 'United States', + flag: 'Deutschland', + cookieName: 'international', + cookieValue: 'de', + }, + tags: '@georouting @georouting-query-param @smoke @regression @milo', + }, + { + tcid: '2', + name: '@Georouting through Change region', + desc: 'User navigating to DE site through "Change region" modal.', + path: '/drafts/nala/features/georouting/georouting', + data: { + title: 'Diese Adobe-Site passt nicht zu deinem Standort.', + text: 'Basierend auf deiner IP-Adresse könnte die Website für Deutschland passender sein.', + button: 'Deutschland', + link: 'United States', + flag: 'Deutschland', + cookieName: 'international', + cookieValue: 'de', + }, + tags: '@georouting @smoke @regression @milo ', + }, + { + tcid: '3', + name: '@Georouting multi tab', + desc: 'User is accessing the CH page with a query parameter, the CH georouting modal will have multiple tabs for languages', + path: '/drafts/nala/features/georouting/georouting?akamaiLocale=CH', + data: { + tab1: { + name: 'Deutsch', + title: 'Diese Adobe-Site passt nicht zu deinem Standort.', + text: 'Basierend auf deiner IP-Adresse könnte die Website für Schweiz passender sein.', + button: 'Schweiz', + link: 'United States', + flag: 'Schweiz', + }, + tab2: { + name: 'Français', + title: 'Ce site Adobe ne correspond pas à votre zone géographique.', + text: 'Pour accéder à du contenu, des offres et des tarifs correspondant davantage à votre zone géographique, rendez-vous plutôt sur le site web Suisse.', + button: 'Suisse', + link: 'United States', + flag: 'Suisse', + }, + tab3: { + name: 'Italiano', + title: 'Questo sito Adobe non corrisponde alla tua posizione geografica.', + text: 'In base alla tua posizione, ti consigliamo di consultare il sito web di Adobe Svizzera, dove troverai contenuti, offerte e prezzi specifici per la tua area geografica.', + button: 'Svizzera', + link: 'United States', + flag: 'Svizzera', + }, + }, + tags: '@georouting @georouting-multi-tab @smoke @regression @milo ', + }, + { + tcid: '4', + name: '@Georouting-off', + desc: 'User is accessing "de" page for which georouting is turned off', + path: '/de/drafts/nala/features/georouting/geo-off', + tags: '@georouting @georouting-off @milo @smoke @regression', + }, + { + tcid: '5', + name: '@Georouting modal close', + desc: 'If user closes an georouting modal, the international cookie should not be added to the browser.', + path: '/de/drafts/nala/features/georouting/georouting', + data: { + cookieName: 'international', + cookieValue: 'de', + }, + tags: '@georouting @georouting-close @regression @milo @nopr', + }, + ], +}; diff --git a/nala/features/georouting/georouting.test.js b/nala/features/georouting/georouting.test.js new file mode 100644 index 0000000000..53b9cd1c61 --- /dev/null +++ b/nala/features/georouting/georouting.test.js @@ -0,0 +1,137 @@ +/* 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'; + +let modal; + +const miloLibs = process.env.MILO_LIBS || ''; + +test.describe('Milo Georouting feature test suite', () => { + test.beforeEach(async ({ page }) => { + modal = new Georouting(page); + }); + + test(`${features[0].name},${features[0].tags}`, async ({ page, baseURL, browserName }) => { + test.skip(browserName === 'webkit', 'This feature is failing on Webkit browsers'); + test.slow(); + console.info(`[Test Page]: ${baseURL}${features[0].path}${miloLibs}`); + const { data } = features[0]; + + await test.step('step-1: Clear cookies and access "DE" page from "US" region', async () => { + await page.context().clearCookies(); + await page.goto(`${baseURL}${features[0].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[0].path}${miloLibs}`); + await modal.geoModal.waitFor({ state: 'visible', timeout: 30000 }); + }); + + await test.step('step-2: Verify georouting modal and its content', async () => { + expect(await modal.verifyGeoModal(data)).toBeTruthy(); + }); + + await test.step('step-3: Click "Deutschland" link from modal and then verify international cookie value', async () => { + await modal.deLink.click(); + expect((await page.context().cookies()).find((cookie) => cookie.name === data.cookieName).value).toEqual(data.cookieValue); + }); + }); + + test(`${features[1].name},${features[1].tags}`, async ({ page, baseURL }) => { + test.slow(); + console.info(`[Test Page]: ${baseURL}${features[1].path}&${miloLibs}`); + const { data } = features[1]; + + await test.step('step-1: Clear cookies and access "US" page with query param (akamailLocale=DE)', async () => { + await page.context().clearCookies(); + await page.goto(`${baseURL}${features[1].path}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[1].path}&${miloLibs}`); + await modal.geoModal.waitFor({ state: 'visible', timeout: 10000 }); + }); + + await test.step('step-2: Verify georouting modal and its content', async () => { + expect(await modal.verifyGeoModal(data)).toBeTruthy(); + }); + + await test.step('step-3: Click "Deutschland" button and then verify international cookie value', async () => { + await modal.deLink.click(); + expect((await page.context().cookies()).find((cookie) => cookie.name === data.cookieName).value).toEqual(data.cookieValue); + }); + }); + + test(`${features[2].name},${features[2].tags}`, async ({ page, baseURL }) => { + test.slow(); + console.info(`[Test Page]: ${baseURL}${features[2].path}${miloLibs}`); + const { data } = features[2]; + + await test.step('step-1: Clear cookies and access "US" page', async () => { + await page.context().clearCookies(); + 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: Click "Change region" link from footer and navigate to "Deutschland" page', async () => { + await modal.changeRegionLink.click(); + await modal.changeRegionModal.waitFor({ state: 'visible', timeout: 10000 }); + await modal.deLink.click(); + }); + + await test.step('step-3: Verify international cookie value', async () => { + expect((await page.context().cookies()).find((cookie) => cookie.name === data.cookieName).value).toEqual(data.cookieValue); + }); + }); + + test(`${features[3].name},${features[3].tags}`, async ({ page, baseURL }) => { + test.slow(); + console.info(`[Test Page]: ${baseURL}${features[3].path}&${miloLibs}`); + const { data } = features[3]; + + await test.step('step-1: Clear cookies and access "US" page', async () => { + await page.context().clearCookies(); + await page.goto(`${baseURL}${features[3].path}&${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[3].path}&${miloLibs}`); + await modal.geoModal.waitFor({ state: 'visible', timeout: 10000 }); + }); + + await test.step('step-2: Verify multi tab georouting modal and its content', async () => { + expect(await modal.verifyMultiTabGeoModal(data)).toBeTruthy(); + }); + }); + + test(`${features[4].name},${features[4].tags}`, async ({ page, baseURL }) => { + console.info(`[Test Page]: ${baseURL}${features[4].path}${miloLibs}`); + + await test.step('step-1: Clear cookies and access given "DE" page', async () => { + await page.context().clearCookies(); + 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 that georouting modal is not shown', async () => { + await expect(await modal.geoModal).not.toBeVisible(); + }); + }); + + test(`${features[5].name},${features[5].tags}`, async ({ page, browserName, baseURL }) => { + test.skip(browserName === 'webkit', 'This feature is failing on Webkit browsers'); + test.slow(); + console.info(`[Test Page]: ${baseURL}${features[5].path}${miloLibs}`); + const { data } = features[5]; + + await test.step('step-1: Clear cookies and access given "DE" page', async () => { + await page.context().clearCookies(); + await page.goto(`${baseURL}${features[5].path}${miloLibs}`); + await page.waitForLoadState('domcontentloaded'); + await expect(page).toHaveURL(`${baseURL}${features[5].path}${miloLibs}`); + await modal.geoModal.waitFor({ state: 'visible', timeout: 10000 }); + }); + + await test.step('step-2: Close the georouting modal and then check that international cookie is not added', async () => { + await modal.geoModalClose.click(); + expect((await page.context().cookies()).find((cookie) => cookie.name === data.cookieName)).toBeUndefined(); + }); + }); +}); diff --git a/nala/libs/baseurl.js b/nala/libs/baseurl.js new file mode 100644 index 0000000000..3dbc001040 --- /dev/null +++ b/nala/libs/baseurl.js @@ -0,0 +1,17 @@ +/* eslint-disable import/no-extraneous-dependencies, import/prefer-default-export, max-len, no-console */ +import { head } from 'axios'; + +export async function isBranchURLValid(url) { + try { + const response = await head(url); + if (response.status === 200) { + console.info(`\nURL (${url}) returned a 200 status code. It is valid.`); + return true; + } + console.info(`\nURL (${url}) returned a non-200 status code (${response.status}). It is invalid.`); + return false; + } catch (error) { + console.info(`\nError checking URL (${url}): returned a non-200 status code (${error.message})`); + return false; + } +} diff --git a/nala/libs/webutil.js b/nala/libs/webutil.js new file mode 100644 index 0000000000..1fcbe0a732 --- /dev/null +++ b/nala/libs/webutil.js @@ -0,0 +1,329 @@ +/* eslint-disable import/no-extraneous-dependencies, max-len, no-console, class-methods-use-this */ + +import { expect } from '@playwright/test'; + +const { request } = require('@playwright/test'); + +/** + * A utility class for common web interactions. + */ +export default class WebUtil { + /** + * Create a new instance of WebUtil. + * @param {object} page - A Playwright page object. + */ + constructor(page) { + this.page = page; + this.locator = null; + } + + /** + * Check if the element associated with the current locator is visible. + * @param {Locator} locator - The Playwright locator for the element to check. + * + */ + static async isVisible(locator) { + this.locator = locator; + await expect(this.locator).toBeVisible(); + return true; + } + + /** + * Check if the element associated with the current locator is displayed. + * @param {Locator} locator - The Playwright locator for the element to check. + * @returns {Promise} - Resolves to `true` if the element is displayed, or `false`. + */ + static async isDisplayed(locator) { + this.locator = locator; + try { + return await this.locator.evaluate((e) => e.offsetWidth > 0 && e.offsetHeight > 0); + } catch (e) { + console.error(`Error checking if element is displayed for locator: ${locator.toString()}`, e); + return false; + } + } + + /** + * Click the element associated with the current locator. + * @param {Locator} locator - The Playwright locator for the element to click. + * @returns {Promise} A Promise that resolves when the element has been clicked. + */ + static async click(locator) { + this.locator = locator; + return this.locator.click(); + } + + /** + * Get the inner text of the element associated with the current locator. + * @param {Locator} locator - The Playwright locator for the element to retrieve text from. + * @returns {Promise} A Promise that resolves to the inner text of the element. + */ + static async getInnerText(locator) { + this.locator = locator; + const innerText = await this.locator.innerText(); + return innerText; + } + + /** + * Get the text of the element associated with the current locator, filtered by the specified tag name. + * @param {Locator} locator - The Playwright locator for the element to retrieve text from. + * @param {string} tagName - The name of the tag to filter by (e.g. "p", "span", etc.). + * @returns {Promise} A Promise that resolves to the text of the element, filtered by the specified tag name. + */ + static async getTextByTag(locator, tagName) { + this.locator = locator; + return this.locator.$eval(tagName, (e) => e.textContent); + } + + /** + * Get the value of the specified attribute on the element associated with the current locator. + * @param {Locator} locator - The Playwright locator for the element to retrieve the attribute from. + * @param {string} attributeName - The name of the attribute to retrieve (e.g. "class", "data-attr", etc.). + * @returns {Promise} A Promise that resolves to the value of the specified attribute on the element. + */ + static async getAttribute(locator, attributeName) { + this.locator = locator; + return this.locator.getAttribute(attributeName); + } + + /** + * Verifies that the specified CSS properties of the given locator match the expected values. + * @param {Object} locator - The locator to verify CSS properties for. + * @param {Object} cssProps - The CSS properties and expected values to verify. + * @returns {Boolean} - True if all CSS properties match the expected values, false otherwise. + */ + async verifyCSS(locator, cssProps) { + this.locator = locator; + let result = true; + await Promise.allSettled( + Object.entries(cssProps).map(async ([property, expectedValue]) => { + try { + await expect(this.locator).toHaveCSS(property, expectedValue); + } catch (error) { + console.error(`CSS property ${property} not found:`, error); + result = false; + } + }), + ); + return result; + } + + /** + * Verifies that the specified attribute properties of the given locator match the expected values. + * @param {Object} locator - The locator to verify attributes. + * @param {Object} attProps - The attribute properties and expected values to verify. + * @returns {Boolean} - True if all attribute properties match the expected values, false otherwise. + */ + async verifyAttributes(locator, attProps) { + this.locator = locator; + let result = true; + await Promise.allSettled( + Object.entries(attProps).map(async ([property, expectedValue]) => { + if (property === 'class' && typeof expectedValue === 'string') { + // If the property is 'class' and the expected value is an string, + // split the string value into individual classes + const classes = expectedValue.split(' '); + try { + await expect(await this.locator).toHaveClass(classes.join(' ')); + } catch (error) { + console.error('Attribute class not found:', error); + result = false; + } + } else { + try { + await expect(await this.locator).toHaveAttribute(property, expectedValue); + } catch (error) { + console.error(`Attribute ${property} not found:`, error); + result = false; + } + } + }), + ); + return result; + } + + /** + * Slow/fast scroll of entire page JS evaluation method, aides with lazy loaded content. + * This wrapper method calls a scroll script in page.evaluate, i.e. page.evaluate(scroll, { dir: 'direction', spd: 'speed' }); + * @param direction string direction you want to scroll on the page + * @param speed string speed you would like to scroll through the page. Options: slow, fast + */ + async scrollPage(direction, speed) { + const scroll = async (args) => { + const { dir, spd } = args; + // eslint-disable-next-line no-promise-executor-return + const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + const scrollHeight = () => document.body.scrollHeight; + const start = dir === 'down' ? 0 : scrollHeight(); + const shouldStop = (position) => (dir === 'down' ? position > scrollHeight() : position < 0); + const increment = dir === 'down' ? 100 : -100; + const delayTime = spd === 'slow' ? 30 : 5; + console.error(start, shouldStop(start), increment); + for (let i = start; !shouldStop(i); i += increment) { + window.scrollTo(0, i); + // eslint-disable-next-line no-await-in-loop + await delay(delayTime); + } + }; + + await this.page.evaluate(scroll, { dir: direction, spd: speed }); + } + + /** + * Check if the modal associated with the current locator is within the viewport. + * @param page - calling method page object. + * @returns {Promise} - Resolves to true if the modal is within the viewport, or false. + */ + static async isModalInViewport(page, selector) { + try { + const inViewport = await page.evaluate((sel) => { + const modalDialog = document.querySelector('.dialog-modal'); + if (!modalDialog) { + throw new Error(`Modal element with selector '${sel}' not found.`); + } + const rect = modalDialog.getBoundingClientRect(); + return ( + rect.top >= 0 + && rect.left >= 0 + && rect.bottom + <= (window.innerHeight || document.documentElement.clientHeight) + && rect.right + <= (window.innerWidth || document.documentElement.clientWidth) + ); + }, selector); + + return inViewport; + } catch (error) { + console.error('Error verifying modal veiwport:', error); + return false; + } + } + + /** + * Load test data from remote json file + * @param {string} path + * @param {string} url + */ + static async loadTestDataFromAPI(url, path) { + const context = await request.newContext({ baseURL: url }); + const res = await context.fetch(path); + return res.json(); + } + + /** + * Enable network logging + * @param {Array} networklogs - An array to store all network logs + */ + async enableNetworkLogging(networklogs) { + await this.page.route('**', (route) => { + const url = route.request().url(); + if (url.includes('sstats.adobe.com/ee/or2/v1/interact') + || url.includes('sstats.adobe.com/ee/or2/v1/collect')) { + networklogs.push(url); + const firstEvent = route.request().postDataJSON().events[0]; + // eslint-disable-next-line no-underscore-dangle + if (firstEvent.data._adobe_corpnew.digitalData.primaryEvent) { + // eslint-disable-next-line no-underscore-dangle + networklogs.push(JSON.stringify(firstEvent.data._adobe_corpnew.digitalData.primaryEvent)); + } + + // eslint-disable-next-line no-underscore-dangle + if (firstEvent.data._adobe_corpnew.digitalData.search) { + // eslint-disable-next-line no-underscore-dangle + networklogs.push(JSON.stringify(firstEvent.data._adobe_corpnew.digitalData.search)); + } + } + route.continue(); + }); + } + + /** + * Disable network logging + */ + async disableNetworkLogging() { + await this.page.unroute('**'); + } + + /** + * Generates analytic string for a given project. + * @param {string} project - The project identifier, defaulting to 'milo' if not provided. + * @returns {string} - A string formatted as 'gnav||nopzn|nopzn'. + */ + async getGnavDaalh(project) { + return `gnav|${project}|nopzn|nopzn`; + } + + /** + * Generates analytic string for a given project. + * @param {string} project - The project identifier, defaulting to 'milo' if not provided. + * @param {string} pznExpName - Personalized experience name, which is sliced to its first 15 characters. + * @param {string} pznFileName - Manifest filename, which is sliced to its first 20 characters. + * @returns {string} - A string formatted as 'gnav|||'. + */ + async getPznGnavDaalh(pznExpName, pznFileName, project) { + const slicedExpName = pznExpName.slice(0, 15); + const slicedFileName = pznFileName.slice(0, 15); + return `gnav|${project}|${slicedExpName}|${slicedFileName}`; + } + + /** + * Generates analytic string for a section based on a given counter value. + * @param {number|string} counter - A counter value used to generate the section identifier. + * @returns {string} - A string formatted as 's'. + */ + async getSectionDaalh(counter) { + return `s${counter}`; + } + + /** + * Generates personalization analytic string for a given block name and a counter. + * @param {string} blockName - The name of the block, which is sliced to its first 20 characters. + * @param {number|string} counter - A counter value i.e. block number. + * @param {string} pznExpName - Personalized experience name, which is sliced to its first 15 characters. + * @param {string} pznExpName - Manifest filename, which is sliced to its first 20 characters. + * @returns {string} - A string formatted as 'b|||'. + */ + async getPznBlockDaalh(blockName, counter, pznExpName, pznFileName) { + const slicedBlockName = blockName.slice(0, 20); + const slicedExpName = pznExpName.slice(0, 15); + const slicedFileName = pznFileName.slice(0, 15); + return `b${counter}|${slicedBlockName}|${slicedExpName}|${slicedFileName}`; + } + + /** + * Generates an analytic string for a given block name and a counter. + * @param {string} blockName - The name of the block, which is sliced to its first 20 characters. + * @param {number|string} counter - A counter value, i.e., block number. + * @param {boolean} [pzn=false] - A boolean flag indicating whether to use pzntext. + * @param {string} [pzntext='nopzn'] - The pzntext to use when pzn is true, sliced to its first 15 characters. + * @returns {string} - A formatted string. + */ + async getBlockDaalh(blockName, counter, pzn = false, pzntext = 'nopzn') { + const slicedBlockName = blockName.slice(0, 15); + const slicedPzntext = pzntext.slice(0, 15); + if (pzn) { + return `b${counter}|${slicedBlockName}|${slicedPzntext}|nopzn`; + } + return `b${counter}|${slicedBlockName}`; + } + + /** + * Generates analytic string for link or button based on link/button text , a counter, and the last header text. + * @param {string} linkText - The text of the link, which is cleaned and sliced to its first 20 characters. + * @param {number|string} counter - A counter value used in the identifier. + * @param {string} lastHeaderText - The last header text, which is cleaned and sliced to its first 20 characters. + * @param {boolean} [pzn=false] - boolean parameter, defaulting to false.(for personalization) + * @returns {string} - A string formatted as '---'. + */ + async getLinkDaall(linkText, counter, lastHeaderText) { + const cleanAndSliceText = (text) => text + ?.replace(/[^\w\s]+/g, ' ') + .replace(/\s+/g, ' ') + .replace(/^_+|_+$/g, '') + .trim() + .slice(0, 20); + const slicedLinkText = cleanAndSliceText(linkText); + const slicedLastHeaderText = cleanAndSliceText(lastHeaderText); + return `${slicedLinkText}-${counter}--${slicedLastHeaderText}`; + } +} diff --git a/nala/utils/base-reporter.js b/nala/utils/base-reporter.js new file mode 100644 index 0000000000..86235f6f26 --- /dev/null +++ b/nala/utils/base-reporter.js @@ -0,0 +1,223 @@ +/* eslint-disable max-len, class-methods-use-this, no-empty-function, no-console */ + +const { sendSlackMessage } = require('./slack.js'); + +// Playwright will include ANSI color characters and regex from below +// https://github.com/microsoft/playwright/issues/13522 +// https://github.com/chalk/ansi-regex/blob/main/index.js#L3 + +const pattern = [ + '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', + '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))', +].join('|'); + +const ansiRegex = new RegExp(pattern, 'g'); + +// limit failed status +const failedStatus = ['failed', 'flaky', 'timedOut', 'interrupted']; + +function stripAnsi(str) { + if (!str || typeof str !== 'string') return str; + return str.replace(ansiRegex, ''); +} + +class BaseReporter { + constructor(options) { + this.options = options; + this.results = []; + this.passedTests = 0; + this.failedTests = 0; + this.skippedTests = 0; + } + + onBegin(config, suite) { + this.config = config; + this.rootSuite = suite; + } + + async onTestEnd(test, result) { + const { title, retries, _projectId } = test; + const { + name, tags, url, browser, env, branch, repo, + } = this.parseTestTitle(title, _projectId); + const { + status, + duration, + error: { message: errorMessage, value: errorValue, stack: errorStack } = {}, + retry, + } = result; + + if (retry < retries && status === 'failed') { + return; + } + this.results.push({ + title, + name, + tags, + url, + env, + browser, + branch, + repo, + status: failedStatus.includes(status) ? 'failed' : status, + errorMessage: stripAnsi(errorMessage), + errorValue, + errorStack: stripAnsi(errorStack), + stdout: test.stdout, + stderr: test.stderr, + duration, + retry, + }); + if (status === 'passed') { + this.passedTests += 1; + } else if (failedStatus.includes(status)) { + this.failedTests += 1; + } else if (status === 'skipped') { + this.skippedTests += 1; + } + } + + async onEnd() { + const summary = this.printResultSummary(); + const resultSummary = { summary }; + + if (process.env.SLACK_WH) { + try { + await sendSlackMessage(process.env.SLACK_WH, resultSummary); + } catch (error) { + console.log('----Failed to publish result to slack channel----'); + } + } + } + + printResultSummary() { + const totalTests = this.results.length; + const passPercentage = ((this.passedTests / totalTests) * 100).toFixed(2); + const failPercentage = ((this.failedTests / totalTests) * 100).toFixed(2); + const miloLibs = process.env.MILO_LIBS || ''; + const prBranchUrl = process.env.PR_BRANCH_LIVE_URL ? (process.env.PR_BRANCH_LIVE_URL + miloLibs) : undefined; + const projectBaseUrl = this.config.projects[0].use.baseURL; + const envURL = prBranchUrl || projectBaseUrl; + + let exeEnv = 'Local Environment'; + let runUrl = 'Local Environment'; + let runName = 'Nala Local Run'; + + if (process.env.GITHUB_ACTIONS === 'true') { + exeEnv = 'GitHub Actions Environment'; + const repo = process.env.GITHUB_REPOSITORY; + const runId = process.env.GITHUB_RUN_ID; + const prNumber = process.env.GITHUB_REF.split('/')[2]; + runUrl = `https://github.com/${repo}/actions/runs/${runId}`; + runName = `${process.env.WORKFLOW_NAME ? (process.env.WORKFLOW_NAME || 'Nala Daily Run') : 'Nala PR Run'} (${prNumber})`; + } else if (process.env.CIRCLECI) { + exeEnv = 'CircleCI Environment'; + const workflowId = process.env.CIRCLE_WORKFLOW_ID; + const jobNumber = process.env.CIRCLE_BUILD_NUM; + runUrl = `https://app.circle.ci.adobe.com/pipelines/github/wcms/nala/${jobNumber}/workflows/${workflowId}/jobs/${jobNumber}`; + runName = 'Nala CircleCI/Stage Run'; + } + + const summary = ` + \x1b[1m\x1b[34m---------Nala Test Run Summary------------\x1b[0m + \x1b[1m\x1b[33m# Total Test executed:\x1b[0m \x1b[32m${totalTests}\x1b[0m + \x1b[1m\x1b[33m# Test Pass :\x1b[0m \x1b[32m${this.passedTests} (${passPercentage}%)\x1b[0m + \x1b[1m\x1b[33m# Test Fail :\x1b[0m \x1b[31m${this.failedTests} (${failPercentage}%)\x1b[0m + \x1b[1m\x1b[33m# Test Skipped :\x1b[0m \x1b[32m${this.skippedTests}\x1b[0m + \x1b[1m\x1b[33m** Application URL :\x1b[0m \x1b[32m${envURL}\x1b[0m + \x1b[1m\x1b[33m** Executed on :\x1b[0m \x1b[32m${exeEnv}\x1b[0m + \x1b[1m\x1b[33m** Execution details:\x1b[0m \x1b[32m${runUrl}\x1b[0m + \x1b[1m\x1b[33m** Workflow name :\x1b[0m \x1b[32m${runName}\x1b[0m`; + + console.log(summary); + + if (this.failedTests > 0) { + console.log('-------- Test Failures --------'); + this.results + .filter((result) => result.status === 'failed') + .forEach((failedTest) => { + console.log(`Test: ${failedTest.title.split('@')[1]}`); + console.log(`Error Message: ${failedTest.errorMessage}`); + console.log(`Error Stack: ${failedTest.errorStack}`); + console.log('-------------------------'); + }); + } + return summary; + } + + /** + This method takes test title and projectId strings and then processes it . + @param {string, string} str - The input string to be processed + @returns {'name', 'tags', 'url', 'browser', 'env', 'branch' and 'repo'} + */ + parseTestTitle(title, projectId) { + let env = 'live'; + let browser = 'chrome'; + let branch; + let repo; + let url; + + const titleParts = title.split('@'); + const name = titleParts[1].trim(); + const tags = titleParts.slice(2).map((tag) => tag.trim()); + + const projectConfig = this.config.projects.find((project) => project.name === projectId); + + // Get baseURL from project config + if (projectConfig?.use?.baseURL) { + ({ baseURL: url, defaultBrowserType: browser } = projectConfig.use); + } else if (this.config.baseURL) { + url = this.config.baseURL; + } + // Get environment from baseURL + if (url.includes('prod')) { + env = 'prod'; + } else if (url.includes('stage')) { + env = 'stage'; + } + // Get branch and repo from baseURL + if (url.includes('localhost')) { + branch = 'local'; + repo = 'local'; + } else { + const urlParts = url.split('/'); + const branchAndRepo = urlParts[urlParts.length - 1]; + [branch, repo] = branchAndRepo.split('--'); + } + + return { + name, tags, url, browser, env, branch, repo, + }; + } + + async persistData() {} + + printPersistingOption() { + if (this.options?.persist) { + console.log( + `Persisting results using ${this.options.persist?.type} to ${this.options.persist?.path}`, + ); + } else { + console.log('Not persisting data'); + } + this.branch = process.env.LOCAL_TEST_LIVE_URL; + } + + getPersistedDataObject() { + const gitBranch = process.env.GITHUB_REF_NAME ?? 'local'; + + // strip out git owner since it can usually be too long to show on the ui + const [, gitRepo] = /[A-Za-z0-9_.-]+\/([A-Za-z0-9_.-]+)/.exec( + process.env.GITHUB_REPOSITORY, + ) ?? [null, 'local']; + + const currTime = new Date(); + return { + gitBranch, + gitRepo, + results: this.results, + timestamp: currTime, + }; + } +} +export default BaseReporter; diff --git a/nala/utils/global.setup.js b/nala/utils/global.setup.js new file mode 100644 index 0000000000..e453973589 --- /dev/null +++ b/nala/utils/global.setup.js @@ -0,0 +1,132 @@ +/* eslint-disable import/no-extraneous-dependencies, no-console */ + +const { execSync } = require('child_process'); +const { isBranchURLValid } = require('../libs/baseurl.js'); + +const MAIN_BRANCH_LIVE_URL = 'https://main--milo--adobecom.hlx.live'; +const STAGE_BRANCH_URL = 'https://milo.stage.adobe.com'; + +async function getGitHubPRBranchLiveUrl() { + // get the pr number + const prReference = process.env.GITHUB_REF; + const prNumber = prReference.split('/')[2]; + + // get the pr branch name + const branch = process.env.GITHUB_HEAD_REF; + const prBranch = branch.replace(/\//g, '-'); + + // get the org and repo + const repository = process.env.GITHUB_REPOSITORY; + const repoParts = repository.split('/'); + const toRepoOrg = repoParts[0]; + const toRepoName = repoParts[1]; + + // Get the org and repo from the environment variables + const prFromOrg = process.env.prOrg; + const prFromRepoName = process.env.prRepo; + + const prBranchLiveUrl = `https://${prBranch}--${prFromRepoName}--${prFromOrg}.hlx.live`; + + try { + if (await isBranchURLValid(prBranchLiveUrl)) { + process.env.PR_BRANCH_LIVE_URL = prBranchLiveUrl; + } + console.info('PR Repository : ', repository); + console.info('PR TO ORG : ', toRepoOrg); + console.info('PR TO REPO : ', toRepoName); + console.info('PR From ORG : ', prFromOrg); + console.info('PR From REPO : ', prFromRepoName); + console.info('PR Branch : ', branch); + console.info('PR Branch(U) : ', prBranch); + console.info('PR Number : ', prNumber); + console.info('PR From Branch live url : ', prBranchLiveUrl); + } catch (err) { + console.error(`Error => Error in setting PR Branch test URL : ${prBranchLiveUrl}`); + console.info(`Note: PR branch test url ${prBranchLiveUrl} is not valid, Exiting test execution.`); + process.exit(1); + } +} + +async function getGitHubMiloLibsBranchLiveUrl() { + const repository = process.env.GITHUB_REPOSITORY; + const prBranchLiveUrl = process.env.PR_BRANCH_MILOLIBS_LIVE_URL; + const miloLibs = process.env.MILO_LIBS; + + try { + if (await isBranchURLValid(prBranchLiveUrl)) { + process.env.PR_BRANCH_LIVE_URL = prBranchLiveUrl; + } + console.info('PR Repository : ', repository); + console.info('PR Branch live url : ', prBranchLiveUrl); + console.info('Milo Libs : ', miloLibs); + } catch (err) { + console.error(`Error => Error in setting PR Branch test URL : ${prBranchLiveUrl}`); + console.info(`Note: PR branch test url ${prBranchLiveUrl} is not valid, Exiting test execution.`); + process.exit(1); + } +} + +async function getCircleCIBranchLiveUrl() { + const stageBranchLiveUrl = STAGE_BRANCH_URL; + + try { + if (await isBranchURLValid(stageBranchLiveUrl)) { + process.env.PR_BRANCH_LIVE_URL = stageBranchLiveUrl; + } + console.info('Stage Branch Live URL : ', stageBranchLiveUrl); + } catch (err) { + console.error('Error => Error in setting Stage Branch test URL : ', stageBranchLiveUrl); + console.info('Note: Stage branch test url is not valid, Exiting test execution.'); + process.exit(1); + } +} + +async function getLocalBranchLiveUrl() { + let localTestLiveUrl; + try { + const localGitRootDir = execSync('git rev-parse --show-toplevel', { encoding: 'utf-8' }).trim(); + + if (localGitRootDir) { + const gitRemoteOriginUrl = execSync('git config --get remote.origin.url', { cwd: localGitRootDir, encoding: 'utf-8' }).trim(); + const match = gitRemoteOriginUrl.match(/github\.com\/(.*?)\/(.*?)\.git/); + + if (match) { + const [localOrg, localRepo] = match.slice(1, 3); + const localBranch = execSync('git rev-parse --abbrev-ref HEAD', { cwd: localGitRootDir, encoding: 'utf-8' }).trim(); + localTestLiveUrl = process.env.LOCAL_TEST_LIVE_URL || MAIN_BRANCH_LIVE_URL; + if (await isBranchURLValid(localTestLiveUrl)) { + console.info('Git ORG : ', localOrg); + console.info('Git REPO : ', localRepo); + console.info('Local Branch : ', localBranch); + console.info('Local Test Live URL : ', localTestLiveUrl); + } + } + } + } catch (error) { + console.error(`Error => Error in setting local test URL : ${localTestLiveUrl}\n`); + console.info('Note: Local or branch test url is not valid, Exiting test execution.\n'); + process.exit(1); + } +} + +async function globalSetup() { + console.info('---- Executing Nala Global setup ----\n'); + + if (process.env.GITHUB_ACTIONS === 'true') { + console.info('---- Running Nala Tests in the GitHub environment ----\n'); + + if (process.env.MILO_LIBS_RUN === 'true') { + await getGitHubMiloLibsBranchLiveUrl(); + } else { + await getGitHubPRBranchLiveUrl(); + } + } else if (process.env.CIRCLECI) { + console.info('---- Running Nala Tests in the CircleCI environment ----\n'); + await getCircleCIBranchLiveUrl(); + } else { + console.info('---- Running Nala Tests in the Local environment ----\n'); + await getLocalBranchLiveUrl(); + } +} + +export default globalSetup; diff --git a/nala/utils/nala.run.js b/nala/utils/nala.run.js new file mode 100644 index 0000000000..11d1354006 --- /dev/null +++ b/nala/utils/nala.run.js @@ -0,0 +1,181 @@ +#!/usr/bin/env node + +/* eslint-disable no-console */ + +const { spawn } = require('child_process'); + +function displayHelp() { + console.log(` + +\x1b[1m\x1b[37m## Nala command:\x1b[0m \x1b[1m\x1b[32mnpm run nala [env] [options]\x1b[0m + +\x1b[1m1] Env:\x1b[0m [\x1b[32mlocal\x1b[0m | \x1b[32mlibs\x1b[0m | \x1b[32mbranch\x1b[0m | \x1b[32mstage\x1b[0m | \x1b[32metc\x1b[0m ] \x1b[3mdefault: local\x1b[0m + +\x1b[1m2] Options:\x1b[0m + + \x1b[33m* browser=\x1b[0m Browser to use (default: chrome) + \x1b[33m* device=\x1b[0m Device (default: desktop) + \x1b[33m* test=<.test.js>\x1b[0m Test file to run (default: all tests) + \x1b[33m* tag=<@tag>\x1b[0m Tags to filter tests by annotations ex: @test1 @accordion @marquee + \x1b[33m* -g, --g=<@tag>\x1b[0m Tags to filter tests by annotations ex: @test1 @accordion @marquee + \x1b[33m* mode=\x1b[0m Mode (default: headless) + \x1b[33m* config=\x1b[0m Configuration file (default: Playwright default) + \x1b[33m* project=\x1b[0m Project configuration (default: milo-live-chromium) + \x1b[33m* milolibs=\x1b[0m Milo library environment (default: none) + +\x1b[1mExamples:\x1b[0m + | \x1b[36mCommand\x1b[0m | \x1b[36mDescription\x1b[0m | + |--------------------------------------------------------|------------------------------------------------------------------------------------| + | npm run nala local | Runs all nala tests on local environment on chrome browser | + | npm run nala local accordion.test.js | Runs only accordion tests on local environment on chrome browser | + | npm run nala local @accordion | Runs only accordion annotated/tagged tests on local environment on chrome browser | + | npm run nala local @accordion browser=firefox | Runs only accordion annotated/tagged tests on local environment on firefox browser | + | npm run nala local mode=ui | Runs all nala tests on local environment in UI mode on chrome browser | + | npm run nala local tags=@tag1,@tag2 | Runs tests annotated with @tag1 and @tag2 on local environment on chrome browser | + +\x1b[1mDebugging:\x1b[0m +----------- + | \x1b[36mCommand\x1b[0m | \x1b[36mDescription\x1b[0m | + |--------------------------------------------------------|------------------------------------------------------------------------------------| + | npm run nala local @test1 mode=debug | Runs @test1 on local environment in debug mode | + +`); +} + +function parseArgs(args) { + const defaultParams = { + env: 'local', + browser: 'chromium', + device: 'desktop', + test: '', + tag: '', + mode: 'headless', + config: '', + project: '', + milolibs: '', + }; + + const parsedParams = { ...defaultParams }; + + args.forEach((arg) => { + if (arg.includes('=')) { + const [key, value] = arg.split('='); + parsedParams[key] = value; + } else if (arg.startsWith('-g') || arg.startsWith('--g')) { + const value = arg.includes('=') ? arg.split('=')[1] : args[args.indexOf(arg) + 1]; + parsedParams.tag = value; + } else if (arg.startsWith('@')) { + parsedParams.tag += parsedParams.tag ? ` ${arg.substring(1)}` : arg.substring(1); + } else if (arg.endsWith('.test.js')) { + parsedParams.test = arg; + } else if (arg.endsWith('.config.js')) { + parsedParams.config = arg; + } else if (['ui', 'debug', 'headless', 'headed'].includes(arg)) { + parsedParams.mode = arg; + } else { + parsedParams.env = arg; + } + }); + + // Set the project if not provided + if (!parsedParams.project) { + parsedParams.project = `milo-live-${parsedParams.browser}`; + } + + return parsedParams; +} + +function getLocalTestLiveUrl(env, milolibs) { + if (milolibs) { + process.env.MILO_LIBS = `?milolibs=${milolibs}`; + if (env === 'local') { + return 'http://127.0.0.1:3000'; + } if (env === 'libs') { + return 'http://127.0.0.1:6456'; + } + return `https://${env}--milo--adobecom.hlx.live`; + } + if (env === 'local') { + return 'http://127.0.0.1:3000'; + } if (env === 'libs') { + return 'http://127.0.0.1:6456'; + } + return `https://${env}--milo--adobecom.hlx.live`; +} + +function buildPlaywrightCommand(parsedParams, localTestLiveUrl) { + const { + browser, device, test, tag, mode, config, project, + } = parsedParams; + + const envVariables = { + ...process.env, + BROWSER: browser, + DEVICE: device, + HEADLESS: mode === 'headless' || mode === 'headed' ? 'true' : 'false', + LOCAL_TEST_LIVE_URL: localTestLiveUrl, + }; + + const command = 'npx playwright test'; + const options = []; + + if (test) { + options.push(test); + } + + options.push(`--project=${project}`); + options.push('--grep-invert nopr'); + + if (tag) { + options.push(`-g "${tag.replace(/,/g, ' ')}"`); + } + + if (mode === 'ui' || mode === 'headed') { + options.push('--headed'); + } else if (mode === 'debug') { + options.push('--debug'); + } + + if (config) { + options.push(`--config=${config}`); + } + + return { finalCommand: `${command} ${options.join(' ')}`, envVariables }; +} + +function runNalaTest() { + const args = process.argv.slice(2); + + if (args.length === 0 || args.includes('help')) { + displayHelp(); + process.exit(0); + } + + const parsedParams = parseArgs(args); + const localTestLiveUrl = getLocalTestLiveUrl(parsedParams.env, parsedParams.milolibs); + const { finalCommand, envVariables } = buildPlaywrightCommand(parsedParams, localTestLiveUrl); + + console.log(`\n Executing nala run command: ${finalCommand}`); + console.log(`\n Using URL: ${localTestLiveUrl}\n`); + console.log(`\n\x1b[1m\x1b[33mExecuting nala run command:\x1b[0m \x1b[32m${finalCommand}\x1b[0m\n\x1b[1m\x1b[33mUsing URL:\x1b[0m \x1b[32m${localTestLiveUrl}\x1b[0m\n`); + + const testProcess = spawn(finalCommand, { stdio: 'inherit', shell: true, env: envVariables }); + + testProcess.on('close', (code) => { + // eslint-disable-next-line no-console + console.log(`Nala tests exited with code ${code}`); + process.exit(code); + }); +} + +if (require.main === module) { + runNalaTest(); +} + +module.exports = { + displayHelp, + parseArgs, + getLocalTestLiveUrl, + buildPlaywrightCommand, + runNalaTest, +}; diff --git a/nala/utils/slack.js b/nala/utils/slack.js new file mode 100644 index 0000000000..f6283f6272 --- /dev/null +++ b/nala/utils/slack.js @@ -0,0 +1,21 @@ +/* eslint-disable import/no-extraneous-dependencies, no-console */ + +import axios from 'axios'; + +/** + * Sends a message to Slack using a webhook URL. + * @param {string} webhookUrl - The Slack channel webhook. + * @param {Object} messageContent - The content of the message to send. + */ +export default async function sendSlackMessage(webhookUrl, messageContent) { + try { + const response = await axios.post(webhookUrl, messageContent, { headers: { 'Content-Type': 'application/json' } }); + + if (response.status !== 200) { + throw new Error(`Error sending message to Slack. Status: ${response.status}. Message: ${response.data}`); + } + console.log('---Result summary is sent to slack---'); + } catch (error) { + console.error('Axios error:', error); + } +} diff --git a/package-lock.json b/package-lock.json index ddfc83e018..af5cc902a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,10 +18,12 @@ "@babel/eslint-parser": "7.17.0", "@esm-bundle/chai": "4.3.4-fix.0", "@octokit/rest": "^20.0.2", + "@playwright/test": "^1.46.1", "@web/dev-server-import-maps": "^0.2.1", "@web/test-runner": "^0.18.2", "@web/test-runner-commands": "^0.9.0", "@web/test-runner-playwright": "^0.11.0", + "axios": "^1.7.5", "chai": "^5.1.1", "eslint": "8.11.0", "eslint-config-airbnb-base": "15.0.0", @@ -2293,6 +2295,21 @@ "@octokit/openapi-types": "^22.2.0" } }, + "node_modules/@playwright/test": { + "version": "1.46.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.1.tgz", + "integrity": "sha512-Fq6SwLujA/DOIvNC2EL/SojJnkKf/rAwJ//APpJJHRyMi1PdKrY3Az+4XNQ51N4RTbItbIByQ0jgd1tayq1aeA==", + "dev": true, + "dependencies": { + "playwright": "1.46.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@preact/signals": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@preact/signals/-/signals-1.0.4.tgz", @@ -3523,6 +3540,12 @@ "tslib": "^2.4.0" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, "node_modules/asyncro": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/asyncro/-/asyncro-3.0.0.tgz", @@ -3581,6 +3604,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/b4a": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", @@ -4375,6 +4409,18 @@ "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/command-line-args": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", @@ -4959,6 +5005,15 @@ "node": ">= 14" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -6513,6 +6568,26 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -6522,6 +6597,20 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -9937,12 +10026,12 @@ } }, "node_modules/playwright": { - "version": "1.45.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.2.tgz", - "integrity": "sha512-ReywF2t/0teRvNBpfIgh5e4wnrI/8Su8ssdo5XsQKpjxJj+jspm00jSoz9BTg91TT0c9HRjXO7LBNVrgYj9X0g==", + "version": "1.46.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.1.tgz", + "integrity": "sha512-oPcr1yqoXLCkgKtD5eNUPLiN40rYEM39odNpIb6VE6S7/15gJmA1NzVv6zJYusV0e7tzvkU/utBFNa/Kpxmwng==", "dev": true, "dependencies": { - "playwright-core": "1.45.2" + "playwright-core": "1.46.1" }, "bin": { "playwright": "cli.js" @@ -9955,9 +10044,9 @@ } }, "node_modules/playwright-core": { - "version": "1.45.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.2.tgz", - "integrity": "sha512-ha175tAWb0dTK0X4orvBIqi3jGEt701SMxMhyujxNrgd8K0Uy5wMSwwcQHtyB4om7INUkfndx02XnQ2p6dvLDw==", + "version": "1.46.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.1.tgz", + "integrity": "sha512-h9LqIQaAv+CYvWzsZ+h3RsrqCStkBHlgo6/TJlFst3cOTlLghBQlJwPOZKQJTKNaD3QIB7aAVQ+gfWbN3NXB7A==", "dev": true, "bin": { "playwright-core": "cli.js" diff --git a/package.json b/package.json index cb1a4d78d5..d780674eed 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "test:watch": "npx playwright install && npm test -- --watch", "test:file": "npx playwright install && wtr --config ./web-test-runner.config.mjs --node-resolve --port=2000 --coverage", "test:file:watch": "npx playwright install && wtr --config ./web-test-runner.config.mjs --node-resolve --port=2000 --coverage --watch", + "nala": "node nala/utils/nala.run.js", "libs": "aem up --port=6456", "lint": "npm run lint:js && npm run lint:css", "lint:js": "eslint .", @@ -37,10 +38,12 @@ "@babel/eslint-parser": "7.17.0", "@esm-bundle/chai": "4.3.4-fix.0", "@octokit/rest": "^20.0.2", + "@playwright/test": "^1.46.1", "@web/dev-server-import-maps": "^0.2.1", "@web/test-runner": "^0.18.2", "@web/test-runner-commands": "^0.9.0", "@web/test-runner-playwright": "^0.11.0", + "axios": "^1.7.5", "chai": "^5.1.1", "eslint": "8.11.0", "eslint-config-airbnb-base": "15.0.0", diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 0000000000..e07668f1c0 --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,71 @@ +/* eslint-disable import/no-extraneous-dependencies */ + +const { devices } = require('@playwright/test'); + +/** + * @see https://playwright.dev/docs/test-configuration + * @type {import('@playwright/test').PlaywrightTestConfig} + */ +const config = { + testDir: './nala', + outputDir: './test-results', + globalSetup: './nala/utils/global.setup.js', + /* Maximum time one test can run for. */ + timeout: 30 * 1000, + expect: { + /** + * Maximum time expect() should wait for the condition to be met. + * For example in `await expect(locator).toHaveText();` + */ + timeout: 5000, + }, + testMatch: '**/*.test.js', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 1 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 4 : 3, + /* Reporter to use. */ + reporter: process.env.CI + ? [['github'], ['list'], ['./nala/utils/base-reporter.js']] + : [['html', { outputFolder: 'test-html-results' }], ['list'], ['./nala/utils/base-reporter.js']], + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ + actionTimeout: 60000, + + trace: 'on-first-retry', + baseURL: process.env.PR_BRANCH_LIVE_URL || (process.env.LOCAL_TEST_LIVE_URL || 'https://main--milo--adobecom.hlx.live'), + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'milo-live-chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + { + name: 'milo-live-firefox', + use: { ...devices['Desktop Firefox'] }, + }, + { + name: 'milo-live-webkit', + use: { ...devices['Desktop Safari'] }, + }, + /* Test Against Mobile View ports */ + { + name: 'mobile-chrome-pixel5', + use: { ...devices['Pixel 5'] }, + }, + { + name: 'mobile-safari-iPhone12', + use: { ...devices['iPhone 12'] }, + }, + ], +}; + +module.exports = config; From fc587078863a8b8a0bb92c6536fc4fe66958cced Mon Sep 17 00:00:00 2001 From: Sean Choi Date: Mon, 2 Sep 2024 17:18:32 +0900 Subject: [PATCH 16/66] [MWPW-157556] Eagerly import lit from merch-card block (#2809) Eagerly import lit from merch-card block --- libs/blocks/merch-card/merch-card.js | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/blocks/merch-card/merch-card.js b/libs/blocks/merch-card/merch-card.js index e7bf71ca32..2517abfb17 100644 --- a/libs/blocks/merch-card/merch-card.js +++ b/libs/blocks/merch-card/merch-card.js @@ -3,6 +3,7 @@ import { getConfig, createTag, loadStyle } from '../../utils/utils.js'; import { getMetadata } from '../section-metadata/section-metadata.js'; import { processTrackingLabels } from '../../martech/attributes.js'; import '../../deps/mas/merch-card.js'; +import '../../deps/lit-all.min.js'; const TAG_PATTERN = /^[a-zA-Z0-9_-]+:[a-zA-Z0-9_-]+\/[a-zA-Z0-9_-].*$/; From 582c244cd194a660859a71a25f379e5dcf35b6ab Mon Sep 17 00:00:00 2001 From: Brandon Marshall Date: Mon, 2 Sep 2024 01:21:12 -0700 Subject: [PATCH 17/66] MWPW-157005 Marketo thank you fixes (#2779) * MWPW-157005 Marketo thank you fixes * PR Changes --- libs/blocks/marketo/marketo.css | 37 ++++++++++++++++------ libs/blocks/marketo/marketo.js | 21 ++++++++++-- test/blocks/marketo/marketo.test.html | 6 +++- test/blocks/marketo/mocks/marketo-utils.js | 4 +++ 4 files changed, 55 insertions(+), 13 deletions(-) diff --git a/libs/blocks/marketo/marketo.css b/libs/blocks/marketo/marketo.css index 5d468a288f..130a6848c7 100644 --- a/libs/blocks/marketo/marketo.css +++ b/libs/blocks/marketo/marketo.css @@ -4,18 +4,25 @@ --marketo-form-border: #6E6E6E; --marketo-form-error: #D7373F; --marketo-form-selected: #0265DC; + --marketo-form-placeholder-height: calc(78px * 3 + 57px); /* 3 rows + submit */ + --marketo-form-min-height: 215px; + --marketo-form-max-height: 10000px; } /* stylelint-disable selector-class-pattern */ .marketo .marketo-form-wrapper { box-sizing: border-box; max-width: 600px; - min-height: 400px; + min-height: var(--marketo-form-min-height); margin: 0 auto; padding: var(--spacing-l) 30px; background-color: var(--marketo-form-background); } +.marketo.loading form { + min-height: var(--marketo-form-placeholder-height); +} + .marketo .marketo-title { font-size: 28px; font-weight: bold; @@ -29,25 +36,37 @@ } .marketo .mktoForm { + display: flex; + flex-flow: row wrap; + justify-content: space-between; + column-gap: 4.6%; + align-items: flex-start; + max-height: var(--marketo-form-placeholder-height); + overflow: hidden; font-size: 16px; font-weight: bold; width: 100%!important; + visibility: hidden; + opacity: 0; } -.marketo form.mktoForm--fade-in { +.marketo.success .marketo-title, +.marketo.success .marketo-description, +.marketo.success form.mktoVisible.mktoForm { + display: none; +} + +.marketo form.mktoForm.mktoForm--fade-in { + transition: opacity 0.5s ease-in, max-height 0.5s ease-in; visibility: hidden; opacity: 0; } -.marketo form.mktoForm--fade-in.mktoVisible { - display: flex; - flex-flow: row wrap; - justify-content: space-between; - column-gap: 4.6%; - align-items: flex-start; +.marketo form.mktoForm.mktoForm--fade-in.mktoVisible { + max-height: var(--marketo-form-max-height); + overflow: visible; visibility: visible; opacity: 1; - transition: opacity 1s ease-in, height 1s ease-in; } .marketo .mktoFormRow.mktoFormRowTop.comments, diff --git a/libs/blocks/marketo/marketo.js b/libs/blocks/marketo/marketo.js index 1602d0694a..d558cfcdba 100644 --- a/libs/blocks/marketo/marketo.js +++ b/libs/blocks/marketo/marketo.js @@ -13,9 +13,16 @@ /* * Marketo Form */ -import { parseEncodedConfig, loadScript, localizeLink, createTag, createIntersectionObserver } from '../../utils/utils.js'; - -const ROOT_MARGIN = 1000; +import { + parseEncodedConfig, + loadScript, + loadLink, + localizeLink, + createTag, + createIntersectionObserver, +} from '../../utils/utils.js'; + +const ROOT_MARGIN = 50; const FORM_ID = 'form id'; const BASE_URL = 'marketo host'; const MUNCHKIN_ID = 'marketo munckin'; @@ -61,6 +68,7 @@ export const decorateURL = (destination, baseURL = window.location) => { return destinationUrl.href; } catch (e) { + /* c8 ignore next 4 */ window.lana?.log(`Error with Marketo destination URL: ${destination} ${e.message}`); } @@ -84,9 +92,11 @@ export const setPreferences = (formData) => { }; export const formSuccess = (formEl, formData) => { + const el = formEl.closest('.marketo'); const parentModal = formEl?.closest('.dialog-modal'); const mktoSubmit = new Event('mktoSubmit'); + el.classList.add('success'); window.dispatchEvent(mktoSubmit); window.mktoSubmitted = true; @@ -115,7 +125,9 @@ export const formSuccess = (formEl, formData) => { const readyForm = (form, formData) => { const formEl = form.getFormElem().get(0); + const el = formEl.closest('.marketo'); const isDesktop = matchMedia('(min-width: 900px)'); + el.classList.remove('loading'); formEl.addEventListener('focus', ({ target }) => { /* c8 ignore next 9 */ @@ -216,6 +228,9 @@ export default function init(el) { fragment.append(formWrapper); el.replaceChildren(fragment); + el.classList.add('loading'); + + loadLink(`https://${baseURL}`, { rel: 'dns-prefetch' }); createIntersectionObserver({ el, diff --git a/test/blocks/marketo/marketo.test.html b/test/blocks/marketo/marketo.test.html index 862ee6f6e1..20556d343a 100644 --- a/test/blocks/marketo/marketo.test.html +++ b/test/blocks/marketo/marketo.test.html @@ -65,7 +65,7 @@

Fill out the form to view the repo import { expect } from '@esm-bundle/chai'; import { stub } from 'sinon'; import { waitForElement } from '../../helpers/waitfor.js'; - import { loadScript, parseEncodedConfig } from '../../../libs/utils/utils.js'; + import { loadScript, loadLink, parseEncodedConfig } from '../../../libs/utils/utils.js'; import init, { setPreferences, formValidate, formSuccess } from '../../../libs/blocks/marketo/marketo.js'; runTests(() => { @@ -86,6 +86,10 @@

Fill out the form to view the repo expect(desc).to.exist; }); + it('preload Marketo', async () => { + expect(loadLink.calledOnce).to.be.true; + }); + it('loads Marketo script', async () => { expect(loadScript.calledOnce).to.be.true; expect(loadScript.calledWith('https://engage.adobe.com/js/forms2/js/forms2.min.js')).to.be.true; diff --git a/test/blocks/marketo/mocks/marketo-utils.js b/test/blocks/marketo/mocks/marketo-utils.js index 0c98787010..9cf1cf9f9a 100644 --- a/test/blocks/marketo/mocks/marketo-utils.js +++ b/test/blocks/marketo/mocks/marketo-utils.js @@ -63,3 +63,7 @@ export function createIntersectionObserver({ el, callback /* , once = true, opti } export const localizeLink = (href) => href; + +export const loadLink = stub().returns(new Promise((resolve) => { + resolve(); +})); From 3331e1170c529e0d6973337556d3038492e643c3 Mon Sep 17 00:00:00 2001 From: Rares Munteanu Date: Mon, 2 Sep 2024 10:22:15 +0200 Subject: [PATCH 18/66] [MWPW-148140] Adjust table strikethrough pricing (#2786) --- libs/blocks/table/table.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libs/blocks/table/table.css b/libs/blocks/table/table.css index 87836b1ae9..5087a46867 100644 --- a/libs/blocks/table/table.css +++ b/libs/blocks/table/table.css @@ -247,6 +247,11 @@ padding: var(--spacing-xxs) 0; } +.table .row-heading .col-heading .pricing .price-strikethrough { + display: inline-block; + font-size: var(--type-body-s-size); +} + /* section */ .table .divider { display: none; From 3ad052c4874a04d2730eb317fd0113673bd51444 Mon Sep 17 00:00:00 2001 From: Okan Sahin <39759830+mokimo@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:19:43 +0200 Subject: [PATCH 19/66] Parallelize Placeholder loading for an LCP improvement (#2752) mwpw-156840: parallelize loading placeholders --- libs/blocks/chart/chart.js | 3 +- libs/blocks/fragment/fragment.js | 3 +- libs/features/placeholders.js | 65 ++++++++++++++-------- libs/utils/helpers.js | 9 --- libs/utils/utils.js | 42 +++++++++----- test/blocks/caas/mocks/utils.js | 2 + test/blocks/instagram/mocks/embed-utils.js | 2 + test/blocks/marketo/mocks/marketo-utils.js | 2 + test/blocks/merch/mocks/embed-utils.js | 2 + test/blocks/ost/mocks/ost-utils.js | 14 ++++- test/blocks/slideshare/mocks/utils.js | 2 +- test/utils/helpers.test.js | 19 ------- test/utils/utils.test.js | 9 ++- 13 files changed, 100 insertions(+), 74 deletions(-) delete mode 100644 test/utils/helpers.test.js diff --git a/libs/blocks/chart/chart.js b/libs/blocks/chart/chart.js index c2a5a23be5..295e9b52a6 100644 --- a/libs/blocks/chart/chart.js +++ b/libs/blocks/chart/chart.js @@ -1,4 +1,4 @@ -import { loadScript, getConfig, createTag } from '../../utils/utils.js'; +import { loadScript, getConfig, createTag, customFetch } from '../../utils/utils.js'; import { throttle, parseValue, @@ -122,7 +122,6 @@ export function processMarkData(series, xUnit) { } export async function fetchData(link) { - const { customFetch } = await import('../../utils/helpers.js'); const resp = await customFetch({ resource: link.href.toLowerCase(), withCacheRules: true }) .catch(() => ({})); diff --git a/libs/blocks/fragment/fragment.js b/libs/blocks/fragment/fragment.js index 84a922d6f5..7e740c50c3 100644 --- a/libs/blocks/fragment/fragment.js +++ b/libs/blocks/fragment/fragment.js @@ -1,5 +1,5 @@ /* eslint-disable max-classes-per-file */ -import { createTag, getConfig, loadArea, localizeLink } from '../../utils/utils.js'; +import { createTag, getConfig, loadArea, localizeLink, customFetch } from '../../utils/utils.js'; const fragMap = {}; @@ -83,7 +83,6 @@ export default async function init(a) { return; } - const { customFetch } = await import('../../utils/helpers.js'); let resourcePath = a.href; if (a.href.includes('/federal/')) { const { getFederatedUrl } = await import('../../utils/federated.js'); diff --git a/libs/features/placeholders.js b/libs/features/placeholders.js index c782e046a9..5898fef5c0 100644 --- a/libs/features/placeholders.js +++ b/libs/features/placeholders.js @@ -1,3 +1,5 @@ +import { customFetch, getConfig } from '../utils/utils.js'; + const fetchedPlaceholders = {}; window.mph = {}; @@ -7,26 +9,23 @@ const getPlaceholdersPath = (config, sheet) => { return `${path}${query}`; }; -const fetchPlaceholders = async (config, sheet) => { - const placeholdersPath = getPlaceholdersPath(config, sheet); - const { customFetch } = await import('../utils/helpers.js'); - - fetchedPlaceholders[placeholdersPath] = fetchedPlaceholders[placeholdersPath] - // eslint-disable-next-line no-async-promise-executor - || new Promise(async (resolve) => { - const resp = await customFetch({ resource: placeholdersPath, withCacheRules: true }) - .catch(() => ({})); - const json = resp.ok ? await resp.json() : { data: [] }; - if (json.data.length === 0) { resolve({}); return; } - const placeholders = {}; - json.data.forEach((item) => { - placeholders[item.key] = item.value; - window.mph[item.key] = item.value; - }); - resolve(placeholders); +const fetchPlaceholders = async ({ config, sheet, placeholderRequest, placeholderPath }) => { + const path = placeholderPath || getPlaceholdersPath(config, sheet); + // eslint-disable-next-line no-async-promise-executor + fetchedPlaceholders[path] = fetchedPlaceholders[path] || new Promise(async (resolve) => { + const resp = await placeholderRequest || await customFetch( + { resource: path, withCacheRules: true }, + ).catch(() => ({})); + const json = resp.ok ? await resp.json() : { data: [] }; + if (json.data.length === 0) { resolve({}); return; } + const placeholders = {}; + json.data.forEach((item) => { + window.mph[item.key] = item.value; + placeholders[item.key] = item.value; }); - - return fetchedPlaceholders[placeholdersPath]; + resolve(placeholders); + }); + return fetchedPlaceholders[path]; }; function keyToStr(key) { @@ -60,7 +59,7 @@ async function getPlaceholder(key, config, sheet) { }, }; - const defaultPlaceholders = await fetchPlaceholders(defaultConfig, sheet) + const defaultPlaceholders = await fetchPlaceholders({ config: defaultConfig, sheet }) .catch(() => ({})); defaultFetched = true; return defaultPlaceholders; @@ -68,7 +67,7 @@ async function getPlaceholder(key, config, sheet) { if (config.placeholders?.[key]) return config.placeholders[key]; - const placeholders = await fetchPlaceholders(config, sheet).catch(async () => { + const placeholders = await fetchPlaceholders({ config, sheet }).catch(async () => { const defaultPlaceholders = await getDefaultPlaceholders(); return defaultPlaceholders; }); @@ -102,14 +101,19 @@ export async function replaceKeyArray(keys, config, sheet = 'default') { return placeholders; } -export async function replaceText(text, config, regex = /{{(.*?)}}|%7B%7B(.*?)%7D%7D/g, sheet = 'default') { +export async function replaceText( + text, + config, + regex = /{{(.*?)}}|%7B%7B(.*?)%7D%7D/g, + sheet = 'default', +) { if (typeof text !== 'string' || !text.length) return ''; const matches = [...text.matchAll(new RegExp(regex))]; if (!matches.length) { return text; } - const keys = Array.from(matches, (match) => (match[1] || match[2])); + const keys = Array.from(matches, (match) => match[1] || match[2]); const placeholders = await replaceKeyArray(keys, config, sheet); // The .shift method is very slow, thus using normal iterator let i = 0; @@ -117,3 +121,18 @@ export async function replaceText(text, config, regex = /{{(.*?)}}|%7B%7B(.*?)%7 const finalText = text.replaceAll(regex, () => placeholders[i++]); return finalText; } + +export async function decoratePlaceholderArea({ + placeholderPath, + placeholderRequest, + nodes, +}) { + 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'); + }); + await Promise.all(replaceNodes); +} diff --git a/libs/utils/helpers.js b/libs/utils/helpers.js index 7e6fc5ad62..73274a4988 100644 --- a/libs/utils/helpers.js +++ b/libs/utils/helpers.js @@ -19,12 +19,3 @@ export function updateLinkWithLangRoot(link) { return link; } } - -export async function customFetch({ resource, withCacheRules }) { - const options = {}; - if (withCacheRules) { - const params = new URLSearchParams(window.location.search); - options.cache = params.get('cache') === 'off' ? 'reload' : 'default'; - } - return fetch(resource, options); -} diff --git a/libs/utils/utils.js b/libs/utils/utils.js index 6931da2a23..ede1d199c0 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -727,15 +727,25 @@ async function decorateIcons(area, config) { await loadIcons(icons, config); } -async function decoratePlaceholders(area, config) { - const el = area.querySelector('main') || area; +export async function customFetch({ resource, withCacheRules }) { + const options = {}; + if (withCacheRules) { + const params = new URLSearchParams(window.location.search); + options.cache = params.get('cache') === 'off' ? 'reload' : 'default'; + } + return fetch(resource, options); +} + +const findReplaceableNodes = (area) => { const regex = /{{(.*?)}}|%7B%7B(.*?)%7D%7D/g; const walker = document.createTreeWalker( - el, + area, NodeFilter.SHOW_TEXT, { acceptNode(node) { - const a = regex.test(node.nodeValue) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; + const a = regex.test(node.nodeValue) + ? NodeFilter.FILTER_ACCEPT + : NodeFilter.FILTER_REJECT; regex.lastIndex = 0; return a; }, @@ -747,13 +757,20 @@ async function decoratePlaceholders(area, config) { nodes.push(node); node = walker.nextNode(); } + return nodes; +}; + +let placeholderRequest; +async function decoratePlaceholders(area, config) { + if (!area) return; + const nodes = findReplaceableNodes(area); if (!nodes.length) return; - const { replaceText } = await import('../features/placeholders.js'); - const replaceNodes = nodes.map(async (textNode) => { - textNode.nodeValue = await replaceText(textNode.nodeValue, config, regex); - textNode.nodeValue = textNode.nodeValue.replace(/ /g, '\u00A0'); - }); - await Promise.all(replaceNodes); + const placeholderPath = `${config.locale?.contentRoot}/placeholders.json`; + placeholderRequest = placeholderRequest + || customFetch({ resource: placeholderPath, withCacheRules: true }) + .catch(() => ({})); + const { decoratePlaceholderArea } = await import('../features/placeholders.js'); + await decoratePlaceholderArea({ placeholderPath, placeholderRequest, nodes }); } async function loadFooter() { @@ -987,6 +1004,7 @@ async function checkForPageMods() { } async function loadPostLCP(config) { + await decoratePlaceholders(document.body.querySelector('header'), config); if (config.mep?.targetEnabled === 'gnav') { /* c8 ignore next 2 */ const { init } = await import('../features/personalization/personalization.js'); @@ -1174,11 +1192,11 @@ async function processSection(section, config, isDoc) { const { default: loadInlineFrags } = await import('../blocks/fragment/fragment.js'); const fragPromises = inlineFrags.map((link) => loadInlineFrags(link)); await Promise.all(fragPromises); - await decoratePlaceholders(section.el, config); const newlyDecoratedSection = decorateSection(section.el, section.idx); section.blocks = newlyDecoratedSection.blocks; section.preloadLinks = newlyDecoratedSection.preloadLinks; } + await decoratePlaceholders(section.el, config); if (section.preloadLinks.length) { const [modals, nonModals] = partition(section.preloadLinks, (block) => block.classList.contains('modal')); @@ -1215,8 +1233,6 @@ export async function loadArea(area = document) { } const config = getConfig(); - await decoratePlaceholders(area, config); - if (isDoc) { decorateDocumentExtras(); } diff --git a/test/blocks/caas/mocks/utils.js b/test/blocks/caas/mocks/utils.js index 65de883798..5736d0e567 100644 --- a/test/blocks/caas/mocks/utils.js +++ b/test/blocks/caas/mocks/utils.js @@ -12,6 +12,8 @@ export const utf8ToB64 = (str) => window.btoa(unescape(encodeURIComponent(str))) export const b64ToUtf8 = (str) => decodeURIComponent(escape(window.atob(str))); +export const customFetch = stub(); + export function getMetadata(name, doc = document) { const attr = name && name.includes(':') ? 'property' : 'name'; const meta = doc.head.querySelector(`meta[${attr}="${name}"]`); diff --git a/test/blocks/instagram/mocks/embed-utils.js b/test/blocks/instagram/mocks/embed-utils.js index 4e37247e24..8f69fa3854 100644 --- a/test/blocks/instagram/mocks/embed-utils.js +++ b/test/blocks/instagram/mocks/embed-utils.js @@ -27,6 +27,8 @@ export function createTag(tag, attributes, html) { export const getConfig = () => ({}); +export const customFetch = stub(); + export const loadStyle = stub(); export const loadScript = stub(); diff --git a/test/blocks/marketo/mocks/marketo-utils.js b/test/blocks/marketo/mocks/marketo-utils.js index 9cf1cf9f9a..6aef9cfc38 100644 --- a/test/blocks/marketo/mocks/marketo-utils.js +++ b/test/blocks/marketo/mocks/marketo-utils.js @@ -67,3 +67,5 @@ export const localizeLink = (href) => href; export const loadLink = stub().returns(new Promise((resolve) => { resolve(); })); + +export const customFetch = stub(); diff --git a/test/blocks/merch/mocks/embed-utils.js b/test/blocks/merch/mocks/embed-utils.js index d5a171656a..9c956f7a7a 100644 --- a/test/blocks/merch/mocks/embed-utils.js +++ b/test/blocks/merch/mocks/embed-utils.js @@ -27,6 +27,8 @@ export const getConfig = () => config; export const setConfig = (c) => { config = c; }; +export const customFetch = stub(); + export const loadArea = stub(); export const loadScript = stub(); diff --git a/test/blocks/ost/mocks/ost-utils.js b/test/blocks/ost/mocks/ost-utils.js index e8f4ff9b28..d3dfa6a52f 100644 --- a/test/blocks/ost/mocks/ost-utils.js +++ b/test/blocks/ost/mocks/ost-utils.js @@ -22,7 +22,6 @@ function getMetadata(name, doc = document) { const loadScript = () => Promise.resolve(); const loadStyle = () => Promise.resolve(); - const mockRes = ({ payload, status = 200 } = {}) => new Promise((resolve) => { resolve({ status, @@ -32,7 +31,6 @@ const mockRes = ({ payload, status = 200 } = {}) => new Promise((resolve) => { text: () => payload, }); }); - function mockOstDeps({ failStatus = false, failMetadata = false, mockToken, overrideParams } = {}) { const options = { country: 'CH', @@ -100,6 +98,16 @@ function unmockOstDeps() { window.history.replaceState({}, '', ogUrl); } +const customFetch = window.fetch; + export { - getConfig, getLocale, getMetadata, loadScript, loadStyle, mockOstDeps, unmockOstDeps, mockRes, + getConfig, + getLocale, + getMetadata, + loadScript, + loadStyle, + mockOstDeps, + unmockOstDeps, + mockRes, + customFetch, }; diff --git a/test/blocks/slideshare/mocks/utils.js b/test/blocks/slideshare/mocks/utils.js index 5e173b915c..26eeff57ef 100644 --- a/test/blocks/slideshare/mocks/utils.js +++ b/test/blocks/slideshare/mocks/utils.js @@ -30,7 +30,7 @@ export const getConfig = () => ({}); export const loadStyle = stub(); export const loadScript = stub(); - +export const customFetch = stub(); export const utf8ToB64 = (str) => window.btoa(unescape(encodeURIComponent(str))); export function createIntersectionObserver({ el, callback /* , once = true, options = {} */ }) { diff --git a/test/utils/helpers.test.js b/test/utils/helpers.test.js deleted file mode 100644 index 2c8fb91ddb..0000000000 --- a/test/utils/helpers.test.js +++ /dev/null @@ -1,19 +0,0 @@ -import { expect } from '@esm-bundle/chai'; -import { stub } from 'sinon'; - -const { customFetch } = await import('../../libs/utils/helpers.js'); - -describe('Cache control', async () => { - it('fetches with cache param', async () => { - const paramsGet = stub(URLSearchParams.prototype, 'get'); - const fetchStub = stub(window, 'fetch'); - const goodResponse = { ok: true, json: () => true }; - const mockUrl = './mocks/taxonomy.json'; - paramsGet.withArgs('cache').returns('off'); - fetchStub.withArgs(mockUrl, { cache: 'reload' }).resolves(goodResponse); - const resp = await customFetch({ resource: mockUrl, withCacheRules: true }); - expect(resp.json()).to.be.true; - paramsGet.restore(); - fetchStub.restore(); - }); -}); diff --git a/test/utils/utils.test.js b/test/utils/utils.test.js index 57822bbe5f..430168e7a2 100644 --- a/test/utils/utils.test.js +++ b/test/utils/utils.test.js @@ -3,10 +3,9 @@ import { expect } from '@esm-bundle/chai'; import sinon from 'sinon'; import { waitFor, waitForElement } from '../helpers/waitfor.js'; import { mockFetch } from '../helpers/generalHelpers.js'; -import { createTag } from '../../libs/utils/utils.js'; +import { createTag, customFetch } from '../../libs/utils/utils.js'; const utils = {}; - const config = { codeRoot: '/libs', locales: { '': { ietf: 'en-US', tk: 'hah7vzn.css' } }, @@ -31,6 +30,12 @@ describe('Utils', () => { delete window.hlx; }); + it('fetches with cache param', async () => { + window.fetch = mockFetch({ payload: true }); + const resp = await customFetch({ resource: './mocks/taxonomy.json', withCacheRules: true }); + expect(resp.json()).to.be.true; + }); + describe('with body', () => { beforeEach(async () => { window.fetch = mockFetch({ payload: { data: '' } }); From 8b62e93dda56194996df775d4a6f77e8c7b1f068 Mon Sep 17 00:00:00 2001 From: Ryan Parrish Date: Mon, 2 Sep 2024 07:18:02 -0600 Subject: [PATCH 20/66] MWPW-151932 - Section metadata style grid enhancement for tablet VP (#2800) * poc for up-tablet * moved one-up to approp mq * remove redundant two-up-tablet. Use min-max defaults --- libs/blocks/section-metadata/section-metadata.css | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/libs/blocks/section-metadata/section-metadata.css b/libs/blocks/section-metadata/section-metadata.css index d053a42980..c027f794d5 100644 --- a/libs/blocks/section-metadata/section-metadata.css +++ b/libs/blocks/section-metadata/section-metadata.css @@ -248,12 +248,17 @@ main > .section[class*='-up'] > .content { .section.two-up.reverse-mobile > div:nth-child(2) { order: 1; } + } @media screen and (min-width: 600px) and (max-width: 1200px) { .section.five-up { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); } + + .section.one-up-tablet { grid-template-columns: 1fr; } + .section.three-up-tablet { grid-template-columns: repeat(3, 1fr); } + .section.four-up-tablet { grid-template-columns: repeat(4, 1fr); } .section.masonry-layout { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); @@ -264,15 +269,6 @@ main > .section[class*='-up'] > .content { } } -@media screen and (max-width: 900px) { - .section.two-up.one-up-tablet, - .section.three-up.one-up-tablet, - .section.four-up.one-up-tablet, - .section.five-up.one-up-tablet { - grid-template-columns: 1fr; - } -} - @media screen and (min-width: 720px) { .section.grid-width-6 { padding-left: calc((100vw - 600px) / 2); From bc61484271c68e85dff0224ae1125db4acae84e0 Mon Sep 17 00:00:00 2001 From: Siva S <163842332+sivasadobe@users.noreply.github.com> Date: Tue, 3 Sep 2024 13:43:24 +0530 Subject: [PATCH 21/66] MWPW-154448: Content should be in a data table but is not (#2667) * fix(a11y): added a11y two header tables * fix(heading): fixing the first row as columnheader * fix: heading more describable * fix(table): dynamic id added * fix: unique id for header body,pricing tag * chore: code opt * chore: eslint fix * fix(tab-focus): fixed key event in expand section * fix: review changes --- libs/blocks/table/table.js | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/libs/blocks/table/table.js b/libs/blocks/table/table.js index b3ffb88085..6f2213c57c 100644 --- a/libs/blocks/table/table.js +++ b/libs/blocks/table/table.js @@ -5,7 +5,7 @@ import { decorateButtons } from '../../utils/decorate.js'; const DESKTOP_SIZE = 900; const MOBILE_SIZE = 768; const tableHighlightLoadedEvent = new Event('milo:table:highlight:loaded'); - +let tableIndex = 0; function defineDeviceByScreenSize() { const screenWidth = window.innerWidth; if (screenWidth >= DESKTOP_SIZE) { @@ -19,7 +19,7 @@ function defineDeviceByScreenSize() { function handleHeading(table, headingCols) { const isPriceBottom = table.classList.contains('pricing-bottom'); - headingCols.forEach((col) => { + headingCols.forEach((col, i) => { col.classList.add('col-heading'); if (!col.innerHTML) return; @@ -65,6 +65,28 @@ function handleHeading(table, headingCols) { headingButton.appendChild(buttonsWrapper); col.append(headingContent, headingButton); } + + const trackingHeader = col.querySelector('.tracking-header'); + const nodeToApplyRoleScope = trackingHeader ?? col; + + if (trackingHeader) { + const trackingHeaderID = `t${tableIndex + 1}-c${i + 1}-header`; + trackingHeader.setAttribute('id', trackingHeaderID); + + const headerBody = col.querySelector('.body:not(.action-area)'); + headerBody?.setAttribute('id', `${trackingHeaderID}-body`); + + const headerPricing = col.querySelector('.pricing'); + headerPricing?.setAttribute('id', `${trackingHeaderID}-pricing`); + + const describedBy = `${headerBody?.id ?? ''} ${headerPricing?.id ?? ''}`.trim(); + trackingHeader.setAttribute('aria-describedby', describedBy); + + col.removeAttribute('role'); + } + + nodeToApplyRoleScope.setAttribute('role', 'columnheader'); + nodeToApplyRoleScope.setAttribute('scope', 'col'); }); } @@ -190,6 +212,8 @@ function handleSection(sectionParams) { if (!isMerch) { const sectionRowTitle = nextRowCols?.[0]; sectionRowTitle.classList.add('section-row-title'); + sectionRowTitle.setAttribute('role', 'rowheader'); + sectionRowTitle.setAttribute('scope', 'row'); } } else if (!row.classList.contains('row-1') && (!isHighlightTable || !row.classList.contains('row-2'))) { row.classList.add('section-row'); @@ -216,6 +240,8 @@ function handleSection(sectionParams) { const sectionRowTitle = rowCols[0]; handleTitleText(sectionRowTitle); sectionRowTitle.classList.add('section-row-title'); + sectionRowTitle.setAttribute('role', 'rowheader'); + sectionRowTitle.setAttribute('scope', 'row'); } } return expandSection; @@ -335,6 +361,7 @@ function applyStylesBasedOnScreenSize(table, originTable) { tableEl.querySelectorAll('.icon.expand').forEach((icon) => { icon.parentElement.classList.add('point-cursor'); icon.parentElement.addEventListener('click', () => handleExpand(icon)); + icon.parentElement.setAttribute('tabindex', 0); icon.parentElement.addEventListener('keydown', (e) => { e.preventDefault(); if (e.key === 'Enter' || e.key === ' ') handleExpand(icon); @@ -469,9 +496,6 @@ export default function init(el) { col.dataset.colIndex = cdx + 1; col.classList.add('col', `col-${cdx + 1}`); col.setAttribute('role', 'cell'); - if (col.innerHTML) { - col.tabIndex = 0; - } }); expandSection = handleSection(sectionParams); @@ -525,4 +549,6 @@ export default function init(el) { }); observer.observe(el); + + tableIndex++; } From 47dea19e01b8afb158b35c8e97a5b27b89b1e040 Mon Sep 17 00:00:00 2001 From: Dave Linhart <132396886+AdobeLinhart@users.noreply.github.com> Date: Tue, 3 Sep 2024 02:13:30 -0600 Subject: [PATCH 22/66] MWPW-156866 [MILO][MEP] Create martech metadata table if placeholders are used in non EN page (#2780) * Initial checkin - Working state. * Optimization. Good State. * Semi-colon fix. * Linting fixes. * Unit test file creation. * Placeholders update/fix. * Fixed US data values. * Unit testing WIP. * fix unit test * unit test working * add before each * fix linter issue of using same name above * update processTrackingLabels * add coverage to attributes.js unit test * Unit test updates. --------- Co-authored-by: vgoodric --- .../personalization/personalization.js | 22 ++++ libs/martech/attributes.js | 10 +- .../createMartechMetadata.test.js | 51 +++++++++ .../personalization/mocks/placeholders.js | 103 ++++++++++++++++++ test/martech/attributes.test.js | 9 ++ 5 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 test/features/personalization/createMartechMetadata.test.js create mode 100644 test/features/personalization/mocks/placeholders.js diff --git a/libs/features/personalization/personalization.js b/libs/features/personalization/personalization.js index df0f282ce8..29cfddd65d 100644 --- a/libs/features/personalization/personalization.js +++ b/libs/features/personalization/personalization.js @@ -582,6 +582,25 @@ export function parseManifestVariants(data, manifestPath, targetId) { return null; } +export async function createMartechMetadata(placeholders, config, column) { + if (config.locale.ietf === 'en-US') return; + + await import('../../martech/attributes.js').then(({ processTrackingLabels }) => { + config.mep.analyticLocalization ??= {}; + + placeholders.forEach((item, i) => { + const firstRow = placeholders[i]; + let usValue = firstRow['en-us'] || firstRow.us || firstRow.en || firstRow.key; + + if (!usValue) return; + + usValue = processTrackingLabels(usValue); + const translatedValue = processTrackingLabels(item[column]); + config.mep.analyticLocalization[translatedValue] = usValue; + }); + }); +} + /* c8 ignore start */ function parsePlaceholders(placeholders, config, selectedVariantName = '') { if (!placeholders?.length || selectedVariantName === 'default') return config; @@ -603,6 +622,9 @@ function parsePlaceholders(placeholders, config, selectedVariantName = '') { }, {}); config.placeholders = { ...(config.placeholders || {}), ...results }; } + + createMartechMetadata(placeholders, config, val); + return config; } diff --git a/libs/martech/attributes.js b/libs/martech/attributes.js index 1f0af83c53..5d661d98dc 100644 --- a/libs/martech/attributes.js +++ b/libs/martech/attributes.js @@ -6,8 +6,14 @@ const LEAD_UNDERSCORES = /^_+|_+$/g; export function processTrackingLabels(text, config, charLimit) { let analyticsValue = text?.replace(INVALID_CHARACTERS, ' ').replace(LEAD_UNDERSCORES, '').trim(); if (config) { - const { analyticLocalization, loc = analyticLocalization?.[analyticsValue] } = config; - if (loc) analyticsValue = loc; + const { analyticLocalization, mep } = config; + const mepLoc = mep?.analyticLocalization?.[analyticsValue]; + if (mepLoc) { + analyticsValue = mepLoc; + } else { + const loc = analyticLocalization?.[analyticsValue]; + if (loc) analyticsValue = loc; + } } if (charLimit) return analyticsValue.slice(0, charLimit); return analyticsValue; diff --git a/test/features/personalization/createMartechMetadata.test.js b/test/features/personalization/createMartechMetadata.test.js new file mode 100644 index 0000000000..ba6a94eae2 --- /dev/null +++ b/test/features/personalization/createMartechMetadata.test.js @@ -0,0 +1,51 @@ +import { expect } from '@esm-bundle/chai'; +import { createMartechMetadata } from '../../../libs/features/personalization/personalization.js'; +import placeholders from './mocks/placeholders.js'; + +const config = { + locale: { ietf: 'fr-fr' }, + mep: {}, +}; + +// Note that the manifestPath doesn't matter as we stub the fetch +describe('test martech metadata creation', () => { + beforeEach(() => { + config.mep = {}; + }); + it('test two non US manifests', async () => { + expect(config.mep).to.deep.equal({}); + + await createMartechMetadata(placeholders.geoTest, config, 'fr'); + expect(config.mep.analyticLocalization).to.deep.equal({ + 'value1 fr': 'value1 en us', + 'value2 fr': 'value2 en us', + 'bonjour fr': 'Hello en us', + 'buy now fr': 'buy now en us', + 'try now fr': 'try now en us', + }); + await createMartechMetadata(placeholders.secondManifestTest, config, 'fr'); + expect(config.mep.analyticLocalization).to.deep.equal({ + 'new fr': 'new en us', + 'value1 fr': 'value1 en us', + 'value2 fr': 'new2 en us', + 'bonjour fr': 'Hello en us', + 'buy now fr': 'buy now en us', + 'try now fr': 'try now en us', + }); + }); + it('test one manifest non US withou en-us keys', async () => { + await createMartechMetadata(placeholders.keyTest, config, 'fr'); + expect(config.mep.analyticLocalization).to.deep.equal({ + 'value1 fr': 'test placeholder', + 'value2 fr': 'test placeholder2', + 'bonjour fr': 'marquee headline', + 'buy now fr': 'marquee hollow', + 'try now fr': 'marquee solid', + }); + }); + it('test one manifest en-US', async () => { + config.locale.ietf = 'en-US'; + await createMartechMetadata(placeholders.keyTest, config, 'us'); + expect(config.mep).to.deep.equal({}); + }); +}); diff --git a/test/features/personalization/mocks/placeholders.js b/test/features/personalization/mocks/placeholders.js new file mode 100644 index 0000000000..6cf3a8a9d3 --- /dev/null +++ b/test/features/personalization/mocks/placeholders.js @@ -0,0 +1,103 @@ +const placeholders = { + geoTest: [ + { + key: 'test-placeholder', + 'mobile-device & us': 'US Mobile Value', + us: 'value1-us', + 'en-us': 'value1-en-us', + 'ca & not fr': 'value1-ca-not-fr', + fr: 'value1-fr', + 'mobile-device': 'value1-mobile', + }, + { + key: 'test-placeholder2', + 'mobile-device & us': 'US Mobile Value2', + us: 'value2-us', + 'en-us': 'value2-en-us', + 'ca & not fr': 'value2-ca-not-fr', + fr: 'value2-fr', + 'mobile-device': 'value2-mobile', + }, + { + key: 'marquee-headline', + 'mobile-device & us': 'hello US mobile', + us: 'hello-us', + 'en-us': 'Hello-en-us', + 'ca & not fr': 'hello-ca-not-fr', + fr: 'bonjour-fr', + 'mobile-device': 'hello-mobile', + }, + { + key: 'marquee-hollow', + 'mobile-device & us': 'buy-now-mobile-us', + us: 'buy-now-us', + 'en-us': 'buy-now-en-us', + 'ca & not fr': 'buy-now-ca-not-fr', + fr: 'buy-now-fr', + 'mobile-device': 'buy-now-mobile', + }, + { + key: 'marquee-solid', + 'mobile-device & us': 'try-now-mobile-us', + us: 'try-now-us', + 'en-us': 'try-now-en-us', + 'ca & not fr': 'try-now-ca-not-fr', + fr: 'try-now-fr', + 'mobile-device': 'try-now-mobile', + }, + ], + secondManifestTest: [ + { + key: 'test-placeholder', + 'mobile-device & us': 'US Mobile Value', + us: 'value1-us', + 'en-us': 'new-en-us', + 'ca & not fr': 'value1-ca-not-fr', + fr: 'new-fr', + 'mobile-device': 'value1-mobile', + }, + { + key: 'test-placeholder2', + 'mobile-device & us': 'US Mobile Value2', + us: 'value2-us', + 'en-us': 'new2-en-us', + 'ca & not fr': 'value2-ca-not-fr', + fr: 'value2-fr', + 'mobile-device': 'value2-mobile', + }, + ], + keyTest: [ + { + key: 'test-placeholder', + 'mobile-device & us': 'US Mobile Value', + fr: 'value1-fr', + 'mobile-device': 'value1-mobile', + }, + { + key: 'test-placeholder2', + 'mobile-device & us': 'US Mobile Value2', + fr: 'value2-fr', + 'mobile-device': 'value2-mobile', + }, + { + key: 'marquee-headline', + 'mobile-device & us': 'hello US mobile', + fr: 'bonjour-fr', + 'mobile-device': 'hello-mobile', + }, + { + key: 'marquee-hollow', + 'mobile-device & us': 'buy-now-mobile-us', + fr: 'buy-now-fr', + 'mobile-device': 'buy-now-mobile', + }, + { + key: 'marquee-solid', + 'mobile-device & us': 'try-now-mobile-us', + fr: 'try-now-fr', + 'mobile-device': 'try-now-mobile', + }, + ], +}; + +export default placeholders; diff --git a/test/martech/attributes.test.js b/test/martech/attributes.test.js index 97593f80d3..263dec0b70 100644 --- a/test/martech/attributes.test.js +++ b/test/martech/attributes.test.js @@ -109,4 +109,13 @@ describe('Analytics', async () => { }, 20); expect(processedString).to.equal('Buy now'); }); + it('should process tracking labels with foreign locale and MEP placeholder', () => { + const translatedString = 'Comprar ahora'; + const processedString = processTrackingLabels(translatedString, { + locale: { ietf: 'es-ES' }, + analyticLocalization: { 'Comprar ahora': 'Buy now' }, + mep: { analyticLocalization: { 'Comprar ahora': 'Buy right now' } }, + }, 20); + expect(processedString).to.equal('Buy right now'); + }); }); From 721645234c38ae69cf110e5b822ce4cc29ea821a Mon Sep 17 00:00:00 2001 From: Chris Peyer Date: Tue, 3 Sep 2024 04:39:05 -0400 Subject: [PATCH 23/66] MWPW-157445 Additional Lana LCP data (#2805) --- libs/utils/logWebVitals.js | 25 +++++++++++++++++++++++-- test/utils/logWebVitals.test.js | 11 +++-------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/libs/utils/logWebVitals.js b/libs/utils/logWebVitals.js index a7e4bc39a5..0564ffe510 100644 --- a/libs/utils/logWebVitals.js +++ b/libs/utils/logWebVitals.js @@ -62,7 +62,21 @@ function getElementInfo(el) { return `${el.outerHTML.substring(0, 100)}...`; } -function observeLCP(lanaData, delay) { +function isFragmentFromMep(fragPath, mep) { + return mep.experiments?.some(({ selectedVariant }) => { + const { commands = [], fragments = [] } = selectedVariant || {}; + + return commands.some((cmd) => { + try { + return new URL(cmd.target).pathname === fragPath; + } catch { + return false; + } + }) || fragments.some((cmd) => cmd?.val === fragPath); + }); +} + +function observeLCP(lanaData, delay, mep) { new PerformanceObserver((list) => { const entries = list.getEntries(); const lastEntry = entries[entries.length - 1]; // Use the latest LCP candidate @@ -70,6 +84,13 @@ function observeLCP(lanaData, delay) { const lcpEl = lastEntry.element; lanaData.lcpElType = lcpEl.nodeName.toLowerCase(); lanaData.lcpEl = getElementInfo(lcpEl); + const closestFrag = lcpEl.closest('.fragment'); + lanaData.isFrag = closestFrag ? 'true' : 'false'; + if (closestFrag) { + lanaData.isMep = isFragmentFromMep(closestFrag.dataset.path, mep) ? 'true' : 'false'; + } else { + lanaData.isMep = 'false'; + } setTimeout(() => { sendToLana(lanaData); @@ -99,7 +120,7 @@ export default function webVitals(mep, { delay = 1000, sampleRate = 50 } = {}) { const lanaData = {}; logMepExperiments(lanaData, mep); observeCLS(lanaData); - observeLCP(lanaData, delay); + observeLCP(lanaData, delay, mep); } if (getConsent()) { handleEvent(); diff --git a/test/utils/logWebVitals.test.js b/test/utils/logWebVitals.test.js index e04db9805a..199e7ca1dd 100644 --- a/test/utils/logWebVitals.test.js +++ b/test/utils/logWebVitals.test.js @@ -5,15 +5,8 @@ import logWebVitals from '../../libs/utils/logWebVitals.js'; document.body.innerHTML = await readFile({ path: './mocks/body.html' }); +window.adobePrivacy = { activeCookieGroups: () => ['C0002'] }; describe('Log Web Vitals', () => { - before(() => { - window.adobePrivacy = { activeCookieGroups: () => ['C0002'] }; - }); - - after(() => { - delete window.adobePrivacy; - }); - it('Logs data to lana', (done) => { window.lana = { log: (logStr, logOpts) => { @@ -39,6 +32,8 @@ describe('Log Web Vitals', () => { expect(vitals.manifest4selected).to.equal('target-var-marqueelink'); expect(vitals.os).to.be.oneOf(['mac', 'win', 'android', 'linux', '']); expect(vitals.url).to.equal('localhost:2000/'); + expect(vitals.isMep).to.equal('false'); + expect(vitals.isFrag).to.equal('false'); expect(parseInt(vitals.windowHeight, 10)).to.be.greaterThan(200); expect(parseInt(vitals.windowWidth, 10)).to.be.greaterThan(200); From 318f2e82f01996d3dbc2d4878bd4baffd0d97738 Mon Sep 17 00:00:00 2001 From: Bandana Laishram Date: Tue, 3 Sep 2024 23:38:12 +0530 Subject: [PATCH 24/66] Checking if Ims is already initialized before loading it (#2802) * Checking if Ims is already initialized * Moving the check to global navigation * Lint fix --- libs/blocks/global-navigation/global-navigation.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/blocks/global-navigation/global-navigation.js b/libs/blocks/global-navigation/global-navigation.js index 1440b0b94c..90e85421d7 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(); From 8df386c952f8c854ed3cbab5c82e019f5d5bd2c9 Mon Sep 17 00:00:00 2001 From: Okan Sahin <39759830+mokimo@users.noreply.github.com> Date: Tue, 3 Sep 2024 20:09:17 +0200 Subject: [PATCH 25/66] Fix video CLS netting +10 lighthouse points (#2750) * MWPW-155412: Fix video CLS netting +10 lighthouse points (#2724) * fix cls by adding the videoEl without a source * adapt the video hover and in view port play * Remove no-lazy and return earlier * Fix linting issue * Consolidate duplicated logic * Move functions into the init function * Move root margin and use optional chaining * Only query for the videoEl once * Add default hash at in the right spot * Fix the figure/video interaction * Remove unused export statement * Keep the previous defaults --- libs/blocks/adobetv/adobetv.js | 35 +++++------------- libs/blocks/figure/figure.js | 35 +++++++++--------- libs/blocks/video/video.js | 40 ++++++--------------- libs/utils/decorate.js | 28 +++++++++++---- test/blocks/adobetv/adobetv.test.js | 11 ------ test/blocks/adobetv/mocks/body.html | 2 +- test/blocks/marquee/marquee.test.js | 21 ++++++++--- test/blocks/video/mocks/body.html | 2 +- test/blocks/video/video.test.js | 56 +++++++++++------------------ 9 files changed, 97 insertions(+), 133 deletions(-) diff --git a/libs/blocks/adobetv/adobetv.js b/libs/blocks/adobetv/adobetv.js index 31cfc7a368..7635da82e3 100644 --- a/libs/blocks/adobetv/adobetv.js +++ b/libs/blocks/adobetv/adobetv.js @@ -1,22 +1,16 @@ -import { createIntersectionObserver } from '../../utils/utils.js'; -import { applyHoverPlay, getVideoAttrs } from '../../utils/decorate.js'; +import { turnAnchorIntoVideo } from '../../utils/decorate.js'; -const ROOT_MARGIN = 1000; - -const loadAdobeTv = (a) => { +export default function init(a) { + a.classList.add('hide-video'); const bgBlocks = ['aside', 'marquee', 'hero-marquee']; if (a.href.includes('.mp4') && bgBlocks.some((b) => a.closest(`.${b}`))) { a.classList.add('hide'); - const { href, hash, dataset } = a; - const attrs = getVideoAttrs(hash || 'autoplay', dataset); - const video = ``; if (!a.parentNode) return; - a.insertAdjacentHTML('afterend', video); - const videoElem = document.body.querySelector(`source[src="${href}"]`)?.parentElement; - applyHoverPlay(videoElem); - a.remove(); + turnAnchorIntoVideo({ + src: a.href, + anchorTag: a, + hash: a.hash || 'autoplay', + }); } else { const embed = `
-
`; - 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/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/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..50504a2436 100644 --- a/test/blocks/vimeo/vimeo.test.html +++ b/test/blocks/vimeo/vimeo.test.html @@ -3,7 +3,8 @@ @@ -16,17 +17,28 @@ import { readFile } from '@web/test-runner-commands'; import { expect } from '@esm-bundle/chai'; import init from '../../../libs/blocks/vimeo/vimeo.js'; - + runTests(() => { describe('vimeo', () => { - it('renders embed video for vimeo link', () => { - const vimeo = document.querySelector('a'); + it('Should load lite vimeo instead of iframe', async () => { + init(document.querySelector('a')); + expect(document.querySelector('iframe')).to.be.null; + expect(document.querySelector('lite-vimeo')).to.not.be.null; + }); + + it('Should load vimeo resources on lite vimeo hover', async () => { + init(document.querySelector('a')); + document.querySelector('lite-vimeo').dispatchEvent(new MouseEvent('pointerover')); + expect(document.querySelector('link[rel="preconnect"][href="https://player.vimeo.com"]')).to.not.be.null; + expect(document.querySelector('link[rel="preconnect"][href="https://i.vimeocdn.com"]')).to.not.be.null; + expect(document.querySelector('link[rel="preconnect"][href="https://f.vimeocdn.com"]')).to.not.be.null; + expect(document.querySelector('link[rel="preconnect"][href="https://fresnel.vimeocdn.com"]')).to.not.be.null; + }); - init(vimeo); - const iframe = document.querySelector("iframe") - const wrapper = document.querySelector('.embed-vimeo'); - expect(wrapper).to.not.be.null; - expect(iframe).to.exist + it('Should add iframe when lite vimeo is clicked', async () => { + document.querySelector('.ltv-playbtn').click(); + expect(document.querySelector('iframe')).to.not.be.null; + expect(document.querySelector('lite-vimeo')).to.be.null; }); }); }); 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 + + - - - - - 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..85ea6f343b 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 @@ -12,7 +12,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; 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..07e9095402 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 @@ -11,7 +11,7 @@ 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'); 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..6e7585b927 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 @@ -18,7 +18,7 @@ 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'; 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..5bf16f6e2b 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 @@ -33,7 +33,7 @@ import { 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'; 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..ab3836525e 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 @@ -8,7 +8,7 @@ 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; 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/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/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() { From 3f32a64666a454876588983129489b4726ebda00 Mon Sep 17 00:00:00 2001 From: Suhani Jain <110388864+suhjainadobe@users.noreply.github.com> Date: Thu, 5 Sep 2024 22:16:03 +0530 Subject: [PATCH 37/66] MWPW-157292: CC and DC: Phone number links wrong (#2841) * MWPW-157292: CC and DC: Phone number links wrong * Unit tests * Unit tests --------- Co-authored-by: Suhani --- libs/features/placeholders.js | 13 +++++++++---- libs/utils/utils.js | 26 ++++++++++++-------------- test/utils/mocks/body.html | 3 +++ test/utils/utils.test.js | 3 +++ 4 files changed, 27 insertions(+), 18 deletions(-) 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/utils/utils.js b/libs/utils/utils.js index 639cb8adc6..3b57b87e96 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -760,23 +760,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/test/utils/mocks/body.html b/test/utils/mocks/body.html index f1d7f8718a..648965a5ed 100644 --- a/test/utils/mocks/body.html +++ b/test/utils/mocks/body.html @@ -54,6 +54,9 @@

I'm not a blockhead.

{{ inkl. MwSt.}}

+
diff --git a/test/utils/utils.test.js b/test/utils/utils.test.js index b2e5a06b6a..d3132ea524 100644 --- a/test/utils/utils.test.js +++ b/test/utils/utils.test.js @@ -230,6 +230,9 @@ describe('Utils', () => { const paragraphs = [...document.querySelectorAll('p')]; const lastPara = paragraphs.pop(); expect(lastPara.textContent).to.equal(' inkl. MwSt.'); + const plceholderhref = document.querySelector('.placeholder'); + const hrefValue = plceholderhref.getAttribute('href'); + expect(hrefValue).to.equal('tel:phone number substance'); }); it('Decorates meta helix url', () => { From 8ea4f298f2fed8969be79467bd34f9ffea3e2732 Mon Sep 17 00:00:00 2001 From: Ryan Parrish Date: Thu, 5 Sep 2024 10:56:20 -0600 Subject: [PATCH 38/66] MWPW-156865 - Hero marquee supports heading element on detail text. (#2801) * enabled config for `allowDetailHeading` on the shared decorateBlockText * hasDetailHeading - refactor so all but first still get decorated --- libs/blocks/hero-marquee/hero-marquee.js | 2 +- libs/utils/decorate.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/blocks/hero-marquee/hero-marquee.js b/libs/blocks/hero-marquee/hero-marquee.js index 550c5aa2c9..52faa48051 100644 --- a/libs/blocks/hero-marquee/hero-marquee.js +++ b/libs/blocks/hero-marquee/hero-marquee.js @@ -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); diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index 1e9d87603f..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; From 510f7a69ed40976c77a767de7b7a41b72a421912 Mon Sep 17 00:00:00 2001 From: Vivian A Goodrich <101133187+vgoodric@users.noreply.github.com> Date: Thu, 5 Sep 2024 10:56:27 -0600 Subject: [PATCH 39/66] MWPW-157783 [MEP] Primary and Secondary CTA simplified selectors do not work well with hero-marquee block (#2830) * Make action-area more flexible * unit test update * also update other new css selectors * get rid of parent in selector * unit test update * MWPW-157785 [MEP] Create "any-marquee" and "header" simplified selectors (#2832) * mepanymarquee * add header * fix unit test --- .../personalization/personalization.js | 11 +++--- .../modifyNonFragmentSelector.test.js | 34 ++++++++++++++----- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/libs/features/personalization/personalization.js b/libs/features/personalization/personalization.js index d32285ed41..fcb8bc6f97 100644 --- a/libs/features/personalization/personalization.js +++ b/libs/features/personalization/personalization.js @@ -356,14 +356,17 @@ function modifySelectorTerm(termParam) { let term = termParam; const specificSelectors = { section: 'main > div', - 'primary-cta': 'p strong a', - 'secondary-cta': 'p em a', + 'primary-cta': 'strong a', + 'secondary-cta': 'em a', 'action-area': '*:has(> em a, > strong a)', + 'any-marquee': '[class*="marquee"]', + 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 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] : ''; if (!startText || htmlEls.includes(startText)) return term; @@ -372,8 +375,8 @@ function modifySelectorTerm(termParam) { 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; } diff --git a/test/features/personalization/modifyNonFragmentSelector.test.js b/test/features/personalization/modifyNonFragmentSelector.test.js index 7b977c3874..302e75cc47 100644 --- a/test/features/personalization/modifyNonFragmentSelector.test.js +++ b/test/features/personalization/modifyNonFragmentSelector.test.js @@ -2,6 +2,10 @@ import { expect } from '@esm-bundle/chai'; import { modifyNonFragmentSelector } from '../../../libs/features/personalization/personalization.js'; const values = [ + { + b: 'any-marquee action-area', + a: '[class*="marquee"] *:has(> em a, > strong a)', + }, { b: 'main section1 marquee action-area', a: 'main > div:nth-child(1) .marquee *:has(> em a, > strong a)', @@ -18,6 +22,18 @@ const values = [ b: 'marquee.light:nth-child(2) h2', a: '.marquee.light:nth-child(2) h2', }, + { + b: 'marquee.light:nth-child(2) header', + a: '.marquee.light:nth-child(2) :is(h1, h2, h3, h4, h5, h6)', + }, + { + b: 'any-marquee.light:nth-child(2) h2', + a: '[class*="marquee"].light:nth-child(2) h2', + }, + { + b: 'any-marquee:nth-child(2) h2', + a: '[class*="marquee"]:nth-child(2) h2', + }, { b: 'section2 text3', a: 'main > div:nth-child(2) .text:nth-child(3 of .text)', @@ -28,35 +44,35 @@ const values = [ }, { b: 'section3 table row2 col2 primary-cta', - a: 'main > div:nth-child(3) .table > div:nth-child(2) > div:nth-child(2) p strong a', + a: 'main > div:nth-child(3) .table > div:nth-child(2) > div:nth-child(2) strong a', }, { b: 'marquee primary-cta #_href', - a: '.marquee p strong a', + a: '.marquee strong a', m: ['href'], }, { b: 'marquee primary-cta #_HREF', - a: '.marquee p strong a', + a: '.marquee strong a', m: ['href'], }, { b: 'marquee primary-cta#_href', - a: '.marquee p strong a#_href', + a: '.marquee strong a#_href', }, { b: 'marquee primary-cta #_href_all', - a: '.marquee p strong a', + a: '.marquee strong a', m: ['href', 'all'], }, { b: 'marquee primary-cta #_href#_all', - a: '.marquee p strong a', + a: '.marquee strong a', m: ['href', 'all'], }, { b: 'marquee primary-cta #_href #_all', - a: '.marquee p strong a', + a: '.marquee strong a', m: ['href', 'all'], }, { @@ -65,7 +81,7 @@ const values = [ }, { b: 'section3 table row2 col4 secondary-cta', - a: 'main > div:nth-child(3) .table > div:nth-child(2) > div:nth-child(4) p em a', + a: 'main > div:nth-child(3) .table > div:nth-child(2) > div:nth-child(4) em a', }, { b: 'section4 merch-card', @@ -85,7 +101,7 @@ const values = [ }, { b: '.text:has(#im-a-unique-text-block) secondary-cta', - a: '.text:has(#im-a-unique-text-block) p em a', + a: '.text:has(#im-a-unique-text-block) em a', }, { b: 'section1 .random-block2', From 99a0fd737cd8d19c0dba29de0c303da4236047cb Mon Sep 17 00:00:00 2001 From: Rares Munteanu Date: Mon, 9 Sep 2024 13:33:28 +0200 Subject: [PATCH 40/66] [MWPW-157980] Update RCP dates (#2845) --- .github/workflows/helpers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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'), From e489d6f0352317718cad18ad78274a2657f39d04 Mon Sep 17 00:00:00 2001 From: Elan Bartholomew <79870969+elan-tbx@users.noreply.github.com> Date: Mon, 9 Sep 2024 10:09:08 -0600 Subject: [PATCH 41/66] MWPW-157701 [Fast-follow] - Add'l Pill updates (#2829) * fix some edge cases * update drop shadow color as per Consonant's request --- libs/blocks/notification/notification.css | 29 ++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) 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; } } From 1790c9918dd6d98dc15f820b32875e6a0bd2bb37 Mon Sep 17 00:00:00 2001 From: Rares Munteanu Date: Mon, 9 Sep 2024 18:09:15 +0200 Subject: [PATCH 42/66] [MWPW-157334] Table pricing alignment (#2826) --- libs/blocks/table/table.css | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libs/blocks/table/table.css b/libs/blocks/table/table.css index f1aab48c79..391c70fb1a 100644 --- a/libs/blocks/table/table.css +++ b/libs/blocks/table/table.css @@ -614,6 +614,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; } From 8138b7944dc6fb0ac4c473e0feb998cd0dd57568 Mon Sep 17 00:00:00 2001 From: Bandana Laishram Date: Mon, 9 Sep 2024 21:39:22 +0530 Subject: [PATCH 43/66] Adding support for getting env config through client env (#2838) * Adding support for getting env config through client env * Attaching client env to window --- libs/navigation/navigation.js | 2 +- libs/utils/utils.js | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/libs/navigation/navigation.js b/libs/navigation/navigation.js index e31dd77195..649efba013 100644 --- a/libs/navigation/navigation.js +++ b/libs/navigation/navigation.js @@ -37,6 +37,7 @@ export default async function loadBlock(configs, customLib) { const { header, footer, authoringPath, env = 'prod', locale = '', theme, } = configs || {}; + window.miloClientEnv = env; const branch = new URLSearchParams(window.location.search).get('navbranch'); const miloLibs = branch ? `https://${branch}--milo--adobecom.hlx.page` : customLib || envMap[env]; if (!header && !footer) { @@ -72,5 +73,4 @@ export default async function loadBlock(configs, customLib) { } }); } - window.loadNavigation = loadBlock; diff --git a/libs/utils/utils.js b/libs/utils/utils.js index 3b57b87e96..a4080597bd 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -150,8 +150,11 @@ const PROMO_PARAM = 'promo'; function getEnv(conf) { const { host } = window.location; const query = PAGE_URL.searchParams.get('env'); - if (query) return { ...ENVS[query], consumer: conf[query] }; + + const clientEnv = window.miloClientEnv; + if (clientEnv) return { ...ENVS[clientEnv], consumer: conf[clientEnv] }; + if (host.includes('localhost')) return { ...ENVS.local, consumer: conf.local }; /* c8 ignore start */ if (host.includes(`${SLD}.page`) From 38beea9218d91f5027ee8d5c544cb32fa0e937d2 Mon Sep 17 00:00:00 2001 From: Vivian A Goodrich <101133187+vgoodric@users.noreply.github.com> Date: Mon, 9 Sep 2024 10:09:28 -0600 Subject: [PATCH 44/66] MWPW-157887 [MEP] HTML elements like body need to be recognized and header simplified selector rename (#2843) * add more html els and change header to any header * MWPW-157686 [MEP] Cannot spoof an experience that exists in manifest but not in Target (#2844) Fix preview bug --------- Co-authored-by: Mark Perry <124626043+markpadbe@users.noreply.github.com> --- libs/features/personalization/personalization.js | 9 +++++---- .../modifyNonFragmentSelector.test.js | 14 +++++++++++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/libs/features/personalization/personalization.js b/libs/features/personalization/personalization.js index fcb8bc6f97..69410cd17b 100644 --- a/libs/features/personalization/personalization.js +++ b/libs/features/personalization/personalization.js @@ -360,10 +360,13 @@ function modifySelectorTerm(termParam) { 'secondary-cta': 'em a', 'action-area': '*:has(> em a, > strong a)', 'any-marquee': '[class*="marquee"]', - header: ':is(h1, h2, h3, h4, h5, h6)', + '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', + ]; const startTextMatch = term.match(/^[a-zA-Z/./-]*/); const startText = startTextMatch ? startTextMatch[0].toLowerCase() : ''; const startTextPart1 = startText.split(/\.|:/)[0]; @@ -890,8 +893,6 @@ export function cleanAndSortManifestList(manifests) { freshManifest = manifestObj[manifest.manifestPath]; } freshManifest.name = fullManifest.name; - freshManifest.selectedVariantName = fullManifest.selectedVariantName; - freshManifest.selectedVariant = freshManifest.variants[freshManifest.selectedVariantName]; manifestObj[manifest.manifestPath] = freshManifest; } else { manifestObj[manifest.manifestPath] = manifest; diff --git a/test/features/personalization/modifyNonFragmentSelector.test.js b/test/features/personalization/modifyNonFragmentSelector.test.js index 302e75cc47..f9345c3eb1 100644 --- a/test/features/personalization/modifyNonFragmentSelector.test.js +++ b/test/features/personalization/modifyNonFragmentSelector.test.js @@ -2,6 +2,18 @@ import { expect } from '@esm-bundle/chai'; import { modifyNonFragmentSelector } from '../../../libs/features/personalization/personalization.js'; const values = [ + { + b: 'body > main > div', + a: 'body > main > div', + }, + { + b: 'main header', + a: 'main header', + }, + { + b: 'main footer', + a: 'main footer', + }, { b: 'any-marquee action-area', a: '[class*="marquee"] *:has(> em a, > strong a)', @@ -23,7 +35,7 @@ const values = [ a: '.marquee.light:nth-child(2) h2', }, { - b: 'marquee.light:nth-child(2) header', + b: 'marquee.light:nth-child(2) any-header', a: '.marquee.light:nth-child(2) :is(h1, h2, h3, h4, h5, h6)', }, { From 347dbca83f35f4c2c156427982a6e48a4e3a2e49 Mon Sep 17 00:00:00 2001 From: Elan Bartholomew <79870969+elan-tbx@users.noreply.github.com> Date: Mon, 9 Sep 2024 12:11:13 -0600 Subject: [PATCH 45/66] MWPW-156365 - Text Block tiger team updates (#2822) * midpoint of text block changes * per breakpoint inroads * add ribbons and lockups * update image classname, tighten up picture element * change variant name to accent-bar * add center image handling * correct flex rule * add right-variant image alignment * handle icon size variants * account for buttons that have icons * adjust icon column gap * proper handling of text decoration via multiviewports * update mocks * improve coverage * update detail bottom margin * simplify bottom margin rules * cleaned up icon/lockup decorator a bit * various fixes based on feedback --- libs/blocks/text/text.css | 94 ++++++++++++++++++++++++++-- libs/blocks/text/text.js | 36 ++++++++--- test/blocks/text/mocks/body.html | 101 ++++++++++++++++++++++++++++++- 3 files changed, 215 insertions(+), 16 deletions(-) diff --git a/libs/blocks/text/text.css b/libs/blocks/text/text.css index 8ed19d150b..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); } @@ -72,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; @@ -82,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; } @@ -144,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/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

+

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.

+
+
+
From 2cc3d12fdb6929572bda1ba37931b7d8b8e73cf3 Mon Sep 17 00:00:00 2001 From: Dev Ashish Saradhana <41122199+Deva309@users.noreply.github.com> Date: Mon, 9 Sep 2024 23:41:20 +0530 Subject: [PATCH 46/66] Set expiry of 'international' cookie for 1 year for selection of region from region selector (#2848) --- libs/blocks/region-nav/region-nav.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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'); From 2802011fe2a7994867e9fdad587789f2a3952c9e Mon Sep 17 00:00:00 2001 From: Okan Sahin <39759830+mokimo@users.noreply.github.com> Date: Tue, 10 Sep 2024 08:58:21 +0200 Subject: [PATCH 47/66] Revert "Adding support for getting env config through client env" (#2858) Revert "Adding support for getting env config through client env (#2838)" This reverts commit 8138b7944dc6fb0ac4c473e0feb998cd0dd57568. Co-authored-by: Saloni Jain <6162294+salonijain3@users.noreply.github.com> --- libs/navigation/navigation.js | 2 +- libs/utils/utils.js | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/libs/navigation/navigation.js b/libs/navigation/navigation.js index 649efba013..e31dd77195 100644 --- a/libs/navigation/navigation.js +++ b/libs/navigation/navigation.js @@ -37,7 +37,6 @@ export default async function loadBlock(configs, customLib) { const { header, footer, authoringPath, env = 'prod', locale = '', theme, } = configs || {}; - window.miloClientEnv = env; const branch = new URLSearchParams(window.location.search).get('navbranch'); const miloLibs = branch ? `https://${branch}--milo--adobecom.hlx.page` : customLib || envMap[env]; if (!header && !footer) { @@ -73,4 +72,5 @@ export default async function loadBlock(configs, customLib) { } }); } + window.loadNavigation = loadBlock; diff --git a/libs/utils/utils.js b/libs/utils/utils.js index a4080597bd..3b57b87e96 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -150,11 +150,8 @@ const PROMO_PARAM = 'promo'; function getEnv(conf) { const { host } = window.location; const query = PAGE_URL.searchParams.get('env'); - if (query) return { ...ENVS[query], consumer: conf[query] }; - - const clientEnv = window.miloClientEnv; - if (clientEnv) return { ...ENVS[clientEnv], consumer: conf[clientEnv] }; + if (query) return { ...ENVS[query], consumer: conf[query] }; if (host.includes('localhost')) return { ...ENVS.local, consumer: conf.local }; /* c8 ignore start */ if (host.includes(`${SLD}.page`) From 5ad94df4dd75e9ec977555b392f922f796f381af Mon Sep 17 00:00:00 2001 From: Vivian A Goodrich <101133187+vgoodric@users.noreply.github.com> Date: Tue, 10 Sep 2024 03:16:26 -0600 Subject: [PATCH 48/66] MWPW-158059 [MEP] PHONE_SIZE limit is too large to exclude smaller tablets (#2854) * MWPW-158059 [MEP] PHONE_SIZE limit is too large to exclude smaller tablets * tweak to 550 --- libs/features/personalization/personalization.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/features/personalization/personalization.js b/libs/features/personalization/personalization.js index 69410cd17b..71857e6c3b 100644 --- a/libs/features/personalization/personalization.js +++ b/libs/features/personalization/personalization.js @@ -7,7 +7,7 @@ import { 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'), From 3fd877c680c5889e377572086a6e7ac7d49bab9b Mon Sep 17 00:00:00 2001 From: Bozo Jovicic <37440641+bozojovicic@users.noreply.github.com> Date: Tue, 10 Sep 2024 18:11:39 +0200 Subject: [PATCH 49/66] MWPW-156357 Price-literals - fail gracefully (#2815) * MWPW-156357 Price-literals - fail gracefully * MWPW-156357 Price-literals - fail gracefully * Trigger Build * MWPW-156357 Price-literals - fail gracefully * MWPW-156357 Price-literals - fail gracefully * MWPW-156357 Price-literals - fail gracefully * MWPW-156357 Price-literals - fail gracefully * MWPW-156357 Price-literals - fail gracefully * MWPW-156357 Price-literals - fail gracefully * MWPW-156357 Price-literals - fail gracefully * MWPW-156357 Price-literals - fail gracefully * MWPW-156357 Price-literals - fail gracefully * Trigger Build * MWPW-156357 Price-literals - fail gracefully --------- Co-authored-by: Bozo Jovicic --- libs/blocks/merch/merch.js | 10 - libs/deps/mas/commerce.js | 4 +- libs/features/mas/commerce/libs/commerce.d.ts | 4 +- .../features/mas/commerce/price-literals.json | 1 + libs/features/mas/commerce/src/index.d.ts | 4 +- libs/features/mas/commerce/src/literals.js | 24 +- libs/features/mas/commerce/src/service.js | 4 +- libs/features/mas/commerce/src/settings.js | 2 - .../mas/commerce/test/checkout.test.js | 3 +- .../mas/commerce/test/literals.test.js | 40 +- .../mas/commerce/test/placeholder.test.js | 4 +- libs/features/mas/commerce/test/price.test.js | 3 +- .../mas/commerce/test/service.test.js | 10 +- .../mas/commerce/test/settings.test.js | 6 - libs/features/mas/mas/src/mas.js | 6 +- libs/features/mas/mocks/config.js | 3 - libs/features/mas/mocks/literals.js | 17 - libs/features/mas/mocks/literals.json | 502 ------------------ .../test/merch-card-collection.test.html.js | 3 +- .../test/merch-card.mini-compare.test.html.js | 4 +- ...erch-card.mini-inline-heading.test.html.js | 4 +- .../test/merch-offer-select.test.html.js | 4 +- .../test/merch-quantity-select.test.html.js | 4 +- .../merch-subscription-panel.test.html.js | 4 +- .../test/merch-twp-d2p.test.html.js | 4 +- .../test/plans-modal.test.html.js | 4 +- test/blocks/merch/merch.test.js | 3 - test/blocks/merch/mocks/fetch.js | 13 +- test/blocks/merch/mocks/literals.json | 16 - 29 files changed, 44 insertions(+), 666 deletions(-) create mode 100644 libs/features/mas/commerce/price-literals.json delete mode 100644 libs/features/mas/mocks/literals.js delete mode 100644 libs/features/mas/mocks/literals.json delete mode 100644 test/blocks/merch/mocks/literals.json diff --git a/libs/blocks/merch/merch.js b/libs/blocks/merch/merch.js index fd49281731..6fb4b58ea7 100644 --- a/libs/blocks/merch/merch.js +++ b/libs/blocks/merch/merch.js @@ -3,7 +3,6 @@ import { } from '../../utils/utils.js'; import { replaceKey } from '../../features/placeholders.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'; @@ -179,14 +178,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) => { @@ -430,7 +421,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 { hostname, searchParams } = new URL(window.location.href); let commerceLibPath = '../../deps/mas/commerce.js'; diff --git a/libs/deps/mas/commerce.js b/libs/deps/mas/commerce.js index b1e4b8c43e..e1afe935b7 100644 --- a/libs/deps/mas/commerce.js +++ b/libs/deps/mas/commerce.js @@ -1,3 +1,3 @@ -var Ir=Object.defineProperty;var Qn=(e,t,n)=>t in e?Ir(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var Kn=(e,t)=>{for(var n in t)Ir(e,n,{get:t[n],enumerable:!0})};var K=(e,t,n)=>(Qn(e,typeof t!="symbol"?t+"":t,n),n),Mr=(e,t,n)=>{if(!t.has(e))throw TypeError("Cannot "+n)};var At=(e,t,n)=>(Mr(e,t,"read from private field"),n?n.call(e):t.get(e)),Dr=(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)=>(Mr(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=ei.get(e))!==null&&t!==void 0?t:e},ei=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 Ur=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.")},kr=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=Ur(Object.entries(e)),s=o.next();!s.done;s=o.next()){var a=kr(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,Ur(Object.entries(o))),a=s.next();!a.done;a=s.next()){var l=kr(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 ti=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 Gr(e){oi(e);var t=e.env,n=e.items,r=e.workflowStep,i=ti(e,["env","items","workflowStep"]),o=new URL(He(t));return o.pathname=r+"/",We(n,o.searchParams),ge(i,o.searchParams,ni),o.toString()}var ni=new Set(["cli","co","lang","ctx","cUrl","mv","nglwfdata","otac","promoid","rUrl","sdid","spint","trackingid","code","campaignid","appctxid"]),ii=["env","workflowStep","clientId","country","items"];function oi(e){var t,n;try{for(var r=ri(ii),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 si=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.")},ci="p_draft_landscape",li="/store/";function Ct(e){fi(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=si(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=""+li+r,r!==V.SEGMENTATION&&r!==V.CHANGE_PLAN_TEAM_PLANS&&We(n,h.searchParams),r===V.SEGMENTATION&&ge(f,h.searchParams,Nt),ge(p,h.searchParams,Nt),c===Oe.DRAFT&&ge({af:ci},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"]),ui=["env","workflowStep","clientId","country"];function fi(e){var t,n;try{for(var r=ai(ui),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 Gr(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 Fr="tacocat.js";var Xe=(e,t)=>String(e??"").toLowerCase()==String(t??"").toLowerCase(),Vr=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=pi(xe(n)?n:e);o=document.documentElement.querySelector(`meta[name="${s}"]`)?.content}return o??t[e]}var ye=()=>{};var jr=e=>typeof e=="boolean",re=e=>typeof e=="function",Ye=e=>typeof e=="number",Hr=e=>e!=null&&typeof e=="object";var xe=e=>typeof e=="string",Gt=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(jr(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 pi(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 mi=Date.now(),Ft=()=>`(+${Date.now()-mi}ms)`,Be=new Set,hi=v(N("tacocat.debug",{},{metadata:!1}),typeof process<"u"&&process.env?.DEBUG);function Wr(e){let t=`[${Fr}/${e}]`,n=(s,a,...l)=>s?!0:(i(a,...l),!1),r=hi?(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 di(e,t){let n=[e,t];return Be.add(n),()=>{Be.delete(n)}}di((e,...t)=>{console.error(e,...t,Ft())},(e,...t)=>{console.warn(e,...t,Ft())});var Ei="no promo",Xr="promo-tag",gi="yellow",xi="neutral",yi=(e,t,n)=>{let r=o=>o||Ei,i=n?` (was "${r(t)}")`:"";return`${r(e)}${i}`},_i="cancel-context",Ne=(e,t)=>{let n=e===_i,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?Xr:`${Xr} no-promo`,text:yi(s,t,i),variant:o?gi:xi,isOverriden:i}};var Vt="ABM",jt="PUF",Ht="M2M",Wt="PERPETUAL",Xt="P3Y",Si="TAX_INCLUSIVE_DETAILS",vi="TAX_EXCLUSIVE",Yr={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}},Br="Value is not an offer",$e=e=>{if(typeof e!="object")return Br;let{commitment:t,term:n}=e,r=Ti(t,n);return{...e,planType:r}};var Ti=(e,t)=>{switch(e){case void 0:return Br;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!==Si)return e;let a={...e,priceDetails:{...t,price:i??n,priceWithoutDiscount:o??r,taxDisplay:vi}};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(Ai,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(rn.test(i.stem)){t.minimumIntegerDigits=i.stem.length;continue}if(Jr.test(i.stem)){if(i.options.length>1)throw new RangeError("Fraction-precision stems only accept a single optional option");i.stem.replace(Jr,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),Qr(i.options[0])));continue}if(tn.test(i.stem)){t=y(y({},t),Qr(i.stem));continue}var o=nn(i.stem);o&&(t=y(y({},t),o));var s=wi(i.stem);s&&(t=y(y({},t),s))}return t}var zt,Li=new RegExp("^"+qt.source+"*"),Oi=new RegExp(qt.source+"*$");function g(e,t){return{start:e,end:t}}var Ni=!!String.prototype.startsWith,Ci=!!String.fromCodePoint,Ri=!!Object.fromEntries,Ii=!!String.prototype.codePointAt,Mi=!!String.prototype.trimStart,Di=!!String.prototype.trimEnd,Ui=!!Number.isSafeInteger,ki=Ui?Number.isSafeInteger:function(e){return typeof e=="number"&&isFinite(e)&&Math.floor(e)===e&&Math.abs(e)<=9007199254740991},Jt=!0;try{sn=un("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu"),Jt=((zt=sn.exec("a"))===null||zt===void 0?void 0:zt[0])==="a"}catch{Jt=!1}var sn,an=Ni?function(t,n,r){return t.startsWith(n,r)}:function(t,n,r){return t.slice(r,r+n.length)===n},Qt=Ci?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},cn=Ri?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}},Gi=Mi?function(t){return t.trimStart()}:function(t){return t.replace(Li,"")},Fi=Di?function(t){return t.trimEnd()}:function(t){return t.replace(Oi,"")};function un(e,t){return new RegExp(e,t)}var Kt;Jt?(Zt=un("([^\\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=ln(t,n);if(i===void 0||pn(i)||Hi(i))break;r.push(i),n+=i>=65536?2:1}return Qt.apply(void 0,r)};var Zt,fn=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()&&ji(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||!Vi(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=Fi(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&&an(u?.style,"::",0)){var S=Gi(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?zr(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:cn(P.val),location:L},err:null}:{val:{type:b.plural,value:r,options:cn(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=en(t)}catch{return this.error(E.INVALID_NUMBER_SKELETON,n)}return{val:{type:ce.number,tokens:r,location:n,parsedOptions:this.shouldParseSkeletons?on(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,ki(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=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(an(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()&&pn(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 Vi(e){return er(e)||e===47}function ji(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 pn(e){return e>=9&&e<=13||e===32||e===133||e>=8206&&e<=8207||e===8232||e===8233}function Hi(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 mn(e,t){t===void 0&&(t={}),t=y({shouldParseSkeletons:!0,requiresOtherClause:!0},t);var n=new fn(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:qi,r=t&&t.serializer?t.serializer:$i,i=t&&t.strategy?t.strategy:Xi;return i(e,{cache:n,serializer:r})}function Wi(e){return e==null||typeof e=="number"||typeof e=="boolean"}function hn(e,t,n,r){var i=Wi(r)?r:n(r),o=t.get(i);return typeof o>"u"&&(o=e.call(this,r),t.set(i,o)),o}function dn(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 Xi(e,t){var n=e.length===1?hn:dn;return rr(e,this,n,t.cache.create(),t.serializer)}function Yi(e,t){return rr(e,this,dn,t.cache.create(),t.serializer)}function Bi(e,t){return rr(e,this,hn,t.cache.create(),t.serializer)}var $i=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 qi={create:function(){return new nr}},rt={variadic:Yi,monadic:Bi};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 En=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 gn=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 zi(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=_n(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(to);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 no(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,io(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 io(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}),lo=[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}},uo=(e,t)=>e.indexOf(`'${t}'`)===0,fo=(e,t=!0)=>{let n=e.replace(/'.*?'/,"").trim(),r=wn(n);return!!r?t||(n=n.replace(/[,\.]0+/,r)):n=n.replace(/\s?(#.*0)(?!\s)?/,"$&"+mo(e)),n},po=e=>{let t=ho(e),n=uo(e,t),r=e.replace(/'.*?'/,""),i=Pn.test(r)||bn.test(r);return{currencySymbol:t,isCurrencyFirst:n,hasCurrencySpace:i}},An=e=>e.replace(Pn,Tn).replace(bn,Tn),mo=e=>e.match(/#(.?)#/)?.[1]===vn?so:vn,ho=e=>e.match(/'(.*?)'/)?.[1]??"",wn=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}=po(e),u=n?wn(e):"",c=fo(e,n),p=n?2:0,f=o(t,{currencySymbol:s}),h=r?f.toLocaleString("hi-IN",{minimumFractionDigits:p,maximumFractionDigits:p}):Sn(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 Ln=e=>{let{commitment:t,term:n,usePrecision:r}=e,i=ao[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}=lo.find(({accept:c})=>c(a));if(!l)throw new Error(`Missing rounding rule for: ${JSON.stringify(a)}`);return(co[s]??(c=>c))(l(a))})},On=({commitment:e,term:t,...n})=>nt(n,ar[e]?.[t]),Nn=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 Eo={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}"},go=Wr("ConsonantTemplates/price"),xo=/<.+?>/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"},yo="TAX_EXCLUSIVE",_o=e=>Hr(e)?Object.entries(e).filter(([,t])=>xe(t)||Ye(t)||t===!0).reduce((t,[n,r])=>t+` ${n}${r===!0?"":'="'+Vr(r)+'"'}`,""):"",X=(e,t,n,r=!1)=>`${r?An(t):t??""}`;function So(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={...Eo,...u},C=`${l.toLowerCase()}-${r.toUpperCase()}`;function P(Q,Pt){let bt=T[Q];if(bt==null)return"";try{return new yn(bt.replace(xo,""),C).format(Pt)}catch{return go.error("Failed to format literal:",bt),""}}let L=t&&h?h:f,D=e?Ln:On;n&&(D=Nn);let{accessiblePrice:j,recurrenceTerm:Z,...te}=D({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===yo?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 So(W,{...te,accessibleLabel:H,recurrenceLabel:oe,perUnitLabel:se,taxInclusivityLabel:J},w);let{currencySymbol:de,decimals:Ve,decimalsDelimiter:je,hasCurrencySpace:we,integer:Tt,isCurrencyFirst:Zn}=te,Ee=[Tt,je,Ve];Zn?(Ee.unshift(we?"\xA0":""),Ee.unshift(de)):(Ee.push(we?"\xA0":""),Ee.push(de)),Ee.push(oe,se,J);let Jn=Ee.join("");return X(W,Jn,w)},Cn=()=>(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=Cn(),ur=fe({displayOptical:!0}),fr=fe({displayStrikethrough:!0}),pr=fe({displayAnnual:!0});var vo=(e,t)=>{if(!(!_e(e)||!_e(t)))return Math.floor((t-e)/t*100)},Rn=()=>(e,t,n)=>{let{price:r,priceWithoutDiscount:i}=t,o=vo(r,i);return o===void 0?'':`${o}%`};var mr=Rn();var{freeze:ke}=Object,Y=ke({...ae}),B=ke({...V}),pe={STAGE:"STAGE",PRODUCTION:"PRODUCTION",LOCAL:"LOCAL"},hr=ke({...R}),dr=ke({...Yr}),Er=ke({...O});var Pr={};Kn(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:()=>To,EVENT_TYPE_FAILED:()=>ct,EVENT_TYPE_PENDING:()=>lt,EVENT_TYPE_READY:()=>Te,EVENT_TYPE_RESOLVED:()=>ut,LOG_NAMESPACE:()=>yr,Landscape:()=>me,PARAM_AOS_API_KEY:()=>Po,PARAM_ENV:()=>_r,PARAM_LANDSCAPE:()=>Sr,PARAM_WCS_API_KEY:()=>bo,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",To="wcms:commerce:error",ct="wcms:placeholder:failed",lt="wcms:placeholder:pending",Te="wcms:commerce:ready",ut="wcms:placeholder:resolved",yr="wcms/commerce",_r="commerce.env",Sr="commerce.landscape",Po="commerce.aosKey",bo="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",me={DRAFT:"DRAFT",PUBLISHED:"PUBLISHED"};var br={clientId:"merch-at-scale",delimiter:"\xB6",ignoredProperties:["analytics","literals"],serializableTypes:["Array","Object"],sampleRate:30,tags:"consumer=milo/commerce"},In=new Set,Ao=e=>e instanceof Error||typeof e.originatingRequest=="string";function Mn(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 wo(e,t){if(!br.ignoredProperties.includes(e))return Mn(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&&(Ao(f)?a:u).push(f)}),a.length&&(l+=" ",l+=a.map(Mn).join(" "));let{pathname:c,search:p}=window.location;l+=`${t}page=`,l+=c+p,u.length&&(l+=`${t}facts=`,l+=JSON.stringify(u,wo)),In.has(l)||(In.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:me.PUBLISHED,wcsBufferLimit:1});function Dn(e,{once:t=!1}={}){let n=null;function r(){let i=document.querySelector(ee);i!==n&&(n=i,i&&e(i))}return document.addEventListener(Te,r,{once:t}),ie(r),()=>document.removeEventListener(Te,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(ve).filter(_e);return n.length||(n=[t]),n}function ft(e){return e==null?[]:(Array.isArray(e)?e:String(e).split(",")).filter(Gt)}function U(){return window.customElements.get(ee)?.instance}var Lo="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 Un({locale:e={}}={}){if(!e.prefix)return{country:x.country,language:x.language,locale:Lo};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?me.PUBLISHED:ne(N(Sr,t),me,x.landscape),P=ve(N("wcsBufferDelay",t),x.wcsBufferDelay),L=ve(N("wcsBufferLimit",t),x.wcsBufferLimit);return{...Un({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 Gn="debug",Oo="error",No="info",Co="warn",Ro=Date.now(),Lr=new Set,Or=new Set,kn=new Map,Fe=Object.freeze({DEBUG:Gn,ERROR:Oo,INFO:No,WARN:Co}),Fn={append({level:e,message:t,params:n,timestamp:r,source:i}){console[e](`${r}ms [${i}] %c${t}`,"font-weight: bold;",...n)}},Vn={filter:({level:e})=>e!==Gn},Io={filter:()=>!1};function Mo(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()-Ro}}function Do(e){[...Or].every(t=>t(e))&&Lr.forEach(t=>t(e))}function jn(e){let t=(kn.get(e)??0)+1;kn.set(e,t);let n=`${e} #${t}`,r=o=>(s,...a)=>Do(Mo(o,s,e,a,n)),i=Object.seal({id:n,namespace:e,module(o){return jn(`${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 Uo(e={}){let{name:t}=e,n=v(N("commerce.debug",{search:!0,storage:!0}),t===pt.LOCAL);return mt(n?Fn:Vn),t===pt.PROD&&mt(Ar),M}function ko(){Lr.clear(),Or.clear()}var M={...jn(yr),Level:Fe,Plugins:{consoleAppender:Fn,debugFilter:Vn,quietFilter:Io,lanaAppender:Ar},init:Uo,reset:ko,use:mt};var Go={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},Fo={[$]:it,[q]:ot,[z]:st},Vo={[$]:ct,[q]:lt,[z]:ut},Et=new WeakMap;function k(e){if(!Et.has(e)){let t=M.module(e.constructor.is);Et.set(e,{changes:new Map,connected:!1,dispose:ye,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=k(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(Vo[i],{bubbles:!0}))}function dt(e){let t=Et.get(e);[$,q,z].forEach(n=>{e.classList.toggle(Fo[n],n===t.state)})}var jo={get error(){return k(this).error},get log(){return k(this).log},get options(){return k(this).options},get state(){return k(this).state},get value(){return k(this).value},attributeChangedCallback(e,t,n){k(this).changes.set(e,n),this.requestUpdate()},connectedCallback(){k(this).dispose=Dn(()=>this.requestUpdate(!0))},disconnectedCallback(){let e=k(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}=k(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=k(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=k(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=k(this);return t.version++,e&&(t.options=e),t.state=q,dt(this),ie(()=>ht(this)),t.version},requestUpdate(e=!1){if(!this.isConnected||!U())return;let t=k(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 Hn(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,Hn(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(jo)),i=Object.defineProperties(e,Object.getOwnPropertyDescriptors(Go)),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,Hn(t)),e):null}var Ho="download",Wo="upgrade",he,be=class be extends HTMLAnchorElement{constructor(){super();Dr(this,he,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=U();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,he))==null||r.call(this,n)}async render(n={}){if(!this.isConnected)return!1;let r=U();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=U();if(!a)return!1;if(r={...JSON.parse(this.placeholder.dataset.extraOptions??"null"),...r,...i},s??(s=this.placeholder.togglePending(r)),At(this,he)&&wt(this,he,void 0),o){this.classList.remove(Ho,Wo),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,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=U();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}};he=new WeakMap,K(be,"is","checkout-link"),K(be,"tag","a");var Nr=be,St=xt(Nr);var Wn=[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],Xo={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=U();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(Wn.includes(t)||Wn.includes(o))return!0;let s=Xo[`${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=U();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=U();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=U();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 Xn({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:D=_,quantity:j=A,entitlement:Z,upgrade:te,modal:H,perpetual:oe,promotionCode:se=S,wcsOsi:J,extraOptions:W,...de}=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=Se({...de,extraOptions:W,checkoutClientId:p,checkoutMarketSegment:w,country:L,quantity:Pe(j,x.quantity),checkoutWorkflow:Ve,checkoutWorkflowStep:je,language:D,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=U(),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:D,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:D}:{id:D,quantity:T[0]})}else L.items.push(...u.map(({offerId:D},j)=>({id:D,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 Yo({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 Bo(e){return e.then(()=>window.adobeIMS?.isSignedInUser()??!1)}function $o(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 Yn({}){let e=Yo(),t=Bo(e),n=$o(t);return{imsReadyPromise:e,imsSignedInPromise:t,imsCountryPromise:n}}function qo(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 Bn(e){let n=await(e.priceLiteralsPromise||qo(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 $n({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:D=u,language:j=_,perpetual:Z,promotionCode:te=S,quantity:H=A,template:oe,wcsOsi:se,...J}=Object.assign({},l?.dataset??{},a??{}),W=Se({...J,country:D,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 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=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,Se(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 qn({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 D={country:c,locale:_,offerSelectorIds:[]};c!=="GB"&&(D.language=p),L={options:D,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 G=class extends HTMLElement{get isWcmsCommerce(){return!0}};K(G,"instance"),K(G,"promise",null);window.customElements.define(ee,G);async function zo(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 Bn(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 G.instance=Object.defineProperties(s,Object.getOwnPropertyDescriptors({...Xn(a,t),...Yn(a),...$n(a),...qn(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(Te,{bubbles:!0,cancelable:!1,detail:G.instance});G.instance.dispatchEvent(l)}),G.instance}function zn(){document.head.querySelector(ee)?.remove(),G.promise=null,M.reset()}function Rr(e,t){let n=re(e)?e():null,r=re(t)?t():{};return n&&(r.force&&zn(),zo(n,r).then(i=>{Rr.resolve(i)})),G.promise??(G.promise=new Promise(i=>{Rr.resolve=i})),G.promise}export{St as CheckoutLink,Y as CheckoutWorkflow,B as CheckoutWorkflowStep,x as Defaults,vt as InlinePrice,me as Landscape,M as Log,ee as TAG_NAME_SERVICE,hr as WcsCommitment,dr as WcsPlanType,Er as WcsTerm,$e as applyPlanType,Un as getLocaleSettings,wr as getSettings,Rr as init,zn as reset}; +`,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/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/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/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 714ce692bd..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); } 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 9dcb50eaad..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'; @@ -48,7 +47,7 @@ afterEach(() => { }); beforeEach(async () => { - await mockFetch(withWcs, withLiterals); + await mockFetch(withWcs); mockLana(); }); 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 3fb92366e1..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'; @@ -32,7 +31,7 @@ afterEach(() => { }); beforeEach(async () => { - 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/mas/src/mas.js b/libs/features/mas/mas/src/mas.js index a2b5c34a42..b045b7d09d 100644 --- a/libs/features/mas/mas/src/mas.js +++ b/libs/features/mas/mas/src/mas.js @@ -10,13 +10,9 @@ const isStage = searchParams.get('env') === 'stage'; const envName = isStage ? 'stage' : 'prod'; const commerceEnv = isStage ? 'STAGE' : 'PROD'; -const priceLiteralsPromise = fetch( - 'https://www.adobe.com/federal/commerce/price-literals.json', -).then((response) => response.json().then(({ data }) => data)); - const config = () => ({ env: { name: envName }, - commerce: { 'commerce.env': commerceEnv, priceLiteralsPromise }, + commerce: { 'commerce.env': commerceEnv }, locale: { prefix: locale }, }); 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/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/web-components/test/merch-card-collection.test.html.js b/libs/features/mas/web-components/test/merch-card-collection.test.html.js index 082651ae66..ee0dfcbfc5 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,7 +16,6 @@ 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 './mas.js'; const searchParams = new URLSearchParams(document.location.search); @@ -75,7 +74,7 @@ const shouldSkipTests = sessionStorage.getItem('skipTests') ? 'true' : 'false'; runTests(async () => { await toggleLargeDesktop(); mockLana(); - await mockFetch(withWcs, withLiterals); + await mockFetch(withWcs); await mas(); if (shouldSkipTests !== 'true') { describe('merch-card-collection web component', () => { diff --git a/libs/features/mas/web-components/test/merch-card.mini-compare.test.html.js b/libs/features/mas/web-components/test/merch-card.mini-compare.test.html.js index 85d7ec0df9..a292ec36ae 100644 --- a/libs/features/mas/web-components/test/merch-card.mini-compare.test.html.js +++ b/libs/features/mas/web-components/test/merch-card.mini-compare.test.html.js @@ -4,7 +4,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'; @@ -12,7 +11,6 @@ import '../src/merch-quantity-select.js'; import { appendMiloStyles } from './utils.js'; import { mockIms } from './mocks/ims.js'; -import { withLiterals } from './mocks/literals.js'; import { withWcs } from './mocks/wcs.js'; import mas from './mas.js'; @@ -21,7 +19,7 @@ const skipTests = sessionStorage.getItem('skipTests'); runTests(async () => { mockIms(); mockLana(); - await mockFetch(withWcs, withLiterals); + await mockFetch(withWcs); await mas(); if (skipTests !== null) { appendMiloStyles(); diff --git a/libs/features/mas/web-components/test/merch-card.mini-inline-heading.test.html.js b/libs/features/mas/web-components/test/merch-card.mini-inline-heading.test.html.js index a61732b4bd..67aee9c7b0 100644 --- a/libs/features/mas/web-components/test/merch-card.mini-inline-heading.test.html.js +++ b/libs/features/mas/web-components/test/merch-card.mini-inline-heading.test.html.js @@ -3,7 +3,6 @@ import { runTests } from '@web/test-runner-mocha'; 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'; @@ -12,7 +11,6 @@ import '../src/merch-quantity-select.js'; import { appendMiloStyles } from './utils.js'; import { mockIms } from './mocks/ims.js'; import { withWcs } from './mocks/wcs.js'; -import { withLiterals } from './mocks/literals.js'; import mas from './mas.js'; const skipTests = sessionStorage.getItem('skipTests'); @@ -20,7 +18,7 @@ const skipTests = sessionStorage.getItem('skipTests'); runTests(async () => { mockIms(); mockLana(); - await mockFetch(withWcs, withLiterals); + await mockFetch(withWcs); await mas(); if (skipTests !== null) { appendMiloStyles(); 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 85ea6f343b..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,7 +10,6 @@ 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 './mas.js'; function getDynamicElements(merchCard, merchOfferSelect) { @@ -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 07e9095402..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,14 +3,12 @@ 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 './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 6e7585b927..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,9 +15,7 @@ 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 './mas.js'; import { getTemplateContent } from './utils.js'; @@ -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 5bf16f6e2b..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,7 +31,6 @@ 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 './mas.js'; const ABM = 'ABM'; @@ -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/plans-modal.test.html.js b/libs/features/mas/web-components/test/plans-modal.test.html.js index ab3836525e..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 './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/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/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" -} From a234e2b61d3b69271f933678d41b8011b3d79c70 Mon Sep 17 00:00:00 2001 From: Mira Fedas <30750556+mirafedas@users.noreply.github.com> Date: Wed, 11 Sep 2024 11:16:06 +0200 Subject: [PATCH 50/66] MWPW-156788: Unable to scroll page with active Sort filter (#2758) * moved merch-card class toggling to other methods * added unit test for merch card collection on phones & tablets * added unit test for closing the filters modal * corrected lit import for merch sidenav --- libs/deps/mas/merch-sidenav.js | 10 +++--- libs/features/mas/web-components/build.mjs | 16 ++++++++- .../src/sidenav/merch-sidenav.js | 9 ++--- .../test/merch-card-collection.test.html.js | 34 ++++++++++++++++--- 4 files changed, 52 insertions(+), 17 deletions(-) diff --git a/libs/deps/mas/merch-sidenav.js b/libs/deps/mas/merch-sidenav.js index ec6cb670c6..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"../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"../lit-all.min.js";var c=L` +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"../lit-all.min.js";var r=class{co line-height: 32px; color: #747474; } -`;import{html as N,LitElement as R}from"../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"../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` +`;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; @@ -36,7 +36,7 @@ import{html as k,css as H,LitElement as P}from"../lit-all.min.js";var r=class{co > ${this.sidenavListTitle?C`

${this.sidenavListTitle}

`:""} -
`}};customElements.define("merch-sidenav-list",m);import{html as V,LitElement as O,css as I}from"../lit-all.min.js";var u=class extends O{static properties={sidenavCheckboxTitle:{type:String},label:{type:String},deeplink:{type:String},selectedValues:{type:Array,reflect:!0},value:{type:String}};static styles=I` +
`}};customElements.define("merch-sidenav-list",m);import{html as V,LitElement as O,css as I}from"/libs/deps/lit-all.min.js";var u=class extends O{static properties={sidenavCheckboxTitle:{type:String},label:{type:String},deeplink:{type:String},selectedValues:{type:Array,reflect:!0},value:{type:String}};static styles=I` :host { display: block; contain: content; @@ -66,7 +66,7 @@ import{html as k,css as H,LitElement as P}from"../lit-all.min.js";var r=class{co >
- `}};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; } @@ -138,4 +138,4 @@ import{html as k,css as H,LitElement as P}from"../lit-all.min.js";var r=class{co `}get asAside(){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/features/mas/web-components/build.mjs b/libs/features/mas/web-components/build.mjs index 3b21388f48..5283a4bc9a 100644 --- a/libs/features/mas/web-components/build.mjs +++ b/libs/features/mas/web-components/build.mjs @@ -57,7 +57,7 @@ Promise.all([ minify: true, outfile: `${outfolder}/merch-sidenav.js`, format: 'esm', - plugins: [rewriteImports()], + plugins: [rewriteImportsToLibsFolder()], external: ['lit'], }), buildLitComponent('merch-icon'), @@ -84,3 +84,17 @@ function rewriteImports(rew) { }, }; } + +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/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/test/merch-card-collection.test.html.js b/libs/features/mas/web-components/test/merch-card-collection.test.html.js index ee0dfcbfc5..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 @@ -72,14 +72,40 @@ let merchCards; const shouldSkipTests = sessionStorage.getItem('skipTests') ? 'true' : 'false'; runTests(async () => { - await toggleLargeDesktop(); + let render; mockLana(); 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); }); From 7921bf87c42491fbce899c8d850e323a342eea65 Mon Sep 17 00:00:00 2001 From: Bozo Jovicic <37440641+bozojovicic@users.noreply.github.com> Date: Wed, 11 Sep 2024 11:16:14 +0200 Subject: [PATCH 51/66] MWPW-157210 STE Promo card not appearing (#2837) * MWPW-157210 STE Promo card not appearing * MWPW-157210 STE Promo card not appearing * MWPW-157210 STE Promo card not appearing * Trigger Build --------- Co-authored-by: Bozo Jovicic --- .../merch-card-collection.js | 3 ++- libs/utils/helpers.js | 18 ++++++++++++++++++ test/utils/helpers.test.js | 14 ++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 test/utils/helpers.test.js 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/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/test/utils/helpers.test.js b/test/utils/helpers.test.js new file mode 100644 index 0000000000..d49a0ec4b3 --- /dev/null +++ b/test/utils/helpers.test.js @@ -0,0 +1,14 @@ +import { expect } from '@esm-bundle/chai'; +import { overrideUrlOrigin } from '../../libs/utils/helpers.js'; + +describe('overrideUrlOrigin', () => { + it('Change origin to http://localhost:2000', async () => { + const link = overrideUrlOrigin('http://www.qa.adobe.com/some/page.html?a=b#hash'); + expect(link).to.equal('http://localhost:2000/some/page.html?a=b#hash'); + }); + + it('Ignore relative URLs', async () => { + const link = overrideUrlOrigin('/some/page.html?a=b#hash'); + expect(link).to.equal('/some/page.html?a=b#hash'); + }); +}); From e03455463a5e69f612422863d9174f5116a30f55 Mon Sep 17 00:00:00 2001 From: Rares Munteanu Date: Wed, 11 Sep 2024 11:16:21 +0200 Subject: [PATCH 52/66] [MWPW-157740] Table pricing gutter (#2851) --- libs/blocks/table/table.css | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/blocks/table/table.css b/libs/blocks/table/table.css index 391c70fb1a..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)) { From 4a4a12e70929d9464e409796abccbc42d7836c85 Mon Sep 17 00:00:00 2001 From: Vivian A Goodrich <101133187+vgoodric@users.noreply.github.com> Date: Wed, 11 Sep 2024 03:16:28 -0600 Subject: [PATCH 53/66] MWPW-158148 [MEP] Can't use ul, ol or li in MEP selectorsf (#2864) * add ol, ul and li * add unit test --- libs/features/personalization/personalization.js | 1 + .../personalization/modifyNonFragmentSelector.test.js | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/libs/features/personalization/personalization.js b/libs/features/personalization/personalization.js index 71857e6c3b..091e6ad027 100644 --- a/libs/features/personalization/personalization.js +++ b/libs/features/personalization/personalization.js @@ -366,6 +366,7 @@ function modifySelectorTerm(termParam) { 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() : ''; diff --git a/test/features/personalization/modifyNonFragmentSelector.test.js b/test/features/personalization/modifyNonFragmentSelector.test.js index f9345c3eb1..fd3cdbf893 100644 --- a/test/features/personalization/modifyNonFragmentSelector.test.js +++ b/test/features/personalization/modifyNonFragmentSelector.test.js @@ -135,6 +135,14 @@ const values = [ b: 'custom-block3', a: '.custom-block:nth-child(3 of .custom-block)', }, + { + b: 'any-marquee ol li:nth-child(2)', + a: '[class*="marquee"] ol li:nth-child(2)', + }, + { + b: 'any-marquee ul li:nth-child(2)', + a: '[class*="marquee"] ul li:nth-child(2)', + }, ]; describe('test different values', () => { values.forEach((value) => { From 9c32237bdedf6904fffe8abe83edd4ba4d7d2cf2 Mon Sep 17 00:00:00 2001 From: Robert Bogos <146744221+robert-bogos@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:18:36 +0300 Subject: [PATCH 54/66] [MWPW-157347] Environment relative URLs (#2834) * environment aware urls * added domains map entry for testing * fixed unit tests * hotfix * moved the convert urls logic to a separate function * added early returns * hotfix * relative urls unit tests * removed fork domain --------- Co-authored-by: milo-pr-merge[bot] <169241390+milo-pr-merge[bot]@users.noreply.github.com> --- libs/scripts/scripts.js | 18 +++++--- libs/utils/utils.js | 21 +++++++-- test/utils/utils.test.js | 97 +++++++++++++++++++++++++++++++++++----- 3 files changed, 116 insertions(+), 20 deletions(-) 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/utils/utils.js b/libs/utils/utils.js index 3b57b87e96..07fee41492 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -638,17 +638,32 @@ const decorateCopyLink = (a, evt) => { }); }; +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', ''); diff --git a/test/utils/utils.test.js b/test/utils/utils.test.js index d3132ea524..669817fe6b 100644 --- a/test/utils/utils.test.js +++ b/test/utils/utils.test.js @@ -10,6 +10,30 @@ const config = { codeRoot: '/libs', locales: { '': { ietf: 'en-US', tk: 'hah7vzn.css' } }, }; +const stageDomainsMap = { + 'www.stage.adobe.com': { + 'www.adobe.com': 'origin', + 'business.adobe.com': 'business.stage.adobe.com', + 'blog.adobe.com': 'blog.stage.adobe.com', + 'helpx.adobe.com': 'helpx.stage.adobe.com', + 'news.adobe.com': 'news.stage.adobe.com', + }, + '--bacom--adobecom.hlx.live': { + 'business.adobe.com': 'origin', + 'blog.adobe.com': 'main--blog--adobecom.hlx.live', + 'helpx.adobe.com': 'main--helpx--adobecom.hlx.live', + 'news.adobe.com': 'main--news--adobecom.hlx.live', + }, + '--blog--adobecom.hlx.page': { + 'blog.adobe.com': 'origin', + 'business.adobe.com': 'main--bacom--adobecom.hlx.page', + 'helpx.adobe.com': 'main--helpx--adobecom.hlx.page', + 'news.adobe.com': 'main--news--adobecom.hlx.page', + }, + '.business-graybox.adobe.com': { 'business.adobe.com': 'origin' }, +}; +const prodDomains = ['www.adobe.com', 'business.adobe.com', 'blog.adobe.com', 'helpx.adobe.com', 'news.adobe.com']; +const externalDomains = ['external1.com', 'external2.com']; const ogFetch = window.fetch; describe('Utils', () => { @@ -114,7 +138,7 @@ describe('Utils', () => { await waitForElement('.login-action'); const login = document.querySelector('.login-action'); utils.decorateLinks(login); - expect(login.href).to.equal('https://www.stage.adobe.com/'); + expect(login.href).to.equal('https://www.adobe.com/'); }); it('Implements a copy link action', async () => { await waitForElement('.copy-action'); @@ -473,22 +497,71 @@ describe('Utils', () => { expect(document.querySelector('.quote.hide-block')).to.be.null; }); - it('should convert prod links to stage links on stage env', async () => { - 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', + it('should convert links on stage when stageDomainsMap provided', async () => { + const stageConfig = { + ...config, + env: { name: 'stage' }, + stageDomainsMap, }; - utils.setConfig({ + + Object.entries(stageDomainsMap).forEach(([hostname, domainsMap]) => { + const anchors = Object.keys(domainsMap).map((d) => utils.createTag('a', { href: `https://${d}` })); + const externalAnchors = externalDomains.map((url) => utils.createTag('a', { href: url })); + + utils.convertStageLinks({ + anchors: [...anchors, ...externalAnchors], + config: stageConfig, + hostname, + }); + + anchors.forEach((a, index) => { + const expectedDomain = Object.values(domainsMap)[index]; + expect(a.href).to.contain(expectedDomain === 'origin' ? hostname : expectedDomain); + }); + + externalAnchors.forEach((a) => expect(a.href).to.equal(a.href)); + }); + }); + + it('should not convert links on stage when no stageDomainsMap provided', async () => { + const stageConfig = { ...config, env: { name: 'stage' }, + }; + + Object.entries(stageDomainsMap).forEach(([hostname, domainsMap]) => { + const anchors = Object.keys(domainsMap).map((d) => utils.createTag('a', { href: `https://${d}` })); + const externalAnchors = externalDomains.map((url) => utils.createTag('a', { href: url })); + + utils.convertStageLinks({ + anchors: [...anchors, ...externalAnchors], + config: stageConfig, + hostname, + }); + + [...anchors, ...externalAnchors].forEach((a) => expect(a.href).to.equal(a.href)); + }); + }); + + it('should not convert links on prod', async () => { + const prodConfig = { + ...config, + env: { name: 'prod' }, stageDomainsMap, + }; + + prodDomains.forEach((hostname) => { + const anchors = prodDomains.map((d) => utils.createTag('a', { href: `https://${d}` })); + const externalAnchors = externalDomains.map((url) => utils.createTag('a', { href: url })); + + utils.convertStageLinks({ + anchors: [...anchors, ...externalAnchors], + config: prodConfig, + hostname, + }); + + [...anchors, ...externalAnchors].forEach((a) => expect(a.href).to.equal(a.href)); }); - const links = Object.keys(stageDomainsMap).map((prodDom) => document.body.appendChild(createTag('a', { href: `https://${prodDom}`, 'data-prod-dom': prodDom }))); - await utils.decorateLinks(document.body); - links.forEach((l) => expect(l.hostname === stageDomainsMap[l.dataset.prodDom]).to.be.true); }); }); From c7107532b6a9f8860679aec89695ef4069491cf2 Mon Sep 17 00:00:00 2001 From: Raghav Sharma <118168183+sharmrj@users.noreply.github.com> Date: Wed, 11 Sep 2024 17:48:43 +0530 Subject: [PATCH 55/66] MWPW-151534 [PEP Prompt] Dismissal action on close button of prompt not seen in windows (#2866) * Made the pep close button focus a consistent color * update franklin cache with dummy change * revert previous change --- libs/features/webapp-prompt/webapp-prompt.css | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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 { From 3679fd07498889cb974f46602dace93b3c577445 Mon Sep 17 00:00:00 2001 From: Vivian A Goodrich <101133187+vgoodric@users.noreply.github.com> Date: Wed, 11 Sep 2024 13:39:40 -0600 Subject: [PATCH 56/66] REVERT MWPW-157686 [MEP] Cannot spoof an experience that exists in manifest but not in Target #2844 (#2870) revert --- libs/features/personalization/personalization.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/features/personalization/personalization.js b/libs/features/personalization/personalization.js index 091e6ad027..b51101f720 100644 --- a/libs/features/personalization/personalization.js +++ b/libs/features/personalization/personalization.js @@ -894,6 +894,8 @@ export function cleanAndSortManifestList(manifests) { freshManifest = manifestObj[manifest.manifestPath]; } freshManifest.name = fullManifest.name; + freshManifest.selectedVariantName = fullManifest.selectedVariantName; + freshManifest.selectedVariant = freshManifest.variants[freshManifest.selectedVariantName]; manifestObj[manifest.manifestPath] = freshManifest; } else { manifestObj[manifest.manifestPath] = manifest; From a7c31b7acb30e0556b32252826e2ddcd56ec21ab Mon Sep 17 00:00:00 2001 From: sharathkannan <138484653+sharath-kannan@users.noreply.github.com> Date: Thu, 12 Sep 2024 22:30:29 +0530 Subject: [PATCH 57/66] fix(MWPW-155964):Lana log error type changed to info. (#2818) * lana log error type changed * modified lana logs * added a few unit tests for lana logs * Updated tag in utils.js * linting error fixed * linting error fix 2 * unit test fix * added unit test for vimeo --- libs/blocks/article-feed/article-helpers.js | 2 +- .../event-rich-results/event-rich-results.js | 2 +- libs/blocks/faas/utils.js | 2 +- libs/blocks/library-config/lists/templates.js | 2 +- .../mobile-app-banner/mobile-app-banner.js | 2 +- libs/blocks/path-finder/path-finder.js | 2 +- libs/blocks/preflight/panels/seo.js | 4 +-- libs/blocks/quiz-entry/mlField.js | 2 +- libs/blocks/quiz-entry/quiz-entry.js | 2 +- libs/blocks/quiz-entry/quizPopover.js | 2 +- libs/blocks/quiz-results/quiz-results.js | 4 +-- libs/blocks/tag-selector/tag-selector.js | 2 +- libs/blocks/video-metadata/video-metadata.js | 8 +++--- libs/blocks/vimeo/vimeo.js | 2 +- libs/features/footer-promo.js | 2 +- libs/martech/martech.js | 4 +-- .../article-header/article-header.test.js | 28 +++++++++++++------ .../global-footer/global-footer.test.js | 6 ++-- test/blocks/quiz-results/mocks/body.html | 10 +++++++ test/blocks/quiz-results/quiz-results.test.js | 7 +++++ test/blocks/vimeo/vimeo.test.html | 12 +++++++- .../webapp-prompt/webapp-prompt.test.js | 4 +-- 22 files changed, 74 insertions(+), 37 deletions(-) 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/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/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/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/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.js b/libs/blocks/quiz-entry/quiz-entry.js index 6c9c9394ba..ea83b47c60 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); } 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-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/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/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.js b/libs/blocks/vimeo/vimeo.js index deabe6bb85..10c55b599d 100644 --- a/libs/blocks/vimeo/vimeo.js +++ b/libs/blocks/vimeo/vimeo.js @@ -36,7 +36,7 @@ class LiteVimeo extends HTMLElement { this.style.backgroundImage = `url("${thumbnailUrl}")`; }) .catch((e) => { - window.lana.log(`Error fetching Vimeo thumbnail: ${e}`, { tags: 'errorType=info,module=vimeo' }); + window.lana.log(`Error fetching Vimeo thumbnail: ${e}`, { tags: 'vimeo', errorType: 'i' }); }); } 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/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/test/blocks/article-header/article-header.test.js b/test/blocks/article-header/article-header.test.js index cbcf096c1f..7c0a20bd17 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 () => { @@ -111,7 +122,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/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/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/vimeo/vimeo.test.html b/test/blocks/vimeo/vimeo.test.html index 50504a2436..8eb108eb54 100644 --- a/test/blocks/vimeo/vimeo.test.html +++ b/test/blocks/vimeo/vimeo.test.html @@ -15,9 +15,11 @@ diff --git a/test/features/webapp-prompt/webapp-prompt.test.js b/test/features/webapp-prompt/webapp-prompt.test.js index c065acd726..e7e8308cfa 100644 --- a/test/features/webapp-prompt/webapp-prompt.test.js +++ b/test/features/webapp-prompt/webapp-prompt.test.js @@ -239,7 +239,7 @@ describe('PEP', () => { }), }); expect(window.lana.log.getCalls().find((c) => c.args[0].includes('Error on getting anchor state'))).to.exist; - expect(window.lana.log.getCalls().find((c) => c.args[1].tags.includes('errorType=error,module=pep'))).to.exist; + expect(window.lana.log.getCalls().find((c) => c.args[1].tags.includes('pep'))).to.exist; }); it('should send log when cannot fetch content for prompt', async () => { @@ -256,7 +256,7 @@ describe('PEP', () => { }); await initPep({}); expect(window.lana.log.getCalls().find((c) => c.args[0].includes('Error fetching content for prompt'))).to.exist; - expect(window.lana.log.getCalls().find((c) => c.args[1].tags.includes('errorType=error,module=pep'))).to.exist; + expect(window.lana.log.getCalls().find((c) => c.args[1].tags.includes('pep'))).to.exist; }); }); }); From 8fd5925aa2bbf6b9bb757c3305727291ecec82b5 Mon Sep 17 00:00:00 2001 From: Sean Archibeque Date: Thu, 12 Sep 2024 11:00:36 -0600 Subject: [PATCH 58/66] MWPW-154980 - Milo advanced page publishing (#2846) * sidekick publish button state * bulk publish publish config errors * dial in functionality add custom messages and denylist * publish permission in bulk pub testing * test permissions and utils coverage * remove extra variable assignment * add no signin message * put back timout for init sidekick pub button decoration * fix edge case where sk event isnt fired on refresh * fix unit tests * put back var assignment * dial in user can publish funtion * move publish utility to tools dir * Update tools/utils/utils.js Co-authored-by: Ryan Parrish --------- Co-authored-by: Ryan Parrish --- .../components/bulk-publisher.js | 17 ++++-- libs/blocks/bulk-publish-v2/services.js | 25 ++++++++ libs/tools/utils/publish.js | 32 ++++++++++ libs/utils/sidekick-decorate.js | 61 ++++++++++++++++--- .../bulk-publish-v2/bulk-publish-v2.test.js | 33 ++++++---- .../bulk-publish-v2/mocks/authentication.js | 2 +- test/blocks/bulk-publish-v2/mocks/fetch.js | 3 +- .../mocks/response/permissions.json | 26 ++++++++ tools/utils/utils.js | 14 ++++- 9 files changed, 183 insertions(+), 30 deletions(-) create mode 100644 libs/tools/utils/publish.js create mode 100644 test/blocks/bulk-publish-v2/mocks/response/permissions.json diff --git a/libs/blocks/bulk-publish-v2/components/bulk-publisher.js b/libs/blocks/bulk-publish-v2/components/bulk-publisher.js index c0c88ae5db..712f663fa0 100644 --- a/libs/blocks/bulk-publish-v2/components/bulk-publisher.js +++ b/libs/blocks/bulk-publish-v2/components/bulk-publisher.js @@ -1,7 +1,7 @@ import './job-process.js'; import { LitElement, html } from '../../../deps/lit-all.min.js'; import { getSheet } from '../../../../tools/utils/utils.js'; -import { authenticate, startJob } from '../services.js'; +import { authenticate, getPublishable, startJob } from '../services.js'; import { getConfig } from '../../../utils/utils.js'; import { delay, @@ -95,7 +95,8 @@ class BulkPublish2 extends LitElement { this.validateUrls(); } - setJobErrors(errors) { + setJobErrors(jobErrors, authErrors) { + const errors = [...jobErrors, ...authErrors]; const urls = []; errors.forEach((error) => { const matched = this.urls.filter((url) => { @@ -323,7 +324,8 @@ class BulkPublish2 extends LitElement { class="panel-title" @click=${handleToggle}> - Job Results + ${this.jobs.length ? html`${this.jobs.length}` : ''} + Job Result${this.jobs.length > 1 ? 's' : ''}
{ + /* c8 ignore next 4 */ const loader = this.renderRoot.querySelector('.load-indicator'); const message = this.renderRoot.querySelector('.message'); loader?.classList.add('hide'); @@ -427,6 +431,7 @@ class BulkPublish2 extends LitElement { const canUse = Object.values(this.user.permissions).filter((perms) => perms.canUse); if (canUse.length) return html``; message = 'Current user is not authorized to use Bulk Publishing Tool'; + /* c8 ignore next 3 */ } else { message = 'Please sign in to AEM sidekick to continue'; } diff --git a/libs/blocks/bulk-publish-v2/services.js b/libs/blocks/bulk-publish-v2/services.js index 2889aa5660..76af892bf2 100644 --- a/libs/blocks/bulk-publish-v2/services.js +++ b/libs/blocks/bulk-publish-v2/services.js @@ -1,3 +1,4 @@ +import userCanPublishPage from '../../tools/utils/publish.js'; import { PROCESS_TYPES, getErrorText, @@ -246,8 +247,32 @@ const updateRetry = async ({ queue, urls, process }) => { return newQueue; }; +// publish authentication service +const getPublishable = async ({ urls, process, user }) => { + let publishable = { authorized: [], unauthorized: [] }; + if (!isLive(process)) { + publishable.authorized = urls; + } else { + const { permissions, profile } = user; + const live = { permissions: ['read'] }; + if (permissions?.publish?.canUse) { + live.permissions.push('write'); + } + publishable = await urls.reduce(async (init, url) => { + const result = await init; + const detail = { webPath: new URL(url).pathname, live, profile }; + const { canPublish, message } = await userCanPublishPage(detail); + if (canPublish) result.authorized.push(url); + else result.unauthorized.push({ href: url, message }); + return result; + }, Promise.resolve(publishable)); + } + return publishable; +}; + export { authenticate, + getPublishable, pollJobStatus, startJob, updateRetry, diff --git a/libs/tools/utils/publish.js b/libs/tools/utils/publish.js new file mode 100644 index 0000000000..49acf0928d --- /dev/null +++ b/libs/tools/utils/publish.js @@ -0,0 +1,32 @@ +import { getCustomConfig } from '../../../tools/utils/utils.js'; + +const userCanPublishPage = async (detail, isBulk = true) => { + if (!detail) return false; + const { live, profile, webPath } = detail; + let canPublish = isBulk ? live?.permissions?.includes('write') : true; + let message = 'Publishing is currently disabled for this page'; + const config = await getCustomConfig('/.milo/publish-permissions-config.json'); + const item = config?.urls?.data?.find(({ url }) => ( + url.endsWith('**') ? webPath.includes(url.slice(0, -2)) : url === webPath + )); + if (item) { + canPublish = false; + if (item.message) message = item.message; + const group = config[item.group]; + if (group && profile?.email) { + let isDeny; + const user = group.data?.find(({ allow, deny }) => { + if (deny) { + /* c8 ignore next 3 */ + isDeny = true; + return deny === profile.email; + } + return allow === profile.email; + }); + canPublish = isDeny ? !user : !!user; + } + } + return { canPublish, message }; +}; + +export default userCanPublishPage; diff --git a/libs/utils/sidekick-decorate.js b/libs/utils/sidekick-decorate.js index ec125ef650..acaa428716 100644 --- a/libs/utils/sidekick-decorate.js +++ b/libs/utils/sidekick-decorate.js @@ -1,4 +1,22 @@ +import userCanPublishPage from '../tools/utils/publish.js'; + +const PUBLISH_BTN = '.publish.plugin button'; +const BACKUP_PROFILE = '.profile-email'; +const CONFIRM_MESSAGE = 'Are you sure? This will publish to production.'; + export default function stylePublish(sk) { + const setupPublishBtn = async (page, btn) => { + const { canPublish, message } = await userCanPublishPage(page, false); + btn.setAttribute('disabled', !canPublish); + const messageText = btn.querySelector('span'); + const text = canPublish ? CONFIRM_MESSAGE : message; + if (messageText) { + messageText.innerText = text; + } else { + btn.insertAdjacentHTML('beforeend', `${text}`); + } + }; + const style = new CSSStyleSheet(); style.replaceSync(` :host { @@ -10,19 +28,21 @@ export default function stylePublish(sk) { order: 100; } .publish.plugin button { + position: relative; + } + .publish.plugin button:not([disabled=true]) { background: var(--bg-color); border-color: #b46157; color: var(--text-color); - position: relative; } - .publish.plugin button:hover { + .publish.plugin button:not([disabled=true]):hover { background-color: var(--hlx-sk-button-hover-bg); border-color: unset; color: var(--hlx-sk-button-hover-color); } .publish.plugin button > span { display: none; - background: var(--bg-color); + background: #666; border-radius: 4px; line-height: 1.2rem; padding: 8px 12px; @@ -33,6 +53,9 @@ export default function stylePublish(sk) { width: 150px; white-space: pre-wrap; } + .publish.plugin button:not([disabled=true]) > span { + background: var(--bg-color); + } .publish.plugin button:hover > span { display: block; color: var(--text-color); @@ -41,19 +64,39 @@ export default function stylePublish(sk) { content: ''; border-left: 6px solid transparent; border-right: 6px solid transparent; - border-bottom: 6px solid var(--bg-color); + border-bottom: 6px solid #666; position: absolute; text-align: center; top: -6px; left: 50%; transform: translateX(-50%); } + .publish.plugin button:not([disabled=true]) > span:before { + border-bottom: 6px solid var(--bg-color); + } `); + sk.shadowRoot.adoptedStyleSheets = [style]; - setTimeout(() => { - const btn = sk.shadowRoot.querySelector('.publish.plugin button'); - btn?.insertAdjacentHTML('beforeend', ` - Are you sure? This will publish to production. - `); + + sk.addEventListener('statusfetched', async (event) => { + const page = event?.detail?.data; + const btn = event?.target?.shadowRoot?.querySelector(PUBLISH_BTN); + if (page && btn) { + setupPublishBtn(page, btn); + } + }); + + setTimeout(async () => { + const btn = sk.shadowRoot.querySelector(PUBLISH_BTN); + btn?.setAttribute('disabled', true); + const message = btn?.querySelector('span'); + if (btn && !message) { + const page = { + webPath: window.location.pathname, + // added for edge case where the statusfetched event isnt fired on refresh + profile: { email: sk.shadowRoot.querySelector(BACKUP_PROFILE)?.innerText }, + }; + setupPublishBtn(page, btn); + } }, 500); } diff --git a/test/blocks/bulk-publish-v2/bulk-publish-v2.test.js b/test/blocks/bulk-publish-v2/bulk-publish-v2.test.js index 3b634681d2..7cd56109b4 100644 --- a/test/blocks/bulk-publish-v2/bulk-publish-v2.test.js +++ b/test/blocks/bulk-publish-v2/bulk-publish-v2.test.js @@ -114,6 +114,16 @@ describe('Bulk Publish Tool', () => { await mouseEvent(rootEl.querySelector('.fix-btn')); }); + it('can trigger cannot publish config', async () => { + await clock.runAllAsync(); + await setProcess(rootEl, 'publish'); + await setTextArea(rootEl, 'https://error--milo--adobecom.hlx.page/not/a/valid/path'); + await mouseEvent(rootEl.querySelector('#RunProcess')); + const errors = rootEl.querySelector('.errors'); + expect(errors.querySelector('strong').innerText).to.equal('Publishing disabled until the test is over'); + await mouseEvent(rootEl.querySelector('.fix-btn')); + }); + it('can validate milo urls and enable form', async () => { await clock.runAllAsync(); await setProcess(rootEl, 'publish'); @@ -132,6 +142,17 @@ describe('Bulk Publish Tool', () => { await mouseEvent(rootEl.querySelector('.switch.half')); }); + it('can toggle job timing flyout', async () => { + await clock.runAllAsync(); + const doneJobProcess = rootEl.querySelector('job-process'); + const jobInfo = doneJobProcess?.shadowRoot.querySelector('job-info'); + const timerDetail = jobInfo?.shadowRoot.querySelector('.timer'); + await mouseEvent(timerDetail); + await clock.runAllAsync(); + await mouseEvent(timerDetail); + expect(timerDetail.classList.contains('show-times')).to.be.false; + }); + it('can submit valid bulk preview job', async () => { await clock.runAllAsync(); await setProcess(rootEl, 'preview'); @@ -174,17 +195,6 @@ describe('Bulk Publish Tool', () => { expect(rootEl.querySelectorAll('job-process')).to.have.lengthOf(4); }); - it('can toggle job timing flyout', async () => { - await clock.runAllAsync(); - const doneJobProcess = rootEl.querySelector('job-process'); - const jobInfo = doneJobProcess?.shadowRoot.querySelector('job-info'); - const timerDetail = jobInfo?.shadowRoot.querySelector('.timer'); - await mouseEvent(timerDetail); - await clock.runAllAsync(); - await mouseEvent(timerDetail); - expect(timerDetail.classList.contains('show-times')).to.be.false; - }); - it('can toggle view mode', async () => { await mouseEvent(rootEl.querySelector('.switch.full')); await clock.runAllAsync(); @@ -218,7 +228,6 @@ describe('Bulk Publish Tool', () => { it('can clear bulk jobs', async () => { await clock.runAllAsync(); await mouseEvent(rootEl.querySelector('.clear-jobs')); - await clock.runAllAsync(); expect(rootEl.querySelectorAll('job-process')).to.have.lengthOf(0); }); }); diff --git a/test/blocks/bulk-publish-v2/mocks/authentication.js b/test/blocks/bulk-publish-v2/mocks/authentication.js index 22d01045ad..ca826fa82d 100644 --- a/test/blocks/bulk-publish-v2/mocks/authentication.js +++ b/test/blocks/bulk-publish-v2/mocks/authentication.js @@ -23,7 +23,7 @@ class MockAuth extends HTMLElement { bubbles: true, detail: { data: { - profile: { name: 'Unit Test' }, + profile: { name: 'Unit Test', email: 'tester@adobe.com' }, preview: { permissions }, live: { permissions }, }, diff --git a/test/blocks/bulk-publish-v2/mocks/fetch.js b/test/blocks/bulk-publish-v2/mocks/fetch.js index 8ab62d86d3..48d7637f54 100644 --- a/test/blocks/bulk-publish-v2/mocks/fetch.js +++ b/test/blocks/bulk-publish-v2/mocks/fetch.js @@ -11,6 +11,7 @@ const requests = { delstatus: 'https://admin.hlx.page/job/adobecom/milo/main/preview-remove/job-2024-01-24t23-16-20-377z/details', retry: 'https://admin.hlx.page/preview/adobecom/milo/main/tools/bulk-publish-v2-test', index: 'https://admin.hlx.page/index/adobecom/milo/main/tools/bulk-publish-v2-test', + permissions: '/.milo/publish-permissions-config.json', }; const getMocks = async () => { @@ -25,7 +26,7 @@ const getMocks = async () => { export async function mockFetch() { const mocks = await getMocks(); stub(window, 'fetch').callsFake((...args) => { - const headers = args[1].body ?? null; + const headers = args[1]?.body ?? null; const body = headers ? JSON.parse(headers) : false; const [resource] = args; const response = mocks.find((mock) => (body.delete ? mock.request === 'delete' : mock.url === resource)); diff --git a/test/blocks/bulk-publish-v2/mocks/response/permissions.json b/test/blocks/bulk-publish-v2/mocks/response/permissions.json new file mode 100644 index 0000000000..37fe5eaec9 --- /dev/null +++ b/test/blocks/bulk-publish-v2/mocks/response/permissions.json @@ -0,0 +1,26 @@ +{ + "urls": { + "total": 4, + "offset": 0, + "limit": 4, + "data": [ + { + "url": "/not/a/valid/path", + "group": "gwp-test", + "message": "Publishing disabled until the test is over" + } + ] + }, + "gwp-test": { + "total": 2, + "offset": 0, + "limit": 2, + "data": [ + { "allow": "testuser@adobe.com" }, + { "allow": "testuser1@adobe.com" } + ] + }, + ":version": 3, + ":names": ["urls", "gwp-US", "no-publish", "gwp-FR"], + ":type": "multi-sheet" +} diff --git a/tools/utils/utils.js b/tools/utils/utils.js index 29d68397d8..8193d21fdf 100644 --- a/tools/utils/utils.js +++ b/tools/utils/utils.js @@ -1,6 +1,7 @@ const IMS_CLIENT_ID = 'milo_ims'; const IMS_PROD_URL = 'https://auth.services.adobe.com/imslib/imslib.min.js'; const STYLE_SHEETS = {}; +const CONFIGS = {}; const getImsToken = async (loadScript) => { window.adobeid = { @@ -25,4 +26,15 @@ const getSheet = async (url) => { return sheet; }; -export { getImsToken, getSheet }; +const getCustomConfig = async (path) => { + /* c8 ignore next 3 */ + if (CONFIGS[path] !== undefined) { + return CONFIGS[path]; + } + const resp = await fetch(path); + const config = resp.ok ? await resp.json() : null; + CONFIGS[path] = config; + return config; +}; + +export { getImsToken, getSheet, getCustomConfig }; From 43573462047147675f45e78c9af63d64debe2730 Mon Sep 17 00:00:00 2001 From: sharathkannan <138484653+sharath-kannan@users.noreply.github.com> Date: Thu, 12 Sep 2024 22:30:42 +0530 Subject: [PATCH 59/66] fix(Mwpw-157971):sticky feature works in landscape mode. (#2856) * sticky-header variants added * removed sticky header from all mobile devices * linting error * Added new sticky variant * updated the selector for table buttons * sticky feature adapts to portrait and landscape * removed linting error --- libs/blocks/table/table.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/libs/blocks/table/table.js b/libs/blocks/table/table.js index 57ed330eb4..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,10 +508,6 @@ export default function init(el) { expandSection = handleSection(sectionParams); }); - const isStickyHeader = el.classList.contains('sticky') - || (el.classList.contains('sticky-desktop-up') && defineDeviceByScreenSize() === 'DESKTOP') - || (el.classList.contains('sticky-tablet-up') && defineDeviceByScreenSize() !== 'MOBILE'); - handleHighlight(el); if (isMerch) formatMerchTable(el); @@ -525,7 +528,7 @@ export default function init(el) { const handleResize = () => { applyStylesBasedOnScreenSize(el, originTable); - if (isStickyHeader) handleScrollEffect(el); + if (isStickyHeader(el)) handleScrollEffect(el); }; handleResize(); From 6fa95584902286108cfea82572288559b3517bb0 Mon Sep 17 00:00:00 2001 From: Nicolas Peltier <1032754+npeltier@users.noreply.github.com> Date: Thu, 12 Sep 2024 19:08:48 +0200 Subject: [PATCH 60/66] MWPW-156612 fragment merch-card variant layouts (#2810) * MWPW-156612 fragment merch-card variant layouts - move from merch-card.js code each layout code related to a given variant under variants/ class - move from global.css.js css related to a given variant under variants/.css.js - move from merch-card.css.js wc css related to a given variant under variants/.variantStyle - uses variants/variants as an index to get related class from related variant, and wc style too, - uses variants/VariantLayout, superclass of all variants, to insert css if card is used, and hold common code * MWPW-156612 changing title selector to something independent of the layout (not initialized when used at first in collections) * MWPW-156612 forking style insertion * MWPW-156612 styles fixes * MWPW-156612 review comments * MWPW-156612 fixing unit tests * MWPW-156612 address review comments * MWPW-156612 catalog & image test cov * MWPW-156612 100% coverage for variants * MWPW-156612 fixing (again) ut * MWPW-156612 fix one inline heading style * MWPW-156612 bg img height --- libs/deps/mas/merch-card.js | 2170 ++++++++--------- libs/deps/mas/plans-modal.js | 747 +----- .../mas/web-components/src/global.css.js | 750 +----- .../mas/web-components/src/merch-card.css.js | 157 +- .../mas/web-components/src/merch-card.js | 454 +--- .../src/variants/catalog.css.js | 89 + .../web-components/src/variants/catalog.js | 97 + .../src/variants/ccd-action.css.js | 40 + .../web-components/src/variants/ccd-action.js | 29 + .../web-components/src/variants/image.css.js | 38 + .../mas/web-components/src/variants/image.js | 36 + .../src/variants/inline-heading.css.js | 39 + .../src/variants/inline-heading.js | 24 + .../src/variants/mini-compare-chart.css.js | 253 ++ .../src/variants/mini-compare-chart.js | 223 ++ .../web-components/src/variants/plans.css.js | 56 + .../mas/web-components/src/variants/plans.js | 53 + .../src/variants/product.css.js | 42 + .../web-components/src/variants/product.js | 26 + .../src/variants/segment.css.js | 48 + .../web-components/src/variants/segment.js | 39 + .../src/variants/special-offer.css.js | 50 + .../src/variants/special-offer.js | 50 + .../web-components/src/variants/twp.css.js | 103 + .../mas/web-components/src/variants/twp.js | 67 + .../src/variants/variant-layout.js | 103 + .../web-components/src/variants/variants.js | 51 + .../test/merch-card.catalog.test.html | 276 +++ .../test/merch-card.catalog.test.html.js | 71 + .../merch-card.mini-compare.mobile.test.html | 634 +++++ ...erch-card.mini-compare.mobile.test.html.js | 83 + .../test/merch-card.mini-compare.test.html | 2 +- .../test/merch-card.mini-compare.test.html.js | 2 +- .../merch-card.special-offer.test.html.js | 53 + .../test/merch-card.special-offers.test.html | 202 ++ .../web-components/test/merch-card.test.html | 380 +-- .../test/merch-card.test.html.js | 39 - .../features/mas/web-components/test/utils.js | 6 + 38 files changed, 4015 insertions(+), 3567 deletions(-) create mode 100644 libs/features/mas/web-components/src/variants/catalog.css.js create mode 100644 libs/features/mas/web-components/src/variants/catalog.js create mode 100644 libs/features/mas/web-components/src/variants/ccd-action.css.js create mode 100644 libs/features/mas/web-components/src/variants/ccd-action.js create mode 100644 libs/features/mas/web-components/src/variants/image.css.js create mode 100644 libs/features/mas/web-components/src/variants/image.js create mode 100644 libs/features/mas/web-components/src/variants/inline-heading.css.js create mode 100644 libs/features/mas/web-components/src/variants/inline-heading.js create mode 100644 libs/features/mas/web-components/src/variants/mini-compare-chart.css.js create mode 100644 libs/features/mas/web-components/src/variants/mini-compare-chart.js create mode 100644 libs/features/mas/web-components/src/variants/plans.css.js create mode 100644 libs/features/mas/web-components/src/variants/plans.js create mode 100644 libs/features/mas/web-components/src/variants/product.css.js create mode 100644 libs/features/mas/web-components/src/variants/product.js create mode 100644 libs/features/mas/web-components/src/variants/segment.css.js create mode 100644 libs/features/mas/web-components/src/variants/segment.js create mode 100644 libs/features/mas/web-components/src/variants/special-offer.css.js create mode 100644 libs/features/mas/web-components/src/variants/special-offer.js create mode 100644 libs/features/mas/web-components/src/variants/twp.css.js create mode 100644 libs/features/mas/web-components/src/variants/twp.js create mode 100644 libs/features/mas/web-components/src/variants/variant-layout.js create mode 100644 libs/features/mas/web-components/src/variants/variants.js create mode 100644 libs/features/mas/web-components/test/merch-card.catalog.test.html create mode 100644 libs/features/mas/web-components/test/merch-card.catalog.test.html.js create mode 100644 libs/features/mas/web-components/test/merch-card.mini-compare.mobile.test.html create mode 100644 libs/features/mas/web-components/test/merch-card.mini-compare.mobile.test.html.js create mode 100644 libs/features/mas/web-components/test/merch-card.special-offer.test.html.js create mode 100644 libs/features/mas/web-components/test/merch-card.special-offers.test.html diff --git a/libs/deps/mas/merch-card.js b/libs/deps/mas/merch-card.js index 588638587a..aa77c87567 100644 --- a/libs/deps/mas/merch-card.js +++ b/libs/deps/mas/merch-card.js @@ -1,6 +1,6 @@ -import{html as n,LitElement as N}from"../lit-all.min.js";import{LitElement as L,html as E,css as O}from"../lit-all.min.js";var h=class extends L{static properties={size:{type:String,attribute:!0},src:{type:String,attribute:!0},alt:{type:String,attribute:!0},href:{type:String,attribute:!0}};constructor(){super(),this.size="m",this.alt=""}render(){let{href:e}=this;return e?E` +import{LitElement as we}from"../lit-all.min.js";import{LitElement as me,html as V,css as de}from"../lit-all.min.js";var n=class extends me{static properties={size:{type:String,attribute:!0},src:{type:String,attribute:!0},alt:{type:String,attribute:!0},href:{type:String,attribute:!0}};constructor(){super(),this.size="m",this.alt=""}render(){let{href:e}=this;return e?V` ${this.alt} - `:E` ${this.alt}`}static styles=O` + `:V` ${this.alt}`}static styles=de` :host { --img-width: 32px; --img-height: 32px; @@ -23,7 +23,7 @@ import{html as n,LitElement as N}from"../lit-all.min.js";import{LitElement as L, width: var(--img-width); height: var(--img-height); } - `};customElements.define("merch-icon",h);import{css as b,unsafeCSS as u}from"../lit-all.min.js";var g="(max-width: 767px)",x="(max-width: 1199px)",c="(min-width: 768px)",a="(min-width: 1200px)",i="(min-width: 1600px)";var C=b` + `};customElements.define("merch-icon",n);import{css as M,unsafeCSS as A}from"../lit-all.min.js";var g="(max-width: 767px)",k="(max-width: 1199px)",m="(min-width: 768px)",s="(min-width: 1200px)",l="(min-width: 1600px)";var G=M` :host { position: relative; display: flex; @@ -42,26 +42,6 @@ import{html as n,LitElement as N}from"../lit-all.min.js";import{LitElement as L, 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; @@ -83,10 +63,6 @@ import{html as n,LitElement as N}from"../lit-all.min.js";import{LitElement as L, background-image: var(--ellipsis-icon); } - :host([variant='mini-compare-chart']) > slot:not([name='icons']) { - display: block; - } - .top-section { display: flex; justify-content: flex-start; @@ -151,19 +127,6 @@ import{html as n,LitElement as N}from"../lit-all.min.js";import{LitElement as L, 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); @@ -267,131 +230,13 @@ import{html as n,LitElement as N}from"../lit-all.min.js";import{LitElement as L, 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 ${u(x)} { - [class*'-merch-cards'] :host([variant='mini-compare-chart']) footer { - flex-direction: column; - align-items: stretch; - text-align: center; - } - } - - @media screen and ${u(a)} { - :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%); - } -`,z=()=>{let l=[b` +`,U=()=>{let r=[M` /* Tablet */ - @media screen and ${u(c)} { + @media screen and ${A(m)} { :host([size='wide']), :host([size='super-wide']) { grid-column: span 3; @@ -402,556 +247,424 @@ import{html as n,LitElement as N}from"../lit-all.min.js";import{LitElement as L, } /* Laptop */ - @media screen and ${u(a)} { + @media screen and ${A(s)} { :host([size='super-wide']) { grid-column: span 3; } - `];return l.push(b` + `];return r.push(M` /* Large desktop */ - @media screen and ${u(i)} { + @media screen and ${A(l)} { :host([size='super-wide']) { grid-column: span 4; } } - `),l};function k(){return window.matchMedia("(max-width: 767px)").matches}function S(){return window.matchMedia("(max-width: 1024px)").matches}var $=document.createElement("style");$.innerHTML=` + `),r};import{html as z}from"../lit-all.min.js";var d=class r{static styleMap={};card;insertVariantStyle(){if(!r.styleMap[this.card.variant]){r.styleMap[this.card.variant]=!0;let e=document.createElement("style");e.innerHTML=this.getGlobalCSS(),document.head.appendChild(e)}}constructor(e){this.card=e,setTimeout(()=>this.insertVariantStyle(),1)}get badge(){let e;if(!(!this.card.badgeBackgroundColor||!this.card.badgeColor||!this.card.badgeText))return this.evergreen&&(e=`border: 1px solid ${this.card.badgeBackgroundColor}; border-right: none;`),z` +
+ ${this.card.badgeText} +
+ `}get cardImage(){return z`
+ + ${this.badge} +
`}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(){let e=this.card.secureLabel?z`${this.card.secureLabel}`:"";return z`
${e}
`}async adjustTitleWidth(){let e=this.card.getBoundingClientRect().width,t=this.card.badgeElement?.getBoundingClientRect().width||0;e===0||t===0||this.card.style.setProperty("--consonant-merch-card-heading-xs-max-width",`${Math.round(e-t-16)}px`)}postCardUpdateHook(){}connectedCallbackHook(){}disconnectedCallbackHook(){}renderLayout(){}};import{html as P,css as le}from"../lit-all.min.js";function q(){return window.matchMedia("(max-width: 767px)").matches}function j(){return window.matchMedia("(max-width: 1024px)").matches}var I="merch-offer-select:ready",K="merch-card:ready",F="merch-card:action-menu-toggle";var O="merch-storage:change",R="merch-quantity-selector:change";var W=` :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-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); +} - --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); +@media screen and ${m} { + :root { + --consonant-merch-card-catalog-width: 302px; + } - /* responsive width */ - --consonant-merch-card-mobile-width: 300px; - --consonant-merch-card-tablet-wide-width: 700px; + .two-merch-cards.catalog, + .three-merch-cards.catalog, + .four-merch-cards.catalog { + grid-template-columns: repeat(2, var(--consonant-merch-card-catalog-width)); + } +} - /* 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; +@media screen and ${s} { + :root { + --consonant-merch-card-catalog-width: 276px; + } - /* cta */ - --consonant-merch-card-cta-font-size: 15px; + .three-merch-cards.catalog, + .four-merch-cards.catalog { + grid-template-columns: repeat(3, var(--consonant-merch-card-catalog-width)); + } +} - /* 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; +@media screen and ${l} { + .four-merch-cards.catalog { + grid-template-columns: repeat(4, var(--consonant-merch-card-catalog-width)); + } +} - /* 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; +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); +} - /* 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; +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); +} - --consonant-merch-card-heading-padding: 0; - --consonant-merch-card-image-height: 180px; +merch-card[variant="catalog"] [slot="action-menu-content"] ::marker { + margin-right: 0; +} - /* 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[variant="catalog"] [slot="action-menu-content"] p { + color: var(--color-white, #fff); +} - /* merch card generic */ - --consonant-merch-card-max-width: 300px; - --transition: cmax-height 0.3s linear, opacity 0.3s linear; +merch-card[variant="catalog"] [slot="action-menu-content"] a { + color: var(--consonant-merch-card-background-color); + text-decoration: underline; +} - /* special offers */ - --consonant-merch-card-special-offers-width: 378px; +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); +}`;var u=class extends d{constructor(e){super(e)}renderLayout(){return P`
+
+ ${this.badge} +
+
+ ${this.card.actionMenuContent} + + + + ${this.promoBottom?"":P``} + + ${this.promoBottom?P``:""} +
+ ${this.secureLabelFooter}`}getGlobalCSS(){return W}toggleActionMenu=e=>{let t=e?.type==="mouseleave"?!0:void 0,o=this.card.shadowRoot.querySelector('slot[name="action-menu-content"]');o&&(t||this.card.dispatchEvent(new CustomEvent(F,{bubbles:!0,composed:!0,detail:{card:this.card.name,type:"action-menu"}})),o.classList.toggle("hidden",t))};connectedCallbackHook(){this.card.addEventListener("mouseleave",this.toggleActionMenu)}disconnectedCallbackHook(){this.card.removeEventListener("mouseleave",this.toggleActionMenu)}static variantStyle=le` + :host([variant='catalog']) { + min-height: 330px; + } - /* image */ - --consonant-merch-card-image-width: 300px; + .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; + } + `};import{html as H,css as pe}from"../lit-all.min.js";var Y=` +:root { + --consonant-merch-card-ccd-action-width: 276px; + --consonant-merch-card-ccd-action-min-height: 320px; +} - /* segment */ - --consonant-merch-card-segment-width: 378px; +.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); +} - /* inline-heading */ - --consonant-merch-card-inline-heading-width: 300px; +merch-card[variant="ccd-action"] .price-strikethrough { + font-size: 18px; +} - /* product */ - --consonant-merch-card-product-width: 300px; +@media screen and ${m} { + .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)); + } +} - /* 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); +@media screen and ${s} { + .three-merch-cards.ccd-action, + .four-merch-cards.ccd-action { + grid-template-columns: repeat(3, var(--consonant-merch-card-ccd-action-width)); + } } -merch-card[variant="twp"] div[class$='twp-badge'] { - padding: 4px 10px 5px 10px; +@media screen and ${l} { + .four-merch-cards.ccd-action { + grid-template-columns: repeat(4, var(--consonant-merch-card-ccd-action-width)); + } } - -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); +`;var f=class extends d{constructor(e){super(e)}getGlobalCSS(){return Y}renderLayout(){return H`
+ ${this.badge} + + + ${this.promoBottom?H``:H``} +
+ +
`}static variantStyle=pe` + :host([variant='ccd-action']:not([size])) { + width: var(--consonant-merch-card-ccd-action-width); + } + `};import{html as v}from"../lit-all.min.js";var Q=` +:root { + --consonant-merch-card-image-width: 300px; } -merch-card[variant="twp"] [slot="body-xs"] ul { - padding: 0; - margin: 0; +.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); } -merch-card[variant="twp"] [slot="body-xs"] ul li { - list-style-type: none; - padding-left: 0; +@media screen and ${m} { + .two-merch-cards.image, + .three-merch-cards.image, + .four-merch-cards.image { + grid-template-columns: repeat(2, var(--consonant-merch-card-image-width)); + } } -merch-card[variant="twp"] [slot="body-xs"] ul li::before { - content: '\xB7'; - font-size: 20px; - padding-right: 5px; - font-weight: bold; +@media screen and ${s} { + :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)); + } } -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; +@media screen and ${l} { + .four-merch-cards.image { + grid-template-columns: repeat(4, var(--consonant-merch-card-image-width)); + } +} +`;var L=class extends d{constructor(e){super(e)}getGlobalCSS(){return Q}renderLayout(){return v`${this.cardImage} +
+ + + + ${this.promoBottom?v``:v``} +
+ ${this.evergreen?v` +
+ +
+ `:v` +
+ ${this.secureLabelFooter} + `}`}};import{html as J}from"../lit-all.min.js";var Z=` +:root { + --consonant-merch-card-inline-heading-width: 300px; } -merch-card[variant='twp'] merch-quantity-select, -merch-card[variant='twp'] merch-offer-select { - display: none; +.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); } -[slot="cci-footer"] p, -[slot="cct-footer"] p, -[slot="cce-footer"] p { - margin: 0; +@media screen and ${m} { + .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)); + } } -/* mini compare chart card styles */ +@media screen and ${s} { + :root { + --consonant-merch-card-inline-heading-width: 378px; + } -merch-card[variant="mini-compare-chart"] [slot="heading-m"] { - padding: 0 var(--consonant-merch-spacing-s) 0; + .three-merch-cards.inline-heading, + .four-merch-cards.inline-heading { + grid-template-columns: repeat(3, var(--consonant-merch-card-inline-heading-width)); + } } -merch-card[variant="mini-compare-chart"] [slot="body-m"] { - padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s); +@media screen and ${l} { + .four-merch-cards.inline-heading { + grid-template-columns: repeat(4, var(--consonant-merch-card-inline-heading-width)); + } } +`;var T=class extends d{constructor(e){super(e)}getGlobalCSS(){return Z}renderLayout(){return J` ${this.badge} +
+
+ + +
+ +
+ ${this.card.customHr?"":J`
`} ${this.secureLabelFooter}`}};import{html as _,css as ge,unsafeCSS as ee}from"../lit-all.min.js";var X=` + :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"] { + 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'] { + 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"] { + merch-card[variant="mini-compare-chart"] [slot='callout-content'] [is="inline-price"] { min-height: unset; -} + } -merch-card[variant="mini-compare-chart"] [slot="price-commitment"] { + 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 { + merch-card[variant="mini-compare-chart"] [slot="price-commitment"] a { display: inline-block; height: 27px; -} + } -merch-card[variant="mini-compare-chart"] [slot="offers"] { + 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"] { + 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"] { + 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 { + merch-card[variant="mini-compare-chart"] [slot="promo-text"] a { text-decoration: underline; -} + } -merch-card[variant="mini-compare-chart"] .footer-row-icon { + merch-card[variant="mini-compare-chart"] .footer-row-icon { display: flex; place-items: center; -} + } -merch-card[variant="mini-compare-chart"] .footer-row-icon img { + 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 { + 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 { + 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 { + 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 { + 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 ${g} { + :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); @@ -977,8 +690,12 @@ merch-card[variant="mini-compare-chart"] .footer-row-cell-description a { } } -/* mini compare tablet */ -@media screen and ${x} { +@media screen and ${k} { + .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); @@ -1004,73 +721,183 @@ merch-card[variant="mini-compare-chart"] .footer-row-cell-description a { line-height: var(--consonant-merch-card-body-xs-line-height); } } +@media screen and ${m} { + :root { + --consonant-merch-card-mini-compare-chart-width: 302px; + --consonant-merch-card-mini-compare-chart-wide-width: 302px; + } -div[slot="footer"] { - display: contents; + .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))); + } } -[slot="footer"] a { - word-wrap: break-word; - text-align: center; +/* desktop */ +@media screen and ${s} { + :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); + } } -[slot="footer"] a:not([class]) { - font-weight: 700; - font-size: var(--consonant-merch-card-cta-font-size); +@media screen and ${l} { + .four-merch-cards.mini-compare-chart { + grid-template-columns: repeat(4, var(--consonant-merch-card-mini-compare-chart-width)); + } } -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; +merch-card .footer-row-cell:nth-child(1) { + min-height: var(--consonant-merch-card-footer-row-1-min-height); } -/* Mobile */ -@media screen and ${g} { - :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; - } +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); +} -/* Tablet */ -@media screen and ${c} { - :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; - } +merch-card .footer-row-cell:nth-child(5) { + min-height: var(--consonant-merch-card-footer-row-5-min-height); } -/* desktop */ -@media screen and ${a} { - :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; +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); +} +`;var xe=32,b=class extends d{constructor(e){super(e)}#e;getRowMinHeightPropertyName=e=>`--consonant-merch-card-footer-row-${e}-min-height`;getContainer(){return this.#e=this.#e??this.card.closest('[class*="-merch-cards"]')??this.card.parentElement,this.#e}getGlobalCSS(){return X}getMiniCompareFooter=()=>{let e=this.card.secureLabel?_` + ${this.card.secureLabel}`:_``;return _`
${e}
`};updateMiniCompareElementMinHeight(e,t){let o=`--consonant-merch-card-mini-compare-${t}-height`,h=Math.max(0,parseInt(window.getComputedStyle(e).height)||0),p=parseInt(this.getContainer().style.getPropertyValue(o))||0;h>p&&this.getContainer().style.setProperty(o,`${h}px`)}adjustMiniCompareBodySlots(){if(this.card.getBoundingClientRect().width===0)return;this.updateMiniCompareElementMinHeight(this.card.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.card.shadowRoot.querySelector(`slot[name="${o}"]`),o)),this.updateMiniCompareElementMinHeight(this.card.shadowRoot.querySelector("footer"),"footer");let t=this.card.shadowRoot.querySelector(".mini-compare-chart-badge");t&&t.textContent!==""&&this.getContainer().style.setProperty("--consonant-merch-card-mini-compare-top-section-mobile-height","32px")}adjustMiniCompareFooterRows(){if(this.card.getBoundingClientRect().width===0)return;[...this.card.querySelector('[slot="footer-rows"]')?.children].forEach((t,o)=>{let h=Math.max(xe,parseInt(window.getComputedStyle(t).height)||0),p=parseInt(this.getContainer().style.getPropertyValue(this.getRowMinHeightPropertyName(o+1)))||0;h>p&&this.getContainer().style.setProperty(this.getRowMinHeightPropertyName(o+1),`${h}px`)})}removeEmptyRows(){this.card.querySelectorAll(".footer-row-cell").forEach(t=>{let o=t.querySelector(".footer-row-cell-description");o&&!o.textContent.trim()&&t.remove()})}renderLayout(){return _`
+ ${this.badge} +
+ + + + + + + + + ${this.getMiniCompareFooter()} + `}postCardUpdateHook(){q()?this.removeEmptyRows():(this.adjustMiniCompareBodySlots(),this.adjustMiniCompareFooterRows())}static variantStyle=ge` + :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 ${ee(k)} { + [class*'-merch-cards'] :host([variant='mini-compare-chart']) footer { + flex-direction: column; + align-items: stretch; + text-align: center; + } + } + + @media screen and ${ee(s)} { + :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 + ); } + `};import{html as $,css as ue}from"../lit-all.min.js";var te=` +: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); } -/* supported cards */ -/* grid style for plans */ .one-merch-card.plans, .two-merch-cards.plans, .three-merch-cards.plans, @@ -1079,399 +906,716 @@ div[slot='bg-image'] img { } /* Tablet */ -@media screen and ${c} { - .two-merch-cards.plans, - .three-merch-cards.plans, - .four-merch-cards.plans { - grid-template-columns: repeat(2, var(--consonant-merch-card-plans-width)); +@media screen and ${m} { + :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 ${s} { + :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 ${l} { + .four-merch-cards.plans { + grid-template-columns: repeat(4, var(--consonant-merch-card-plans-width)); + } +} +`;var y=class extends d{constructor(e){super(e)}getGlobalCSS(){return te}postCardUpdateHook(){this.adjustTitleWidth()}get stockCheckbox(){return this.card.checkboxLabel?$``:""}renderLayout(){return $` ${this.badge} +
+ + + + + ${this.promoBottom?"":$` `} + + ${this.promoBottom?$` `:""} + ${this.stockCheckbox} +
+ + ${this.secureLabelFooter}`}static variantStyle=ue` + :host([variant='plans']) { + min-height: 348px; + } + + :host([variant='plans']) ::slotted([slot='heading-xs']) { + max-width: var(--consonant-merch-card-heading-xs-max-width, 100%); + } + `};import{html as N}from"../lit-all.min.js";var re=` +: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 ${m} { + .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 ${a} { - .three-merch-cards.plans, - .four-merch-cards.plans { - grid-template-columns: repeat(3, var(--consonant-merch-card-plans-width)); - } +@media screen and ${s} { + :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 ${i} { - .four-merch-cards.plans { - grid-template-columns: repeat(4, var(--consonant-merch-card-plans-width)); - } +@media screen and ${l} { + .four-merch-cards.product { + grid-template-columns: repeat(4, var(--consonant-merch-card-product-width)); + } +} +`;var w=class extends d{constructor(e){super(e)}getGlobalCSS(){return re}renderLayout(){return N` ${this.badge} +
+ + + + ${this.promoBottom?"":N``} + + ${this.promoBottom?N``:""} +
+ ${this.secureLabelFooter}`}};import{html as B,css as fe}from"../lit-all.min.js";var oe=` +: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)); +} -/* 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); +/* Mobile */ +@media screen and ${g} { + :root { + --consonant-merch-card-segment-width: 276px; + } } -/* Tablet */ -@media screen and ${c} { - .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 ${m} { + :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 ${a} { - .three-merch-cards.catalog, - .four-merch-cards.catalog { - grid-template-columns: repeat(3, var(--consonant-merch-card-catalog-width)); - } -} +@media screen and ${s} { + :root { + --consonant-merch-card-segment-width: 302px; + } + + .three-merch-cards.segment { + grid-template-columns: repeat(3, minmax(276px, var(--consonant-merch-card-segment-width))); + } -/* Large desktop */ - @media screen and ${i} { - .four-merch-cards.catalog { - grid-template-columns: repeat(4, var(--consonant-merch-card-catalog-width)); + .four-merch-cards.segment { + grid-template-columns: repeat(4, minmax(276px, var(--consonant-merch-card-segment-width))); + } +} +`;var E=class extends d{constructor(e){super(e)}getGlobalCSS(){return oe}postCardUpdateHook(){this.adjustTitleWidth()}renderLayout(){return B` ${this.badge} +
+ + + ${this.promoBottom?"":B``} + + ${this.promoBottom?B``:""} +
+
+ ${this.secureLabelFooter}`}static variantStyle=fe` + :host([variant='segment']) { + min-height: 214px; + } + :host([variant='segment']) ::slotted([slot='heading-xs']) { + max-width: var(--consonant-merch-card-heading-xs-max-width, 100%); } + `};import{html as D,css as ve}from"../lit-all.min.js";var ne=` +: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)); + grid-template-columns: minmax(300px, var(--consonant-merch-card-special-offers-width)); } -/* Tablet */ -@media screen and ${c} { - .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))); - } +@media screen and ${g} { + :root { + --consonant-merch-card-special-offers-width: 302px; + } +} + +@media screen and ${m} { + :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 ${a} { - .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 ${s} { + .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 ${i} { - .four-merch-cards.special-offers { - grid-template-columns: repeat(4, minmax(300px, var(--consonant-merch-card-special-offers-width))); +@media screen and ${l} { + .four-merch-cards.special-offers { + grid-template-columns: repeat(4, minmax(300px, var(--consonant-merch-card-special-offers-width))); + } +} +`;var S=class extends d{constructor(e){super(e)}getGlobalCSS(){return ne}get headingSelector(){return'[slot="detail-m"]'}renderLayout(){return D`${this.cardImage} +
+ + + +
+ ${this.evergreen?D` +
+ +
+ `:D` +
+ ${this.secureLabelFooter} + `} + `}static variantStyle=ve` + :host([variant='special-offers']) { + min-height: 439px; } + + :host([variant='special-offers'].center) { + text-align: center; + } + `};import{html as be,css as ye}from"../lit-all.min.js";var ae=` +: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); +} -/* 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); +merch-card[variant="twp"] [slot="body-xs"] ul { + padding: 0; + margin: 0; } -/* Tablet */ -@media screen and ${c} { - .two-merch-cards.image, - .three-merch-cards.image, - .four-merch-cards.image { - grid-template-columns: repeat(2, var(--consonant-merch-card-image-width)); - } +merch-card[variant="twp"] [slot="body-xs"] ul li { + list-style-type: none; + padding-left: 0; } -/* desktop */ -@media screen and ${a} { - .three-merch-cards.image, - .four-merch-cards.image { - grid-template-columns: repeat(3, var(--consonant-merch-card-image-width)); - } +merch-card[variant="twp"] [slot="body-xs"] ul li::before { + content: '\xB7'; + font-size: 20px; + padding-right: 5px; + font-weight: bold; } -/* Large desktop */ - @media screen and ${i} { - .four-merch-cards.image { - grid-template-columns: repeat(4, var(--consonant-merch-card-image-width)); - } +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; +} -/* 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)); +.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 ${c} { - .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))); +@media screen and ${g} { + :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 ${m} { + :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 ${s} { + :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 ${l} { + .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)); + } } +`;var C=class extends d{constructor(e){super(e)}getGlobalCSS(){return ae}renderLayout(){return be`${this.badge} +
+ + + +
+
+ +
+
`}static variantStyle=ye` + :host([variant='twp']) { + padding: 4px 10px 5px 10px; + } + .twp-badge { + padding: 4px 10px 5px 10px; + } -/* desktop */ -@media screen and ${a} { - .three-merch-cards.segment { - grid-template-columns: repeat(3, minmax(276px, var(--consonant-merch-card-segment-width))); + :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; } - .four-merch-cards.segment { - grid-template-columns: repeat(4, minmax(276px, var(--consonant-merch-card-segment-width))); + :host([variant='twp']) footer { + gap: var(--consonant-merch-spacing-xxs); + flex-direction: column; + align-self: flex-start; } + `};var ce=r=>{switch(r.variant){case"catalog":return new u(r);case"ccd-action":return new f(r);case"image":return new L(r);case"inline-heading":return new T(r);case"mini-compare-chart":return new b(r);case"plans":return new y(r);case"product":return new w(r);case"segment":return new E(r);case"special-offers":return new S(r);case"twp":return new C(r);default:return new w(r)}},ie=()=>{let r=[];return r.push(u.variantStyle),r.push(f.variantStyle),r.push(b.variantStyle),r.push(y.variantStyle),r.push(E.variantStyle),r.push(S.variantStyle),r.push(C.variantStyle),r};var se=document.createElement("style");se.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; + + /* 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; + + /* 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"); + + --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; } - -/* 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); +merch-card-collection > p[slot], +merch-card-collection > div[slot] p { + margin: 0; } -/* Tablet */ -@media screen and ${c} { - .two-merch-cards.product, - .three-merch-cards.product, - .four-merch-cards.product { - grid-template-columns: repeat(2, var(--consonant-merch-card-product-width)); - } +.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); } -/* desktop */ -@media screen and ${a} { - .three-merch-cards.product, - .four-merch-cards.product { - grid-template-columns: repeat(3, var(--consonant-merch-card-product-width)); - } +merch-card.background-opacity-70 { + background-color: rgba(255 255 255 / 70%); } -/* Large desktop */ - @media screen and ${i} { - .four-merch-cards.product { - grid-template-columns: repeat(4, var(--consonant-merch-card-product-width)); - } +merch-card.has-divider hr { + margin-bottom: var(--consonant-merch-spacing-xs); + height: 1px; + border: none; } -/* 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); +merch-card p, merch-card h3, merch-card h4 { + margin: 0; } -/* Tablet */ -@media screen and ${c} { - .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)); - } +merch-card span[is=inline-price] { + display: inline-block; } -/* desktop */ -@media screen and ${a} { - .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)); - } +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; } -/* Large desktop */ - @media screen and ${i} { - .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)); - } +merch-card.dc-pricing [slot='heading-xs'] { + margin-bottom: var(--consonant-merch-spacing-xxs); } -/* Mobile */ -@media screen and ${g} { - .one-merch-card.twp, - .two-merch-cards.twp, - .three-merch-cards.twp { - grid-template-columns: repeat(1, var(--consonant-merch-card-twp-mobile-width)); - } +merch-card:not([variant='inline-heading']) [slot='heading-xs'] a { + color: var(--merch-color-grey-80); } -/* 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); +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; } -/* Tablet */ -@media screen and ${c} { - .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)); - } +merch-card [slot='heading-xs'] a:not(:hover) { + text-decoration: inherit; } -/* desktop */ -@media screen and ${a} { - .three-merch-cards.inline-heading, - .four-merch-cards.inline-heading { - grid-template-columns: repeat(3, var(--consonant-merch-card-inline-heading-width)); - } +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); } -/* Large desktop */ - @media screen and ${i} { - .four-merch-cards.inline-heading { - grid-template-columns: repeat(4, var(--consonant-merch-card-inline-heading-width)); - } +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; } -/* 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); +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); } -/* Tablet */ -@media screen and ${c} { - .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)); - } +merch-card [slot='offers'] { + padding: var(--consonant-merch-spacing-xxs) var(--consonant-merch-spacing-s); } -/* desktop */ -@media screen and ${a} { - .three-merch-cards.ccd-action, - .four-merch-cards.ccd-action { - grid-template-columns: repeat(3, var(--consonant-merch-card-ccd-action-width)); - } +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); } -/* Large desktop */ - @media screen and ${i} { - .four-merch-cards.ccd-action { - grid-template-columns: repeat(4, var(--consonant-merch-card-ccd-action-width)); - } +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); } -/* 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); +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); } -.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); +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; } -@media screen and ${g} { - .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 [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); } -@media screen and ${x} { - .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 [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); } -/* Tablet */ -@media screen and ${c} { - .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))); - } +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; } -/* desktop */ -@media screen and ${a} { - .one-merch-card.mini-compare-chart { - grid-template-columns: var(--consonant-merch-card-mini-compare-chart-wide-width); - } +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); +} - .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); - } +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; +} - .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); - } +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); } -@media screen and ${i} { - .four-merch-cards.mini-compare-chart { - grid-template-columns: repeat(4, var(--consonant-merch-card-mini-compare-chart-width)); - } +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); } -/* 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 [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 .footer-row-cell:nth-child(2) { - min-height: var(--consonant-merch-card-footer-row-2-min-height); +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 .footer-row-cell:nth-child(3) { - min-height: var(--consonant-merch-card-footer-row-3-min-height); +[slot="cci-footer"] p, +[slot="cct-footer"] p, +[slot="cce-footer"] p { + margin: 0; } -merch-card .footer-row-cell:nth-child(4) { - min-height: var(--consonant-merch-card-footer-row-4-min-height); +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 .footer-row-cell:nth-child(5) { - min-height: var(--consonant-merch-card-footer-row-5-min-height); +div[slot="footer"] { + display: contents; } -merch-card .footer-row-cell:nth-child(6) { - min-height: var(--consonant-merch-card-footer-row-6-min-height); +[slot="footer"] a { + word-wrap: break-word; + text-align: center; } -merch-card .footer-row-cell:nth-child(7) { - min-height: var(--consonant-merch-card-footer-row-7-min-height); +[slot="footer"] a:not([class]) { + font-weight: 700; + font-size: var(--consonant-merch-card-cta-font-size); } -merch-card .footer-row-cell:nth-child(8) { - min-height: var(--consonant-merch-card-footer-row-8-min-height); +div[slot='bg-image'] img { + position: relative; + width: 100%; + 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; } span[is="inline-price"][data-template='strikethrough'] { @@ -1510,152 +1654,4 @@ body.merch-modal { scrollbar-gutter: stable; height: 100vh; } -`;document.head.appendChild($);var T="merch-offer-select:ready",_="merch-card:ready",M="merch-card:action-menu-toggle";var y="merch-storage:change",w="merch-quantity-selector:change";var m="merch-card",H=32,v="mini-compare-chart",R=l=>`--consonant-merch-card-footer-row-${l}-min-height`,d=class extends N{static 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:e=>{let[t,r,o]=e.split(",");return{PUF:t,ABM:r,M2M:o}}}},filters:{type:String,reflect:!0,converter:{fromAttribute:e=>Object.fromEntries(e.split(",").map(t=>{let[r,o,s]=t.split(":"),p=Number(o);return[r,{order:isNaN(p)?void 0:p,size:s}]})),toAttribute:e=>Object.entries(e).map(([t,{order:r,size:o}])=>[t,r,o].filter(s=>s!=null).join(":")).join(",")}},types:{type:String,attribute:"types",reflect:!0},merchOffer:{type:Object}};static styles=[C,...z()];customerSegment;marketSegment;constructor(){super(),this.filters={},this.types="",this.selected=!1}#e;updated(e){(e.has("badgeBackgroundColor")||e.has("borderColor"))&&(this.style.border=this.computedBorderStyle),this.updateComplete.then(async()=>{let r=Array.from(this.querySelectorAll('span[is="inline-price"][data-wcs-osi]')).filter(o=>!o.closest('[slot="callout-content"]'));await Promise.all(r.map(o=>o.onceSettled())),this.adjustTitleWidth(),k()?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?n``:""}get cardImage(){return n`
- - ${this.badge} -
`}get secureLabelFooter(){let e=this.secureLabel?n`${this.secureLabel}`:"";return n`
${e}
`}get miniCompareFooter(){let e=this.secureLabel?n` - ${this.secureLabel}`:n``;return n`
${e}
`}get badge(){let e;if(!(!this.badgeBackgroundColor||!this.badgeColor||!this.badgeText))return this.evergreen&&(e=`border: 1px solid ${this.badgeBackgroundColor}; border-right: none;`),n` -
- ${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:e}){if(!this.stockOfferOsis)return;let t=this.checkoutLinks;if(t.length!==0)for(let r of t){await r.onceSettled();let o=r.value?.[0]?.planType;if(!o)return;let s=this.stockOfferOsis[o];if(!s)return;let p=r.dataset.wcsOsi.split(",").filter(A=>A!==s);e.checked&&p.push(s),r.dataset.wcsOsi=p.join(",")}}toggleActionMenu(e){let t=e?.type==="mouseleave"?!0:void 0,r=this.shadowRoot.querySelector('slot[name="action-menu-content"]');r&&(t||this.dispatchEvent(new CustomEvent(M,{bubbles:!0,composed:!0,detail:{card:this.name,type:"action-menu"}})),r.classList.toggle("hidden",t))}handleQuantitySelection(e){let t=this.checkoutLinks;for(let r of t)r.dataset.quantity=e.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(e){let t={...this.filters};Object.keys(t).forEach(r=>{if(e){t[r].order=Math.min(t[r].order||2,2);return}let o=t[r].order;o===1||isNaN(o)||(t[r].order=Number(o)+1)}),this.filters=t}includes(e){return this.textContent.match(new RegExp(e,"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 v:return this.renderMiniCompareChart();case"ccd-action":return this.renderCcdAction();case"twp":return this.renderTwp();default:return this.renderProduct()}}renderSpecialOffer(){return n`${this.cardImage} -
- - - -
- ${this.evergreen?n` -
- -
- `:n` -
- ${this.secureLabelFooter} - `} - `}get promoBottom(){return this.classList.contains("promo-bottom")}get startingAt(){return this.classList.contains("starting-at")}renderSegment(){return n` ${this.badge} -
- - - ${this.promoBottom?"":n``} - - ${this.promoBottom?n``:""} -
-
- ${this.secureLabelFooter}`}renderPlans(){return n` ${this.badge} -
- - - - - ${this.promoBottom?"":n` `} - - ${this.promoBottom?n` `:""} - ${this.stockCheckbox} -
- - ${this.secureLabelFooter}`}renderCatalog(){return n`
-
- ${this.badge} -
-
- ${this.actionMenuContent} - - - - ${this.promoBottom?"":n``} - - ${this.promoBottom?n``:""} -
- ${this.secureLabelFooter}`}renderImage(){return n`${this.cardImage} -
- - - - ${this.promoBottom?n``:n``} -
- ${this.evergreen?n` -
- -
- `:n` -
- ${this.secureLabelFooter} - `}`}renderInlineHeading(){return n` ${this.badge} -
-
- - -
- -
- ${this.customHr?"":n`
`} ${this.secureLabelFooter}`}renderProduct(){return n` ${this.badge} -
- - - - ${this.promoBottom?"":n``} - - ${this.promoBottom?n``:""} -
- ${this.secureLabelFooter}`}renderMiniCompareChart(){let{badge:e}=this;return n`
- ${e} -
- - - - - - - - - ${this.miniCompareFooter} - `}renderTwp(){return n`${this.badge} -
- - - -
-
- -
-
`}renderCcdAction(){return n`
- ${this.badge} - - - ${this.promoBottom?n``:n``} -
- -
`}connectedCallback(){super.connectedCallback(),this.#e=this.getContainer(),this.setAttribute("tabindex",this.getAttribute("tabindex")??"0"),this.addEventListener("mouseleave",this.toggleActionMenu),this.addEventListener(w,this.handleQuantitySelection),this.addEventListener(T,this.merchCardReady,{once:!0}),this.updateComplete.then(()=>{this.merchCardReady()}),this.storageOptions?.addEventListener("change",this.handleStorageChange)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener(w,this.handleQuantitySelection),this.storageOptions?.removeEventListener(y,this.handleStorageChange)}updateMiniCompareElementMinHeight(e,t){let r=`--consonant-merch-card-mini-compare-${t}-height`,o=Math.max(0,parseInt(window.getComputedStyle(e).height)||0),s=parseInt(this.#e.style.getPropertyValue(r))||0;o>s&&this.#e.style.setProperty(r,`${o}px`)}async adjustTitleWidth(){if(!["segment","plans"].includes(this.variant))return;let e=this.getBoundingClientRect().width,t=this.badgeElement?.getBoundingClientRect().width||0;e===0||t===0||this.style.setProperty("--consonant-merch-card-heading-xs-max-width",`${Math.round(e-t-16)}px`)}async adjustMiniCompareBodySlots(){if(this.variant!==v||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(r=>this.updateMiniCompareElementMinHeight(this.shadowRoot.querySelector(`slot[name="${r}"]`),r)),this.updateMiniCompareElementMinHeight(this.shadowRoot.querySelector("footer"),"footer");let t=this.shadowRoot.querySelector(".mini-compare-chart-badge");t&&t.textContent!==""&&this.#e.style.setProperty("--consonant-merch-card-mini-compare-top-section-mobile-height","32px")}adjustMiniCompareFooterRows(){if(this.variant!==v||this.getBoundingClientRect().width===0)return;[...this.querySelector('[slot="footer-rows"]').children].forEach((t,r)=>{let o=Math.max(H,parseInt(window.getComputedStyle(t).height)||0),s=parseInt(this.#e.style.getPropertyValue(R(r+1)))||0;o>s&&this.#e.style.setProperty(R(r+1),`${o}px`)})}removeEmptyRows(){if(this.variant!==v)return;this.querySelectorAll(".footer-row-cell").forEach(t=>{let r=t.querySelector(".footer-row-cell-description");r&&!r.textContent.trim()&&t.remove()})}get storageOptions(){return this.querySelector("sp-radio-group#storage")}get storageSpecificOfferSelect(){let e=this.storageOptions?.selected;if(e){let t=this.querySelector(`merch-offer-select[storage="${e}"]`);if(t)return t}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(_,{bubbles:!0}))}handleStorageChange(){let e=this.closest("merch-card")?.offerSelect.cloneNode(!0);e&&this.dispatchEvent(new CustomEvent(y,{detail:{offerSelect:e},bubbles:!0}))}get dynamicPrice(){return this.querySelector('[slot="price"]')}selectMerchOffer(e){if(e===this.merchOffer)return;this.merchOffer=e;let t=this.dynamicPrice;if(e.price&&t){let r=e.price.cloneNode(!0);t.onceSettled?t.onceSettled().then(()=>{t.replaceWith(r)}):t.replaceWith(r)}}};customElements.define(m,d); +`;document.head.appendChild(se);var c="merch-card",a=class extends we{static 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:e=>{let[t,o,h]=e.split(",");return{PUF:t,ABM:o,M2M:h}}}},filters:{type:String,reflect:!0,converter:{fromAttribute:e=>Object.fromEntries(e.split(",").map(t=>{let[o,h,p]=t.split(":"),x=Number(h);return[o,{order:isNaN(x)?void 0:x,size:p}]})),toAttribute:e=>Object.entries(e).map(([t,{order:o,size:h}])=>[t,o,h].filter(p=>p!=null).join(":")).join(",")}},types:{type:String,attribute:"types",reflect:!0},merchOffer:{type:Object}};static styles=[G,ie(),...U()];customerSegment;marketSegment;variantLayout;constructor(){super(),this.filters={},this.types="",this.selected=!1}updated(e){(e.has("badgeBackgroundColor")||e.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(h=>!h.closest('[slot="callout-content"]'));await Promise.all(o.map(h=>h.onceSettled())),this.variantLayout.postCardUpdateHook(this)})}render(){if(!(!this.isConnected||this.style.display==="none"))return this.variantLayout.renderLayout()}get computedBorderStyle(){return this.variant!=="twp"?`1px solid ${this.borderColor?this.borderColor:this.badgeBackgroundColor}`:""}get badgeElement(){return this.shadowRoot.getElementById("badge")}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:e}){if(!this.stockOfferOsis)return;let t=this.checkoutLinks;if(t.length!==0)for(let o of t){await o.onceSettled();let h=o.value?.[0]?.planType;if(!h)return;let p=this.stockOfferOsis[h];if(!p)return;let x=o.dataset.wcsOsi.split(",").filter(he=>he!==p);e.checked&&x.push(p),o.dataset.wcsOsi=x.join(",")}}handleQuantitySelection(e){let t=this.checkoutLinks;for(let o of t)o.dataset.quantity=e.detail.option}get titleElement(){return this.querySelector(this.variantLayout?.headingSelector||".card-heading")}get title(){return this.titleElement?.textContent?.trim()}get description(){return this.querySelector('[slot="body-xs"]')?.textContent?.trim()}updateFilters(e){let t={...this.filters};Object.keys(t).forEach(o=>{if(e){t[o].order=Math.min(t[o].order||2,2);return}let h=t[o].order;h===1||isNaN(h)||(t[o].order=Number(h)+1)}),this.filters=t}includes(e){return this.textContent.match(new RegExp(e,"i"))!==null}get startingAt(){return this.classList.contains("starting-at")}connectedCallback(){super.connectedCallback(),this.variantLayout=ce(this),this.variantLayout.connectedCallbackHook(),this.setAttribute("tabindex",this.getAttribute("tabindex")??"0"),this.addEventListener(R,this.handleQuantitySelection),this.addEventListener(I,this.merchCardReady,{once:!0}),this.updateComplete.then(()=>{this.merchCardReady()}),this.storageOptions?.addEventListener("change",this.handleStorageChange)}disconnectedCallback(){super.disconnectedCallback(),this.variantLayout.disconnectedCallbackHook(),this.removeEventListener(R,this.handleQuantitySelection),this.storageOptions?.removeEventListener(O,this.handleStorageChange)}get storageOptions(){return this.querySelector("sp-radio-group#storage")}get storageSpecificOfferSelect(){let e=this.storageOptions?.selected;if(e){let t=this.querySelector(`merch-offer-select[storage="${e}"]`);if(t)return t}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(K,{bubbles:!0}))}handleStorageChange(){let e=this.closest("merch-card")?.offerSelect.cloneNode(!0);e&&this.dispatchEvent(new CustomEvent(O,{detail:{offerSelect:e},bubbles:!0}))}get dynamicPrice(){return this.querySelector('[slot="price"]')}selectMerchOffer(e){if(e===this.merchOffer)return;this.merchOffer=e;let t=this.dynamicPrice;if(e.price&&t){let o=e.price.cloneNode(!0);t.onceSettled?t.onceSettled().then(()=>{t.replaceWith(o)}):t.replaceWith(o)}}};customElements.define(c,a); 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/mas/web-components/src/global.css.js b/libs/features/mas/web-components/src/global.css.js index a143726907..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,458 +320,13 @@ 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; } 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 6599a6e153..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,5 +1,5 @@ import { css, unsafeCSS } from 'lit'; -import { DESKTOP_UP, LARGE_DESKTOP, TABLET_UP, TABLET_DOWN } from './media.js'; +import { DESKTOP_UP, LARGE_DESKTOP, TABLET_UP, } from './media.js'; export const styles = css` :host { @@ -20,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; @@ -61,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; @@ -129,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); @@ -245,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 025be0ef1c..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,19 +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'; 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 }, @@ -89,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(); @@ -101,8 +95,6 @@ export class MerchCard extends LitElement { this.selected = false; } - #container; - updated(changedProperties) { if ( changedProperties.has('badgeBackgroundColor') || @@ -112,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 ${ @@ -138,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"]') @@ -252,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) { @@ -280,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() { @@ -321,225 +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} -
- - - -
-
- -
-
`; - } - - renderCcdAction() { - return html`
- ${this.badge} - - - ${this.promoBottom ? html`` : html``} -
- -
`; - } - 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, @@ -560,6 +250,7 @@ export class MerchCard extends LitElement { disconnectedCallback() { super.disconnectedCallback(); + this.variantLayout.disconnectedCallbackHook(); this.removeEventListener( EVENT_MERCH_QUANTITY_SELECTOR_CHANGE, @@ -570,119 +261,8 @@ export class MerchCard extends LitElement { this.handleStorageChange, ); } - // 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'); } 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/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 38d0cf0893..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 { const miniCompareChart = document.querySelector( 'merch-card[variant="mini-compare-chart"]', ); - miniCompareChart.removeEmptyRows(); + 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.special-offer.test.html.js b/libs/features/mas/web-components/test/merch-card.special-offer.test.html.js new file mode 100644 index 0000000000..63eb936984 --- /dev/null +++ b/libs/features/mas/web-components/test/merch-card.special-offer.test.html.js @@ -0,0 +1,53 @@ +// @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 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 return title for special offer card', async () => { + const title = document.querySelector( + 'merch-card[variant="special-offers"]', + ).title; + expect(title).to.equal('INDIVIDUALS'); + }); +}); diff --git a/libs/features/mas/web-components/test/merch-card.special-offers.test.html b/libs/features/mas/web-components/test/merch-card.special-offers.test.html new file mode 100644 index 0000000000..b0d613bf94 --- /dev/null +++ b/libs/features/mas/web-components/test/merch-card.special-offers.test.html @@ -0,0 +1,202 @@ + + + + + + + Merch Card Web Component demo page + + + + + + +

+
+ + + +
+
+

Three cards

+ +

INDIVIDUALS

+

Save over 30% on Creative Cloud All Apps.

+

+
+
+ Save now +
+
+ + + + + + +
+ + +

STUDENTS AND TEACHERS

+

Save over 60% on Creative Cloud All Apps.

+
+

Get 20+ apps — including Photoshop, Premiere Pro, and Illustrator — and save big for the first + year. +

+

Get 20+ apps — including Photoshop, Premiere Pro, and Illustrator — and save big for the first + year. +

+

Get 20+ apps — including Photoshop, Premiere Pro, and Illustrator — and save big for the first + year. +

+
+
+ Save now +
+
+ + + + + + +
+
+ +

INDIVIDUALS

+

+

Get 10% off Photoshop.

+

+

+

US$19.99/mo US$54.99/mo

+

+
+

Create gorgeous images, rich graphics, and incredible art. Save 10% for the first year. Ends Mar + 20. +

+

See terms.

+
+
+ Save now +
+
+ + + + + + +
+
+
+
+ +

INDIVIDUALS

+

+

Get 10% off Photoshop.

+

+

+

US$19.99/mo US$54.99/mo

+

+
+

Create gorgeous images, rich graphics, and incredible art. Save 10% for the first year. Ends Mar + 20. +

+

See terms.

+
+
+ Save now +
+
+ + + + + + +
+
+
+
+ +

INDIVIDUALS

+

+

Get 10% off Photoshop.

+

+

+

US$19.99/mo US$54.99/mo

+

+
+

Create gorgeous images, rich graphics, and incredible art. Save 10% for the first year. Ends Mar + 20. +

+

See terms.

+
+
+ Save now +
+
+ + + + + + +
+
+
+
+ + diff --git a/libs/features/mas/web-components/test/merch-card.test.html b/libs/features/mas/web-components/test/merch-card.test.html index 477880ec37..3c6dbc8340 100644 --- a/libs/features/mas/web-components/test/merch-card.test.html +++ b/libs/features/mas/web-components/test/merch-card.test.html @@ -56,147 +56,6 @@
-
-

Three cards

- -

INDIVIDUALS

-

Save over 30% on Creative Cloud All Apps.

-

-
-

Get 20+ creative apps and save big when you choose a yearly plan instead of a monthly plan.

-

See terms.

-
-
- Save now -
-
- - - - - - -
-
- -

STUDENTS AND TEACHERS

-

Save over 60% on Creative Cloud All Apps.

-
-

Get 20+ apps — including Photoshop, Premiere Pro, and Illustrator — and save big for the first - year. -

-

Get 20+ apps — including Photoshop, Premiere Pro, and Illustrator — and save big for the first - year. -

-

Get 20+ apps — including Photoshop, Premiere Pro, and Illustrator — and save big for the first - year. -

-
-
- Save now -
-
- - - - - - -
-
- -

INDIVIDUALS

-

-

Get 10% off Photoshop.

-

-

-

US$19.99/mo US$54.99/mo

-

-
-

Create gorgeous images, rich graphics, and incredible art. Save 10% for the first year. Ends Mar - 20. -

-

See terms.

-
-
- Save now -
-
- - - - - - -
-
-
-
- -

INDIVIDUALS

-

-

Get 10% off Photoshop.

-

-

-

US$19.99/mo US$54.99/mo

-

-
-

Create gorgeous images, rich graphics, and incredible art. Save 10% for the first year. Ends Mar - 20. -

-

See terms.

-
-
- Save now -
-
- - - - - - -
-
-
-
- -

INDIVIDUALS

-

-

Get 10% off Photoshop.

-

-

-

US$19.99/mo US$54.99/mo

-

-
-

Create gorgeous images, rich graphics, and incredible art. Save 10% for the first year. Ends Mar - 20. -

-

See terms.

-
-
- Save now -
-
- - - - - - -
-
-
@@ -339,88 +198,6 @@

-
- - -
-

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

-
-

Acrobat

@@ -540,6 +317,29 @@

Desktop + Mobile

+ + +

Photoshop

+

Desktop + Mobile

+
+

Our real-time customer data platform collects B2C and B2B data from across systems and unifies it + into real-time profiles ready for activation across any channel.

+
+ +
+ + + + + + +
+
-
- - -
-

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

-
-
Desktop + Mobile

secure-label="Secure Transaction" tabindex="0" filters="" + variant="product" types="" > 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 fd98d757a2..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 @@ -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,26 +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; - catalogCard.toggleActionMenu(); - - }); it('should have and interact with quantity-selector', async () => { const plansCard = document.querySelector('merch-card[type="q-ty"]'); @@ -124,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/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'); From 13b62d5d724ee367952e5009427bfff45d0c9180 Mon Sep 17 00:00:00 2001 From: sonawanesnehal3 <152426902+sonawanesnehal3@users.noreply.github.com> Date: Mon, 16 Sep 2024 14:22:46 +0530 Subject: [PATCH 61/66] Create a container element for search in standalone gnav (#2833) * Adding container for for search in standalone gnav * Updating class name * Adding unit test * Removing space to fix eslint issue * Updating unit test * Updating unit test * Accommodating review comments * Fixing Eslint * Accommodating review comments * Updating client search * Updating client search --------- Co-authored-by: Snehal Sonawane Co-authored-by: Snehal Sonawane --- libs/blocks/global-navigation/global-navigation.js | 1 + libs/navigation/navigation.js | 2 +- test/blocks/global-navigation/global-navigation.test.js | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/libs/blocks/global-navigation/global-navigation.js b/libs/blocks/global-navigation/global-navigation.js index 90e85421d7..5722a778c9 100644 --- a/libs/blocks/global-navigation/global-navigation.js +++ b/libs/blocks/global-navigation/global-navigation.js @@ -822,6 +822,7 @@ class Gnav { ${isDesktop.matches ? '' : this.decorateSearch()} ${this.elements.mainNav} ${isDesktop.matches ? this.decorateSearch() : ''} + ${getConfig().searchEnabled === 'on' ? toFragment`` : ''} `; 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/test/blocks/global-navigation/global-navigation.test.js b/test/blocks/global-navigation/global-navigation.test.js index 4af0799b83..945328606d 100644 --- a/test/blocks/global-navigation/global-navigation.test.js +++ b/test/blocks/global-navigation/global-navigation.test.js @@ -620,4 +620,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; + }); + }); }); From 2ee8d3b257411cff632dc51a2be8ddd411e6a8e0 Mon Sep 17 00:00:00 2001 From: Suhani Jain <110388864+suhjainadobe@users.noreply.github.com> Date: Mon, 16 Sep 2024 14:22:53 +0530 Subject: [PATCH 62/66] MWPW-157682 Check CLS for photoshop page (#2860) * eagerloading icons * eagerloading icons * eagerloading icons * eagerloading icons * eagerloading icons * eagerloading icons * eagerloading icons * eagerloading icons * eagerloading icons --------- Co-authored-by: Suhani Co-authored-by: Suhani --- libs/blocks/hero-marquee/hero-marquee.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libs/blocks/hero-marquee/hero-marquee.js b/libs/blocks/hero-marquee/hero-marquee.js index 52faa48051..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); @@ -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); } From 66694aa4b4fb77bc9a95319a007e41286d924e7f Mon Sep 17 00:00:00 2001 From: Megan Thomas Date: Mon, 16 Sep 2024 01:53:00 -0700 Subject: [PATCH 63/66] MWPW-144470 Support article header author without link (#2863) --- libs/blocks/article-header/article-header.js | 9 +++--- .../article-header/article-header.test.js | 15 ++++++++++ test/blocks/article-header/mocks/body.html | 28 +++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) 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/test/blocks/article-header/article-header.test.js b/test/blocks/article-header/article-header.test.js index 7c0a20bd17..3056c62da5 100644 --- a/test/blocks/article-header/article-header.test.js +++ b/test/blocks/article-header/article-header.test.js @@ -105,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', () => { 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 +

+
+
+ From d7ae16e46d3af3414367c01dd11ef43910f5a2cf Mon Sep 17 00:00:00 2001 From: Santoshkumar Nateekar Date: Mon, 16 Sep 2024 01:54:19 -0700 Subject: [PATCH 64/66] [MWPW-158021] Move Nala tests to Milo Repo (#2847) * add nala tests to milo repo * update the nalarun for single run * fix georouting test and add eslint overrides for nala files * add eslint overrides for nala files * fix operator assignment eslint error * update eslint lines * remove eslint disable lines * move prrun to nala folder * update the path correctly * add nala folder to .hlxignore file * update tag for ost --------- Co-authored-by: milo-pr-merge[bot] <169241390+milo-pr-merge[bot]@users.noreply.github.com> Co-authored-by: Santoshkumar Sharanappa Nateekar Co-authored-by: Santoshkumar Sharanappa Nateekar Co-authored-by: Santoshkumar Sharanappa Nateekar --- .eslintrc.js | 11 + .github/workflows/run-nala-default.yml | 48 ++ .hlxignore | 1 + nala/blocks/accordion/accordion.spec.js | 2 +- nala/blocks/actionitem/actionitem.page.js | 33 + nala/blocks/actionitem/actionitem.spec.js | 87 +++ nala/blocks/actionitem/actionitem.test.js | 188 ++++++ nala/blocks/aside/aside.page.js | 65 ++ nala/blocks/aside/aside.spec.js | 107 +++ nala/blocks/aside/aside.test.js | 525 +++++++++++++++ nala/blocks/card/card.page.js | 57 ++ nala/blocks/card/card.spec.js | 84 +++ nala/blocks/card/card.test.js | 192 ++++++ nala/blocks/carousel/carousel.page.js | 222 +++++++ nala/blocks/carousel/carousel.spec.js | 26 + nala/blocks/carousel/carousel.test.js | 115 ++++ nala/blocks/chart/chart.page.js | 49 ++ nala/blocks/chart/chart.spec.js | 91 +++ nala/blocks/chart/chart.test.js | 197 ++++++ nala/blocks/columns/columns.page.js | 52 ++ nala/blocks/columns/columns.spec.js | 70 ++ nala/blocks/columns/columns.test.js | 160 +++++ nala/blocks/figure/figure.page.js | 9 + nala/blocks/figure/figure.spec.js | 23 + nala/blocks/figure/figure.test.js | 51 ++ nala/blocks/howto/howto.page.js | 52 ++ nala/blocks/howto/howto.spec.js | 23 + nala/blocks/howto/howto.test.js | 76 +++ nala/blocks/icon/icon.page.js | 102 +++ nala/blocks/icon/icon.spec.js | 43 ++ nala/blocks/icon/icon.test.js | 58 ++ nala/blocks/iframe/iframe.page.js | 14 + nala/blocks/iframe/iframe.spec.js | 11 + nala/blocks/iframe/iframe.test.js | 24 + nala/blocks/marketo/marketo.page.js | 131 ++++ nala/blocks/marketo/marketo.spec.js | 73 ++ nala/blocks/marketo/marketo.test.js | 278 ++++++++ nala/blocks/marquee/marquee.page.js | 186 ++++++ nala/blocks/marquee/marquee.spec.js | 187 ++++++ nala/blocks/marquee/marquee.test.js | 572 ++++++++++++++++ nala/blocks/media/media.page.js | 79 +++ nala/blocks/media/media.spec.js | 66 ++ nala/blocks/media/media.test.js | 165 +++++ nala/blocks/merchcard/merchcard.pages.js | 99 +++ nala/blocks/merchcard/merchcard.spec.js | 194 ++++++ nala/blocks/merchcard/merchcard.test.js | 392 +++++++++++ nala/blocks/modal/modal.page.js | 62 ++ nala/blocks/modal/modal.spec.js | 46 ++ nala/blocks/modal/modal.test.js | 114 ++++ nala/blocks/quote/quote.page.js | 64 ++ nala/blocks/quote/quote.spec.js | 73 ++ nala/blocks/quote/quote.test.js | 151 +++++ nala/blocks/review/review.page.js | 89 +++ nala/blocks/review/review.spec.js | 31 + nala/blocks/review/review.test.js | 48 ++ nala/blocks/table/table.page.js | 75 +++ nala/blocks/table/table.spec.js | 121 ++++ nala/blocks/table/table.test.js | 195 ++++++ nala/blocks/tabs/tabs.page.js | 26 + nala/blocks/tabs/tabs.spec.js | 37 ++ nala/blocks/tabs/tabs.test.js | 140 ++++ nala/blocks/text/text.page.js | 115 ++++ nala/blocks/text/text.spec.js | 97 +++ nala/blocks/text/text.test.js | 243 +++++++ nala/blocks/video/video.page.js | 72 ++ nala/blocks/video/video.spec.js | 84 +++ nala/blocks/video/video.test.js | 212 ++++++ nala/blocks/zpattern/zpattern.page.js | 39 ++ nala/blocks/zpattern/zpattern.spec.js | 131 ++++ nala/blocks/zpattern/zpattern.test.js | 168 +++++ nala/features/commerce/commerce.page.js | 19 + nala/features/commerce/commerce.spec.js | 89 +++ nala/features/commerce/commerce.test.js | 474 +++++++++++++ nala/features/feds/footer/footer.page.js | 77 +++ nala/features/feds/footer/footer.spec.js | 26 + nala/features/feds/footer/footer.test.js | 164 +++++ nala/features/feds/header/header.page.js | 110 ++++ nala/features/feds/header/header.spec.js | 12 + nala/features/feds/header/header.test.js | 52 ++ nala/features/feds/login/login.page.js | 118 ++++ nala/features/georouting/georouting.page.js | 2 +- nala/features/georouting/georouting.test.js | 1 - nala/features/imslogin/imslogin.page.js | 23 + nala/features/osttools/ost.page.js | 31 + nala/features/osttools/ost.spec.js | 128 ++++ nala/features/osttools/ost.test.js | 695 ++++++++++++++++++++ nala/features/promotions/promotions.page.js | 25 + nala/features/promotions/promotions.spec.js | 163 +++++ nala/features/promotions/promotions.test.js | 534 +++++++++++++++ nala/libs/imslogin.js | 37 ++ nala/utils/pr.run.sh | 60 ++ 91 files changed, 10560 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/run-nala-default.yml create mode 100644 nala/blocks/actionitem/actionitem.page.js create mode 100644 nala/blocks/actionitem/actionitem.spec.js create mode 100644 nala/blocks/actionitem/actionitem.test.js create mode 100644 nala/blocks/aside/aside.page.js create mode 100644 nala/blocks/aside/aside.spec.js create mode 100644 nala/blocks/aside/aside.test.js create mode 100644 nala/blocks/card/card.page.js create mode 100644 nala/blocks/card/card.spec.js create mode 100644 nala/blocks/card/card.test.js create mode 100644 nala/blocks/carousel/carousel.page.js create mode 100644 nala/blocks/carousel/carousel.spec.js create mode 100644 nala/blocks/carousel/carousel.test.js create mode 100644 nala/blocks/chart/chart.page.js create mode 100644 nala/blocks/chart/chart.spec.js create mode 100644 nala/blocks/chart/chart.test.js create mode 100644 nala/blocks/columns/columns.page.js create mode 100644 nala/blocks/columns/columns.spec.js create mode 100644 nala/blocks/columns/columns.test.js create mode 100644 nala/blocks/figure/figure.page.js create mode 100644 nala/blocks/figure/figure.spec.js create mode 100644 nala/blocks/figure/figure.test.js create mode 100644 nala/blocks/howto/howto.page.js create mode 100644 nala/blocks/howto/howto.spec.js create mode 100644 nala/blocks/howto/howto.test.js create mode 100644 nala/blocks/icon/icon.page.js create mode 100644 nala/blocks/icon/icon.spec.js create mode 100644 nala/blocks/icon/icon.test.js create mode 100644 nala/blocks/iframe/iframe.page.js create mode 100644 nala/blocks/iframe/iframe.spec.js create mode 100644 nala/blocks/iframe/iframe.test.js create mode 100644 nala/blocks/marketo/marketo.page.js create mode 100644 nala/blocks/marketo/marketo.spec.js create mode 100644 nala/blocks/marketo/marketo.test.js create mode 100644 nala/blocks/marquee/marquee.page.js create mode 100644 nala/blocks/marquee/marquee.spec.js create mode 100644 nala/blocks/marquee/marquee.test.js create mode 100644 nala/blocks/media/media.page.js create mode 100644 nala/blocks/media/media.spec.js create mode 100644 nala/blocks/media/media.test.js create mode 100644 nala/blocks/merchcard/merchcard.pages.js create mode 100644 nala/blocks/merchcard/merchcard.spec.js create mode 100644 nala/blocks/merchcard/merchcard.test.js create mode 100644 nala/blocks/modal/modal.page.js create mode 100644 nala/blocks/modal/modal.spec.js create mode 100644 nala/blocks/modal/modal.test.js create mode 100644 nala/blocks/quote/quote.page.js create mode 100644 nala/blocks/quote/quote.spec.js create mode 100644 nala/blocks/quote/quote.test.js create mode 100644 nala/blocks/review/review.page.js create mode 100644 nala/blocks/review/review.spec.js create mode 100644 nala/blocks/review/review.test.js create mode 100644 nala/blocks/table/table.page.js create mode 100644 nala/blocks/table/table.spec.js create mode 100644 nala/blocks/table/table.test.js create mode 100644 nala/blocks/tabs/tabs.page.js create mode 100644 nala/blocks/tabs/tabs.spec.js create mode 100644 nala/blocks/tabs/tabs.test.js create mode 100644 nala/blocks/text/text.page.js create mode 100644 nala/blocks/text/text.spec.js create mode 100644 nala/blocks/text/text.test.js create mode 100644 nala/blocks/video/video.page.js create mode 100644 nala/blocks/video/video.spec.js create mode 100644 nala/blocks/video/video.test.js create mode 100644 nala/blocks/zpattern/zpattern.page.js create mode 100644 nala/blocks/zpattern/zpattern.spec.js create mode 100644 nala/blocks/zpattern/zpattern.test.js create mode 100644 nala/features/commerce/commerce.page.js create mode 100644 nala/features/commerce/commerce.spec.js create mode 100644 nala/features/commerce/commerce.test.js create mode 100644 nala/features/feds/footer/footer.page.js create mode 100644 nala/features/feds/footer/footer.spec.js create mode 100644 nala/features/feds/footer/footer.test.js create mode 100644 nala/features/feds/header/header.page.js create mode 100644 nala/features/feds/header/header.spec.js create mode 100644 nala/features/feds/header/header.test.js create mode 100644 nala/features/feds/login/login.page.js create mode 100644 nala/features/imslogin/imslogin.page.js create mode 100644 nala/features/osttools/ost.page.js create mode 100644 nala/features/osttools/ost.spec.js create mode 100644 nala/features/osttools/ost.test.js create mode 100644 nala/features/promotions/promotions.page.js create mode 100644 nala/features/promotions/promotions.spec.js create mode 100644 nala/features/promotions/promotions.test.js create mode 100644 nala/libs/imslogin.js create mode 100755 nala/utils/pr.run.sh 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/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/.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/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 From cb05c67577ac0e50816e0c866f30c0680768e7ec Mon Sep 17 00:00:00 2001 From: Robert Bogos <146744221+robert-bogos@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:55:26 +0300 Subject: [PATCH 65/66] [MWPW-157973] Run Nala checks on all PRs by default (#2877) removed run nala label check Co-authored-by: milo-pr-merge[bot] <169241390+milo-pr-merge[bot]@users.noreply.github.com> --- .github/workflows/run-nala.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/run-nala.yml b/.github/workflows/run-nala.yml index 7b701eb8e5..55120b8caa 100644 --- a/.github/workflows/run-nala.yml +++ b/.github/workflows/run-nala.yml @@ -7,7 +7,6 @@ on: jobs: action: name: Running E2E & IT - if: contains(github.event.pull_request.labels.*.name, 'run-nala') runs-on: ubuntu-latest steps: From 840e037c88e9369f16a56f8f8ac7088d2f51f994 Mon Sep 17 00:00:00 2001 From: Ryan Clayton Date: Mon, 16 Sep 2024 10:17:35 -0600 Subject: [PATCH 66/66] Revert "MWPW-154980 - Milo advanced page publishing" (#2883) Revert "MWPW-154980 - Milo advanced page publishing (#2846)" This reverts commit 8fd5925aa2bbf6b9bb757c3305727291ecec82b5. --- .../components/bulk-publisher.js | 17 ++---- libs/blocks/bulk-publish-v2/services.js | 25 -------- libs/tools/utils/publish.js | 32 ---------- libs/utils/sidekick-decorate.js | 61 +++---------------- .../bulk-publish-v2/bulk-publish-v2.test.js | 33 ++++------ .../bulk-publish-v2/mocks/authentication.js | 2 +- test/blocks/bulk-publish-v2/mocks/fetch.js | 3 +- .../mocks/response/permissions.json | 26 -------- tools/utils/utils.js | 14 +---- 9 files changed, 30 insertions(+), 183 deletions(-) delete mode 100644 libs/tools/utils/publish.js delete mode 100644 test/blocks/bulk-publish-v2/mocks/response/permissions.json diff --git a/libs/blocks/bulk-publish-v2/components/bulk-publisher.js b/libs/blocks/bulk-publish-v2/components/bulk-publisher.js index 712f663fa0..c0c88ae5db 100644 --- a/libs/blocks/bulk-publish-v2/components/bulk-publisher.js +++ b/libs/blocks/bulk-publish-v2/components/bulk-publisher.js @@ -1,7 +1,7 @@ import './job-process.js'; import { LitElement, html } from '../../../deps/lit-all.min.js'; import { getSheet } from '../../../../tools/utils/utils.js'; -import { authenticate, getPublishable, startJob } from '../services.js'; +import { authenticate, startJob } from '../services.js'; import { getConfig } from '../../../utils/utils.js'; import { delay, @@ -95,8 +95,7 @@ class BulkPublish2 extends LitElement { this.validateUrls(); } - setJobErrors(jobErrors, authErrors) { - const errors = [...jobErrors, ...authErrors]; + setJobErrors(errors) { const urls = []; errors.forEach((error) => { const matched = this.urls.filter((url) => { @@ -324,8 +323,7 @@ class BulkPublish2 extends LitElement { class="panel-title" @click=${handleToggle}> - ${this.jobs.length ? html`${this.jobs.length}` : ''} - Job Result${this.jobs.length > 1 ? 's' : ''} + Job Results
{ - /* c8 ignore next 4 */ const loader = this.renderRoot.querySelector('.load-indicator'); const message = this.renderRoot.querySelector('.message'); loader?.classList.add('hide'); @@ -431,7 +427,6 @@ class BulkPublish2 extends LitElement { const canUse = Object.values(this.user.permissions).filter((perms) => perms.canUse); if (canUse.length) return html``; message = 'Current user is not authorized to use Bulk Publishing Tool'; - /* c8 ignore next 3 */ } else { message = 'Please sign in to AEM sidekick to continue'; } diff --git a/libs/blocks/bulk-publish-v2/services.js b/libs/blocks/bulk-publish-v2/services.js index 76af892bf2..2889aa5660 100644 --- a/libs/blocks/bulk-publish-v2/services.js +++ b/libs/blocks/bulk-publish-v2/services.js @@ -1,4 +1,3 @@ -import userCanPublishPage from '../../tools/utils/publish.js'; import { PROCESS_TYPES, getErrorText, @@ -247,32 +246,8 @@ const updateRetry = async ({ queue, urls, process }) => { return newQueue; }; -// publish authentication service -const getPublishable = async ({ urls, process, user }) => { - let publishable = { authorized: [], unauthorized: [] }; - if (!isLive(process)) { - publishable.authorized = urls; - } else { - const { permissions, profile } = user; - const live = { permissions: ['read'] }; - if (permissions?.publish?.canUse) { - live.permissions.push('write'); - } - publishable = await urls.reduce(async (init, url) => { - const result = await init; - const detail = { webPath: new URL(url).pathname, live, profile }; - const { canPublish, message } = await userCanPublishPage(detail); - if (canPublish) result.authorized.push(url); - else result.unauthorized.push({ href: url, message }); - return result; - }, Promise.resolve(publishable)); - } - return publishable; -}; - export { authenticate, - getPublishable, pollJobStatus, startJob, updateRetry, diff --git a/libs/tools/utils/publish.js b/libs/tools/utils/publish.js deleted file mode 100644 index 49acf0928d..0000000000 --- a/libs/tools/utils/publish.js +++ /dev/null @@ -1,32 +0,0 @@ -import { getCustomConfig } from '../../../tools/utils/utils.js'; - -const userCanPublishPage = async (detail, isBulk = true) => { - if (!detail) return false; - const { live, profile, webPath } = detail; - let canPublish = isBulk ? live?.permissions?.includes('write') : true; - let message = 'Publishing is currently disabled for this page'; - const config = await getCustomConfig('/.milo/publish-permissions-config.json'); - const item = config?.urls?.data?.find(({ url }) => ( - url.endsWith('**') ? webPath.includes(url.slice(0, -2)) : url === webPath - )); - if (item) { - canPublish = false; - if (item.message) message = item.message; - const group = config[item.group]; - if (group && profile?.email) { - let isDeny; - const user = group.data?.find(({ allow, deny }) => { - if (deny) { - /* c8 ignore next 3 */ - isDeny = true; - return deny === profile.email; - } - return allow === profile.email; - }); - canPublish = isDeny ? !user : !!user; - } - } - return { canPublish, message }; -}; - -export default userCanPublishPage; diff --git a/libs/utils/sidekick-decorate.js b/libs/utils/sidekick-decorate.js index acaa428716..ec125ef650 100644 --- a/libs/utils/sidekick-decorate.js +++ b/libs/utils/sidekick-decorate.js @@ -1,22 +1,4 @@ -import userCanPublishPage from '../tools/utils/publish.js'; - -const PUBLISH_BTN = '.publish.plugin button'; -const BACKUP_PROFILE = '.profile-email'; -const CONFIRM_MESSAGE = 'Are you sure? This will publish to production.'; - export default function stylePublish(sk) { - const setupPublishBtn = async (page, btn) => { - const { canPublish, message } = await userCanPublishPage(page, false); - btn.setAttribute('disabled', !canPublish); - const messageText = btn.querySelector('span'); - const text = canPublish ? CONFIRM_MESSAGE : message; - if (messageText) { - messageText.innerText = text; - } else { - btn.insertAdjacentHTML('beforeend', `${text}`); - } - }; - const style = new CSSStyleSheet(); style.replaceSync(` :host { @@ -28,21 +10,19 @@ export default function stylePublish(sk) { order: 100; } .publish.plugin button { - position: relative; - } - .publish.plugin button:not([disabled=true]) { background: var(--bg-color); border-color: #b46157; color: var(--text-color); + position: relative; } - .publish.plugin button:not([disabled=true]):hover { + .publish.plugin button:hover { background-color: var(--hlx-sk-button-hover-bg); border-color: unset; color: var(--hlx-sk-button-hover-color); } .publish.plugin button > span { display: none; - background: #666; + background: var(--bg-color); border-radius: 4px; line-height: 1.2rem; padding: 8px 12px; @@ -53,9 +33,6 @@ export default function stylePublish(sk) { width: 150px; white-space: pre-wrap; } - .publish.plugin button:not([disabled=true]) > span { - background: var(--bg-color); - } .publish.plugin button:hover > span { display: block; color: var(--text-color); @@ -64,39 +41,19 @@ export default function stylePublish(sk) { content: ''; border-left: 6px solid transparent; border-right: 6px solid transparent; - border-bottom: 6px solid #666; + border-bottom: 6px solid var(--bg-color); position: absolute; text-align: center; top: -6px; left: 50%; transform: translateX(-50%); } - .publish.plugin button:not([disabled=true]) > span:before { - border-bottom: 6px solid var(--bg-color); - } `); - sk.shadowRoot.adoptedStyleSheets = [style]; - - sk.addEventListener('statusfetched', async (event) => { - const page = event?.detail?.data; - const btn = event?.target?.shadowRoot?.querySelector(PUBLISH_BTN); - if (page && btn) { - setupPublishBtn(page, btn); - } - }); - - setTimeout(async () => { - const btn = sk.shadowRoot.querySelector(PUBLISH_BTN); - btn?.setAttribute('disabled', true); - const message = btn?.querySelector('span'); - if (btn && !message) { - const page = { - webPath: window.location.pathname, - // added for edge case where the statusfetched event isnt fired on refresh - profile: { email: sk.shadowRoot.querySelector(BACKUP_PROFILE)?.innerText }, - }; - setupPublishBtn(page, btn); - } + setTimeout(() => { + const btn = sk.shadowRoot.querySelector('.publish.plugin button'); + btn?.insertAdjacentHTML('beforeend', ` + Are you sure? This will publish to production. + `); }, 500); } diff --git a/test/blocks/bulk-publish-v2/bulk-publish-v2.test.js b/test/blocks/bulk-publish-v2/bulk-publish-v2.test.js index 7cd56109b4..3b634681d2 100644 --- a/test/blocks/bulk-publish-v2/bulk-publish-v2.test.js +++ b/test/blocks/bulk-publish-v2/bulk-publish-v2.test.js @@ -114,16 +114,6 @@ describe('Bulk Publish Tool', () => { await mouseEvent(rootEl.querySelector('.fix-btn')); }); - it('can trigger cannot publish config', async () => { - await clock.runAllAsync(); - await setProcess(rootEl, 'publish'); - await setTextArea(rootEl, 'https://error--milo--adobecom.hlx.page/not/a/valid/path'); - await mouseEvent(rootEl.querySelector('#RunProcess')); - const errors = rootEl.querySelector('.errors'); - expect(errors.querySelector('strong').innerText).to.equal('Publishing disabled until the test is over'); - await mouseEvent(rootEl.querySelector('.fix-btn')); - }); - it('can validate milo urls and enable form', async () => { await clock.runAllAsync(); await setProcess(rootEl, 'publish'); @@ -142,17 +132,6 @@ describe('Bulk Publish Tool', () => { await mouseEvent(rootEl.querySelector('.switch.half')); }); - it('can toggle job timing flyout', async () => { - await clock.runAllAsync(); - const doneJobProcess = rootEl.querySelector('job-process'); - const jobInfo = doneJobProcess?.shadowRoot.querySelector('job-info'); - const timerDetail = jobInfo?.shadowRoot.querySelector('.timer'); - await mouseEvent(timerDetail); - await clock.runAllAsync(); - await mouseEvent(timerDetail); - expect(timerDetail.classList.contains('show-times')).to.be.false; - }); - it('can submit valid bulk preview job', async () => { await clock.runAllAsync(); await setProcess(rootEl, 'preview'); @@ -195,6 +174,17 @@ describe('Bulk Publish Tool', () => { expect(rootEl.querySelectorAll('job-process')).to.have.lengthOf(4); }); + it('can toggle job timing flyout', async () => { + await clock.runAllAsync(); + const doneJobProcess = rootEl.querySelector('job-process'); + const jobInfo = doneJobProcess?.shadowRoot.querySelector('job-info'); + const timerDetail = jobInfo?.shadowRoot.querySelector('.timer'); + await mouseEvent(timerDetail); + await clock.runAllAsync(); + await mouseEvent(timerDetail); + expect(timerDetail.classList.contains('show-times')).to.be.false; + }); + it('can toggle view mode', async () => { await mouseEvent(rootEl.querySelector('.switch.full')); await clock.runAllAsync(); @@ -228,6 +218,7 @@ describe('Bulk Publish Tool', () => { it('can clear bulk jobs', async () => { await clock.runAllAsync(); await mouseEvent(rootEl.querySelector('.clear-jobs')); + await clock.runAllAsync(); expect(rootEl.querySelectorAll('job-process')).to.have.lengthOf(0); }); }); diff --git a/test/blocks/bulk-publish-v2/mocks/authentication.js b/test/blocks/bulk-publish-v2/mocks/authentication.js index ca826fa82d..22d01045ad 100644 --- a/test/blocks/bulk-publish-v2/mocks/authentication.js +++ b/test/blocks/bulk-publish-v2/mocks/authentication.js @@ -23,7 +23,7 @@ class MockAuth extends HTMLElement { bubbles: true, detail: { data: { - profile: { name: 'Unit Test', email: 'tester@adobe.com' }, + profile: { name: 'Unit Test' }, preview: { permissions }, live: { permissions }, }, diff --git a/test/blocks/bulk-publish-v2/mocks/fetch.js b/test/blocks/bulk-publish-v2/mocks/fetch.js index 48d7637f54..8ab62d86d3 100644 --- a/test/blocks/bulk-publish-v2/mocks/fetch.js +++ b/test/blocks/bulk-publish-v2/mocks/fetch.js @@ -11,7 +11,6 @@ const requests = { delstatus: 'https://admin.hlx.page/job/adobecom/milo/main/preview-remove/job-2024-01-24t23-16-20-377z/details', retry: 'https://admin.hlx.page/preview/adobecom/milo/main/tools/bulk-publish-v2-test', index: 'https://admin.hlx.page/index/adobecom/milo/main/tools/bulk-publish-v2-test', - permissions: '/.milo/publish-permissions-config.json', }; const getMocks = async () => { @@ -26,7 +25,7 @@ const getMocks = async () => { export async function mockFetch() { const mocks = await getMocks(); stub(window, 'fetch').callsFake((...args) => { - const headers = args[1]?.body ?? null; + const headers = args[1].body ?? null; const body = headers ? JSON.parse(headers) : false; const [resource] = args; const response = mocks.find((mock) => (body.delete ? mock.request === 'delete' : mock.url === resource)); diff --git a/test/blocks/bulk-publish-v2/mocks/response/permissions.json b/test/blocks/bulk-publish-v2/mocks/response/permissions.json deleted file mode 100644 index 37fe5eaec9..0000000000 --- a/test/blocks/bulk-publish-v2/mocks/response/permissions.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "urls": { - "total": 4, - "offset": 0, - "limit": 4, - "data": [ - { - "url": "/not/a/valid/path", - "group": "gwp-test", - "message": "Publishing disabled until the test is over" - } - ] - }, - "gwp-test": { - "total": 2, - "offset": 0, - "limit": 2, - "data": [ - { "allow": "testuser@adobe.com" }, - { "allow": "testuser1@adobe.com" } - ] - }, - ":version": 3, - ":names": ["urls", "gwp-US", "no-publish", "gwp-FR"], - ":type": "multi-sheet" -} diff --git a/tools/utils/utils.js b/tools/utils/utils.js index 8193d21fdf..29d68397d8 100644 --- a/tools/utils/utils.js +++ b/tools/utils/utils.js @@ -1,7 +1,6 @@ const IMS_CLIENT_ID = 'milo_ims'; const IMS_PROD_URL = 'https://auth.services.adobe.com/imslib/imslib.min.js'; const STYLE_SHEETS = {}; -const CONFIGS = {}; const getImsToken = async (loadScript) => { window.adobeid = { @@ -26,15 +25,4 @@ const getSheet = async (url) => { return sheet; }; -const getCustomConfig = async (path) => { - /* c8 ignore next 3 */ - if (CONFIGS[path] !== undefined) { - return CONFIGS[path]; - } - const resp = await fetch(path); - const config = resp.ok ? await resp.json() : null; - CONFIGS[path] = config; - return config; -}; - -export { getImsToken, getSheet, getCustomConfig }; +export { getImsToken, getSheet };