diff options
Diffstat (limited to 'spec/frontend_integration/test_helpers/utils')
3 files changed, 124 insertions, 0 deletions
diff --git a/spec/frontend_integration/test_helpers/utils/obj.js b/spec/frontend_integration/test_helpers/utils/obj.js new file mode 100644 index 00000000000..6c301798489 --- /dev/null +++ b/spec/frontend_integration/test_helpers/utils/obj.js @@ -0,0 +1,36 @@ +import { has, mapKeys, pick } from 'lodash'; + +/** + * This method is used to type-safely set values on the given object + * + * @template T + * @returns {T} A shallow copy of `obj`, with the values from `values` + * @throws {Error} If `values` contains a key that isn't already on `obj` + * @param {T} source + * @param {Object} values + */ +export const withValues = (source, values) => + Object.entries(values).reduce( + (acc, [key, value]) => { + if (!has(acc, key)) { + throw new Error( + `[mock_server] Cannot write property that does not exist on object '${key}'`, + ); + } + + return { + ...acc, + [key]: value, + }; + }, + { ...source }, + ); + +/** + * This method returns a subset of the given object and maps the key names based on the + * given `keys`. + * + * @param {Object} obj The source object. + * @param {Object} map The object which contains the keys to use and mapped key names. + */ +export const withKeys = (obj, map) => mapKeys(pick(obj, Object.keys(map)), (val, key) => map[key]); diff --git a/spec/frontend_integration/test_helpers/utils/obj_spec.js b/spec/frontend_integration/test_helpers/utils/obj_spec.js new file mode 100644 index 00000000000..0ad7b4a1a4c --- /dev/null +++ b/spec/frontend_integration/test_helpers/utils/obj_spec.js @@ -0,0 +1,23 @@ +import { withKeys, withValues } from './obj'; + +describe('frontend_integration/test_helpers/utils/obj', () => { + describe('withKeys', () => { + it('picks and maps keys', () => { + expect(withKeys({ a: '123', b: 456, c: 'd' }, { b: 'lorem', c: 'ipsum', z: 'zed ' })).toEqual( + { lorem: 456, ipsum: 'd' }, + ); + }); + }); + + describe('withValues', () => { + it('sets values', () => { + expect(withValues({ a: '123', b: 456 }, { b: 789 })).toEqual({ a: '123', b: 789 }); + }); + + it('throws if values has non-existent key', () => { + expect(() => withValues({ a: '123', b: 456 }, { b: 789, bogus: 'throws' })).toThrow( + `[mock_server] Cannot write property that does not exist on object 'bogus'`, + ); + }); + }); +}); diff --git a/spec/frontend_integration/test_helpers/utils/overclock_timers.js b/spec/frontend_integration/test_helpers/utils/overclock_timers.js new file mode 100644 index 00000000000..046c7f8e527 --- /dev/null +++ b/spec/frontend_integration/test_helpers/utils/overclock_timers.js @@ -0,0 +1,65 @@ +/** + * 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 + */ +// eslint-disable-next-line import/prefer-default-export +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; + }); +}; |