20219: Improve useAsyncInterval testability, add unit test
[arvados-workbench2.git] / src / common / use-async-interval.test.tsx
diff --git a/src/common/use-async-interval.test.tsx b/src/common/use-async-interval.test.tsx
new file mode 100644 (file)
index 0000000..188f184
--- /dev/null
@@ -0,0 +1,96 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import React from 'react';
+import { useAsyncInterval } from './use-async-interval';
+import { configure, mount } from 'enzyme';
+import Adapter from 'enzyme-adapter-react-16';
+import FakeTimers from "@sinonjs/fake-timers";
+
+configure({ adapter: new Adapter() });
+const clock = FakeTimers.install();
+
+jest.mock('react', () => {
+    const originalReact = jest.requireActual('react');
+    const mUseRef = jest.fn();
+    return {
+        ...originalReact,
+        useRef: mUseRef,
+    };
+});
+
+const TestComponent = (props): JSX.Element => {
+    useAsyncInterval(props.callback, 2000);
+    return <span />;
+};
+
+describe('useAsyncInterval', () => {
+    it('should fire repeatedly after the interval', async () => {
+        const mockedReact = React as jest.Mocked<typeof React>;
+        const ref = { current: {} };
+        mockedReact.useRef.mockReturnValue(ref);
+
+        const syncCallback = jest.fn();
+        const testComponent = mount(<TestComponent
+            callback={syncCallback}
+        />);
+
+        // cb queued with interval but not called
+        expect(syncCallback).not.toHaveBeenCalled();
+
+        // wait for first tick
+        await clock.tickAsync(2000);
+        expect(syncCallback).toHaveBeenCalledTimes(1);
+
+        // wait for second tick
+        await clock.tickAsync(2000);
+        expect(syncCallback).toHaveBeenCalledTimes(2);
+
+        // wait for third tick
+        await clock.tickAsync(2000);
+        expect(syncCallback).toHaveBeenCalledTimes(3);
+    });
+
+    it('should wait for async callbacks to complete in between polling', async () => {
+        const mockedReact = React as jest.Mocked<typeof React>;
+        const ref = { current: {} };
+        mockedReact.useRef.mockReturnValue(ref);
+
+        const delayedCallback = jest.fn(() => (
+            new Promise<void>((resolve) => {
+                setTimeout(() => {
+                    resolve();
+                }, 2000);
+            })
+        ));
+        const testComponent = mount(<TestComponent
+            callback={delayedCallback}
+        />);
+
+        // cb queued with setInterval but not called
+        expect(delayedCallback).not.toHaveBeenCalled();
+
+        // Wait 2 seconds for first tick
+        await clock.tickAsync(2000);
+        // First cb called after 2 seconds
+        expect(delayedCallback).toHaveBeenCalledTimes(1);
+        // Wait for cb to resolve for 2 seconds
+        await clock.tickAsync(2000);
+        expect(delayedCallback).toHaveBeenCalledTimes(1);
+
+        // Wait 2 seconds for second tick
+        await clock.tickAsync(2000);
+        expect(delayedCallback).toHaveBeenCalledTimes(2);
+        // Wait for cb to resolve for 2 seconds
+        await clock.tickAsync(2000);
+        expect(delayedCallback).toHaveBeenCalledTimes(2);
+
+        // Wait 2 seconds for third tick
+        await clock.tickAsync(2000);
+        expect(delayedCallback).toHaveBeenCalledTimes(3);
+        // Wait for cb to resolve for 2 seconds
+        await clock.tickAsync(2000);
+        expect(delayedCallback).toHaveBeenCalledTimes(3);
+    });
+});