Skip to content

Commit 2050c22

Browse files
Merge pull request #1736 from ral-facilities/fix-flaky-unit-tests
Fix flaky unit tests
2 parents be47692 + 87a736c commit 2050c22

File tree

4 files changed

+135
-105
lines changed

4 files changed

+135
-105
lines changed

packages/datagateway-common/src/hooks/useSticky.test.ts

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,72 @@
1-
import { renderHook, waitFor } from '@testing-library/react';
1+
import { fireEvent, renderHook, waitFor } from '@testing-library/react';
2+
import { Mock } from 'vitest';
23
import useSticky from './useSticky';
3-
import { fireEvent } from '@testing-library/react';
4+
5+
// Mock lodash.debounce to return the function we want to call without delay
6+
vi.mock('lodash.debounce', () => ({
7+
default: (fn: (args: unknown) => unknown) => fn,
8+
}));
49

510
describe('useSticky', () => {
6-
it('returns isSticky true if the bottom of the target element is scrolled past', () => {
7-
const mockGetBoundingClientRect = vi.fn();
8-
const target = document.createElement('div');
11+
let mockGetBoundingClientRect: Mock<() => DOMRect>;
12+
let target: HTMLDivElement;
13+
const initScrollY = global.scrollY;
14+
15+
beforeEach(() => {
16+
mockGetBoundingClientRect = vi.fn(() => new DOMRect(0, 0, 0, 100));
17+
target = document.createElement('div');
918
target.getBoundingClientRect = mockGetBoundingClientRect;
1019

20+
global.scrollY = 100;
21+
});
22+
23+
afterEach(() => {
24+
global.scrollY = initScrollY;
25+
});
26+
27+
it('returns isSticky true if the bottom of the target element is scrolled past', async () => {
1128
// if window scrollY is greater than boundingClientRect bottom
1229
// that means the element is scrolled out of view
13-
global.scrollY = 100;
14-
mockGetBoundingClientRect.mockReturnValue({
15-
bottom: 50,
16-
});
30+
mockGetBoundingClientRect.mockReturnValue(new DOMRect(0, 0, 0, 50));
1731

1832
const { result } = renderHook(() => useSticky({ current: target }));
1933

2034
// scroll the window
2135
fireEvent.scroll(window, {});
2236

23-
waitFor(() => {
37+
await waitFor(() => {
2438
expect(result.current.isSticky).toBe(true);
2539
});
2640
});
2741

28-
it('returns isSticky false if the bottom of the target element is still in view', () => {
29-
const mockGetBoundingClientRect = vi.fn();
30-
const target = document.createElement('div');
31-
target.getBoundingClientRect = mockGetBoundingClientRect;
32-
42+
it('returns isSticky false if the bottom of the target element is still in view', async () => {
3343
// if window scrollY is greater than boundingClientRect bottom
3444
// that means the element is scrolled out of view
35-
global.scrollY = 100;
36-
mockGetBoundingClientRect.mockReturnValue({
37-
bottom: 150,
38-
});
45+
mockGetBoundingClientRect.mockReturnValue(new DOMRect(0, 0, 0, 150));
3946

4047
const { result } = renderHook(() => useSticky({ current: target }));
4148

4249
// scroll the window
4350
fireEvent.scroll(window, {});
4451

45-
waitFor(() => {
52+
await waitFor(() => {
4653
expect(result.current.isSticky).toBe(false);
4754
});
4855
});
4956

50-
it('returns isSticky false upon window scroll if the given react ref points to null', () => {
57+
it('returns isSticky false upon window scroll if the given react ref points to null', async () => {
5158
const { result } = renderHook(() => useSticky({ current: null }));
5259
// scroll the window
5360
fireEvent.scroll(window, {});
54-
waitFor(() => {
61+
await waitFor(() => {
5562
expect(result.current.isSticky).toBe(false);
5663
});
5764
});
5865

59-
it('returns isStick false upon window scroll if bottom coordinate of the target element is unavailable', () => {
60-
const mockGetBoundingClientRect = vi.fn();
61-
const target = document.createElement('div');
62-
target.getBoundingClientRect = mockGetBoundingClientRect;
63-
66+
it('returns isStick false upon window scroll if bottom coordinate of the target element is unavailable', async () => {
6467
// if window scrollY is greater than boundingClientRect bottom
6568
// that means the element is scrolled out of view
66-
global.scrollY = 100;
69+
// @ts-expect-error purposefully providing an invalid value
6770
mockGetBoundingClientRect.mockReturnValue({
6871
top: 150,
6972
});
@@ -73,7 +76,7 @@ describe('useSticky', () => {
7376
// scroll the window
7477
fireEvent.scroll(window, {});
7578

76-
waitFor(() => {
79+
await waitFor(() => {
7780
expect(result.current.isSticky).toBe(false);
7881
});
7982
});

packages/datagateway-dataview/src/views/table/datafileTable.component.test.tsx

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
1-
import {
2-
type Datafile,
3-
type DownloadCartItem,
4-
dGCommonInitialState,
5-
} from 'datagateway-common';
6-
import { Provider } from 'react-redux';
7-
import { Router } from 'react-router-dom';
8-
import configureStore from 'redux-mock-store';
9-
import thunk from 'redux-thunk';
10-
import type { StateType } from '../../state/app.types';
11-
import { initialState as dgDataViewInitialState } from '../../state/reducers/dgdataview.reducer';
12-
import DatafileTable from './datafileTable.component';
131
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
14-
import { createMemoryHistory, type History } from 'history';
15-
import { findAllRows, findColumnHeaderByName } from '../../setupTests';
162
import {
173
render,
18-
type RenderResult,
194
screen,
205
waitFor,
216
within,
7+
type RenderResult,
228
} from '@testing-library/react';
239
import userEvent from '@testing-library/user-event';
10+
import axios, { type AxiosResponse } from 'axios';
11+
import {
12+
dGCommonInitialState,
13+
type Datafile,
14+
type DownloadCartItem,
15+
} from 'datagateway-common';
2416
import {
2517
findCellInRow,
2618
findColumnIndexByName,
2719
} from 'datagateway-search/src/setupTests';
28-
import axios, { type AxiosResponse } from 'axios';
20+
import { createMemoryHistory, type History } from 'history';
21+
import { Provider } from 'react-redux';
22+
import { Router } from 'react-router-dom';
23+
import configureStore from 'redux-mock-store';
24+
import thunk from 'redux-thunk';
25+
import { findAllRows, findColumnHeaderByName } from '../../setupTests';
26+
import type { StateType } from '../../state/app.types';
27+
import { initialState as dgDataViewInitialState } from '../../state/reducers/dgdataview.reducer';
28+
import DatafileTable from './datafileTable.component';
2929

3030
describe('Datafile table component', () => {
3131
const mockStore = configureStore([thunk]);
@@ -151,11 +151,14 @@ describe('Datafile table component', () => {
151151
renderComponent();
152152

153153
let rows: HTMLElement[] = [];
154-
await waitFor(async () => {
155-
rows = await findAllRows();
156-
// should have 1 row in the table
157-
expect(rows).toHaveLength(1);
158-
});
154+
await waitFor(
155+
async () => {
156+
rows = await findAllRows();
157+
// should have 1 row in the table
158+
expect(rows).toHaveLength(1);
159+
},
160+
{ timeout: 5_000 }
161+
);
159162

160163
const row = rows[0];
161164

@@ -310,9 +313,12 @@ describe('Datafile table component', () => {
310313

311314
renderComponent();
312315
// wait for rows to show up
313-
await waitFor(async () => {
314-
expect(await findAllRows()).toHaveLength(1);
315-
});
316+
await waitFor(
317+
async () => {
318+
expect(await findAllRows()).toHaveLength(1);
319+
},
320+
{ timeout: 5_000 }
321+
);
316322

317323
const selectAllCheckbox = await screen.findByRole('checkbox', {
318324
name: 'select all rows',
@@ -327,9 +333,12 @@ describe('Datafile table component', () => {
327333

328334
renderComponent();
329335
// wait for rows to show up
330-
await waitFor(async () => {
331-
expect(await findAllRows()).toHaveLength(1);
332-
});
336+
await waitFor(
337+
async () => {
338+
expect(await findAllRows()).toHaveLength(1);
339+
},
340+
{ timeout: 5_000 }
341+
);
333342

334343
await waitFor(() => {
335344
expect(
@@ -343,10 +352,13 @@ describe('Datafile table component', () => {
343352

344353
// wait for rows to show up
345354
let rows: HTMLElement[] = [];
346-
await waitFor(async () => {
347-
rows = await findAllRows();
348-
expect(rows).toHaveLength(1);
349-
});
355+
await waitFor(
356+
async () => {
357+
rows = await findAllRows();
358+
expect(rows).toHaveLength(1);
359+
},
360+
{ timeout: 5_000 }
361+
);
350362

351363
expect(
352364
within(rows[0]).getByRole('button', { name: 'buttons.download' })
@@ -358,10 +370,13 @@ describe('Datafile table component', () => {
358370

359371
// wait for rows to show up
360372
let rows: HTMLElement[] = [];
361-
await waitFor(async () => {
362-
rows = await findAllRows();
363-
expect(rows).toHaveLength(1);
364-
});
373+
await waitFor(
374+
async () => {
375+
rows = await findAllRows();
376+
expect(rows).toHaveLength(1);
377+
},
378+
{ timeout: 5_000 }
379+
);
365380

366381
expect(screen.queryByTestId('datafile-details-panel')).toBeNull();
367382

packages/datagateway-dataview/src/views/table/dls/dlsDatafilesTable.component.test.tsx

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
2+
import {
3+
render,
4+
screen,
5+
waitFor,
6+
within,
7+
type RenderResult,
8+
} from '@testing-library/react';
9+
import userEvent from '@testing-library/user-event';
10+
import axios, { AxiosResponse } from 'axios';
111
import {
212
Datafile,
313
dGCommonInitialState,
@@ -8,31 +18,21 @@ import {
818
useIds,
919
useRemoveFromCart,
1020
} from 'datagateway-common';
21+
import { createMemoryHistory, type History } from 'history';
1122
import { Provider } from 'react-redux';
1223
import { Router } from 'react-router-dom';
1324
import configureStore from 'redux-mock-store';
1425
import thunk from 'redux-thunk';
15-
import { StateType } from '../../../state/app.types';
16-
import { initialState as dgDataViewInitialState } from '../../../state/reducers/dgdataview.reducer';
17-
import DLSDatafilesTable from './dlsDatafilesTable.component';
18-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
19-
import { createMemoryHistory, type History } from 'history';
2026
import {
2127
findAllRows,
2228
findCellInRow,
2329
findColumnHeaderByName,
2430
findColumnIndexByName,
2531
findRowAt,
2632
} from '../../../setupTests';
27-
import {
28-
render,
29-
type RenderResult,
30-
screen,
31-
waitFor,
32-
within,
33-
} from '@testing-library/react';
34-
import userEvent from '@testing-library/user-event';
35-
import axios, { AxiosResponse } from 'axios';
33+
import { StateType } from '../../../state/app.types';
34+
import { initialState as dgDataViewInitialState } from '../../../state/reducers/dgdataview.reducer';
35+
import DLSDatafilesTable from './dlsDatafilesTable.component';
3636

3737
vi.mock('datagateway-common', async () => {
3838
const originalModule = await vi.importActual('datagateway-common');
@@ -135,9 +135,15 @@ describe('DLS datafiles table component', () => {
135135
it('renders correctly', async () => {
136136
renderComponent();
137137

138-
const rows = await findAllRows();
139-
// should have 1 row in the table
140-
expect(rows).toHaveLength(1);
138+
let rows: HTMLElement[] = [];
139+
await waitFor(
140+
async () => {
141+
rows = await findAllRows();
142+
// should have 1 row in the table
143+
expect(rows).toHaveLength(1);
144+
},
145+
{ timeout: 5_000 }
146+
);
141147

142148
expect(await findColumnHeaderByName('datafiles.name')).toBeInTheDocument();
143149
expect(

packages/datagateway-dataview/src/views/table/isis/isisDatafilesTable.component.test.tsx

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
1-
import ISISDatafilesTable from './isisDatafilesTable.component';
2-
import { initialState as dgDataViewInitialState } from '../../../state/reducers/dgdataview.reducer';
3-
import configureStore from 'redux-mock-store';
4-
import type { StateType } from '../../../state/app.types';
5-
import {
6-
type Datafile,
7-
dGCommonInitialState,
8-
DownloadCartItem,
9-
} from 'datagateway-common';
10-
import { Provider } from 'react-redux';
11-
import thunk from 'redux-thunk';
12-
import { Router } from 'react-router-dom';
131
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
14-
import { createMemoryHistory, type History } from 'history';
15-
import { findAllRows, findColumnHeaderByName } from '../../../setupTests';
162
import {
173
render,
18-
type RenderResult,
194
screen,
205
waitFor,
216
within,
7+
type RenderResult,
228
} from '@testing-library/react';
239
import userEvent from '@testing-library/user-event';
10+
import axios, { AxiosResponse } from 'axios';
11+
import {
12+
DownloadCartItem,
13+
dGCommonInitialState,
14+
type Datafile,
15+
} from 'datagateway-common';
2416
import {
2517
findCellInRow,
2618
findColumnIndexByName,
2719
} from 'datagateway-search/src/setupTests';
28-
import axios, { AxiosResponse } from 'axios';
20+
import { createMemoryHistory, type History } from 'history';
21+
import { Provider } from 'react-redux';
22+
import { Router } from 'react-router-dom';
23+
import configureStore from 'redux-mock-store';
24+
import thunk from 'redux-thunk';
25+
import { findAllRows, findColumnHeaderByName } from '../../../setupTests';
26+
import type { StateType } from '../../../state/app.types';
27+
import { initialState as dgDataViewInitialState } from '../../../state/reducers/dgdataview.reducer';
28+
import ISISDatafilesTable from './isisDatafilesTable.component';
2929

3030
describe('ISIS datafiles table component', () => {
3131
const mockStore = configureStore([thunk]);
@@ -151,11 +151,14 @@ describe('ISIS datafiles table component', () => {
151151
renderComponent();
152152

153153
let rows: HTMLElement[] = [];
154-
await waitFor(async () => {
155-
rows = await findAllRows();
156-
// should have 1 row in the table
157-
expect(rows).toHaveLength(1);
158-
});
154+
await waitFor(
155+
async () => {
156+
rows = await findAllRows();
157+
// should have 1 row in the table
158+
expect(rows).toHaveLength(1);
159+
},
160+
{ timeout: 5_000 }
161+
);
159162

160163
// check that column headers are shown correctly
161164
expect(await findColumnHeaderByName('datafiles.name')).toBeInTheDocument();
@@ -285,9 +288,12 @@ describe('ISIS datafiles table component', () => {
285288
renderComponent();
286289

287290
// wait for rows to show up
288-
await waitFor(async () => {
289-
expect(await findAllRows()).toHaveLength(1);
290-
});
291+
await waitFor(
292+
async () => {
293+
expect(await findAllRows()).toHaveLength(1);
294+
},
295+
{ timeout: 5_000 }
296+
);
291297

292298
// row should not be selected initially as the cart is empty
293299
expect(

0 commit comments

Comments
 (0)