Fix test flakiness by using jest timers

Summary: Some tests were occasionally flaky, by emulating delays, time variation should no longer influence tests.

Reviewed By: passy

Differential Revision: D28572946

fbshipit-source-id: f4134a6509a0ec0be2e8f36e5623c4882b5531b8
This commit is contained in:
Michel Weststrate
2021-05-25 02:21:03 -07:00
committed by Facebook GitHub Bot
parent 8d508c8634
commit d9c986fcf8
2 changed files with 40 additions and 27 deletions

View File

@@ -7,6 +7,8 @@
* @format * @format
*/ */
jest.useFakeTimers();
import React from 'react'; import React from 'react';
import produce from 'immer'; import produce from 'immer';
import {FlipperPlugin} from '../plugin'; import {FlipperPlugin} from '../plugin';
@@ -24,7 +26,6 @@ import {
import {selectPlugin} from '../reducers/connections'; import {selectPlugin} from '../reducers/connections';
import {updateSettings} from '../reducers/settings'; import {updateSettings} from '../reducers/settings';
import {switchPlugin} from '../reducers/pluginManager'; import {switchPlugin} from '../reducers/pluginManager';
import {sleep} from 'flipper-plugin/src/utils/sleep';
interface PersistedState { interface PersistedState {
count: 1; count: 1;
@@ -521,7 +522,7 @@ test('PluginContainer + Sandy plugin supports deeplink', async () => {
); );
}); });
await sleep(10); jest.runAllTimers();
expect(linksSeen).toEqual(['universe!']); expect(linksSeen).toEqual(['universe!']);
expect(renderer.baseElement).toMatchInlineSnapshot(` expect(renderer.baseElement).toMatchInlineSnapshot(`
<body> <body>
@@ -552,7 +553,7 @@ test('PluginContainer + Sandy plugin supports deeplink', async () => {
}), }),
); );
}); });
await sleep(10); jest.runAllTimers();
expect(linksSeen).toEqual(['universe!']); expect(linksSeen).toEqual(['universe!']);
// ...nor does a random other store update that does trigger a plugin container render // ...nor does a random other store update that does trigger a plugin container render
@@ -575,7 +576,7 @@ test('PluginContainer + Sandy plugin supports deeplink', async () => {
}), }),
); );
}); });
await sleep(10); jest.runAllTimers();
expect(linksSeen).toEqual(['universe!', 'london!']); expect(linksSeen).toEqual(['universe!', 'london!']);
// and same link does trigger if something else was selected in the mean time // and same link does trigger if something else was selected in the mean time
@@ -597,7 +598,7 @@ test('PluginContainer + Sandy plugin supports deeplink', async () => {
}), }),
); );
}); });
await sleep(10); jest.runAllTimers();
expect(linksSeen).toEqual(['universe!', 'london!', 'london!']); expect(linksSeen).toEqual(['universe!', 'london!', 'london!']);
}); });
@@ -798,7 +799,7 @@ test('PluginContainer + Sandy device plugin supports deeplink', async () => {
); );
}); });
await sleep(10); jest.runAllTimers();
expect(linksSeen).toEqual([theUniverse]); expect(linksSeen).toEqual([theUniverse]);
expect(renderer.baseElement).toMatchInlineSnapshot(` expect(renderer.baseElement).toMatchInlineSnapshot(`
<body> <body>
@@ -829,7 +830,7 @@ test('PluginContainer + Sandy device plugin supports deeplink', async () => {
}), }),
); );
}); });
await sleep(10); jest.runAllTimers();
expect(linksSeen).toEqual([theUniverse]); expect(linksSeen).toEqual([theUniverse]);
// ...nor does a random other store update that does trigger a plugin container render // ...nor does a random other store update that does trigger a plugin container render
@@ -852,7 +853,7 @@ test('PluginContainer + Sandy device plugin supports deeplink', async () => {
}), }),
); );
}); });
await sleep(10); jest.runAllTimers();
expect(linksSeen).toEqual([theUniverse, 'london!']); expect(linksSeen).toEqual([theUniverse, 'london!']);
// and same link does trigger if something else was selected in the mean time // and same link does trigger if something else was selected in the mean time
@@ -874,7 +875,7 @@ test('PluginContainer + Sandy device plugin supports deeplink', async () => {
}), }),
); );
}); });
await sleep(10); jest.runAllTimers();
expect(linksSeen).toEqual([theUniverse, 'london!', 'london!']); expect(linksSeen).toEqual([theUniverse, 'london!', 'london!']);
}); });
@@ -976,7 +977,7 @@ test('Sandy plugins support isPluginSupported + selectPlugin', async () => {
pluginInstance.selectPlugin(definition.id, 'data'); pluginInstance.selectPlugin(definition.id, 'data');
expect(store.getState().connections.selectedPlugin).toBe(definition.id); expect(store.getState().connections.selectedPlugin).toBe(definition.id);
expect(pluginInstance.activatedStub).toBeCalledTimes(2); expect(pluginInstance.activatedStub).toBeCalledTimes(2);
await sleep(10); jest.runAllTimers();
expect(renderer.baseElement.querySelector('h1')).toMatchInlineSnapshot(` expect(renderer.baseElement.querySelector('h1')).toMatchInlineSnapshot(`
<h1> <h1>
Plugin1 Plugin1

View File

@@ -12,6 +12,8 @@ import {createStore, Store} from 'redux';
import produce from 'immer'; import produce from 'immer';
import {sleep} from '../promiseTimeout'; import {sleep} from '../promiseTimeout';
jest.useFakeTimers();
const initialState = { const initialState = {
counter: {count: 0}, counter: {count: 0},
somethingUnrelated: false, somethingUnrelated: false,
@@ -73,13 +75,13 @@ describe('sideeffect', () => {
expect(events.length).toBe(0); expect(events.length).toBe(0);
// arrive as a single effect // arrive as a single effect
await sleep(10); jest.advanceTimersByTime(10);
expect(events).toEqual(['counter: 2']); expect(events).toEqual(['counter: 2']);
// no more events arrive after unsubscribe // no more events arrive after unsubscribe
unsubscribe(); unsubscribe();
store.dispatch({type: 'inc'}); store.dispatch({type: 'inc'});
await sleep(10); jest.advanceTimersByTime(10);
expect(events).toEqual(['counter: 2']); expect(events).toEqual(['counter: 2']);
expect(warn).not.toBeCalled(); expect(warn).not.toBeCalled();
expect(error).not.toBeCalled(); expect(error).not.toBeCalled();
@@ -99,13 +101,13 @@ describe('sideeffect', () => {
expect(events.length).toBe(0); expect(events.length).toBe(0);
// unrelated event doesn't trigger // unrelated event doesn't trigger
await sleep(10); jest.advanceTimersByTime(10);
expect(events.length).toBe(0); expect(events.length).toBe(0);
// counter increment does // counter increment does
store.dispatch({type: 'inc'}); store.dispatch({type: 'inc'});
await sleep(10); jest.advanceTimersByTime(10);
expect(events).toEqual(['counter: 1']); expect(events).toEqual(['counter: 1']);
expect(warn).not.toBeCalled(); expect(warn).not.toBeCalled();
expect(error).not.toBeCalled(); expect(error).not.toBeCalled();
@@ -125,13 +127,13 @@ describe('sideeffect', () => {
expect(events.length).toBe(0); expect(events.length).toBe(0);
// unrelated event doesn't trigger // unrelated event doesn't trigger
await sleep(10); jest.advanceTimersByTime(10);
expect(events.length).toBe(0); expect(events.length).toBe(0);
// counter increment does // counter increment does
store.dispatch({type: 'inc'}); store.dispatch({type: 'inc'});
await sleep(10); jest.advanceTimersByTime(10);
expect(events).toEqual(['counter: 1']); expect(events).toEqual(['counter: 1']);
expect(warn).not.toBeCalled(); expect(warn).not.toBeCalled();
expect(error).not.toBeCalled(); expect(error).not.toBeCalled();
@@ -151,7 +153,7 @@ describe('sideeffect', () => {
store.dispatch({type: 'inc'}); store.dispatch({type: 'inc'});
}).not.toThrow(); }).not.toThrow();
await sleep(10); jest.advanceTimersByTime(10);
expect(error.mock.calls).toMatchInlineSnapshot(` expect(error.mock.calls).toMatchInlineSnapshot(`
Array [ Array [
Array [ Array [
@@ -178,7 +180,7 @@ describe('sideeffect', () => {
); );
store.dispatch({type: 'inc'}); store.dispatch({type: 'inc'});
await sleep(200); jest.advanceTimersByTime(200);
expect(done).toBe(true); expect(done).toBe(true);
expect(warn.mock.calls[0][0]).toContain("Side effect 'test' took"); expect(warn.mock.calls[0][0]).toContain("Side effect 'test' took");
}); });
@@ -195,30 +197,40 @@ describe('sideeffect', () => {
// Fires immediately // Fires immediately
store.dispatch({type: 'inc'}); store.dispatch({type: 'inc'});
await sleep(100); jest.advanceTimersByTime(100);
expect(events).toEqual(['counter: 1']); expect(events).toEqual(['counter: 1']);
// no new tick in the next 100 ms // no new tick in the next 100 ms
await sleep(300); jest.advanceTimersByTime(300);
store.dispatch({type: 'inc'}); store.dispatch({type: 'inc'});
await sleep(300); jest.advanceTimersByTime(300);
store.dispatch({type: 'inc'}); store.dispatch({type: 'inc'});
expect(events).toEqual(['counter: 1']); expect(events).toEqual(['counter: 1']);
await sleep(1000); jest.advanceTimersByTime(1000);
expect(events).toEqual(['counter: 1', 'counter: 3']); expect(events).toEqual(['counter: 1', 'counter: 3']);
// long time now effect, it will fire right away again // long time no effect, it will fire right away again
await sleep(2000); // N.b. we need call sleep here to create a timeout, as time wouldn't progress otherwise
const p = sleep(2000);
jest.advanceTimersByTime(2000);
await p;
// ..but firing an event that doesn't match the selector doesn't reset the timer // ..but firing an event that doesn't match the selector doesn't reset the timer
store.dispatch({type: 'unrelated'}); store.dispatch({type: 'unrelated'});
await sleep(100); expect(events).toEqual(['counter: 1', 'counter: 3']);
jest.advanceTimersByTime(100);
store.dispatch({type: 'inc'}); store.dispatch({type: 'inc'});
store.dispatch({type: 'inc'}); store.dispatch({type: 'inc'});
await sleep(100); jest.advanceTimersByTime(100);
const p2 = sleep(2000);
jest.advanceTimersByTime(2000);
await p2;
expect(events).toEqual(['counter: 1', 'counter: 3', 'counter: 5']); expect(events).toEqual(['counter: 1', 'counter: 3', 'counter: 5']);
}); });
@@ -239,7 +251,7 @@ describe('sideeffect', () => {
store.dispatch({type: 'inc'}); store.dispatch({type: 'inc'});
store.dispatch({type: 'inc'}); store.dispatch({type: 'inc'});
// arrive as a single effect // arrive as a single effect
await sleep(10); jest.advanceTimersByTime(10);
expect(events).toEqual(['counter: 2', 'counter: 4']); expect(events).toEqual(['counter: 2', 'counter: 4']);
unsubscribe?.(); unsubscribe?.();
}); });