Skip to content

Commit e58f54b

Browse files
authored
Restrict codemod to known packages (#8)
* Restrict codemod to known packages * Fix types
1 parent c1031ac commit e58f54b

File tree

4 files changed

+287
-30
lines changed

4 files changed

+287
-30
lines changed

.changeset/orange-readers-hope.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"codemod-missing-await-act": minor
3+
---
4+
5+
Restrict codemod to known modules

README.md

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ We all track usage of these methods throughout the file.
55
For example, given
66

77
```tsx
8+
import { act } from "react-dom/test-utils";
9+
810
function focus(element) {
911
act(() => {
1012
element.focus();
@@ -22,6 +24,8 @@ will add an `await` to `act` and also add `await` to `focus` since `focus` is no
2224
The end result will be
2325

2426
```tsx
27+
import { act } from "react-dom/test-utils";
28+
2529
async function focus(element) {
2630
await act(() => {
2731
element.focus();
@@ -35,11 +39,24 @@ test("focusing", async () => {
3539
});
3640
```
3741

38-
Right now we assume calls to `act`, `render`, `rerender`, `fireEvent` and `cleanup` should be awaited.
39-
These are all names of methods from React Testing Library.
40-
41-
Note that any call expression that calls a method from an object (so called "member expression") are not codemodded.
42-
For example, this codemod will not add an `await` to `someObj.cleanup()`.
42+
The following methods will be awaited when the codemod is applied:
43+
44+
- from `react`:
45+
- `unstable_act`
46+
- from `react-dom/test-utils`:
47+
- `act`
48+
- from `react-test-renderer`:
49+
- `act`
50+
- from `@testing-library/react`:
51+
- `act`
52+
- `cleanup`
53+
- `fireEvent`
54+
- `fireEvent.*`
55+
- `render`
56+
- `renderHook`
57+
58+
Right now we assume that any call to `rerender` and `unmount` should be awaited.
59+
These are all names of methods from React Testing Library.´
4360

4461
## Getting started
4562

transforms/__tests__/codemod-missing-await-act.js

Lines changed: 162 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ function applyTransform(source, options = {}) {
1717
test("act in test", () => {
1818
expect(
1919
applyTransform(`
20+
import { act } from "@testing-library/react"
2021
test("void works", () => {
2122
act()
2223
})
@@ -25,7 +26,8 @@ test("act in test", () => {
2526
})
2627
`)
2728
).toMatchInlineSnapshot(`
28-
"test("void works", async () => {
29+
"import { act } from "@testing-library/react"
30+
test("void works", async () => {
2931
await act()
3032
})
3133
test("return works", () => {
@@ -34,9 +36,26 @@ test("act in test", () => {
3436
`);
3537
});
3638

39+
test("local act untouched", () => {
40+
expect(
41+
applyTransform(`
42+
function act() {}
43+
test("void works", () => {
44+
act()
45+
})
46+
`)
47+
).toMatchInlineSnapshot(`
48+
"function act() {}
49+
test("void works", () => {
50+
act()
51+
})"
52+
`);
53+
});
54+
3755
test("act in utils #1", () => {
3856
expect(
3957
applyTransform(`
58+
import { act } from "@testing-library/react"
4059
function caseA() {
4160
return act()
4261
}
@@ -51,7 +70,8 @@ test("act in utils #1", () => {
5170
}
5271
`)
5372
).toMatchInlineSnapshot(`
54-
"function caseA() {
73+
"import { act } from "@testing-library/react"
74+
function caseA() {
5575
return act()
5676
}
5777
@@ -69,6 +89,7 @@ test("act in utils #1", () => {
6989
test("act in utils #2", () => {
7090
expect(
7191
applyTransform(`
92+
import { act } from "@testing-library/react"
7293
function caseA() {
7394
act()
7495
}
@@ -84,7 +105,8 @@ test("act in utils #2", () => {
84105
}
85106
`)
86107
).toMatchInlineSnapshot(`
87-
"async function caseA() {
108+
"import { act } from "@testing-library/react"
109+
async function caseA() {
88110
await act()
89111
}
90112
@@ -103,6 +125,7 @@ test("act in utils #2", () => {
103125
test("act in utils #3", () => {
104126
expect(
105127
applyTransform(`
128+
import { act } from "@testing-library/react"
106129
const caseA = () => {
107130
act()
108131
}
@@ -116,7 +139,8 @@ test("act in utils #3", () => {
116139
}
117140
`)
118141
).toMatchInlineSnapshot(`
119-
"const caseA = async () => {
142+
"import { act } from "@testing-library/react"
143+
const caseA = async () => {
120144
await act()
121145
}
122146
@@ -217,3 +241,137 @@ test("React Testing Library api", () => {
217241
});"
218242
`);
219243
});
244+
245+
test("React Testing Library api as namespace", () => {
246+
expect(
247+
applyTransform(`
248+
import * as RTL from "@testing-library/react";
249+
250+
beforeEach(() => {
251+
RTL.cleanup();
252+
});
253+
254+
function renderWithProviders(element) {
255+
const { rerender, unmount } = RTL.render(<TestProvider>{element}</TestProvider>);
256+
257+
return { rerender, unmount };
258+
}
259+
260+
test("test", () => {
261+
const { rerender, unmount } = renderWithProviders(<button>Test</button>);
262+
263+
RTL.fireEvent.click(screen.getByRole("button"));
264+
265+
rerender(<span />);
266+
267+
RTL.fireEvent(
268+
screen.getByRole("button"),
269+
new MouseEvent("click", {
270+
bubbles: true,
271+
cancelable: true,
272+
})
273+
);
274+
275+
unmount();
276+
});
277+
278+
test("renderHook", () => {
279+
const { result, unmount } = renderHook(() => useHook());
280+
281+
unmount();
282+
});
283+
284+
`)
285+
).toMatchInlineSnapshot(`
286+
"import * as RTL from "@testing-library/react";
287+
288+
beforeEach(async () => {
289+
await RTL.cleanup();
290+
});
291+
292+
async function renderWithProviders(element) {
293+
const { rerender, unmount } = await RTL.render(<TestProvider>{element}</TestProvider>);
294+
295+
return { rerender, unmount };
296+
}
297+
298+
test("test", async () => {
299+
const { rerender, unmount } = await renderWithProviders(<button>Test</button>);
300+
301+
RTL.fireEvent.click(screen.getByRole("button"));
302+
303+
await rerender(<span />);
304+
305+
await RTL.fireEvent(screen.getByRole("button"), new MouseEvent("click", {
306+
bubbles: true,
307+
cancelable: true,
308+
}));
309+
310+
await unmount();
311+
});
312+
313+
test("renderHook", async () => {
314+
const { result, unmount } = renderHook(() => useHook());
315+
316+
await unmount();
317+
});"
318+
`);
319+
});
320+
321+
test("react API", () => {
322+
expect(
323+
applyTransform(`
324+
import * as React from 'react'
325+
326+
test('test', () => {
327+
React.unstable_act()
328+
})
329+
`)
330+
).toMatchInlineSnapshot(`
331+
"import * as React from 'react'
332+
333+
test('test', async () => {
334+
await React.unstable_act()
335+
})"
336+
`);
337+
});
338+
339+
test("react-test-renderer API", () => {
340+
expect(
341+
applyTransform(`
342+
import { act } from 'react-test-renderer'
343+
344+
test('test', () => {
345+
act()
346+
})
347+
`)
348+
).toMatchInlineSnapshot(`
349+
"import { act } from 'react-test-renderer'
350+
351+
test('test', async () => {
352+
await act()
353+
})"
354+
`);
355+
});
356+
357+
test("react-dom API", () => {
358+
expect(
359+
applyTransform(`
360+
import { act } from 'react-dom/test-utils'
361+
import { flushSync } from 'react-dom'
362+
363+
test('test', () => {
364+
act()
365+
flushSync()
366+
})
367+
`)
368+
).toMatchInlineSnapshot(`
369+
"import { act } from 'react-dom/test-utils'
370+
import { flushSync } from 'react-dom'
371+
372+
test('test', async () => {
373+
await act()
374+
flushSync()
375+
})"
376+
`);
377+
});

0 commit comments

Comments
 (0)