forked from adobecom/milo
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
MWPW-137634 footer unit tests (adobecom#1471)
* feat: footer unit tests * feat: unit tests for mobile, tablet, and wide screen layouts * hotfix * optimizing * Improving Code Readability * hotfix: added link for favicon * hotfix * hotfix * increased branches code coverage * increased branches code coverage for global footer --------- Co-authored-by: Narcis Radu <[email protected]>
- Loading branch information
1 parent
19eb173
commit 7c51d4c
Showing
9 changed files
with
575 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,275 @@ | ||
import { expect } from '@esm-bundle/chai'; | ||
import sinon, { stub } from 'sinon'; | ||
import { readFile } from '@web/test-runner-commands'; | ||
import { | ||
allElementsVisible, | ||
visibleSelectorsDesktop, | ||
visibleSelectorsMobile, | ||
createFullGlobalFooter, | ||
insertDummyElementOnTop, | ||
waitForFooterToDecorate, | ||
allSelectors, | ||
} from './test-utilities.js'; | ||
import baseFooter from './mocks/base-footer.js'; | ||
import fetchedFooter from './mocks/fetched-footer.js'; | ||
import icons from './mocks/icons.js'; | ||
import { isElementVisible, mockRes } from '../global-navigation/test-utilities.js'; | ||
import placeholders from '../global-navigation/mocks/placeholders.js'; | ||
|
||
describe('global footer', () => { | ||
let clock = null; | ||
beforeEach(async () => { | ||
document.body.innerHTML = baseFooter; | ||
clock = sinon.useFakeTimers({ | ||
toFake: ['setTimeout'], | ||
shouldAdvanceTime: true, | ||
}); | ||
|
||
stub(window, 'fetch').callsFake(async (url) => { | ||
if (url.includes('/footer')) { | ||
return mockRes({ | ||
payload: fetchedFooter( | ||
{ regionPickerHash: '/fragments/regions#langnav' }, | ||
), | ||
}); | ||
} | ||
if (url.includes('/placeholders')) return mockRes({ payload: placeholders }); | ||
if (url.includes('icons.svg')) return mockRes({ payload: icons }); | ||
if (url.includes('/regions.plain.html')) return mockRes({ payload: await readFile({ path: '../region-nav/mocks/regions.html' }) }); | ||
return null; | ||
}); | ||
}); | ||
|
||
afterEach(() => { | ||
clock.restore(); | ||
window.fetch.restore(); | ||
document.body.innerHTML = ''; | ||
}); | ||
|
||
describe('wide screen', async () => { | ||
it('should render the footer on wide screen', async () => { | ||
await createFullGlobalFooter({ waitForDecoration: true, viewport: 'wide' }); | ||
|
||
Object.keys(allSelectors).forEach((key) => expect( | ||
document.querySelector(allSelectors[key]) instanceof HTMLElement, | ||
).to.equal(true)); | ||
|
||
expect(allElementsVisible( | ||
visibleSelectorsDesktop, | ||
document.querySelector(allSelectors.container), | ||
)).to.equal(true); | ||
}); | ||
}); | ||
|
||
describe('desktop', () => { | ||
describe('basic sanity tests', () => { | ||
it('should have footer', async () => { | ||
await createFullGlobalFooter({ waitForDecoration: true }); | ||
expect(document.querySelector('footer')).to.exist; | ||
|
||
Object.keys(allSelectors).forEach((key) => expect( | ||
document.querySelector(allSelectors[key]) instanceof HTMLElement, | ||
).to.equal(true)); | ||
|
||
expect(allElementsVisible( | ||
visibleSelectorsDesktop, | ||
document.querySelector(allSelectors.container), | ||
)).to.equal(true); | ||
}); | ||
|
||
it('should handle failed fetch for footer content', async () => { | ||
window.fetch.restore(); | ||
stub(window, 'fetch').callsFake((url) => { | ||
if (url.includes('/footer')) { | ||
return mockRes({ | ||
payload: null, | ||
ok: false, | ||
status: 400, | ||
}); | ||
} | ||
if (url.includes('/placeholders')) return mockRes({ payload: placeholders }); | ||
if (url.includes('icons.svg')) return mockRes({ payload: icons }); | ||
return null; | ||
}); | ||
|
||
const globalFooter = await createFullGlobalFooter({ waitForDecoration: false }); | ||
expect(await globalFooter.decorateContent()).to.equal(undefined); | ||
}); | ||
|
||
it('should handle missing elements', async () => { | ||
const globalFooter = await createFullGlobalFooter({ waitForDecoration: true }); | ||
globalFooter.body = document.createElement('div'); | ||
expect(await globalFooter.decorateGrid()).to.equal(''); | ||
expect(await globalFooter.decorateProducts()).to.equal(''); | ||
expect(await globalFooter.decorateRegionPicker()).to.equal(''); | ||
expect(await globalFooter.decorateSocial()).to.equal(''); | ||
expect(await globalFooter.decoratePrivacy()).to.equal(''); | ||
}); | ||
}); | ||
|
||
describe('conditional render tests', () => { | ||
const { container, ...childSelectors } = allSelectors; | ||
it('should render the footer when in viewport', async () => { | ||
await createFullGlobalFooter({ waitForDecoration: true }); | ||
|
||
Object.keys(allSelectors).forEach((key) => expect( | ||
document.querySelector(allSelectors[key]) instanceof HTMLElement, | ||
).to.equal(true)); | ||
|
||
expect(allElementsVisible( | ||
visibleSelectorsDesktop, | ||
document.querySelector(allSelectors.container), | ||
)).to.equal(true); | ||
}); | ||
|
||
it('should render the footer after 3s when outside of the 300px range of the viewport', async () => { | ||
insertDummyElementOnTop({ height: window.innerHeight + 400 }); | ||
|
||
await createFullGlobalFooter({ waitForDecoration: false }); | ||
|
||
Object.keys(childSelectors).forEach((key) => expect( | ||
document.querySelector(allSelectors[key]) instanceof HTMLElement, | ||
).to.equal(false)); | ||
|
||
clock.tick(3000); | ||
await waitForFooterToDecorate(); | ||
|
||
Object.keys(allSelectors).forEach((key) => expect( | ||
document.querySelector(allSelectors[key]) instanceof HTMLElement, | ||
).to.equal(true)); | ||
}); | ||
|
||
it('should render the footer when outside of the viewport, but within 300px range', async () => { | ||
insertDummyElementOnTop({ height: window.innerHeight + 200 }); | ||
const startTime = performance.now(); | ||
await createFullGlobalFooter({ waitForDecoration: true }); | ||
const endTime = performance.now(); | ||
const timeDiff = endTime - startTime; | ||
|
||
Object.keys(allSelectors).forEach((key) => expect( | ||
document.querySelector(allSelectors[key]) instanceof HTMLElement, | ||
).to.equal(true)); | ||
// footer decoration should take less than 3s if within 300px range of viewport | ||
expect(timeDiff < 3000).to.equal(true); | ||
}); | ||
|
||
it('should render the footer when outside of the 300px viewport range, but scrolled into view earlier than 3s', async () => { | ||
insertDummyElementOnTop({ height: window.innerHeight + 400 }); | ||
const startTime = performance.now(); | ||
await createFullGlobalFooter({ waitForDecoration: false }); | ||
|
||
Object.keys(childSelectors).forEach((key) => expect( | ||
document.querySelector(allSelectors[key]) instanceof HTMLElement, | ||
).to.equal(false)); | ||
|
||
window.scrollBy(0, window.innerHeight); | ||
await waitForFooterToDecorate(); | ||
|
||
Object.keys(allSelectors).forEach((key) => expect( | ||
document.querySelector(allSelectors[key]) instanceof HTMLElement, | ||
).to.equal(true)); | ||
|
||
const endTime = performance.now(); | ||
const timeDiff = endTime - startTime; | ||
// footer decoration should take less than 3s when scrolled into view | ||
expect(timeDiff < 3000).to.equal(true); | ||
|
||
expect(allElementsVisible( | ||
visibleSelectorsDesktop, | ||
document.querySelector(allSelectors.container), | ||
)).to.equal(true); | ||
}); | ||
}); | ||
|
||
describe('region picker tests', () => { | ||
it('should handle non-empty hash', async () => { | ||
await createFullGlobalFooter({ waitForDecoration: true }); | ||
|
||
const regionPickerElem = document.querySelector(allSelectors.regionPicker); | ||
regionPickerElem.dispatchEvent(new Event('click')); | ||
|
||
expect(regionPickerElem.getAttribute('href') === '#langnav').to.equal(true); | ||
expect(regionPickerElem.getAttribute('aria-expanded')).to.equal('true'); | ||
|
||
window.dispatchEvent(new Event('milo:modal:closed')); | ||
expect(regionPickerElem.getAttribute('aria-expanded')).to.equal('false'); | ||
}); | ||
|
||
it('should handle empty hash', async () => { | ||
window.fetch.restore(); | ||
stub(window, 'fetch').callsFake(async (url) => { | ||
if (url.includes('/footer')) { | ||
return mockRes({ | ||
payload: fetchedFooter( | ||
{ regionPickerHash: '' }, | ||
), | ||
}); | ||
} | ||
if (url.includes('/placeholders')) return mockRes({ payload: placeholders }); | ||
if (url.includes('icons.svg')) return mockRes({ payload: icons }); | ||
return null; | ||
}); | ||
|
||
await createFullGlobalFooter({ waitForDecoration: true }); | ||
const regionPickerElem = document.querySelector(allSelectors.regionPicker); | ||
expect(regionPickerElem.getAttribute('href') === '#').to.equal(true); | ||
|
||
regionPickerElem.dispatchEvent(new Event('click')); | ||
expect(regionPickerElem.getAttribute('aria-expanded')).to.equal('true'); | ||
|
||
document.body.dispatchEvent(new Event('click', { bubbles: true })); | ||
expect(regionPickerElem.getAttribute('aria-expanded')).to.equal('false'); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('small desktop', async () => { | ||
it('should render the footer on small desktop', async () => { | ||
await createFullGlobalFooter({ waitForDecoration: true, viewport: 'smallDesktop' }); | ||
|
||
Object.keys(allSelectors).forEach((key) => expect( | ||
document.querySelector(allSelectors[key]) instanceof HTMLElement, | ||
).to.equal(true)); | ||
|
||
expect(allElementsVisible( | ||
visibleSelectorsDesktop, | ||
document.querySelector(allSelectors.container), | ||
)).to.equal(true); | ||
}); | ||
}); | ||
|
||
describe('mobile', () => { | ||
it('should render the footer on mobile', async () => { | ||
await createFullGlobalFooter({ waitForDecoration: true, viewport: 'mobile' }); | ||
|
||
Object.keys(allSelectors).forEach((key) => expect( | ||
document.querySelector(allSelectors[key]) instanceof HTMLElement, | ||
).to.equal(true)); | ||
|
||
expect(allElementsVisible( | ||
visibleSelectorsMobile, | ||
document.querySelector(allSelectors.container), | ||
)).to.equal(true); | ||
}); | ||
|
||
it('should open/close dropdowns on click', async () => { | ||
await createFullGlobalFooter({ waitForDecoration: true, viewport: 'mobile' }); | ||
|
||
for (const dropdown of Array.from(document.getElementsByClassName('feds-menu-section'))) { | ||
const header = dropdown.querySelector('.feds-menu-headline'); | ||
const links = Array.from(dropdown.getElementsByClassName('feds-navLink')); | ||
|
||
// open the dropdown | ||
header.dispatchEvent(new Event('click')); | ||
for (const link of links) { | ||
expect(isElementVisible(link)).to.equal(true); | ||
} | ||
// close the dropdown | ||
header.dispatchEvent(new Event('click')); | ||
for (const link of links) { | ||
expect(isElementVisible(link)).to.equal(false); | ||
} | ||
} | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export default '<footer class="global-footer"></footer>'; |
Oops, something went wrong.