describe('Visibility', function () { var document; beforeEach(function () { Visibility._chechedPrefix = null; Visibility._listening = false; Visibility._changeCallbacks = []; Visibility._lastTimer = 0; Visibility._timers = { }; Visibility.__hiddenBefore = false; Visibility._doc = document = { addEventListener: function () { } }; delete window.jQuery; }); it('should detect a browser with non-prefixed API', function () { document.visibilityState = 'visible'; expect( Visibility._prefix() ).toEqual(''); }); it('should detect vendor prefix', function () { document.mozVisibilityState = 'visible'; expect( Visibility._prefix() ).toEqual('moz'); }); it('should cache vendor prefix', function () { document.visibilityState = 'visible'; expect( Visibility._prefix() ).toEqual(''); delete document.visibilityState; document.webkitVisibilityState = 'visible'; expect( Visibility._prefix() ).toEqual(''); Visibility._chechedPrefix = null; expect( Visibility._prefix() ).toEqual('webkit'); }); it('should detect whether the Page Visibility API is supported', function () { expect( Visibility.isSupported() ).toBeFalsy(); document.webkitVisibilityState = 'visible'; Visibility._chechedPrefix = null; expect( Visibility.isSupported() ).toBeTruthy(); }); it('should use properties with vendor prefix', function () { Visibility._chechedPrefix = ''; expect( Visibility._name('hidden') ).toEqual('hidden'); Visibility._chechedPrefix = 'webkit'; expect( Visibility._name('hidden') ).toEqual('webkitHidden'); }); it('should return value from property with vendor prefix', function () { document.hidden = 2; document.webkitHidden = 1; Visibility._chechedPrefix = 'webkit'; expect( Visibility._prop('hidden') ).toEqual(1); Visibility._chechedPrefix = ''; expect( Visibility._prop('hidden') ).toEqual(2); }); it('should check if the page is hidden', function () { Visibility._chechedPrefix = 'webkit'; document.webkitHidden = true; expect( Visibility.hidden() ).toBeTruthy(); document.webkitHidden = false; expect( Visibility.hidden() ).toBeFalsy(); }); it('should return visibility state', function () { Visibility._chechedPrefix = 'webkit'; document.webkitVisibilityState = 'visible'; expect( Visibility.state() ).toEqual('visible'); }); it('should set listener only once', function () { Visibility._chechedPrefix = 'webkit'; spyOn(document, 'addEventListener'); Visibility._setListener(); Visibility._setListener(); expect( document.addEventListener ).toHaveBeenCalledWith( 'webkitvisibilitychange', jasmine.any(Function), false); expect( document.addEventListener.callCount ).toEqual(1); }); it('should set listener', function () { Visibility._chechedPrefix = 'webkit'; var listener; document.addEventListener = function (a, b, c) { listener = b; }; spyOn(Visibility, '_onVisibilityChange').andCallFake(function () { expect( this ).toBe(Visibility); }); Visibility._setListener(); listener(); expect( Visibility._onVisibilityChange ).toHaveBeenCalled(); }); it('should set listener in IE', function () { Visibility._chechedPrefix = 'ms'; Visibility._doc = document = { attachEvent: function () { } }; spyOn(document, 'attachEvent'); Visibility._setListener(); expect( document.attachEvent ).toHaveBeenCalled(); }); it('should return false upon calling `change` method when' + ' API is not supported', function () { spyOn(Visibility, 'isSupported').andReturn(false); spyOn(Visibility, '_setListener'); var callback = jasmine.createSpy(); expect( Visibility.change(callback) ).toEqual(false); expect( callback ).not.toHaveBeenCalled(); expect( Visibility._setListener ).not.toHaveBeenCalled(); }); it('should call callback on visibility state changes', function () { Visibility._chechedPrefix = 'webkit'; spyOn(Visibility, '_setListener'); var callback = jasmine.createSpy(); expect( Visibility.change(callback) ).toEqual(true); expect( Visibility._setListener ).toHaveBeenCalled(); var event = { }; document.webkitVisibilityState = 'visible'; Visibility._onVisibilityChange(event); expect( callback ).toHaveBeenCalledWith(event, 'visible'); document.webkitVisibilityState = 'hidden'; Visibility._onVisibilityChange(event); expect( callback.callCount ).toEqual(2); expect( callback.mostRecentCall.args ).toEqual([event, 'hidden']); }); it('should call onVisible callback immediately when API is not supported', function () { spyOn(Visibility, 'isSupported').andReturn(false); spyOn(Visibility, '_setListener'); var callback = jasmine.createSpy(); Visibility.onVisible(callback); expect( callback ).toHaveBeenCalled(); expect( Visibility._setListener ).not.toHaveBeenCalled(); }); it('should run onVisible callback immediately if page is visible', function () { Visibility._chechedPrefix = 'webkit'; document.webkitHidden = false; spyOn(Visibility, '_setListener'); var callback = jasmine.createSpy(); Visibility.onVisible(callback); expect( callback ).toHaveBeenCalled(); expect( Visibility._setListener ).not.toHaveBeenCalled(); }); it('should run onVisible callback by listener on hidden page', function () { Visibility._chechedPrefix = 'webkit'; document.webkitHidden = true; spyOn(Visibility, '_setListener'); var callback = jasmine.createSpy(); Visibility.onVisible(callback); expect( callback ).not.toHaveBeenCalled(); expect( Visibility._setListener ).toHaveBeenCalled(); Visibility._onVisibilityChange(); expect( callback ).not.toHaveBeenCalled(); document.webkitHidden = false; Visibility._onVisibilityChange(); expect( callback ).toHaveBeenCalled(); Visibility._onVisibilityChange(); expect( callback.callCount ).toEqual(1); }); it('should call DOM setInterval from internal method', function () { spyOn(window, 'setInterval').andReturn(102); var callback = function () { }; expect( Visibility._originalSetInterval(callback, 1000) ).toEqual(102); expect( window.setInterval ).toHaveBeenCalledWith(callback, 1000); }); it('should call jQuery Chrono plugin from internal method', function () { window.jQuery = { }; jQuery.every = jasmine.createSpy().andReturn(102); var callback = function () { }; expect( Visibility._chronoSetInterval(callback, '1 sec') ).toEqual(102); expect( jQuery.every ).toHaveBeenCalledWith('1 sec', callback); }); it('should autodetect the function to use as _setInterval', function () { Visibility._init(); expect( Visibility._setInterval ).toBe(Visibility._originalSetInterval); window.jQuery = { }; jQuery.every = function () { }; Visibility._init(); expect( Visibility._setInterval ).toBe(Visibility._chronoSetInterval); }); it('should create a new timer from every method', function () { Visibility._chechedPrefix = 'webkit'; document.webkitHidden = true; spyOn(Visibility, '_runTimer'); spyOn(Visibility, '_setListener'); var callback1 = function () { }; var id1 = Visibility.every(1, 10, callback1); expect( Visibility._lastTimer ).toEqual(id1); var callback2 = function () { }; var id2 = Visibility.every(2, callback2); expect( Visibility._lastTimer ).toEqual(id2); var right = { }; right[id1] = { interval: 1, hiddenInterval: 10, callback: callback1 }; right[id2] = { interval: 2, hiddenInterval: null, callback: callback2 }; expect( Visibility._timers ).toEqual(right); expect( Visibility._runTimer.callCount ).toEqual(2); expect( Visibility._runTimer.argsForCall[0] ).toEqual([id1, false]); expect( Visibility._runTimer.argsForCall[1] ).toEqual([id2, false]); expect( Visibility._setListener ).toHaveBeenCalled(); }); it('should set visible timer from every method without API', function () { spyOn(Visibility, '_setInterval'); spyOn(Visibility, '_setListener'); var callback = function () { }; Visibility.every(1, 10, callback); expect( Visibility._setInterval ).toHaveBeenCalledWith(callback, 1) expect( Visibility._setListener ).not.toHaveBeenCalled(); }); it('should execute timers', function () { Visibility._chechedPrefix = 'webkit'; document.webkitHidden = true; var intervalID = 100; spyOn(Visibility, '_setInterval').andCallFake(function () { return intervalID += 1; }); callback1 = jasmine.createSpy().andCallFake(function () { expect( this ).toBe(window); }); callback2 = jasmine.createSpy(); Visibility._timers = { 1: { interval: 1, hiddenInterval: 10, callback: callback1 }, 2: { interval: 2, hiddenInterval: null, callback: callback2 } }; Visibility._runTimer(1, false); expect( Visibility._timers[1].intervalID ).toEqual(101); expect( Visibility._setInterval.callCount ).toEqual(1); expect( Visibility._setInterval.mostRecentCall.args ). toEqual([callback1, 10]); expect( callback1 ).not.toHaveBeenCalled(); Visibility._runTimer(2, false); expect( Visibility._timers[2].intervalID ).not.toBeDefined(); expect( Visibility._setInterval.callCount ).toEqual(1); document.webkitHidden = false; Visibility._runTimer(1, true); expect( Visibility._timers[1].intervalID ).toEqual(102); expect( Visibility._setInterval.callCount ).toEqual(2); expect( Visibility._setInterval.mostRecentCall.args ). toEqual([callback1, 1]); expect( callback1 ).toHaveBeenCalled(); }); it('should stop timer', function () { spyOn(window, 'clearInterval'); Visibility._timers = { 1: { interval: 1, hiddenInterval: 2, callback: function () { }, intervalID: 101 }, }; Visibility._stopTimer(1); expect( window.clearInterval ).toHaveBeenCalledWith(101); expect( Visibility._timers[1].intervalID ).not.toBeDefined(); }); it('should remember if page is hidden on loading', function () { Visibility._chechedPrefix = 'webkit'; document.webkitHidden= true; Visibility._init(); expect( Visibility._hiddenBefore ).toBeTruthy(); document.webkitHidden = false; Visibility._init(); expect( Visibility._hiddenBefore ).toBeFalsy(); }); it('should remember if previous state is `visible`', function () { Visibility._chechedPrefix = 'webkit'; document.webkitHidden = true; Visibility._onVisibilityChange(); expect( Visibility._hiddenBefore ).toBeTruthy(); document.webkitHidden = false; Visibility._onVisibilityChange(); expect( Visibility._hiddenBefore ).toBeFalsy(); }); it('should stop and run timers on state changes', function () { Visibility._chechedPrefix = 'webkit'; document.webkitHidden = true; Visibility._hiddenBefore = true; spyOn(Visibility, '_stopTimer'); spyOn(Visibility, '_runTimer'); callback = jasmine.createSpy(); Visibility._timers = { 1: { interval: 1, hiddenInterval: 10, callback: callback }, 3: { interval: 2, hiddenInterval: null, callback: callback } }; Visibility._onVisibilityChange(); expect( Visibility._stopTimer ).not.toHaveBeenCalled(); expect( Visibility._runTimer ).not.toHaveBeenCalled(); document.webkitHidden = false; Visibility._onVisibilityChange(); expect( Visibility._stopTimer.callCount ).toEqual(2); expect( Visibility._stopTimer.argsForCall[0] ).toEqual(['1']); expect( Visibility._stopTimer.argsForCall[1] ).toEqual(['3']); expect( Visibility._runTimer.callCount ).toEqual(2); expect( Visibility._runTimer.argsForCall[0] ).toEqual(['1', true]); expect( Visibility._runTimer.argsForCall[1] ).toEqual(['3', true]); Visibility._onVisibilityChange(); expect( Visibility._stopTimer.callCount ).toEqual(2); expect( Visibility._runTimer.callCount ).toEqual(2); document.webkitHidden = true; Visibility._onVisibilityChange(); expect( Visibility._stopTimer.callCount ).toEqual(4); expect( Visibility._stopTimer.argsForCall[2] ).toEqual(['1']); expect( Visibility._stopTimer.argsForCall[3] ).toEqual(['3']); expect( Visibility._runTimer.callCount ).toEqual(4); expect( Visibility._runTimer.argsForCall[2] ).toEqual(['1', false]); expect( Visibility._runTimer.argsForCall[3] ).toEqual(['3', false]); }); it('should run afterPrerendering callback immediately' + ' if the API is not supported', function () { spyOn(Visibility, 'isSupported').andReturn(false); spyOn(Visibility, '_setListener'); var callback = jasmine.createSpy(); Visibility.afterPrerendering(callback); expect( callback ).toHaveBeenCalled(); expect( Visibility._setListener ).not.toHaveBeenCalled(); }); it('should run afterPrerendering immediately if page isn’t prerended', function () { Visibility._chechedPrefix = 'webkit'; document.webkitVisibilityState = 'hidden'; spyOn(Visibility, '_setListener'); var callback = jasmine.createSpy(); Visibility.afterPrerendering(callback); expect( callback ).toHaveBeenCalled(); expect( Visibility._setListener ).not.toHaveBeenCalled(); }); it('should run afterPrerendering listeners on prerended page', function () { Visibility._chechedPrefix = 'webkit'; document.webkitVisibilityState = 'prerender'; spyOn(Visibility, '_setListener'); var callback = jasmine.createSpy(); Visibility.afterPrerendering(callback); expect( callback ).not.toHaveBeenCalled(); expect( Visibility._setListener ).toHaveBeenCalled(); Visibility._onVisibilityChange(); expect( callback ).not.toHaveBeenCalled(); document.webkitVisibilityState = 'visible'; Visibility._onVisibilityChange(); expect( callback ).toHaveBeenCalled(); Visibility._onVisibilityChange(); expect( callback.callCount ).toEqual(1); }); });