Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions test/blocks/unity-verb-marquee/mocks/body-word-to-pdf.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<main>
<div>
<div class="unity-verb-marquee word-to-pdf">
<div>
<div>
<h1>Word to PDF</h1>
</div>
</div>
</div>
<div class="unity workflow-acrobat"></div>
</div>
</main>
17 changes: 17 additions & 0 deletions test/blocks/unity-verb-marquee/mocks/placeholders.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"total": 10,
"offset": 0,
"limit": 10,
"data": [
{ "key": "verb-widget-cta", "value": "Select a file" },
{ "key": "verb-widget-word-to-pdf-dragndrop-text", "value": "or drag and drop a file" },
{ "key": "verb-widget-word-to-pdf-file-limit", "value": "PDF or Word, up to 100 MB" },
{ "key": "verb-marquee-legal", "value": "By using this service, you agree to the Adobe Terms of Use and Privacy Policy." },
{ "key": "verb-marquee-legal-2", "value": "" },
{ "key": "verb-widget-terms-of-use", "value": "Terms of Use" },
{ "key": "verb-widget-privacy-policy", "value": "Privacy Policy" },
{ "key": "verb-widget-tool-tip", "value": "Your files are secure with us." },
{ "key": "verb-widget-privacy-policy-url", "value": "https://www.adobe.com/privacy/policy.html" },
{ "key": "verb-widget-terms-of-use-url", "value": "https://www.adobe.com/legal/terms.html" }
]
}
177 changes: 177 additions & 0 deletions test/blocks/unity-verb-marquee/unity-verb-marquee.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/* eslint-disable compat/compat */
import { readFile } from '@web/test-runner-commands';
import { expect } from '@esm-bundle/chai';
import sinon from 'sinon';
import { getConfig, setConfig } from 'https://main--milo--adobecom.aem.live/libs/utils/utils.js'; // eslint-disable-line import/no-unresolved, import/order
import { delay } from '../../helpers/waitfor.js';

const { default: init } = await import(
'../../../acrobat/blocks/unity-verb-marquee/unity-verb-marquee.js'
);

describe('unity-verb-marquee block', () => {
let xhr;
let placeholders;

beforeEach(async () => {
sinon.stub(window, 'fetch');
window.fetch.callsFake((x) => {
if (x.endsWith('.svg')) {
return window.fetch.wrappedMethod.call(window, x);
}
return Promise.resolve();
});
const placeholdersText = await readFile({ path: './mocks/placeholders.json' });
placeholders = JSON.parse(placeholdersText);

window.mph = {};
placeholders.data.forEach((item) => {
window.mph[item.key] = item.value;
});
xhr = sinon.useFakeXMLHttpRequest();
document.body.innerHTML = await readFile({ path: './mocks/body-word-to-pdf.html' });
window.adobeIMS = { isSignedInUser: () => false };
window.lana = { log: sinon.spy() };
});

afterEach(() => {
xhr.restore();
sinon.restore();
});

it('init unity-verb-marquee', async () => {
const conf = getConfig();
setConfig({ ...conf, locale: { prefix: '' } });
const block = document.body.querySelector('.unity-verb-marquee');
await init(block);
expect(document.querySelector('.unity-verb-marquee .acrobat-icon svg')).to.exist;
expect(document.querySelector('.unity-verb-marquee .unity-verb-marquee-cta')).to.exist;
expect(document.querySelector('.unity-verb-marquee .unity-verb-marquee-dropzone')).to.exist;
});

it('show error toast', async () => {
const conf = getConfig();
setConfig({ ...conf, locale: { prefix: '' } });
const block = document.body.querySelector('.unity-verb-marquee');
await init(block);
await delay(100);

window.analytics = { verbAnalytics: sinon.spy(), sendAnalyticsToSplunk: sinon.spy() };

block.dispatchEvent(new CustomEvent('unity:show-error-toast', {
detail: {
code: 'error_only_accept_one_file',
info: 'Test error info',
metaData: 'metadata',
errorData: 'errorData',
sendToSplunk: true,
message: 'Test error message',
},
}));

expect(window.analytics.verbAnalytics.called).to.be.true;
expect(window.analytics.sendAnalyticsToSplunk.called).to.be.true;
expect(window.lana.log.called).to.be.true;
});

it('error toast does not auto-close after 5 seconds', async () => {
const conf = getConfig();
setConfig({ ...conf, locale: { prefix: '' } });
const block = document.body.querySelector('.unity-verb-marquee');
await init(block);
await delay(100);

const clock = sinon.useFakeTimers();
block.dispatchEvent(new CustomEvent('unity:show-error-toast', { detail: { code: 'error_generic', message: 'Test error', sendToSplunk: false } }));

const errorState = block.querySelector('.error');
expect(errorState.classList.contains('hide')).to.be.false;

clock.tick(6000);
expect(errorState.classList.contains('hide')).to.be.false;
});

it('error toast closes when clicking outside the toast', async () => {
const conf = getConfig();
setConfig({ ...conf, locale: { prefix: '' } });
const block = document.body.querySelector('.unity-verb-marquee');
await init(block);
await delay(100);

block.dispatchEvent(new CustomEvent('unity:show-error-toast', { detail: { code: 'error_generic', message: 'Test error', sendToSplunk: false } }));

const errorState = block.querySelector('.error');
expect(errorState.classList.contains('hide')).to.be.false;

await delay(50);
document.body.dispatchEvent(new MouseEvent('click', { bubbles: true }));
expect(errorState.classList.contains('hide')).to.be.true;
});

it('error toast does not close on click inside toast', async () => {
const conf = getConfig();
setConfig({ ...conf, locale: { prefix: '' } });
const block = document.body.querySelector('.unity-verb-marquee');
await init(block);
await delay(100);

block.dispatchEvent(new CustomEvent('unity:show-error-toast', { detail: { code: 'error_generic', message: 'Test error', sendToSplunk: false } }));

const errorState = block.querySelector('.error');
expect(errorState.classList.contains('hide')).to.be.false;

errorState.dispatchEvent(new MouseEvent('click', { bubbles: true }));
expect(errorState.classList.contains('hide')).to.be.false;
});

it('error close button closes toast on Enter key', async () => {
const conf = getConfig();
setConfig({ ...conf, locale: { prefix: '' } });
const block = document.body.querySelector('.unity-verb-marquee');
await init(block);
await delay(100);

block.dispatchEvent(new CustomEvent('unity:show-error-toast', { detail: { code: 'error_generic', message: 'Test error', sendToSplunk: false } }));

const errorState = block.querySelector('.error');
const errorCloseBtn = block.querySelector('.unity-verb-marquee-errorBtn');
expect(errorState.classList.contains('hide')).to.be.false;

errorCloseBtn.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
expect(errorState.classList.contains('hide')).to.be.true;
});

it('error close button closes toast on Space key', async () => {
const conf = getConfig();
setConfig({ ...conf, locale: { prefix: '' } });
const block = document.body.querySelector('.unity-verb-marquee');
await init(block);
await delay(100);

block.dispatchEvent(new CustomEvent('unity:show-error-toast', { detail: { code: 'error_generic', message: 'Test error', sendToSplunk: false } }));

const errorState = block.querySelector('.error');
const errorCloseBtn = block.querySelector('.unity-verb-marquee-errorBtn');
expect(errorState.classList.contains('hide')).to.be.false;

errorCloseBtn.dispatchEvent(new KeyboardEvent('keydown', { key: ' ', bubbles: true }));
expect(errorState.classList.contains('hide')).to.be.true;
});

it('error close button hides error toast', async () => {
const conf = getConfig();
setConfig({ ...conf, locale: { prefix: '' } });
const block = document.body.querySelector('.unity-verb-marquee');
await init(block);
await delay(100);

block.dispatchEvent(new CustomEvent('unity:show-error-toast', { detail: { code: 'error_generic', message: 'Test error', sendToSplunk: false } }));

const errorState = block.querySelector('.error');
const errorCloseBtn = block.querySelector('.unity-verb-marquee-errorBtn');
expect(errorState.classList.contains('hide')).to.be.false;

errorCloseBtn.click();
expect(errorState.classList.contains('hide')).to.be.true;
});
});
130 changes: 130 additions & 0 deletions test/helpers/waitfor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
export const waitForElement = (
selector,
{
options = {
childList: true,
subtree: true,
},
rootEl = document.body,
textContent = '',
} = {},
) => new Promise((resolve) => {
const el = document.querySelector(selector);

if (el) {
resolve(el);
return;
}

const observer = new MutationObserver((mutations, obsv) => {
mutations.forEach((mutation) => {
const nodes = [...mutation.addedNodes];
nodes.some((node) => {
if (node.matches && node.matches(selector)) {
if (textContent && node.textContent !== textContent) return false;

obsv.disconnect();
resolve(node);
return true;
}

// check for child in added node
const treeWalker = document.createTreeWalker(node);
let { currentNode } = treeWalker;
while (currentNode) {
if (currentNode.matches && currentNode.matches(selector)) {
obsv.disconnect();
resolve(currentNode);
return true;
}
currentNode = treeWalker.nextNode();
}
return false;
});
});
});

observer.observe(rootEl, options);
});

