diff --git a/.eslintrc.js b/.eslintrc.js index 5e06277005..f24adfa387 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -45,6 +45,17 @@ module.exports = { files: ['test/**/*.js'], rules: { 'no-console': 0 }, }, + { + // Override for nala test + files: ['nala/**/*.js', 'nala/**/*.test.js'], + rules: { + 'no-console': 0, + 'import/no-extraneous-dependencies': 0, + 'max-len': 0, + 'chai-friendly/no-unused-expressions': 0, + 'no-plusplus': 0, + }, + }, ], ignorePatterns: [ '/libs/deps/*', diff --git a/.github/workflows/helpers.js b/.github/workflows/helpers.js index 7b36bc38b1..056ac563f4 100644 --- a/.github/workflows/helpers.js +++ b/.github/workflows/helpers.js @@ -26,8 +26,8 @@ const RCPDates = [ end: new Date('2024-09-12T14:00:00-07:00'), }, { - start: new Date('2024-10-14T00:00:00-07:00'), - end: new Date('2024-11-18T17:00:00-08:00'), + start: new Date('2024-10-07T00:00:00-07:00'), + end: new Date('2024-10-18T17:00:00-07:00'), }, { start: new Date('2024-11-17T00:00:00-08:00'), diff --git a/.github/workflows/run-mas-tests.yaml b/.github/workflows/run-mas-tests.yaml new file mode 100644 index 0000000000..c5ccba75b7 --- /dev/null +++ b/.github/workflows/run-mas-tests.yaml @@ -0,0 +1,46 @@ +name: MAS Unit Tests +on: + push: + branches: + - main + pull_request: + types: [opened, synchronize, reopened] +jobs: + run-tests: + name: Running tests + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [20.x] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Set up Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Install npm dependencies + run: npm install + working-directory: libs/features/mas + + - name: Run npm test + run: npm test + working-directory: libs/features/mas + + - name: Upload commerce coverage to Codecov + uses: codecov/codecov-action@v4 + with: + name: mas-commerce + token: ${{ secrets.CODECOV_TOKEN }} + files: libs/features/mas/commerce/coverage/lcov.info + + - name: Upload web-components coverage to Codecov + uses: codecov/codecov-action@v4 + with: + name: mas-web-components + token: ${{ secrets.CODECOV_TOKEN }} + files: libs/features/mas/web-components/coverage/lcov.info diff --git a/.github/workflows/run-nala-default.yml b/.github/workflows/run-nala-default.yml new file mode 100644 index 0000000000..4031be7c8d --- /dev/null +++ b/.github/workflows/run-nala-default.yml @@ -0,0 +1,48 @@ +name: Run Nala Tests + +on: + push: + branches: + - stage + - main + pull_request: + branches: + - stage + - main + types: [opened, synchronize, reopened] + +jobs: + run-nala-tests: + name: Running Nala E2E UI Tests + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [20.x] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Set up Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Set execute permission for nalarun.sh + run: chmod +x ./nala/utils/pr.run.sh + + - name: Run Nala Tests via pr.run.sh + run: ./nala/utils/pr.run.sh + env: + labels: ${{ join(github.event.pull_request.labels.*.name, ' ') }} + branch: ${{ github.event.pull_request.head.ref }} + repoName: ${{ github.repository }} + prUrl: ${{ github.event.pull_request.head.repo.html_url }} + prOrg: ${{ github.event.pull_request.head.repo.owner.login }} + prRepo: ${{ github.event.pull_request.head.repo.name }} + prBranch: ${{ github.event.pull_request.head.ref }} + prBaseBranch: ${{ github.event.pull_request.base.ref }} + GITHUB_ACTION_PATH: ${{ github.workspace }} + IMS_EMAIL: ${{ secrets.IMS_EMAIL }} + IMS_PASS: ${{ secrets.IMS_PASS }} diff --git a/.github/workflows/run-nala.yml b/.github/workflows/run-nala.yml index 7b701eb8e5..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: 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/.hlxignore b/.hlxignore index e394ce0b3b..0fdf90bee2 100644 --- a/.hlxignore +++ b/.hlxignore @@ -3,6 +3,7 @@ .* *.md test/* +nala build/* LICENSE web-test-runner.config.mjs diff --git a/libs/blocks/article-feed/article-helpers.js b/libs/blocks/article-feed/article-helpers.js index 779099e8f1..e9cfe1d59b 100644 --- a/libs/blocks/article-feed/article-helpers.js +++ b/libs/blocks/article-feed/article-helpers.js @@ -129,7 +129,7 @@ export async function loadTaxonomy() { a.href = tax.link; } else { // eslint-disable-next-line no-console - window.lana.log(`Trying to get a link for an unknown topic: ${topic} (current page)`, { tags: 'errorType=warn,module=article-feed' }); + window.lana.log(`Trying to get a link for an unknown topic: ${topic} (current page)`, { tags: 'article-feed' }); a.href = '#'; } delete a.dataset.topicLink; diff --git a/libs/blocks/article-header/article-header.js b/libs/blocks/article-header/article-header.js index fb4ea8235f..8f94e0b405 100644 --- a/libs/blocks/article-header/article-header.js +++ b/libs/blocks/article-header/article-header.js @@ -32,14 +32,15 @@ function openPopup(e) { } async function buildAuthorInfo(authorEl, bylineContainer) { - const { href, textContent } = authorEl; + const { textContent } = authorEl; + const link = authorEl.href || authorEl.dataset.authorPage; const config = getConfig(); const base = config.miloLibs || config.codeRoot; const authorImg = createTag('div', { class: 'article-author-image' }); authorImg.style.backgroundImage = `url(${base}/blocks/article-header/adobe-logo.svg)`; bylineContainer.prepend(authorImg); - const doc = await validateAuthorUrl(href); + const doc = await validateAuthorUrl(link); if (!doc) { const p = createTag('p', null, textContent); authorEl.replaceWith(p); @@ -48,7 +49,7 @@ async function buildAuthorInfo(authorEl, bylineContainer) { const img = doc.querySelector('img'); if (img) { - img.setAttribute('alt', authorEl.textContent); + img.setAttribute('alt', textContent); authorImg.append(img); if (!img.complete) { img.addEventListener('load', () => { @@ -197,7 +198,7 @@ export default async function init(blockEl) { bylineContainer.firstElementChild.classList.add('article-byline-info'); const authorContainer = bylineContainer.firstElementChild.firstElementChild; - const authorEl = authorContainer.querySelector('a'); + const authorEl = authorContainer.firstElementChild; authorContainer.classList.add('article-author'); buildAuthorInfo(authorEl, bylineContainer); diff --git a/libs/blocks/aside/aside.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..fc488d4ada 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']; @@ -96,6 +96,7 @@ function addCloseButton(el) { el.querySelector('.foreground').appendChild(closeBtn); closeBtn.addEventListener('click', (e) => { e.target.closest('.section').classList.add('close-sticky-section'); + document.dispatchEvent(new CustomEvent('milo:sticky:closed')); }); } @@ -163,6 +164,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 +189,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 +214,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 +225,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/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/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/editorial-card/editorial-card.css b/libs/blocks/editorial-card/editorial-card.css index 8191393f80..a244581810 100644 --- a/libs/blocks/editorial-card/editorial-card.css +++ b/libs/blocks/editorial-card/editorial-card.css @@ -92,6 +92,11 @@ margin-top: auto; } +.editorial-card .vp-media .tablet-only, +.editorial-card .vp-media .desktop-only { + display: none; +} + .editorial-card .card-footer > div { display: flex; flex-direction: column; @@ -142,11 +147,22 @@ height: 100%; } +.editorial-card .background .milo-video, +.editorial-card .background .milo-iframe { + height: 100%; + padding-bottom: 0; +} + .editorial-card .lockup-area, .editorial-card .device { margin: 0; } +.editorial-card .device { + font-size: var(--type-body-xxs-size); + line-height: var(--type-body-xxs-lh); +} + .editorial-card .lockup-area { row-gap: var(--spacing-xxs); } @@ -173,7 +189,6 @@ justify-content: center; } - .editorial-card hr { background-color: currentcolor; border: none; @@ -248,3 +263,24 @@ .editorial-card.footer-align-left .action-area { justify-content: start; } .editorial-card.footer-align-center .action-area { justify-content: center; } + + +/* Tablet up */ +@media screen and (min-width: 600px) { + .editorial-card .vp-media .mobile-only, + .editorial-card .vp-media .desktop-only { + display: none; + } + + .editorial-card .vp-media .tablet-only { display: block; } +} + +/* desktop up */ +@media screen and (min-width: 1200px) { + .editorial-card .vp-media .mobile-only, + .editorial-card .vp-media .tablet-only { + display: none; + } + + .editorial-card .vp-media .desktop-only { display: block; } +} diff --git a/libs/blocks/editorial-card/editorial-card.js b/libs/blocks/editorial-card/editorial-card.js index 9cd9a31d2b..166b5c1211 100644 --- a/libs/blocks/editorial-card/editorial-card.js +++ b/libs/blocks/editorial-card/editorial-card.js @@ -25,9 +25,10 @@ async function decorateLockupFromContent(el) { const extendDeviceContent = (el) => { const detail = el.querySelector('[class^="detail-"]'); const prevElem = detail?.previousElementSibling; - if (!prevElem || ![...prevElem.classList].some((c) => c.startsWith('body-'))) return; - prevElem.classList.remove('body-m'); - prevElem.classList.add('body-xxs', 'device'); + if (!prevElem) return; + const elBodyClass = [...prevElem.classList].find((c) => c.startsWith('body-')); + prevElem.classList.remove(elBodyClass); + prevElem.classList.add('device'); }; const decorateMedia = (el, media) => { @@ -37,14 +38,14 @@ const decorateMedia = (el, media) => { if (mediaVideo) { applyHoverPlay(mediaVideo); } - if (media.children.length > 1) decorateBlockBg(el, media); + if (media.children.length > 1) decorateBlockBg(el, media, { className: 'vp-media' }); }; const decorateForeground = async (el, rows) => { - rows.forEach(async (row, i) => { + rows.forEach((row, i) => { if (i === 0) { row.classList.add('foreground'); - await decorateLockupFromContent(row); + decorateLockupFromContent(row); } else if (i === (rows.length - 1)) { row.classList.add('card-footer'); if (!row.textContent.trim()) row.classList.add('empty'); @@ -57,12 +58,13 @@ const decorateForeground = async (el, rows) => { }; const decorateBgRow = (el, background) => { - if (background.textContent.trim() === '') { + decorateBlockBg(el, background); + const rows = background.querySelectorAll(':scope > div'); + const bgRowsEmpty = [...rows].every((row) => row.innerHTML.trim() === ''); + if (bgRowsEmpty) { el.classList.add('no-bg'); background.remove(); - return; } - decorateBlockBg(el, background); }; function handleClickableCard(el) { 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/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/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..cf32fe32ec 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,18 @@ 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-utilities .unav-comp-profile .secondary-button{ + line-height: inherit; } #feds-googleLogin { @@ -482,7 +486,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 +592,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 +646,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 +662,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..5722a778c9 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, @@ -320,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') { @@ -328,7 +330,7 @@ class Gnav { return; } lanaLog({ message: 'GNAV: Error with IMS', e, tags: 'errorType=info,module=gnav' }); - }); + })); decorateTopNav = () => { this.elements.mobileToggle = this.decorateToggle(); @@ -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; @@ -812,6 +822,7 @@ class Gnav { ${isDesktop.matches ? '' : this.decorateSearch()} ${this.elements.mainNav} ${isDesktop.matches ? this.decorateSearch() : ''} + ${getConfig().searchEnabled === 'on' ? toFragment`` : ''} `; @@ -1027,6 +1038,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/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..91a0499e68 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) { @@ -100,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); + await 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, 'hasDetailHeading'); + 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')) { @@ -205,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]; @@ -216,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); @@ -224,4 +259,5 @@ export default async function init(el) { } }); decorateTextOverrides(el, ['-heading', '-body', '-detail'], mainCopy); + await Promise.all(promiseArr); } diff --git a/libs/blocks/library-config/lists/templates.js b/libs/blocks/library-config/lists/templates.js index 2c1f780078..736f79c35c 100644 --- a/libs/blocks/library-config/lists/templates.js +++ b/libs/blocks/library-config/lists/templates.js @@ -43,7 +43,7 @@ function formatDom(aemDom, path) { async function formatTemplate(path) { const resp = await fetch(path); - if (!resp.ok) window.lana.log('Could not fetch template path', { tags: 'errorType=info,module=sidekick-templates' }); + if (!resp.ok) window.lana.log('Could not fetch template path', { tags: 'sidekick-templates', errorType: 'i' }); const html = await resp.text(); const dom = new DOMParser().parseFromString(html, 'text/html'); diff --git a/libs/blocks/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/libs/blocks/merch-card-collection/merch-card-collection.js b/libs/blocks/merch-card-collection/merch-card-collection.js index 695f9da51e..dad54cb600 100644 --- a/libs/blocks/merch-card-collection/merch-card-collection.js +++ b/libs/blocks/merch-card-collection/merch-card-collection.js @@ -1,3 +1,4 @@ +import { overrideUrlOrigin } from '../../utils/helpers.js'; import { createTag, decorateLinks, getConfig, loadBlock, loadStyle, localizeLink, } from '../../utils/utils.js'; @@ -57,7 +58,7 @@ async function getCardsRoot(config, html) { } const fetchOverrideCard = (action, config) => new Promise((resolve, reject) => { - fetch(`${localizeLink(action?.target, config)}.plain.html`).then((res) => { + fetch(`${localizeLink(overrideUrlOrigin(action?.target))}.plain.html`).then((res) => { if (res.ok) { res.text().then((cardContent) => { const response = { path: action.target, cardContent: /^
(.*)<\/div>$/.exec(cardContent.replaceAll('\n', ''))[1] }; diff --git a/libs/blocks/merch-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_-].*$/; diff --git a/libs/blocks/merch/merch.js b/libs/blocks/merch/merch.js index 4dd85521cf..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) => { @@ -364,6 +355,8 @@ async function openExternalModal(url, getModal) { }); } +const isInternalModal = (url) => /\/fragments\//.test(url); + export async function openModal(e, url, offerType) { e.preventDefault(); e.stopImmediatePropagation(); @@ -371,10 +364,10 @@ export async function openModal(e, url, offerType) { await import('../modal/modal.merch.js'); const offerTypeClass = offerType === OFFER_TYPE_TRIAL ? 'twp' : 'crm'; let modal; - if (/\/fragments\//.test(url)) { + if (isInternalModal(url)) { const fragmentPath = url.split(/hlx.(page|live)/).pop(); modal = await openFragmentModal(fragmentPath, getModal); - } else if (/^https?:/.test(url)) { + } else { modal = await openExternalModal(url, getModal); } if (modal) { @@ -398,7 +391,8 @@ export async function getModalAction(offers, options) { const columnName = (offerType === OFFER_TYPE_TRIAL) ? FREE_TRIAL_PATH : BUY_NOW_PATH; let url = checkoutLinkConfig[columnName]; if (!url) return undefined; - url = localizeLink(checkoutLinkConfig[columnName]); + url = isInternalModal(url) + ? localizeLink(checkoutLinkConfig[columnName]) : checkoutLinkConfig[columnName]; return { url, handler: (e) => openModal(e, url, offerType) }; } @@ -427,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/blocks/mobile-app-banner/mobile-app-banner.js b/libs/blocks/mobile-app-banner/mobile-app-banner.js index 0e2e546b07..62f68b7086 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: '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/libs/blocks/notification/notification.css b/libs/blocks/notification/notification.css index 0dff835631..7131cc89eb 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 #00000029; 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 { @@ -296,8 +310,22 @@ 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; } .notification.pill .foreground .text > :not(.action-area) { @@ -305,6 +333,16 @@ inline-size: calc(100% - var(--spacing-xxs)); } +.notification.pill .copy-wrap p:first-child:not(:only-child) { + 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; @@ -329,6 +367,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 +464,8 @@ .notification.pill.flexible { pointer-events: none; + box-shadow: none; + padding-block-end: var(--spacing-xxs); } .notification .flexible-inner { @@ -435,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) { @@ -444,7 +498,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 +527,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 +574,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 +589,6 @@ } .notification.pill .foreground .text [class*="heading-"] { - margin-inline-end: var(--spacing-xxs); margin-block-end: 0; } @@ -549,7 +612,7 @@ } .notification.pill .foreground .icon-area { - margin-inline-end: var(--spacing-xs); + margin-inline-end: var(--spacing-s); margin-block-end: 0; } @@ -576,6 +639,7 @@ flex-wrap: wrap; align-items: baseline; text-align: start; + gap: var(--spacing-xxs); } .notification.pill .copy-wrap > * { @@ -585,4 +649,14 @@ .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; + } + + .notification.pill.max-width-6-desktop { max-width: 600px; } + .notification.pill.max-width-8-desktop { max-width: 800px; } + .notification.pill.max-width-10-desktop { max-width: 1000px; } + .notification.pill.max-width-12-desktop { max-width: 1200px; } + .notification.pill.max-width-auto-desktop { max-width: unset; } } diff --git a/libs/blocks/notification/notification.js b/libs/blocks/notification/notification.js index 52787c2521..5ee6398d6c 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,18 +75,25 @@ 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) { const btn = createTag('button', { 'aria-label': 'close', class: 'close' }, closeSvg); - btn.addEventListener('click', () => (el.style.display = 'none')); + btn.addEventListener('click', () => { + el.style.display = 'none'; + el.closest('.section')?.classList.add('close-sticky-section'); + document.dispatchEvent(new CustomEvent('milo:sticky:closed')); + }); + el.appendChild(btn); } @@ -104,10 +113,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 +132,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 +170,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/path-finder/path-finder.js b/libs/blocks/path-finder/path-finder.js index 8cad4c1c9d..3ba93e71df 100644 --- a/libs/blocks/path-finder/path-finder.js +++ b/libs/blocks/path-finder/path-finder.js @@ -63,7 +63,7 @@ function buildUi(el, path) { async function setup(el) { await login({ scopes: SCOPES, telemetry: TELEMETRY }); if (!account.value.username) { - window.lana.log('Could not login to MS Graph', { tags: 'errorType=info,module=path-finder' }); + window.lana.log('Could not login to MS Graph', { tags: 'path-finder', errorType: 'i' }); return; } el.innerHTML = ''; diff --git a/libs/blocks/preflight/panels/seo.js b/libs/blocks/preflight/panels/seo.js index e69c561a7d..443b35d7c8 100644 --- a/libs/blocks/preflight/panels/seo.js +++ b/libs/blocks/preflight/panels/seo.js @@ -160,7 +160,7 @@ async function spidyCheck(url) { connectionError(); } catch (e) { connectionError(); - window.lana.log(`There was a problem connecting to the link check API ${url}. ${e}`, { tags: 'errorType=info,module=preflight' }); + window.lana.log(`There was a problem connecting to the link check API ${url}. ${e}`, { tags: 'preflight', errorType: 'i' }); } return false; } @@ -182,7 +182,7 @@ async function getSpidyResults(url, opts) { return acc; }, []); } catch (e) { - window.lana.log(`There was a problem connecting to the link check API ${url}/api/url-http-status. ${e}`, { tags: 'errorType=info,module=preflight' }); + window.lana.log(`There was a problem connecting to the link check API ${url}/api/url-http-status. ${e}`, { tags: 'preflight', errorType: 'i' }); return []; } } diff --git a/libs/blocks/quiz-entry/mlField.js b/libs/blocks/quiz-entry/mlField.js index a04545b80c..17b9f72fc9 100644 --- a/libs/blocks/quiz-entry/mlField.js +++ b/libs/blocks/quiz-entry/mlField.js @@ -26,7 +26,7 @@ export const getMLResults = async (endpoint, apiKey, threshold, input, count, va body: JSON.stringify(params), }) .then((response) => response.json()) - .catch((error) => window.lana.log(`ERROR: Fetching fi codes ${error}`, { tags: 'errorType=info,module=quiz-entry' })); + .catch((error) => window.lana.log(`ERROR: Fetching fi codes ${error}`, { tags: 'quiz-entry', errorType: 'i' })); let value; let highestProb = null; diff --git a/libs/blocks/quiz-entry/quiz-entry.css b/libs/blocks/quiz-entry/quiz-entry.css index 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/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-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``}
`; }; diff --git a/libs/blocks/quiz-results/quiz-results.js b/libs/blocks/quiz-results/quiz-results.js index 7465782fc7..7c9622366a 100644 --- a/libs/blocks/quiz-results/quiz-results.js +++ b/libs/blocks/quiz-results/quiz-results.js @@ -20,7 +20,7 @@ async function loadFragments(el, experiences) { function redirectPage(quizUrl, debug, message) { const url = quizUrl ? getLocalizedURL(quizUrl.text) : 'https://adobe.com'; - window.lana.log(message, { tags: 'errorType=error,module=quiz-results' }); + window.lana.log(message, { tags: 'quiz-results' }); if (debug === 'quiz-results') { // eslint-disable-next-line no-console @@ -97,7 +97,7 @@ export default async function init(el, debug = null, localStoreKey = null) { loadFragments(el, basic); } else { - window.lana.log(`${LOADING_ERROR} The quiz-results block is misconfigured`, { tags: 'errorType=error,module=quiz-results' }); + window.lana.log(`${LOADING_ERROR} The quiz-results block is misconfigured`, { tags: 'quiz-results' }); return; } diff --git a/libs/blocks/region-nav/region-nav.js b/libs/blocks/region-nav/region-nav.js index 7c626978ef..109051ffcd 100644 --- a/libs/blocks/region-nav/region-nav.js +++ b/libs/blocks/region-nav/region-nav.js @@ -3,7 +3,8 @@ import { getConfig } from '../../utils/utils.js'; /* c8 ignore next 11 */ function handleEvent(prefix, link) { const domain = window.location.host.endsWith('.adobe.com') ? 'domain=adobe.com' : ''; - document.cookie = `international=${prefix};path=/;${domain}`; + const maxAge = 365 * 24 * 60 * 60; // max-age in seconds for 365 days + document.cookie = `international=${prefix};max-age=${maxAge};path=/;${domain}`; sessionStorage.setItem('international', prefix); fetch(link.href, { method: 'HEAD' }).then((resp) => { if (!resp.ok) throw new Error('request failed'); diff --git a/libs/blocks/section-metadata/section-metadata.css b/libs/blocks/section-metadata/section-metadata.css index 82d18fbb26..8b17e07611 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; @@ -201,7 +205,7 @@ .section.sticky-bottom.promo-sticky-section { background: none; - z-index: 4; + z-index: 40000; } .section.sticky-bottom.popup { @@ -244,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)); @@ -260,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); 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/blocks/table/table.css b/libs/blocks/table/table.css index 87836b1ae9..fd95c0714b 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; @@ -259,6 +264,7 @@ .table .section-row .col { padding: 16px 24px; + column-gap: 0.5ch; } .table .section-row .col:has(> p:nth-child(2)) { @@ -609,6 +615,14 @@ header.global-navigation { right: initial; } + .table .row-heading .col-heading .pricing { + overflow-wrap: anywhere; + } + + .table .row-heading .col-heading span[is='inline-price'] { + display: inline; + } + .table .row-heading .col:nth-child(n+1) { padding: 20px; } @@ -700,4 +714,12 @@ header.global-navigation { flex-direction: column; align-items: initial; } + + .table :is(.heading-button,.action-area) { + max-width: 100%; + } + + .table .action-area .con-button.button-l { + overflow: hidden; + } } diff --git a/libs/blocks/table/table.js b/libs/blocks/table/table.js index b3ffb88085..9d41802ff6 100644 --- a/libs/blocks/table/table.js +++ b/libs/blocks/table/table.js @@ -5,7 +5,8 @@ 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; +const isMobileLandscape = () => (window.matchMedia('(orientation: landscape)').matches && window.innerHeight <= MOBILE_SIZE); function defineDeviceByScreenSize() { const screenWidth = window.innerWidth; if (screenWidth >= DESKTOP_SIZE) { @@ -17,9 +18,15 @@ 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) => { + headingCols.forEach((col, i) => { col.classList.add('col-heading'); if (!col.innerHTML) return; @@ -65,6 +72,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 +219,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 +247,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 +368,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); @@ -355,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; @@ -469,16 +503,11 @@ 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); }); - const isStickyHeader = el.classList.contains('sticky'); - handleHighlight(el); if (isMerch) formatMerchTable(el); @@ -499,7 +528,7 @@ export default function init(el) { const handleResize = () => { applyStylesBasedOnScreenSize(el, originTable); - if (isStickyHeader) handleScrollEffect(el); + if (isStickyHeader(el)) handleScrollEffect(el); }; handleResize(); @@ -525,4 +554,6 @@ export default function init(el) { }); observer.observe(el); + + tableIndex++; } diff --git a/libs/blocks/tag-selector/tag-selector.js b/libs/blocks/tag-selector/tag-selector.js index 8607fed08c..ccfc43f9c9 100644 --- a/libs/blocks/tag-selector/tag-selector.js +++ b/libs/blocks/tag-selector/tag-selector.js @@ -86,7 +86,7 @@ const TagSelector = ({ consumerUrls = [] }) => { const fetchCasS = async () => { const { tags, errorMsg } = await loadCaasTags(caasTagUrl); - if (errorMsg) window.lana.log(`Tag Selector. Error fetching caas tags: ${errorMsg}`, { tags: 'errorType=info,module=tag-selector' }); + if (errorMsg) window.lana.log(`Tag Selector. Error fetching caas tags: ${errorMsg}`, { tags: 'tag-selector', errorType: 'i' }); setTagSelectorTags((prevConsumerTags) => ({ CaaS: tags, ...prevConsumerTags })); }; diff --git a/libs/blocks/text/text.css b/libs/blocks/text/text.css index 0104689af2..6242f50132 100644 --- a/libs/blocks/text/text.css +++ b/libs/blocks/text/text.css @@ -1,4 +1,6 @@ .text-block { + --accent-height: 10px; + position: relative; } @@ -16,6 +18,8 @@ .text-block [class^="detail"] { margin: 0 0 var(--spacing-xxs) 0; } +.text-block [class^="detail"] strong { font-weight: unset; } /* bugfix for FF */ + .text-block .cta-container, .text-block p.action-area { margin-top: var(--spacing-s); } @@ -31,7 +35,6 @@ position: absolute; right: 0; top: 0; - z-index: -1; overflow: hidden; } @@ -73,6 +76,25 @@ margin: var(--spacing-m) 0; } +.text-block .title-l { + font-size: var(--type-body-m-size); + line-height: var(--type-body-m-lh); + font-weight: 700; + text-transform: none; + margin-bottom: var(--spacing-xs); +} + +.text-block :is(.image, .lockup-area) { + margin-block-end: var(--spacing-m); +} + +.text-block .image em { + display: block; + font-size: var(--type-body-s-size); + line-height: var(--type-body-s-lh); + margin-block-start: var(--spacing-xs); +} + .text-block .icon-list-item { list-style: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'/%3E"); /* Must not be `none` for accessibility */ position: relative; @@ -83,17 +105,68 @@ inset: 0 100% auto auto; } +.text-block.accent-bar { + inline-size: 100%; + block-size: var(--accent-height); +} + +.text-block .lockup-area img { + width: auto; +} + /* Alignment */ .text-block.center { text-align: center; align-items: center; } +.text-block .icon-area { + display: flex; + column-gap: var(--spacing-xs); +} + +.text-block p.icon-area { /* NOT tags with icons in them */ + margin-block-end: var(--spacing-m); +} + +.text-block.xxs-icon .icon-area { + height: var(--icon-size-xxs); +} + +.text-block.xs-icon .icon-area { + height: var(--icon-size-xs); +} + +.text-block.s-icon .icon-area { + height: var(--icon-size-s); +} + +.text-block.m-icon .icon-area { + height: var(--icon-size-m); +} + +.text-block.l-icon .icon-area { + height: var(--icon-size-l); +} + +.text-block.xl-icon .icon-area { + height: var(--icon-size-xl); +} + +.text-block.xxl-icon .icon-area { + height: var(--icon-size-xxl); +} + +.text-block .icon-area img { + max-height: 100%; + width: auto; +} + .text-block.center .action-area, .text-block.center .icon-area { justify-content: center; } .text-block.right { - text-align: right; + text-align: end; align-items: end; } @@ -145,17 +218,25 @@ max-width: unset; } -.text-block .icon-area { - display: flex; - column-gap: var(--spacing-xxs); -} - .text-block .icon-area.con-button { column-gap: unset; } .text-block .icon-area picture { line-height: 0em; + height: inherit; /* Safari + FF bug fix */ +} + +.text-block .image picture { + display: flex; +} + +.text-block.center .image picture { + justify-content: center; +} + +.text-block.right .image picture { + justify-content: flex-end; } /* icon inline */ diff --git a/libs/blocks/text/text.js b/libs/blocks/text/text.js index 5886a8f23b..947922b312 100644 --- a/libs/blocks/text/text.js +++ b/libs/blocks/text/text.js @@ -24,9 +24,8 @@ const blockTypeSizes = { }, }; -function decorateMultiViewport(el) { +function decorateMultiViewport(foreground) { const viewports = ['mobile-up', 'tablet-up', 'desktop-up']; - const foreground = el.querySelector('.foreground'); if (foreground.childElementCount === 2 || foreground.childElementCount === 3) { [...foreground.children].forEach((child, index) => { child.className = viewports[index]; @@ -36,8 +35,13 @@ function decorateMultiViewport(el) { return foreground; } -function decorateBlockIconArea(el) { - const headings = el.querySelectorAll('h1, h2, h3, h4, h5, h6'); +function decorateBlockIconArea(content, el) { + const first = content.children[0]; + if (first?.querySelector('img')) { + const areaClass = el.className.match(/-(lockup|icon)/); + first.classList.add(areaClass ? `${areaClass[1]}-area` : 'image'); + } + const headings = content.querySelectorAll('h1, h2, h3, h4, h5, h6'); if (!headings) return; headings.forEach((h) => { const hPrevElem = h.previousElementSibling; @@ -76,14 +80,20 @@ function decorateLinkFarms(el) { }); } +function addStyle(filename) { + const { miloLibs, codeRoot } = getConfig(); + const base = miloLibs || codeRoot; + loadStyle(`${base}/styles/${filename}.css`); +} + export default async function init(el) { el.classList.add('text-block', 'con-block'); let rows = el.querySelectorAll(':scope > div'); - if (rows.length > 1) { + if (rows.length > 1 || el.matches('.accent-bar')) { if (rows[0].textContent !== '') el.classList.add('has-bg'); const [head, ...tail] = rows; decorateBlockBg(el, head); - rows = tail; + rows = tail || rows; } const helperClasses = []; let blockType = 'text'; @@ -97,8 +107,11 @@ export default async function init(el) { const hasLinkFarm = el.classList.contains('link-farm'); rows.forEach((row) => { row.classList.add('foreground'); - if (!hasLinkFarm) decorateBlockText(row, blockTypeSizes[blockType][size]); - decorateBlockIconArea(row); + if (!hasLinkFarm) { + [...row.children].forEach((c) => decorateBlockText(c, blockTypeSizes[blockType][size])); + decorateMultiViewport(row); + } + [...row.children].forEach((c) => decorateBlockIconArea(c, el)); }); if (el.classList.contains('full-width')) helperClasses.push('max-width-8-desktop', 'center', 'xxl-spacing'); if (el.classList.contains('intro')) helperClasses.push('max-width-8-desktop', 'xxl-spacing-top', 'xl-spacing-bottom'); @@ -109,7 +122,6 @@ export default async function init(el) { if (hasLinkFarm) decorateLinkFarms(el); el.classList.add(...helperClasses); decorateTextOverrides(el); - if (!hasLinkFarm) decorateMultiViewport(el); const lastActionArea = el.querySelector('.action-area:last-of-type'); if (lastActionArea) { @@ -124,4 +136,10 @@ export default async function init(el) { mnemonicList.querySelectorAll('p').forEach((product) => product.removeAttribute('class')); await loadBlock(mnemonicList); } + if (el.matches('[class*="rounded-corners"]')) addStyle('rounded-corners'); + if (el.matches('[class*="-lockup"]')) addStyle('iconography'); + // Override Detail with Title L style if class exists - Temporary solution until Spectrum 2 + if (el.classList.contains('l-title')) { + el.querySelectorAll('[class*="detail-"]')?.forEach((detail) => detail.classList.add('title-l')); + } } diff --git a/libs/blocks/video-metadata/video-metadata.js b/libs/blocks/video-metadata/video-metadata.js index 388cb62034..d94b9410b4 100644 --- a/libs/blocks/video-metadata/video-metadata.js +++ b/libs/blocks/video-metadata/video-metadata.js @@ -25,7 +25,7 @@ function addBroadcastEventField(videoObj, blockKey, blockValue) { videoObj.publication[i][camelize(key)] = blockValue; break; default: - window.lana.log(`VideoMetadata -- Unknown BroadcastEvent property: ${blockKey}`, { tags: 'errorType=warn,module=video-metadata' }); + window.lana.log(`VideoMetadata -- Unknown BroadcastEvent property: ${blockKey}`, { tags: 'video-metadata' }); break; } } @@ -45,7 +45,7 @@ function addClipField(videoObj, blockKey, blockValue) { videoObj.hasPart[i][camelize(key)] = blockValue; break; default: - window.lana.log(`VideoMetadata -- Unhandled Clip property: ${blockKey}`, { tags: 'errorType=warn,module=video-metadata' }); + window.lana.log(`VideoMetadata -- Unhandled Clip property: ${blockKey}`, { tags: 'video-metadata' }); break; } } @@ -61,7 +61,7 @@ function addSeekToActionField(videoObj, blockKey, blockValue) { videoObj.potentialAction['startOffset-input'] = blockValue; break; default: - window.lana.log(`VideoMetadata -- Unhandled SeekToAction property: ${blockKey}`, { tags: 'errorType=warn,module=video-metadata' }); + window.lana.log(`VideoMetadata -- Unhandled SeekToAction property: ${blockKey}`, { tags: 'video-metadata' }); break; } } @@ -96,7 +96,7 @@ export function createVideoObject(record) { addSeekToActionField(video, blockKey, blockVal); break; default: - window.lana.log(`VideoMetadata -- Unhandled VideoObject property: ${blockKey}`, { tags: 'errorType=warn,module=video-metadata' }); + window.lana.log(`VideoMetadata -- Unhandled VideoObject property: ${blockKey}`, { tags: 'video-metadata' }); break; } }); diff --git a/libs/blocks/vimeo/vimeo.css b/libs/blocks/vimeo/vimeo.css index 2724a958eb..90bdd59030 100644 --- a/libs/blocks/vimeo/vimeo.css +++ b/libs/blocks/vimeo/vimeo.css @@ -5,3 +5,59 @@ position: relative; padding-bottom: 56.25%; } + +lite-vimeo { + aspect-ratio: 16 / 9; + background-color: #000; + position: relative; + display: block; + contain: content; + background-position: center center; + background-size: cover; + cursor: pointer; +} + +lite-vimeo > .ltv-playbtn { + font-size: 10px; + padding: 0; + width: 6.5em; + height: 4em; + background: rgb(23, 35, 34, 75%); + z-index: 1; + opacity: .8; + border-radius: .5em; + transition: opacity .2s ease-out, background .2s ease-out; + outline: 0; + border: 0; + cursor: pointer; +} + +lite-vimeo:hover > .ltv-playbtn { + background-color: rgb(0, 173, 239); + opacity: 1; +} + +lite-vimeo > .ltv-playbtn::before { + content: ''; + border-style: solid; + border-width: 10px 0 10px 20px; + border-color: transparent transparent transparent #fff; +} + +lite-vimeo > .ltv-playbtn, +lite-vimeo > .ltv-playbtn::before { + position: absolute; + top: 50%; + left: 50%; + transform: translate3d(-50%, -50%, 0); +} + +lite-vimeo.ltv-activated { + cursor: unset; +} + +lite-vimeo.ltv-activated::before, +lite-vimeo.ltv-activated > .ltv-playbtn { + opacity: 0; + pointer-events: none; +} diff --git a/libs/blocks/vimeo/vimeo.js b/libs/blocks/vimeo/vimeo.js index 8019259af4..10c55b599d 100644 --- a/libs/blocks/vimeo/vimeo.js +++ b/libs/blocks/vimeo/vimeo.js @@ -1,25 +1,80 @@ -import { createIntersectionObserver, createTag, isInTextNode } from '../../utils/utils.js'; +// part of the code is an optimized version of lite-vimeo-embed -> https://github.com/luwes/lite-vimeo-embed +import { replaceKey } from '../../features/placeholders.js'; +import { createIntersectionObserver, createTag, getConfig, isInTextNode, loadLink } from '../../utils/utils.js'; -export default function init(a) { - if (isInTextNode(a)) return; - const embedVimeo = () => { - const url = new URL(a.href); - let src = url.href; - if (url.hostname !== 'player.vimeo.com') { - const video = url.pathname.split('/')[1]; - src = `https://player.vimeo.com/video/${video}?app_id=122963`; - } - const iframe = createTag('iframe', { - src, - style: 'border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute;', +class LiteVimeo extends HTMLElement { + static preconnected = false; + + connectedCallback() { + this.isMobile = navigator.userAgent.includes('Mobi'); + this.videoId = this.getAttribute('videoid'); + this.setupThumbnail(); + this.setupPlayButton(); + this.addEventListener('pointerover', LiteVimeo.warmConnections, { once: true }); + this.addEventListener('click', this.addIframe); + } + + static warmConnections() { + if (LiteVimeo.preconnected) return; + LiteVimeo.preconnected = true; + ['player.vimeo.com', + 'i.vimeocdn.com', + 'f.vimeocdn.com', + 'fresnel.vimeocdn.com', + ].forEach((url) => loadLink(`https://${url}`, { rel: 'preconnect' })); + } + + setupThumbnail() { + const { width, height } = this.getBoundingClientRect(); + const roundedWidth = Math.min(Math.ceil(width / 100) * 100, 1920); + const roundedHeight = Math.round((roundedWidth / width) * height); + + fetch(`https://vimeo.com/api/v2/video/${this.videoId}.json`) + .then((response) => response.json()) + .then((data) => { + const thumbnailUrl = data[0]?.thumbnail_large?.replace(/-d_[\dx]+$/i, `-d_${roundedWidth}x${roundedHeight}`); + this.style.backgroundImage = `url("${thumbnailUrl}")`; + }) + .catch((e) => { + window.lana.log(`Error fetching Vimeo thumbnail: ${e}`, { tags: 'vimeo', errorType: 'i' }); + }); + } + + async setupPlayButton() { + const playBtnEl = createTag('button', { + type: 'button', + 'aria-label': `${await replaceKey('play-video', getConfig())}`, + class: 'ltv-playbtn', + }); + this.append(playBtnEl); + } + + addIframe() { + if (this.classList.contains('ltv-activated')) return; + this.classList.add('ltv-activated'); + const iframeEl = createTag('iframe', { + style: 'border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute; background-color: #000;', frameborder: '0', - allow: 'autoplay; fullscreen; picture-in-picture', - allowfullscreen: 'true', title: 'Content from Vimeo', - loading: 'lazy', + allow: 'accelerometer; fullscreen; autoplay; encrypted-media; gyroscope; picture-in-picture', + allowFullscreen: true, + src: `https://player.vimeo.com/video/${encodeURIComponent(this.videoId)}?autoplay=1&muted=${this.isMobile ? 1 : 0}`, }); - const wrapper = createTag('div', { class: 'embed-vimeo' }, iframe); + this.insertAdjacentElement('afterend', iframeEl); + iframeEl.addEventListener('load', () => iframeEl.focus(), { once: true }); + this.remove(); + } +} +export default async function init(a) { + if (isInTextNode(a)) return; + if (!customElements.get('lite-vimeo')) customElements.define('lite-vimeo', LiteVimeo); + + const embedVimeo = () => { + const url = new URL(a.href); + const videoid = url.pathname.split('/')[url.hostname === 'player.vimeo.com' ? 2 : 1]; + const liteVimeo = createTag('lite-vimeo', { videoid }); + const wrapper = createTag('div', { class: 'embed-vimeo' }, liteVimeo); a.parentElement.replaceChild(wrapper, a); }; diff --git a/libs/blocks/youtube/youtube.css b/libs/blocks/youtube/youtube.css index 35e7775617..d682a05679 100644 --- a/libs/blocks/youtube/youtube.css +++ b/libs/blocks/youtube/youtube.css @@ -1 +1,56 @@ @import url('../../styles/iframe.css'); + +lite-youtube { + top: 0; + left: 0; + width: 100%; + height: 100%; + position: absolute; + cursor: pointer; +} + +lite-youtube > .lty-playbtn { + display: block; + width: 68px; + height: 48px; + position: absolute; + cursor: pointer; + transform: translate3d(-50%, -50%, 0); + top: 50%; + left: 50%; + z-index: 1; + background-color: transparent; + background-image: url('data:image/svg+xml;utf8,'); + filter: grayscale(100%); + transition: filter .1s cubic-bezier(0, 0, 0.2, 1); + border: none; +} + +lite-youtube:hover > .lty-playbtn, +lite-youtube .lty-playbtn:focus { + filter: none; +} + +lite-youtube.lyt-activated { + cursor: unset; +} + +lite-youtube.lyt-activated::before, +lite-youtube.lyt-activated > .lty-playbtn { + opacity: 0; + pointer-events: none; +} + +.lyt-visually-hidden { + clip: rect(0 0 0 0); + clip-path: inset(50%); + height: 1px; + overflow: hidden; + position: absolute; + white-space: nowrap; + width: 1px; +} + +.dark-background { + background-color: #000; +} diff --git a/libs/blocks/youtube/youtube.js b/libs/blocks/youtube/youtube.js index d8589e96e9..1dd4063975 100644 --- a/libs/blocks/youtube/youtube.js +++ b/libs/blocks/youtube/youtube.js @@ -1,36 +1,109 @@ -import { createIntersectionObserver, isInTextNode } from '../../utils/utils.js'; +// part of the code is an optimized version of lite-youtube-embed -> https://github.com/paulirish/lite-youtube-embed +import { createIntersectionObserver, createTag, isInTextNode, loadLink } from '../../utils/utils.js'; + +class LiteYTEmbed extends HTMLElement { + connectedCallback() { + this.isMobile = navigator.userAgent.includes('Mobi'); + this.videoId = this.getAttribute('videoid'); + const playBtnEl = createTag('button', { type: 'button', class: 'lty-playbtn' }); + this.append(playBtnEl); + this.playLabel = this.getAttribute('playlabel') || 'Play'; + this.style.backgroundImage = `url("https://i.ytimg.com/vi/${this.videoId}/hqdefault.jpg")`; + this.style.backgroundSize = 'cover'; + this.style.backgroundPosition = 'center'; + const playBtnLabelEl = createTag('span', { class: 'lyt-visually-hidden' }); + playBtnLabelEl.textContent = this.playLabel; + playBtnEl.append(playBtnLabelEl); + this.addEventListener('pointerover', LiteYTEmbed.warmConnections, { once: true }); + this.addEventListener('click', this.addIframe); + this.needsYTApiForAutoplay = navigator.vendor.includes('Apple') || this.isMobile; + } + + static warmConnections() { + if (LiteYTEmbed.preconnected) return; + LiteYTEmbed.preconnected = true; + ['www.youtube-nocookie.com', + 'www.google.com', + 'googleads.g.doubleclick.net', + 'static.doubleclick.net', + ].forEach((url) => loadLink(`https://${url}`, { rel: 'preconnect' })); + } + + static loadYouTubeAPI() { + return new Promise((resolve) => { + if (window.YT?.Player) { + resolve(); + return; + } + const tag = createTag('script', { src: 'https://www.youtube.com/iframe_api' }); + window.onYouTubeIframeAPIReady = resolve; + document.head.appendChild(tag); + }); + } + + async addIframe() { + if (this.classList.contains('lyt-activated')) return; + + this.classList.add('lyt-activated'); + const params = new URLSearchParams(this.getAttribute('params') || []); + params.append('autoplay', '1'); + params.append('playsinline', '1'); + if (this.isMobile) params.append('mute', '1'); + + if (this.needsYTApiForAutoplay) { + await LiteYTEmbed.loadYouTubeAPI(); + await new Promise((resolve) => { window.YT.ready(resolve); }); + // eslint-disable-next-line + new window.YT.Player(this, { + videoId: this.videoId, + playerVars: Object.fromEntries(params), + allow: 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture', + allowfullscreen: true, + title: this.playLabel, + }); + } else { + const iframeEl = createTag('iframe', { + src: `https://www.youtube-nocookie.com/embed/${encodeURIComponent(this.videoId)}?${params.toString()}`, + allowFullscreen: true, + allow: 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture', + title: this.playLabel, + }); + this.insertAdjacentElement('afterend', iframeEl); + iframeEl.focus(); + this.remove(); + } + } +} + +export default async function init(a) { + if (!customElements.get('lite-youtube')) customElements.define('lite-youtube', LiteYTEmbed); -export default function init(a) { const embedVideo = () => { if (isInTextNode(a) || !a.origin?.includes('youtu')) return; const title = !a.textContent.includes('http') ? a.textContent : 'Youtube Video'; const searchParams = new URLSearchParams(a.search); const id = searchParams.get('v') || a.pathname.split('/').pop(); searchParams.delete('v'); - const src = `https://www.youtube.com/embed/${id}?${searchParams.toString()}`; - const embedHTML = ` -
- -
`; - a.insertAdjacentHTML('afterend', embedHTML); + const liteYTElement = createTag('lite-youtube', { videoid: id, playlabel: title }); + + if (searchParams.toString()) liteYTElement.setAttribute('params', searchParams.toString()); + + const ytContainer = createTag('div', { class: 'milo-video dark-background' }, liteYTElement); + a.insertAdjacentElement('afterend', ytContainer); a.remove(); + if (document.readyState === 'complete') { - /* eslint-disable-next-line no-underscore-dangle */ + // eslint-disable-next-line no-underscore-dangle window._satellite?.track('trackYoutube'); } else { document.addEventListener('readystatechange', () => { if (document.readyState === 'complete') { - /* eslint-disable-next-line no-underscore-dangle */ + // eslint-disable-next-line no-underscore-dangle window._satellite?.track('trackYoutube'); } }); } }; + createIntersectionObserver({ el: a, callback: embedVideo }); } diff --git a/libs/deps/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..e1afe935b7 100644 --- a/libs/deps/mas/commerce.js +++ b/libs/deps/mas/commerce.js @@ -1,4 +1,3 @@ -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?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,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}; -//# sourceMappingURL=commerce.js.map +`,ce.MISSING_INTL_API,o);var O=n.getPluralRules(t,{type:l.pluralType}).select(f-(l.offset||0));L=l.options[O]||l.options.other}if(!L)throw new ar(l.value,f,Object.keys(l.options),o);s.push.apply(s,Re(L.value,t,n,r,i,f-(l.offset||0)));continue}}return na(s)}function aa(e,t){return t?E(E(E({},e||{}),t||{}),Object.keys(e).reduce(function(n,r){return n[r]=E(E({},e[r]),t[r]||{}),n},{})):e}function oa(e,t){return t?Object.keys(e).reduce(function(n,r){return n[r]=aa(e[r],t[r]),n},E({},e)):e}function or(e){return{create:function(){return{get:function(t){return e[t]},set:function(t,n){e[t]=n}}}}}function sa(e){return e===void 0&&(e={number:{},dateTime:{},pluralRules:{}}),{getNumberFormat:we(function(){for(var t,n=[],r=0;r0?e.substring(0,r):"";let i=En(e.split("").reverse().join("")),a=n-i,o=e.substring(a,a+1),s=a+(o==="."||o===","?1:0);t.suffix=i>0?e.substring(s,n):"",t.mask=e.substring(r,s),t.maskHasNegativeSign=t.mask.charAt(0)==="-",t.maskHasPositiveSign=t.mask.charAt(0)==="+";let c=t.mask.match(ca);return t.decimal=c&&c[c.length-1]||".",t.separator=c&&c[1]&&c[0]||",",c=t.mask.split(t.decimal),t.integer=c[0],t.fraction=c[1],t}function fa(e,t,n){let r=!1,i={value:e};e<0&&(r=!0,i.value=-i.value),i.sign=r?"-":"",i.value=Number(i.value).toFixed(t.fraction&&t.fraction.length),i.value=Number(i.value).toString();let a=t.fraction&&t.fraction.lastIndexOf("0"),[o="0",s=""]=i.value.split(".");return(!s||s&&s.length<=a)&&(s=a<0?"":(+("0."+s)).toFixed(a+1).replace("0.","")),i.integer=o,i.fraction=s,pa(i,t),(i.result==="0"||i.result==="")&&(r=!1,i.sign=""),!r&&t.maskHasPositiveSign?i.sign="+":r&&t.maskHasPositiveSign?i.sign="-":r&&(i.sign=n&&n.enforceMaskSign&&!t.maskHasNegativeSign?"":"-"),i}function pa(e,t){e.result="";let n=t.integer.split(t.separator),r=n.join(""),i=r&&r.indexOf("0");if(i>-1)for(;e.integer.lengthMath.round(e*20)/20},sr=(e,t)=>({accept:e,round:t}),da=[sr(({divisor:e,price:t})=>t%e==0,({divisor:e,price:t})=>t/e),sr(({usePrecision:e})=>e,({divisor:e,price:t})=>Math.ceil(Math.floor(t*1e4/e)/100)/100),sr(()=>!0,({divisor:e,price:t})=>Math.ceil(Math.floor(t*100/e)/100))],lr={[C.YEAR]:{[N.MONTHLY]:De.MONTH,[N.ANNUAL]:De.YEAR},[C.MONTH]:{[N.MONTHLY]:De.MONTH}},Sa=(e,t)=>e.indexOf(`'${t}'`)===0,Ea=(e,t=!0)=>{let n=e.replace(/'.*?'/,"").trim(),r=Pn(n);return!!r?t||(n=n.replace(/[,\.]0+/,r)):n=n.replace(/\s?(#.*0)(?!\s)?/,"$&"+ba(e)),n},xa=e=>{let t=ga(e),n=Sa(e,t),r=e.replace(/'.*?'/,""),i=Ln.test(r)||vn.test(r);return{currencySymbol:t,isCurrencyFirst:n,hasCurrencySpace:i}},yn=e=>e.replace(Ln,gn).replace(vn,gn),ba=e=>e.match(/#(.?)#/)?.[1]===bn?ma:bn,ga=e=>e.match(/'(.*?)'/)?.[1]??"",Pn=e=>e.match(/0(.?)0/)?.[1]??"";function it({formatString:e,price:t,usePrecision:n,isIndianPrice:r=!1},i,a=o=>o){let{currencySymbol:o,isCurrencyFirst:s,hasCurrencySpace:c}=xa(e),u=n?Pn(e):"",l=Ea(e,n),p=n?2:0,f=a(t,{currencySymbol:o}),m=r?f.toLocaleString("hi-IN",{minimumFractionDigits:p,maximumFractionDigits:p}):xn(l,f),T=n?m.lastIndexOf(u):m.length,x=m.substring(0,T),b=m.substring(T+1);return{accessiblePrice:e.replace(/'.*?'/,"SYMBOL").replace(/#.*0/,m).replace(/SYMBOL/,o),currencySymbol:o,decimals:b,decimalsDelimiter:u,hasCurrencySpace:c,integer:x,isCurrencyFirst:s,recurrenceTerm:i}}var _n=e=>{let{commitment:t,term:n,usePrecision:r}=e,i=Ta[n]??1;return it(e,i>1?De.MONTH:lr[t]?.[n],(a,{currencySymbol:o})=>{let s={divisor:i,price:a,usePrecision:r},{round:c}=da.find(({accept:l})=>l(s));if(!c)throw new Error(`Missing rounding rule for: ${JSON.stringify(s)}`);return(Aa[o]??(l=>l))(c(s))})},In=({commitment:e,term:t,...n})=>it(n,lr[e]?.[t]),Nn=e=>{let{commitment:t,term:n}=e;return t===C.YEAR&&n===N.MONTHLY?it(e,De.YEAR,r=>r*12):it(e,lr[t]?.[n])};var La={recurrenceLabel:"{recurrenceTerm, select, MONTH {/mo} YEAR {/yr} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {per month} YEAR {per year} other {}}",perUnitLabel:"{perUnit, select, LICENSE {per license} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {per license} other {}}",freeLabel:"Free",freeAriaLabel:"Free",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"Alternatively at {alternativePrice}",strikethroughAriaLabel:"Regularly at {strikethroughPrice}"},va=Xr("ConsonantTemplates/price"),ya=/<.+?>/g,G={container:"price",containerOptical:"price-optical",containerStrikethrough:"price-strikethrough",containerAnnual:"price-annual",disabled:"disabled",currencySpace:"price-currency-space",currencySymbol:"price-currency-symbol",decimals:"price-decimals",decimalsDelimiter:"price-decimals-delimiter",integer:"price-integer",recurrence:"price-recurrence",taxInclusivity:"price-tax-inclusivity",unitType:"price-unit-type"},ue={perUnitLabel:"perUnitLabel",perUnitAriaLabel:"perUnitAriaLabel",recurrenceLabel:"recurrenceLabel",recurrenceAriaLabel:"recurrenceAriaLabel",taxExclusiveLabel:"taxExclusiveLabel",taxInclusiveLabel:"taxInclusiveLabel",strikethroughAriaLabel:"strikethroughAriaLabel"},Pa="TAX_EXCLUSIVE",_a=e=>Fr(e)?Object.entries(e).filter(([,t])=>Se(t)||je(t)||t===!0).reduce((t,[n,r])=>t+` ${n}${r===!0?"":'="'+Gr(r)+'"'}`,""):"",K=(e,t,n,r=!1)=>`${r?yn(t):t??""}`;function Ia(e,{accessibleLabel:t,currencySymbol:n,decimals:r,decimalsDelimiter:i,hasCurrencySpace:a,integer:o,isCurrencyFirst:s,recurrenceLabel:c,perUnitLabel:u,taxInclusivityLabel:l},p={}){let f=K(G.currencySymbol,n),m=K(G.currencySpace,a?" ":""),T="";return s&&(T+=f+m),T+=K(G.integer,o),T+=K(G.decimalsDelimiter,i),T+=K(G.decimals,r),s||(T+=m+f),T+=K(G.recurrence,c,null,!0),T+=K(G.unitType,u,null,!0),T+=K(G.taxInclusivity,l,!0),K(e,T,{...p,"aria-label":t})}var fe=({displayOptical:e=!1,displayStrikethrough:t=!1,displayAnnual:n=!1}={})=>({country:r,displayFormatted:i=!0,displayRecurrence:a=!0,displayPerUnit:o=!1,displayTax:s=!1,language:c,literals:u={}}={},{commitment:l,formatString:p,price:f,priceWithoutDiscount:m,taxDisplay:T,taxTerm:x,term:b,usePrecision:P}={},_={})=>{Object.entries({country:r,formatString:p,language:c,price:f}).forEach(([J,yt])=>{if(yt==null)throw new Error(`Argument "${J}" is missing`)});let L={...La,...u},O=`${c.toLowerCase()}-${r.toUpperCase()}`;function v(J,yt){let Pt=L[J];if(Pt==null)return"";try{return new Sn(Pt.replace(ya,""),O).format(yt)}catch{return va.error("Failed to format literal:",Pt),""}}let I=t&&m?m:f,R=e?_n:In;n&&(R=Nn);let{accessiblePrice:F,recurrenceTerm:q,...te}=R({commitment:l,formatString:p,term:b,price:e?f:I,usePrecision:P,isIndianPrice:r==="IN"}),X=F,ae="";if(g(a)&&q){let J=v(ue.recurrenceAriaLabel,{recurrenceTerm:q});J&&(X+=" "+J),ae=v(ue.recurrenceLabel,{recurrenceTerm:q})}let oe="";if(g(o)){oe=v(ue.perUnitLabel,{perUnit:"LICENSE"});let J=v(ue.perUnitAriaLabel,{perUnit:"LICENSE"});J&&(X+=" "+J)}let Z="";g(s)&&x&&(Z=v(T===Pa?ue.taxExclusiveLabel:ue.taxInclusiveLabel,{taxTerm:x}),Z&&(X+=" "+Z)),t&&(X=v(ue.strikethroughAriaLabel,{strikethroughPrice:X}));let Y=G.container;if(e&&(Y+=" "+G.containerOptical),t&&(Y+=" "+G.containerStrikethrough),n&&(Y+=" "+G.containerAnnual),g(i))return Ia(Y,{...te,accessibleLabel:X,recurrenceLabel:ae,perUnitLabel:oe,taxInclusivityLabel:Z},_);let{currencySymbol:Te,decimals:He,decimalsDelimiter:Fe,hasCurrencySpace:_e,integer:vt,isCurrencyFirst:qn}=te,Ae=[vt,Fe,He];qn?(Ae.unshift(_e?"\xA0":""),Ae.unshift(Te)):(Ae.push(_e?"\xA0":""),Ae.push(Te)),Ae.push(ae,oe,Z);let Zn=Ae.join("");return K(Y,Zn,_)},Vn=()=>(e,t,n)=>{let i=(e.displayOldPrice===void 0||g(e.displayOldPrice))&&t.priceWithoutDiscount&&t.priceWithoutDiscount!=t.price;return`${fe()(e,t,n)}${i?" "+fe({displayStrikethrough:!0})(e,t,n):""}`};var cr=fe(),ur=Vn(),fr=fe({displayOptical:!0}),pr=fe({displayStrikethrough:!0}),hr=fe({displayAnnual:!0});var Na=(e,t)=>{if(!(!xe(e)||!xe(t)))return Math.floor((t-e)/t*100)},On=()=>(e,t,n)=>{let{price:r,priceWithoutDiscount:i}=t,a=Na(r,i);return a===void 0?'':`${a}%`};var mr=On();var{freeze:Ue}=Object,z=Ue({...se}),j=Ue({...H}),pe={STAGE:"STAGE",PRODUCTION:"PRODUCTION",LOCAL:"LOCAL"},Tr=Ue({...C}),Ar=Ue({...Kr}),dr=Ue({...N});var vr={};ai(vr,{CLASS_NAME_FAILED:()=>at,CLASS_NAME_PENDING:()=>ot,CLASS_NAME_RESOLVED:()=>st,ERROR_MESSAGE_BAD_REQUEST:()=>lt,ERROR_MESSAGE_MISSING_LITERALS_URL:()=>Va,ERROR_MESSAGE_OFFER_NOT_FOUND:()=>Sr,EVENT_TYPE_ERROR:()=>Oa,EVENT_TYPE_FAILED:()=>ct,EVENT_TYPE_PENDING:()=>ut,EVENT_TYPE_READY:()=>Le,EVENT_TYPE_RESOLVED:()=>ft,LOG_NAMESPACE:()=>Er,Landscape:()=>he,PARAM_AOS_API_KEY:()=>Ca,PARAM_ENV:()=>xr,PARAM_LANDSCAPE:()=>br,PARAM_WCS_API_KEY:()=>wa,STATE_FAILED:()=>W,STATE_PENDING:()=>B,STATE_RESOLVED:()=>$,TAG_NAME_SERVICE:()=>ee,WCS_PROD_URL:()=>gr,WCS_STAGE_URL:()=>Lr});var at="placeholder-failed",ot="placeholder-pending",st="placeholder-resolved",lt="Bad WCS request",Sr="Commerce offer not found",Va="Literals URL not provided",Oa="wcms:commerce:error",ct="wcms:placeholder:failed",ut="wcms:placeholder:pending",Le="wcms:commerce:ready",ft="wcms:placeholder:resolved",Er="wcms/commerce",xr="commerce.env",br="commerce.landscape",Ca="commerce.aosKey",wa="commerce.wcsKey",gr="https://www.adobe.com/web_commerce_artifact",Lr="https://www.stage.adobe.com/web_commerce_artifact_stage",W="failed",B="pending",$="resolved",ee="wcms-commerce",he={DRAFT:"DRAFT",PUBLISHED:"PUBLISHED"};var yr={clientId:"merch-at-scale",delimiter:"\xB6",ignoredProperties:["analytics","literals"],serializableTypes:["Array","Object"],sampleRate:30,tags:"consumer=milo/commerce"},Cn=new Set,ka=e=>e instanceof Error||typeof e.originatingRequest=="string";function wn(e){if(e==null)return;let t=typeof e;if(t==="function"){let{name:n}=e;return n?`${t} ${n}`:t}if(t==="object"){if(e instanceof Error)return e.message;if(typeof e.originatingRequest=="string"){let{message:r,originatingRequest:i,status:a}=e;return[r,a,i].filter(o=>o).join(" ")}let n=e[Symbol.toStringTag]??Object.getPrototypeOf(e).constructor.name;if(!yr.serializableTypes.includes(n))return n}return e}function Ra(e,t){if(!yr.ignoredProperties.includes(e))return wn(t)}var Pr={append(e){let{delimiter:t,sampleRate:n,tags:r,clientId:i}=yr,{message:a,params:o}=e,s=[],c=a,u=[];o.forEach(f=>{f!=null&&(ka(f)?s:u).push(f)}),s.length&&(c+=" ",c+=s.map(wn).join(" "));let{pathname:l,search:p}=window.location;c+=`${t}page=`,c+=l+p,u.length&&(c+=`${t}facts=`,c+=JSON.stringify(u,Ra)),Cn.has(c)||(Cn.add(c),window.lana?.log(c,{sampleRate:n,tags:r,clientId:i}))}};var S=Object.freeze({checkoutClientId:"adobe_com",checkoutWorkflow:z.V3,checkoutWorkflowStep:j.EMAIL,country:"US",displayOldPrice:!0,displayPerUnit:!1,displayRecurrence:!0,displayTax:!1,env:pe.PRODUCTION,forceTaxExclusive:!1,language:"en",entitlement:!1,extraOptions:{},modal:!1,promotionCode:"",quantity:1,wcsApiKey:"wcms-commerce-ims-ro-user-milo",wcsBufferDelay:1,wcsURL:"https://www.adobe.com/web_commerce_artifact",landscape:he.PUBLISHED,wcsBufferLimit:1});function kn(e,{once:t=!1}={}){let n=null;function r(){let i=document.querySelector(ee);i!==n&&(n=i,i&&e(i))}return document.addEventListener(Le,r,{once:t}),ie(r),()=>document.removeEventListener(Le,r)}function Me(e,{country:t,forceTaxExclusive:n,perpetual:r}){let i;if(e.length<2)i=e;else{let a=t==="GB"||r?"EN":"MULT",[o,s]=e;i=[o.language===a?o:s]}return n&&(i=i.map(jt)),i}var ie=e=>window.setTimeout(e);function ve(e,t=1){if(e==null)return[t];let n=(Array.isArray(e)?e:String(e).split(",")).map(ge).filter(xe);return n.length||(n=[t]),n}function pt(e){return e==null?[]:(Array.isArray(e)?e:String(e).split(",")).filter(Gt)}function D(){return window.customElements.get(ee)?.instance}var Da="en_US",h={ar:"AR_es",be_en:"BE_en",be_fr:"BE_fr",be_nl:"BE_nl",br:"BR_pt",ca:"CA_en",ch_de:"CH_de",ch_fr:"CH_fr",ch_it:"CH_it",cl:"CL_es",co:"CO_es",la:"DO_es",mx:"MX_es",pe:"PE_es",africa:"MU_en",dk:"DK_da",de:"DE_de",ee:"EE_et",eg_ar:"EG_ar",eg_en:"EG_en",es:"ES_es",fr:"FR_fr",gr_el:"GR_el",gr_en:"GR_en",ie:"IE_en",il_he:"IL_iw",it:"IT_it",lv:"LV_lv",lt:"LT_lt",lu_de:"LU_de",lu_en:"LU_en",lu_fr:"LU_fr",my_en:"MY_en",my_ms:"MY_ms",hu:"HU_hu",mt:"MT_en",mena_en:"DZ_en",mena_ar:"DZ_ar",nl:"NL_nl",no:"NO_nb",pl:"PL_pl",pt:"PT_pt",ro:"RO_ro",si:"SI_sl",sk:"SK_sk",fi:"FI_fi",se:"SE_sv",tr:"TR_tr",uk:"GB_en",at:"AT_de",cz:"CZ_cs",bg:"BG_bg",ru:"RU_ru",ua:"UA_uk",au:"AU_en",in_en:"IN_en",in_hi:"IN_hi",id_en:"ID_en",id_id:"ID_in",nz:"NZ_en",sa_ar:"SA_ar",sa_en:"SA_en",sg:"SG_en",cn:"CN_zh-Hans",tw:"TW_zh-Hant",hk_zh:"HK_zh-hant",jp:"JP_ja",kr:"KR_ko",za:"ZA_en",ng:"NG_en",cr:"CR_es",ec:"EC_es",pr:"US_es",gt:"GT_es",cis_en:"AZ_en",cis_ru:"AZ_ru",sea:"SG_en",th_en:"TH_en",th_th:"TH_th"},ht=Object.freeze({LOCAL:"local",PROD:"prod",STAGE:"stage"});function Rn({locale:e={}}={}){if(!e.prefix)return{country:S.country,language:S.language,locale:Da};let t=e.prefix.replace("/","")??"",[n=S.country,r=S.language]=(h[t]??t).split("_",2);return n=n.toUpperCase(),r=r.toLowerCase(),{country:n,language:r,locale:`${r}_${n}`}}function _r(e={}){let{commerce:t={},locale:n=void 0}=e,r=pe.PRODUCTION,i=gr,a=["local","stage"].includes(e.env?.name),o=V(xr,t,{metadata:!1})?.toLowerCase()==="stage";a&&o&&(r=pe.STAGE,i=Lr);let s=V("checkoutClientId",t)??S.checkoutClientId,c=ne(V("checkoutWorkflow",t),z,S.checkoutWorkflow),u=j.CHECKOUT;c===z.V3&&(u=ne(V("checkoutWorkflowStep",t),j,S.checkoutWorkflowStep));let l=g(V("displayOldPrice",t),S.displayOldPrice),p=g(V("displayPerUnit",t),S.displayPerUnit),f=g(V("displayRecurrence",t),S.displayRecurrence),m=g(V("displayTax",t),S.displayTax),T=g(V("entitlement",t),S.entitlement),x=g(V("modal",t),S.modal),b=g(V("forceTaxExclusive",t),S.forceTaxExclusive),P=V("promotionCode",t)??S.promotionCode,_=ve(V("quantity",t)),L=V("wcsApiKey",t)??S.wcsApiKey,O=e.env?.name===ht.PROD?he.PUBLISHED:ne(V(br,t),he,S.landscape),v=ge(V("wcsBufferDelay",t),S.wcsBufferDelay),I=ge(V("wcsBufferLimit",t),S.wcsBufferLimit);return{...Rn({locale:n}),displayOldPrice:l,checkoutClientId:s,checkoutWorkflow:c,checkoutWorkflowStep:u,displayPerUnit:p,displayRecurrence:f,displayTax:m,entitlement:T,extraOptions:S.extraOptions,modal:x,env:r,forceTaxExclusive:b,promotionCode:P,quantity:_,wcsApiKey:L,wcsBufferDelay:v,wcsBufferLimit:I,wcsURL:i,landscape:O}}var Un="debug",Ua="error",Ma="info",Ga="warn",Ha=Date.now(),Ir=new Set,Nr=new Set,Dn=new Map,Ge=Object.freeze({DEBUG:Un,ERROR:Ua,INFO:Ma,WARN:Ga}),Mn={append({level:e,message:t,params:n,timestamp:r,source:i}){console[e](`${r}ms [${i}] %c${t}`,"font-weight: bold;",...n)}},Gn={filter:({level:e})=>e!==Un},Fa={filter:()=>!1};function Xa(e,t,n,r,i){return{level:e,message:t,namespace:n,get params(){if(r.length===1){let[a]=r;re(a)&&(r=a(),Array.isArray(r)||(r=[r]))}return r},source:i,timestamp:Date.now()-Ha}}function Ya(e){[...Nr].every(t=>t(e))&&Ir.forEach(t=>t(e))}function Hn(e){let t=(Dn.get(e)??0)+1;Dn.set(e,t);let n=`${e} #${t}`,r=a=>(o,...s)=>Ya(Xa(a,o,e,s,n)),i=Object.seal({id:n,namespace:e,module(a){return Hn(`${i.namespace}/${a}`)},debug:r(Ge.DEBUG),error:r(Ge.ERROR),info:r(Ge.INFO),warn:r(Ge.WARN)});return i}function mt(...e){e.forEach(t=>{let{append:n,filter:r}=t;re(r)?Nr.add(r):re(n)&&Ir.add(n)})}function Ka(e={}){let{name:t}=e,n=g(V("commerce.debug",{search:!0,storage:!0}),t===ht.LOCAL);return mt(n?Mn:Gn),t===ht.PROD&&mt(Pr),k}function za(){Ir.clear(),Nr.clear()}var k={...Hn(Er),Level:Ge,Plugins:{consoleAppender:Mn,debugFilter:Gn,quietFilter:Fa,lanaAppender:Pr},init:Ka,reset:za,use:mt};var ja={CLASS_NAME_FAILED:at,CLASS_NAME_PENDING:ot,CLASS_NAME_RESOLVED:st,EVENT_TYPE_FAILED:ct,EVENT_TYPE_PENDING:ut,EVENT_TYPE_RESOLVED:ft,STATE_FAILED:W,STATE_PENDING:B,STATE_RESOLVED:$},Wa={[W]:at,[B]:ot,[$]:st},Ba={[W]:ct,[B]:ut,[$]:ft},dt=new WeakMap;function U(e){if(!dt.has(e)){let t=k.module(e.constructor.is);dt.set(e,{changes:new Map,connected:!1,dispose:Ee,error:void 0,log:t,options:void 0,promises:[],state:B,timer:null,value:void 0,version:0})}return dt.get(e)}function Tt(e){let t=U(e),{error:n,promises:r,state:i}=t;(i===$||i===W)&&(t.promises=[],i===$?r.forEach(({resolve:a})=>a(e)):i===W&&r.forEach(({reject:a})=>a(n))),e.dispatchEvent(new CustomEvent(Ba[i],{bubbles:!0}))}function At(e){let t=dt.get(e);[W,B,$].forEach(n=>{e.classList.toggle(Wa[n],n===t.state)})}var $a={get error(){return U(this).error},get log(){return U(this).log},get options(){return U(this).options},get state(){return U(this).state},get value(){return U(this).value},attributeChangedCallback(e,t,n){U(this).changes.set(e,n),this.requestUpdate()},connectedCallback(){U(this).dispose=kn(()=>this.requestUpdate(!0))},disconnectedCallback(){let e=U(this);e.connected&&(e.connected=!1,e.log.debug("Disconnected:",{element:this})),e.dispose(),e.dispose=Ee},onceSettled(){let{error:e,promises:t,state:n}=U(this);return $===n?Promise.resolve(this):W===n?Promise.reject(e):new Promise((r,i)=>{t.push({resolve:r,reject:i})})},toggleResolved(e,t,n){let r=U(this);return e!==r.version?!1:(n!==void 0&&(r.options=n),r.state=$,r.value=t,At(this),this.log.debug("Resolved:",{element:this,value:t}),ie(()=>Tt(this)),!0)},toggleFailed(e,t,n){let r=U(this);return e!==r.version?!1:(n!==void 0&&(r.options=n),r.error=t,r.state=W,At(this),r.log.error("Failed:",{element:this,error:t}),ie(()=>Tt(this)),!0)},togglePending(e){let t=U(this);return t.version++,e&&(t.options=e),t.state=B,At(this),ie(()=>Tt(this)),t.version},requestUpdate(e=!1){if(!this.isConnected||!D())return;let t=U(this);if(t.timer)return;let{error:n,options:r,state:i,value:a,version:o}=t;t.state=B,t.timer=ie(async()=>{t.timer=null;let s=null;if(t.changes.size&&(s=Object.fromEntries(t.changes.entries()),t.changes.clear()),t.connected?t.log.debug("Updated:",{element:this,changes:s}):(t.connected=!0,t.log.debug("Connected:",{element:this,changes:s})),s||e)try{await this.render?.()===!1&&t.state===B&&t.version===o&&(t.state=i,t.error=n,t.value=a,At(this),Tt(this))}catch(c){this.toggleFailed(t.version,c,r)}})}};function Fn(e={}){return Object.entries(e).forEach(([t,n])=>{(n==null||n===""||n?.length===0)&&delete e[t]}),e}function St(e,t={}){let{tag:n,is:r}=e,i=document.createElement(n,{is:r});return i.setAttribute("is",r),Object.assign(i.dataset,Fn(t)),i}function Et(e){let{tag:t,is:n,prototype:r}=e,i=window.customElements.get(n);return i||(Object.defineProperties(r,Object.getOwnPropertyDescriptors($a)),i=Object.defineProperties(e,Object.getOwnPropertyDescriptors(ja)),window.customElements.define(n,i,{extends:t})),i}function xt(e,t=document.body){return Array.from(t?.querySelectorAll(`${e.tag}[is="${e.is}"]`)??[])}function bt(e,t={}){return e instanceof HTMLElement?(Object.assign(e.dataset,Fn(t)),e):null}var qa="download",Za="upgrade",me,ye=class ye extends HTMLAnchorElement{constructor(){super();kr(this,me,void 0);this.addEventListener("click",this.clickHandler)}static get observedAttributes(){return["data-checkout-workflow","data-checkout-workflow-step","data-extra-options","data-ims-country","data-perpetual","data-promotion-code","data-quantity","data-template","data-wcs-osi","data-entitlement","data-upgrade","data-modal"]}static createCheckoutLink(n={},r=""){let i=D();if(!i)return null;let{checkoutMarketSegment:a,checkoutWorkflow:o,checkoutWorkflowStep:s,entitlement:c,upgrade:u,modal:l,perpetual:p,promotionCode:f,quantity:m,wcsOsi:T,extraOptions:x}=i.collectCheckoutOptions(n),b=St(ye,{checkoutMarketSegment:a,checkoutWorkflow:o,checkoutWorkflowStep:s,entitlement:c,upgrade:u,modal:l,perpetual:p,promotionCode:f,quantity:m,wcsOsi:T,extraOptions:x});return r&&(b.innerHTML=`${r}`),b}static getCheckoutLinks(n){return xt(ye,n)}get isCheckoutLink(){return!0}get placeholder(){return this}clickHandler(n){var r;(r=_t(this,me))==null||r.call(this,n)}async render(n={}){if(!this.isConnected)return!1;let r=D();if(!r)return!1;this.dataset.imsCountry||r.imsCountryPromise.then(l=>{l&&(this.dataset.imsCountry=l)},Ee);let i=r.collectCheckoutOptions(n,this.placeholder);if(!i.wcsOsi.length)return!1;let a;try{a=JSON.parse(i.extraOptions??"{}")}catch(l){this.placeholder.log.error("cannot parse exta checkout options",l)}let o=this.placeholder.togglePending(i);this.href="";let s=r.resolveOfferSelectors(i),c=await Promise.all(s);c=c.map(l=>Me(l,i));let u=await r.buildCheckoutAction(c.flat(),{...a,...i});return this.renderOffers(c.flat(),i,{},u,o)}renderOffers(n,r,i={},a=void 0,o=void 0){if(!this.isConnected)return!1;let s=D();if(!s)return!1;if(r={...JSON.parse(this.placeholder.dataset.extraOptions??"null"),...r,...i},o??(o=this.placeholder.togglePending(r)),_t(this,me)&&It(this,me,void 0),a){this.classList.remove(qa,Za),this.placeholder.toggleResolved(o,n,r);let{url:u,text:l,className:p,handler:f}=a;return u&&(this.href=u),l&&(this.firstElementChild.innerHTML=l),p&&this.classList.add(...p.split(" ")),f&&(this.setAttribute("href","#"),It(this,me,f.bind(this))),!0}else if(n.length){if(this.placeholder.toggleResolved(o,n,r)){let u=s.buildCheckoutURL(n,r);return this.setAttribute("href",u),!0}}else{let u=new Error(`Not provided: ${r?.wcsOsi??"-"}`);if(this.placeholder.toggleFailed(o,u,r))return this.setAttribute("href","#"),!0}return!1}updateOptions(n={}){let r=D();if(!r)return!1;let{checkoutMarketSegment:i,checkoutWorkflow:a,checkoutWorkflowStep:o,entitlement:s,upgrade:c,modal:u,perpetual:l,promotionCode:p,quantity:f,wcsOsi:m}=r.collectCheckoutOptions(n);return bt(this,{checkoutMarketSegment:i,checkoutWorkflow:a,checkoutWorkflowStep:o,entitlement:s,upgrade:c,modal:u,perpetual:l,promotionCode:p,quantity:f,wcsOsi:m}),!0}};me=new WeakMap,Q(ye,"is","checkout-link"),Q(ye,"tag","a");var Vr=ye,gt=Et(Vr);var Xn=[h.uk,h.au,h.fr,h.at,h.be_en,h.be_fr,h.be_nl,h.bg,h.ch_de,h.ch_fr,h.ch_it,h.cz,h.de,h.dk,h.ee,h.eg_ar,h.eg_en,h.es,h.fi,h.fr,h.gr_el,h.gr_en,h.hu,h.ie,h.it,h.lu_de,h.lu_en,h.lu_fr,h.nl,h.no,h.pl,h.pt,h.ro,h.se,h.si,h.sk,h.tr,h.ua,h.id_en,h.id_id,h.in_en,h.in_hi,h.jp,h.my_en,h.my_ms,h.nz,h.th_en,h.th_th],Ja={INDIVIDUAL_COM:[h.za,h.lt,h.lv,h.ng,h.sa_ar,h.sa_en,h.za,h.sg,h.kr],TEAM_COM:[h.za,h.lt,h.lv,h.ng,h.za,h.co,h.kr],INDIVIDUAL_EDU:[h.lt,h.lv,h.sa_en,h.sea],TEAM_EDU:[h.sea,h.kr]},Pe=class Pe extends HTMLSpanElement{static get observedAttributes(){return["data-display-old-price","data-display-per-unit","data-display-recurrence","data-display-tax","data-perpetual","data-promotion-code","data-tax-exclusive","data-template","data-wcs-osi"]}static createInlinePrice(t){let n=D();if(!n)return null;let{displayOldPrice:r,displayPerUnit:i,displayRecurrence:a,displayTax:o,forceTaxExclusive:s,perpetual:c,promotionCode:u,quantity:l,template:p,wcsOsi:f}=n.collectPriceOptions(t);return St(Pe,{displayOldPrice:r,displayPerUnit:i,displayRecurrence:a,displayTax:o,forceTaxExclusive:s,perpetual:c,promotionCode:u,quantity:l,template:p,wcsOsi:f})}static getInlinePrices(t){return xt(Pe,t)}get isInlinePrice(){return!0}get placeholder(){return this}resolveDisplayTaxForGeoAndSegment(t,n,r,i){let a=`${t}_${n}`;if(Xn.includes(t)||Xn.includes(a))return!0;let o=Ja[`${r}_${i}`];return o?!!(o.includes(t)||o.includes(a)):!1}async resolveDisplayTax(t,n){let[r]=await t.resolveOfferSelectors(n),i=Me(await r,n);if(i?.length){let{country:a,language:o}=n,s=i[0],[c=""]=s.marketSegments;return this.resolveDisplayTaxForGeoAndSegment(a,o,s.customerSegment,c)}}async render(t={}){if(!this.isConnected)return!1;let n=D();if(!n)return!1;let r=n.collectPriceOptions(t,this.placeholder);if(!r.wcsOsi.length)return!1;let i=this.placeholder.togglePending(r);this.innerHTML="";let[a]=n.resolveOfferSelectors(r);return this.renderOffers(Me(await a,r),r,i)}renderOffers(t,n={},r=void 0){if(!this.isConnected)return;let i=D();if(!i)return!1;let a=i.collectPriceOptions({...this.dataset,...n});if(r??(r=this.placeholder.togglePending(a)),t.length){if(this.placeholder.toggleResolved(r,t,a))return this.innerHTML=i.buildPriceHTML(t,a),!0}else{let o=new Error(`Not provided: ${a?.wcsOsi??"-"}`);if(this.placeholder.toggleFailed(r,o,a))return this.innerHTML="",!0}return!1}updateOptions(t){let n=D();if(!n)return!1;let{displayOldPrice:r,displayPerUnit:i,displayRecurrence:a,displayTax:o,forceTaxExclusive:s,perpetual:c,promotionCode:u,quantity:l,template:p,wcsOsi:f}=n.collectPriceOptions(t);return bt(this,{displayOldPrice:r,displayPerUnit:i,displayRecurrence:a,displayTax:o,forceTaxExclusive:s,perpetual:c,promotionCode:u,quantity:l,template:p,wcsOsi:f}),!0}};Q(Pe,"is","inline-price"),Q(Pe,"tag","span");var Or=Pe,Lt=Et(Or);function Yn({providers:e,settings:t},n){let r=k.module("checkout");function i(u,l){let{checkoutClientId:p,checkoutWorkflow:f,checkoutWorkflowStep:m,country:T,language:x,promotionCode:b,quantity:P}=t,{checkoutMarketSegment:_,checkoutWorkflow:L=f,checkoutWorkflowStep:O=m,imsCountry:v,country:I=v??T,language:R=x,quantity:F=P,entitlement:q,upgrade:te,modal:X,perpetual:ae,promotionCode:oe=b,wcsOsi:Z,extraOptions:Y,...Te}=Object.assign({},l?.dataset??{},u??{}),He=ne(L,z,S.checkoutWorkflow),Fe=j.CHECKOUT;He===z.V3&&(Fe=ne(O,j,S.checkoutWorkflowStep));let _e=be({...Te,extraOptions:Y,checkoutClientId:p,checkoutMarketSegment:_,country:I,quantity:ve(F,S.quantity),checkoutWorkflow:He,checkoutWorkflowStep:Fe,language:R,entitlement:g(q),upgrade:g(te),modal:g(X),perpetual:g(ae),promotionCode:Ve(oe).effectivePromoCode,wcsOsi:pt(Z)});if(l)for(let vt of e.checkout)vt(l,_e);return _e}async function a(u,l){let p=D(),f=await n.getCheckoutAction?.(u,l,p.imsSignedInPromise);return f||null}function o(u,l){if(!Array.isArray(u)||!u.length||!l)return"";let{env:p,landscape:f}=t,{checkoutClientId:m,checkoutMarketSegment:T,checkoutWorkflow:x,checkoutWorkflowStep:b,country:P,promotionCode:_,quantity:L,...O}=i(l),v=window.frameElement?"if":"fp",I={checkoutPromoCode:_,clientId:m,context:v,country:P,env:p,items:[],marketSegment:T,workflowStep:b,landscape:f,...O};if(u.length===1){let[{offerId:R,offerType:F,productArrangementCode:q}]=u,{marketSegments:[te]}=u[0];Object.assign(I,{marketSegment:te,offerType:F,productArrangementCode:q}),I.items.push(L[0]===1?{id:R}:{id:R,quantity:L[0]})}else I.items.push(...u.map(({offerId:R},F)=>({id:R,quantity:L[F]??S.quantity})));return wt(x,I)}let{createCheckoutLink:s,getCheckoutLinks:c}=gt;return{CheckoutLink:gt,CheckoutWorkflow:z,CheckoutWorkflowStep:j,buildCheckoutAction:a,buildCheckoutURL:o,collectCheckoutOptions:i,createCheckoutLink:s,getCheckoutLinks:c}}function Qa({interval:e=200,maxAttempts:t=25}={}){let n=k.module("ims");return new Promise(r=>{n.debug("Waing for IMS to be ready");let i=0;function a(){window.adobeIMS?.initialized?r():++i>t?(n.debug("Timeout"),r()):setTimeout(a,e)}a()})}function eo(e){return e.then(()=>window.adobeIMS?.isSignedInUser()??!1)}function to(e){let t=k.module("ims");return e.then(n=>n?window.adobeIMS.getProfile().then(({countryCode:r})=>(t.debug("Got user country:",r),r),r=>{t.error("Unable to get user country:",r)}):null)}function Kn({}){let e=Qa(),t=eo(e),n=to(t);return{imsReadyPromise:e,imsSignedInPromise:t,imsCountryPromise:n}}async function jn(e,t){let{data:n}=t||await Promise.resolve().then(()=>si(zn(),1));if(Array.isArray(n)){let r=a=>n.find(o=>ze(o.lang,a)),i=r(e.language)??r(S.language);if(i)return Object.freeze(i)}return{}}function Wn({literals:e,providers:t,settings:n}){function r(s,c){let{country:u,displayOldPrice:l,displayPerUnit:p,displayRecurrence:f,displayTax:m,forceTaxExclusive:T,language:x,promotionCode:b,quantity:P}=n,{displayOldPrice:_=l,displayPerUnit:L=p,displayRecurrence:O=f,displayTax:v=m,forceTaxExclusive:I=T,country:R=u,language:F=x,perpetual:q,promotionCode:te=b,quantity:X=P,template:ae,wcsOsi:oe,...Z}=Object.assign({},c?.dataset??{},s??{}),Y=be({...Z,country:R,displayOldPrice:g(_),displayPerUnit:g(L),displayRecurrence:g(O),displayTax:g(v),forceTaxExclusive:g(I),language:F,perpetual:g(q),promotionCode:Ve(te).effectivePromoCode,quantity:ve(X,S.quantity),template:ae,wcsOsi:pt(oe)});if(c)for(let Te of t.price)Te(c,Y);return Y}function i(s,c){if(!Array.isArray(s)||!s.length||!c)return"";let{template:u}=c,l;switch(u){case"discount":l=mr;break;case"strikethrough":l=pr;break;case"optical":l=fr;break;case"annual":l=hr;break;default:l=c.promotionCode?ur:cr}let p=r(c);p.literals=Object.assign({},e.price,be(c.literals??{}));let[f]=s;return f={...f,...f.priceDetails},l(p,f)}let{createInlinePrice:a,getInlinePrices:o}=Lt;return{InlinePrice:Lt,buildPriceHTML:i,collectPriceOptions:r,createInlinePrice:a,getInlinePrices:o}}function Bn({settings:e}){let t=k.module("wcs"),{env:n,wcsApiKey:r}=e,i=new Map,a=new Map,o;async function s(l,p,f=!0){let m=Sr;t.debug("Fetching:",l);try{l.offerSelectorIds=l.offerSelectorIds.sort();let T=new URL(e.wcsURL);T.searchParams.set("offer_selector_ids",l.offerSelectorIds.join(",")),T.searchParams.set("country",l.country),T.searchParams.set("locale",l.locale),T.searchParams.set("landscape",n===pe.STAGE?"ALL":e.landscape),T.searchParams.set("api_key",r),l.language&&T.searchParams.set("language",l.language),l.promotionCode&&T.searchParams.set("promotion_code",l.promotionCode),l.currency&&T.searchParams.set("currency",l.currency);let x=await fetch(T.toString(),{credentials:"omit"});if(x.ok){let b=await x.json();t.debug("Fetched:",l,b);let P=b.resolvedOffers??[];P=P.map(Be),p.forEach(({resolve:_},L)=>{let O=P.filter(({offerSelectorIds:v})=>v.includes(L)).flat();O.length&&(p.delete(L),_(O))})}else x.status===404&&l.offerSelectorIds.length>1?(t.debug("Multi-osi 404, fallback to fetch-by-one strategy"),await Promise.allSettled(l.offerSelectorIds.map(b=>s({...l,offerSelectorIds:[b]},p,!1)))):(m=lt,t.error(m,l))}catch(T){m=lt,t.error(m,l,T)}f&&p.size&&(t.debug("Missing:",{offerSelectorIds:[...p.keys()]}),p.forEach(T=>{T.reject(new Error(m))}))}function c(){clearTimeout(o);let l=[...a.values()];a.clear(),l.forEach(({options:p,promises:f})=>s(p,f))}function u({country:l,language:p,perpetual:f=!1,promotionCode:m="",wcsOsi:T=[]}){let x=`${p}_${l}`;l!=="GB"&&(p=f?"EN":"MULT");let b=[l,p,m].filter(P=>P).join("-").toLowerCase();return T.map(P=>{let _=`${P}-${b}`;if(!i.has(_)){let L=new Promise((O,v)=>{let I=a.get(b);if(!I){let R={country:l,locale:x,offerSelectorIds:[]};l!=="GB"&&(R.language=p),I={options:R,promises:new Map},a.set(b,I)}m&&(I.options.promotionCode=m),I.options.offerSelectorIds.push(P),I.promises.set(P,{resolve:O,reject:v}),I.options.offerSelectorIds.length>=e.wcsBufferLimit?c():(t.debug("Queued:",I.options),o||(o=setTimeout(c,e.wcsBufferDelay)))});i.set(_,L)}return i.get(_)})}return{WcsCommitment:Tr,WcsPlanType:Ar,WcsTerm:dr,resolveOfferSelectors:u}}var M=class extends HTMLElement{get isWcmsCommerce(){return!0}};Q(M,"instance"),Q(M,"promise",null);window.customElements.define(ee,M);async function no(e,t){let n=k.init(e.env).module("service");n.debug("Activating:",e);let r={price:{}},i=Object.freeze(_r(e));try{r.price=await jn(i,e.commerce.priceLiterals)}catch(c){n.warn("Price literals were not fetched:",c)}let a={checkout:new Set,price:new Set},o=document.createElement(ee),s={literals:r,providers:a,settings:i};return M.instance=Object.defineProperties(o,Object.getOwnPropertyDescriptors({...Yn(s,t),...Kn(s),...Wn(s),...Bn(s),...vr,Log:k,get defaults(){return S},get literals(){return r},get log(){return k},get providers(){return{checkout(c){return a.checkout.add(c),()=>a.checkout.delete(c)},price(c){return a.price.add(c),()=>a.price.delete(c)}}},get settings(){return i}})),n.debug("Activated:",{literals:r,settings:i,element:o}),document.head.append(o),ie(()=>{let c=new CustomEvent(Le,{bubbles:!0,cancelable:!1,detail:M.instance});M.instance.dispatchEvent(c)}),M.instance}function $n(){document.head.querySelector(ee)?.remove(),M.promise=null,k.reset()}function Cr(e,t){let n=re(e)?e():null,r=re(t)?t():{};return n&&(r.force&&$n(),no(n,r).then(i=>{Cr.resolve(i)})),M.promise??(M.promise=new Promise(i=>{Cr.resolve=i})),M.promise}export{gt as CheckoutLink,z as CheckoutWorkflow,j as CheckoutWorkflowStep,S as Defaults,Lt as InlinePrice,he as Landscape,k as Log,ee as TAG_NAME_SERVICE,Tr as WcsCommitment,Ar as WcsPlanType,dr as WcsTerm,Be as applyPlanType,Rn as getLocaleSettings,_r as getSettings,Cr as init,$n as reset}; diff --git a/libs/deps/mas/mas.js b/libs/deps/mas/mas.js index 83267703e1..4e5c80f3a9 100644 --- a/libs/deps/mas/mas.js +++ b/libs/deps/mas/mas.js @@ -1,4 +1,1725 @@ -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 Ms=(e,t,r)=>t in e?bo(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var Us=(e,t)=>{for(var r in t)bo(e,r,{get:t[r],enumerable:!0})};var O=(e,t,r)=>Ms(e,typeof t!="symbol"?t+"":t,r),wo=(e,t,r)=>t.has(e)||_o("Cannot "+r);var R=(e,t,r)=>(wo(e,t,"read from private field"),r?r.call(e):t.get(e)),Q=(e,t,r)=>t.has(e)?_o("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,r),K=(e,t,r,n)=>(wo(e,t,"write to private field"),n?n.call(e,r):t.set(e,r),r);var lt;(function(e){e.STAGE="STAGE",e.PRODUCTION="PRODUCTION",e.LOCAL="LOCAL"})(lt||(lt={}));var Mr;(function(e){e.STAGE="STAGE",e.PRODUCTION="PROD",e.LOCAL="LOCAL"})(Mr||(Mr={}));var ht;(function(e){e.DRAFT="DRAFT",e.PUBLISHED="PUBLISHED"})(ht||(ht={}));var Ae;(function(e){e.V2="UCv2",e.V3="UCv3"})(Ae||(Ae={}));var Y;(function(e){e.CHECKOUT="checkout",e.CHECKOUT_EMAIL="checkout/email",e.SEGMENTATION="segmentation",e.BUNDLE="bundle",e.COMMITMENT="commitment",e.RECOMMENDATION="recommendation",e.EMAIL="email",e.PAYMENT="payment",e.CHANGE_PLAN_TEAM_PLANS="change-plan/team-upgrade/plans",e.CHANGE_PLAN_TEAM_PAYMENT="change-plan/team-upgrade/payment"})(Y||(Y={}));var Ur=function(e){var t;return(t=Ds.get(e))!==null&&t!==void 0?t:e},Ds=new Map([["countrySpecific","cs"],["quantity","q"],["authCode","code"],["checkoutPromoCode","apc"],["rurl","rUrl"],["curl","cUrl"],["ctxrturl","ctxRtUrl"],["country","co"],["language","lang"],["clientId","cli"],["context","ctx"],["productArrangementCode","pa"],["offerType","ot"],["marketSegment","ms"]]);var Ao=function(e){var t=typeof Symbol=="function"&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&typeof e.length=="number")return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},So=function(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),o,i=[],s;try{for(;(t===void 0||t-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(c){s={error:c}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return i};function je(e,t,r){var n,o;try{for(var i=Ao(Object.entries(e)),s=i.next();!s.done;s=i.next()){var c=So(s.value,2),a=c[0],h=c[1],l=Ur(a);h!=null&&r.has(l)&&t.set(l,h)}}catch(d){n={error:d}}finally{try{s&&!s.done&&(o=i.return)&&o.call(i)}finally{if(n)throw n.error}}}function Ft(e){switch(e){case lt.PRODUCTION:return"https://commerce.adobe.com";default:return"https://commerce-stg.adobe.com"}}function Ht(e,t){var r,n;for(var o in e){var i=e[o];try{for(var s=(r=void 0,Ao(Object.entries(i))),c=s.next();!c.done;c=s.next()){var a=So(c.value,2),h=a[0],l=a[1];if(l!=null){var d=Ur(h);t.set("items["+o+"]["+d+"]",l)}}}catch(u){r={error:u}}finally{try{c&&!c.done&&(n=s.return)&&n.call(s)}finally{if(r)throw r.error}}}}var Fs=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var o=0,n=Object.getOwnPropertySymbols(e);o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};function To(e){Vs(e);var t=e.env,r=e.items,n=e.workflowStep,o=Fs(e,["env","items","workflowStep"]),i=new URL(Ft(t));return i.pathname=n+"/",Ht(r,i.searchParams),je(o,i.searchParams,zs),i.toString()}var zs=new Set(["cli","co","lang","ctx","cUrl","mv","nglwfdata","otac","promoid","rUrl","sdid","spint","trackingid","code","campaignid","appctxid"]),Gs=["env","workflowStep","clientId","country","items"];function Vs(e){var t,r;try{for(var n=Hs(Gs),o=n.next();!o.done;o=n.next()){var i=o.value;if(!e[i])throw new Error('Argument "checkoutData" is not valid, missing: '+i)}}catch(s){t={error:s}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}return!0}var js=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var o=0,n=Object.getOwnPropertySymbols(e);o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},Ws="p_draft_landscape",qs="/store/";function Fr(e){Xs(e);var t=e.env,r=e.items,n=e.workflowStep,o=e.ms,i=e.marketSegment,s=e.ot,c=e.offerType,a=e.pa,h=e.productArrangementCode,l=e.landscape,d=js(e,["env","items","workflowStep","ms","marketSegment","ot","offerType","pa","productArrangementCode","landscape"]),u={marketSegment:i??o,offerType:c??s,productArrangementCode:h??a},m=new URL(Ft(t));return m.pathname=""+qs+n,n!==Y.SEGMENTATION&&n!==Y.CHANGE_PLAN_TEAM_PLANS&&Ht(r,m.searchParams),n===Y.SEGMENTATION&&je(u,m.searchParams,Dr),je(d,m.searchParams,Dr),l===ht.DRAFT&&je({af:Ws},m.searchParams,Dr),m.toString()}var Dr=new Set(["af","ai","apc","appctxid","cli","co","csm","ctx","ctxRtUrl","DCWATC","dp","fr","gsp","ijt","lang","lo","mal","ms","mv","mv2","nglwfdata","ot","otac","pa","pcid","promoid","q","rf","sc","scl","sdid","sid","spint","svar","th","thm","trackingid","usid","workflowid","context.guid","so.ca","so.su","so.tr","so.va"]),Ys=["env","workflowStep","clientId","country"];function Xs(e){var t,r;try{for(var n=Bs(Ys),o=n.next();!o.done;o=n.next()){var i=o.value;if(!e[i])throw new Error('Argument "checkoutData" is not valid, missing: '+i)}}catch(s){t={error:s}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}if(e.workflowStep!==Y.SEGMENTATION&&e.workflowStep!==Y.CHANGE_PLAN_TEAM_PLANS&&!e.items)throw new Error('Argument "checkoutData" is not valid, missing: items');return!0}function Hr(e,t){switch(e){case Ae.V2:return To(t);case Ae.V3:return Fr(t);default:return console.warn("Unsupported CheckoutType, will use UCv3 as default. Given type: "+e),Fr(t)}}var zr;(function(e){e.BASE="BASE",e.TRIAL="TRIAL",e.PROMOTION="PROMOTION"})(zr||(zr={}));var k;(function(e){e.MONTH="MONTH",e.YEAR="YEAR",e.TWO_YEARS="TWO_YEARS",e.THREE_YEARS="THREE_YEARS",e.PERPETUAL="PERPETUAL",e.TERM_LICENSE="TERM_LICENSE",e.ACCESS_PASS="ACCESS_PASS",e.THREE_MONTHS="THREE_MONTHS",e.SIX_MONTHS="SIX_MONTHS"})(k||(k={}));var L;(function(e){e.ANNUAL="ANNUAL",e.MONTHLY="MONTHLY",e.TWO_YEARS="TWO_YEARS",e.THREE_YEARS="THREE_YEARS",e.P1D="P1D",e.P1Y="P1Y",e.P3Y="P3Y",e.P10Y="P10Y",e.P15Y="P15Y",e.P3D="P3D",e.P7D="P7D",e.P30D="P30D",e.HALF_YEARLY="HALF_YEARLY",e.QUARTERLY="QUARTERLY"})(L||(L={}));var Gr;(function(e){e.INDIVIDUAL="INDIVIDUAL",e.TEAM="TEAM",e.ENTERPRISE="ENTERPRISE"})(Gr||(Gr={}));var Vr;(function(e){e.COM="COM",e.EDU="EDU",e.GOV="GOV"})(Vr||(Vr={}));var jr;(function(e){e.DIRECT="DIRECT",e.INDIRECT="INDIRECT"})(jr||(jr={}));var Br;(function(e){e.ENTERPRISE_PRODUCT="ENTERPRISE_PRODUCT",e.ETLA="ETLA",e.RETAIL="RETAIL",e.VIP="VIP",e.VIPMP="VIPMP",e.FREE="FREE"})(Br||(Br={}));var Po="tacocat.js";var zt=(e,t)=>String(e??"").toLowerCase()==String(t??"").toLowerCase(),Co=e=>`${e??""}`.replace(/[&<>'"]/g,t=>({"&":"&","<":"<",">":">","'":"'",'"':"""})[t]??t)??"";function N(e,t={},{metadata:r=!0,search:n=!0,storage:o=!0}={}){let i;if(n&&i==null){let s=new URLSearchParams(window.location.search),c=Be(n)?n:e;i=s.get(c)}if(o&&i==null){let s=Be(o)?o:e;i=window.sessionStorage.getItem(s)??window.localStorage.getItem(s)}if(r&&i==null){let s=Zs(Be(r)?r:e);i=document.documentElement.querySelector(`meta[name="${s}"]`)?.content}return i??t[e]}var We=()=>{};var $o=e=>typeof e=="boolean",me=e=>typeof e=="function",Gt=e=>typeof e=="number",Oo=e=>e!=null&&typeof e=="object";var Be=e=>typeof e=="string",Wr=e=>Be(e)&&e,qe=e=>Gt(e)&&Number.isFinite(e)&&e>0;function Ye(e,t=r=>r==null||r===""){return e!=null&&Object.entries(e).forEach(([r,n])=>{t(n)&&delete e[r]}),e}function w(e,t){if($o(e))return e;let r=String(e);return r==="1"||r==="true"?!0:r==="0"||r==="false"?!1:t}function pe(e,t,r){let n=Object.values(t);return n.find(o=>zt(o,e))??r??n[0]}function Zs(e=""){return String(e).replace(/(\p{Lowercase_Letter})(\p{Uppercase_Letter})/gu,(t,r,n)=>`${r}-${n}`).replace(/\W+/gu,"-").toLowerCase()}function Xe(e,t=1){return Gt(e)||(e=Number.parseInt(e,10)),!Number.isNaN(e)&&e>0&&Number.isFinite(e)?e:t}var Js=Date.now(),qr=()=>`(+${Date.now()-Js}ms)`,Vt=new Set,Qs=w(N("tacocat.debug",{},{metadata:!1}),typeof process<"u"&&process.env?.DEBUG);function Lo(e){let t=`[${Po}/${e}]`,r=(s,c,...a)=>s?!0:(o(c,...a),!1),n=Qs?(s,...c)=>{console.debug(`${t} ${s}`,...c,qr())}:()=>{},o=(s,...c)=>{let a=`${t} ${s}`;Vt.forEach(([h])=>h(a,...c))};return{assert:r,debug:n,error:o,warn:(s,...c)=>{let a=`${t} ${s}`;Vt.forEach(([,h])=>h(a,...c))}}}function Ks(e,t){let r=[e,t];return Vt.add(r),()=>{Vt.delete(r)}}Ks((e,...t)=>{console.error(e,...t,qr())},(e,...t)=>{console.warn(e,...t,qr())});var ea="no promo",No="promo-tag",ta="yellow",ra="neutral",na=(e,t,r)=>{let n=i=>i||ea,o=r?` (was "${n(t)}")`:"";return`${n(e)}${o}`},oa="cancel-context",dt=(e,t)=>{let r=e===oa,n=!r&&e?.length>0,o=(n||r)&&(t&&t!=e||!t&&!r),i=o&&n||!o&&!!t,s=i?e||t:void 0;return{effectivePromoCode:s,overridenPromoCode:e,className:i?No:`${No} no-promo`,text:na(s,t,o),variant:i?ta:ra,isOverriden:o}};var Yr="ABM",Xr="PUF",Zr="M2M",Jr="PERPETUAL",Qr="P3Y",ia="TAX_INCLUSIVE_DETAILS",sa="TAX_EXCLUSIVE",Ro={ABM:Yr,PUF:Xr,M2M:Zr,PERPETUAL:Jr,P3Y:Qr},Ol={[Yr]:{commitment:k.YEAR,term:L.MONTHLY},[Xr]:{commitment:k.YEAR,term:L.ANNUAL},[Zr]:{commitment:k.MONTH,term:L.MONTHLY},[Jr]:{commitment:k.PERPETUAL,term:void 0},[Qr]:{commitment:k.THREE_MONTHS,term:L.P3Y}},Io="Value is not an offer",Kr=e=>{if(typeof e!="object")return Io;let{commitment:t,term:r}=e,n=aa(t,r);return{...e,planType:n}};var aa=(e,t)=>{switch(e){case void 0:return Io;case"":return"";case k.YEAR:return t===L.MONTHLY?Yr:t===L.ANNUAL?Xr:"";case k.MONTH:return t===L.MONTHLY?Zr:"";case k.PERPETUAL:return Jr;case k.TERM_LICENSE:return t===L.P3Y?Qr:"";default:return""}};function en(e){let{priceDetails:t}=e,{price:r,priceWithoutDiscount:n,priceWithoutTax:o,priceWithoutDiscountAndTax:i,taxDisplay:s}=t;if(s!==ia)return e;let c={...e,priceDetails:{...t,price:o??r,priceWithoutDiscount:i??n,taxDisplay:sa}};return c.offerType==="TRIAL"&&c.priceDetails.price===0&&(c.priceDetails.price=c.priceDetails.priceWithoutDiscount),c}var tn=function(e,t){return tn=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,n){r.__proto__=n}||function(r,n){for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(r[o]=n[o])},tn(e,t)};function ut(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");tn(e,t);function r(){this.constructor=e}e.prototype=t===null?Object.create(t):(r.prototype=t.prototype,new r)}var E=function(){return E=Object.assign||function(t){for(var r,n=1,o=arguments.length;n0}),r=[],n=0,o=t;n1)throw new RangeError("integer-width stems only accept a single optional option");o.options[0].replace(ha,function(c,a,h,l,d,u){if(a)t.minimumIntegerDigits=h.length;else{if(l&&d)throw new Error("We currently do not support maximum integer digits");if(u)throw new Error("We currently do not support exact integer digits")}return""});continue}if(jo.test(o.stem)){t.minimumIntegerDigits=o.stem.length;continue}if(Fo.test(o.stem)){if(o.options.length>1)throw new RangeError("Fraction-precision stems only accept a single optional option");o.stem.replace(Fo,function(c,a,h,l,d,u){return h==="*"?t.minimumFractionDigits=a.length:l&&l[0]==="#"?t.maximumFractionDigits=l.length:d&&u?(t.minimumFractionDigits=d.length,t.maximumFractionDigits=d.length+u.length):(t.minimumFractionDigits=a.length,t.maximumFractionDigits=a.length),""}),o.options.length&&(t=E(E({},t),Ho(o.options[0])));continue}if(Vo.test(o.stem)){t=E(E({},t),Ho(o.stem));continue}var i=Bo(o.stem);i&&(t=E(E({},t),i));var s=da(o.stem);s&&(t=E(E({},t),s))}return t}var on,ua=new RegExp("^"+nn.source+"*"),ma=new RegExp(nn.source+"*$");function y(e,t){return{start:e,end:t}}var pa=!!String.prototype.startsWith,fa=!!String.fromCodePoint,ga=!!Object.fromEntries,xa=!!String.prototype.codePointAt,va=!!String.prototype.trimStart,ya=!!String.prototype.trimEnd,Ea=!!Number.isSafeInteger,ba=Ea?Number.isSafeInteger:function(e){return typeof e=="number"&&isFinite(e)&&Math.floor(e)===e&&Math.abs(e)<=9007199254740991},an=!0;try{qo=Jo("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu"),an=((on=qo.exec("a"))===null||on===void 0?void 0:on[0])==="a"}catch{an=!1}var qo,Yo=pa?function(t,r,n){return t.startsWith(r,n)}:function(t,r,n){return t.slice(n,n+r.length)===r},cn=fa?String.fromCodePoint:function(){for(var t=[],r=0;ri;){if(s=t[i++],s>1114111)throw RangeError(s+" is not a valid code point");n+=s<65536?String.fromCharCode(s):String.fromCharCode(((s-=65536)>>10)+55296,s%1024+56320)}return n},Xo=ga?Object.fromEntries:function(t){for(var r={},n=0,o=t;n=n)){var o=t.charCodeAt(r),i;return o<55296||o>56319||r+1===n||(i=t.charCodeAt(r+1))<56320||i>57343?o:(o-55296<<10)+(i-56320)+65536}},_a=va?function(t){return t.trimStart()}:function(t){return t.replace(ua,"")},wa=ya?function(t){return t.trimEnd()}:function(t){return t.replace(ma,"")};function Jo(e,t){return new RegExp(e,t)}var ln;an?(sn=Jo("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu"),ln=function(t,r){var n;sn.lastIndex=r;var o=sn.exec(t);return(n=o[1])!==null&&n!==void 0?n:""}):ln=function(t,r){for(var n=[];;){var o=Zo(t,r);if(o===void 0||Ko(o)||Ta(o))break;n.push(o),r+=o>=65536?2:1}return cn.apply(void 0,n)};var sn,Qo=function(){function e(t,r){r===void 0&&(r={}),this.message=t,this.position={offset:0,line:1,column:1},this.ignoreTag=!!r.ignoreTag,this.requiresOtherClause=!!r.requiresOtherClause,this.shouldParseSkeletons=!!r.shouldParseSkeletons}return e.prototype.parse=function(){if(this.offset()!==0)throw Error("parser can only be used once");return this.parseMessage(0,"",!1)},e.prototype.parseMessage=function(t,r,n){for(var o=[];!this.isEOF();){var i=this.char();if(i===123){var s=this.parseArgument(t,n);if(s.err)return s;o.push(s.val)}else{if(i===125&&t>0)break;if(i===35&&(r==="plural"||r==="selectordinal")){var c=this.clonePosition();this.bump(),o.push({type:T.pound,location:y(c,this.clonePosition())})}else if(i===60&&!this.ignoreTag&&this.peek()===47){if(n)break;return this.error(v.UNMATCHED_CLOSING_TAG,y(this.clonePosition(),this.clonePosition()))}else if(i===60&&!this.ignoreTag&&hn(this.peek()||0)){var s=this.parseTag(t,r);if(s.err)return s;o.push(s.val)}else{var s=this.parseLiteral(t,r);if(s.err)return s;o.push(s.val)}}}return{val:o,err:null}},e.prototype.parseTag=function(t,r){var n=this.clonePosition();this.bump();var o=this.parseTagName();if(this.bumpSpace(),this.bumpIf("/>"))return{val:{type:T.literal,value:"<"+o+"/>",location:y(n,this.clonePosition())},err:null};if(this.bumpIf(">")){var i=this.parseMessage(t+1,r,!0);if(i.err)return i;var s=i.val,c=this.clonePosition();if(this.bumpIf("")?{val:{type:T.tag,value:o,children:s,location:y(n,this.clonePosition())},err:null}:this.error(v.INVALID_TAG,y(c,this.clonePosition())))}else return this.error(v.UNCLOSED_TAG,y(n,this.clonePosition()))}else return this.error(v.INVALID_TAG,y(n,this.clonePosition()))},e.prototype.parseTagName=function(){var t=this.offset();for(this.bump();!this.isEOF()&&Sa(this.char());)this.bump();return this.message.slice(t,this.offset())},e.prototype.parseLiteral=function(t,r){for(var n=this.clonePosition(),o="";;){var i=this.tryParseQuote(r);if(i){o+=i;continue}var s=this.tryParseUnquoted(t,r);if(s){o+=s;continue}var c=this.tryParseLeftAngleBracket();if(c){o+=c;continue}break}var a=y(n,this.clonePosition());return{val:{type:T.literal,value:o,location:a},err:null}},e.prototype.tryParseLeftAngleBracket=function(){return!this.isEOF()&&this.char()===60&&(this.ignoreTag||!Aa(this.peek()||0))?(this.bump(),"<"):null},e.prototype.tryParseQuote=function(t){if(this.isEOF()||this.char()!==39)return null;switch(this.peek()){case 39:return this.bump(),this.bump(),"'";case 123:case 60:case 62:case 125:break;case 35:if(t==="plural"||t==="selectordinal")break;return null;default:return null}this.bump();var r=[this.char()];for(this.bump();!this.isEOF();){var n=this.char();if(n===39)if(this.peek()===39)r.push(39),this.bump();else{this.bump();break}else r.push(n);this.bump()}return cn.apply(void 0,r)},e.prototype.tryParseUnquoted=function(t,r){if(this.isEOF())return null;var n=this.char();return n===60||n===123||n===35&&(r==="plural"||r==="selectordinal")||n===125&&t>0?null:(this.bump(),cn(n))},e.prototype.parseArgument=function(t,r){var n=this.clonePosition();if(this.bump(),this.bumpSpace(),this.isEOF())return this.error(v.EXPECT_ARGUMENT_CLOSING_BRACE,y(n,this.clonePosition()));if(this.char()===125)return this.bump(),this.error(v.EMPTY_ARGUMENT,y(n,this.clonePosition()));var o=this.parseIdentifierIfPossible().value;if(!o)return this.error(v.MALFORMED_ARGUMENT,y(n,this.clonePosition()));if(this.bumpSpace(),this.isEOF())return this.error(v.EXPECT_ARGUMENT_CLOSING_BRACE,y(n,this.clonePosition()));switch(this.char()){case 125:return this.bump(),{val:{type:T.argument,value:o,location:y(n,this.clonePosition())},err:null};case 44:return this.bump(),this.bumpSpace(),this.isEOF()?this.error(v.EXPECT_ARGUMENT_CLOSING_BRACE,y(n,this.clonePosition())):this.parseArgumentOptions(t,r,o,n);default:return this.error(v.MALFORMED_ARGUMENT,y(n,this.clonePosition()))}},e.prototype.parseIdentifierIfPossible=function(){var t=this.clonePosition(),r=this.offset(),n=ln(this.message,r),o=r+n.length;this.bumpTo(o);var i=this.clonePosition(),s=y(t,i);return{value:n,location:s}},e.prototype.parseArgumentOptions=function(t,r,n,o){var i,s=this.clonePosition(),c=this.parseIdentifierIfPossible().value,a=this.clonePosition();switch(c){case"":return this.error(v.EXPECT_ARGUMENT_TYPE,y(s,a));case"number":case"date":case"time":{this.bumpSpace();var h=null;if(this.bumpIf(",")){this.bumpSpace();var l=this.clonePosition(),d=this.parseSimpleArgStyleIfPossible();if(d.err)return d;var u=wa(d.val);if(u.length===0)return this.error(v.EXPECT_ARGUMENT_STYLE,y(this.clonePosition(),this.clonePosition()));var m=y(l,this.clonePosition());h={style:u,styleLocation:m}}var f=this.tryParseArgumentClose(o);if(f.err)return f;var g=y(o,this.clonePosition());if(h&&Yo(h?.style,"::",0)){var _=_a(h.style.slice(2));if(c==="number"){var d=this.parseNumberSkeletonFromString(_,h.styleLocation);return d.err?d:{val:{type:T.number,value:n,location:g,style:d.val},err:null}}else{if(_.length===0)return this.error(v.EXPECT_DATE_TIME_SKELETON,g);var u={type:Se.dateTime,pattern:_,location:h.styleLocation,parsedOptions:this.shouldParseSkeletons?Uo(_):{}},P=c==="date"?T.date:T.time;return{val:{type:P,value:n,location:g,style:u},err:null}}}return{val:{type:c==="number"?T.number:c==="date"?T.date:T.time,value:n,location:g,style:(i=h?.style)!==null&&i!==void 0?i:null},err:null}}case"plural":case"selectordinal":case"select":{var C=this.clonePosition();if(this.bumpSpace(),!this.bumpIf(","))return this.error(v.EXPECT_SELECT_ARGUMENT_OPTIONS,y(C,E({},C)));this.bumpSpace();var A=this.parseIdentifierIfPossible(),I=0;if(c!=="select"&&A.value==="offset"){if(!this.bumpIf(":"))return this.error(v.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE,y(this.clonePosition(),this.clonePosition()));this.bumpSpace();var d=this.tryParseDecimalInteger(v.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE,v.INVALID_PLURAL_ARGUMENT_OFFSET_VALUE);if(d.err)return d;this.bumpSpace(),A=this.parseIdentifierIfPossible(),I=d.val}var S=this.tryParsePluralOrSelectOptions(t,c,r,A);if(S.err)return S;var f=this.tryParseArgumentClose(o);if(f.err)return f;var $=y(o,this.clonePosition());return c==="select"?{val:{type:T.select,value:n,options:Xo(S.val),location:$},err:null}:{val:{type:T.plural,value:n,options:Xo(S.val),offset:I,pluralType:c==="plural"?"cardinal":"ordinal",location:$},err:null}}default:return this.error(v.INVALID_ARGUMENT_TYPE,y(s,a))}},e.prototype.tryParseArgumentClose=function(t){return this.isEOF()||this.char()!==125?this.error(v.EXPECT_ARGUMENT_CLOSING_BRACE,y(t,this.clonePosition())):(this.bump(),{val:!0,err:null})},e.prototype.parseSimpleArgStyleIfPossible=function(){for(var t=0,r=this.clonePosition();!this.isEOF();){var n=this.char();switch(n){case 39:{this.bump();var o=this.clonePosition();if(!this.bumpUntil("'"))return this.error(v.UNCLOSED_QUOTE_IN_ARGUMENT_STYLE,y(o,this.clonePosition()));this.bump();break}case 123:{t+=1,this.bump();break}case 125:{if(t>0)t-=1;else return{val:this.message.slice(r.offset,this.offset()),err:null};break}default:this.bump();break}}return{val:this.message.slice(r.offset,this.offset()),err:null}},e.prototype.parseNumberSkeletonFromString=function(t,r){var n=[];try{n=Go(t)}catch{return this.error(v.INVALID_NUMBER_SKELETON,r)}return{val:{type:Se.number,tokens:n,location:r,parsedOptions:this.shouldParseSkeletons?Wo(n):{}},err:null}},e.prototype.tryParsePluralOrSelectOptions=function(t,r,n,o){for(var i,s=!1,c=[],a=new Set,h=o.value,l=o.location;;){if(h.length===0){var d=this.clonePosition();if(r!=="select"&&this.bumpIf("=")){var u=this.tryParseDecimalInteger(v.EXPECT_PLURAL_ARGUMENT_SELECTOR,v.INVALID_PLURAL_ARGUMENT_SELECTOR);if(u.err)return u;l=y(d,this.clonePosition()),h=this.message.slice(d.offset,this.offset())}else break}if(a.has(h))return this.error(r==="select"?v.DUPLICATE_SELECT_ARGUMENT_SELECTOR:v.DUPLICATE_PLURAL_ARGUMENT_SELECTOR,l);h==="other"&&(s=!0),this.bumpSpace();var m=this.clonePosition();if(!this.bumpIf("{"))return this.error(r==="select"?v.EXPECT_SELECT_ARGUMENT_SELECTOR_FRAGMENT:v.EXPECT_PLURAL_ARGUMENT_SELECTOR_FRAGMENT,y(this.clonePosition(),this.clonePosition()));var f=this.parseMessage(t+1,r,n);if(f.err)return f;var g=this.tryParseArgumentClose(m);if(g.err)return g;c.push([h,{value:f.val,location:y(m,this.clonePosition())}]),a.add(h),this.bumpSpace(),i=this.parseIdentifierIfPossible(),h=i.value,l=i.location}return c.length===0?this.error(r==="select"?v.EXPECT_SELECT_ARGUMENT_SELECTOR:v.EXPECT_PLURAL_ARGUMENT_SELECTOR,y(this.clonePosition(),this.clonePosition())):this.requiresOtherClause&&!s?this.error(v.MISSING_OTHER_CLAUSE,y(this.clonePosition(),this.clonePosition())):{val:c,err:null}},e.prototype.tryParseDecimalInteger=function(t,r){var n=1,o=this.clonePosition();this.bumpIf("+")||this.bumpIf("-")&&(n=-1);for(var i=!1,s=0;!this.isEOF();){var c=this.char();if(c>=48&&c<=57)i=!0,s=s*10+(c-48),this.bump();else break}var a=y(o,this.clonePosition());return i?(s*=n,ba(s)?{val:s,err:null}:this.error(r,a)):this.error(t,a)},e.prototype.offset=function(){return this.position.offset},e.prototype.isEOF=function(){return this.offset()===this.message.length},e.prototype.clonePosition=function(){return{offset:this.position.offset,line:this.position.line,column:this.position.column}},e.prototype.char=function(){var t=this.position.offset;if(t>=this.message.length)throw Error("out of bound");var r=Zo(this.message,t);if(r===void 0)throw Error("Offset "+t+" is at invalid UTF-16 code unit boundary");return r},e.prototype.error=function(t,r){return{val:null,err:{kind:t,message:this.message,location:r}}},e.prototype.bump=function(){if(!this.isEOF()){var t=this.char();t===10?(this.position.line+=1,this.position.column=1,this.position.offset+=1):(this.position.column+=1,this.position.offset+=t<65536?1:2)}},e.prototype.bumpIf=function(t){if(Yo(this.message,t,this.offset())){for(var r=0;r=0?(this.bumpTo(n),!0):(this.bumpTo(this.message.length),!1)},e.prototype.bumpTo=function(t){if(this.offset()>t)throw Error("targetOffset "+t+" must be greater than or equal to the current offset "+this.offset());for(t=Math.min(t,this.message.length);;){var r=this.offset();if(r===t)break;if(r>t)throw Error("targetOffset "+t+" is at invalid UTF-16 code unit boundary");if(this.bump(),this.isEOF())break}},e.prototype.bumpSpace=function(){for(;!this.isEOF()&&Ko(this.char());)this.bump()},e.prototype.peek=function(){if(this.isEOF())return null;var t=this.char(),r=this.offset(),n=this.message.charCodeAt(r+(t>=65536?2:1));return n??null},e}();function hn(e){return e>=97&&e<=122||e>=65&&e<=90}function Aa(e){return hn(e)||e===47}function Sa(e){return e===45||e===46||e>=48&&e<=57||e===95||e>=97&&e<=122||e>=65&&e<=90||e==183||e>=192&&e<=214||e>=216&&e<=246||e>=248&&e<=893||e>=895&&e<=8191||e>=8204&&e<=8205||e>=8255&&e<=8256||e>=8304&&e<=8591||e>=11264&&e<=12271||e>=12289&&e<=55295||e>=63744&&e<=64975||e>=65008&&e<=65533||e>=65536&&e<=983039}function Ko(e){return e>=9&&e<=13||e===32||e===133||e>=8206&&e<=8207||e===8232||e===8233}function Ta(e){return e>=33&&e<=35||e===36||e>=37&&e<=39||e===40||e===41||e===42||e===43||e===44||e===45||e>=46&&e<=47||e>=58&&e<=59||e>=60&&e<=62||e>=63&&e<=64||e===91||e===92||e===93||e===94||e===96||e===123||e===124||e===125||e===126||e===161||e>=162&&e<=165||e===166||e===167||e===169||e===171||e===172||e===174||e===176||e===177||e===182||e===187||e===191||e===215||e===247||e>=8208&&e<=8213||e>=8214&&e<=8215||e===8216||e===8217||e===8218||e>=8219&&e<=8220||e===8221||e===8222||e===8223||e>=8224&&e<=8231||e>=8240&&e<=8248||e===8249||e===8250||e>=8251&&e<=8254||e>=8257&&e<=8259||e===8260||e===8261||e===8262||e>=8263&&e<=8273||e===8274||e===8275||e>=8277&&e<=8286||e>=8592&&e<=8596||e>=8597&&e<=8601||e>=8602&&e<=8603||e>=8604&&e<=8607||e===8608||e>=8609&&e<=8610||e===8611||e>=8612&&e<=8613||e===8614||e>=8615&&e<=8621||e===8622||e>=8623&&e<=8653||e>=8654&&e<=8655||e>=8656&&e<=8657||e===8658||e===8659||e===8660||e>=8661&&e<=8691||e>=8692&&e<=8959||e>=8960&&e<=8967||e===8968||e===8969||e===8970||e===8971||e>=8972&&e<=8991||e>=8992&&e<=8993||e>=8994&&e<=9e3||e===9001||e===9002||e>=9003&&e<=9083||e===9084||e>=9085&&e<=9114||e>=9115&&e<=9139||e>=9140&&e<=9179||e>=9180&&e<=9185||e>=9186&&e<=9254||e>=9255&&e<=9279||e>=9280&&e<=9290||e>=9291&&e<=9311||e>=9472&&e<=9654||e===9655||e>=9656&&e<=9664||e===9665||e>=9666&&e<=9719||e>=9720&&e<=9727||e>=9728&&e<=9838||e===9839||e>=9840&&e<=10087||e===10088||e===10089||e===10090||e===10091||e===10092||e===10093||e===10094||e===10095||e===10096||e===10097||e===10098||e===10099||e===10100||e===10101||e>=10132&&e<=10175||e>=10176&&e<=10180||e===10181||e===10182||e>=10183&&e<=10213||e===10214||e===10215||e===10216||e===10217||e===10218||e===10219||e===10220||e===10221||e===10222||e===10223||e>=10224&&e<=10239||e>=10240&&e<=10495||e>=10496&&e<=10626||e===10627||e===10628||e===10629||e===10630||e===10631||e===10632||e===10633||e===10634||e===10635||e===10636||e===10637||e===10638||e===10639||e===10640||e===10641||e===10642||e===10643||e===10644||e===10645||e===10646||e===10647||e===10648||e>=10649&&e<=10711||e===10712||e===10713||e===10714||e===10715||e>=10716&&e<=10747||e===10748||e===10749||e>=10750&&e<=11007||e>=11008&&e<=11055||e>=11056&&e<=11076||e>=11077&&e<=11078||e>=11079&&e<=11084||e>=11085&&e<=11123||e>=11124&&e<=11125||e>=11126&&e<=11157||e===11158||e>=11159&&e<=11263||e>=11776&&e<=11777||e===11778||e===11779||e===11780||e===11781||e>=11782&&e<=11784||e===11785||e===11786||e===11787||e===11788||e===11789||e>=11790&&e<=11798||e===11799||e>=11800&&e<=11801||e===11802||e===11803||e===11804||e===11805||e>=11806&&e<=11807||e===11808||e===11809||e===11810||e===11811||e===11812||e===11813||e===11814||e===11815||e===11816||e===11817||e>=11818&&e<=11822||e===11823||e>=11824&&e<=11833||e>=11834&&e<=11835||e>=11836&&e<=11839||e===11840||e===11841||e===11842||e>=11843&&e<=11855||e>=11856&&e<=11857||e===11858||e>=11859&&e<=11903||e>=12289&&e<=12291||e===12296||e===12297||e===12298||e===12299||e===12300||e===12301||e===12302||e===12303||e===12304||e===12305||e>=12306&&e<=12307||e===12308||e===12309||e===12310||e===12311||e===12312||e===12313||e===12314||e===12315||e===12316||e===12317||e>=12318&&e<=12319||e===12320||e===12336||e===64830||e===64831||e>=65093&&e<=65094}function dn(e){e.forEach(function(t){if(delete t.location,Yt(t)||Xt(t))for(var r in t.options)delete t.options[r].location,dn(t.options[r].value);else Bt(t)&&Jt(t.style)||(Wt(t)||qt(t))&&mt(t.style)?delete t.style.location:Zt(t)&&dn(t.children)})}function ei(e,t){t===void 0&&(t={}),t=E({shouldParseSkeletons:!0,requiresOtherClause:!0},t);var r=new Qo(e,t).parse();if(r.err){var n=SyntaxError(v[r.err.kind]);throw n.location=r.err.location,n.originalMessage=r.err.message,n}return t?.captureLocation||dn(r.val),r.val}function pt(e,t){var r=t&&t.cache?t.cache:Na,n=t&&t.serializer?t.serializer:La,o=t&&t.strategy?t.strategy:Ca;return o(e,{cache:r,serializer:n})}function Pa(e){return e==null||typeof e=="number"||typeof e=="boolean"}function ti(e,t,r,n){var o=Pa(n)?n:r(n),i=t.get(o);return typeof i>"u"&&(i=e.call(this,n),t.set(o,i)),i}function ri(e,t,r){var n=Array.prototype.slice.call(arguments,3),o=r(n),i=t.get(o);return typeof i>"u"&&(i=e.apply(this,n),t.set(o,i)),i}function un(e,t,r,n,o){return r.bind(t,e,n,o)}function Ca(e,t){var r=e.length===1?ti:ri;return un(e,this,r,t.cache.create(),t.serializer)}function $a(e,t){return un(e,this,ri,t.cache.create(),t.serializer)}function Oa(e,t){return un(e,this,ti,t.cache.create(),t.serializer)}var La=function(){return JSON.stringify(arguments)};function mn(){this.cache=Object.create(null)}mn.prototype.get=function(e){return this.cache[e]};mn.prototype.set=function(e,t){this.cache[e]=t};var Na={create:function(){return new mn}},Qt={variadic:$a,monadic:Oa};var Te;(function(e){e.MISSING_VALUE="MISSING_VALUE",e.INVALID_VALUE="INVALID_VALUE",e.MISSING_INTL_API="MISSING_INTL_API"})(Te||(Te={}));var ft=function(e){ut(t,e);function t(r,n,o){var i=e.call(this,r)||this;return i.code=n,i.originalMessage=o,i}return t.prototype.toString=function(){return"[formatjs Error: "+this.code+"] "+this.message},t}(Error);var pn=function(e){ut(t,e);function t(r,n,o,i){return e.call(this,'Invalid values for "'+r+'": "'+n+'". Options are "'+Object.keys(o).join('", "')+'"',Te.INVALID_VALUE,i)||this}return t}(ft);var ni=function(e){ut(t,e);function t(r,n,o){return e.call(this,'Value for "'+r+'" must be of type '+n,Te.INVALID_VALUE,o)||this}return t}(ft);var oi=function(e){ut(t,e);function t(r,n){return e.call(this,'The intl string context variable "'+r+'" was not provided to the string "'+n+'"',Te.MISSING_VALUE,n)||this}return t}(ft);var D;(function(e){e[e.literal=0]="literal",e[e.object=1]="object"})(D||(D={}));function Ra(e){return e.length<2?e:e.reduce(function(t,r){var n=t[t.length-1];return!n||n.type!==D.literal||r.type!==D.literal?t.push(r):n.value+=r.value,t},[])}function Ia(e){return typeof e=="function"}function gt(e,t,r,n,o,i,s){if(e.length===1&&rn(e[0]))return[{type:D.literal,value:e[0].value}];for(var c=[],a=0,h=e;a0?e.substring(0,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`); -//# sourceMappingURL=mas.js.map +`,Te.MISSING_INTL_API,s);var I=r.getPluralRules(t,{type:l.pluralType}).select(u-(l.offset||0));A=l.options[I]||l.options.other}if(!A)throw new pn(l.value,u,Object.keys(l.options),s);c.push.apply(c,gt(A.value,t,r,n,o,u-(l.offset||0)));continue}}return Ra(c)}function ka(e,t){return t?E(E(E({},e||{}),t||{}),Object.keys(e).reduce(function(r,n){return r[n]=E(E({},e[n]),t[n]||{}),r},{})):e}function Ma(e,t){return t?Object.keys(e).reduce(function(r,n){return r[n]=ka(e[n],t[n]),r},E({},e)):e}function fn(e){return{create:function(){return{get:function(t){return e[t]},set:function(t,r){e[t]=r}}}}}function Ua(e){return e===void 0&&(e={number:{},dateTime:{},pluralRules:{}}),{getNumberFormat:pt(function(){for(var t,r=[],n=0;n0?e.substring(0,n):"";let o=ai(e.split("").reverse().join("")),i=r-o,s=e.substring(i,i+1),c=i+(s==="."||s===","?1:0);t.suffix=o>0?e.substring(c,r):"",t.mask=e.substring(n,c),t.maskHasNegativeSign=t.mask.charAt(0)==="-",t.maskHasPositiveSign=t.mask.charAt(0)==="+";let a=t.mask.match(Fa);return t.decimal=a&&a[a.length-1]||".",t.separator=a&&a[1]&&a[0]||",",a=t.mask.split(t.decimal),t.integer=a[0],t.fraction=a[1],t}function za(e,t,r){let n=!1,o={value:e};e<0&&(n=!0,o.value=-o.value),o.sign=n?"-":"",o.value=Number(o.value).toFixed(t.fraction&&t.fraction.length),o.value=Number(o.value).toString();let i=t.fraction&&t.fraction.lastIndexOf("0"),[s="0",c=""]=o.value.split(".");return(!c||c&&c.length<=i)&&(c=i<0?"":(+("0."+c)).toFixed(i+1).replace("0.","")),o.integer=s,o.fraction=c,Ga(o,t),(o.result==="0"||o.result==="")&&(n=!1,o.sign=""),!n&&t.maskHasPositiveSign?o.sign="+":n&&t.maskHasPositiveSign?o.sign="-":n&&(o.sign=r&&r.enforceMaskSign&&!t.maskHasNegativeSign?"":"-"),o}function Ga(e,t){e.result="";let r=t.integer.split(t.separator),n=r.join(""),o=n&&n.indexOf("0");if(o>-1)for(;e.integer.lengthMath.round(e*20)/20},gn=(e,t)=>({accept:e,round:t}),qa=[gn(({divisor:e,price:t})=>t%e==0,({divisor:e,price:t})=>t/e),gn(({usePrecision:e})=>e,({divisor:e,price:t})=>Math.ceil(Math.floor(t*1e4/e)/100)/100),gn(()=>!0,({divisor:e,price:t})=>Math.ceil(Math.floor(t*100/e)/100))],xn={[k.YEAR]:{[L.MONTHLY]:xt.MONTH,[L.ANNUAL]:xt.YEAR},[k.MONTH]:{[L.MONTHLY]:xt.MONTH}},Ya=(e,t)=>e.indexOf(`'${t}'`)===0,Xa=(e,t=!0)=>{let r=e.replace(/'.*?'/,"").trim(),n=pi(r);return!!n?t||(r=r.replace(/[,\.]0+/,n)):r=r.replace(/\s?(#.*0)(?!\s)?/,"$&"+Ja(e)),r},Za=e=>{let t=Qa(e),r=Ya(e,t),n=e.replace(/'.*?'/,""),o=di.test(n)||ui.test(n);return{currencySymbol:t,isCurrencyFirst:r,hasCurrencySpace:o}},mi=e=>e.replace(di,hi).replace(ui,hi),Ja=e=>e.match(/#(.?)#/)?.[1]===li?ja:li,Qa=e=>e.match(/'(.*?)'/)?.[1]??"",pi=e=>e.match(/0(.?)0/)?.[1]??"";function Kt({formatString:e,price:t,usePrecision:r,isIndianPrice:n=!1},o,i=s=>s){let{currencySymbol:s,isCurrencyFirst:c,hasCurrencySpace:a}=Za(e),h=r?pi(e):"",l=Xa(e,r),d=r?2:0,u=i(t,{currencySymbol:s}),m=n?u.toLocaleString("hi-IN",{minimumFractionDigits:d,maximumFractionDigits:d}):ci(l,u),f=r?m.lastIndexOf(h):m.length,g=m.substring(0,f),_=m.substring(f+1);return{accessiblePrice:e.replace(/'.*?'/,"SYMBOL").replace(/#.*0/,m).replace(/SYMBOL/,s),currencySymbol:s,decimals:_,decimalsDelimiter:h,hasCurrencySpace:a,integer:g,isCurrencyFirst:c,recurrenceTerm:o}}var fi=e=>{let{commitment:t,term:r,usePrecision:n}=e,o=Ba[r]??1;return Kt(e,o>1?xt.MONTH:xn[t]?.[r],(i,{currencySymbol:s})=>{let c={divisor:o,price:i,usePrecision:n},{round:a}=qa.find(({accept:l})=>l(c));if(!a)throw new Error(`Missing rounding rule for: ${JSON.stringify(c)}`);return(Wa[s]??(l=>l))(a(c))})},gi=({commitment:e,term:t,...r})=>Kt(r,xn[e]?.[t]),xi=e=>{let{commitment:t,term:r}=e;return t===k.YEAR&&r===L.MONTHLY?Kt(e,xt.YEAR,n=>n*12):Kt(e,xn[t]?.[r])};var Ka={recurrenceLabel:"{recurrenceTerm, select, MONTH {/mo} YEAR {/yr} other {}}",recurrenceAriaLabel:"{recurrenceTerm, select, MONTH {per month} YEAR {per year} other {}}",perUnitLabel:"{perUnit, select, LICENSE {per license} other {}}",perUnitAriaLabel:"{perUnit, select, LICENSE {per license} other {}}",freeLabel:"Free",freeAriaLabel:"Free",taxExclusiveLabel:"{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}",taxInclusiveLabel:"{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}",alternativePriceAriaLabel:"Alternatively at {alternativePrice}",strikethroughAriaLabel:"Regularly at {strikethroughPrice}"},ec=Lo("ConsonantTemplates/price"),tc=/<.+?>/g,W={container:"price",containerOptical:"price-optical",containerStrikethrough:"price-strikethrough",containerAnnual:"price-annual",disabled:"disabled",currencySpace:"price-currency-space",currencySymbol:"price-currency-symbol",decimals:"price-decimals",decimalsDelimiter:"price-decimals-delimiter",integer:"price-integer",recurrence:"price-recurrence",taxInclusivity:"price-tax-inclusivity",unitType:"price-unit-type"},Pe={perUnitLabel:"perUnitLabel",perUnitAriaLabel:"perUnitAriaLabel",recurrenceLabel:"recurrenceLabel",recurrenceAriaLabel:"recurrenceAriaLabel",taxExclusiveLabel:"taxExclusiveLabel",taxInclusiveLabel:"taxInclusiveLabel",strikethroughAriaLabel:"strikethroughAriaLabel"},rc="TAX_EXCLUSIVE",nc=e=>Oo(e)?Object.entries(e).filter(([,t])=>Be(t)||Gt(t)||t===!0).reduce((t,[r,n])=>t+` ${r}${n===!0?"":'="'+Co(n)+'"'}`,""):"",ee=(e,t,r,n=!1)=>`${n?mi(t):t??""}`;function oc(e,{accessibleLabel:t,currencySymbol:r,decimals:n,decimalsDelimiter:o,hasCurrencySpace:i,integer:s,isCurrencyFirst:c,recurrenceLabel:a,perUnitLabel:h,taxInclusivityLabel:l},d={}){let u=ee(W.currencySymbol,r),m=ee(W.currencySpace,i?" ":""),f="";return c&&(f+=u+m),f+=ee(W.integer,s),f+=ee(W.decimalsDelimiter,o),f+=ee(W.decimals,n),c||(f+=m+u),f+=ee(W.recurrence,a,null,!0),f+=ee(W.unitType,h,null,!0),f+=ee(W.taxInclusivity,l,!0),ee(e,f,{...d,"aria-label":t})}var Ce=({displayOptical:e=!1,displayStrikethrough:t=!1,displayAnnual:r=!1}={})=>({country:n,displayFormatted:o=!0,displayRecurrence:i=!0,displayPerUnit:s=!1,displayTax:c=!1,language:a,literals:h={}}={},{commitment:l,formatString:d,price:u,priceWithoutDiscount:m,taxDisplay:f,taxTerm:g,term:_,usePrecision:P}={},C={})=>{Object.entries({country:n,formatString:d,language:a,price:u}).forEach(([ae,Ir])=>{if(Ir==null)throw new Error(`Argument "${ae}" is missing`)});let A={...Ka,...h},I=`${a.toLowerCase()}-${n.toUpperCase()}`;function S(ae,Ir){let kr=A[ae];if(kr==null)return"";try{return new si(kr.replace(tc,""),I).format(Ir)}catch{return ec.error("Failed to format literal:",kr),""}}let $=t&&m?m:u,z=e?fi:gi;r&&(z=xi);let{accessiblePrice:X,recurrenceTerm:ie,...ue}=z({commitment:l,formatString:d,term:_,price:e?u:$,usePrecision:P,isIndianPrice:n==="IN"}),Z=X,_e="";if(w(i)&&ie){let ae=S(Pe.recurrenceAriaLabel,{recurrenceTerm:ie});ae&&(Z+=" "+ae),_e=S(Pe.recurrenceLabel,{recurrenceTerm:ie})}let we="";if(w(s)){we=S(Pe.perUnitLabel,{perUnit:"LICENSE"});let ae=S(Pe.perUnitAriaLabel,{perUnit:"LICENSE"});ae&&(Z+=" "+ae)}let se="";w(c)&&g&&(se=S(f===rc?Pe.taxExclusiveLabel:Pe.taxInclusiveLabel,{taxTerm:g}),se&&(Z+=" "+se)),t&&(Z=S(Pe.strikethroughAriaLabel,{strikethroughPrice:Z}));let J=W.container;if(e&&(J+=" "+W.containerOptical),t&&(J+=" "+W.containerStrikethrough),r&&(J+=" "+W.containerAnnual),w(o))return oc(J,{...ue,accessibleLabel:Z,recurrenceLabel:_e,perUnitLabel:we,taxInclusivityLabel:se},C);let{currencySymbol:Ge,decimals:Ut,decimalsDelimiter:Dt,hasCurrencySpace:ct,integer:Rr,isCurrencyFirst:Is}=ue,Ve=[Rr,Dt,Ut];Is?(Ve.unshift(ct?"\xA0":""),Ve.unshift(Ge)):(Ve.push(ct?"\xA0":""),Ve.push(Ge)),Ve.push(_e,we,se);let ks=Ve.join("");return ee(J,ks,C)},vi=()=>(e,t,r)=>{let o=(e.displayOldPrice===void 0||w(e.displayOldPrice))&&t.priceWithoutDiscount&&t.priceWithoutDiscount!=t.price;return`${Ce()(e,t,r)}${o?" "+Ce({displayStrikethrough:!0})(e,t,r):""}`};var vn=Ce(),yn=vi(),En=Ce({displayOptical:!0}),bn=Ce({displayStrikethrough:!0}),_n=Ce({displayAnnual:!0});var ic=(e,t)=>{if(!(!qe(e)||!qe(t)))return Math.floor((t-e)/t*100)},yi=()=>(e,t,r)=>{let{price:n,priceWithoutDiscount:o}=t,i=ic(n,o);return i===void 0?'':`${i}%`};var wn=yi();var{freeze:vt}=Object,ce=vt({...Ae}),le=vt({...Y}),$e={STAGE:"STAGE",PRODUCTION:"PRODUCTION",LOCAL:"LOCAL"},Ei=vt({...k}),bi=vt({...Ro}),_i=vt({...L});var Ln={};Us(Ln,{CLASS_NAME_FAILED:()=>er,CLASS_NAME_PENDING:()=>tr,CLASS_NAME_RESOLVED:()=>rr,ERROR_MESSAGE_BAD_REQUEST:()=>nr,ERROR_MESSAGE_MISSING_LITERALS_URL:()=>Sn,ERROR_MESSAGE_OFFER_NOT_FOUND:()=>An,EVENT_TYPE_ERROR:()=>sc,EVENT_TYPE_FAILED:()=>or,EVENT_TYPE_PENDING:()=>ir,EVENT_TYPE_READY:()=>Ze,EVENT_TYPE_RESOLVED:()=>sr,LOG_NAMESPACE:()=>Tn,Landscape:()=>Je,PARAM_AOS_API_KEY:()=>ac,PARAM_ENV:()=>Pn,PARAM_LANDSCAPE:()=>Cn,PARAM_WCS_API_KEY:()=>cc,STATE_FAILED:()=>te,STATE_PENDING:()=>re,STATE_RESOLVED:()=>ne,TAG_NAME_SERVICE:()=>fe,WCS_PROD_URL:()=>$n,WCS_STAGE_URL:()=>On});var er="placeholder-failed",tr="placeholder-pending",rr="placeholder-resolved",nr="Bad WCS request",An="Commerce offer not found",Sn="Literals URL not provided",sc="wcms:commerce:error",or="wcms:placeholder:failed",ir="wcms:placeholder:pending",Ze="wcms:commerce:ready",sr="wcms:placeholder:resolved",Tn="wcms/commerce",Pn="commerce.env",Cn="commerce.landscape",ac="commerce.aosKey",cc="commerce.wcsKey",$n="https://www.adobe.com/web_commerce_artifact",On="https://www.stage.adobe.com/web_commerce_artifact_stage",te="failed",re="pending",ne="resolved",fe="wcms-commerce",Je={DRAFT:"DRAFT",PUBLISHED:"PUBLISHED"};var Nn={clientId:"merch-at-scale",delimiter:"\xB6",ignoredProperties:["analytics","literals"],serializableTypes:["Array","Object"],sampleRate:30,tags:"consumer=milo/commerce"},wi=new Set,lc=e=>e instanceof Error||typeof e.originatingRequest=="string";function Ai(e){if(e==null)return;let t=typeof e;if(t==="function"){let{name:r}=e;return r?`${t} ${r}`:t}if(t==="object"){if(e instanceof Error)return e.message;if(typeof e.originatingRequest=="string"){let{message:n,originatingRequest:o,status:i}=e;return[n,i,o].filter(s=>s).join(" ")}let r=e[Symbol.toStringTag]??Object.getPrototypeOf(e).constructor.name;if(!Nn.serializableTypes.includes(r))return r}return e}function hc(e,t){if(!Nn.ignoredProperties.includes(e))return Ai(t)}var Rn={append(e){let{delimiter:t,sampleRate:r,tags:n,clientId:o}=Nn,{message:i,params:s}=e,c=[],a=i,h=[];s.forEach(u=>{u!=null&&(lc(u)?c:h).push(u)}),c.length&&(a+=" ",a+=c.map(Ai).join(" "));let{pathname:l,search:d}=window.location;a+=`${t}page=`,a+=l+d,h.length&&(a+=`${t}facts=`,a+=JSON.stringify(h,hc)),wi.has(a)||(wi.add(a),window.lana?.log(a,{sampleRate:r,tags:n,clientId:o}))}};var b=Object.freeze({checkoutClientId:"adobe_com",checkoutWorkflow:ce.V3,checkoutWorkflowStep:le.EMAIL,country:"US",displayOldPrice:!0,displayPerUnit:!1,displayRecurrence:!0,displayTax:!1,env:$e.PRODUCTION,forceTaxExclusive:!1,language:"en",entitlement:!1,extraOptions:{},modal:!1,promotionCode:"",quantity:1,wcsApiKey:"wcms-commerce-ims-ro-user-milo",wcsBufferDelay:1,wcsURL:"https://www.adobe.com/web_commerce_artifact",landscape:Je.PUBLISHED,wcsBufferLimit:1});function Si(e,{once:t=!1}={}){let r=null;function n(){let o=document.querySelector(fe);o!==r&&(r=o,o&&e(o))}return document.addEventListener(Ze,n,{once:t}),ge(n),()=>document.removeEventListener(Ze,n)}function yt(e,{country:t,forceTaxExclusive:r,perpetual:n}){let o;if(e.length<2)o=e;else{let i=t==="GB"||n?"EN":"MULT",[s,c]=e;o=[s.language===i?s:c]}return r&&(o=o.map(en)),o}var ge=e=>window.setTimeout(e);function Qe(e,t=1){if(e==null)return[t];let r=(Array.isArray(e)?e:String(e).split(",")).map(Xe).filter(qe);return r.length||(r=[t]),r}function ar(e){return e==null?[]:(Array.isArray(e)?e:String(e).split(",")).filter(Wr)}function G(){return window.customElements.get(fe)?.instance}var dc="en_US",p={ar:"AR_es",be_en:"BE_en",be_fr:"BE_fr",be_nl:"BE_nl",br:"BR_pt",ca:"CA_en",ch_de:"CH_de",ch_fr:"CH_fr",ch_it:"CH_it",cl:"CL_es",co:"CO_es",la:"DO_es",mx:"MX_es",pe:"PE_es",africa:"MU_en",dk:"DK_da",de:"DE_de",ee:"EE_et",eg_ar:"EG_ar",eg_en:"EG_en",es:"ES_es",fr:"FR_fr",gr_el:"GR_el",gr_en:"GR_en",ie:"IE_en",il_he:"IL_iw",it:"IT_it",lv:"LV_lv",lt:"LT_lt",lu_de:"LU_de",lu_en:"LU_en",lu_fr:"LU_fr",my_en:"MY_en",my_ms:"MY_ms",hu:"HU_hu",mt:"MT_en",mena_en:"DZ_en",mena_ar:"DZ_ar",nl:"NL_nl",no:"NO_nb",pl:"PL_pl",pt:"PT_pt",ro:"RO_ro",si:"SI_sl",sk:"SK_sk",fi:"FI_fi",se:"SE_sv",tr:"TR_tr",uk:"GB_en",at:"AT_de",cz:"CZ_cs",bg:"BG_bg",ru:"RU_ru",ua:"UA_uk",au:"AU_en",in_en:"IN_en",in_hi:"IN_hi",id_en:"ID_en",id_id:"ID_in",nz:"NZ_en",sa_ar:"SA_ar",sa_en:"SA_en",sg:"SG_en",cn:"CN_zh-Hans",tw:"TW_zh-Hant",hk_zh:"HK_zh-hant",jp:"JP_ja",kr:"KR_ko",za:"ZA_en",ng:"NG_en",cr:"CR_es",ec:"EC_es",pr:"US_es",gt:"GT_es",cis_en:"AZ_en",cis_ru:"AZ_ru",sea:"SG_en",th_en:"TH_en",th_th:"TH_th"},cr=Object.freeze({LOCAL:"local",PROD:"prod",STAGE:"stage"});function uc({locale:e={}}={}){if(!e.prefix)return{country:b.country,language:b.language,locale:dc};let t=e.prefix.replace("/","")??"",[r=b.country,n=b.language]=(p[t]??t).split("_",2);return r=r.toUpperCase(),n=n.toLowerCase(),{country:r,language:n,locale:`${n}_${r}`}}function Ti(e={}){let{commerce:t={},locale:r=void 0}=e,n=$e.PRODUCTION,o=$n,i=["local","stage"].includes(e.env?.name),s=N(Pn,t,{metadata:!1})?.toLowerCase()==="stage";i&&s&&(n=$e.STAGE,o=On);let c=N("checkoutClientId",t)??b.checkoutClientId,a=pe(N("checkoutWorkflow",t),ce,b.checkoutWorkflow),h=le.CHECKOUT;a===ce.V3&&(h=pe(N("checkoutWorkflowStep",t),le,b.checkoutWorkflowStep));let l=w(N("displayOldPrice",t),b.displayOldPrice),d=w(N("displayPerUnit",t),b.displayPerUnit),u=w(N("displayRecurrence",t),b.displayRecurrence),m=w(N("displayTax",t),b.displayTax),f=w(N("entitlement",t),b.entitlement),g=w(N("modal",t),b.modal),_=w(N("forceTaxExclusive",t),b.forceTaxExclusive),P=N("promotionCode",t)??b.promotionCode,C=Qe(N("quantity",t)),A=N("wcsApiKey",t)??b.wcsApiKey,I=e.env?.name===cr.PROD?Je.PUBLISHED:pe(N(Cn,t),Je,b.landscape),S=Xe(N("wcsBufferDelay",t),b.wcsBufferDelay),$=Xe(N("wcsBufferLimit",t),b.wcsBufferLimit);return{...uc({locale:r}),displayOldPrice:l,checkoutClientId:c,checkoutWorkflow:a,checkoutWorkflowStep:h,displayPerUnit:d,displayRecurrence:u,displayTax:m,entitlement:f,extraOptions:b.extraOptions,modal:g,env:n,forceTaxExclusive:_,priceLiteralsURL:t.priceLiteralsURL,priceLiteralsPromise:t.priceLiteralsPromise,promotionCode:P,quantity:C,wcsApiKey:A,wcsBufferDelay:S,wcsBufferLimit:$,wcsURL:o,landscape:I}}var Ci="debug",mc="error",pc="info",fc="warn",gc=Date.now(),In=new Set,kn=new Set,Pi=new Map,Et=Object.freeze({DEBUG:Ci,ERROR:mc,INFO:pc,WARN:fc}),$i={append({level:e,message:t,params:r,timestamp:n,source:o}){console[e](`${n}ms [${o}] %c${t}`,"font-weight: bold;",...r)}},Oi={filter:({level:e})=>e!==Ci},xc={filter:()=>!1};function vc(e,t,r,n,o){return{level:e,message:t,namespace:r,get params(){if(n.length===1){let[i]=n;me(i)&&(n=i(),Array.isArray(n)||(n=[n]))}return n},source:o,timestamp:Date.now()-gc}}function yc(e){[...kn].every(t=>t(e))&&In.forEach(t=>t(e))}function Li(e){let t=(Pi.get(e)??0)+1;Pi.set(e,t);let r=`${e} #${t}`,n=i=>(s,...c)=>yc(vc(i,s,e,c,r)),o=Object.seal({id:r,namespace:e,module(i){return Li(`${o.namespace}/${i}`)},debug:n(Et.DEBUG),error:n(Et.ERROR),info:n(Et.INFO),warn:n(Et.WARN)});return o}function lr(...e){e.forEach(t=>{let{append:r,filter:n}=t;me(n)?kn.add(n):me(r)&&In.add(r)})}function Ec(e={}){let{name:t}=e,r=w(N("commerce.debug",{search:!0,storage:!0}),t===cr.LOCAL);return lr(r?$i:Oi),t===cr.PROD&&lr(Rn),F}function bc(){In.clear(),kn.clear()}var F={...Li(Tn),Level:Et,Plugins:{consoleAppender:$i,debugFilter:Oi,quietFilter:xc,lanaAppender:Rn},init:Ec,reset:bc,use:lr};var _c={CLASS_NAME_FAILED:er,CLASS_NAME_PENDING:tr,CLASS_NAME_RESOLVED:rr,EVENT_TYPE_FAILED:or,EVENT_TYPE_PENDING:ir,EVENT_TYPE_RESOLVED:sr,STATE_FAILED:te,STATE_PENDING:re,STATE_RESOLVED:ne},wc={[te]:er,[re]:tr,[ne]:rr},Ac={[te]:or,[re]:ir,[ne]:sr},ur=new WeakMap;function V(e){if(!ur.has(e)){let t=F.module(e.constructor.is);ur.set(e,{changes:new Map,connected:!1,dispose:We,error:void 0,log:t,options:void 0,promises:[],state:re,timer:null,value:void 0,version:0})}return ur.get(e)}function hr(e){let t=V(e),{error:r,promises:n,state:o}=t;(o===ne||o===te)&&(t.promises=[],o===ne?n.forEach(({resolve:i})=>i(e)):o===te&&n.forEach(({reject:i})=>i(r))),e.dispatchEvent(new CustomEvent(Ac[o],{bubbles:!0}))}function dr(e){let t=ur.get(e);[te,re,ne].forEach(r=>{e.classList.toggle(wc[r],r===t.state)})}var Sc={get error(){return V(this).error},get log(){return V(this).log},get options(){return V(this).options},get state(){return V(this).state},get value(){return V(this).value},attributeChangedCallback(e,t,r){V(this).changes.set(e,r),this.requestUpdate()},connectedCallback(){V(this).dispose=Si(()=>this.requestUpdate(!0))},disconnectedCallback(){let e=V(this);e.connected&&(e.connected=!1,e.log.debug("Disconnected:",{element:this})),e.dispose(),e.dispose=We},onceSettled(){let{error:e,promises:t,state:r}=V(this);return ne===r?Promise.resolve(this):te===r?Promise.reject(e):new Promise((n,o)=>{t.push({resolve:n,reject:o})})},toggleResolved(e,t,r){let n=V(this);return e!==n.version?!1:(r!==void 0&&(n.options=r),n.state=ne,n.value=t,dr(this),this.log.debug("Resolved:",{element:this,value:t}),ge(()=>hr(this)),!0)},toggleFailed(e,t,r){let n=V(this);return e!==n.version?!1:(r!==void 0&&(n.options=r),n.error=t,n.state=te,dr(this),n.log.error("Failed:",{element:this,error:t}),ge(()=>hr(this)),!0)},togglePending(e){let t=V(this);return t.version++,e&&(t.options=e),t.state=re,dr(this),ge(()=>hr(this)),t.version},requestUpdate(e=!1){if(!this.isConnected||!G())return;let t=V(this);if(t.timer)return;let{error:r,options:n,state:o,value:i,version:s}=t;t.state=re,t.timer=ge(async()=>{t.timer=null;let c=null;if(t.changes.size&&(c=Object.fromEntries(t.changes.entries()),t.changes.clear()),t.connected?t.log.debug("Updated:",{element:this,changes:c}):(t.connected=!0,t.log.debug("Connected:",{element:this,changes:c})),c||e)try{await this.render?.()===!1&&t.state===re&&t.version===s&&(t.state=o,t.error=r,t.value=i,dr(this),hr(this))}catch(a){this.toggleFailed(t.version,a,n)}})}};function Ni(e={}){return Object.entries(e).forEach(([t,r])=>{(r==null||r===""||r?.length===0)&&delete e[t]}),e}function mr(e,t={}){let{tag:r,is:n}=e,o=document.createElement(r,{is:n});return o.setAttribute("is",n),Object.assign(o.dataset,Ni(t)),o}function pr(e){let{tag:t,is:r,prototype:n}=e,o=window.customElements.get(r);return o||(Object.defineProperties(n,Object.getOwnPropertyDescriptors(Sc)),o=Object.defineProperties(e,Object.getOwnPropertyDescriptors(_c)),window.customElements.define(r,o,{extends:t})),o}function fr(e,t=document.body){return Array.from(t?.querySelectorAll(`${e.tag}[is="${e.is}"]`)??[])}function gr(e,t={}){return e instanceof HTMLElement?(Object.assign(e.dataset,Ni(t)),e):null}var Tc="download",Pc="upgrade",Oe,Ke=class Ke extends HTMLAnchorElement{constructor(){super();Q(this,Oe);this.addEventListener("click",this.clickHandler)}static get observedAttributes(){return["data-checkout-workflow","data-checkout-workflow-step","data-extra-options","data-ims-country","data-perpetual","data-promotion-code","data-quantity","data-template","data-wcs-osi","data-entitlement","data-upgrade","data-modal"]}static createCheckoutLink(r={},n=""){let o=G();if(!o)return null;let{checkoutMarketSegment:i,checkoutWorkflow:s,checkoutWorkflowStep:c,entitlement:a,upgrade:h,modal:l,perpetual:d,promotionCode:u,quantity:m,wcsOsi:f,extraOptions:g}=o.collectCheckoutOptions(r),_=mr(Ke,{checkoutMarketSegment:i,checkoutWorkflow:s,checkoutWorkflowStep:c,entitlement:a,upgrade:h,modal:l,perpetual:d,promotionCode:u,quantity:m,wcsOsi:f,extraOptions:g});return n&&(_.innerHTML=`${n}`),_}static getCheckoutLinks(r){return fr(Ke,r)}get isCheckoutLink(){return!0}get placeholder(){return this}clickHandler(r){var n;(n=R(this,Oe))==null||n.call(this,r)}async render(r={}){if(!this.isConnected)return!1;let n=G();if(!n)return!1;this.dataset.imsCountry||n.imsCountryPromise.then(l=>{l&&(this.dataset.imsCountry=l)},We);let o=n.collectCheckoutOptions(r,this.placeholder);if(!o.wcsOsi.length)return!1;let i;try{i=JSON.parse(o.extraOptions??"{}")}catch(l){this.placeholder.log.error("cannot parse exta checkout options",l)}let s=this.placeholder.togglePending(o);this.href="";let c=n.resolveOfferSelectors(o),a=await Promise.all(c);a=a.map(l=>yt(l,o));let h=await n.buildCheckoutAction(a.flat(),{...i,...o});return this.renderOffers(a.flat(),o,{},h,s)}renderOffers(r,n,o={},i=void 0,s=void 0){if(!this.isConnected)return!1;let c=G();if(!c)return!1;if(n={...JSON.parse(this.placeholder.dataset.extraOptions??"null"),...n,...o},s??(s=this.placeholder.togglePending(n)),R(this,Oe)&&K(this,Oe,void 0),i){this.classList.remove(Tc,Pc),this.placeholder.toggleResolved(s,r,n);let{url:h,text:l,className:d,handler:u}=i;return h&&(this.href=h),l&&(this.firstElementChild.innerHTML=l),d&&this.classList.add(...d.split(" ")),u&&(this.setAttribute("href","#"),K(this,Oe,u.bind(this))),!0}else if(r.length){if(this.placeholder.toggleResolved(s,r,n)){let h=c.buildCheckoutURL(r,n);return this.setAttribute("href",h),!0}}else{let h=new Error(`Not provided: ${n?.wcsOsi??"-"}`);if(this.placeholder.toggleFailed(s,h,n))return this.setAttribute("href","#"),!0}return!1}updateOptions(r={}){let n=G();if(!n)return!1;let{checkoutMarketSegment:o,checkoutWorkflow:i,checkoutWorkflowStep:s,entitlement:c,upgrade:a,modal:h,perpetual:l,promotionCode:d,quantity:u,wcsOsi:m}=n.collectCheckoutOptions(r);return gr(this,{checkoutMarketSegment:o,checkoutWorkflow:i,checkoutWorkflowStep:s,entitlement:c,upgrade:a,modal:h,perpetual:l,promotionCode:d,quantity:u,wcsOsi:m}),!0}};Oe=new WeakMap,O(Ke,"is","checkout-link"),O(Ke,"tag","a");var Mn=Ke,Un=pr(Mn);var Ri=[p.uk,p.au,p.fr,p.at,p.be_en,p.be_fr,p.be_nl,p.bg,p.ch_de,p.ch_fr,p.ch_it,p.cz,p.de,p.dk,p.ee,p.eg_ar,p.eg_en,p.es,p.fi,p.fr,p.gr_el,p.gr_en,p.hu,p.ie,p.it,p.lu_de,p.lu_en,p.lu_fr,p.nl,p.no,p.pl,p.pt,p.ro,p.se,p.si,p.sk,p.tr,p.ua,p.id_en,p.id_id,p.in_en,p.in_hi,p.jp,p.my_en,p.my_ms,p.nz,p.th_en,p.th_th],Cc={INDIVIDUAL_COM:[p.za,p.lt,p.lv,p.ng,p.sa_ar,p.sa_en,p.za,p.sg,p.kr],TEAM_COM:[p.za,p.lt,p.lv,p.ng,p.za,p.co,p.kr],INDIVIDUAL_EDU:[p.lt,p.lv,p.sa_en,p.sea],TEAM_EDU:[p.sea,p.kr]},et=class et extends HTMLSpanElement{static get observedAttributes(){return["data-display-old-price","data-display-per-unit","data-display-recurrence","data-display-tax","data-perpetual","data-promotion-code","data-tax-exclusive","data-template","data-wcs-osi"]}static createInlinePrice(t){let r=G();if(!r)return null;let{displayOldPrice:n,displayPerUnit:o,displayRecurrence:i,displayTax:s,forceTaxExclusive:c,perpetual:a,promotionCode:h,quantity:l,template:d,wcsOsi:u}=r.collectPriceOptions(t);return mr(et,{displayOldPrice:n,displayPerUnit:o,displayRecurrence:i,displayTax:s,forceTaxExclusive:c,perpetual:a,promotionCode:h,quantity:l,template:d,wcsOsi:u})}static getInlinePrices(t){return fr(et,t)}get isInlinePrice(){return!0}get placeholder(){return this}resolveDisplayTaxForGeoAndSegment(t,r,n,o){let i=`${t}_${r}`;if(Ri.includes(t)||Ri.includes(i))return!0;let s=Cc[`${n}_${o}`];return s?!!(s.includes(t)||s.includes(i)):!1}async resolveDisplayTax(t,r){let[n]=await t.resolveOfferSelectors(r),o=yt(await n,r);if(o?.length){let{country:i,language:s}=r,c=o[0],[a=""]=c.marketSegments;return this.resolveDisplayTaxForGeoAndSegment(i,s,c.customerSegment,a)}}async render(t={}){if(!this.isConnected)return!1;let r=G();if(!r)return!1;let n=r.collectPriceOptions(t,this.placeholder);if(!n.wcsOsi.length)return!1;let o=this.placeholder.togglePending(n);this.innerHTML="";let[i]=r.resolveOfferSelectors(n);return this.renderOffers(yt(await i,n),n,o)}renderOffers(t,r={},n=void 0){if(!this.isConnected)return;let o=G();if(!o)return!1;let i=o.collectPriceOptions({...this.dataset,...r});if(n??(n=this.placeholder.togglePending(i)),t.length){if(this.placeholder.toggleResolved(n,t,i))return this.innerHTML=o.buildPriceHTML(t,i),!0}else{let s=new Error(`Not provided: ${i?.wcsOsi??"-"}`);if(this.placeholder.toggleFailed(n,s,i))return this.innerHTML="",!0}return!1}updateOptions(t){let r=G();if(!r)return!1;let{displayOldPrice:n,displayPerUnit:o,displayRecurrence:i,displayTax:s,forceTaxExclusive:c,perpetual:a,promotionCode:h,quantity:l,template:d,wcsOsi:u}=r.collectPriceOptions(t);return gr(this,{displayOldPrice:n,displayPerUnit:o,displayRecurrence:i,displayTax:s,forceTaxExclusive:c,perpetual:a,promotionCode:h,quantity:l,template:d,wcsOsi:u}),!0}};O(et,"is","inline-price"),O(et,"tag","span");var Dn=et,Fn=pr(Dn);function Ii({providers:e,settings:t},r){let n=F.module("checkout");function o(h,l){let{checkoutClientId:d,checkoutWorkflow:u,checkoutWorkflowStep:m,country:f,language:g,promotionCode:_,quantity:P}=t,{checkoutMarketSegment:C,checkoutWorkflow:A=u,checkoutWorkflowStep:I=m,imsCountry:S,country:$=S??f,language:z=g,quantity:X=P,entitlement:ie,upgrade:ue,modal:Z,perpetual:_e,promotionCode:we=_,wcsOsi:se,extraOptions:J,...Ge}=Object.assign({},l?.dataset??{},h??{}),Ut=pe(A,ce,b.checkoutWorkflow),Dt=le.CHECKOUT;Ut===ce.V3&&(Dt=pe(I,le,b.checkoutWorkflowStep));let ct=Ye({...Ge,extraOptions:J,checkoutClientId:d,checkoutMarketSegment:C,country:$,quantity:Qe(X,b.quantity),checkoutWorkflow:Ut,checkoutWorkflowStep:Dt,language:z,entitlement:w(ie),upgrade:w(ue),modal:w(Z),perpetual:w(_e),promotionCode:dt(we).effectivePromoCode,wcsOsi:ar(se)});if(l)for(let Rr of e.checkout)Rr(l,ct);return ct}async function i(h,l){let d=G(),u=await r.getCheckoutAction?.(h,l,d.imsSignedInPromise);return u||null}function s(h,l){if(!Array.isArray(h)||!h.length||!l)return"";let{env:d,landscape:u}=t,{checkoutClientId:m,checkoutMarketSegment:f,checkoutWorkflow:g,checkoutWorkflowStep:_,country:P,promotionCode:C,quantity:A,...I}=o(l),S=window.frameElement?"if":"fp",$={checkoutPromoCode:C,clientId:m,context:S,country:P,env:d,items:[],marketSegment:f,workflowStep:_,landscape:u,...I};if(h.length===1){let[{offerId:z,offerType:X,productArrangementCode:ie}]=h,{marketSegments:[ue]}=h[0];Object.assign($,{marketSegment:ue,offerType:X,productArrangementCode:ie}),$.items.push(A[0]===1?{id:z}:{id:z,quantity:A[0]})}else $.items.push(...h.map(({offerId:z},X)=>({id:z,quantity:A[X]??b.quantity})));return Hr(g,$)}let{createCheckoutLink:c,getCheckoutLinks:a}=Un;return{CheckoutLink:Un,CheckoutWorkflow:ce,CheckoutWorkflowStep:le,buildCheckoutAction:i,buildCheckoutURL:s,collectCheckoutOptions:o,createCheckoutLink:c,getCheckoutLinks:a}}function $c({interval:e=200,maxAttempts:t=25}={}){let r=F.module("ims");return new Promise(n=>{r.debug("Waing for IMS to be ready");let o=0;function i(){window.adobeIMS?.initialized?n():++o>t?(r.debug("Timeout"),n()):setTimeout(i,e)}i()})}function Oc(e){return e.then(()=>window.adobeIMS?.isSignedInUser()??!1)}function Lc(e){let t=F.module("ims");return e.then(r=>r?window.adobeIMS.getProfile().then(({countryCode:n})=>(t.debug("Got user country:",n),n),n=>{t.error("Unable to get user country:",n)}):null)}function ki({}){let e=$c(),t=Oc(e),r=Lc(t);return{imsReadyPromise:e,imsSignedInPromise:t,imsCountryPromise:r}}function Nc(e){if(!e.priceLiteralsURL)throw new Error(Sn);return new Promise(t=>{window.fetch(e.priceLiteralsURL).then(r=>{r.json().then(({data:n})=>{t(n)})})})}async function Mi(e){let r=await(e.priceLiteralsPromise||Nc(e));if(Array.isArray(r)){let n=i=>r.find(s=>zt(s.lang,i)),o=n(e.language)??n(b.language);if(o)return Object.freeze(o)}return{}}function Ui({literals:e,providers:t,settings:r}){function n(c,a){let{country:h,displayOldPrice:l,displayPerUnit:d,displayRecurrence:u,displayTax:m,forceTaxExclusive:f,language:g,promotionCode:_,quantity:P}=r,{displayOldPrice:C=l,displayPerUnit:A=d,displayRecurrence:I=u,displayTax:S=m,forceTaxExclusive:$=f,country:z=h,language:X=g,perpetual:ie,promotionCode:ue=_,quantity:Z=P,template:_e,wcsOsi:we,...se}=Object.assign({},a?.dataset??{},c??{}),J=Ye({...se,country:z,displayOldPrice:w(C),displayPerUnit:w(A),displayRecurrence:w(I),displayTax:w(S),forceTaxExclusive:w($),language:X,perpetual:w(ie),promotionCode:dt(ue).effectivePromoCode,quantity:Qe(Z,b.quantity),template:_e,wcsOsi:ar(we)});if(a)for(let Ge of t.price)Ge(a,J);return J}function o(c,a){if(!Array.isArray(c)||!c.length||!a)return"";let{template:h}=a,l;switch(h){case"discount":l=wn;break;case"strikethrough":l=bn;break;case"optical":l=En;break;case"annual":l=_n;break;default:l=a.promotionCode?yn:vn}let d=n(a);d.literals=Object.assign({},e.price,Ye(a.literals??{}));let[u]=c;return u={...u,...u.priceDetails},l(d,u)}let{createInlinePrice:i,getInlinePrices:s}=Fn;return{InlinePrice:Fn,buildPriceHTML:o,collectPriceOptions:n,createInlinePrice:i,getInlinePrices:s}}function Di({settings:e}){let t=F.module("wcs"),{env:r,wcsApiKey:n}=e,o=new Map,i=new Map,s;async function c(l,d,u=!0){let m=An;t.debug("Fetching:",l);try{l.offerSelectorIds=l.offerSelectorIds.sort();let f=new URL(e.wcsURL);f.searchParams.set("offer_selector_ids",l.offerSelectorIds.join(",")),f.searchParams.set("country",l.country),f.searchParams.set("locale",l.locale),f.searchParams.set("landscape",r===$e.STAGE?"ALL":e.landscape),f.searchParams.set("api_key",n),l.language&&f.searchParams.set("language",l.language),l.promotionCode&&f.searchParams.set("promotion_code",l.promotionCode),l.currency&&f.searchParams.set("currency",l.currency);let g=await fetch(f.toString(),{credentials:"omit"});if(g.ok){let _=await g.json();t.debug("Fetched:",l,_);let P=_.resolvedOffers??[];P=P.map(Kr),d.forEach(({resolve:C},A)=>{let I=P.filter(({offerSelectorIds:S})=>S.includes(A)).flat();I.length&&(d.delete(A),C(I))})}else g.status===404&&l.offerSelectorIds.length>1?(t.debug("Multi-osi 404, fallback to fetch-by-one strategy"),await Promise.allSettled(l.offerSelectorIds.map(_=>c({...l,offerSelectorIds:[_]},d,!1)))):(m=nr,t.error(m,l))}catch(f){m=nr,t.error(m,l,f)}u&&d.size&&(t.debug("Missing:",{offerSelectorIds:[...d.keys()]}),d.forEach(f=>{f.reject(new Error(m))}))}function a(){clearTimeout(s);let l=[...i.values()];i.clear(),l.forEach(({options:d,promises:u})=>c(d,u))}function h({country:l,language:d,perpetual:u=!1,promotionCode:m="",wcsOsi:f=[]}){let g=`${d}_${l}`;l!=="GB"&&(d=u?"EN":"MULT");let _=[l,d,m].filter(P=>P).join("-").toLowerCase();return f.map(P=>{let C=`${P}-${_}`;if(!o.has(C)){let A=new Promise((I,S)=>{let $=i.get(_);if(!$){let z={country:l,locale:g,offerSelectorIds:[]};l!=="GB"&&(z.language=d),$={options:z,promises:new Map},i.set(_,$)}m&&($.options.promotionCode=m),$.options.offerSelectorIds.push(P),$.promises.set(P,{resolve:I,reject:S}),$.options.offerSelectorIds.length>=e.wcsBufferLimit?a():(t.debug("Queued:",$.options),s||(s=setTimeout(a,e.wcsBufferDelay)))});o.set(C,A)}return o.get(C)})}return{WcsCommitment:Ei,WcsPlanType:bi,WcsTerm:_i,resolveOfferSelectors:h}}var j=class extends HTMLElement{get isWcmsCommerce(){return!0}};O(j,"instance"),O(j,"promise",null);window.customElements.define(fe,j);async function Rc(e,t){let r=F.init(e.env).module("service");r.debug("Activating:",e);let n={price:{}},o=Object.freeze(Ti(e));try{n.price=await Mi(o)}catch(a){r.warn("Price literals were not fetched:",a)}let i={checkout:new Set,price:new Set},s=document.createElement(fe),c={literals:n,providers:i,settings:o};return j.instance=Object.defineProperties(s,Object.getOwnPropertyDescriptors({...Ii(c,t),...ki(c),...Ui(c),...Di(c),...Ln,Log:F,get defaults(){return b},get literals(){return n},get log(){return F},get providers(){return{checkout(a){return i.checkout.add(a),()=>i.checkout.delete(a)},price(a){return i.price.add(a),()=>i.price.delete(a)}}},get settings(){return o}})),r.debug("Activated:",{literals:n,settings:o,element:s}),document.head.append(s),ge(()=>{let a=new CustomEvent(Ze,{bubbles:!0,cancelable:!1,detail:j.instance});j.instance.dispatchEvent(a)}),j.instance}function Fi(){document.head.querySelector(fe)?.remove(),j.promise=null,F.reset()}function bt(e,t){let r=me(e)?e():null,n=me(t)?t():{};return r&&(n.force&&Fi(),Rc(r,n).then(o=>{bt.resolve(o)})),j.promise??(j.promise=new Promise(o=>{bt.resolve=o})),j.promise}var xr=window,yr=xr.ShadowRoot&&(xr.ShadyCSS===void 0||xr.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,zi=Symbol(),Hi=new WeakMap,vr=class{constructor(t,r,n){if(this._$cssResult$=!0,n!==zi)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=r}get styleSheet(){let t=this.o,r=this.t;if(yr&&t===void 0){let n=r!==void 0&&r.length===1;n&&(t=Hi.get(r)),t===void 0&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),n&&Hi.set(r,t))}return t}toString(){return this.cssText}},Gi=e=>new vr(typeof e=="string"?e:e+"",void 0,zi);var Hn=(e,t)=>{yr?e.adoptedStyleSheets=t.map(r=>r instanceof CSSStyleSheet?r:r.styleSheet):t.forEach(r=>{let n=document.createElement("style"),o=xr.litNonce;o!==void 0&&n.setAttribute("nonce",o),n.textContent=r.cssText,e.appendChild(n)})},Er=yr?e=>e:e=>e instanceof CSSStyleSheet?(t=>{let r="";for(let n of t.cssRules)r+=n.cssText;return Gi(r)})(e):e;var zn,br=window,Vi=br.trustedTypes,Ic=Vi?Vi.emptyScript:"",ji=br.reactiveElementPolyfillSupport,Vn={toAttribute(e,t){switch(t){case Boolean:e=e?Ic:null;break;case Object:case Array:e=e==null?e:JSON.stringify(e)}return e},fromAttribute(e,t){let r=e;switch(t){case Boolean:r=e!==null;break;case Number:r=e===null?null:Number(e);break;case Object:case Array:try{r=JSON.parse(e)}catch{r=null}}return r}},Bi=(e,t)=>t!==e&&(t==t||e==e),Gn={attribute:!0,type:String,converter:Vn,reflect:!1,hasChanged:Bi},jn="finalized",Le=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this._$Eu()}static addInitializer(t){var r;this.finalize(),((r=this.h)!==null&&r!==void 0?r:this.h=[]).push(t)}static get observedAttributes(){this.finalize();let t=[];return this.elementProperties.forEach((r,n)=>{let o=this._$Ep(n,r);o!==void 0&&(this._$Ev.set(o,n),t.push(o))}),t}static createProperty(t,r=Gn){if(r.state&&(r.attribute=!1),this.finalize(),this.elementProperties.set(t,r),!r.noAccessor&&!this.prototype.hasOwnProperty(t)){let n=typeof t=="symbol"?Symbol():"__"+t,o=this.getPropertyDescriptor(t,n,r);o!==void 0&&Object.defineProperty(this.prototype,t,o)}}static getPropertyDescriptor(t,r,n){return{get(){return this[r]},set(o){let i=this[t];this[r]=o,this.requestUpdate(t,i,n)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||Gn}static finalize(){if(this.hasOwnProperty(jn))return!1;this[jn]=!0;let t=Object.getPrototypeOf(this);if(t.finalize(),t.h!==void 0&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){let r=this.properties,n=[...Object.getOwnPropertyNames(r),...Object.getOwnPropertySymbols(r)];for(let o of n)this.createProperty(o,r[o])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){let r=[];if(Array.isArray(t)){let n=new Set(t.flat(1/0).reverse());for(let o of n)r.unshift(Er(o))}else t!==void 0&&r.push(Er(t));return r}static _$Ep(t,r){let n=r.attribute;return n===!1?void 0:typeof n=="string"?n:typeof t=="string"?t.toLowerCase():void 0}_$Eu(){var t;this._$E_=new Promise(r=>this.enableUpdating=r),this._$AL=new Map,this._$Eg(),this.requestUpdate(),(t=this.constructor.h)===null||t===void 0||t.forEach(r=>r(this))}addController(t){var r,n;((r=this._$ES)!==null&&r!==void 0?r:this._$ES=[]).push(t),this.renderRoot!==void 0&&this.isConnected&&((n=t.hostConnected)===null||n===void 0||n.call(t))}removeController(t){var r;(r=this._$ES)===null||r===void 0||r.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach((t,r)=>{this.hasOwnProperty(r)&&(this._$Ei.set(r,this[r]),delete this[r])})}createRenderRoot(){var t;let r=(t=this.shadowRoot)!==null&&t!==void 0?t:this.attachShadow(this.constructor.shadowRootOptions);return Hn(r,this.constructor.elementStyles),r}connectedCallback(){var t;this.renderRoot===void 0&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),(t=this._$ES)===null||t===void 0||t.forEach(r=>{var n;return(n=r.hostConnected)===null||n===void 0?void 0:n.call(r)})}enableUpdating(t){}disconnectedCallback(){var t;(t=this._$ES)===null||t===void 0||t.forEach(r=>{var n;return(n=r.hostDisconnected)===null||n===void 0?void 0:n.call(r)})}attributeChangedCallback(t,r,n){this._$AK(t,n)}_$EO(t,r,n=Gn){var o;let i=this.constructor._$Ep(t,n);if(i!==void 0&&n.reflect===!0){let s=(((o=n.converter)===null||o===void 0?void 0:o.toAttribute)!==void 0?n.converter:Vn).toAttribute(r,n.type);this._$El=t,s==null?this.removeAttribute(i):this.setAttribute(i,s),this._$El=null}}_$AK(t,r){var n;let o=this.constructor,i=o._$Ev.get(t);if(i!==void 0&&this._$El!==i){let s=o.getPropertyOptions(i),c=typeof s.converter=="function"?{fromAttribute:s.converter}:((n=s.converter)===null||n===void 0?void 0:n.fromAttribute)!==void 0?s.converter:Vn;this._$El=i,this[i]=c.fromAttribute(r,s.type),this._$El=null}}requestUpdate(t,r,n){let o=!0;t!==void 0&&(((n=n||this.constructor.getPropertyOptions(t)).hasChanged||Bi)(this[t],r)?(this._$AL.has(t)||this._$AL.set(t,r),n.reflect===!0&&this._$El!==t&&(this._$EC===void 0&&(this._$EC=new Map),this._$EC.set(t,n))):o=!1),!this.isUpdatePending&&o&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(r){Promise.reject(r)}let t=this.scheduleUpdate();return t!=null&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach((o,i)=>this[i]=o),this._$Ei=void 0);let r=!1,n=this._$AL;try{r=this.shouldUpdate(n),r?(this.willUpdate(n),(t=this._$ES)===null||t===void 0||t.forEach(o=>{var i;return(i=o.hostUpdate)===null||i===void 0?void 0:i.call(o)}),this.update(n)):this._$Ek()}catch(o){throw r=!1,this._$Ek(),o}r&&this._$AE(n)}willUpdate(t){}_$AE(t){var r;(r=this._$ES)===null||r===void 0||r.forEach(n=>{var o;return(o=n.hostUpdated)===null||o===void 0?void 0:o.call(n)}),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){this._$EC!==void 0&&(this._$EC.forEach((r,n)=>this._$EO(n,this[n],r)),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}};Le[jn]=!0,Le.elementProperties=new Map,Le.elementStyles=[],Le.shadowRootOptions={mode:"open"},ji?.({ReactiveElement:Le}),((zn=br.reactiveElementVersions)!==null&&zn!==void 0?zn:br.reactiveElementVersions=[]).push("1.6.3");var Bn,_r=window,tt=_r.trustedTypes,Wi=tt?tt.createPolicy("lit-html",{createHTML:e=>e}):void 0,qn="$lit$",xe=`lit$${(Math.random()+"").slice(9)}$`,Ki="?"+xe,kc=`<${Ki}>`,Ie=document,wr=()=>Ie.createComment(""),wt=e=>e===null||typeof e!="object"&&typeof e!="function",es=Array.isArray,Mc=e=>es(e)||typeof e?.[Symbol.iterator]=="function",Wn=`[ +\f\r]`,_t=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,qi=/-->/g,Yi=/>/g,Ne=RegExp(`>|${Wn}(?:([^\\s"'>=/]+)(${Wn}*=${Wn}*(?:[^ +\f\r"'\`<>=]|("|')|))|$)`,"g"),Xi=/'/g,Zi=/"/g,ts=/^(?:script|style|textarea|title)$/i,rs=e=>(t,...r)=>({_$litType$:e,strings:t,values:r}),nu=rs(1),ou=rs(2),At=Symbol.for("lit-noChange"),M=Symbol.for("lit-nothing"),Ji=new WeakMap,Re=Ie.createTreeWalker(Ie,129,null,!1);function ns(e,t){if(!Array.isArray(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return Wi!==void 0?Wi.createHTML(t):t}var Uc=(e,t)=>{let r=e.length-1,n=[],o,i=t===2?"":"",s=_t;for(let c=0;c"?(s=o??_t,d=-1):l[1]===void 0?d=-2:(d=s.lastIndex-l[2].length,h=l[1],s=l[3]===void 0?Ne:l[3]==='"'?Zi:Xi):s===Zi||s===Xi?s=Ne:s===qi||s===Yi?s=_t:(s=Ne,o=void 0);let m=s===Ne&&e[c+1].startsWith("/>")?" ":"";i+=s===_t?a+kc:d>=0?(n.push(h),a.slice(0,d)+qn+a.slice(d)+xe+m):a+xe+(d===-2?(n.push(void 0),c):m)}return[ns(e,i+(e[r]||"")+(t===2?"":"")),n]},St=class e{constructor({strings:t,_$litType$:r},n){let o;this.parts=[];let i=0,s=0,c=t.length-1,a=this.parts,[h,l]=Uc(t,r);if(this.el=e.createElement(h,n),Re.currentNode=this.el.content,r===2){let d=this.el.content,u=d.firstChild;u.remove(),d.append(...u.childNodes)}for(;(o=Re.nextNode())!==null&&a.length0){o.textContent=tt?tt.emptyScript:"";for(let m=0;m2||n[0]!==""||n[1]!==""?(this._$AH=Array(n.length-1).fill(new String),this.strings=n):this._$AH=M}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,r=this,n,o){let i=this.strings,s=!1;if(i===void 0)t=rt(this,t,r,0),s=!wt(t)||t!==this._$AH&&t!==At,s&&(this._$AH=t);else{let c=t,a,h;for(t=i[0],a=0;anew Tt(typeof e=="string"?e:e+"",void 0,Kn),ke=(e,...t)=>{let r=e.length===1?e[0]:t.reduce((n,o,i)=>n+(s=>{if(s._$cssResult$===!0)return s.cssText;if(typeof s=="number")return s;throw Error("Value passed to 'css' function must be a 'css' function result: "+s+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(o)+e[i+1],e[0]);return new Tt(r,e,Kn)},eo=(e,t)=>{Tr?e.adoptedStyleSheets=t.map(r=>r instanceof CSSStyleSheet?r:r.styleSheet):t.forEach(r=>{let n=document.createElement("style"),o=Sr.litNonce;o!==void 0&&n.setAttribute("nonce",o),n.textContent=r.cssText,e.appendChild(n)})},Pr=Tr?e=>e:e=>e instanceof CSSStyleSheet?(t=>{let r="";for(let n of t.cssRules)r+=n.cssText;return ve(r)})(e):e;var to,Cr=window,is=Cr.trustedTypes,Fc=is?is.emptyScript:"",ss=Cr.reactiveElementPolyfillSupport,no={toAttribute(e,t){switch(t){case Boolean:e=e?Fc:null;break;case Object:case Array:e=e==null?e:JSON.stringify(e)}return e},fromAttribute(e,t){let r=e;switch(t){case Boolean:r=e!==null;break;case Number:r=e===null?null:Number(e);break;case Object:case Array:try{r=JSON.parse(e)}catch{r=null}}return r}},as=(e,t)=>t!==e&&(t==t||e==e),ro={attribute:!0,type:String,converter:no,reflect:!1,hasChanged:as},oo="finalized",he=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this._$Eu()}static addInitializer(t){var r;this.finalize(),((r=this.h)!==null&&r!==void 0?r:this.h=[]).push(t)}static get observedAttributes(){this.finalize();let t=[];return this.elementProperties.forEach((r,n)=>{let o=this._$Ep(n,r);o!==void 0&&(this._$Ev.set(o,n),t.push(o))}),t}static createProperty(t,r=ro){if(r.state&&(r.attribute=!1),this.finalize(),this.elementProperties.set(t,r),!r.noAccessor&&!this.prototype.hasOwnProperty(t)){let n=typeof t=="symbol"?Symbol():"__"+t,o=this.getPropertyDescriptor(t,n,r);o!==void 0&&Object.defineProperty(this.prototype,t,o)}}static getPropertyDescriptor(t,r,n){return{get(){return this[r]},set(o){let i=this[t];this[r]=o,this.requestUpdate(t,i,n)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||ro}static finalize(){if(this.hasOwnProperty(oo))return!1;this[oo]=!0;let t=Object.getPrototypeOf(this);if(t.finalize(),t.h!==void 0&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){let r=this.properties,n=[...Object.getOwnPropertyNames(r),...Object.getOwnPropertySymbols(r)];for(let o of n)this.createProperty(o,r[o])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){let r=[];if(Array.isArray(t)){let n=new Set(t.flat(1/0).reverse());for(let o of n)r.unshift(Pr(o))}else t!==void 0&&r.push(Pr(t));return r}static _$Ep(t,r){let n=r.attribute;return n===!1?void 0:typeof n=="string"?n:typeof t=="string"?t.toLowerCase():void 0}_$Eu(){var t;this._$E_=new Promise(r=>this.enableUpdating=r),this._$AL=new Map,this._$Eg(),this.requestUpdate(),(t=this.constructor.h)===null||t===void 0||t.forEach(r=>r(this))}addController(t){var r,n;((r=this._$ES)!==null&&r!==void 0?r:this._$ES=[]).push(t),this.renderRoot!==void 0&&this.isConnected&&((n=t.hostConnected)===null||n===void 0||n.call(t))}removeController(t){var r;(r=this._$ES)===null||r===void 0||r.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach((t,r)=>{this.hasOwnProperty(r)&&(this._$Ei.set(r,this[r]),delete this[r])})}createRenderRoot(){var t;let r=(t=this.shadowRoot)!==null&&t!==void 0?t:this.attachShadow(this.constructor.shadowRootOptions);return eo(r,this.constructor.elementStyles),r}connectedCallback(){var t;this.renderRoot===void 0&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),(t=this._$ES)===null||t===void 0||t.forEach(r=>{var n;return(n=r.hostConnected)===null||n===void 0?void 0:n.call(r)})}enableUpdating(t){}disconnectedCallback(){var t;(t=this._$ES)===null||t===void 0||t.forEach(r=>{var n;return(n=r.hostDisconnected)===null||n===void 0?void 0:n.call(r)})}attributeChangedCallback(t,r,n){this._$AK(t,n)}_$EO(t,r,n=ro){var o;let i=this.constructor._$Ep(t,n);if(i!==void 0&&n.reflect===!0){let s=(((o=n.converter)===null||o===void 0?void 0:o.toAttribute)!==void 0?n.converter:no).toAttribute(r,n.type);this._$El=t,s==null?this.removeAttribute(i):this.setAttribute(i,s),this._$El=null}}_$AK(t,r){var n;let o=this.constructor,i=o._$Ev.get(t);if(i!==void 0&&this._$El!==i){let s=o.getPropertyOptions(i),c=typeof s.converter=="function"?{fromAttribute:s.converter}:((n=s.converter)===null||n===void 0?void 0:n.fromAttribute)!==void 0?s.converter:no;this._$El=i,this[i]=c.fromAttribute(r,s.type),this._$El=null}}requestUpdate(t,r,n){let o=!0;t!==void 0&&(((n=n||this.constructor.getPropertyOptions(t)).hasChanged||as)(this[t],r)?(this._$AL.has(t)||this._$AL.set(t,r),n.reflect===!0&&this._$El!==t&&(this._$EC===void 0&&(this._$EC=new Map),this._$EC.set(t,n))):o=!1),!this.isUpdatePending&&o&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(r){Promise.reject(r)}let t=this.scheduleUpdate();return t!=null&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach((o,i)=>this[i]=o),this._$Ei=void 0);let r=!1,n=this._$AL;try{r=this.shouldUpdate(n),r?(this.willUpdate(n),(t=this._$ES)===null||t===void 0||t.forEach(o=>{var i;return(i=o.hostUpdate)===null||i===void 0?void 0:i.call(o)}),this.update(n)):this._$Ek()}catch(o){throw r=!1,this._$Ek(),o}r&&this._$AE(n)}willUpdate(t){}_$AE(t){var r;(r=this._$ES)===null||r===void 0||r.forEach(n=>{var o;return(o=n.hostUpdated)===null||o===void 0?void 0:o.call(n)}),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){this._$EC!==void 0&&(this._$EC.forEach((r,n)=>this._$EO(n,this[n],r)),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}};he[oo]=!0,he.elementProperties=new Map,he.elementStyles=[],he.shadowRootOptions={mode:"open"},ss?.({ReactiveElement:he}),((to=Cr.reactiveElementVersions)!==null&&to!==void 0?to:Cr.reactiveElementVersions=[]).push("1.6.3");var io,$r=window,ot=$r.trustedTypes,cs=ot?ot.createPolicy("lit-html",{createHTML:e=>e}):void 0,ao="$lit$",ye=`lit$${(Math.random()+"").slice(9)}$`,fs="?"+ye,Hc=`<${fs}>`,De=document,Ct=()=>De.createComment(""),$t=e=>e===null||typeof e!="object"&&typeof e!="function",gs=Array.isArray,zc=e=>gs(e)||typeof e?.[Symbol.iterator]=="function",so=`[ +\f\r]`,Pt=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,ls=/-->/g,hs=/>/g,Me=RegExp(`>|${so}(?:([^\\s"'>=/]+)(${so}*=${so}*(?:[^ +\f\r"'\`<>=]|("|')|))|$)`,"g"),ds=/'/g,us=/"/g,xs=/^(?:script|style|textarea|title)$/i,vs=e=>(t,...r)=>({_$litType$:e,strings:t,values:r}),x=vs(1),hu=vs(2),Fe=Symbol.for("lit-noChange"),U=Symbol.for("lit-nothing"),ms=new WeakMap,Ue=De.createTreeWalker(De,129,null,!1);function ys(e,t){if(!Array.isArray(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return cs!==void 0?cs.createHTML(t):t}var Gc=(e,t)=>{let r=e.length-1,n=[],o,i=t===2?"":"",s=Pt;for(let c=0;c"?(s=o??Pt,d=-1):l[1]===void 0?d=-2:(d=s.lastIndex-l[2].length,h=l[1],s=l[3]===void 0?Me:l[3]==='"'?us:ds):s===us||s===ds?s=Me:s===ls||s===hs?s=Pt:(s=Me,o=void 0);let m=s===Me&&e[c+1].startsWith("/>")?" ":"";i+=s===Pt?a+Hc:d>=0?(n.push(h),a.slice(0,d)+ao+a.slice(d)+ye+m):a+ye+(d===-2?(n.push(void 0),c):m)}return[ys(e,i+(e[r]||"")+(t===2?"":"")),n]},Ot=class e{constructor({strings:t,_$litType$:r},n){let o;this.parts=[];let i=0,s=0,c=t.length-1,a=this.parts,[h,l]=Gc(t,r);if(this.el=e.createElement(h,n),Ue.currentNode=this.el.content,r===2){let d=this.el.content,u=d.firstChild;u.remove(),d.append(...u.childNodes)}for(;(o=Ue.nextNode())!==null&&a.length0){o.textContent=ot?ot.emptyScript:"";for(let m=0;m2||n[0]!==""||n[1]!==""?(this._$AH=Array(n.length-1).fill(new String),this.strings=n):this._$AH=U}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,r=this,n,o){let i=this.strings,s=!1;if(i===void 0)t=it(this,t,r,0),s=!$t(t)||t!==this._$AH&&t!==Fe,s&&(this._$AH=t);else{let c=t,a,h;for(t=i[0],a=0;a{var n,o;let i=(n=r?.renderBefore)!==null&&n!==void 0?n:t,s=i._$litPart$;if(s===void 0){let c=(o=r?.renderBefore)!==null&&o!==void 0?o:null;i._$litPart$=s=new Lt(t.insertBefore(Ct(),c),c,void 0,r??{})}return s._$AI(e),s};var po,fo;var oe=class extends he{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){var t,r;let n=super.createRenderRoot();return(t=(r=this.renderOptions).renderBefore)!==null&&t!==void 0||(r.renderBefore=n.firstChild),n}update(t){let r=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=Es(r,this.renderRoot,this.renderOptions)}connectedCallback(){var t;super.connectedCallback(),(t=this._$Do)===null||t===void 0||t.setConnected(!0)}disconnectedCallback(){var t;super.disconnectedCallback(),(t=this._$Do)===null||t===void 0||t.setConnected(!1)}render(){return Fe}};oe.finalized=!0,oe._$litElement$=!0,(po=globalThis.litElementHydrateSupport)===null||po===void 0||po.call(globalThis,{LitElement:oe});var bs=globalThis.litElementPolyfillSupport;bs?.({LitElement:oe});((fo=globalThis.litElementVersions)!==null&&fo!==void 0?fo:globalThis.litElementVersions=[]).push("3.3.3");var Nt="(max-width: 767px)",Rt="(max-width: 1199px)",B="(min-width: 768px)",H="(min-width: 1200px)",q="(min-width: 1600px)";var _s=ke` + :host { + position: relative; + display: flex; + flex-direction: column; + text-align: start; + background-color: var(--consonant-merch-card-background-color); + grid-template-columns: repeat(auto-fit, minmax(300px, max-content)); + background-color: var(--consonant-merch-card-background-color); + font-family: var(--body-font-family, 'Adobe Clean'); + border-radius: var(--consonant-merch-spacing-xs); + border: 1px solid var(--consonant-merch-card-border-color); + box-sizing: border-box; + } + + :host(.placeholder) { + visibility: hidden; + } + + :host([variant='special-offers']) { + min-height: 439px; + } + + :host([variant='catalog']) { + min-height: 330px; + } + + :host([variant='plans']) { + min-height: 348px; + } + + :host([variant='segment']) { + min-height: 214px; + } + + :host([variant='ccd-action']:not([size])) { + width: var(--consonant-merch-card-ccd-action-width); + } + + :host([aria-selected]) { + outline: none; + box-sizing: border-box; + box-shadow: inset 0 0 0 2px var(--color-accent); + } + + .invisible { + visibility: hidden; + } + + :host(:hover) .invisible { + visibility: visible; + background-image: var(--ellipsis-icon); + cursor: pointer; + } + + .action-menu.always-visible { + visibility: visible; + background-image: var(--ellipsis-icon); + } + + :host([variant='mini-compare-chart']) > slot:not([name='icons']) { + display: block; + } + + .top-section { + display: flex; + justify-content: flex-start; + align-items: flex-start; + gap: 16px; + } + + .top-section.badge { + min-height: 32px; + } + + .body { + flex: 1; + display: flex; + flex-direction: column; + justify-content: flex-start; + height: 100%; + gap: var(--consonant-merch-spacing-xxs); + padding: var(--consonant-merch-spacing-xs); + } + + footer { + display: flex; + justify-content: flex-end; + box-sizing: border-box; + align-items: flex-end; + width: 100%; + flex-flow: wrap; + gap: var(--consonant-merch-spacing-xs); + + padding: var(--consonant-merch-spacing-xs); + } + + hr { + background-color: var(--color-gray-200); + border: none; + height: 1px; + width: auto; + margin-top: 0; + margin-bottom: 0; + margin-left: var(--consonant-merch-spacing-xs); + margin-right: var(--consonant-merch-spacing-xs); + } + + div[class$='-badge'] { + position: absolute; + top: 16px; + right: 0; + font-size: var(--type-heading-xxs-size); + font-weight: 500; + max-width: 180px; + line-height: 16px; + text-align: center; + padding: 8px 11px; + border-radius: 5px 0 0 5px; + } + + div[class$='-badge']:dir(rtl) { + left: 0; + right: initial; + padding: 8px 11px; + border-radius: 0 5px 5px 0; + } + + .body .catalog-badge { + display: flex; + height: fit-content; + flex-direction: column; + width: fit-content; + max-width: 140px; + border-radius: 5px; + position: relative; + top: 0; + margin-left: var(--consonant-merch-spacing-xxs); + box-sizing: border-box; + } + + .detail-bg-container { + right: 0; + padding: var(--consonant-merch-spacing-xs); + border-radius: 5px; + font-size: var(--consonant-merch-card-body-font-size); + margin: var(--consonant-merch-spacing-xs); + } + + .action-menu { + display: flex; + width: 32px; + height: 32px; + position: absolute; + top: 16px; + right: 16px; + background-color: #f6f6f6; + background-repeat: no-repeat; + background-position: center; + background-size: 16px 16px; + } + .hidden { + visibility: hidden; + } + + #stock-checkbox, + .secure-transaction-label { + font-size: var(--consonant-merch-card-body-xxs-font-size); + line-height: 1.3; + color: var(--color-gray-600); + } + + #stock-checkbox { + display: inline-flex; + align-items: center; + cursor: pointer; + gap: 10px; /*same as spectrum */ + } + + #stock-checkbox > input { + display: none; + } + + #stock-checkbox > span { + display: inline-block; + box-sizing: border-box; + border: 2px solid rgb(117, 117, 117); + border-radius: 2px; + width: 14px; + height: 14px; + } + + #stock-checkbox > input:checked + span { + background: var(--checkmark-icon) no-repeat var(--color-accent); + border-color: var(--color-accent); + } + + .secure-transaction-label { + white-space: nowrap; + display: inline-flex; + gap: var(--consonant-merch-spacing-xxs); + align-items: center; + flex: 1; + line-height: normal; + } + + .secure-transaction-label::before { + display: inline-block; + content: ''; + width: 12px; + height: 15px; + background: var(--secure-icon) no-repeat; + background-position: center; + background-size: contain; + } + + .checkbox-container { + display: flex; + align-items: center; + gap: var(--consonant-merch-spacing-xxs); + } + + .checkbox-container input[type='checkbox']:checked + .checkmark { + background-color: var(--color-accent); + background-image: var(--checkmark-icon); + border-color: var(--color-accent); + } + + .checkbox-container input[type='checkbox'] { + display: none; + } + + .checkbox-container .checkmark { + position: relative; + display: inline-block; + width: 12px; + height: 12px; + border: 2px solid #757575; + background: #fff; + border-radius: 2px; + cursor: pointer; + margin-top: 2px; + } + + .twp-badge { + padding: 4px 10px 5px 10px; + } + + :host([aria-selected]) .twp-badge { + margin-inline-end: 2px; + padding-inline-end: 9px; + } + + :host([variant='twp']) { + padding: 4px 10px 5px 10px; + } + + slot[name='icons'] { + display: flex; + gap: 8px; + } + + :host([variant='twp']) ::slotted(merch-offer-select) { + display: none; + } + + :host([variant='twp']) .top-section { + flex: 0; + display: flex; + flex-direction: column; + justify-content: flex-start; + height: 100%; + gap: var(--consonant-merch-spacing-xxs); + padding: var(--consonant-merch-spacing-xs) + var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-xs) + var(--consonant-merch-spacing-xs); + align-items: flex-start; + } + + :host([variant='twp']) .body { + padding: 0 var(--consonant-merch-spacing-xs); + } + + :host([variant='twp']) footer { + gap: var(--consonant-merch-spacing-xxs); + flex-direction: column; + align-self: flex-start; + } + + :host([variant='special-offers'].center) { + text-align: center; + } + + /* plans */ + :host([variant='plans']) { + min-height: 348px; + } + + :host([variant='mini-compare-chart']) footer { + min-height: var(--consonant-merch-card-mini-compare-footer-height); + padding: var(--consonant-merch-spacing-xs); + } + + /* mini-compare card */ + :host([variant='mini-compare-chart']) .top-section { + padding-top: var(--consonant-merch-spacing-s); + padding-inline-start: var(--consonant-merch-spacing-s); + height: var(--consonant-merch-card-mini-compare-top-section-height); + } + + @media screen and ${ve(Rt)} { + [class*'-merch-cards'] :host([variant='mini-compare-chart']) footer { + flex-direction: column; + align-items: stretch; + text-align: center; + } + } + + @media screen and ${ve(H)} { + :host([variant='mini-compare-chart']) footer { + padding: var(--consonant-merch-spacing-xs) + var(--consonant-merch-spacing-s) + var(--consonant-merch-spacing-s) + var(--consonant-merch-spacing-s); + } + } + + :host([variant='mini-compare-chart']) slot[name='footer-rows'] { + flex: 1; + display: flex; + flex-direction: column; + justify-content: end; + } + /* mini-compare card heights for the slots: heading-m, body-m, heading-m-price, price-commitment, offers, promo-text, footer */ + :host([variant='mini-compare-chart']) slot[name='heading-m'] { + min-height: var(--consonant-merch-card-mini-compare-heading-m-height); + } + :host([variant='mini-compare-chart']) slot[name='body-m'] { + min-height: var(--consonant-merch-card-mini-compare-body-m-height); + } + :host([variant='mini-compare-chart']) slot[name='heading-m-price'] { + min-height: var( + --consonant-merch-card-mini-compare-heading-m-price-height + ); + } + :host([variant='mini-compare-chart']) slot[name='price-commitment'] { + min-height: var( + --consonant-merch-card-mini-compare-price-commitment-height + ); + } + :host([variant='mini-compare-chart']) slot[name='offers'] { + min-height: var(--consonant-merch-card-mini-compare-offers-height); + } + :host([variant='mini-compare-chart']) slot[name='promo-text'] { + min-height: var(--consonant-merch-card-mini-compare-promo-text-height); + } + :host([variant='mini-compare-chart']) slot[name='callout-content'] { + min-height: var( + --consonant-merch-card-mini-compare-callout-content-height + ); + } + + :host([variant='plans']) ::slotted([slot='heading-xs']), + :host([variant='segment']) ::slotted([slot='heading-xs']) { + max-width: var(--consonant-merch-card-heading-xs-max-width, 100%); + } +`,ws=()=>{let e=[ke` + /* Tablet */ + @media screen and ${ve(B)} { + :host([size='wide']), + :host([size='super-wide']) { + grid-column: span 3; + width: 100%; + max-width: var(--consonant-merch-card-tablet-wide-width); + margin: 0 auto; + } + } + + /* Laptop */ + @media screen and ${ve(H)} { + :host([size='super-wide']) { + grid-column: span 3; + } + `];return e.push(ke` + /* Large desktop */ + @media screen and ${ve(q)} { + :host([size='super-wide']) { + grid-column: span 4; + } + } + `),e};function Ee(e,t={},r){let n=document.createElement(e);r instanceof HTMLElement?n.appendChild(r):n.innerHTML=r;for(let[o,i]of Object.entries(t))n.setAttribute(o,i);return n}function As(){return window.matchMedia("(max-width: 767px)").matches}function Ss(){return window.matchMedia("(max-width: 1024px)").matches}function Ts(e=1e3){return new Promise(t=>setTimeout(t,e))}var Ps=document.createElement("style");Ps.innerHTML=` +:root { + --consonant-merch-card-detail-font-size: 12px; + --consonant-merch-card-detail-font-weight: 500; + --consonant-merch-card-detail-letter-spacing: 0.8px; + --consonant-merch-card-background-color: #fff; + + --consonant-merch-card-heading-font-size: 18px; + --consonant-merch-card-heading-line-height: 22.5px; + --consonant-merch-card-heading-secondary-font-size: 14px; + --consonant-merch-card-body-font-size: 14px; + --consonant-merch-card-body-line-height: 21px; + --consonant-merch-card-promo-text-height: var(--consonant-merch-card-body-font-size); + + /* responsive width */ + --consonant-merch-card-mobile-width: 300px; + --consonant-merch-card-tablet-wide-width: 700px; + + /* spacing */ + --consonant-merch-spacing-xxxs: 4px; + --consonant-merch-spacing-xxs: 8px; + --consonant-merch-spacing-xs: 16px; + --consonant-merch-spacing-s: 24px; + --consonant-merch-spacing-m: 32px; + + /* cta */ + --consonant-merch-card-cta-font-size: 15px; + + /* headings */ + --consonant-merch-card-heading-xs-font-size: 18px; + --consonant-merch-card-heading-xs-line-height: 22.5px; + --consonant-merch-card-heading-s-font-size: 20px; + --consonant-merch-card-heading-s-line-height: 25px; + --consonant-merch-card-heading-m-font-size: 24px; + --consonant-merch-card-heading-m-line-height: 30px; + --consonant-merch-card-heading-l-font-size: 20px; + --consonant-merch-card-heading-l-line-height: 30px; + --consonant-merch-card-heading-xl-font-size: 36px; + --consonant-merch-card-heading-xl-line-height: 45px; + + /* detail */ + --consonant-merch-card-detail-m-font-size: 12px; + --consonant-merch-card-detail-m-line-height: 15px; + --consonant-merch-card-detail-m-font-weight: 700; + --consonant-merch-card-detail-m-letter-spacing: 1px; + + /* body */ + --consonant-merch-card-body-xxs-font-size: 12px; + --consonant-merch-card-body-xxs-line-height: 18px; + --consonant-merch-card-body-xxs-letter-spacing: 1px; + --consonant-merch-card-body-xs-font-size: 14px; + --consonant-merch-card-body-xs-line-height: 21px; + --consonant-merch-card-body-s-font-size: 16px; + --consonant-merch-card-body-s-line-height: 24px; + --consonant-merch-card-body-m-font-size: 18px; + --consonant-merch-card-body-m-line-height: 27px; + --consonant-merch-card-body-l-font-size: 20px; + --consonant-merch-card-body-l-line-height: 30px; + --consonant-merch-card-body-xl-font-size: 22px; + --consonant-merch-card-body-xl-line-height: 33px; + + + --consonant-merch-card-heading-padding: 0; + --consonant-merch-card-image-height: 180px; + + /* colors */ + --consonant-merch-card-border-color: #eaeaea; + --color-accent: #1473E6; + --merch-color-focus-ring: #1473E6; + --merch-color-grey-80: #2c2c2c; + --merch-color-green-promo: #2D9D78; + + /* merch card generic */ + --consonant-merch-card-max-width: 300px; + --transition: cmax-height 0.3s linear, opacity 0.3s linear; + + /* special offers */ + --consonant-merch-card-special-offers-width: 378px; + + /* image */ + --consonant-merch-card-image-width: 300px; + + /* segment */ + --consonant-merch-card-segment-width: 378px; + + /* inline-heading */ + --consonant-merch-card-inline-heading-width: 300px; + + /* product */ + --consonant-merch-card-product-width: 300px; + + /* plans */ + --consonant-merch-card-plans-width: 300px; + --consonant-merch-card-plans-icon-size: 40px; + + /* catalog */ + --consonant-merch-card-catalog-width: 276px; + --consonant-merch-card-catalog-icon-size: 40px; + + /* twp */ + --consonant-merch-card-twp-width: 268px; + --consonant-merch-card-twp-mobile-width: 300px; + --consonant-merch-card-twp-mobile-height: 358px; + + /* ccd-action */ + --consonant-merch-card-ccd-action-width: 276px; + --consonant-merch-card-ccd-action-min-height: 320px; + + + /*mini compare chart */ + --consonant-merch-card-mini-compare-chart-icon-size: 32px; + --consonant-merch-card-mini-compare-mobile-cta-font-size: 15px; + --consonant-merch-card-mini-compare-mobile-cta-width: 75px; + --consonant-merch-card-mini-compare-badge-mobile-max-width: 50px; + + /* inline SVGs */ + --checkmark-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath fill='%23fff' d='M3.788 9A.999.999 0 0 1 3 8.615l-2.288-3a1 1 0 1 1 1.576-1.23l1.5 1.991 3.924-4.991a1 1 0 1 1 1.576 1.23l-4.712 6A.999.999 0 0 1 3.788 9z' class='spectrum-UIIcon--medium'/%3E%3C/svg%3E%0A"); + + --secure-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23757575' viewBox='0 0 12 15'%3E%3Cpath d='M11.5 6H11V5A5 5 0 1 0 1 5v1H.5a.5.5 0 0 0-.5.5v8a.5.5 0 0 0 .5.5h11a.5.5 0 0 0 .5-.5v-8a.5.5 0 0 0-.5-.5ZM3 5a3 3 0 1 1 6 0v1H3Zm4 6.111V12.5a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1.389a1.5 1.5 0 1 1 2 0Z'/%3E%3C/svg%3E"); + + --info-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 36 36'>'); + + /* callout */ + --consonant-merch-card-callout-line-height: 21px; + --consonant-merch-card-callout-font-size: 14px; + --consonant-merch-card-callout-font-color: #2C2C2C; + --consonant-merch-card-callout-icon-size: 16px; + --consonant-merch-card-callout-icon-top: 6px; + --consonant-merch-card-callout-icon-right: 8px; + --consonant-merch-card-callout-letter-spacing: 0px; + --consonant-merch-card-callout-icon-padding: 34px; + --consonant-merch-card-callout-spacing-xxs: 8px; +} + +merch-card-collection { + display: contents; +} + +merch-card-collection > merch-card:not([style]) { + display: none; +} + +merch-card-collection > p[slot], +merch-card-collection > div[slot] p { + margin: 0; +} + +.one-merch-card, +.two-merch-cards, +.three-merch-cards, +.four-merch-cards { + display: grid; + justify-content: center; + justify-items: stretch; + gap: var(--consonant-merch-spacing-m); + padding: var(--spacing-m); +} + +merch-card.background-opacity-70 { + background-color: rgba(255 255 255 / 70%); +} + +merch-card.has-divider hr { + margin-bottom: var(--consonant-merch-spacing-xs); + height: 1px; + border: none; +} + +merch-card[variant="special-offers"] span[is="inline-price"][data-template="strikethrough"] { + font-size: var(--consonant-merch-card-body-xs-font-size); +} + +merch-card p, merch-card h3, merch-card h4 { + margin: 0; +} + +merch-card span[is=inline-price] { + display: inline-block; +} + +merch-card [slot='heading-xs'] { + color: var(--merch-color-grey-80); + font-size: var(--consonant-merch-card-heading-xs-font-size); + line-height: var(--consonant-merch-card-heading-xs-line-height); + margin: 0; + text-decoration: none; +} + +merch-card.dc-pricing [slot='heading-xs'] { + margin-bottom: var(--consonant-merch-spacing-xxs); +} + +merch-card:not([variant='inline-heading']) [slot='heading-xs'] a { + color: var(--merch-color-grey-80); +} + +merch-card [slot='starting-at'] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + font-weight: 500; +} + +merch-card [slot='heading-xs'] a:not(:hover) { + text-decoration: inherit; +} + +merch-card [slot='heading-s'] { + font-size: var(--consonant-merch-card-heading-s-font-size); + line-height: var(--consonant-merch-card-heading-s-line-height); + margin: 0; + color: var(--merch-color-grey-80); +} + +merch-card [slot='heading-m'] { + font-size: var(--consonant-merch-card-heading-m-font-size); + line-height: var(--consonant-merch-card-heading-m-line-height); + margin: 0; + color: var(--merch-color-grey-80); + font-weight: 700; +} + +merch-card [slot='heading-m-price'] { + font-size: var(--consonant-merch-card-heading-m-font-size); + line-height: var(--consonant-merch-card-heading-m-line-height); + padding: 0 var(--consonant-merch-spacing-s); + font-weight: 700; + margin: 0; + color: var(--merch-color-grey-80); +} + +merch-card [slot='offers'] { + padding: var(--consonant-merch-spacing-xxs) var(--consonant-merch-spacing-s); +} + +merch-card [slot='heading-l'] { + font-size: var(--consonant-merch-card-heading-l-font-size); + line-height: var(--consonant-merch-card-heading-l-line-height); + margin: 0; + color: var(--merch-color-grey-80); +} + +merch-card [slot='heading-xl'] { + font-size: var(--consonant-merch-card-heading-xl-font-size); + line-height: var(--consonant-merch-card-heading-xl-line-height); + margin: 0; + color: var(--merch-color-grey-80); +} + +merch-card [slot='callout-content'] { + display: flex; + flex-direction: column; + margin: var(--consonant-merch-spacing-xxxs) 0px; + gap: var(--consonant-merch-card-callout-spacing-xxs); +} + +merch-card [slot='callout-content'] > div { + display: flex; + flex-direction: column; + margin: var(--consonant-merch-spacing-xxxs) 0px; + gap: var(--consonant-merch-card-callout-spacing-xxs); + align-items: flex-start; +} + +merch-card [slot='callout-content'] > div > div { + display: flex; + background: rgba(203 203 203 / 50%); + border-radius: var(--consonant-merch-spacing-xxxs); + padding: var(--consonant-merch-spacing-xxxs) var(--consonant-merch-spacing-xxxs) var(--consonant-merch-spacing-xxxs) var(--consonant-merch-spacing-xxs); +} + +merch-card [slot='callout-content'] > div > div > div { + display: inline-block; + text-align: left; + font: normal normal normal var(--consonant-merch-card-callout-font-size)/var(--consonant-merch-card-callout-line-height) var(--body-font-family, 'Adobe Clean'); + letter-spacing: var(--consonant-merch-card-callout-letter-spacing); + color: var(--consonant-merch-card-callout-font-color); +} + +merch-card [slot='callout-content'] img { + width: var(--consonant-merch-card-callout-icon-size); + height: var(--consonant-merch-card-callout-icon-size); + margin: 2.5px 0px 0px 9px; +} + +merch-card [slot='detail-m'] { + font-size: var(--consonant-merch-card-detail-m-font-size); + letter-spacing: var(--consonant-merch-card-detail-m-letter-spacing); + font-weight: var(--consonant-merch-card-detail-m-font-weight); + text-transform: uppercase; + margin: 0; + color: var(--merch-color-grey-80); +} + +merch-card [slot="body-xxs"] { + font-size: var(--consonant-merch-card-body-xxs-font-size); + line-height: var(--consonant-merch-card-body-xxs-line-height); + font-weight: normal; + letter-spacing: var(--consonant-merch-card-body-xxs-letter-spacing); + color: var(--merch-color-grey-80); + margin: 0; +} + +merch-card [slot="body-xs"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + color: var(--merch-color-grey-80); +} + +merch-card [slot="body-m"] { + font-size: var(--consonant-merch-card-body-m-font-size); + line-height: var(--consonant-merch-card-body-m-line-height); + color: var(--merch-color-grey-80); +} + +merch-card [slot="body-l"] { + font-size: var(--consonant-merch-card-body-l-font-size); + line-height: var(--consonant-merch-card-body-l-line-height); + color: var(--merch-color-grey-80); +} + +merch-card [slot="body-xl"] { + font-size: var(--consonant-merch-card-body-xl-font-size); + line-height: var(--consonant-merch-card-body-xl-line-height); + color: var(--merch-color-grey-80); +} + +merch-card[variant="plans"] [slot="description"] { + min-height: 84px; +} + +merch-card[variant="catalog"] [slot="action-menu-content"] { + background-color: #000; + color: var(--color-white, #fff); + font-size: var(--consonant-merch-card-body-xs-font-size); + width: fit-content; + padding: var(--consonant-merch-spacing-xs); + border-radius: var(--consonant-merch-spacing-xxxs); + position: absolute; + top: 55px; + right: 15px; + line-height: var(--consonant-merch-card-body-line-height); +} + +merch-card[variant="catalog"] [slot="action-menu-content"] ul { + padding-left: 0; + padding-bottom: var(--consonant-merch-spacing-xss); + margin-top: 0; + margin-bottom: 0; + list-style-position: inside; + list-style-type: '\u2022 '; +} + +merch-card[variant="catalog"] [slot="action-menu-content"] ul li { + padding-left: 0; + line-height: var(--consonant-merch-card-body-line-height); +} + +merch-card[variant="catalog"] [slot="action-menu-content"] ::marker { + margin-right: 0; +} + +merch-card[variant="catalog"] [slot="action-menu-content"] p { + color: var(--color-white, #fff); +} + +merch-card[variant="catalog"] [slot="action-menu-content"] a { + color: var(--consonant-merch-card-background-color); + text-decoration: underline; +} + +merch-card[variant="catalog"] .payment-details { + font-size: var(--consonant-merch-card-body-font-size); + font-style: italic; + font-weight: 400; + line-height: var(--consonant-merch-card-body-line-height); +} + +merch-card[variant="ccd-action"] .price-strikethrough { + font-size: 18px; +} + +merch-card[variant="plans"] [slot="quantity-select"] { + display: flex; + justify-content: flex-start; + box-sizing: border-box; + width: 100%; + padding: var(--consonant-merch-spacing-xs); +} + +merch-card[variant="twp"] div[class$='twp-badge'] { + padding: 4px 10px 5px 10px; +} + +merch-card[variant="twp"] [slot="body-xs-top"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + color: var(--merch-color-grey-80); +} + +merch-card[variant="twp"] [slot="body-xs"] ul { + padding: 0; + margin: 0; +} + +merch-card[variant="twp"] [slot="body-xs"] ul li { + list-style-type: none; + padding-left: 0; +} + +merch-card[variant="twp"] [slot="body-xs"] ul li::before { + content: '\xB7'; + font-size: 20px; + padding-right: 5px; + font-weight: bold; +} + +merch-card[variant="twp"] [slot="footer"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + padding: var(--consonant-merch-spacing-s) + var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-xs); + color: var(--merch-color-grey-80); + display: flex; + flex-flow: wrap; +} + +merch-card[variant='twp'] merch-quantity-select, +merch-card[variant='twp'] merch-offer-select { + display: none; +} + +[slot="cci-footer"] p, +[slot="cct-footer"] p, +[slot="cce-footer"] p { + margin: 0; +} + +/* mini compare chart card styles */ + +merch-card[variant="mini-compare-chart"] [slot="heading-m"] { + padding: 0 var(--consonant-merch-spacing-s) 0; +} + +merch-card[variant="mini-compare-chart"] [slot="body-m"] { + padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s); +} + +merch-card[variant="mini-compare-chart"] [is="inline-price"] { + display: inline-block; + min-height: 30px; + min-width: 1px; +} + +merch-card[variant="mini-compare-chart"] [slot='callout-content'] { + padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s) 0px; +} + +merch-card[variant="mini-compare-chart"] [slot='callout-content'] [is="inline-price"] { + min-height: unset; +} + +merch-card[variant="mini-compare-chart"] [slot="price-commitment"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + padding: 0 var(--consonant-merch-spacing-s); +} + +merch-card[variant="mini-compare-chart"] [slot="price-commitment"] a { + display: inline-block; + height: 27px; +} + +merch-card[variant="mini-compare-chart"] [slot="offers"] { + font-size: var(--consonant-merch-card-body-xs-font-size); +} + +merch-card [slot="promo-text"] { + color: var(--merch-color-green-promo); + font-size: var(--consonant-merch-card-promo-text-height); + font-weight: 700; + line-height: var(--consonant-merch-card-promo-text-height); + margin: 0; + min-height: var(--consonant-merch-card-promo-text-height); + padding: 0; +} + + +merch-card[variant="mini-compare-chart"] [slot="body-xxs"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s) 0; +} + +merch-card[variant="mini-compare-chart"] [slot="promo-text"] { + font-size: var(--consonant-merch-card-body-m-font-size); + padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s) 0; +} + +merch-card[variant="mini-compare-chart"] [slot="promo-text"] a { + text-decoration: underline; +} + +merch-card[variant="mini-compare-chart"] .footer-row-icon { + display: flex; + place-items: center; +} + +merch-card[variant="mini-compare-chart"] .footer-row-icon img { + max-width: initial; + width: var(--consonant-merch-card-mini-compare-chart-icon-size); + height: var(--consonant-merch-card-mini-compare-chart-icon-size); +} + +merch-card[variant="mini-compare-chart"] .footer-row-cell { + border-top: 1px solid var(--consonant-merch-card-border-color); + display: flex; + gap: var(--consonant-merch-spacing-xs); + justify-content: start; + place-items: center; + padding: var(--consonant-merch-spacing-xs) var(--consonant-merch-spacing-s); +} + +merch-card[variant="mini-compare-chart"] .footer-row-cell-description { + font-size: var(--consonant-merch-card-body-s-font-size); + line-height: var(--consonant-merch-card-body-s-line-height); +} + +merch-card[variant="mini-compare-chart"] .footer-row-cell-description p { + color: var(--merch-color-grey-80); + vertical-align: bottom; +} + +merch-card[variant="mini-compare-chart"] .footer-row-cell-description a { + color: var(--color-accent); + text-decoration: solid; +} + +/* mini compare mobile */ +@media screen and ${Nt} { + merch-card[variant="mini-compare-chart"] [slot='heading-m'] { + font-size: var(--consonant-merch-card-body-s-font-size); + line-height: var(--consonant-merch-card-body-s-line-height); + } + + merch-card[variant="mini-compare-chart"] [slot='heading-m-price'] { + font-size: var(--consonant-merch-card-body-s-font-size); + line-height: var(--consonant-merch-card-body-s-line-height); + } + + merch-card[variant="mini-compare-chart"] [slot='body-m'] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + } + + merch-card[variant="mini-compare-chart"] [slot="promo-text"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + } + merch-card[variant="mini-compare-chart"] .footer-row-cell-description { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + } +} + +/* mini compare tablet */ +@media screen and ${Rt} { + merch-card[variant="mini-compare-chart"] [slot='heading-m'] { + font-size: var(--consonant-merch-card-body-s-font-size); + line-height: var(--consonant-merch-card-body-s-line-height); + } + + merch-card[variant="mini-compare-chart"] [slot='heading-m-price'] { + font-size: var(--consonant-merch-card-body-s-font-size); + line-height: var(--consonant-merch-card-body-s-line-height); + } + + merch-card[variant="mini-compare-chart"] [slot='body-m'] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + } + + merch-card[variant="mini-compare-chart"] [slot="promo-text"] { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + } + + merch-card[variant="mini-compare-chart"] .footer-row-cell-description { + font-size: var(--consonant-merch-card-body-xs-font-size); + line-height: var(--consonant-merch-card-body-xs-line-height); + } +} + +div[slot="footer"] { + display: contents; +} + +[slot="footer"] a { + word-wrap: break-word; + text-align: center; +} + +[slot="footer"] a:not([class]) { + font-weight: 700; + font-size: var(--consonant-merch-card-cta-font-size); +} + +div[slot='bg-image'] img { + position: relative; + width: 100%; + min-height: var(--consonant-merch-card-image-height); + max-height: var(--consonant-merch-card-image-height); + object-fit: cover; + border-top-left-radius: 16px; + border-top-right-radius: 16px; +} + +/* Mobile */ +@media screen and ${Nt} { + :root { + --consonant-merch-card-mini-compare-chart-width: 302px; + --consonant-merch-card-segment-width: 276px; + --consonant-merch-card-mini-compare-chart-wide-width: 302px; + --consonant-merch-card-special-offers-width: 302px; + --consonant-merch-card-twp-width: 300px; + } +} + + +/* Tablet */ +@media screen and ${B} { + :root { + --consonant-merch-card-catalog-width: 302px; + --consonant-merch-card-plans-width: 302px; + --consonant-merch-card-segment-width: 276px; + --consonant-merch-card-mini-compare-chart-width: 302px; + --consonant-merch-card-mini-compare-chart-wide-width: 302px; + --consonant-merch-card-special-offers-width: 302px; + --consonant-merch-card-twp-width: 268px; + } +} + +/* desktop */ +@media screen and ${H} { + :root { + --consonant-merch-card-catalog-width: 276px; + --consonant-merch-card-plans-width: 276px; + --consonant-merch-card-segment-width: 302px; + --consonant-merch-card-inline-heading-width: 378px; + --consonant-merch-card-product-width: 378px; + --consonant-merch-card-image-width: 378px; + --consonant-merch-card-mini-compare-chart-width: 378px; + --consonant-merch-card-mini-compare-chart-wide-width: 484px; + --consonant-merch-card-twp-width: 268px; + } +} + +/* supported cards */ +/* grid style for plans */ +.one-merch-card.plans, +.two-merch-cards.plans, +.three-merch-cards.plans, +.four-merch-cards.plans { + grid-template-columns: var(--consonant-merch-card-plans-width); +} + +/* Tablet */ +@media screen and ${B} { + .two-merch-cards.plans, + .three-merch-cards.plans, + .four-merch-cards.plans { + grid-template-columns: repeat(2, var(--consonant-merch-card-plans-width)); + } +} + +/* desktop */ +@media screen and ${H} { + .three-merch-cards.plans, + .four-merch-cards.plans { + grid-template-columns: repeat(3, var(--consonant-merch-card-plans-width)); + } +} + +/* Large desktop */ + @media screen and ${q} { + .four-merch-cards.plans { + grid-template-columns: repeat(4, var(--consonant-merch-card-plans-width)); + } +} + + +/* grid style for catalog */ +.one-merch-card.catalog, +.two-merch-cards.catalog, +.three-merch-cards.catalog, +.four-merch-cards.catalog { + grid-template-columns: var(--consonant-merch-card-catalog-width); +} + +/* Tablet */ +@media screen and ${B} { + .two-merch-cards.catalog, + .three-merch-cards.catalog, + .four-merch-cards.catalog { + grid-template-columns: repeat(2, var(--consonant-merch-card-catalog-width)); + } +} + +/* desktop */ +@media screen and ${H} { + .three-merch-cards.catalog, + .four-merch-cards.catalog { + grid-template-columns: repeat(3, var(--consonant-merch-card-catalog-width)); + } +} + +/* Large desktop */ + @media screen and ${q} { + .four-merch-cards.catalog { + grid-template-columns: repeat(4, var(--consonant-merch-card-catalog-width)); + } +} + + +/* grid style for special-offers */ +.one-merch-card.special-offers, +.two-merch-cards.special-offers, +.three-merch-cards.special-offers, +.four-merch-cards.special-offers { + grid-template-columns: minmax(300px, var(--consonant-merch-card-special-offers-width)); +} + +/* Tablet */ +@media screen and ${B} { + .two-merch-cards.special-offers, + .three-merch-cards.special-offers, + .four-merch-cards.special-offers { + grid-template-columns: repeat(2, minmax(300px, var(--consonant-merch-card-special-offers-width))); + } +} + +/* desktop */ +@media screen and ${H} { + .three-merch-cards.special-offers, + .four-merch-cards.special-offers { + grid-template-columns: repeat(3, minmax(300px, var(--consonant-merch-card-special-offers-width))); + } +} + +@media screen and ${q} { + .four-merch-cards.special-offers { + grid-template-columns: repeat(4, minmax(300px, var(--consonant-merch-card-special-offers-width))); + } +} + + +/* grid style for image */ +.one-merch-card.image, +.two-merch-cards.image, +.three-merch-cards.image, +.four-merch-cards.image { + grid-template-columns: var(--consonant-merch-card-image-width); +} + +/* Tablet */ +@media screen and ${B} { + .two-merch-cards.image, + .three-merch-cards.image, + .four-merch-cards.image { + grid-template-columns: repeat(2, var(--consonant-merch-card-image-width)); + } +} + +/* desktop */ +@media screen and ${H} { + .three-merch-cards.image, + .four-merch-cards.image { + grid-template-columns: repeat(3, var(--consonant-merch-card-image-width)); + } +} + +/* Large desktop */ + @media screen and ${q} { + .four-merch-cards.image { + grid-template-columns: repeat(4, var(--consonant-merch-card-image-width)); + } +} + + +/* grid style for segment */ +.one-merch-card.segment, +.two-merch-cards.segment, +.three-merch-cards.segment, +.four-merch-cards.segment { + grid-template-columns: minmax(276px, var(--consonant-merch-card-segment-width)); +} + +/* Tablet */ +@media screen and ${B} { + .two-merch-cards.segment, + .three-merch-cards.segment, + .four-merch-cards.segment { + grid-template-columns: repeat(2, minmax(276px, var(--consonant-merch-card-segment-width))); + } +} + +/* desktop */ +@media screen and ${H} { + .three-merch-cards.segment { + grid-template-columns: repeat(3, minmax(276px, var(--consonant-merch-card-segment-width))); + } + + .four-merch-cards.segment { + grid-template-columns: repeat(4, minmax(276px, var(--consonant-merch-card-segment-width))); + } +} + + +/* grid style for product */ +.one-merch-card.product, +.two-merch-cards.product, +.three-merch-cards.product, +.four-merch-cards.product { + grid-template-columns: var(--consonant-merch-card-product-width); +} + +/* Tablet */ +@media screen and ${B} { + .two-merch-cards.product, + .three-merch-cards.product, + .four-merch-cards.product { + grid-template-columns: repeat(2, var(--consonant-merch-card-product-width)); + } +} + +/* desktop */ +@media screen and ${H} { + .three-merch-cards.product, + .four-merch-cards.product { + grid-template-columns: repeat(3, var(--consonant-merch-card-product-width)); + } +} + +/* Large desktop */ + @media screen and ${q} { + .four-merch-cards.product { + grid-template-columns: repeat(4, var(--consonant-merch-card-product-width)); + } +} + +/* grid style for twp */ +.one-merch-card.twp, +.two-merch-cards.twp, +.three-merch-cards.twp { + grid-template-columns: var(--consonant-merch-card-image-width); +} + +/* Tablet */ +@media screen and ${B} { + .one-merch-card.twp, + .two-merch-cards.twp { + grid-template-columns: repeat(2, var(--consonant-merch-card-twp-width)); + } + .three-merch-cards.twp { + grid-template-columns: repeat(3, var(--consonant-merch-card-twp-width)); + } +} + +/* desktop */ +@media screen and ${H} { + .one-merch-card.twp + .two-merch-cards.twp { + grid-template-columns: repeat(2, var(--consonant-merch-card-twp-width)); + } + .three-merch-cards.twp { + grid-template-columns: repeat(3, var(--consonant-merch-card-twp-width)); + } +} + +/* Large desktop */ + @media screen and ${q} { + .one-merch-card.twp + .two-merch-cards.twp { + grid-template-columns: repeat(2, var(--consonant-merch-card-twp-width)); + } + .three-merch-cards.twp { + grid-template-columns: repeat(3, var(--consonant-merch-card-twp-width)); + } +} + +/* Mobile */ +@media screen and ${Nt} { + .one-merch-card.twp, + .two-merch-cards.twp, + .three-merch-cards.twp { + grid-template-columns: repeat(1, var(--consonant-merch-card-twp-mobile-width)); + } +} + +/* grid style for inline-heading */ +.one-merch-card.inline-heading, +.two-merch-cards.inline-heading, +.three-merch-cards.inline-heading, +.four-merch-cards.inline-heading { + grid-template-columns: var(--consonant-merch-card-inline-heading-width); +} + +/* Tablet */ +@media screen and ${B} { + .two-merch-cards.inline-heading, + .three-merch-cards.inline-heading, + .four-merch-cards.inline-heading { + grid-template-columns: repeat(2, var(--consonant-merch-card-inline-heading-width)); + } +} + +/* desktop */ +@media screen and ${H} { + .three-merch-cards.inline-heading, + .four-merch-cards.inline-heading { + grid-template-columns: repeat(3, var(--consonant-merch-card-inline-heading-width)); + } +} + +/* Large desktop */ + @media screen and ${q} { + .four-merch-cards.inline-heading { + grid-template-columns: repeat(4, var(--consonant-merch-card-inline-heading-width)); + } +} + +/* grid style for ccd-action */ +.one-merch-card.ccd-action, +.two-merch-cards.ccd-action, +.three-merch-cards.ccd-action, +.four-merch-cards.ccd-action { + grid-template-columns: var(--consonant-merch-card-ccd-action-width); +} + +/* Tablet */ +@media screen and ${B} { + .two-merch-cards.ccd-action, + .three-merch-cards.ccd-action, + .four-merch-cards.ccd-action { + grid-template-columns: repeat(2, var(--consonant-merch-card-ccd-action-width)); + } +} + +/* desktop */ +@media screen and ${H} { + .three-merch-cards.ccd-action, + .four-merch-cards.ccd-action { + grid-template-columns: repeat(3, var(--consonant-merch-card-ccd-action-width)); + } +} + +/* Large desktop */ + @media screen and ${q} { + .four-merch-cards.ccd-action { + grid-template-columns: repeat(4, var(--consonant-merch-card-ccd-action-width)); + } +} + +/* grid style for mini-compare-chart */ +.one-merch-card.mini-compare-chart { + grid-template-columns: var(--consonant-merch-card-mini-compare-chart-wide-width); + gap: var(--consonant-merch-spacing-xs); +} + +.two-merch-cards.mini-compare-chart, +.three-merch-cards.mini-compare-chart, +.four-merch-cards.mini-compare-chart { + grid-template-columns: repeat(2, var(--consonant-merch-card-mini-compare-chart-width)); + gap: var(--consonant-merch-spacing-xs); +} + +@media screen and ${Nt} { + .two-merch-cards.mini-compare-chart, + .three-merch-cards.mini-compare-chart, + .four-merch-cards.mini-compare-chart { + grid-template-columns: var(--consonant-merch-card-mini-compare-chart-width); + gap: var(--consonant-merch-spacing-xs); + } +} + +@media screen and ${Rt} { + .three-merch-cards.mini-compare-chart merch-card [slot="footer"] a, + .four-merch-cards.mini-compare-chart merch-card [slot="footer"] a { + flex: 1; + } +} + +/* Tablet */ +@media screen and ${B} { + .two-merch-cards.mini-compare-chart { + grid-template-columns: repeat(2, minmax(var(--consonant-merch-card-mini-compare-chart-width), var(--consonant-merch-card-mini-compare-chart-wide-width))); + gap: var(--consonant-merch-spacing-m); + } + + .three-merch-cards.mini-compare-chart, + .four-merch-cards.mini-compare-chart { + grid-template-columns: repeat(2, minmax(var(--consonant-merch-card-mini-compare-chart-width), var(--consonant-merch-card-mini-compare-chart-wide-width))); + } +} + +/* desktop */ +@media screen and ${H} { + .one-merch-card.mini-compare-chart { + grid-template-columns: var(--consonant-merch-card-mini-compare-chart-wide-width); + } + + .two-merch-cards.mini-compare-chart { + grid-template-columns: repeat(2, var(--consonant-merch-card-mini-compare-chart-wide-width)); + gap: var(--consonant-merch-spacing-m); + } + + .three-merch-cards.mini-compare-chart, + .four-merch-cards.mini-compare-chart { + grid-template-columns: repeat(3, var(--consonant-merch-card-mini-compare-chart-width)); + gap: var(--consonant-merch-spacing-m); + } +} + +@media screen and ${q} { + .four-merch-cards.mini-compare-chart { + grid-template-columns: repeat(4, var(--consonant-merch-card-mini-compare-chart-width)); + } +} + +/* mini-compare card footer rows */ +merch-card .footer-row-cell:nth-child(1) { + min-height: var(--consonant-merch-card-footer-row-1-min-height); +} + +merch-card .footer-row-cell:nth-child(2) { + min-height: var(--consonant-merch-card-footer-row-2-min-height); +} + +merch-card .footer-row-cell:nth-child(3) { + min-height: var(--consonant-merch-card-footer-row-3-min-height); +} + +merch-card .footer-row-cell:nth-child(4) { + min-height: var(--consonant-merch-card-footer-row-4-min-height); +} + +merch-card .footer-row-cell:nth-child(5) { + min-height: var(--consonant-merch-card-footer-row-5-min-height); +} + +merch-card .footer-row-cell:nth-child(6) { + min-height: var(--consonant-merch-card-footer-row-6-min-height); +} + +merch-card .footer-row-cell:nth-child(7) { + min-height: var(--consonant-merch-card-footer-row-7-min-height); +} + +merch-card .footer-row-cell:nth-child(8) { + min-height: var(--consonant-merch-card-footer-row-8-min-height); +} + +span[is="inline-price"][data-template='strikethrough'] { + text-decoration: line-through; +} + +merch-card sp-button a { + text-decoration: none; + color: var( + --highcontrast-button-content-color-default, + var( + --mod-button-content-color-default, + var(--spectrum-button-content-color-default) + ) + ); +} + +merch-card span.placeholder-resolved[data-template='strikethrough'], +merch-card span.price.price-strikethrough { + font-size: var(--consonant-merch-card-body-xs-font-size); + font-weight: normal; +} + +/* merch-offer-select */ +merch-offer-select[variant="subscription-options"] merch-offer span[is="inline-price"][data-display-tax='true'] .price-tax-inclusivity { + font-size: 12px; + font-style: italic; + font-weight: normal; + position: absolute; + left: 0; + top: 20px; +} + +body.merch-modal { + overflow: hidden; + scrollbar-gutter: stable; + height: 100vh; +} +`;document.head.appendChild(Ps);var Cs="merch-offer-select:ready",$s="merch-card:ready",Os="merch-card:action-menu-toggle";var go="merch-storage:change",xo="merch-quantity-selector:change";var jc="merch-card",Bc=32,Or="mini-compare-chart",Ls=e=>`--consonant-merch-card-footer-row-${e}-min-height`,de,It=class extends oe{constructor(){super();O(this,"customerSegment");O(this,"marketSegment");Q(this,de);this.filters={},this.types="",this.selected=!1}updated(r){(r.has("badgeBackgroundColor")||r.has("borderColor"))&&(this.style.border=this.computedBorderStyle),this.updateComplete.then(async()=>{let o=Array.from(this.querySelectorAll('span[is="inline-price"][data-wcs-osi]')).filter(i=>!i.closest('[slot="callout-content"]'));await Promise.all(o.map(i=>i.onceSettled())),this.adjustTitleWidth(),As()?this.removeEmptyRows():(this.adjustMiniCompareBodySlots(),this.adjustMiniCompareFooterRows())})}get computedBorderStyle(){return this.variant!=="twp"?`1px solid ${this.borderColor?this.borderColor:this.badgeBackgroundColor}`:""}get evergreen(){return this.classList.contains("intro-pricing")}get stockCheckbox(){return this.checkboxLabel?x``:""}get cardImage(){return x`
+ + ${this.badge} +
`}get secureLabelFooter(){let r=this.secureLabel?x`${this.secureLabel}`:"";return x`
${r}
`}get miniCompareFooter(){let r=this.secureLabel?x` + ${this.secureLabel}`:x``;return x`
${r}
`}get badge(){let r;if(!(!this.badgeBackgroundColor||!this.badgeColor||!this.badgeText))return this.evergreen&&(r=`border: 1px solid ${this.badgeBackgroundColor}; border-right: none;`),x` +
+ ${this.badgeText} +
+ `}get badgeElement(){return this.shadowRoot.getElementById("badge")}getContainer(){return this.closest('[class*="-merch-cards"]')??this.parentElement}get headingmMSlot(){return this.shadowRoot.querySelector('slot[name="heading-m"]').assignedElements()[0]}get footerSlot(){return this.shadowRoot.querySelector('slot[name="footer"]')?.assignedElements()[0]}get price(){return this.headingmMSlot?.querySelector('span[is="inline-price"]')}get checkoutLinks(){return[...this.footerSlot?.querySelectorAll('a[is="checkout-link"]')??[]]}async toggleStockOffer({target:r}){if(!this.stockOfferOsis)return;let n=this.checkoutLinks;if(n.length!==0)for(let o of n){await o.onceSettled();let i=o.value?.[0]?.planType;if(!i)return;let s=this.stockOfferOsis[i];if(!s)return;let c=o.dataset.wcsOsi.split(",").filter(a=>a!==s);r.checked&&c.push(s),o.dataset.wcsOsi=c.join(",")}}toggleActionMenu(r){let n=r?.type==="mouseleave"?!0:void 0,o=this.shadowRoot.querySelector('slot[name="action-menu-content"]');o&&(n||this.dispatchEvent(new CustomEvent(Os,{bubbles:!0,composed:!0,detail:{card:this.name,type:"action-menu"}})),o.classList.toggle("hidden",n))}handleQuantitySelection(r){let n=this.checkoutLinks;for(let o of n)o.dataset.quantity=r.detail.option}get titleElement(){return this.variant==="special-offers"?this.querySelector('[slot="detail-m"]'):this.querySelector('[slot="heading-xs"]')}get title(){return this.titleElement?.textContent?.trim()}get description(){return this.querySelector('[slot="body-xs"]')?.textContent?.trim()}updateFilters(r){let n={...this.filters};Object.keys(n).forEach(o=>{if(r){n[o].order=Math.min(n[o].order||2,2);return}let i=n[o].order;i===1||isNaN(i)||(n[o].order=Number(i)+1)}),this.filters=n}includes(r){return this.textContent.match(new RegExp(r,"i"))!==null}render(){if(!(!this.isConnected||this.style.display==="none"))switch(this.variant){case"special-offers":return this.renderSpecialOffer();case"segment":return this.renderSegment();case"plans":return this.renderPlans();case"catalog":return this.renderCatalog();case"image":return this.renderImage();case"product":return this.renderProduct();case"inline-heading":return this.renderInlineHeading();case Or:return this.renderMiniCompareChart();case"ccd-action":return this.renderCcdAction();case"twp":return this.renderTwp();default:return this.renderProduct()}}renderSpecialOffer(){return x`${this.cardImage} +
+ + + +
+ ${this.evergreen?x` +
+ +
+ `:x` +
+ ${this.secureLabelFooter} + `} + `}get promoBottom(){return this.classList.contains("promo-bottom")}get startingAt(){return this.classList.contains("starting-at")}renderSegment(){return x` ${this.badge} +
+ + + ${this.promoBottom?"":x``} + + ${this.promoBottom?x``:""} +
+
+ ${this.secureLabelFooter}`}renderPlans(){return x` ${this.badge} +
+ + + + + ${this.promoBottom?"":x` `} + + ${this.promoBottom?x` `:""} + ${this.stockCheckbox} +
+ + ${this.secureLabelFooter}`}renderCatalog(){return x`
+
+ ${this.badge} +
+
+ ${this.actionMenuContent} + + + + ${this.promoBottom?"":x``} + + ${this.promoBottom?x``:""} +
+ ${this.secureLabelFooter}`}renderImage(){return x`${this.cardImage} +
+ + + + ${this.promoBottom?x``:x``} +
+ ${this.evergreen?x` +
+ +
+ `:x` +
+ ${this.secureLabelFooter} + `}`}renderInlineHeading(){return x` ${this.badge} +
+
+ + +
+ +
+ ${this.customHr?"":x`
`} ${this.secureLabelFooter}`}renderProduct(){return x` ${this.badge} +
+ + + + ${this.promoBottom?"":x``} + + ${this.promoBottom?x``:""} +
+ ${this.secureLabelFooter}`}renderMiniCompareChart(){let{badge:r}=this;return x`
+ ${r} +
+ + + + + + + + + ${this.miniCompareFooter} + `}renderTwp(){return x`${this.badge} +
+ + + +
+
+ +
+
`}renderCcdAction(){return x`
+ ${this.badge} + + + ${this.promoBottom?x``:x``} +
+ +
`}connectedCallback(){super.connectedCallback(),K(this,de,this.getContainer()),this.setAttribute("tabindex",this.getAttribute("tabindex")??"0"),this.addEventListener("mouseleave",this.toggleActionMenu),this.addEventListener(xo,this.handleQuantitySelection),this.addEventListener(Cs,this.merchCardReady,{once:!0}),this.updateComplete.then(()=>{this.merchCardReady()}),this.storageOptions?.addEventListener("change",this.handleStorageChange)}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener(xo,this.handleQuantitySelection),this.storageOptions?.removeEventListener(go,this.handleStorageChange)}updateMiniCompareElementMinHeight(r,n){let o=`--consonant-merch-card-mini-compare-${n}-height`,i=Math.max(0,parseInt(window.getComputedStyle(r).height)||0),s=parseInt(R(this,de).style.getPropertyValue(o))||0;i>s&&R(this,de).style.setProperty(o,`${i}px`)}async adjustTitleWidth(){if(!["segment","plans"].includes(this.variant))return;let r=this.getBoundingClientRect().width,n=this.badgeElement?.getBoundingClientRect().width||0;r===0||n===0||this.style.setProperty("--consonant-merch-card-heading-xs-max-width",`${Math.round(r-n-16)}px`)}async adjustMiniCompareBodySlots(){if(this.variant!==Or||this.getBoundingClientRect().width===0)return;this.updateMiniCompareElementMinHeight(this.shadowRoot.querySelector(".top-section"),"top-section"),["heading-m","body-m","heading-m-price","price-commitment","offers","promo-text","callout-content","secure-transaction-label"].forEach(o=>this.updateMiniCompareElementMinHeight(this.shadowRoot.querySelector(`slot[name="${o}"]`),o)),this.updateMiniCompareElementMinHeight(this.shadowRoot.querySelector("footer"),"footer");let n=this.shadowRoot.querySelector(".mini-compare-chart-badge");n&&n.textContent!==""&&R(this,de).style.setProperty("--consonant-merch-card-mini-compare-top-section-mobile-height","32px")}adjustMiniCompareFooterRows(){if(this.variant!==Or||this.getBoundingClientRect().width===0)return;[...this.querySelector('[slot="footer-rows"]').children].forEach((n,o)=>{let i=Math.max(Bc,parseInt(window.getComputedStyle(n).height)||0),s=parseInt(R(this,de).style.getPropertyValue(Ls(o+1)))||0;i>s&&R(this,de).style.setProperty(Ls(o+1),`${i}px`)})}removeEmptyRows(){if(this.variant!==Or)return;this.querySelectorAll(".footer-row-cell").forEach(n=>{let o=n.querySelector(".footer-row-cell-description");o&&!o.textContent.trim()&&n.remove()})}get storageOptions(){return this.querySelector("sp-radio-group#storage")}get storageSpecificOfferSelect(){let r=this.storageOptions?.selected;if(r){let n=this.querySelector(`merch-offer-select[storage="${r}"]`);if(n)return n}return this.querySelector("merch-offer-select")}get offerSelect(){return this.storageOptions?this.storageSpecificOfferSelect:this.querySelector("merch-offer-select")}get quantitySelect(){return this.querySelector("merch-quantity-select")}merchCardReady(){this.offerSelect&&!this.offerSelect.planType||this.dispatchEvent(new CustomEvent($s,{bubbles:!0}))}handleStorageChange(){let r=this.closest("merch-card")?.offerSelect.cloneNode(!0);r&&this.dispatchEvent(new CustomEvent(go,{detail:{offerSelect:r},bubbles:!0}))}get dynamicPrice(){return this.querySelector('[slot="price"]')}selectMerchOffer(r){if(r===this.merchOffer)return;this.merchOffer=r;let n=this.dynamicPrice;if(r.price&&n){let o=r.price.cloneNode(!0);n.onceSettled?n.onceSettled().then(()=>{n.replaceWith(o)}):n.replaceWith(o)}}};de=new WeakMap,O(It,"properties",{name:{type:String,attribute:"name",reflect:!0},variant:{type:String,reflect:!0},size:{type:String,attribute:"size",reflect:!0},badgeColor:{type:String,attribute:"badge-color"},borderColor:{type:String,attribute:"border-color"},badgeBackgroundColor:{type:String,attribute:"badge-background-color"},badgeText:{type:String,attribute:"badge-text"},actionMenu:{type:Boolean,attribute:"action-menu"},actionMenuContent:{type:String,attribute:"action-menu-content"},customHr:{type:Boolean,attribute:"custom-hr"},detailBg:{type:String,attribute:"detail-bg"},secureLabel:{type:String,attribute:"secure-label"},checkboxLabel:{type:String,attribute:"checkbox-label"},selected:{type:Boolean,attribute:"aria-selected",reflect:!0},storageOption:{type:String,attribute:"storage",reflect:!0},stockOfferOsis:{type:Object,attribute:"stock-offer-osis",converter:{fromAttribute:r=>{let[n,o,i]=r.split(",");return{PUF:n,ABM:o,M2M:i}}}},filters:{type:String,reflect:!0,converter:{fromAttribute:r=>Object.fromEntries(r.split(",").map(n=>{let[o,i,s]=n.split(":"),c=Number(i);return[o,{order:isNaN(c)?void 0:c,size:s}]})),toAttribute:r=>Object.entries(r).map(([n,{order:o,size:i}])=>[n,o,i].filter(s=>s!=null).join(":")).join(",")}},types:{type:String,attribute:"types",reflect:!0},merchOffer:{type:Object}}),O(It,"styles",[_s,...ws()]);customElements.define(jc,It);var at=class extends oe{constructor(){super(),this.size="m",this.alt=""}render(){let{href:t}=this;return t?x`
+ ${this.alt} + `:x` ${this.alt}`}};O(at,"properties",{size:{type:String,attribute:!0},src:{type:String,attribute:!0},alt:{type:String,attribute:!0},href:{type:String,attribute:!0}}),O(at,"styles",ke` + :host { + --img-width: 32px; + --img-height: 32px; + display: block; + width: var(--img-width); + height: var(--img-height); + } + + :host([size='s']) { + --img-width: 24px; + --img-height: 24px; + } + + :host([size='l']) { + --img-width: 40px; + --img-height: 40px; + } + + img { + width: var(--img-width); + height: var(--img-height); + } + `);customElements.define("merch-icon",at);var He="Network error",kt,Lr=class{constructor(t){Q(this,kt);O(this,"sites",{cf:{fragments:{search:this.searchFragment.bind(this),getByPath:this.getFragmentByPath.bind(this),getById:this.getFragmentById.bind(this),save:this.saveFragment.bind(this),copy:this.copyFragmentClassic.bind(this),publish:this.publishFragment.bind(this),delete:this.deleteFragment.bind(this)}}});K(this,kt,/^author-/.test(t));let r=`https://${t}.adobeaemcloud.com`,n=`${r}/adobe/sites`;this.cfFragmentsUrl=`${n}/cf/fragments`,this.cfSearchUrl=`${this.cfFragmentsUrl}/search`,this.cfPublishUrl=`${this.cfFragmentsUrl}/publish`,this.wcmcommandUrl=`${r}/bin/wcmcommand`,this.csrfTokenUrl=`${r}/libs/granite/csrf/token.json`,this.headers={Authorization:`Bearer ${sessionStorage.getItem("masAccessToken")??window.adobeid?.authorize?.()}`,pragma:"no-cache","cache-control":"no-cache"}}async getCsrfToken(){let t=await fetch(this.csrfTokenUrl,{headers:this.headers}).catch(n=>{throw new Error(`${He}: ${n.message}`)});if(!t.ok)throw new Error(`Failed to get CSRF token: ${t.status} ${t.statusText}`);let{token:r}=await t.json();return r}async searchFragment({path:t,query:r,variant:n}){let o={};t&&(o.path=t),r&&(o.fullText={text:encodeURIComponent(r),queryMode:"EXACT_WORDS"});let i=new URLSearchParams({query:JSON.stringify({filter:o})}).toString(),s=await fetch(`${this.cfSearchUrl}?${i}`,{headers:this.headers}).catch(h=>{throw new Error(`${He}: ${h.message}`)});if(!s.ok)throw new Error(`Search failed: ${s.status} ${s.statusText}`);let a=(await s.json()).items;return n&&(a=a.filter(h=>{let[l]=h.fields.find(d=>d.name==="variant")?.values;return l===n})),a}async getFragmentByPath(t){let r=R(this,kt)?this.headers:{},n=await fetch(`${this.cfFragmentsUrl}?path=${t}`,{headers:r}).catch(i=>{throw new Error(`${He}: ${i.message}`)});if(!n.ok)throw new Error(`Failed to get fragment: ${n.status} ${n.statusText}`);let{items:o}=await n.json();if(!o||o.length===0)throw new Error("Fragment not found");return o[0]}async getFragment(t){let r=t.headers.get("Etag"),n=await t.json();return n.etag=r,n}async getFragmentById(t){let r=await fetch(`${this.cfFragmentsUrl}/${t}`,{headers:this.headers});if(!r.ok)throw new Error(`Failed to get fragment: ${r.status} ${r.statusText}`);return await this.getFragment(r)}async saveFragment(t){let{title:r,fields:n}=t,o=await fetch(`${this.cfFragmentsUrl}/${t.id}`,{method:"PUT",headers:{"Content-Type":"application/json","If-Match":t.etag,...this.headers},body:JSON.stringify({title:r,fields:n})}).catch(i=>{throw new Error(`${He}: ${i.message}`)});if(!o.ok)throw new Error(`Failed to save fragment: ${o.status} ${o.statusText}`);return await this.getFragment(o)}async copyFragmentClassic(t){let r=await this.getCsrfToken(),n=t.path.split("/").slice(0,-1).join("/"),o=new FormData;o.append("cmd","copyPage"),o.append("srcPath",t.path),o.append("destParentPath",n),o.append("shallow","false"),o.append("_charset_","UTF-8");let i=await fetch(this.wcmcommandUrl,{method:"POST",headers:{...this.headers,"csrf-token":r},body:o}).catch(u=>{throw new Error(`${He}: ${u.message}`)});if(!i.ok)throw new Error(`Failed to copy fragment: ${i.status} ${i.statusText}`);let s=await i.text(),l=new DOMParser().parseFromString(s,"text/html").getElementById("Message")?.textContent.trim();if(!l)throw new Error("Failed to extract new path from copy response");await Ts();let d=await this.getFragmentByPath(l);return d&&(d=await this.getFragmentById(d.id)),d}async publishFragment(t){let r=await fetch(this.cfPublishUrl,{method:"POST",headers:{"Content-Type":"application/json","If-Match":t.etag,...this.headers},body:JSON.stringify({paths:[t.path],filterReferencesByStatus:["DRAFT","UNPUBLISHED"],workflowModelId:"/var/workflow/models/scheduled_activation_with_references"})}).catch(n=>{throw new Error(`${He}: ${n.message}`)});if(!r.ok)throw new Error(`Failed to publish fragment: ${r.status} ${r.statusText}`);return await r.json()}async deleteFragment(t){let r=await fetch(`${this.cfFragmentsUrl}/${t.id}`,{method:"DELETE",headers:{"Content-Type":"application/json","If-Match":t.etag,...this.headers}}).catch(n=>{throw new Error(`${He}: ${n.message}`)});if(!r.ok)throw new Error(`Failed to delete fragment: ${r.status} ${r.statusText}`);return r}};kt=new WeakMap;var Wc="aem-bucket",Nr={CATALOG:"catalog",AH:"ah",CCD_ACTION:"ccd-action",SPECIAL_OFFERS:"special-offers"},qc={[Nr.CATALOG]:{title:{tag:"h3",slot:"heading-xs"},prices:{tag:"h3",slot:"heading-xs"},description:{tag:"div",slot:"body-xs"},ctas:{size:"l"}},[Nr.AH]:{title:{tag:"h3",slot:"heading-xxs"},prices:{tag:"h3",slot:"heading-xs"},description:{tag:"div",slot:"body-xxs"},ctas:{size:"s"}},[Nr.CCD_ACTION]:{title:{tag:"h3",slot:"heading-xs"},prices:{tag:"h3",slot:"heading-xs"},description:{tag:"div",slot:"body-xs"},ctas:{size:"l"}},[Nr.SPECIAL_OFFERS]:{name:{tag:"h4",slot:"detail-m"},title:{tag:"h4",slot:"detail-m"},backgroundImage:{tag:"div",slot:"bg-image"},prices:{tag:"h3",slot:"heading-xs"},description:{tag:"div",slot:"body-xs"},ctas:{size:"l"}}};async function Yc(e,t,r,n){let o=e.fields.reduce((c,{name:a,multiple:h,values:l})=>(c[a]=h?l:l[0],c),{id:e.id});o.model=o.model;let{variant:i="catalog"}=o;r.setAttribute("variant",i);let s=qc[i]??"catalog";if(o.icon?.forEach(c=>{let a=Ee("merch-icon",{slot:"icons",src:c,alt:"",href:"",size:"l"});t(a)}),o.title&&s.title&&t(Ee(s.title.tag,{slot:s.title.slot},o.title)),o.backgroundImage&&s.backgroundImage&&t(Ee(s.backgroundImage.tag,{slot:s.backgroundImage.slot},``)),o.prices&&s.prices){let c=o.prices,a=Ee(s.prices.tag,{slot:s.prices.slot},c);t(a)}if(o.description&&s.description){let c=Ee(s.description.tag,{slot:s.description.slot},o.description);t(c)}if(o.ctas){let c=Ee("div",{slot:"footer"},o.ctas),a=[];[...c.querySelectorAll("a")].forEach(h=>{let l=h.parentElement.tagName==="STRONG";if(n)h.classList.add("con-button"),l&&h.classList.add("blue"),a.push(h);else{let m=Ee("sp-button",{treatment:l?"fill":"outline",variant:l?"accent":"primary"},h);m.addEventListener("click",f=>{f.stopPropagation(),h.click()}),a.push(m)}}),c.innerHTML="",c.append(...a),t(c)}}var be,yo=class{constructor(){Q(this,be,new Map)}clear(){R(this,be).clear()}add(...t){t.forEach(r=>{let{path:n}=r;n&&R(this,be).set(n,r)})}has(t){return R(this,be).has(t)}get(t){return R(this,be).get(t)}remove(t){R(this,be).delete(t)}};be=new WeakMap;var vo=new yo,Mt,ze,Eo=class extends HTMLElement{constructor(){super(...arguments);Q(this,Mt);O(this,"cache",vo);O(this,"refs",[]);O(this,"path");O(this,"consonant",!1);Q(this,ze)}static get observedAttributes(){return["source","path","consonant"]}attributeChangedCallback(r,n,o){this[r]=o}connectedCallback(){this.consonant=this.hasAttribute("consonant"),this.clearRefs();let r=this.getAttribute(Wc)??"publish-p22655-e59341";K(this,Mt,new Lr(r)),this.refresh(!1)}clearRefs(){this.refs.forEach(r=>{r.remove()})}async refresh(r=!0){this.path&&(R(this,ze)&&!await Promise.race([R(this,ze),Promise.resolve(!1)])||(this.clearRefs(),this.refs=[],r&&this.cache.remove(this.path),K(this,ze,this.fetchData().then(()=>!0))))}async fetchData(){let r=vo.get(this.path);if(r||(r=await R(this,Mt).sites.cf.fragments.getByPath(this.path),vo.add(r)),r){Yc(r,o=>{this.parentElement.appendChild(o),this.refs.push(o)},this.parentElement,this.consonant);return}}get updateComplete(){return R(this,ze)??Promise.reject(new Error("datasource is not correctly configured"))}};Mt=new WeakMap,ze=new WeakMap;customElements.define("merch-datasource",Eo);var{searchParams:Ns}=new URL(import.meta.url),Xc=Ns.get("locale")??"US_en",Rs=Ns.get("env")==="stage",Zc=Rs?"stage":"prod",Jc=Rs?"STAGE":"PROD",Qc=fetch("https://www.adobe.com/federal/commerce/price-literals.json").then(e=>e.json().then(({data:t})=>t)),Kc=()=>({env:{name:Zc},commerce:{"commerce.env":Jc,priceLiteralsPromise:Qc},locale:{prefix:Xc}}),el=bt(Kc),Qu=el;export{Qu as default}; +/*! Bundled license information: + +@lit/reactive-element/css-tag.js: + (** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +@lit/reactive-element/reactive-element.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +lit-html/lit-html.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +@lit/reactive-element/css-tag.js: + (** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +@lit/reactive-element/reactive-element.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +lit-html/lit-html.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +lit-element/lit-element.js: + (** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) + +lit-html/is-server.js: + (** + * @license + * Copyright 2022 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + *) +*/ diff --git a/libs/deps/mas/merch-card-collection.js b/libs/deps/mas/merch-card-collection.js index 07e8efd230..e0333d6fc3 100644 --- a/libs/deps/mas/merch-card-collection.js +++ b/libs/deps/mas/merch-card-collection.js @@ -1,4 +1,4 @@ -import{html as l,LitElement as A}from"/libs/deps/lit-all.min.js";var m=class{constructor(e,t){this.key=Symbol("match-media-key"),this.matches=!1,this.host=e,this.host.addController(this),this.media=window.matchMedia(t),this.matches=this.media.matches,this.onChange=this.onChange.bind(this),e.addController(this)}hostConnected(){var e;(e=this.media)==null||e.addEventListener("change",this.onChange)}hostDisconnected(){var e;(e=this.media)==null||e.removeEventListener("change",this.onChange)}onChange(e){this.matches!==e.matches&&(this.matches=e.matches,this.host.requestUpdate(this.key,!this.matches))}};var E="hashchange";function S(r=window.location.hash){let e=[],t=r.replace(/^#/,"").split("&");for(let o of t){let[n,c=""]=o.split("=");n&&e.push([n,decodeURIComponent(c.replace(/\+/g," "))])}return Object.fromEntries(e)}function u(r){let e=new URLSearchParams(window.location.hash.slice(1));Object.entries(r).forEach(([n,c])=>{c?e.set(n,c):e.delete(n)}),e.sort();let t=e.toString();if(t===window.location.hash)return;let o=window.scrollY||document.documentElement.scrollTop;window.location.hash=t,window.scrollTo(0,o)}function x(r){let e=()=>{if(window.location.hash&&!window.location.hash.includes("="))return;let t=S(window.location.hash);r(t)};return e(),window.addEventListener(E,e),()=>{window.removeEventListener(E,e)}}var T="merch-card-collection:sort",g="merch-card-collection:showmore";var C=(r,e={})=>{r.querySelectorAll("span[data-placeholder]").forEach(t=>{let{placeholder:o}=t.dataset;t.innerText=e[o]??""})};var _="(max-width: 1199px)",y="(min-width: 768px)",w="(min-width: 1200px)";import{css as R,unsafeCSS as b}from"/libs/deps/lit-all.min.js";var N=R` +import{html as l,LitElement as N}from"../lit-all.min.js";var m=class{constructor(e,t){this.key=Symbol("match-media-key"),this.matches=!1,this.host=e,this.host.addController(this),this.media=window.matchMedia(t),this.matches=this.media.matches,this.onChange=this.onChange.bind(this),e.addController(this)}hostConnected(){var e;(e=this.media)==null||e.addEventListener("change",this.onChange)}hostDisconnected(){var e;(e=this.media)==null||e.removeEventListener("change",this.onChange)}onChange(e){this.matches!==e.matches&&(this.matches=e.matches,this.host.requestUpdate(this.key,!this.matches))}};var f="hashchange";function R(r=window.location.hash){let e=[],t=r.replace(/^#/,"").split("&");for(let o of t){let[i,c=""]=o.split("=");i&&e.push([i,decodeURIComponent(c.replace(/\+/g," "))])}return Object.fromEntries(e)}function p(r){let e=new URLSearchParams(window.location.hash.slice(1));Object.entries(r).forEach(([i,c])=>{c?e.set(i,c):e.delete(i)}),e.sort();let t=e.toString();if(t===window.location.hash)return;let o=window.scrollY||document.documentElement.scrollTop;window.location.hash=t,window.scrollTo(0,o)}function T(r){let e=()=>{if(window.location.hash&&!window.location.hash.includes("="))return;let t=R(window.location.hash);r(t)};return e(),window.addEventListener(f,e),()=>{window.removeEventListener(f,e)}}var x="merch-card-collection:sort",g="merch-card-collection:showmore";var C=(r,e={})=>{r.querySelectorAll("span[data-placeholder]").forEach(t=>{let{placeholder:o}=t.dataset;t.innerText=e[o]??""})};var _="(max-width: 1199px)",b="(min-width: 768px)",w="(min-width: 1200px)";import{css as A,unsafeCSS as y}from"../lit-all.min.js";var S=A` #header, #resultText, #footer { @@ -65,7 +65,7 @@ import{html as l,LitElement as A}from"/libs/deps/lit-all.min.js";var m=class{con } /* tablets */ - @media screen and ${b(y)} { + @media screen and ${y(b)} { #header { grid-template-columns: 1fr fit-content(100%) fit-content(100%); } @@ -84,7 +84,7 @@ import{html as l,LitElement as A}from"/libs/deps/lit-all.min.js";var m=class{con } /* Laptop */ - @media screen and ${b(w)} { + @media screen and ${y(w)} { #resultText { grid-column: span 2; order: -3; @@ -96,9 +96,9 @@ import{html as l,LitElement as A}from"/libs/deps/lit-all.min.js";var m=class{con justify-content: end; } } -`;var p=(r,e)=>r.querySelector(`[slot="${e}"]`).textContent.trim();var M="merch-card-collection",i={alphabetical:"alphabetical",authored:"authored"},v={filters:["noResultText","resultText","resultsText"],mobile:["noSearchResultsMobileText","searchResultMobileText","searchResultsMobileText"],desktop:["noSearchResultsText","searchResultText","searchResultsText"]},O=(r,{filter:e})=>r.filter(t=>t.filters.hasOwnProperty(e)),L=(r,{types:e})=>e?(e=e.split(","),r.filter(t=>e.some(o=>t.types.includes(o)))):r,D=r=>r.sort((e,t)=>(e.title??"").localeCompare(t.title??"","en",{sensitivity:"base"})),k=(r,{filter:e})=>r.sort((t,o)=>o.filters[e]?.order==null||isNaN(o.filters[e]?.order)?-1:t.filters[e]?.order==null||isNaN(t.filters[e]?.order)?1:t.filters[e].order-o.filters[e].order),H=(r,{search:e})=>e?.length?(e=e.toLowerCase(),r.filter(t=>(t.title??"").toLowerCase().includes(e))):r,f=class extends A{static properties={filter:{type:String,attribute:"filter",reflect:!0},filtered:{type:String,attribute:"filtered"},search:{type:String,attribute:"search",reflect:!0},sort:{type:String,attribute:"sort",default:i.authored,reflect:!0},types:{type:String,attribute:"types",reflect:!0},limit:{type:Number,attribute:"limit"},page:{type:Number,attribute:"page",reflect:!0},singleApp:{type:String,attribute:"single-app",reflect:!0},hasMore:{type:Boolean},displayResult:{type:Boolean,attribute:"display-result"},resultCount:{type:Number},sidenav:{type:Object}};mobileAndTablet=new m(this,_);constructor(){super(),this.filter="all",this.hasMore=!1,this.resultCount=void 0,this.displayResult=!1}render(){return l`${this.header} +`;var u=(r,e)=>r.querySelector(`[slot="${e}"]`).textContent.trim();var M="merch-card-collection",n={alphabetical:"alphabetical",authored:"authored"},O={filters:["noResultText","resultText","resultsText"],mobile:["noSearchResultsMobileText","searchResultMobileText","searchResultsMobileText"],desktop:["noSearchResultsText","searchResultText","searchResultsText"]},v=(r,{filter:e})=>r.filter(t=>t.filters.hasOwnProperty(e)),L=(r,{types:e})=>e?(e=e.split(","),r.filter(t=>e.some(o=>t.types.includes(o)))):r,D=r=>r.sort((e,t)=>(e.title??"").localeCompare(t.title??"","en",{sensitivity:"base"})),H=(r,{filter:e})=>r.sort((t,o)=>o.filters[e]?.order==null||isNaN(o.filters[e]?.order)?-1:t.filters[e]?.order==null||isNaN(t.filters[e]?.order)?1:t.filters[e].order-o.filters[e].order),B=(r,{search:e})=>e?.length?(e=e.toLowerCase(),r.filter(t=>(t.title??"").toLowerCase().includes(e))):r,E=class extends N{static properties={filter:{type:String,attribute:"filter",reflect:!0},filtered:{type:String,attribute:"filtered"},search:{type:String,attribute:"search",reflect:!0},sort:{type:String,attribute:"sort",default:n.authored,reflect:!0},types:{type:String,attribute:"types",reflect:!0},limit:{type:Number,attribute:"limit"},page:{type:Number,attribute:"page",reflect:!0},singleApp:{type:String,attribute:"single-app",reflect:!0},hasMore:{type:Boolean},displayResult:{type:Boolean,attribute:"display-result"},resultCount:{type:Number},sidenav:{type:Object}};mobileAndTablet=new m(this,_);constructor(){super(),this.filter="all",this.hasMore=!1,this.resultCount=void 0,this.displayResult=!1}render(){return l`${this.header} - ${this.footer}`}updated(e){if(!this.querySelector("merch-card"))return;let t=window.scrollY||document.documentElement.scrollTop,o=[...this.children].filter(s=>s.tagName==="MERCH-CARD");if(o.length===0)return;e.has("singleApp")&&this.singleApp&&o.forEach(s=>{s.updateFilters(s.name===this.singleApp)});let n=this.sort===i.alphabetical?D:k,h=[O,L,H,n].reduce((s,a)=>a(s,this),o).map((s,a)=>[s,a]);if(this.resultCount=h.length,this.page&&this.limit){let s=this.page*this.limit;this.hasMore=h.length>s,h=h.filter(([,a])=>a{if(d.has(s)){let a=d.get(s);s.style.order=a,s.setAttribute("tabindex",a+1),s.size=s.filters[this.filter]?.size,s.style.removeProperty("display"),s.requestUpdate()}else s.style.display="none",s.size=void 0,s.style.removeProperty("order")}),window.scrollTo(0,t),this.updateComplete.then(()=>{let s=this.shadowRoot.getElementById("resultText")?.firstElementChild?.assignedElements?.()?.[0];s&&C(s,{resultCount:this.resultCount,searchTerm:this.search,filter:this.sidenav?.filters.selectedText})})}connectedCallback(){super.connectedCallback(),this.filtered?(this.filter=this.filtered,this.page=1):this.startDeeplink(),this.sidenav=document.querySelector("merch-sidenav")}disconnectedCallback(){super.disconnectedCallback(),this.stopDeeplink?.()}get header(){if(!this.filtered)return l` `}};customElements.define("merch-quantity-select",s);export{s as MerchQuantitySelect}; -//# sourceMappingURL=merch-quantity-select.js.map diff --git a/libs/deps/mas/merch-secure-transaction.js b/libs/deps/mas/merch-secure-transaction.js index af0e609c50..4542cb127d 100644 --- a/libs/deps/mas/merch-secure-transaction.js +++ b/libs/deps/mas/merch-secure-transaction.js @@ -1,4 +1,4 @@ -import{LitElement as p,html as n}from"/libs/deps/lit-all.min.js";import{css as l}from"/libs/deps/lit-all.min.js";var i=l` +import{LitElement as p,html as n}from"../lit-all.min.js";import{css as l}from"../lit-all.min.js";var i=l` #label { align-items: center; cursor: pointer; @@ -30,4 +30,3 @@ import{LitElement as p,html as n}from"/libs/deps/lit-all.min.js";import{css as l > `:o}};window.customElements.define(a,t);export{t as default}; -//# sourceMappingURL=merch-secure-transaction.js.map diff --git a/libs/deps/mas/merch-sidenav.js b/libs/deps/mas/merch-sidenav.js index 8376332a79..09057e8ecc 100644 --- a/libs/deps/mas/merch-sidenav.js +++ b/libs/deps/mas/merch-sidenav.js @@ -1,4 +1,4 @@ -import{html as k,css as H,LitElement as P}from"/libs/deps/lit-all.min.js";var a=class{constructor(e,t){this.key=Symbol("match-media-key"),this.matches=!1,this.host=e,this.host.addController(this),this.media=window.matchMedia(t),this.matches=this.media.matches,this.onChange=this.onChange.bind(this),e.addController(this)}hostConnected(){var e;(e=this.media)==null||e.addEventListener("change",this.onChange)}hostDisconnected(){var e;(e=this.media)==null||e.removeEventListener("change",this.onChange)}onChange(e){this.matches!==e.matches&&(this.matches=e.matches,this.host.requestUpdate(this.key,!this.matches))}};import{css as N}from"/libs/deps/lit-all.min.js";var c=N` +import{html as k,css as H,LitElement as P}from"/libs/deps/lit-all.min.js";var r=class{constructor(e,t){this.key=Symbol("match-media-key"),this.matches=!1,this.host=e,this.host.addController(this),this.media=window.matchMedia(t),this.matches=this.media.matches,this.onChange=this.onChange.bind(this),e.addController(this)}hostConnected(){var e;(e=this.media)==null||e.addEventListener("change",this.onChange)}hostDisconnected(){var e;(e=this.media)==null||e.removeEventListener("change",this.onChange)}onChange(e){this.matches!==e.matches&&(this.matches=e.matches,this.host.requestUpdate(this.key,!this.matches))}};import{css as L}from"/libs/deps/lit-all.min.js";var c=L` h2 { font-size: 11px; font-style: normal; @@ -9,7 +9,7 @@ import{html as k,css as H,LitElement as P}from"/libs/deps/lit-all.min.js";var a= line-height: 32px; color: #747474; } -`;import{html as L,LitElement as D}from"/libs/deps/lit-all.min.js";var x="merch-search:change";var v="merch-sidenav:select";function d(s,e){let t;return function(){let o=this,n=arguments;clearTimeout(t),t=setTimeout(()=>s.apply(o,n),e)}}var g="hashchange";function i(s=window.location.hash){let e=[],t=s.replace(/^#/,"").split("&");for(let o of t){let[n,l=""]=o.split("=");n&&e.push([n,decodeURIComponent(l.replace(/\+/g," "))])}return Object.fromEntries(e)}function r(s,e){if(s.deeplink){let t={};t[s.deeplink]=e,A(t)}}function A(s){let e=new URLSearchParams(window.location.hash.slice(1));Object.entries(s).forEach(([n,l])=>{l?e.set(n,l):e.delete(n)}),e.sort();let t=e.toString();if(t===window.location.hash)return;let o=window.scrollY||document.documentElement.scrollTop;window.location.hash=t,window.scrollTo(0,o)}function b(s){let e=()=>{if(window.location.hash&&!window.location.hash.includes("="))return;let t=i(window.location.hash);s(t)};return e(),window.addEventListener(g,e),()=>{window.removeEventListener(g,e)}}var p=class extends D{static properties={deeplink:{type:String}};get search(){return this.querySelector("sp-search")}constructor(){super(),this.handleInput=()=>{r(this,this.search.value),this.search.value&&this.dispatchEvent(new CustomEvent(x,{bubbles:!0,composed:!0,detail:{type:"search",value:this.search.value}}))},this.handleInputDebounced=d(this.handleInput.bind(this))}connectedCallback(){super.connectedCallback(),this.search&&(this.search.addEventListener("input",this.handleInputDebounced),this.search.addEventListener("submit",this.handleInputSubmit),this.updateComplete.then(()=>{this.setStateFromURL()}),this.startDeeplink())}disconnectedCallback(){super.disconnectedCallback(),this.search.removeEventListener("input",this.handleInputDebounced),this.search.removeEventListener("submit",this.handleInputSubmit),this.stopDeeplink?.()}setStateFromURL(){let t=i()[this.deeplink];t&&(this.search.value=t)}startDeeplink(){this.stopDeeplink=b(({search:e})=>{this.search.value=e??""})}handleInputSubmit(e){e.preventDefault()}render(){return L``}};customElements.define("merch-search",p);import{html as C,LitElement as R,css as M}from"/libs/deps/lit-all.min.js";var m=class extends R{static properties={sidenavListTitle:{type:String},label:{type:String},deeplink:{type:String,attribute:"deeplink"},selectedText:{type:String,reflect:!0,attribute:"selected-text"},selectedValue:{type:String,reflect:!0,attribute:"selected-value"}};static styles=[M` +`;import{html as N,LitElement as R}from"/libs/deps/lit-all.min.js";function d(s,e){let t;return function(){let o=this,i=arguments;clearTimeout(t),t=setTimeout(()=>s.apply(o,i),e)}}var x="merch-search:change";var v="merch-sidenav:select";var g="hashchange";function n(s=window.location.hash){let e=[],t=s.replace(/^#/,"").split("&");for(let o of t){let[i,l=""]=o.split("=");i&&e.push([i,decodeURIComponent(l.replace(/\+/g," "))])}return Object.fromEntries(e)}function a(s,e){if(s.deeplink){let t={};t[s.deeplink]=e,A(t)}}function A(s){let e=new URLSearchParams(window.location.hash.slice(1));Object.entries(s).forEach(([i,l])=>{l?e.set(i,l):e.delete(i)}),e.sort();let t=e.toString();if(t===window.location.hash)return;let o=window.scrollY||document.documentElement.scrollTop;window.location.hash=t,window.scrollTo(0,o)}function b(s){let e=()=>{if(window.location.hash&&!window.location.hash.includes("="))return;let t=n(window.location.hash);s(t)};return e(),window.addEventListener(g,e),()=>{window.removeEventListener(g,e)}}var p=class extends R{static properties={deeplink:{type:String}};get search(){return this.querySelector("sp-search")}constructor(){super(),this.handleInput=()=>{a(this,this.search.value),this.search.value&&this.dispatchEvent(new CustomEvent(x,{bubbles:!0,composed:!0,detail:{type:"search",value:this.search.value}}))},this.handleInputDebounced=d(this.handleInput.bind(this))}connectedCallback(){super.connectedCallback(),this.search&&(this.search.addEventListener("input",this.handleInputDebounced),this.search.addEventListener("submit",this.handleInputSubmit),this.updateComplete.then(()=>{this.setStateFromURL()}),this.startDeeplink())}disconnectedCallback(){super.disconnectedCallback(),this.search.removeEventListener("input",this.handleInputDebounced),this.search.removeEventListener("submit",this.handleInputSubmit),this.stopDeeplink?.()}setStateFromURL(){let t=n()[this.deeplink];t&&(this.search.value=t)}startDeeplink(){this.stopDeeplink=b(({search:e})=>{this.search.value=e??""})}handleInputSubmit(e){e.preventDefault()}render(){return N``}};customElements.define("merch-search",p);import{html as C,LitElement as D,css as M}from"/libs/deps/lit-all.min.js";var m=class extends D{static properties={sidenavListTitle:{type:String},label:{type:String},deeplink:{type:String,attribute:"deeplink"},selectedText:{type:String,reflect:!0,attribute:"selected-text"},selectedValue:{type:String,reflect:!0,attribute:"selected-value"}};static styles=[M` :host { display: block; contain: content; @@ -30,7 +30,7 @@ import{html as k,css as H,LitElement as P}from"/libs/deps/lit-all.min.js";var a= ) ); } - `,c];constructor(){super(),this.handleClickDebounced=d(this.handleClick.bind(this))}selectElement(e,t=!0){e.parentNode.tagName==="SP-SIDENAV-ITEM"&&this.selectElement(e.parentNode,!1),e.firstElementChild?.tagName==="SP-SIDENAV-ITEM"&&(e.expanded=!0),t&&(this.selectedElement=e,this.selectedText=e.label,this.selectedValue=e.value,setTimeout(()=>{e.selected=!0},1),this.dispatchEvent(new CustomEvent(v,{bubbles:!0,composed:!0,detail:{type:"sidenav",value:this.selectedValue,elt:this.selectedElement}})))}setStateFromURL(){let t=i()[this.deeplink]??"all";if(t){let o=this.querySelector(`sp-sidenav-item[value="${t}"]`);if(!o)return;this.updateComplete.then(()=>{this.selectElement(o)})}}handleClick({target:e}){let{value:t,parentNode:o}=e;this.selectElement(e),o&&o.tagName==="SP-SIDENAV"&&(r(this,t),e.selected=!0,o.querySelectorAll("sp-sidenav-item[expanded],sp-sidenav-item[selected]").forEach(n=>{n.value!==t&&(n.expanded=!1,n.selected=!1)}))}selectionChanged({target:{value:e,parentNode:t}}){this.selectElement(this.querySelector(`sp-sidenav-item[value="${e}"]`)),r(this,e)}connectedCallback(){super.connectedCallback(),this.addEventListener("click",this.handleClickDebounced),this.updateComplete.then(()=>{this.setStateFromURL()})}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener("click",this.handleClickDebounced)}render(){return C`
{e.selected=!0},1),this.dispatchEvent(new CustomEvent(v,{bubbles:!0,composed:!0,detail:{type:"sidenav",value:this.selectedValue,elt:this.selectedElement}})))}setStateFromURL(){let t=n()[this.deeplink]??"all";if(t){let o=this.querySelector(`sp-sidenav-item[value="${t}"]`);if(!o)return;this.updateComplete.then(()=>{this.selectElement(o)})}}handleClick({target:e}){let{value:t,parentNode:o}=e;this.selectElement(e),o&&o.tagName==="SP-SIDENAV"&&(a(this,t),e.selected=!0,o.querySelectorAll("sp-sidenav-item[expanded],sp-sidenav-item[selected]").forEach(i=>{i.value!==t&&(i.expanded=!1,i.selected=!1)}))}selectionChanged({target:{value:e,parentNode:t}}){this.selectElement(this.querySelector(`sp-sidenav-item[value="${e}"]`)),a(this,e)}connectedCallback(){super.connectedCallback(),this.addEventListener("click",this.handleClickDebounced),this.updateComplete.then(()=>{this.setStateFromURL()})}disconnectedCallback(){super.disconnectedCallback(),this.removeEventListener("click",this.handleClickDebounced)}render(){return C`
@@ -58,7 +58,7 @@ import{html as k,css as H,LitElement as P}from"/libs/deps/lit-all.min.js";var a= display: flex; flex-direction: column; } - `;setStateFromURL(){this.selectedValues=[];let{types:e}=i();e&&(this.selectedValues=e.split(","),this.selectedValues.forEach(t=>{let o=this.querySelector(`sp-checkbox[name=${t}]`);o&&(o.checked=!0)}))}selectionChanged(e){let{target:t}=e,o=t.getAttribute("name");if(o){let n=this.selectedValues.indexOf(o);t.checked&&n===-1?this.selectedValues.push(o):!t.checked&&n>=0&&this.selectedValues.splice(n,1)}r(this,this.selectedValues.join(","))}connectedCallback(){super.connectedCallback(),this.updateComplete.then(async()=>{this.setStateFromURL()})}render(){return V`
+ `;setStateFromURL(){this.selectedValues=[];let{types:e}=n();e&&(this.selectedValues=e.split(","),this.selectedValues.forEach(t=>{let o=this.querySelector(`sp-checkbox[name=${t}]`);o&&(o.checked=!0)}))}selectionChanged(e){let{target:t}=e,o=t.getAttribute("name");if(o){let i=this.selectedValues.indexOf(o);t.checked&&i===-1?this.selectedValues.push(o):!t.checked&&i>=0&&this.selectedValues.splice(i,1)}a(this,this.selectedValues.join(","))}connectedCallback(){super.connectedCallback(),this.updateComplete.then(async()=>{this.setStateFromURL()})}render(){return V`

${this.sidenavCheckboxTitle}

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

${this.sidenavTitle}

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

Save over 30% on Creative Cloud All Apps.

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

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

See terms

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

Buy now

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

Best for:

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

Storage:

+

100 GB of cloud storage

+

See system requirements

+
+

Photography

+

+ +

+
+

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

+

Learn more

+
+ +
+ + +
+

Best for:

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

Storage:

+

100 GB of cloud storage

+

See system requirements

+
+

Photography

+

+ +
Inclusive of VAT
+

+
+

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

+

Learn more

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

Creative Cloud All Apps

+

+
Desktop
+
+

+

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

+

See what’s included | Learn more

+
+

Buy now free trial

+
+
+
+ + +
+

Best for:

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

Storage:

+

100 GB of cloud storage

+

See system requirements

+
+

Photography

+

+ +

+
+

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

+

Learn more

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

Best for:

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

Storage:

+

100 GB of cloud storage

+

See system requirements

+
+

Photography

+

+ +

+
+

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

+

Learn more

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

Creative Cloud All Apps

+

+
Desktop
+
+

+

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

+

See what’s included | Learn more

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

Buy now free trial

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

This is promo text with a + link

+
+

+

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

+
+

+

Creative Cloud All Apps +

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

+ free + trial + Buy now +

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

+
+

+

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

+
+

+

Illustrator

+

+

+ free trial + Buy nowBuy +

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

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

+
+

+

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

+

See terms.

+
+

+

Illustrator

+

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

+
+
+

+ Free + trial + Buy now +

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

This is promo text with a + link

+
+

+

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

+
+

+

Creative Cloud All Apps +

+ +
+

+ free + trial + Buy now +

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

This is promo text with a + link

+
+

+

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

+
+

+

Creative Cloud All Apps +

+ +
+

+ free + trial + Buy now +

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

This is promo text with a + link

+
+

+

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

+
+

+

Creative Cloud All Apps +

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

+ free + trial + Buy now +

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

This is promo text with a + link

+
+

+

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

+
+

+

Creative Cloud All Apps +

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

+ free + trial + Buy now +

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

This is promo text with a + link

+
+

+

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

+
+

+

Creative Cloud All Apps +

+ +
+

+ free + trial + Buy now +

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

This is promo text <

-
- - -
-

Best for:

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

Storage:

-

100 GB of cloud storage

-

See system requirements

-
-

Photography

-

- -

-
-

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

-

Learn more

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

Best for:

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

Storage:

-

100 GB of cloud storage

-

See system requirements

-
-

Photography

-

- -

-
-

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

-

Learn more

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

Creative Cloud All Apps

-

-
Desktop
-
-

-

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

-

See what’s included | Learn more

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

Buy now free trial

-
-
-

Creative Cloud All Apps

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

-

Photography

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

-

Photography

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

Desktop + Mobile

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

Desktop + Mobile

+
+ + + undefined +

CC All Apps

+

+

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

+

+
+
+ Get 20+ creative apps including Photoshop and Illustrator.  +
+
+
+
+ diff --git a/libs/features/mas/web-components/test/merch-card.test.html.js b/libs/features/mas/web-components/test/merch-card.test.html.js index e949bcffc1..0bbd0c477d 100644 --- a/libs/features/mas/web-components/test/merch-card.test.html.js +++ b/libs/features/mas/web-components/test/merch-card.test.html.js @@ -13,7 +13,7 @@ import '../src/merch-quantity-select.js'; import { appendMiloStyles, delay } from './utils.js'; import { mockIms } from './mocks/ims.js'; import { withWcs } from './mocks/wcs.js'; -import mas from './mocks/mas.js'; +import mas from './mas.js'; const skipTests = sessionStorage.getItem('skipTests'); @@ -30,18 +30,6 @@ runTests(async () => { it('should exist in the HTML document', async () => { expect(document.querySelector('merch-card')).to.exist; }); - it('should exist special offers card in HTML document', async () => { - expect( - document.querySelector('merch-card[variant="special-offers"]'), - ).to.exist; - }); - it('should display a merch-badge', async () => { - expect( - document - .querySelector('merch-card[variant="special-offers"]') - .shadowRoot.querySelector('.special-offers-badge'), - ).to.exist; - }); it('should exist segment card in HTML document', async () => { expect(document.querySelector('merch-card[variant="segment"]')).to .exist; @@ -81,24 +69,6 @@ runTests(async () => { 'm2m,stock-m2m', ); }); - it('should display an action menu on hover for catalog variant', async () => { - const catalogCard = document.querySelector( - 'merch-card[variant="catalog"]', - ); - catalogCard.dispatchEvent( - new MouseEvent('mouseover', { bubbles: true }), - ); - await delay(100); - const shadowRoot = catalogCard.shadowRoot; - const actionMenu = shadowRoot.querySelector('.action-menu'); - const actionMenuContent = shadowRoot.querySelector( - '.action-menu-content', - ); - expect(actionMenu.classList.contains('invisible')).to.be.true; - expect(actionMenuContent.classList.contains('hidden')).to.be.true; - expect(actionMenu).to.exist; - expect(actionMenuContent).to.exist; - }); it('should have and interact with quantity-selector', async () => { const plansCard = document.querySelector('merch-card[type="q-ty"]'); @@ -122,13 +92,6 @@ runTests(async () => { }); }); - it('should return title for special offer card', async () => { - const title = document.querySelector( - 'merch-card[variant="special-offers"]', - ).title; - expect(title).to.equal('INDIVIDUALS'); - }); - it('should return title for segment card', async () => { const title = document.querySelector( 'merch-card[variant="segment"]', diff --git a/libs/features/mas/web-components/test/merch-datasource.test.html b/libs/features/mas/web-components/test/merch-datasource.test.html index d8696505a1..db8afd95dd 100644 --- a/libs/features/mas/web-components/test/merch-datasource.test.html +++ b/libs/features/mas/web-components/test/merch-datasource.test.html @@ -1,42 +1,50 @@ - + + + + + + Merch Datasource Web Component test page + + + - - - - - Merch Datasource Web Component test page - - - - - - -
- -
- - - + + +
+ + diff --git a/libs/features/mas/web-components/test/merch-datasource.test.html.js b/libs/features/mas/web-components/test/merch-datasource.test.html.js index 1a0373eafc..70ba7f4ee4 100644 --- a/libs/features/mas/web-components/test/merch-datasource.test.html.js +++ b/libs/features/mas/web-components/test/merch-datasource.test.html.js @@ -1,18 +1,35 @@ import { runTests } from '@web/test-runner-mocha'; -import { expect } from '@esm-bundle/chai'; +import chai from '@esm-bundle/chai'; +import chaiAsPromised from '@esm-bundle/chai-as-promised'; import { mockFetch } from './mocks/fetch.js'; import { withWcs } from './mocks/wcs.js'; -import mas from './mocks/mas.js'; +import { withAem } from './mocks/aem.js'; +import mas from './mas.js'; import { getTemplateContent } from './utils.js'; +chai.use(chaiAsPromised); +const expect = chai.expect; + runTests(async () => { - await mockFetch(withWcs); + const [cc, photoshop] = await fetch( + 'mocks/sites/cf/fragments/search/default.json', + ) + .then((res) => res.json()) + .then(({ items }) => items); + await mas(); await customElements.whenDefined('merch-datasource'); const { cache } = document.createElement('merch-datasource'); describe('merch-datasource web component', () => { + let aemMock; + + beforeEach(async () => { + [, aemMock] = await mockFetch(withWcs, withAem); + cache.clear(); + }); + it('has fragment cache', async () => { expect(cache).to.exist; expect(cache.has('/test')).to.false; @@ -23,15 +40,37 @@ runTests(async () => { }); it('renders a merch card from cache', async () => { - cache.clear(); + cache.add(cc, photoshop); + const [ccCard, photoshopCard] = getTemplateContent('cards'); + document.querySelector('main').append(ccCard, photoshopCard); + expect(aemMock.count).to.equal(0); + }); + + it('re-renders a card after clearing the cache', async () => { + const [, , ccCard] = getTemplateContent('cards'); //special offers students-and-teachers. + const dataSource = ccCard.querySelector('merch-datasource'); + + document.querySelector('main').append(ccCard); + await dataSource.updateComplete; + const before = ccCard.innerHTML; + ccCard.footerSlot.test = true; + await dataSource.refresh(true); + await dataSource.refresh(true); // for extra coverage + await dataSource.updateComplete; + const after = ccCard.innerHTML; + expect(before).to.equal(after); + expect(ccCard.footerSlot.test).to.undefined; + expect(aemMock.count).to.equal(2); + }); - const searchResult = await fetch( - '/test/mocks/sites/cf/fragments/search/default.json', - ).then((res) => res.json()); - cache.add(...searchResult.items); + it('ignores incomplete markup', async () => { + const [, , , cardWithMissingPath] = getTemplateContent('cards'); + const dataSource = + cardWithMissingPath.querySelector('merch-datasource'); - const cards = getTemplateContent('cards'); - document.querySelector('main').append(...cards); + document.querySelector('main').append(cardWithMissingPath); + await expect(dataSource.updateComplete).to.be.rejectedWith('datasource is not correctly configured', + ); }); }); }); diff --git a/libs/features/mas/web-components/test/merch-offer-select.test.html.js b/libs/features/mas/web-components/test/merch-offer-select.test.html.js index a43ee6a8b9..37ccfd58c9 100644 --- a/libs/features/mas/web-components/test/merch-offer-select.test.html.js +++ b/libs/features/mas/web-components/test/merch-offer-select.test.html.js @@ -3,7 +3,6 @@ import { expect } from '@esm-bundle/chai'; import { mockLana } from './mocks/lana.js'; import { mockFetch } from './mocks/fetch.js'; -import { mockConfig } from './mocks/config.js'; import '../src/merch-offer.js'; import '../src/merch-offer-select.js'; @@ -11,8 +10,7 @@ import '../src/merch-quantity-select.js'; import { delay } from './utils.js'; import { withWcs } from './mocks/wcs.js'; -import { withLiterals } from './mocks/literals.js'; -import mas from './mocks/mas.js'; +import mas from './mas.js'; function getDynamicElements(merchCard, merchOfferSelect) { const price = merchOfferSelect.price; @@ -51,7 +49,7 @@ const renderCard = async (id) => { runTests(async () => { mockLana(); - await mockFetch(withWcs, withLiterals); + await mockFetch(withWcs); await mas(); describe('merch-offer-select web component', async () => { diff --git a/libs/features/mas/web-components/test/merch-quantity-select.test.html.js b/libs/features/mas/web-components/test/merch-quantity-select.test.html.js index 243007acd5..e673a07a5c 100644 --- a/libs/features/mas/web-components/test/merch-quantity-select.test.html.js +++ b/libs/features/mas/web-components/test/merch-quantity-select.test.html.js @@ -3,15 +3,13 @@ import { expect } from '@esm-bundle/chai'; import { mockLana } from './mocks/lana.js'; import { mockFetch } from './mocks/fetch.js'; -import { mockConfig } from './mocks/config.js'; import '../src/merch-quantity-select.js'; import { appendMiloStyles, delay } from './utils.js'; import { ARROW_DOWN, ARROW_UP } from '../src/focus.js'; import { withWcs } from './mocks/wcs.js'; -import { withLiterals } from './mocks/literals.js'; -import mas from './mocks/mas.js'; +import mas from './mas.js'; const skipTests = sessionStorage.getItem('skipTests'); @@ -20,7 +18,7 @@ runTests(async () => { if (skipTests === null) { mockLana(); - await mockFetch(withWcs, withLiterals); + await mockFetch(withWcs); await mas(); describe('merch-quantity-selector web component', () => { const quantitySelect = document.querySelector( diff --git a/libs/features/mas/web-components/test/merch-subscription-panel.test.html.js b/libs/features/mas/web-components/test/merch-subscription-panel.test.html.js index d2c8ec511f..4f57ee8c17 100644 --- a/libs/features/mas/web-components/test/merch-subscription-panel.test.html.js +++ b/libs/features/mas/web-components/test/merch-subscription-panel.test.html.js @@ -15,10 +15,8 @@ import '../src/merch-subscription-panel.js'; import { delay } from './utils.js'; import { mockIms } from './mocks/ims.js'; -import { _$LE } from 'lit'; import { withWcs } from './mocks/wcs.js'; -import { withLiterals } from './mocks/literals.js'; -import mas from './mocks/mas.js'; +import mas from './mas.js'; import { getTemplateContent } from './utils.js'; const shouldSkipTests = sessionStorage.getItem('skipTests') === 'true'; @@ -38,7 +36,7 @@ runTests(async () => { // Activate mocks mockLana(); await mockIms(); - await mockFetch(withWcs, withLiterals); + await mockFetch(withWcs); await mas(); }); diff --git a/libs/features/mas/web-components/test/merch-twp-d2p.test.html.js b/libs/features/mas/web-components/test/merch-twp-d2p.test.html.js index 7437d75c6e..cf0b78667c 100644 --- a/libs/features/mas/web-components/test/merch-twp-d2p.test.html.js +++ b/libs/features/mas/web-components/test/merch-twp-d2p.test.html.js @@ -6,7 +6,6 @@ import { expect } from '@esm-bundle/chai'; import { mockLana } from './mocks/lana.js'; import { mockIms } from './mocks/ims.js'; import { mockFetch } from './mocks/fetch.js'; -import { mockConfig } from './mocks/config.js'; import '../src/global.css.js'; import '../src/merch-offer-select.js'; @@ -32,8 +31,7 @@ import { } from './merch-twp-d2p.utils.js'; import { appendMiloStyles, delay } from './utils.js'; import { withWcs } from './mocks/wcs.js'; -import { withLiterals } from './mocks/literals.js'; -import mas from './mocks/mas.js'; +import mas from './mas.js'; const ABM = 'ABM'; const PUF = 'PUF'; @@ -42,7 +40,7 @@ const M2M = 'M2M'; runTests(async () => { appendMiloStyles(); mockLana(); - await mockFetch(withWcs, withLiterals); + await mockFetch(withWcs); await mockIms(); await mas(); diff --git a/libs/features/mas/web-components/test/merch-whats-included.html.js b/libs/features/mas/web-components/test/merch-whats-included.html.js index f1873d01e9..0107310e6a 100644 --- a/libs/features/mas/web-components/test/merch-whats-included.html.js +++ b/libs/features/mas/web-components/test/merch-whats-included.html.js @@ -5,7 +5,7 @@ import { expect } from '@esm-bundle/chai'; import { mockLana } from '/test/mocks/lana.js'; import { mockFetch } from '/test/mocks/fetch.js'; import { mockConfig } from '/test/mocks/config.js'; -import mas from './mocks/mas.js'; +import mas from './mas.js'; import '../src/merch-mnemonic-list.js'; import '../src/merch-whats-included.js'; diff --git a/libs/features/mas/web-components/test/plans-modal.test.html.js b/libs/features/mas/web-components/test/plans-modal.test.html.js index 09f971e8eb..5ab3f369e1 100644 --- a/libs/features/mas/web-components/test/plans-modal.test.html.js +++ b/libs/features/mas/web-components/test/plans-modal.test.html.js @@ -3,18 +3,16 @@ import { expect } from '@esm-bundle/chai'; import { mockLana } from './mocks/lana.js'; import { mockFetch } from './mocks/fetch.js'; -import { mockConfig } from './mocks/config.js'; import './utils.js'; import { withWcs } from './mocks/wcs.js'; -import { withLiterals } from './mocks/literals.js'; -import mas from './mocks/mas.js'; +import mas from './mas.js'; const shouldSkipTests = sessionStorage.getItem('skipTests') ?? false; runTests(async () => { mockLana(); - await mockFetch(withWcs, withLiterals); + await mockFetch(withWcs); await mas(); if (shouldSkipTests !== 'true') { describe('plans-modal web component', () => { diff --git a/libs/features/mas/web-components/test/utils.js b/libs/features/mas/web-components/test/utils.js index 1fef69beac..9fc4e11acb 100644 --- a/libs/features/mas/web-components/test/utils.js +++ b/libs/features/mas/web-components/test/utils.js @@ -23,6 +23,12 @@ export async function toggleLargeDesktop() { } catch {} } +export async function toggleMobile() { + try { + await setViewport({ width: 430, height: 932 }); + } catch {} +} + window.skipTests = () => { window.location.hash = ''; sessionStorage.setItem('skipTests', 'true'); diff --git a/libs/features/mas/web-components/web-test-runner.config.mjs b/libs/features/mas/web-components/web-test-runner.config.mjs index a4a118f426..75cde5195c 100644 --- a/libs/features/mas/web-components/web-test-runner.config.mjs +++ b/libs/features/mas/web-components/web-test-runner.config.mjs @@ -19,7 +19,23 @@ export default { ], coverageConfig: { include: ['src/**'], - exclude: ['test/mocks/**', 'test/**', '**/node_modules/**'], + exclude: [ + 'test/mocks/**', + 'test/**', + '**/node_modules/**', + 'src/merch-twp-d2p.js', // on hold + 'src/aem.js', // WIP + 'src/bodyScrollLock.js', // todo + 'src/merch-subscription-panel.js', // on hold + 'src/ merch-whats-included.js', // on hold + ], + threshold: { + // TODO bump to 100% + branches: 85, + functions: 65, + statements: 85, + lines: 85, + }, }, debug: false, files: ['test/**/*.test.(js|html)'], diff --git a/libs/features/personalization/personalization.js b/libs/features/personalization/personalization.js index 4586cc158b..b51101f720 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'), @@ -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,95 @@ 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': 'strong a', + 'secondary-cta': 'em a', + 'action-area': '*:has(> em a, > strong a)', + 'any-marquee': '[class*="marquee"]', + 'any-header': ':is(h1, h2, h3, h4, h5, h6)', + }; + const otherSelectors = ['row', 'col']; + const htmlEls = [ + 'html', 'body', 'header', 'footer', 'main', + 'div', 'a', 'p', 'strong', 'em', 'picture', 'source', 'img', 'h', + 'ul', 'ol', 'li', + ]; + const startTextMatch = term.match(/^[a-zA-Z/./-]*/); + const startText = startTextMatch ? startTextMatch[0].toLowerCase() : ''; + const startTextPart1 = startText.split(/\.|:/)[0]; + const endNumberMatch = term.match(/[0-9]*$/); + const endNumber = endNumberMatch ? endNumberMatch[0] : ''; + 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(startTextPart1)) { + term = term.replace(startTextPart1, specificSelectors[startTextPart1]); + 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 +460,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 +472,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 +495,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 +514,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, @@ -540,6 +589,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; @@ -561,6 +629,9 @@ function parsePlaceholders(placeholders, config, selectedVariantName = '') { }, {}); config.placeholders = { ...(config.placeholders || {}), ...results }; } + + createMartechMetadata(placeholders, config, val); + return config; } @@ -842,13 +913,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 +955,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/libs/features/placeholders.js b/libs/features/placeholders.js index c782e046a9..73bdd5d1fa 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,18 +101,43 @@ 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; // 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; } + +export async function decoratePlaceholderArea({ + placeholderPath, + placeholderRequest, + nodes, +}) { + if (!nodes.length) return; + const config = getConfig(); + await fetchPlaceholders({ placeholderPath, config, placeholderRequest }); + const replaceNodes = nodes.map(async (nodeEl) => { + if (nodeEl.nodeType === Node.TEXT_NODE) { + nodeEl.nodeValue = await replaceText(nodeEl.nodeValue, config); + } else if (nodeEl.nodeType === Node.ELEMENT_NODE) { + const hrefVal = await replaceText(nodeEl.getAttribute('href'), config); + nodeEl.setAttribute('href', hrefVal); + } + }); + await Promise.all(replaceNodes); +} diff --git a/libs/features/webapp-prompt/webapp-prompt.css b/libs/features/webapp-prompt/webapp-prompt.css index 39d012916a..dee60577b1 100644 --- a/libs/features/webapp-prompt/webapp-prompt.css +++ b/libs/features/webapp-prompt/webapp-prompt.css @@ -2,6 +2,7 @@ .appPrompt { --pep-background-prompt: #ffffff; --pep-background-progress: #e9e9e9; + --pep-dismiss-button-focus-border-color: #3b63fb; display: none; } @@ -159,10 +160,7 @@ } .appPrompt-close:focus { - /* For Firefox */ - outline: auto; - /* For Chrome, Edge, and Safari */ - outline: 2px solid -webkit-focus-ring-color; + outline: 2px solid var(--pep-dismiss-button-focus-border-color); } .appPrompt-progressWrapper { diff --git a/libs/martech/attributes.js b/libs/martech/attributes.js index 9bb2b3dc5f..5d661d98dc 100644 --- a/libs/martech/attributes.js +++ b/libs/martech/attributes.js @@ -1,16 +1,31 @@ +import { getMetadata } from '../utils/utils.js'; + const INVALID_CHARACTERS = /[^\u00C0-\u1FFF\u2C00-\uD7FF\w]+/g; 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; } +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 +35,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 +43,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 +75,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/libs/martech/martech.js b/libs/martech/martech.js index 91906910bc..01492cadfe 100644 --- a/libs/martech/martech.js +++ b/libs/martech/martech.js @@ -118,14 +118,14 @@ export const getTargetPersonalization = async () => { const responseStart = Date.now(); window.addEventListener(ALLOY_SEND_EVENT, () => { const responseTime = calculateResponseTime(responseStart); - window.lana.log(`target response time: ${responseTime}`, { tags: 'errorType=info,module=martech' }); + window.lana.log(`target response time: ${responseTime}`, { tags: 'martech', errorType: 'i' }); }, { once: true }); let manifests = []; let propositions = []; const response = await waitForEventOrTimeout(ALLOY_SEND_EVENT, timeout); if (response.error) { - window.lana.log('target response time: ad blocker', { tags: 'errorType=info,module=martech' }); + window.lana.log('target response time: ad blocker', { tags: 'martech', errorType: 'i' }); return []; } if (response.timeout) { diff --git a/libs/navigation/navigation.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..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', @@ -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/scripts/scripts.js b/libs/scripts/scripts.js index 4388eea325..7c97265400 100644 --- a/libs/scripts/scripts.js +++ b/libs/scripts/scripts.js @@ -22,11 +22,19 @@ import locales from '../utils/locales.js'; const prodDomains = ['milo.adobe.com']; const stageDomainsMap = { - 'www.adobe.com': 'www.stage.adobe.com', - 'blog.adobe.com': 'blog.stage.adobe.com', - 'business.adobe.com': 'business.stage.adobe.com', - 'helpx.adobe.com': 'helpx.stage.adobe.com', - 'news.adobe.com': 'news.stage.adobe.com', + 'www.stage.adobe.com': { + 'www.adobe.com': 'origin', + 'helpx.adobe.com': 'helpx.stage.adobe.com', + }, + '--bacom--adobecom.hlx.live': { + 'business.adobe.com': 'origin', + 'news.adobe.com': 'main--news--adobecom.hlx.live', + }, + '--blog--adobecom.hlx.page': { + 'blog.adobe.com': 'origin', + 'business.adobe.com': 'main--bacom--adobecom.hlx.page', + }, + '.business-graybox.adobe.com': { 'business.adobe.com': 'origin' }, }; const config = { diff --git a/libs/styles/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/libs/styles/styles.css b/libs/styles/styles.css index 7413807321..749ca43a02 100644 --- a/libs/styles/styles.css +++ b/libs/styles/styles.css @@ -211,7 +211,8 @@ .detail-xl, .detail-l, .detail-m, -.detail-s { +.detail-s, +.detail-xs { margin: 0; } @@ -703,7 +704,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; } @@ -808,6 +809,10 @@ a.static:hover { text-decoration: underline; } +.copy-link { + font-weight: 700; +} + /* Tooltip */ .tooltip-wrapper { visibility: hidden; diff --git a/libs/utils/decorate.js b/libs/utils/decorate.js index ab3363489f..556670c79f 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'); @@ -51,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; @@ -94,10 +105,10 @@ export function handleFocalpoint(pic, child, removeChild) { image.style.objectPosition = `${x} ${y}`; } -export async function decorateBlockBg(block, node, { useHandleFocalpoint = false } = {}) { +export async function decorateBlockBg(block, node, { useHandleFocalpoint = false, className = 'background' } = {}) { const childCount = node.childElementCount; if (node.querySelector('img, video, a[href*=".mp4"]') || childCount > 1) { - node.classList.add('background'); + node.classList.add(className); const binaryVP = [['mobile-only'], ['tablet-only', 'desktop-only']]; const allVP = [['mobile-only'], ['tablet-only'], ['desktop-only']]; const viewports = childCount === 2 ? binaryVP : allVP; @@ -278,3 +289,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/libs/utils/helpers.js b/libs/utils/helpers.js index 7e6fc5ad62..f461139f3e 100644 --- a/libs/utils/helpers.js +++ b/libs/utils/helpers.js @@ -20,11 +20,20 @@ export function updateLinkWithLangRoot(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'; +/** + * 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 fetch(resource, options); + return link; } 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/libs/utils/utils.js b/libs/utils/utils.js index 52d3b0e190..07fee41492 100644 --- a/libs/utils/utils.js +++ b/libs/utils/utils.js @@ -620,17 +620,50 @@ export function decorateAutoBlock(a) { }); } +const decorateCopyLink = (a, evt) => { + const userAgent = navigator.userAgent.toLowerCase(); + const isMobile = /android|iphone|mobile/.test(userAgent) && !/ipad/.test(userAgent); + if (!isMobile || !navigator.share) { + a.remove(); + return; + } + const link = a.href.replace(evt, ''); + const isConButton = ['EM', 'STRONG'].includes(a.parentElement.nodeName) + || a.classList.contains('con-button'); + if (!isConButton) a.classList.add('static', 'copy-link'); + a.href = ''; + a.addEventListener('click', async (e) => { + e.preventDefault(); + if (navigator.share) await navigator.share({ title: link, url: link }); + }); +}; + +export function convertStageLinks({ anchors, config, hostname }) { + if (config.env?.name === 'prod' || !config.stageDomainsMap) return; + const matchedRules = Object.entries(config.stageDomainsMap) + .find(([domain]) => hostname.includes(domain)); + if (!matchedRules) return; + const [, domainsMap] = matchedRules; + [...anchors].forEach((a) => { + const matchedDomain = Object.keys(domainsMap) + .find((domain) => a.href.includes(domain)); + if (!matchedDomain) return; + a.href = a.href.replace(a.hostname, domainsMap[matchedDomain] === 'origin' + ? hostname + : domainsMap[matchedDomain]); + }); +} + export function decorateLinks(el) { const config = getConfig(); decorateImageLinks(el); const anchors = el.getElementsByTagName('a'); + const { hostname } = window.location; + convertStageLinks({ anchors, config, hostname }); return [...anchors].reduce((rdx, a) => { appendHtmlToLink(a); a.href = localizeLink(a.href); decorateSVG(a); - if (config.env?.name === 'stage' && config.stageDomainsMap?.[a.hostname]) { - a.href = a.href.replace(a.hostname, config.stageDomainsMap[a.hostname]); - } if (a.href.includes('#_blank')) { a.setAttribute('target', '_blank'); a.href = a.href.replace('#_blank', ''); @@ -656,6 +689,10 @@ export function decorateLinks(el) { window.adobeIMS?.signIn(); }); } + const copyEvent = '#_evt-copy'; + if (a.href.includes(copyEvent)) { + decorateCopyLink(a, copyEvent); + } return rdx; }, []); } @@ -727,33 +764,48 @@ 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, - 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; +}; + +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 +1039,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'); @@ -1013,10 +1066,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 +1121,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() { @@ -1174,11 +1227,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 +1268,6 @@ export async function loadArea(area = document) { } const config = getConfig(); - await decoratePlaceholders(area, config); - if (isDoc) { decorateDocumentExtras(); } 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..de3a6a19e5 --- /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 @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/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 new file mode 100644 index 0000000000..0df953c8eb --- /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..b3524815ed --- /dev/null +++ b/nala/features/georouting/georouting.test.js @@ -0,0 +1,136 @@ +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/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/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/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/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/pr.run.sh b/nala/utils/pr.run.sh new file mode 100755 index 0000000000..0d72a8d66a --- /dev/null +++ b/nala/utils/pr.run.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +TAGS="" +REPORTER="" +EXCLUDE_TAGS="--grep-invert nopr" +EXIT_STATUS=0 +PR_NUMBER=$(echo "$GITHUB_REF" | awk -F'/' '{print $3}') +echo "PR Number: $PR_NUMBER" + +# Extract feature branch name from GITHUB_HEAD_REF +FEATURE_BRANCH="$GITHUB_HEAD_REF" +# Replace "/" characters in the feature branch name with "-" +FEATURE_BRANCH=$(echo "$FEATURE_BRANCH" | sed 's/\//-/g') +echo "Feature Branch Name: $FEATURE_BRANCH" + +PR_BRANCH_LIVE_URL_GH="https://$FEATURE_BRANCH--$prRepo--$prOrg.hlx.live" +# set pr branch url as env +export PR_BRANCH_LIVE_URL_GH +export PR_NUMBER + +echo "PR Branch live URL: $PR_BRANCH_LIVE_URL_GH" +echo "*******************************" + +# Convert GitHub Tag(@) labels that can be grepped +for label in ${labels}; do + if [[ "$label" = \@* ]]; then + label="${label:1}" + TAGS+="|$label" + fi +done + +# Remove the first pipe from tags if tags are not empty +[[ ! -z "$TAGS" ]] && TAGS="${TAGS:1}" && TAGS="-g $TAGS" + +# Retrieve GitHub reporter parameter if not empty +# Otherwise, use reporter settings in playwright.config.js +REPORTER=$reporter +[[ ! -z "$REPORTER" ]] && REPORTER="--reporter $REPORTER" + +echo "*** Running Nala on $FEATURE_BRANCH ***" +echo "Tags : $TAGS" +echo "npx playwright test ${TAGS} ${EXCLUDE_TAGS} ${REPORTER}" + +# Navigate to the GitHub Action path and install dependencies +cd "$GITHUB_ACTION_PATH" || exit +npm ci +npx playwright install --with-deps + +# Run Playwright tests on the specific projects using root-level playwright.config.js +# This will be changed later +echo "*** Running tests on specific projects ***" +npx playwright test --config=./playwright.config.js ${TAGS} ${EXCLUDE_TAGS} --project=milo-live-chromium --project=milo-live-firefox ${REPORTER} || EXIT_STATUS=$? + +# Check if tests passed or failed +if [ $EXIT_STATUS -ne 0 ]; then + echo "Some tests failed. Exiting with error." + exit $EXIT_STATUS +else + echo "All tests passed successfully." +fi diff --git a/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; diff --git a/test/blocks/article-header/article-header.test.js b/test/blocks/article-header/article-header.test.js index cbcf096c1f..3056c62da5 100644 --- a/test/blocks/article-header/article-header.test.js +++ b/test/blocks/article-header/article-header.test.js @@ -1,7 +1,6 @@ import { readFile } from '@web/test-runner-commands'; import { expect } from '@esm-bundle/chai'; -import sinon from 'sinon'; - +import sinon, { stub } from 'sinon'; import { setConfig, getConfig } from '../../../libs/utils/utils.js'; import { delay, waitForElement } from '../../helpers/waitfor.js'; @@ -9,9 +8,11 @@ const locales = { '': { ietf: 'en-US', tk: 'hah7vzn.css' } }; const conf = { locales, miloLibs: 'http://localhost:2000/libs' }; setConfig(conf); const config = getConfig(); +window.lana = { log: stub() }; document.body.innerHTML = await readFile({ path: './mocks/body.html' }); const { default: init } = await import('../../../libs/blocks/article-header/article-header.js'); +const { loadTaxonomy } = await import('../../../libs/blocks/article-feed/article-helpers.js'); const invalidDoc = await readFile({ path: './mocks/body-invalid.html' }); @@ -20,10 +21,20 @@ describe('article header', () => { const block = document.body.querySelector('.article-header'); config.locale.contentRoot = '/test/blocks/article-header/mocks'; config.taxonomyRoot = undefined; - await init(block); }); + it('should log unknown topic', async () => { + try { + const div = document.createElement('div'); + div.setAttribute('data-topic-link', ['abcd']); + document.body.append(div); + await loadTaxonomy(); + expect(window.lana.log.args[0][0]).to.equal('Trying to get a link for an unknown topic: abcd (current page)'); + } catch (e) { + console.log(e); + } + }); it('creates article header block', () => { expect(document.body.querySelector('.article-category')).to.exist; expect(document.body.querySelector('.article-title')).to.exist; @@ -36,16 +47,16 @@ describe('article header', () => { it('should open link popup when share links are clicked', () => { // first share link is twitter const shareLink = document.querySelector('.article-byline-sharing a'); - const stub = sinon.stub(window, 'open'); + const windowStub = sinon.stub(window, 'open'); shareLink.click(); const url = encodeURIComponent(window.location.href); const title = encodeURIComponent(document.querySelector('h1').textContent); - expect(stub.calledOnce).to.be.true; - expect(stub.firstCall.args[0]).to.equal(`https://www.twitter.com/share?&url=${url}&text=${title}`); - expect(stub.firstCall.args[2]).to.equal('popup,top=233,left=233,width=700,height=467'); + expect(windowStub.calledOnce).to.be.true; + expect(windowStub.firstCall.args[0]).to.equal(`https://www.twitter.com/share?&url=${url}&text=${title}`); + expect(windowStub.firstCall.args[2]).to.equal('popup,top=233,left=233,width=700,height=467'); - stub.restore(); + windowStub.restore(); }); it('updates share text after deferred event', async () => { @@ -94,6 +105,21 @@ describe('article header', () => { const categoryLink = document.querySelector('.article-category a'); expect(categoryLink.href.includes('/topics/')).to.be.true; }); + + it('adds an author image from a link', () => { + const img = document.querySelector('.article-author-image img'); + const link = document.querySelector('.article-author a'); + expect(img).to.exist; + expect(link).to.exist; + }); + + it('adds an author image from data attribute', async () => { + await init(document.querySelector('#article-header-no-author-link')); + const img = document.querySelector('.article-author-image img'); + const author = document.querySelector('.article-author [data-author-page]'); + expect(img).to.exist; + expect(author).to.exist; + }); }); describe('test the invalid article header', () => { @@ -111,7 +137,6 @@ describe('test the invalid article header', () => { it('adds invalid-date when invalid date is provided', async () => { await init(document.body.querySelector('.article-header')); - const date = await waitForElement('.article-date-invalid'); expect(date).to.exist; }); diff --git a/test/blocks/article-header/mocks/body.html b/test/blocks/article-header/mocks/body.html index ffe3737645..ec8471006d 100644 --- a/test/blocks/article-header/mocks/body.html +++ b/test/blocks/article-header/mocks/body.html @@ -39,3 +39,31 @@

+
+ +
+
+
+

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

+
+
+
+
+

Adobe Communications Team

+

12-05-2022

+
+
+
+
+

+ + + Caption +

+
+
+ diff --git a/test/blocks/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

+
+
+ + + +
+
+
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/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/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' }, diff --git a/test/blocks/global-footer/global-footer.test.js b/test/blocks/global-footer/global-footer.test.js index 0010dfdc56..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 () => { @@ -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..945328606d 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,30 @@ 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'); + }); + }); + + describe('Client search feature in global navigation', () => { + it('should append the feds-client-search div when search is enabled', async () => { + await createFullGlobalNavigation({ customConfig: { searchEnabled: 'on' } }); + expect(document.querySelector(selectors.topNavWrapper).classList.contains('feds-client-search')).to.exist; + }); + }); }); diff --git a/test/blocks/global-navigation/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 +

+
+
+ +
+
+ +
+ +
+ `; diff --git a/test/blocks/instagram/mocks/embed-utils.js b/test/blocks/instagram/mocks/embed-utils.js index 4e37247e24..88da37b5cf 100644 --- a/test/blocks/instagram/mocks/embed-utils.js +++ b/test/blocks/instagram/mocks/embed-utils.js @@ -25,8 +25,30 @@ 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(); + export const loadStyle = stub(); export const loadScript = stub(); 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..6aef9cfc38 100644 --- a/test/blocks/marketo/mocks/marketo-utils.js +++ b/test/blocks/marketo/mocks/marketo-utils.js @@ -63,3 +63,9 @@ export function createIntersectionObserver({ el, callback /* , once = true, opti } export const localizeLink = (href) => href; + +export const loadLink = stub().returns(new Promise((resolve) => { + resolve(); +})); + +export const customFetch = stub(); diff --git a/test/blocks/merch-twp/merch-twp.2cards.test.html b/test/blocks/merch-twp/merch-twp.2cards.test.html index c76061ab12..982b30039a 100644 --- a/test/blocks/merch-twp/merch-twp.2cards.test.html +++ b/test/blocks/merch-twp/merch-twp.2cards.test.html @@ -41,7 +41,7 @@ setConfig(config); runTests(async () => { - describe('Merch TWP', async () => { + describe.skip('Merch TWP', async () => { // not finished and on hold before(async () => { mockIms(); await mockFetch(); diff --git a/test/blocks/merch/merch.test.js b/test/blocks/merch/merch.test.js index 4a7641f9c2..cee0f412cc 100644 --- a/test/blocks/merch/merch.test.js +++ b/test/blocks/merch/merch.test.js @@ -12,14 +12,12 @@ import merch, { buildCta, getCheckoutContext, initService, - fetchLiterals, fetchCheckoutLinkConfigs, getCheckoutLinkConfig, getDownloadAction, fetchEntitlements, getModalAction, getCheckoutAction, - PRICE_LITERALS_URL, PRICE_TEMPLATE_REGULAR, getMasBase, appendTabName, @@ -151,7 +149,6 @@ describe('Merch Block', () => { document.head.innerHTML = await readMockText('/test/blocks/merch/mocks/head.html'); document.body.innerHTML = await readMockText('/test/blocks/merch/mocks/body.html'); ({ setCheckoutLinkConfigs, setSubscriptionsData } = await mockFetch()); - config.commerce = { priceLiteralsPromise: fetchLiterals(PRICE_LITERALS_URL) }; setCheckoutLinkConfigs(CHECKOUT_LINK_CONFIGS); }); diff --git a/test/blocks/merch/mocks/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/merch/mocks/fetch.js b/test/blocks/merch/mocks/fetch.js index e67d70369d..08bef2ad7b 100644 --- a/test/blocks/merch/mocks/fetch.js +++ b/test/blocks/merch/mocks/fetch.js @@ -1,6 +1,4 @@ import sinon from 'sinon'; - -import { PRICE_LITERALS_URL } from '../../../../libs/blocks/merch/merch.js'; import { applyPlanType } from '../../../../libs/deps/mas/commerce.js'; const { fetch } = window; @@ -18,7 +16,6 @@ export const readMockText = async (path) => { export async function mockFetch() { // this path allows to import this mock from tests for other blocks (e.g. commerce) const basePath = '/test/blocks/merch/mocks/'; - const literals = await readMockJSON(`${basePath}literals.json`); const offers = await readMockJSON(`${basePath}offers.json`); const namedOffers = await readMockJSON(`${basePath}named-offers.json`); @@ -51,14 +48,8 @@ export async function mockFetch() { }; sinon.stub(window, 'fetch').callsFake((...args) => { - const { href, pathname, searchParams } = new URL(String(args[0])); - // literals mock - if (href === PRICE_LITERALS_URL) { - return Promise.resolve({ - ok: true, - json: () => Promise.resolve(literals), - }); - } + const { pathname, searchParams } = new URL(String(args[0])); + // wcs mock if (pathname.endsWith('/web_commerce_artifact')) { const osis = searchParams.get('offer_selector_ids').split(','); diff --git a/test/blocks/merch/mocks/ims.js b/test/blocks/merch/mocks/ims.js index a3ba0d18da..8e1e79ba86 100644 --- a/test/blocks/merch/mocks/ims.js +++ b/test/blocks/merch/mocks/ims.js @@ -1,7 +1,4 @@ -import { loadIms } from '../../../../libs/utils/utils.js'; - export async function mockIms(countryCode, userId = 1) { - loadIms(); window.adobeIMS = { initialized: true, isSignedInUser: () => !!countryCode, @@ -9,7 +6,6 @@ export async function mockIms(countryCode, userId = 1) { getAccessToken: () => ({ token: 'test_client_token' }), adobeIdData: { client_id: 'test_client_id' }, }; - window.adobeid.onReady(); } export function unmockIms() { diff --git a/test/blocks/merch/mocks/literals.json b/test/blocks/merch/mocks/literals.json deleted file mode 100644 index c29f836e35..0000000000 --- a/test/blocks/merch/mocks/literals.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "data": [{ - "lang": "en", - "recurrenceLabel": "{recurrenceTerm, select, MONTH {/mo} YEAR {/yr} other {}}", - "recurrenceAriaLabel": "{recurrenceTerm, select, MONTH {per month} YEAR {per year} other {}}", - "perUnitLabel": "{perUnit, select, LICENSE {per license} other {}}", - "perUnitAriaLabel": "{perUnit, select, LICENSE {per license} other {}}", - "freeLabel": "Free", - "freeAriaLabel": "Free", - "taxExclusiveLabel": "{taxTerm, select, GST {excl. GST} VAT {excl. VAT} TAX {excl. tax} IVA {excl. IVA} SST {excl. SST} KDV {excl. KDV} other {}}", - "taxInclusiveLabel": "{taxTerm, select, GST {incl. GST} VAT {incl. VAT} TAX {incl. tax} IVA {incl. IVA} SST {incl. SST} KDV {incl. KDV} other {}}", - "alternativePriceAriaLabel": "Alternatively at {alternativePrice}", - "strikethroughAriaLabel": "Regularly at {strikethroughPrice}" - }], - ":type": "sheet" -} diff --git a/test/blocks/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(); }); }); 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

+ + +
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/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/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/blocks/text/mocks/body.html b/test/blocks/text/mocks/body.html index 3c10028ad6..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. +

@@ -220,3 +222,115 @@

+
+
+

+ + + mock + This is a caption? +

+

Membership plans

+

Mobile Content

+

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

+

Learn more Learn moreLearn more

+
+
+

+ + + mock + This is a caption? +

+

Membership plans

+

Tablet Content

+

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

+

Learn more Learn moreLearn more

+
+
+

+ + + mock + This is a caption? +

+

Membership plans

+

Desktop Content

+

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

+

Learn more Learn moreLearn more

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

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

+

Membership plans

+

Get started with photo editing apps.

+

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

+
+
+
+ +
+
+
+

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

+

Membership plans

+

Get started with photo editing apps.

+

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

+
+
+
+
+
+
+
#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

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