diff options
Diffstat (limited to 'spec/frontend/lazy_loader_spec.js')
-rw-r--r-- | spec/frontend/lazy_loader_spec.js | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/spec/frontend/lazy_loader_spec.js b/spec/frontend/lazy_loader_spec.js new file mode 100644 index 00000000000..79a49aedf37 --- /dev/null +++ b/spec/frontend/lazy_loader_spec.js @@ -0,0 +1,153 @@ +import { noop } from 'lodash'; +import LazyLoader from '~/lazy_loader'; +import { TEST_HOST } from 'helpers/test_constants'; +import waitForPromises from './helpers/wait_for_promises'; +import { useMockMutationObserver, useMockIntersectionObserver } from 'helpers/mock_dom_observer'; + +const execImmediately = callback => { + callback(); +}; + +const TEST_PATH = `${TEST_HOST}/img/testimg.png`; + +describe('LazyLoader', () => { + let lazyLoader = null; + + const { trigger: triggerMutation } = useMockMutationObserver(); + const { trigger: triggerIntersection } = useMockIntersectionObserver(); + + const triggerChildMutation = () => { + triggerMutation(document.body, { options: { childList: true, subtree: true } }); + }; + + const triggerIntersectionWithRatio = img => { + triggerIntersection(img, { entry: { intersectionRatio: 0.1 } }); + }; + + const createLazyLoadImage = () => { + const newImg = document.createElement('img'); + newImg.className = 'lazy'; + newImg.setAttribute('data-src', TEST_PATH); + + document.body.appendChild(newImg); + triggerChildMutation(); + + return newImg; + }; + + const createImage = () => { + const newImg = document.createElement('img'); + newImg.setAttribute('src', TEST_PATH); + + document.body.appendChild(newImg); + triggerChildMutation(); + + return newImg; + }; + + beforeEach(() => { + jest.spyOn(window, 'requestAnimationFrame').mockImplementation(execImmediately); + jest.spyOn(window, 'requestIdleCallback').mockImplementation(execImmediately); + jest.spyOn(LazyLoader, 'loadImage'); + }); + + afterEach(() => { + document.body.innerHTML = ''; + }); + + describe.each` + hasIntersectionObserver | trigger + ${true} | ${triggerIntersectionWithRatio} + ${false} | ${noop} + `( + 'with hasIntersectionObserver=$hasIntersectionObserver', + ({ hasIntersectionObserver, trigger }) => { + let origIntersectionObserver; + + beforeEach(() => { + origIntersectionObserver = global.IntersectionObserver; + global.IntersectionObserver = hasIntersectionObserver + ? global.IntersectionObserver + : undefined; + + lazyLoader = new LazyLoader({ + observerNode: 'foobar', + }); + }); + + afterEach(() => { + global.IntersectionObserver = origIntersectionObserver; + lazyLoader.unregister(); + }); + + it('determines intersection observer support', () => { + expect(LazyLoader.supportsIntersectionObserver()).toBe(hasIntersectionObserver); + }); + + it('should copy value from data-src to src for img 1', () => { + const img = createLazyLoadImage(); + + // Doing everything that happens normally in onload + lazyLoader.register(); + + trigger(img); + + expect(LazyLoader.loadImage).toHaveBeenCalledWith(img); + expect(img.getAttribute('src')).toBe(TEST_PATH); + expect(img.getAttribute('data-src')).toBe(null); + expect(img).toHaveClass('js-lazy-loaded'); + }); + + it('should lazy load dynamically added data-src images', async () => { + lazyLoader.register(); + + const newImg = createLazyLoadImage(); + + trigger(newImg); + + await waitForPromises(); + + expect(LazyLoader.loadImage).toHaveBeenCalledWith(newImg); + expect(newImg.getAttribute('src')).toBe(TEST_PATH); + expect(newImg).toHaveClass('js-lazy-loaded'); + }); + + it('should not alter normal images', () => { + const newImg = createImage(); + + lazyLoader.register(); + + expect(LazyLoader.loadImage).not.toHaveBeenCalled(); + expect(newImg).not.toHaveClass('js-lazy-loaded'); + }); + + it('should not load dynamically added pictures if content observer is turned off', async () => { + lazyLoader.register(); + lazyLoader.stopContentObserver(); + + const newImg = createLazyLoadImage(); + + await waitForPromises(); + + expect(LazyLoader.loadImage).not.toHaveBeenCalledWith(newImg); + expect(newImg).not.toHaveClass('js-lazy-loaded'); + }); + + it('should load dynamically added pictures if content observer is turned off and on again', async () => { + lazyLoader.register(); + lazyLoader.stopContentObserver(); + lazyLoader.startContentObserver(); + + const newImg = createLazyLoadImage(); + + trigger(newImg); + + await waitForPromises(); + + expect(LazyLoader.loadImage).toHaveBeenCalledWith(newImg); + expect(newImg.getAttribute('src')).toBe(TEST_PATH); + expect(newImg).toHaveClass('js-lazy-loaded'); + }); + }, + ); +}); |