From a4aa1f20ead5d769ca9dd976b25153debb3ad7fa Mon Sep 17 00:00:00 2001 From: Robert Bogos Date: Thu, 11 Apr 2024 15:25:43 +0300 Subject: [PATCH 1/3] unit tests for pep --- libs/features/webapp-prompt/webapp-prompt.js | 2 +- .../webapp-prompt/mocks/media-icon.png | Bin 0 -> 786 bytes .../webapp-prompt/mocks/pep-prompt-content.js | 31 ++++ test/features/webapp-prompt/test-utilities.js | 46 ++++++ .../webapp-prompt/webapp-prompt.test.js | 153 ++++++++++++++++++ 5 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 test/features/webapp-prompt/mocks/media-icon.png create mode 100644 test/features/webapp-prompt/mocks/pep-prompt-content.js create mode 100644 test/features/webapp-prompt/test-utilities.js create mode 100644 test/features/webapp-prompt/webapp-prompt.test.js diff --git a/libs/features/webapp-prompt/webapp-prompt.js b/libs/features/webapp-prompt/webapp-prompt.js index 8bfec3fb6f..bfa2de9579 100644 --- a/libs/features/webapp-prompt/webapp-prompt.js +++ b/libs/features/webapp-prompt/webapp-prompt.js @@ -116,7 +116,7 @@ class AppPrompt { const metadata = getMetadata(content.querySelector('.section-metadata')); metadata['loader-duration'] = parseInt(metadata['loader-duration'] || CONFIG.delay, 10); - metadata['loader-color'] = metadata['loader-color'] || CONFIG.defaultColor; + metadata['loader-color'] = metadata['loader-color'] || CONFIG.loaderColor; this.options = metadata; }; diff --git a/test/features/webapp-prompt/mocks/media-icon.png b/test/features/webapp-prompt/mocks/media-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ae39a1539343b04c34cc6bb043cf85b4760ea919 GIT binary patch literal 786 zcmV+t1MU1$Nk&Er0{{S5MM6+kP&il$0000G0001E003bC06|PpNYelS00D4Y+qM#2 zDzFAd>ma27Iv|3Iv=xmAA#$mOalJ2`H4!lZc=+fANxKYRKM$$z9r0o(U8$zdE3A5@ zTU$>BE9KxZqg+Xs#sg}z(r(my)k?g&6R*_c8|F&h1fX>6It8#-`ZiGTE3jDw+_)0B zkpxjLLEcNyPZCUn1jFBdKk6n4re1`Yw8 z++y6Wm{qu|HoO}39*mMzY2e|2Q-Ei!non)vg-*&Pb@1Y&=cmIweErfUfp^5i09H^q zAP@oo0PqX|odGIj0Ac_>kw~6QC8VOFAy^6Euo4Mr01kXsA1PiAX*FiO;LI}6wp6d1 zYj33fAVHHz_<4{#vU2Jup=c~4Z0XPN#0q+&W}QCXFXd_`Kz$NJ{DQLSUo+!b6dNRx z8k#-bas3X0%x+_q=(Yg8V@cwju+%r~WHBjI7q95O-!%`p{$xhXwYLBW2 zgiRV=_JkU@+o**b%r8uLlrvo3sWS433^H!qnE7IL`X-b_t*1%t(j^c=-bk=luybYx z3W?Ct1t%`#XwU*eP!|r2_72ouuCSB@Uhmx#!{Guwo-7UsBbc61faq%Q@>nC^6@mzoho2b*gkOnPT0Fop Qu-<_5Z{|!5TPOek05>*cD*ylh literal 0 HcmV?d00001 diff --git a/test/features/webapp-prompt/mocks/pep-prompt-content.js b/test/features/webapp-prompt/mocks/pep-prompt-content.js new file mode 100644 index 0000000000..9f32193ae4 --- /dev/null +++ b/test/features/webapp-prompt/mocks/pep-prompt-content.js @@ -0,0 +1,31 @@ +export default ({ color, loaderDuration, redirectUrl, productName }) => `
+

+ + + + + + +

+

Taking you to Creative Cloud

+

Cancel to stay on web page

+

Stay on this page

+ +
`; diff --git a/test/features/webapp-prompt/test-utilities.js b/test/features/webapp-prompt/test-utilities.js new file mode 100644 index 0000000000..8158d449f9 --- /dev/null +++ b/test/features/webapp-prompt/test-utilities.js @@ -0,0 +1,46 @@ +import { setViewport } from '@web/test-runner-commands'; +import init from '../../../libs/features/webapp-prompt/webapp-prompt.js'; +import { viewports, mockRes as importedMockRes } from '../../blocks/global-navigation/test-utilities.js'; +import { getConfig, loadStyle, setConfig, updateConfig } from '../../../libs/utils/utils.js'; + +export const allSelectors = { + fedsUtilities: '.feds-utilities', + pepWrapper: '.appPrompt', + closeIcon: '.appPrompt-close', + promptIcon: '.appPrompt-icon', + avatarImage: '.appPrompt-avatar-image', + title: '.appPrompt-title', + footer: '.appPrompt-footer', + subtitle: '.appPrompt-text', + cta: '.appPrompt-cta--close', + progressWrapper: '.appPrompt-progressWrapper', + progress: '.appPrompt-progress', + appSwitcher: '#unav-app-switcher', +}; + +export const defaultConfig = { + color: '#b30b00', + loaderDuration: 7500, + redirectUrl: 'https://www.adobe.com/?pep=true', + productName: 'photoshop', +}; + +export const mockRes = importedMockRes; + +export const initPep = async ({ entName = 'firefly-web-usage', isAnchorOpen = false }) => { + setConfig({ + imsClientId: 'milo', + codeRoot: '/libs', + locales: { '': { ietf: 'en-US', tk: 'hah7vzn.css' } }, + }); + updateConfig({ ...getConfig(), entitlements: () => ['firefly-web-usage'] }); + await setViewport(viewports.desktop); + await loadStyle('../../../libs/features/webapp-prompt/webapp-prompt.css'); + + return init({ + promptPath: 'https://pep-mocks.test/pep-prompt-content.plain.html', + getAnchorState: async () => ({ id: 'unav-app-switcher', isOpen: isAnchorOpen }), + entName, + parent: document.querySelector('div.feds-utilities'), + }); +}; diff --git a/test/features/webapp-prompt/webapp-prompt.test.js b/test/features/webapp-prompt/webapp-prompt.test.js new file mode 100644 index 0000000000..38c67e3677 --- /dev/null +++ b/test/features/webapp-prompt/webapp-prompt.test.js @@ -0,0 +1,153 @@ +import { expect } from '@esm-bundle/chai'; +import sinon, { stub } from 'sinon'; +import pepPromptContent from './mocks/pep-prompt-content.js'; + +describe('PEP', () => { + let clock; + let allSelectors; + let defaultConfig; + let mockRes; + + beforeEach(async () => { + clock = sinon.useFakeTimers({ + toFake: ['setTimeout'], + shouldAdvanceTime: true, + }); + // We need to import the utilities after mocking setTimeout to ensure + // their setTimeout calls use Sinon's mocked implementation. + // Importing before mocking would lead to a 5s PEP timeout, exceeding the 2s test limit. + const { allSelectors: importedAllSelectors, defaultConfig: importedDefaultConfig, mockRes: importedMockRes } = await import('./test-utilities.js'); + allSelectors = importedAllSelectors; + defaultConfig = importedDefaultConfig; + mockRes = importedMockRes; + + document.body.innerHTML = `
+
App Switcher
+
`; + stub(window, 'fetch').callsFake(async (url) => { + if (url.includes('pep-prompt-content.plain.html')) return mockRes({ payload: pepPromptContent({ ...defaultConfig }) }); + return null; + }); + }); + + afterEach(() => { + sinon.restore(); + clock.restore(); + document.body.innerHTML = ''; + document.cookie = `${document.cookie};expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/`; + }); + + describe('PEP rendering tests', () => { + it('should render PEP', async () => { + await (await import('./test-utilities.js')).initPep({}); + await clock.runAllAsync(); + expect(document.querySelector(allSelectors.pepWrapper)).to.exist; + }); + + it('should not render PEP when previously dismissed', async () => { + document.cookie = 'dismissedAppPrompts=["pep-prompt-content.plain.html"]'; + await (await import('./test-utilities.js')).initPep({}); + await clock.runAllAsync(); + expect(document.querySelector(allSelectors.pepWrapper)).to.not.exist; + }); + + it('should not render PEP when the entitlement does not match', async () => { + await (await import('./test-utilities.js')).initPep({ entName: 'not-matching-entitlement' }); + await clock.runAllAsync(); + expect(document.querySelector(allSelectors.pepWrapper)).to.not.exist; + }); + + it('should not render PEP when there is no prompt content', async () => { + sinon.restore(); + stub(window, 'fetch').callsFake(async (url) => { + if (url.includes('pep-prompt-content.plain.html')) { + return mockRes({ + payload: null, + ok: false, + status: 400, + }); + } + return null; + }); + await (await import('./test-utilities.js')).initPep({}); + await clock.runAllAsync(); + expect(document.querySelector(allSelectors.pepWrapper)).to.not.exist; + }); + + it('should not render PEP when the redirect url or product name are not provided', async () => { + sinon.restore(); + stub(window, 'fetch').callsFake(async (url) => { + if (url.includes('pep-prompt-content.plain.html')) return mockRes({ payload: pepPromptContent({ ...defaultConfig, redirectUrl: false, productName: false }) }); + return null; + }); + await (await import('./test-utilities.js')).initPep({}); + await clock.runAllAsync(); + expect(document.querySelector(allSelectors.pepWrapper)).to.not.exist; + }); + + it('should not render PEP when the anchor element is open', async () => { + await (await import('./test-utilities.js')).initPep({ isAnchorOpen: true }); + await clock.runAllAsync(); + expect(document.querySelector(allSelectors.pepWrapper)).to.not.exist; + }); + }); + + describe('PEP configuration tests', () => { + it('should use config values when metadata loader color or duration are not provided', async () => { + sinon.restore(); + stub(window, 'fetch').callsFake(async (url) => { + if (url.includes('pep-prompt-content.plain.html')) return mockRes({ payload: pepPromptContent({ ...defaultConfig, color: false, loaderDuration: false }) }); + return null; + }); + const pep = await (await import('./test-utilities.js')).initPep({}); + await clock.runAllAsync(); + const { 'loader-color': pepColor, 'loader-duration': pepDuration } = pep.options; + expect(!!pepColor && !!pepDuration).to.equal(true); + }); + }); + + describe('PEP interaction tests', () => { + it('should close PEP on Escape key', async () => { + await (await import('./test-utilities.js')).initPep({}); + await clock.runAllAsync(); + document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' })); + expect(document.querySelector(allSelectors.pepWrapper)).to.not.exist; + }); + + it('should close PEP on clicking the close icon', async () => { + await (await import('./test-utilities.js')).initPep({}); + await clock.runAllAsync(); + document.querySelector(allSelectors.closeIcon).click(); + expect(document.querySelector(allSelectors.pepWrapper)).to.not.exist; + }); + + it('should close PEP on clicking the CTA', async () => { + await (await import('./test-utilities.js')).initPep({}); + await clock.runAllAsync(); + document.querySelector(allSelectors.cta).click(); + expect(document.querySelector(allSelectors.pepWrapper)).to.not.exist; + }); + + it('should close PEP on clicking the anchor element', async () => { + await (await import('./test-utilities.js')).initPep({}); + await clock.runAllAsync(); + document.querySelector(allSelectors.appSwitcher).click(); + expect(document.querySelector(allSelectors.pepWrapper)).to.not.exist; + }); + }); + + describe('PEP focus tests', () => { + it('should focus on the close icon on initial render', async () => { + await (await import('./test-utilities.js')).initPep({}); + await clock.runAllAsync(); + expect(document.activeElement).to.equal(document.querySelector(allSelectors.closeIcon)); + }); + + it('should focus on the anchor element after closing', async () => { + await (await import('./test-utilities.js')).initPep({}); + await clock.runAllAsync(); + document.querySelector(allSelectors.closeIcon).click(); + expect(document.activeElement).to.equal(document.querySelector(allSelectors.appSwitcher)); + }); + }); +}); From d2f8f6d118320ca76f494b55b099d4c5af57da06 Mon Sep 17 00:00:00 2001 From: Robert Bogos Date: Thu, 11 Apr 2024 15:40:06 +0300 Subject: [PATCH 2/3] fixes for gnav unit tests --- test/blocks/global-navigation/global-navigation.test.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/blocks/global-navigation/global-navigation.test.js b/test/blocks/global-navigation/global-navigation.test.js index f3a5b42888..bce1b590d6 100644 --- a/test/blocks/global-navigation/global-navigation.test.js +++ b/test/blocks/global-navigation/global-navigation.test.js @@ -34,7 +34,7 @@ describe('global navigation', () => { before(() => { document.head.innerHTML = ` - + `; }); @@ -71,12 +71,15 @@ describe('global navigation', () => { }); it("should log when there's issues within onReady", async () => { + const ogIms = window.adobeIMS; const gnav = await createFullGlobalNavigation({}); sinon.stub(gnav, 'decorateProfile').callsFake(() => { throw new Error('error'); }); + window.adobeIMS = { isSignedInUser: () => true }; await gnav.imsReady(); expect(window.lana.log.getCalls().find((c) => c.args[0].includes('issues within onReady'))).to.exist; + window.adobeIMS = ogIms; }); it('should log when IMS signIn method is not available', async () => { From 7922b39ab1a7920117dc06ddc695f107a4bff429 Mon Sep 17 00:00:00 2001 From: Robert Bogos Date: Mon, 15 Apr 2024 14:37:25 +0300 Subject: [PATCH 3/3] optimizing pep unit tests --- .../webapp-prompt/webapp-prompt.test.js | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/test/features/webapp-prompt/webapp-prompt.test.js b/test/features/webapp-prompt/webapp-prompt.test.js index 38c67e3677..d6d11f95da 100644 --- a/test/features/webapp-prompt/webapp-prompt.test.js +++ b/test/features/webapp-prompt/webapp-prompt.test.js @@ -7,6 +7,7 @@ describe('PEP', () => { let allSelectors; let defaultConfig; let mockRes; + let initPep; beforeEach(async () => { clock = sinon.useFakeTimers({ @@ -20,7 +21,7 @@ describe('PEP', () => { allSelectors = importedAllSelectors; defaultConfig = importedDefaultConfig; mockRes = importedMockRes; - + initPep = (await import('./test-utilities.js')).initPep; document.body.innerHTML = `
App Switcher
`; @@ -39,20 +40,20 @@ describe('PEP', () => { describe('PEP rendering tests', () => { it('should render PEP', async () => { - await (await import('./test-utilities.js')).initPep({}); + await initPep({}); await clock.runAllAsync(); expect(document.querySelector(allSelectors.pepWrapper)).to.exist; }); it('should not render PEP when previously dismissed', async () => { document.cookie = 'dismissedAppPrompts=["pep-prompt-content.plain.html"]'; - await (await import('./test-utilities.js')).initPep({}); + await initPep({}); await clock.runAllAsync(); expect(document.querySelector(allSelectors.pepWrapper)).to.not.exist; }); it('should not render PEP when the entitlement does not match', async () => { - await (await import('./test-utilities.js')).initPep({ entName: 'not-matching-entitlement' }); + await initPep({ entName: 'not-matching-entitlement' }); await clock.runAllAsync(); expect(document.querySelector(allSelectors.pepWrapper)).to.not.exist; }); @@ -69,7 +70,7 @@ describe('PEP', () => { } return null; }); - await (await import('./test-utilities.js')).initPep({}); + await initPep({}); await clock.runAllAsync(); expect(document.querySelector(allSelectors.pepWrapper)).to.not.exist; }); @@ -80,13 +81,13 @@ describe('PEP', () => { if (url.includes('pep-prompt-content.plain.html')) return mockRes({ payload: pepPromptContent({ ...defaultConfig, redirectUrl: false, productName: false }) }); return null; }); - await (await import('./test-utilities.js')).initPep({}); + await initPep({}); await clock.runAllAsync(); expect(document.querySelector(allSelectors.pepWrapper)).to.not.exist; }); it('should not render PEP when the anchor element is open', async () => { - await (await import('./test-utilities.js')).initPep({ isAnchorOpen: true }); + await initPep({ isAnchorOpen: true }); await clock.runAllAsync(); expect(document.querySelector(allSelectors.pepWrapper)).to.not.exist; }); @@ -99,7 +100,7 @@ describe('PEP', () => { if (url.includes('pep-prompt-content.plain.html')) return mockRes({ payload: pepPromptContent({ ...defaultConfig, color: false, loaderDuration: false }) }); return null; }); - const pep = await (await import('./test-utilities.js')).initPep({}); + const pep = await initPep({}); await clock.runAllAsync(); const { 'loader-color': pepColor, 'loader-duration': pepDuration } = pep.options; expect(!!pepColor && !!pepDuration).to.equal(true); @@ -108,28 +109,28 @@ describe('PEP', () => { describe('PEP interaction tests', () => { it('should close PEP on Escape key', async () => { - await (await import('./test-utilities.js')).initPep({}); + await initPep({}); await clock.runAllAsync(); document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' })); expect(document.querySelector(allSelectors.pepWrapper)).to.not.exist; }); it('should close PEP on clicking the close icon', async () => { - await (await import('./test-utilities.js')).initPep({}); + await initPep({}); await clock.runAllAsync(); document.querySelector(allSelectors.closeIcon).click(); expect(document.querySelector(allSelectors.pepWrapper)).to.not.exist; }); it('should close PEP on clicking the CTA', async () => { - await (await import('./test-utilities.js')).initPep({}); + await initPep({}); await clock.runAllAsync(); document.querySelector(allSelectors.cta).click(); expect(document.querySelector(allSelectors.pepWrapper)).to.not.exist; }); it('should close PEP on clicking the anchor element', async () => { - await (await import('./test-utilities.js')).initPep({}); + await initPep({}); await clock.runAllAsync(); document.querySelector(allSelectors.appSwitcher).click(); expect(document.querySelector(allSelectors.pepWrapper)).to.not.exist; @@ -138,13 +139,13 @@ describe('PEP', () => { describe('PEP focus tests', () => { it('should focus on the close icon on initial render', async () => { - await (await import('./test-utilities.js')).initPep({}); + await initPep({}); await clock.runAllAsync(); expect(document.activeElement).to.equal(document.querySelector(allSelectors.closeIcon)); }); it('should focus on the anchor element after closing', async () => { - await (await import('./test-utilities.js')).initPep({}); + await initPep({}); await clock.runAllAsync(); document.querySelector(allSelectors.closeIcon).click(); expect(document.activeElement).to.equal(document.querySelector(allSelectors.appSwitcher));