eslint-plugin-task-needs-wait-for
Version:
Helps to prevent writing ember-concurrency tasks that would create flaky tests.
89 lines (85 loc) • 2.89 kB
text/typescript
import {
AST_NODE_TYPES,
ESLintUtils,
TSESTree,
} from "@typescript-eslint/utils";
const TASKS = [
"task",
"restartableTask",
"dropTask",
"keepLatestTask",
"enqueueTask",
];
const WAIT_FOR = "waitFor";
const WAIT_FOR_D = `@${WAIT_FOR}`;
const IMPORT_D = `import { waitFor } from '@ember/test-waiters';`;
const ERR_TASK_WITHOUT_WAIT_FOR =
"@task usage without @waitFor can be a cause of promises not being awaited for in tests. That is why every usage of @task should be accompanied with @waitFor - https://github.com/emberjs/ember-test-waiters#waitfor-function";
const createRule = ESLintUtils.RuleCreator((name) => `decorator-presence`);
// Type: RuleModule<"uppercase", ...>
export const decoratorPresence = createRule({
create(context) {
return {
["MethodDefinition:has([decorators])"](node: TSESTree.Node) {
if (node.type === AST_NODE_TYPES.MethodDefinition) {
// TODO: No idea why, but this does not work: https://typescript-eslint.io/developers/custom-rules#explicit-node-types
// @ts-ignore
const decorators = node.decorators.map((d) => d.expression.name);
const taskPosition = decorators.findIndex((elm) =>
TASKS.includes(elm),
);
const waitForPosition = decorators.findIndex(
(elm) => elm === WAIT_FOR,
);
if (taskPosition > -1) {
if (waitForPosition === -1) {
context.report({
node,
messageId: "task-without-wait-for",
*fix(fixer) {
yield fixer.insertTextAfter(
node.decorators[taskPosition],
" ",
);
yield fixer.insertTextAfter(
node.decorators[taskPosition],
WAIT_FOR_D,
);
const top = context?.getAncestors()?.[0];
// @ts-ignore
const hasImport = top?.body.some((elm) => {
return (
elm.type === "ImportDeclaration" &&
elm.specifiers &&
elm.specifiers.some(
// @ts-ignore
(spec) => spec?.local?.name === "waitFor",
)
);
});
if (!hasImport) {
yield fixer.insertTextBefore(top, IMPORT_D);
}
},
});
return;
}
}
}
},
};
},
name: "decorator-presence",
meta: {
docs: {
description: "@task should be accompanied by @waitFor",
},
messages: {
"task-without-wait-for": ERR_TASK_WITHOUT_WAIT_FOR,
},
type: "suggestion",
schema: [],
fixable: "code",
},
defaultOptions: [],
});