1 import { act, renderHook } from '@testing-library/react-hooks';
3 import { useLinksQueue } from './useLinksQueue';
5 const mockedLoadLinksMeta = jest.fn();
6 jest.mock('./useLinksListing', () => ({
7 useLinksListing: () => ({
8 loadLinksMeta: mockedLoadLinksMeta,
12 const mockedGetLink = jest.fn();
13 jest.mock('./useLinksState', () => ({
16 getLink: mockedGetLink,
21 jest.spyOn(global, 'setTimeout');
23 const mockedAbort = jest.fn();
24 let mockedAbortSignal = { aborted: false };
25 // @ts-ignore - not mocking the entire AbortSignal, sorry =)
26 global.AbortController = jest.fn(() => ({
27 signal: mockedAbortSignal,
31 const SHARE_ID = 'shareId';
33 const getLinkIds = (count: number) => [...Array(count).keys()].map((id) => `linkId-${id}`);
34 const getFakeRef: <T>(value: T) => React.MutableRefObject<T> = (value) => ({ current: value });
36 describe('useLinksQueue', () => {
38 current: ReturnType<typeof useLinksQueue>;
40 let unmountHook: () => void;
43 const { result, unmount } = renderHook(() => useLinksQueue());
46 unmountHook = unmount;
48 jest.clearAllTimers();
50 mockedGetLink.mockReset();
51 mockedLoadLinksMeta.mockReset();
53 mockedAbort.mockReset();
54 mockedAbortSignal.aborted = false;
56 mockedGetLink.mockImplementation(() => undefined);
59 it('should not add to queue if link is already in state', async () => {
60 mockedGetLink.mockImplementation((shareId, linkId) => ({
65 await act(async () => {
66 hook.current.addToQueue(SHARE_ID, 'linkId');
69 expect(setTimeout).not.toHaveBeenCalled();
72 it('should not add to queue if linkId is already in queue', async () => {
73 await act(async () => {
74 hook.current.addToQueue(SHARE_ID, 'linkId');
75 hook.current.addToQueue(SHARE_ID, 'linkId');
78 expect(setTimeout).toHaveBeenCalledTimes(1);
81 it('should debounce processing to wait for the queue to fill up', async () => {
82 const items = getLinkIds(7);
84 await act(async () => {
85 items.forEach((item) => hook.current.addToQueue(SHARE_ID, item));
90 expect(setTimeout).toHaveBeenCalledTimes(items.length);
91 expect(mockedLoadLinksMeta).toHaveBeenCalledTimes(1);
94 it('should not load links if the domRef is null', async () => {
95 await act(async () => {
96 hook.current.addToQueue(SHARE_ID, 'linkId', getFakeRef(null));
101 expect(setTimeout).toHaveBeenCalledTimes(1);
102 expect(mockedLoadLinksMeta).not.toHaveBeenCalled();
105 it('should abort when the hook is unmounted', async () => {
106 await act(async () => {
107 hook.current.addToQueue(SHARE_ID, 'linkId');
113 expect(mockedAbort).toHaveBeenCalled();
116 it('should not load links if aborted', async () => {
117 mockedAbortSignal.aborted = true;
119 await act(async () => {
120 hook.current.addToQueue(SHARE_ID, 'linkId');
125 expect(setTimeout).toHaveBeenCalledTimes(1);
126 expect(mockedLoadLinksMeta).not.toHaveBeenCalled();
129 it('should not infinite loop if loadLinksMeta fails', async () => {
130 // Silence console errors
131 const consoleErrorMock = jest.spyOn(console, 'error').mockImplementation(() => {});
133 mockedLoadLinksMeta.mockRejectedValue(new Error('oh no'));
135 await act(async () => {
136 hook.current.addToQueue(SHARE_ID, 'linkId');
139 await jest.runAllTimersAsync();
141 expect(setTimeout).toHaveBeenCalledTimes(1);
142 expect(mockedLoadLinksMeta).toHaveBeenCalledTimes(1);
143 expect(consoleErrorMock).toHaveBeenCalledTimes(1);
145 consoleErrorMock.mockRestore();