Skip to content

feat(await-async-utils): add auto-fix #918

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -296,7 +296,7 @@ module.exports = [
| :------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------- | :------------------------------------------------------------------ | :-- |
| [await-async-events](docs/rules/await-async-events.md) | Enforce promises from async event methods are handled | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-vue][] | | 🔧 |
| [await-async-queries](docs/rules/await-async-queries.md) | Enforce promises from async queries to be handled | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-vue][] | | |
| [await-async-utils](docs/rules/await-async-utils.md) | Enforce promises from async utils to be awaited properly | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-vue][] | | |
| [await-async-utils](docs/rules/await-async-utils.md) | Enforce promises from async utils to be awaited properly | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-vue][] | | 🔧 |
| [consistent-data-testid](docs/rules/consistent-data-testid.md) | Ensures consistent usage of `data-testid` | | | |
| [no-await-sync-events](docs/rules/no-await-sync-events.md) | Disallow unnecessary `await` for sync events | ![badge-angular][] ![badge-dom][] ![badge-react][] | | |
| [no-await-sync-queries](docs/rules/no-await-sync-queries.md) | Disallow unnecessary `await` for sync queries | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-vue][] | | |
2 changes: 2 additions & 0 deletions docs/rules/await-async-utils.md
Original file line number Diff line number Diff line change
@@ -2,6 +2,8 @@

💼 This rule is enabled in the following configs: `angular`, `dom`, `marko`, `react`, `vue`.

🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).

<!-- end auto-generated rule header -->

