summaryrefslogtreecommitdiff
path: root/spec/frontend_integration/test_helpers/utils/overclock_timers.js
blob: 7df8af30a619ed37aeec9db3db045eb053baf412 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
 * This function replaces the existing `setTimeout` and `setInterval` with wrappers that
 * discount the `ms` passed in by `boost`.
 *
 * For example, if a module has:
 *
 * ```
 * setTimeout(cb, 100);
 * ```
 *
 * But a test has:
 *
 * ```
 * useOverclockTimers(25);
 * ```
 *
 * Then the module's call to `setTimeout` effectively becomes:
 *
 * ```
 * setTimeout(cb, 4);
 * ```
 *
 * It's important to note that the timing for `setTimeout` and order of execution is non-deterministic
 * and discounting the `ms` passed could make this very obvious and expose some underlying issues
 * with flaky failures.
 *
 * WARNING: If flaky spec failures show up in a spec that is using this helper, please consider either:
 *
 *   - Refactoring the production code so that it's reactive to state changes, not dependent on timers.
 *   - Removing the call to this helper from the spec.
 *
 * @param {Number} boost
 */
export const useOverclockTimers = (boost = 50) => {
  if (boost <= 0) {
    throw new Error(`[overclock_timers] boost (${boost}) cannot be <= 0`);
  }

  let origSetTimeout;
  let origSetInterval;
  const newSetTimeout = (fn, msParam = 0) => {
    const ms = msParam > 0 ? Math.floor(msParam / boost) : msParam;

    return origSetTimeout(fn, ms);
  };
  const newSetInterval = (fn, msParam = 0) => {
    const ms = msParam > 0 ? Math.floor(msParam / boost) : msParam;

    return origSetInterval(fn, ms);
  };

  beforeEach(() => {
    origSetTimeout = global.setTimeout;
    origSetInterval = global.setInterval;

    global.setTimeout = newSetTimeout;
    global.setInterval = newSetInterval;
  });

  afterEach(() => {
    global.setTimeout = origSetTimeout;
    global.setInterval = origSetInterval;
  });
};