197 lines
5.8 KiB
JavaScript
197 lines
5.8 KiB
JavaScript
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();
|
|
});
|
|
});
|
|
});
|