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