Ensure that promises returned by async utils are handled properly.
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@ export type TestingLibrarySettings = {

export type TestingLibraryContext<
TOptions extends readonly unknown[],
TMessageIds extends string,
TMessageIds extends string
> = Readonly<
TSESLint.RuleContext<TMessageIds, TOptions> & {
settings: TestingLibrarySettings;
@@ -45,7 +45,7 @@ export type TestingLibraryContext<
export type EnhancedRuleCreate<
TOptions extends readonly unknown[],
TMessageIds extends string,
TRuleListener extends TSESLint.RuleListener = TSESLint.RuleListener,
TRuleListener extends TSESLint.RuleListener = TSESLint.RuleListener
> = (
context: TestingLibraryContext<TOptions, TMessageIds>,
optionsWithDefault: Readonly<TOptions>,
@@ -156,7 +156,7 @@ export type DetectionOptions = {
export function detectTestingLibraryUtils<
TOptions extends readonly unknown[],
TMessageIds extends string,
TRuleListener extends TSESLint.RuleListener = TSESLint.RuleListener,
TRuleListener extends TSESLint.RuleListener = TSESLint.RuleListener
>(
ruleCreate: EnhancedRuleCreate<TOptions, TMessageIds, TRuleListener>,
{ skipRuleReportingCheck = false }: Partial<DetectionOptions> = {}
2 changes: 1 addition & 1 deletion lib/create-testing-library-rule/index.ts
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ import {
export function createTestingLibraryRule<
TOptions extends readonly unknown[],
TMessageIds extends string,
TRuleListener extends TSESLint.RuleListener = TSESLint.RuleListener,
TRuleListener extends TSESLint.RuleListener = TSESLint.RuleListener
>({
create,
detectionOptions = {},
2 changes: 1 addition & 1 deletion lib/rules/await-async-events.ts
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ type EventModules = (typeof EVENTS_SIMULATORS)[number];
export type Options = [
{
eventModule: EventModules | EventModules[];
},
}
];

export default createTestingLibraryRule<Options, MessageIds>({
11 changes: 11 additions & 0 deletions lib/rules/await-async-utils.ts
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ import {
getFunctionName,
getInnermostReturningFunction,
getVariableReferences,
isMemberExpression,
isObjectPattern,
isPromiseHandled,
isProperty,
@@ -36,6 +37,7 @@ export default createTestingLibraryRule<Options, MessageIds>({
'Promise returned from {{ name }} wrapper over async util must be handled',
},
schema: [],
fixable: 'code',
},
defaultOptions: [],

@@ -149,6 +151,12 @@ export default createTestingLibraryRule<Options, MessageIds>({
data: {
name: node.name,
},
fix: (fixer) => {
if (isMemberExpression(node.parent)) {
return fixer.insertTextBefore(node.parent, 'await ');
}
return fixer.insertTextBefore(node, 'await ');
},
});
}
} else {
@@ -161,6 +169,9 @@ export default createTestingLibraryRule<Options, MessageIds>({
data: {
name: node.name,
},
fix: (fixer) => {
return fixer.insertTextBefore(referenceNode, 'await ');
},
});
return;
}
2 changes: 1 addition & 1 deletion lib/rules/consistent-data-testid.ts
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ export type Options = [
testIdAttribute?: string[] | string;
testIdPattern: string;
customMessage?: string;
},
}
];

const FILENAME_PLACEHOLDER = '{fileName}';
2 changes: 1 addition & 1 deletion lib/rules/no-render-in-lifecycle.ts
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ export type MessageIds = 'noRenderInSetup';
type Options = [
{
allowTestingFrameworkSetupHook?: string;
},
}
];

export function findClosestBeforeHook(
2 changes: 1 addition & 1 deletion lib/rules/prefer-explicit-assert.ts
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ type Options = [
{
assertion?: string;
includeFindQueries?: boolean;
},
}
];

const isAtTopLevel = (node: TSESTree.Node) =>
2 changes: 1 addition & 1 deletion lib/rules/prefer-presence-queries.ts
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ export type Options = [
{
presence?: boolean;
absence?: boolean;
},
}
];

export default createTestingLibraryRule<Options, MessageIds>({
2 changes: 1 addition & 1 deletion lib/rules/prefer-query-matchers.ts
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ export type Options = [
query: 'get' | 'query';
matcher: string;
}[];
},
}
];

export default createTestingLibraryRule<Options, MessageIds>({
2 changes: 1 addition & 1 deletion lib/utils/compat.ts
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ declare module '@typescript-eslint/utils/dist/ts-eslint/Rule' {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
TMessageIds extends string,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
TOptions extends readonly unknown[],
TOptions extends readonly unknown[]
> {
/**
* The filename associated with the source.
2 changes: 1 addition & 1 deletion lib/utils/types.ts
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ export type TestingLibraryRuleMetaDocs<TOptions extends readonly unknown[]> =
};
export type TestingLibraryRuleMeta<
TMessageIds extends string,
TOptions extends readonly unknown[],
TOptions extends readonly unknown[]
> = Omit<TSESLint.RuleMetaData<TMessageIds>, 'docs'> & {
docs: TestingLibraryRuleMetaDocs<TOptions>;
};
2 changes: 1 addition & 1 deletion tests/create-testing-library-rule.test.ts
Original file line number Diff line number Diff line change
@@ -459,7 +459,7 @@ ruleTester.run(RULE_NAME, rule, {
messageId: 'renderError',
},
],
}) as const
} as const)
),
{
code: `
4 changes: 2 additions & 2 deletions tests/lib/FlatCompatRuleTester.ts
Original file line number Diff line number Diff line change
@@ -122,7 +122,7 @@ export class FlatCompatRuleTester extends TSESLint.RuleTester {

public override run<
TMessageIds extends string,
TOptions extends readonly unknown[],
TOptions extends readonly unknown[]
>(
ruleName: string,
rule: TSESLint.RuleModule<TMessageIds, TOptions>,
@@ -141,7 +141,7 @@ export class FlatCompatRuleTester extends TSESLint.RuleTester {
| TSESLint.RuleTesterConfig
| string
| TSESLint.ValidTestCase<unknown[]>
| TSESLint.InvalidTestCase<string, unknown[]>,
| TSESLint.InvalidTestCase<string, unknown[]>
>(config: T): T {
if (!config || !usingFlatConfig || typeof config === 'string') {
return config;
44 changes: 22 additions & 22 deletions tests/lib/rules/await-async-events.test.ts
Original file line number Diff line number Diff line change
@@ -427,7 +427,7 @@ ruleTester.run(RULE_NAME, rule, {
await fireEvent.${eventMethod}(getByLabelText('username'))
})
`,
}) as const
} as const)
),
...FIRE_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -448,7 +448,7 @@ ruleTester.run(RULE_NAME, rule, {
],
options: [{ eventModule: 'fireEvent' }],
output: null,
}) as const
} as const)
),
...FIRE_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -481,7 +481,7 @@ ruleTester.run(RULE_NAME, rule, {

test('should handle external function', run)
`,
}) as const
} as const)
),
...FIRE_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -508,7 +508,7 @@ ruleTester.run(RULE_NAME, rule, {
await testingLibraryFireEvent.${eventMethod}(getByLabelText('username'))
})
`,
}) as const
} as const)
),
...FIRE_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -535,7 +535,7 @@ ruleTester.run(RULE_NAME, rule, {
await testingLibrary.fireEvent.${eventMethod}(getByLabelText('username'))
})
`,
}) as const
} as const)
),
...FIRE_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -569,7 +569,7 @@ ruleTester.run(RULE_NAME, rule, {
await fireEvent.${eventMethod}(getByLabelText('username'))
})
`,
}) as const
} as const)
),
...FIRE_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -598,7 +598,7 @@ ruleTester.run(RULE_NAME, rule, {
await fireEvent.${eventMethod}(getByLabelText('username'))
})
`,
}) as const
} as const)
),
...FIRE_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -631,7 +631,7 @@ ruleTester.run(RULE_NAME, rule, {
await fireEvent.${eventMethod}(getByLabelText('username'))
})
`,
}) as const
} as const)
),
...FIRE_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -664,7 +664,7 @@ ruleTester.run(RULE_NAME, rule, {
await fireEvent.${eventMethod}(getByLabelText('username'))
})
`,
}) as const
} as const)
),

...FIRE_EVENT_ASYNC_FUNCTIONS.map(
@@ -695,7 +695,7 @@ ruleTester.run(RULE_NAME, rule, {
const promise = await fireEvent.${eventMethod}(getByLabelText('username'))
})
`,
}) as const
} as const)
),
...FIRE_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -731,7 +731,7 @@ ruleTester.run(RULE_NAME, rule, {
await triggerEvent()
})
`,
}) as const
} as const)
),
...FIRE_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -756,7 +756,7 @@ ruleTester.run(RULE_NAME, rule, {
],
options: [{ eventModule: 'fireEvent' }],
output: null,
}) as const
} as const)
),
]),
...USER_EVENT_ASYNC_FRAMEWORKS.flatMap((testingFramework) => [
@@ -785,7 +785,7 @@ ruleTester.run(RULE_NAME, rule, {
await userEvent.${eventMethod}(getByLabelText('username'))
})
`,
}) as const
} as const)
),
...USER_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -806,7 +806,7 @@ ruleTester.run(RULE_NAME, rule, {
],
options: [{ eventModule: 'userEvent' }],
output: null,
}) as const
} as const)
),
...USER_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -833,7 +833,7 @@ ruleTester.run(RULE_NAME, rule, {
await testingLibraryUserEvent.${eventMethod}(getByLabelText('username'))
})
`,
}) as const
} as const)
),
...USER_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -867,7 +867,7 @@ ruleTester.run(RULE_NAME, rule, {
await userEvent.${eventMethod}(getByLabelText('username'))
})
`,
}) as const
} as const)
),
...USER_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -897,7 +897,7 @@ ruleTester.run(RULE_NAME, rule, {
const promise = await userEvent.${eventMethod}(getByLabelText('username'))
})
`,
}) as const
} as const)
),
...USER_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -933,7 +933,7 @@ ruleTester.run(RULE_NAME, rule, {
await triggerEvent()
})
`,
}) as const
} as const)
),
...USER_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -958,7 +958,7 @@ ruleTester.run(RULE_NAME, rule, {
],
options: [{ eventModule: 'userEvent' }],
output: null,
}) as const
} as const)
),
...USER_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -984,7 +984,7 @@ ruleTester.run(RULE_NAME, rule, {
condition ? null : (null, true && await userEvent.${eventMethod}(getByLabelText('username')));
});
`,
}) as const
} as const)
),
...USER_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -1010,7 +1010,7 @@ ruleTester.run(RULE_NAME, rule, {
await (await userEvent.${eventMethod}(getByLabelText('username')) && userEvent.${eventMethod}(getByLabelText('username')));
});
`,
}) as const
} as const)
),
...USER_EVENT_ASYNC_FUNCTIONS.map(
(eventMethod) =>
@@ -1044,7 +1044,7 @@ ruleTester.run(RULE_NAME, rule, {
await (await userEvent.${eventMethod}(getByLabelText('username')), null);
});
`,
}) as const
} as const)
),
]),
{
Loading