Fix stuff happening based on timers not being run correctly in unit tests (#5764)

* Introduce helper for advancing fake time

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Introduce reactive now to kludge around global shared state in library

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Replace all usages of "now" from mobx-utils with our own kludge to get rid of shared global state between unit tests

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Consolidate all usages of advanceTimersByTime to make sure things happening based on timers are run correctly

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Fix incorrect expect in test

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Enable skipped unit test since prerequisites are done

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
Janne Savolainen authored and GitHub committed 2022-07-01 19:40:34 +03:00
1 parent 3480b517c1
commit 5f57213179
13 files changed
+190 -30

No files matched your search

+25
View File
@@ -0,0 +1,25 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { act } from "@testing-library/react";
let usingFakeTime = false;
export const advanceFakeTime = (milliseconds: number) => {
if (!usingFakeTime) {
throw new Error("Tried to advance fake time but it was not enabled. Call useFakeTime() first.");
}
act(() => {
jest.advanceTimersByTime(milliseconds);
});
};
export const useFakeTime = (dateTime: string) => {
usingFakeTime = true;
jest.useFakeTimers();
jest.setSystemTime(new Date(dateTime));
};
@@ -0,0 +1,70 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { RenderResult } from "@testing-library/react";
import { render } from "@testing-library/react";
import type { IComputedValue } from "mobx";
import { computed, observe } from "mobx";
import React from "react";
import { observer } from "mobx-react";
import { advanceFakeTime, useFakeTime } from "../../test-utils/use-fake-time";
import { reactiveNow } from "./reactive-now";
describe("reactiveNow", () => {
let someComputed: IComputedValue<boolean>;
beforeEach(() => {
useFakeTime("2015-10-21T07:28:00Z");
someComputed = computed(() => {
const currentTimestamp = reactiveNow();
return currentTimestamp > new Date("2015-10-21T07:28:00Z").getTime();
});
});
describe("react-context", () => {
let rendered: RenderResult;
beforeEach(() => {
const TestComponent = observer(
({ someComputed }: { someComputed: IComputedValue<boolean> }) => (
<div>{someComputed.get() ? "true" : "false"}</div>
),
);
rendered = render(<TestComponent someComputed={someComputed} />);
});
it("given time passes, works", () => {
advanceFakeTime(1000);
expect(rendered.container.textContent).toBe("true");
});
it("does not share the state from previous test", () => {
expect(rendered.container.textContent).toBe("false");
});
});
describe("non-react-context", () => {
let actual: boolean;
beforeEach(() => {
observe(someComputed, (changed) => {
actual = changed.newValue as boolean;
}, true);
});
it("given time passes, works", () => {
advanceFakeTime(1000);
expect(actual).toBe(true);
});
it("does not share the state from previous test", () => {
expect(actual).toBe(false);
});
});
});
@@ -0,0 +1,60 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { _isComputingDerivation } from "mobx";
import type { IResource } from "mobx-utils";
import { fromResource } from "mobx-utils";
// Note: This file is copy-pasted from mobx-utils to fix very specific issue.
// TODO: Remove this file once https://github.com/mobxjs/mobx-utils/issues/306 is fixed.
const tickers: Record<number|string, IResource<number>> = {};
export function reactiveNow(interval?: number | "frame") {
if (interval === void 0) { interval = 1000; }
if (!_isComputingDerivation()) {
// See #40
return Date.now();
}
// Note: This is the kludge until https://github.com/mobxjs/mobx-utils/issues/306 is fixed
const synchronizationIsEnabled = !process.env.JEST_WORKER_ID;
if (!tickers[interval] || !synchronizationIsEnabled) {
if (typeof interval === "number")
tickers[interval] = createIntervalTicker(interval);
else
tickers[interval] = createAnimationFrameTicker();
}
return tickers[interval].current();
}
function createIntervalTicker(interval: number) {
let subscriptionHandle: NodeJS.Timer;
return fromResource(function (sink) {
sink(Date.now());
subscriptionHandle = setInterval(function () { return sink(Date.now()); }, interval);
}, function () {
clearInterval(subscriptionHandle);
}, Date.now());
}
function createAnimationFrameTicker() {
const frameBasedTicker = fromResource(function (sink) {
sink(Date.now());
function scheduleTick() {
window.requestAnimationFrame(function () {
sink(Date.now());
if (frameBasedTicker.isAlive())
scheduleTick();
});
}
scheduleTick();
}, function () { }, Date.now());
return frameBasedTicker;
}