diff options
author | Lukas Eipert <leipert@gitlab.com> | 2018-09-06 17:36:05 +0300 |
---|---|---|
committer | Lukas Eipert <leipert@gitlab.com> | 2018-10-01 16:42:19 +0300 |
commit | 4552a9f9fb14b82ae9c0b6b8cf971b0517721a75 (patch) | |
tree | f12fddb7167e77e42d92063fe33b73be61bb6606 /spec/javascripts/lazy_loader_spec.js | |
parent | df73116f75d4a7545fb4a7684aa76624efede7d0 (diff) |
Improve performance of LazyLoader by using IntersectionObserver
Every browser which supports IntersectionObserver will now use it over
observing scroll and resize events. Older browsers without support fall
back on the previous behavior.
Additionally the MutationObserver can be enabled and disabled manually
via the helper method `startContentObserver` and `stopContentObserver`.
This might prove useful on pages where we manipulate the DOM
extensively.
Diffstat (limited to 'spec/javascripts/lazy_loader_spec.js')
-rw-r--r-- | spec/javascripts/lazy_loader_spec.js | 185 |
1 files changed, 171 insertions, 14 deletions
diff --git a/spec/javascripts/lazy_loader_spec.js b/spec/javascripts/lazy_loader_spec.js index c177d79b9e0..eac4756e8a9 100644 --- a/spec/javascripts/lazy_loader_spec.js +++ b/spec/javascripts/lazy_loader_spec.js @@ -1,57 +1,214 @@ import LazyLoader from '~/lazy_loader'; +import { TEST_HOST } from './test_constants'; let lazyLoader = null; +const execImmediately = callback => { + callback(); +}; + describe('LazyLoader', function() { preloadFixtures('issues/issue_with_comment.html.raw'); - beforeEach(function() { - loadFixtures('issues/issue_with_comment.html.raw'); - lazyLoader = new LazyLoader({ - observerNode: 'body', + describe('with IntersectionObserver disabled', () => { + beforeEach(function() { + loadFixtures('issues/issue_with_comment.html.raw'); + + lazyLoader = new LazyLoader({ + observerNode: 'foobar', + }); + + spyOn(LazyLoader, 'supportsIntersectionObserver').and.callFake(() => false); + + spyOn(LazyLoader, 'loadImage').and.callThrough(); + + spyOn(window, 'requestAnimationFrame').and.callFake(execImmediately); + spyOn(window, 'requestIdleCallback').and.callFake(execImmediately); + + // Doing everything that happens normally in onload + lazyLoader.register(); + }); + + afterEach(() => { + lazyLoader.unregister(); + }); + + it('should copy value from data-src to src for img 1', function(done) { + const img = document.querySelectorAll('img[data-src]')[0]; + const originalDataSrc = img.getAttribute('data-src'); + img.scrollIntoView(); + + setTimeout(() => { + expect(LazyLoader.loadImage).toHaveBeenCalled(); + expect(img.getAttribute('src')).toBe(originalDataSrc); + expect(img).toHaveClass('js-lazy-loaded'); + done(); + }, 50); + }); + + it('should lazy load dynamically added data-src images', function(done) { + const newImg = document.createElement('img'); + const testPath = `${TEST_HOST}/img/testimg.png`; + newImg.className = 'lazy'; + newImg.setAttribute('data-src', testPath); + document.body.appendChild(newImg); + newImg.scrollIntoView(); + + setTimeout(() => { + expect(LazyLoader.loadImage).toHaveBeenCalled(); + expect(newImg.getAttribute('src')).toBe(testPath); + expect(newImg).toHaveClass('js-lazy-loaded'); + done(); + }, 50); + }); + + it('should not alter normal images', function(done) { + const newImg = document.createElement('img'); + const testPath = `${TEST_HOST}/img/testimg.png`; + newImg.setAttribute('src', testPath); + document.body.appendChild(newImg); + newImg.scrollIntoView(); + + setTimeout(() => { + expect(LazyLoader.loadImage).not.toHaveBeenCalled(); + expect(newImg).not.toHaveClass('js-lazy-loaded'); + done(); + }, 50); + }); + + it('should not load dynamically added pictures if content observer is turned off', done => { + lazyLoader.stopContentObserver(); + + const newImg = document.createElement('img'); + const testPath = `${TEST_HOST}/img/testimg.png`; + newImg.className = 'lazy'; + newImg.setAttribute('data-src', testPath); + document.body.appendChild(newImg); + newImg.scrollIntoView(); + + setTimeout(() => { + expect(LazyLoader.loadImage).not.toHaveBeenCalled(); + expect(newImg).not.toHaveClass('js-lazy-loaded'); + done(); + }, 50); + }); + + it('should load dynamically added pictures if content observer is turned off and on again', done => { + lazyLoader.stopContentObserver(); + lazyLoader.startContentObserver(); + + const newImg = document.createElement('img'); + const testPath = `${TEST_HOST}/img/testimg.png`; + newImg.className = 'lazy'; + newImg.setAttribute('data-src', testPath); + document.body.appendChild(newImg); + newImg.scrollIntoView(); + + setTimeout(() => { + expect(LazyLoader.loadImage).toHaveBeenCalled(); + expect(newImg).toHaveClass('js-lazy-loaded'); + done(); + }, 50); }); - // Doing everything that happens normally in onload - lazyLoader.loadCheck(); }); - describe('behavior', function() { + + describe('with IntersectionObserver enabled', () => { + beforeEach(function() { + loadFixtures('issues/issue_with_comment.html.raw'); + + lazyLoader = new LazyLoader({ + observerNode: 'foobar', + }); + + spyOn(LazyLoader, 'loadImage').and.callThrough(); + + spyOn(window, 'requestAnimationFrame').and.callFake(execImmediately); + spyOn(window, 'requestIdleCallback').and.callFake(execImmediately); + + // Doing everything that happens normally in onload + lazyLoader.register(); + }); + + afterEach(() => { + lazyLoader.unregister(); + }); + it('should copy value from data-src to src for img 1', function(done) { const img = document.querySelectorAll('img[data-src]')[0]; const originalDataSrc = img.getAttribute('data-src'); img.scrollIntoView(); setTimeout(() => { + expect(LazyLoader.loadImage).toHaveBeenCalled(); expect(img.getAttribute('src')).toBe(originalDataSrc); - expect(document.getElementsByClassName('js-lazy-loaded').length).toBeGreaterThan(0); + expect(img).toHaveClass('js-lazy-loaded'); done(); - }, 100); + }, 50); }); it('should lazy load dynamically added data-src images', function(done) { const newImg = document.createElement('img'); - const testPath = '/img/testimg.png'; + const testPath = `${TEST_HOST}/img/testimg.png`; newImg.className = 'lazy'; newImg.setAttribute('data-src', testPath); document.body.appendChild(newImg); newImg.scrollIntoView(); setTimeout(() => { + expect(LazyLoader.loadImage).toHaveBeenCalled(); expect(newImg.getAttribute('src')).toBe(testPath); - expect(document.getElementsByClassName('js-lazy-loaded').length).toBeGreaterThan(0); + expect(newImg).toHaveClass('js-lazy-loaded'); done(); - }, 100); + }, 50); }); it('should not alter normal images', function(done) { const newImg = document.createElement('img'); - const testPath = '/img/testimg.png'; + const testPath = `${TEST_HOST}/img/testimg.png`; newImg.setAttribute('src', testPath); document.body.appendChild(newImg); newImg.scrollIntoView(); setTimeout(() => { + expect(LazyLoader.loadImage).not.toHaveBeenCalled(); + expect(newImg).not.toHaveClass('js-lazy-loaded'); + done(); + }, 50); + }); + + it('should not load dynamically added pictures if content observer is turned off', done => { + lazyLoader.stopContentObserver(); + + const newImg = document.createElement('img'); + const testPath = `${TEST_HOST}/img/testimg.png`; + newImg.className = 'lazy'; + newImg.setAttribute('data-src', testPath); + document.body.appendChild(newImg); + newImg.scrollIntoView(); + + setTimeout(() => { + expect(LazyLoader.loadImage).not.toHaveBeenCalled(); expect(newImg).not.toHaveClass('js-lazy-loaded'); done(); - }, 100); + }, 50); + }); + + it('should load dynamically added pictures if content observer is turned off and on again', done => { + lazyLoader.stopContentObserver(); + lazyLoader.startContentObserver(); + + const newImg = document.createElement('img'); + const testPath = `${TEST_HOST}/img/testimg.png`; + newImg.className = 'lazy'; + newImg.setAttribute('data-src', testPath); + document.body.appendChild(newImg); + newImg.scrollIntoView(); + + setTimeout(() => { + expect(LazyLoader.loadImage).toHaveBeenCalled(); + expect(newImg).toHaveClass('js-lazy-loaded'); + done(); + }, 50); }); }); }); |