export const waitForUpdate = (
el,
options = {
childList: true,
subtree: true,
},
) => new Promise((resolve) => {
const observer = new MutationObserver((mutations, obsv) => {
obsv.disconnect();
resolve();
});
observer.observe(el, options);
});

export const waitForRemoval = (
selector,
options = {
childList: true,
subtree: false,
},
) => new Promise((resolve) => {
const el = document.querySelector(selector);

if (!el) {
resolve();
return;
}

const observer = new MutationObserver((mutations, obsv) => {
mutations.forEach((mutation) => {
const nodes = [...mutation.removedNodes];
nodes.some((node) => {
if (node.matches(selector)) {
obsv.disconnect();
resolve();
return true;
}
return false;
});
});
});

observer.observe(el.parentElement, options);
});

/**
* Promise based setTimeout that can be await'd
* @param {int} timeOut time out in milliseconds
* @param {*} cb Callback function to call when time elapses
* @returns
*/
export const delay = (timeOut, cb) => new Promise((resolve) => {
setTimeout(() => {
resolve((cb && cb()) || null);
}, timeOut);
});

/**
* Waits for predicate function to be true or times out.
* @param {function} predicate Callback that returns boolean
* @param {number} timeout Timeout in milliseconds
* @param {number} interval Interval delay in milliseconds
* @returns {Promise}
*/
export function waitFor(predicate, timeout = 1000, interval = 100) {
return new Promise((resolve, reject) => {
if (predicate()) resolve();

const intervalId = setInterval(() => {
if (predicate()) {
clearInterval(intervalId);
resolve();
}
}, interval);

setTimeout(() => {
clearInterval(intervalId);
reject(new Error('Timed out waiting for predicate to be true'));
}, timeout);
});
}
2 changes: 1 addition & 1 deletion test/utils/experiment-provider.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-underscore-dangle */
import { expect } from '@esm-bundle/chai';
import { getExperimentData, getDecisionScopesForVerb } from '../../unitylibs/utils/experiment-provider.js';
import getExperimentData, { getDecisionScopesForVerb } from '../../unitylibs/utils/experiment-provider.js';

describe('getExperimentData', () => {
// Helper function to setup mock with result and error
Expand Down
Loading
Loading