GithubHelp home page GithubHelp logo

eps1lon / codemod-missing-await-act Goto Github PK

View Code? Open in Web Editor NEW
1.0 2.0 0.0 33.4 MB

WIP Adds missing await to act calls (or its callers)

License: MIT License

JavaScript 99.94% TypeScript 0.06%

codemod-missing-await-act's Introduction

codemod-missing-await-act

Adds missing await to act calls or methods that like contain an act call using jscodeshift. We all track usage of these methods throughout the file. For example, given

import { act } from "react-dom/test-utils";

function focus(element) {
	act(() => {
		element.focus();
	});
}

test("focusing", () => {
	const { container } = render("<button />");

	focus(container);
});

will add an await to act and also add await to focus since focus is now an async method. The end result will be

import { act } from "react-dom/test-utils";

async function focus(element) {
	await act(() => {
		element.focus();
	});
}

test("focusing", async () => {
	const { container } = await render("<button />");

	await focus(container);
});

Right now we assume that any call to rerender and unmount should be awaited.

Methods that will be awaited by default when the codemod is applied
// codemod-missing-await/default-import-config.js
// Import aliases have no effect on the codemod.
// They're only used to not cause JS Syntax errors in this file.
// The codemod will only consider the imported name.
import {
	act,
	cleanup,
	/**
	 * @includeMemberCalls
	 * e.g. fireEvent.click()
	 */
	fireEvent,
	render,
	renderHook,
} from "@testing-library/react";
import {
	act as act2,
	cleanup as cleanup2,
	/**
	 * @includeMemberCalls
	 * e.g. fireEvent.click()
	 */
	fireEvent as fireEvent2,
	render as render2,
	renderHook as renderHook2,
} from "@testing-library/react/pure";
import {
	act as act3,
	cleanup as cleanup3,
	/**
	 * @includeMemberCalls
	 * e.g. fireEvent.click()
	 */
	fireEvent as fireEvent3,
	render as render3,
	renderHook as renderHook3,
} from "@testing-library/react-native";
import {
	act as act4,
	cleanup as cleanup4,
	/**
	 * @includeMemberCalls
	 * e.g. fireEvent.click()
	 */
	fireEvent as fireEvent4,
	render as render4,
	renderHook as renderHook4,
} from "@testing-library/react-native/pure";
import { unstable_act } from "react";
import { act as ReactTestUtilsAct } from "react-dom/test-utils";
import { act as ReactTestRendererAct } from "react-test-renderer";

You can add more methods if they're imported from somewhere by passing a JS file that only includes imports of those methods e.g. npx codemod-missing-await-act ./src --import-config ./my-testing-library-imports.js where my-testing-library-imports.js contains

import { renderWithProviders } from "@mycompany/testing-library";

Now the codemod will also add an await to any renderWithProviders call imported from @mycompany/testing-library.

By default, the codemod uses the code listed in codemod-missing-await/default-import-config.js.

Getting started

$ npx codemod-missing-await-act ./src
Processing 4 files...
All done.
Results:
0 errors
3 unmodified
0 skipped
1 ok
Time elapsed: 0.428seconds

Usage

$ npx codemod-missing-await-act

codemod-missing-await-act <paths...>

Positionals:
  paths                                                      [string] [required]

Options:
  --version         Show version number                                [boolean]
  --help            Show help                                          [boolean]
  --dry                                               [boolean] [default: false]
  --ignore-pattern                      [string] [default: "**/node_modules/**"]
  --import-config   A path to a JS file importing all methods whose calls should
                    be awaited.                                         [string]
  --verbose                                           [boolean] [default: false]

Examples:
  codemod-missing-await-act ./              Ignores `node_modules` and `build`
  --ignore-pattern                          folders
  "**/{node_modules,build}/**"
  codemod-missing-await-act ./              Adds await to to all calls of
  --import-confg                            methods imported in that file.
  ./missing-await-import-config.js

Limitations

Forces call expressions to be awaited

const acting = act(scope);
await anotherPromise;
await acting;

will add await to the act call.

This codemod is targetted at act calls. They're not allowed to be interleaved anyway so the original code was already problematic since it could also contain an act call. I don't think this is common in practice (TODO: check if we use this pattern).

async class methods are not propagated

We don't track references to class methods. Only references to (arrow-) function declarations or expressions are tracked.

class MyTestUtils {
	render() {
		act(scope);
	}
}

const utils = new MyTestUtils();

utils.render();

will be transformed to

class MyTestUtils {
	async render() {
		await act(scope);
	}
}

const utils = new MyTestUtils();

// no `await` added
utils.render();

Supported platforms

The following list contains officially supported runtimes. Please file an issue for runtimes that are not included in this list.

  • Node.js 14.x || 16.x || 18.x || 19.x || 20.x

codemod-missing-await-act's People

Contributors

eps1lon avatar github-actions[bot] avatar

Stargazers

 avatar

Watchers

 avatar  avatar

codemod-missing-await-act's Issues

Common manual fixes

Adding an Awaited when using ReturnType<typeof someNewlyAsyncTestingLibraryMethod>

 import { render } from '@testing-library/react';

-let screen: ReturnType<typeof render>;
+let screen: Awaited<ReturnType<typeof render>>;

Add config for what contains an `act` call

Codebases will have their own wrappers around React Testing Libraries. Once the codemod is applied these wrappers will now also be async which needs to be propagated.

Ideally we could just propagate this through files but that needs a full resolver implementation and building up a fully dependency tree (in memory). It seems better to use an iterative approach to propagating these changes:

rules = ReactTestingLibraryAPIS
while (rules are not empty) 
  apply codemod with rules
  rules = public APIs that just now became `async`

This issue will just add the config option.

default import name should be ignored

With a file like

import render from '../render'

render()

and an import config like

import render2 from '../render'

the transform isn't applied but should.

Output new rules to run codemod again

When an exported method changed to async we need to propagate that.

We'll stick to ESM for now for simplicity. However, the module specifier will be unknown so that requires user input.

Once #6 is done we can directly output the recommended config and prompt the user for the source specifier.

missed patterns

checkboxes.slice(0, 2).forEach((switchEl) => toggleSwitch(switchEl))
									         ^^^^^^^^^^^^ became async

Curried calls:

const renderMock = (Component) => (props) => render(<Component {...props} />)
// missing `await`
const view = renderMock(() => null)({})

Link to LOC when warning about newly async export

e.g.

/Users/sebastian.silbermann/klapp/clients/packages/features/products-and-search/redux-fetch-module/testUtils/index.tsx: Export 'renderWithStore' is now async. Make sure to update the rules of this codemod and run it again.

should link to line and column directly

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.