diff options
Diffstat (limited to 'spec/frontend/smart_interval_spec.js')
-rw-r--r-- | spec/frontend/smart_interval_spec.js | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/spec/frontend/smart_interval_spec.js b/spec/frontend/smart_interval_spec.js new file mode 100644 index 00000000000..b32ac99e4e4 --- /dev/null +++ b/spec/frontend/smart_interval_spec.js @@ -0,0 +1,197 @@ +import $ from 'jquery'; +import { assignIn } from 'lodash'; +import waitForPromises from 'helpers/wait_for_promises'; +import SmartInterval from '~/smart_interval'; + +jest.useFakeTimers(); + +let interval; + +describe('SmartInterval', () => { + const DEFAULT_MAX_INTERVAL = 100; + const DEFAULT_STARTING_INTERVAL = 5; + const DEFAULT_INCREMENT_FACTOR = 2; + + function createDefaultSmartInterval(config) { + const defaultParams = { + callback: () => Promise.resolve(), + startingInterval: DEFAULT_STARTING_INTERVAL, + maxInterval: DEFAULT_MAX_INTERVAL, + incrementByFactorOf: DEFAULT_INCREMENT_FACTOR, + lazyStart: false, + immediateExecution: false, + hiddenInterval: null, + }; + + if (config) { + assignIn(defaultParams, config); + } + + return new SmartInterval(defaultParams); + } + + afterEach(() => { + interval.destroy(); + }); + + describe('Increment Interval', () => { + it('should increment the interval delay', () => { + interval = createDefaultSmartInterval(); + + jest.runOnlyPendingTimers(); + + return waitForPromises().then(() => { + const intervalConfig = interval.cfg; + const iterationCount = 4; + const maxIntervalAfterIterations = + intervalConfig.startingInterval * intervalConfig.incrementByFactorOf ** iterationCount; + const currentInterval = interval.getCurrentInterval(); + + // Provide some flexibility for performance of testing environment + expect(currentInterval).toBeGreaterThan(intervalConfig.startingInterval); + expect(currentInterval).toBeLessThanOrEqual(maxIntervalAfterIterations); + }); + }); + + it('should not increment past maxInterval', () => { + interval = createDefaultSmartInterval({ maxInterval: DEFAULT_STARTING_INTERVAL }); + + jest.runOnlyPendingTimers(); + + return waitForPromises().then(() => { + const currentInterval = interval.getCurrentInterval(); + + expect(currentInterval).toBe(interval.cfg.maxInterval); + }); + }); + + it('does not increment while waiting for callback', () => { + interval = createDefaultSmartInterval({ + callback: () => new Promise($.noop), + }); + + jest.runOnlyPendingTimers(); + + return waitForPromises().then(() => { + const oneInterval = interval.cfg.startingInterval * DEFAULT_INCREMENT_FACTOR; + + expect(interval.getCurrentInterval()).toEqual(oneInterval); + }); + }); + }); + + describe('Public methods', () => { + beforeEach(() => { + interval = createDefaultSmartInterval(); + }); + + it('should cancel an interval', () => { + jest.runOnlyPendingTimers(); + + interval.cancel(); + + return waitForPromises().then(() => { + const { intervalId } = interval.state; + const currentInterval = interval.getCurrentInterval(); + const intervalLowerLimit = interval.cfg.startingInterval; + + expect(intervalId).toBeUndefined(); + expect(currentInterval).toBe(intervalLowerLimit); + }); + }); + + it('should resume an interval', () => { + jest.runOnlyPendingTimers(); + + interval.cancel(); + + interval.resume(); + + return waitForPromises().then(() => { + const { intervalId } = interval.state; + + expect(intervalId).toBeTruthy(); + }); + }); + }); + + describe('DOM Events', () => { + beforeEach(() => { + // This ensures DOM and DOM events are initialized for these specs. + setFixtures('<div></div>'); + + interval = createDefaultSmartInterval(); + }); + + it('should pause when page is not visible', () => { + jest.runOnlyPendingTimers(); + + return waitForPromises().then(() => { + expect(interval.state.intervalId).toBeTruthy(); + + // simulates triggering of visibilitychange event + interval.onVisibilityChange({ target: { visibilityState: 'hidden' } }); + + expect(interval.state.intervalId).toBeUndefined(); + }); + }); + + it('should change to the hidden interval when page is not visible', () => { + interval.destroy(); + + const HIDDEN_INTERVAL = 1500; + interval = createDefaultSmartInterval({ hiddenInterval: HIDDEN_INTERVAL }); + + jest.runOnlyPendingTimers(); + + return waitForPromises().then(() => { + expect(interval.state.intervalId).toBeTruthy(); + expect( + interval.getCurrentInterval() >= DEFAULT_STARTING_INTERVAL && + interval.getCurrentInterval() <= DEFAULT_MAX_INTERVAL, + ).toBeTruthy(); + + // simulates triggering of visibilitychange event + interval.onVisibilityChange({ target: { visibilityState: 'hidden' } }); + + expect(interval.state.intervalId).toBeTruthy(); + expect(interval.getCurrentInterval()).toBe(HIDDEN_INTERVAL); + }); + }); + + it('should resume when page is becomes visible at the previous interval', () => { + jest.runOnlyPendingTimers(); + + return waitForPromises().then(() => { + expect(interval.state.intervalId).toBeTruthy(); + + // simulates triggering of visibilitychange event + interval.onVisibilityChange({ target: { visibilityState: 'hidden' } }); + + expect(interval.state.intervalId).toBeUndefined(); + + // simulates triggering of visibilitychange event + interval.onVisibilityChange({ target: { visibilityState: 'visible' } }); + + expect(interval.state.intervalId).toBeTruthy(); + }); + }); + + it('should cancel on page unload', () => { + jest.runOnlyPendingTimers(); + + return waitForPromises().then(() => { + $(document).triggerHandler('beforeunload'); + + expect(interval.state.intervalId).toBeUndefined(); + expect(interval.getCurrentInterval()).toBe(interval.cfg.startingInterval); + }); + }); + + it('should execute callback before first interval', () => { + interval = createDefaultSmartInterval({ immediateExecution: true }); + + expect(interval.cfg.immediateExecution).toBeFalsy(); + }); + }); +}); |