diff --git a/acrobat/blocks/mobile-widget/mobile-widget.js b/acrobat/blocks/mobile-widget/mobile-widget.js index 72bfdf75..5675d373 100644 --- a/acrobat/blocks/mobile-widget/mobile-widget.js +++ b/acrobat/blocks/mobile-widget/mobile-widget.js @@ -1,6 +1,8 @@ import mobileAnalytics from '../../scripts/alloy/mobile-widget.js'; import mobileAnalyticsShown from '../../scripts/alloy/mobile-widget-shown.js'; -import { getEnv, isOldBrowser } from '../../scripts/utils.js'; +import { setLibs, getEnv, isOldBrowser } from '../../scripts/utils.js'; + +const miloLibs = setLibs('/libs'); const verbRedirMap = { createpdf: 'createpdf', @@ -32,6 +34,7 @@ const verbRedirMapAnalytics = { const EOLBrowserPage = 'https://acrobat.adobe.com/home/index-browser-eol.html'; const fallBack = 'https://www.adobe.com/go/acrobat-overview'; +// Redirect Logic function redDir(verb) { if (isOldBrowser()) { window.location.href = EOLBrowserPage; @@ -49,6 +52,7 @@ function redDir(verb) { window.location.href = newLocation; } +// Helper to Create HTML Tags function createTag(tag, attributes, html) { const el = document.createElement(tag); if (html) { @@ -62,8 +66,17 @@ function createTag(tag, attributes, html) { return el; } +let aaVerbName = ''; +let appLink = ''; +let modalCtaLink = ''; +let externalLinkHref = null; +let isAcrobatExitOpen = false; +let inactivityTimer; +const inactivityInterval = 20000; + +// Mobile Widget Creation function createMobileWidget(element, content, verb) { - const aaVerbName = `${verbRedirMapAnalytics[verb] || verb}`; + aaVerbName = `${verbRedirMapAnalytics[verb] || verb}`; const artID = content[1].querySelector('a')?.href || content[1].querySelector('img')?.src; const wrapper = createTag('div', { class: 'mobile-widget_wrapper' }); @@ -78,7 +91,6 @@ function createMobileWidget(element, content, verb) { const artworkWrapper = createTag('div', { class: 'mobile-widget_artwork-wrapper' }); const copy = createTag('div', { class: 'mobile-widget_copy' }, content[2].textContent); - let appLink = ''; if (/iPad|iPhone|iPod/.test(window?.browser?.ua) && !window.MSStream) { appLink = content[4].textContent.toString().trim(); } else if (/android/i.test(window?.browser?.ua)) { @@ -87,7 +99,7 @@ function createMobileWidget(element, content, verb) { appLink = content[4].textContent.toString().trim(); } - const mobileCta = createTag('a', { class: 'mobile-widget_cta', href: appLink }, content[3].textContent); + const mobileCta = createTag('a', { class: 'mobile-widget_cta', href: appLink, target: '_blank' }, content[3].textContent); const ctaWrapper = createTag('div', { class: 'mobile-widget_cta-wrapper' }); titleWrapper.append(titleImg, title); @@ -106,16 +118,146 @@ function createMobileWidget(element, content, verb) { }); } +window.addEventListener('milo:modal:closed', () => { + if (!isAcrobatExitOpen) return; + mobileAnalytics(aaVerbName, 'acrobat-exit-modal:closed'); + isAcrobatExitOpen = false; + sessionStorage.setItem('modalDismissed', 'true'); + if (externalLinkHref) { + window.location.href = externalLinkHref; + externalLinkHref = ''; + } +}); + +const getPlaceHolders = async () => { + const { getConfig } = await import(`${miloLibs}/utils/utils.js`); + const config = await getConfig(); + + if (!Object.keys(window.mph || {}).length) { + const placeholdersPath = `${config.locale.contentRoot}/placeholders.json`; + try { + const response = await fetch(placeholdersPath); + if (response.ok) { + const placeholderData = await response.json(); + placeholderData.data.forEach(({ key, value }) => { + window.mph[key] = value.replace(/\u00A0/g, ' '); + }); + } + } catch (error) { + window.lana?.log(`Failed to load placeholders: ${error?.message}`); + } + } +}; + +async function showModal(event = 'shown') { + const mph = window.mph || {}; + await getPlaceHolders(); + const { getModal } = await import(`${miloLibs}/blocks/modal/modal.js`); + const acrobatIcon = createTag('img', { class: 'modal-icon', src: '/acrobat/img/icons/widget-icon.png' }); + const acrobatLabel = createTag('p', { class: 'modal-label' }, 'Adobe Acrobat'); + const acrobatLogo = createTag('div', { class: 'modal-logo' }); + acrobatLogo.append(acrobatIcon, acrobatLabel); + const modalTitle = createTag('h2', { class: 'modal-title' }, mph['acrobat-exit-modal-title'] || 'Before you go, get the free mobile app.'); + const modalMessage = createTag('p', { class: 'modal-message' }, mph['acrobat-exit-modal-message'] || 'Quickly comment, sign, and share PDFs — all from your phone.'); + const modalCta = createTag('a', { class: 'modal-cta', href: mph['acrobat-exit-modal-cta-url'] || '/acrobat/mobile-app.html' }, mph['acrobat-exit-modal-cta-label'] || 'Download app'); + modalCtaLink = modalCta.href; + const modalContent = createTag('div', { class: 'modal-content' }); + modalContent.append(acrobatLogo, modalTitle, modalMessage, modalCta); + isAcrobatExitOpen = true; + await getModal(null, { + id: 'acrobat-exit', + content: modalContent, + closeEvent: 'Closed', + class: 'acrobat-exit', + }); + mobileAnalyticsShown(aaVerbName, `acrobat-exit-modal:shown-${event}`); +} + +const internalDomains = [ + 'adobe.com', + 'www.adobe.com', + 'www.stage.adobe.com', +]; + +const internalPatterns = [ + '**--dc--adobecom.aem.page', + '**--dc--adobecom.aem.live', + '**--dc--adobecom.hlx.page', + '**--dc--adobecom.hlx.live', +]; + +function isInternalDomain(hostname) { + if (internalDomains.includes(hostname)) return true; + return internalPatterns.some((pattern) => hostname.includes(pattern)); +} + +function isExternalLink(link) { + // eslint-disable-next-line compat/compat + const linkHost = new URL(link.href).hostname; + return !( + linkHost === window.location.hostname + || isInternalDomain(linkHost) + ); +} + +function shouldOpenModal(link) { + return ( + link && link.href !== appLink && link.href !== modalCtaLink && isExternalLink(link) + ); +} + +document.addEventListener('click', (event) => { + const link = event.target.closest('a'); + if (shouldOpenModal(link)) { + event.preventDefault(); + externalLinkHref = link.href; + if (!isAcrobatExitOpen) { + showModal('external-link'); + } else { + window.location.href = link.href; + } + } +}); + +window.addEventListener('beforeunload', async (event) => { + if (sessionStorage.getItem('modalDismissed') === 'true') { + return; + } + event.preventDefault(); + event.returnValue = ''; + if (!isAcrobatExitOpen) { + await showModal('before-unload'); + } +}); + export default async function init(element) { element.closest('main > div').dataset.section = 'widget'; const content = Array.from(element.querySelectorAll(':scope > div')); const VERB = element.dataset.verb; content.forEach((con) => con.classList.add('hide')); - createMobileWidget(element, content, VERB); - // Listen for the IMS:Ready event and call redDir if user is signed in + + if (!element.querySelector('.mobile-widget_wrapper')) { + createMobileWidget(element, content, VERB); + } + window.addEventListener('IMS:Ready', async () => { if (window.adobeIMS.isSignedInUser()) { redDir(VERB); } }); } + +function resetInactivityTimer() { + clearTimeout(inactivityTimer); + inactivityTimer = setTimeout(async () => { + if (!sessionStorage.getItem('modalDismissed') && !isAcrobatExitOpen) { + await showModal('idle'); + } + }, inactivityInterval); +} + +['mousemove', 'click', 'keypress', 'scroll', 'touchstart'].forEach((event) => { + document.addEventListener(event, resetInactivityTimer); +}); + +resetInactivityTimer(); diff --git a/acrobat/blocks/unity/unity.js b/acrobat/blocks/unity/unity.js index 34458fb3..9f53387b 100644 --- a/acrobat/blocks/unity/unity.js +++ b/acrobat/blocks/unity/unity.js @@ -1,6 +1,6 @@ import LIMITS from '../verb-widget/limits.js'; -const localeMap = { +export const localeMap = { '': 'en-us', br: 'pt-br', ca: 'en-ca', @@ -57,8 +57,8 @@ const localeMap = { in: 'en-in', in_hi: 'hi-in', nz: 'en-nz', - hk_zh: 'zh-hant-hk', - tw: 'zh-hant-tw', + hk_zh: 'zh-tw', + tw: 'zh-tw', jp: 'ja-jp', kr: 'ko-kr', ae_en: 'en-ae', diff --git a/acrobat/blocks/verb-widget/icons.js b/acrobat/blocks/verb-widget/icons.js index bf90dfc1..460dbbb7 100644 --- a/acrobat/blocks/verb-widget/icons.js +++ b/acrobat/blocks/verb-widget/icons.js @@ -4,6 +4,7 @@ const ICONS = { WIDGET_ICON: '', fillsign: '', + 'compress-pdf': '', UPLOAD_ICON: '', SECURITY_ICON: '', INFO_ICON: '', diff --git a/acrobat/blocks/verb-widget/limits.js b/acrobat/blocks/verb-widget/limits.js index 4457b30b..f9436f94 100644 --- a/acrobat/blocks/verb-widget/limits.js +++ b/acrobat/blocks/verb-widget/limits.js @@ -1,25 +1,37 @@ const LIMITS = { fillsign: { - maxFileSize: 100000000, // 100 MB + maxFileSize: 104857600, // 100 MB maxFileSizeFriendly: '100 MB', // 100 MB - acceptedFiles: '.pdf', + acceptedFiles: ['application/pdf'], maxNumFiles: 1, + multipleFiles: false, mobileApp: true, }, 'delete-pages': { maxFileSize: 100000000, // 1 MB - acceptedFiles: '.pdf', + acceptedFiles: ['application/pdf'], maxNumFiles: 1, }, 'number-pages': { maxFileSize: 100000000, // 1 MB - acceptedFiles: '.pdf', + acceptedFiles: ['application/pdf'], maxNumFiles: 1, }, 'compress-pdf': { - maxFileSize: 100000000, - acceptedFiles: '.pdf', - maxNumFiles: 1, + maxFileSize: 2147483648, + maxFileSizeFriendly: '2 GB', + acceptedFiles: [ + 'application/pdf', + 'application/msword', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/vnd.ms-powerpoint', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'application/vnd.ms-excel', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'image/jpeg', + 'image/png', + ], + multipleFiles: true, }, }; diff --git a/acrobat/blocks/verb-widget/verb-widget.css b/acrobat/blocks/verb-widget/verb-widget.css index b5c705c5..d9c0c61d 100644 --- a/acrobat/blocks/verb-widget/verb-widget.css +++ b/acrobat/blocks/verb-widget/verb-widget.css @@ -26,7 +26,6 @@ font-size: 14px; line-height: 20px; max-width: 527px; - text-align: left; display: inline-block; color: #2c2c2c; } @@ -60,16 +59,17 @@ display: flex; width: 42px; height: 42px; - margin: 15px 3px 0 0; + margin: 15px 0 0; + margin-inline-end: 3px; } .info-icon { display: flex; - margin: 15px 0 0 20px; + margin: 15px 0 0; + margin-inline-start: 20px; } .verb-footer { - text-align: center; align-items: center; display: flex; flex-flow: row; @@ -143,7 +143,6 @@ } .verb-copy { - text-align: left; font-size: 22px; font-weight: 400; line-height: 33px; @@ -236,7 +235,6 @@ .verb-heading { color: #2c2c2c; - text-align: left; font-weight: 600; font-size: 44px; margin: revert; @@ -254,7 +252,7 @@ font-size: 30px; font-weight: bold; color: black; - margin-left: 15px; + margin-inline-start: 15px; } .verb-mobile-cta { @@ -285,7 +283,7 @@ display: flex; flex: 1 1 55%; flex-direction: row; - padding: 48px 0 48px 48px; + padding: 48px; /* cursor: pointer; */ } diff --git a/acrobat/blocks/verb-widget/verb-widget.js b/acrobat/blocks/verb-widget/verb-widget.js index 6d559290..8f5eaee1 100644 --- a/acrobat/blocks/verb-widget/verb-widget.js +++ b/acrobat/blocks/verb-widget/verb-widget.js @@ -2,6 +2,7 @@ import LIMITS from './limits.js'; import { setLibs, getEnv, isOldBrowser } from '../../scripts/utils.js'; import verbAnalytics from '../../scripts/alloy/verb-widget.js'; import createSvgElement from './icons.js'; +import { localeMap } from '../unity/unity.js'; const miloLibs = setLibs('/libs'); const { createTag, getConfig } = await import(`${miloLibs}/utils/utils.js`); @@ -42,15 +43,17 @@ const setDraggingClass = (widget, shouldToggle) => { }; function prefetchNextPage(verb) { - const ENV = getEnv(); - const isProd = ENV === 'prod'; - const nextPageHost = isProd ? 'acrobat.adobe.com' : 'stage.acrobat.adobe.com'; - const nextPageUrl = `https://${nextPageHost}/us/en/${verb}`; + const { locale } = getConfig(); + const localePath = localeMap[locale.prefix.replace('/', '')].split('-').reverse().join('/'); + const nextPageHost = getEnv() === 'prod' ? 'acrobat.adobe.com' : 'stage.acrobat.adobe.com'; + const nextPageUrl = `https://${nextPageHost}/${localePath}/${verb}`; + const link = document.createElement('link'); link.rel = 'prefetch'; link.href = nextPageUrl; link.crossOrigin = 'anonymous'; link.as = 'document'; + document.head.appendChild(link); } @@ -130,7 +133,14 @@ export default async function init(element) { } const widgetMobileButton = createTag('a', { class: 'verb-mobile-cta', href: mobileLink }, window.mph['verb-widget-cta-mobile']); - const button = createTag('input', { type: 'file', accept: LIMITS[VERB].acceptedFiles, id: 'file-upload', class: 'hide', 'aria-hidden': true }); + const button = createTag('input', { + type: 'file', + accept: LIMITS[VERB]?.acceptedFiles, + id: 'file-upload', + class: 'hide', + 'aria-hidden': true, + ...(LIMITS[VERB]?.multipleFiles && { multiple: '' }), + }); const widgetImage = createTag('div', { class: 'verb-image' }); const verbIconName = `${VERB}`; const verbImageSvg = createSvgElement(verbIconName); @@ -267,7 +277,7 @@ export default async function init(element) { } if (e.detail?.event === 'uploaded') { - verbAnalytics('job:test-uploaded', VERB, e.detail?.data); + verbAnalytics('job:test-uploaded', VERB, e.detail?.data, false); exitFlag = true; setUser(); document.cookie = `UTS_Uploaded=${Date.now()};domain=.adobe.com;path=/;expires=${cookieExp}`; diff --git a/acrobat/scripts/alloy/mobile-widget-shown.js b/acrobat/scripts/alloy/mobile-widget-shown.js index d2b37eef..a6f9644f 100644 --- a/acrobat/scripts/alloy/mobile-widget-shown.js +++ b/acrobat/scripts/alloy/mobile-widget-shown.js @@ -23,7 +23,8 @@ if (params.dropzone2) { appTags.push('dropzone2'); } -export default function init(verb) { +export default function init(verb, type = 'landing:shown') { + const eventName = `acrobat:verb-${verb}:${type}`; const event = { documentUnloading: true, data: { @@ -32,14 +33,14 @@ export default function init(verb) { webInteraction: { linkClicks: { value: 1 }, type: 'other', - name: `acrobat:verb-${verb}:landing:shown`, + name: eventName, }, }, _adobe_corpnew: { digitalData: { - dcweb: { event: { pagename: `acrobat:verb-${verb}:landing:shown` } }, + dcweb: { event: { pagename: eventName } }, dcweb2: { - event: { pagename: `acrobat:verb-${verb}:landing:shown` }, + event: { pagename: eventName }, source: { user_agent: navigator.userAgent, lang: document.documentElement.lang, diff --git a/acrobat/scripts/alloy/mobile-widget.js b/acrobat/scripts/alloy/mobile-widget.js index 2894874a..f398fe48 100644 --- a/acrobat/scripts/alloy/mobile-widget.js +++ b/acrobat/scripts/alloy/mobile-widget.js @@ -23,7 +23,8 @@ if (params.dropzone2) { appTags.push('dropzone2'); } -export default function init(verb) { +export default function init(verb, type = 'goto-app:clicked') { + const eventName = `acrobat:verb-${verb}:${type}`; const event = { documentUnloading: true, data: { @@ -32,14 +33,14 @@ export default function init(verb) { webInteraction: { linkClicks: { value: 1 }, type: 'other', - name: `acrobat:verb-${verb}:goto-app:clicked`, + name: eventName, }, }, _adobe_corpnew: { digitalData: { - dcweb: { event: { pagename: `acrobat:verb-${verb}:goto-app:clicked` } }, + dcweb: { event: { pagename: eventName } }, dcweb2: { - event: { pagename: `acrobat:verb-${verb}:goto-app:clicked` }, + event: { pagename: eventName }, source: { user_agent: navigator.userAgent, lang: document.documentElement.lang, diff --git a/acrobat/scripts/alloy/verb-widget.js b/acrobat/scripts/alloy/verb-widget.js index a17720c7..435d9a7d 100644 --- a/acrobat/scripts/alloy/verb-widget.js +++ b/acrobat/scripts/alloy/verb-widget.js @@ -23,7 +23,7 @@ if (params.dropzone2) { appTags.push('dropzone2'); } -export default function init(eventName, verb, metaData) { +export default function init(eventName, verb, metaData, documentUnloading = true) { function getSessionID() { const aToken = window.adobeIMS.getAccessToken(); const arrayToken = aToken?.token.split('.'); @@ -33,7 +33,25 @@ export default function init(eventName, verb, metaData) { return tokenPayload.sub || tokenPayload.user_id; } const event = { - documentUnloading: true, + documentUnloading, + // eslint-disable-next-line + done: function (AJOPropositionResult, error) { + if (!documentUnloading) { + const accountType = window?.adobeIMS?.getAccountType(); + const verbEvent = `acrobat:verb-${verb}:${eventName}`; + if (error) { + window.lana?.log( + `Error Code: ${error}, Status: 'Unknown', Message: An error occurred while sending ${verbEvent}, Account Type: ${accountType}`, + { sampleRate: 100, tags: 'DC_Milo,Project Unity (DC)' }, + ); + } else { + window.lana?.log( + `Message: Event ${verbEvent} has been sent successfully, Account Type: ${accountType}`, + { sampleRate: 100, tags: 'DC_Milo,Project Unity (DC)' }, + ); + } + } + }, data: { eventType: 'web.webinteraction.linkClicks', web: { @@ -104,15 +122,6 @@ export default function init(eventName, verb, metaData) { }, }, }; - - // Alloy Ready... - const AlloyReady = setInterval(() => { - // eslint-disable-next-line no-underscore-dangle - if (window?._satellite?.track) { - clearInterval(AlloyReady); - // eslint-disable-next-line no-underscore-dangle - window._satellite?.track('event', event); - } - }, 1000); // eslint-disable-next-line no-underscore-dangle + window._satellite?.track?.('event', event); } diff --git a/acrobat/styles/styles.css b/acrobat/styles/styles.css index ded8c54b..ce595e78 100644 --- a/acrobat/styles/styles.css +++ b/acrobat/styles/styles.css @@ -299,6 +299,56 @@ div.how-to ol li::before{ margin-top: 10px; } +.dialog-modal.acrobat-exit .modal-content { + padding: 40px 24px; +} + +.dialog-modal.acrobat-exit .modal-content .modal-logo { + display: flex; + flex-direction: row; + align-items: center; + height: 40px; +} + +.modal-logo .modal-icon { + width: 40px; + height: auto; +} + +.modal-logo .modal-label { + font-weight: 700; + line-height: 20px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + margin-left: 16px; +} + +.modal-content .modal-title { + margin-block-start: 24px; + margin-block-end: 16px; +} + +.modal-content .modal-message { + margin-block-start: 16px; +} + +.modal-content .modal-cta { + font-size: 15px; + font-weight: 700; + line-height: 19px; + border: solid 2px #0265dc; + background-color: #0265dc; + border-radius: 24px; + text-align: center; + color: white; + text-decoration: none; + padding: 5px 18px; + display: inline-flex; + align-items: center; + justify-content: center; +} + .dialog-modal.extension-modal.backdrop-off { width: 30rem; max-height: 188px; diff --git a/package-lock.json b/package-lock.json index 929bcedd..f7b27588 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ }, "devDependencies": { "@amwp/platform-ui-automation": "^0.0.6", - "@amwp/platform-ui-lib-adobe": "^0.0.7", + "@amwp/platform-ui-lib-adobe": "^0.0.9", "@babel/core": "7.23.2", "@babel/eslint-parser": "7.22.15", "@babel/register": "7.22.15", @@ -119,9 +119,9 @@ } }, "node_modules/@amwp/platform-ui-lib-adobe": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/@amwp/platform-ui-lib-adobe/-/platform-ui-lib-adobe-0.0.7.tgz", - "integrity": "sha512-5tF6QFLZIvGDS/thYanj2srt6NrSQ0lq34m9KzaPEIVfj2OywLLVoavDaxgJvmhTGywIGngMrXyDx+YIUIInyA==", + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@amwp/platform-ui-lib-adobe/-/platform-ui-lib-adobe-0.0.9.tgz", + "integrity": "sha512-F/1En1XlUJMYxbpesvd0G64XDyO8xsLUGIyyoBs4YehcDVk7CbHRHVShs6U9LoQ7A4ElEb5KXK7W5YqtRWp+nw==", "dev": true }, "node_modules/@babel/code-frame": { diff --git a/package.json b/package.json index 3b3bd3c9..f4ce8a6e 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "homepage": "https://github.com/adobecom/dc#readme", "devDependencies": { "@amwp/platform-ui-automation": "^0.0.6", - "@amwp/platform-ui-lib-adobe": "^0.0.7", + "@amwp/platform-ui-lib-adobe": "^0.0.9", "@babel/core": "7.23.2", "@babel/eslint-parser": "7.22.15", "@babel/register": "7.22.15", diff --git a/test/blocks/mobile-widget/mobile-widget.test.js b/test/blocks/mobile-widget/mobile-widget.test.js index 7ba3b5c5..677ea20b 100644 --- a/test/blocks/mobile-widget/mobile-widget.test.js +++ b/test/blocks/mobile-widget/mobile-widget.test.js @@ -3,6 +3,8 @@ import { expect } from '@esm-bundle/chai'; const { default: init } = await import('../../../acrobat/blocks/mobile-widget/mobile-widget.js'); +sessionStorage.setItem('modalDismissed', 'true'); + describe('Mobile widget', () => { it('is complete', async () => { document.body.innerHTML = await readFile({ path: './mocks/body.html' }); diff --git a/test/e2e/features/unity/verbs.feature b/test/e2e/features/unity/verbs.feature new file mode 100644 index 00000000..2e5db0ce --- /dev/null +++ b/test/e2e/features/unity/verbs.feature @@ -0,0 +1,26 @@ +Feature: Frictionless Converter Block + + Background: + Given I have a new browser context + + @smoke @unity @sign-pdf @choosefile + Scenario Outline: L1 Verb - Upload and sign-in + Given I go to the page + Then I choose the file "" to upload + Then I wait for 5 seconds + Then I should see the address bar contains "acrobat.adobe.com" + + Examples: + | Verb | File | + | sign-pdf | test-files/test.pdf | + + @smoke @unity @sign-pdf @dragndrop + Scenario Outline: L1 Verb - Upload and sign-in + Given I go to the page + Then I drag-and-drop the file "" to upload + Then I wait for 5 seconds + Then I should see the address bar contains "acrobat.adobe.com" + + Examples: + | Verb | File | + | sign-pdf | test-files/test.pdf | \ No newline at end of file diff --git a/test/e2e/page-objects/unity.page.js b/test/e2e/page-objects/unity.page.js new file mode 100644 index 00000000..31ebbe71 --- /dev/null +++ b/test/e2e/page-objects/unity.page.js @@ -0,0 +1,34 @@ +import { classes } from "polytype"; +import { DcGnavPage } from "./dcgnav.page"; +import { CaaSSection } from "./caas.section"; +import { VerbWidgetSection } from "./verbwidget.section"; + +export class UnityPage extends classes(DcGnavPage, VerbWidgetSection, CaaSSection) { + constructor(contentPath) { + super({ + super: DcGnavPage, + arguments: [contentPath], + }); + this.buildProps({ + howToDefault: 'div[data-path*="how-to/default"]', + howTo2ndConversion: '[data-tag="2nd conversion"] div[data-path*="how-to/2nd-conversion"]', + verbSubfooter: '.verb-subfooter', + reviewComponent: '.review', + reviewStats: '.hlx-ReviewStats', + reviewSubmitResponse: '.hlx-submitResponse', + reviewDisabled: '.hlx-Review-ratingFields[disabled]', + reviewCommentField: '.hlx-Review-commentFields textarea', + reviewCommentSubmit: '.hlx-Review-commentFields input[type="submit"]', + reviewInputField: 'fieldset.hlx-Review-ratingFields input', + signUp: '[href*="https://auth.services.adobe.com"][href*="signup"]', + extensionModal: '#chromeext, #edgeext', + closeExtensionModal: '#chromeext .dialog-close, #edgeext .dialog-close', + eventwrapperOnload: '.eventwrapper.onload', + previewDescription: 'div[class*="previewDescription"]', + }); + } + + reviewStartInput(rating) { + return this.native.locator(`.hlx-Review-ratingFields input[value="${rating}"]`); + } +} diff --git a/test/e2e/page-objects/verbwidget.section.js b/test/e2e/page-objects/verbwidget.section.js new file mode 100644 index 00000000..8cfeee3f --- /dev/null +++ b/test/e2e/page-objects/verbwidget.section.js @@ -0,0 +1,69 @@ +import fs from 'fs'; +import path from 'path'; +import { Section } from '@amwp/platform-ui-automation/lib/common/page-objects/section'; + +export class VerbWidgetSection extends Section { + constructor() { + super(); + this.buildProps({ + selectButton: '.verb-cta', + fileUploadInput: '#file-upload', + dropZone: '#drop-zone', + }) + } + + async chooseFiles(filePaths) { + const fileChooserPromise = this.native.waitForEvent('filechooser'); + this.selectButton.click(); + const fileChooser = await fileChooserPromise; + await fileChooser.setFiles(filePaths); + } + + async dragndropFiles(filePaths) { + const filePath = filePaths[0]; + const buffer = fs.readFileSync(filePath).toString('base64'); + const basename = path.basename(filePath); + + const dataTransfer = await this.dropZone.evaluateHandle(async({bufferData, basename}) => { + const dt = new DataTransfer(); + const blobData = await fetch(bufferData).then((res) => res.blob()); + const file = new File([blobData], basename, { type: 'application/pdf' }); + dt.items.add(file); + return dt; + }, { + bufferData: `data:application/octet-stream;base64,${buffer}`, + basename + }); + + await this.dropZone.dispatchEvent('drop', { dataTransfer }); + } +} + +async function dragAndDropFile( + page, + selector, + filePath, + fileName, + fileType = '' + ) { + const buffer = readFileSync(filePath).toString('base64'); + + const dataTransfer = await page.evaluateHandle( + async ({ bufferData, localFileName, localFileType }) => { + const dt = new DataTransfer(); + + const blobData = await fetch(bufferData).then((res) => res.blob()); + + const file = new File([blobData], localFileName, { type: localFileType }); + dt.items.add(file); + return dt; + }, + { + bufferData: `data:application/octet-stream;base64,${buffer}`, + localFileName: fileName, + localFileType: fileType, + } + ); + + await page.dispatchEvent(selector, 'drop', { dataTransfer }); + }; \ No newline at end of file diff --git a/test/e2e/step-definitions/dc.steps.js b/test/e2e/step-definitions/dc.steps.js index a42ac101..8fe20c8e 100644 --- a/test/e2e/step-definitions/dc.steps.js +++ b/test/e2e/step-definitions/dc.steps.js @@ -23,6 +23,7 @@ import { MergePdfPage } from "../page-objects/mergepdf.page"; import { CompressPdfPage } from "../page-objects/compresspdf.page"; import { PasswordProtectPdfPage } from "../page-objects/passwordprotectpdf.page"; import { FrictionlessPage } from "../page-objects/frictionless.page"; +import { UnityPage } from "../page-objects/unity.page"; import { DCPage } from "../page-objects/dc.page"; import { cardinal } from "../support/cardinal"; import { expect } from "@playwright/test"; @@ -643,3 +644,45 @@ Then(/^I confirm phone number is different and has geo-ip value "([^"]*)"$/, asy expect(this.phoneNumber).not.toEqual(geoIpPhoneNumber); expect(geoIpPhoneNumber).toEqual(geoIpPhoneNumberValue); }) + +Then(/^I choose the (?:PDF|file|files) "([^\"]*)" to upload$/, async function (filePath) { + this.context(UnityPage); + const filePaths = filePath.split(","); + const absPaths = filePaths.map((x) => + path.resolve(global.config.profile.site, x) + ); + let retry = 3; + while (retry > 0) { + await expect(this.page.selectButton).toHaveCount(1, { timeout: 15000 }); + await this.page.native.waitForTimeout(2000); + try { + await this.page.chooseFiles(absPaths); + await this.page.native.waitForTimeout(2000); + await expect(this.page.selectButton).toHaveCount(0, { timeout: 15000 }); + retry = 0; + } catch { + retry--; + } + } +}); + +Then(/^I drag-and-drop the (?:PDF|file|files) "([^\"]*)" to upload$/, async function (filePath) { + this.context(UnityPage); + const filePaths = filePath.split(","); + const absPaths = filePaths.map((x) => + path.resolve(global.config.profile.site, x) + ); + let retry = 3; + while (retry > 0) { + await expect(this.page.selectButton).toHaveCount(1, { timeout: 15000 }); + await this.page.native.waitForTimeout(2000); + try { + await this.page.dragndropFiles(absPaths); + await this.page.native.waitForTimeout(2000); + await expect(this.page.selectButton).toHaveCount(0, { timeout: 15000 }); + retry = 0; + } catch { + retry--; + } + } +}); \ No newline at end of file