Skip to content

Commit

Permalink
New testing helpers using using, replace old console mocking functi…
Browse files Browse the repository at this point in the history
…ons. (#11177)
  • Loading branch information
phryneas authored Sep 14, 2023
1 parent 563c1c9 commit dc4baed
Show file tree
Hide file tree
Showing 42 changed files with 1,812 additions and 1,573 deletions.
18 changes: 14 additions & 4 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint", "import"],
"plugins": ["@typescript-eslint", "import", "local-rules"],
"env": {
"browser": true,
"node": true,
Expand Down Expand Up @@ -52,15 +52,25 @@
"ignorePackages": true,
"checkTypeImports": true
}
]
],
"local-rules/require-using-disposable": "error"
}
},
{
"files": ["**/__tests__/**/*.[jt]sx", "**/?(*.)+(test).[jt]sx"],
"files": [
"**/__tests__/**/*.[jt]s",
"**/__tests__/**/*.[jt]sx",
"**/?(*.)+(test).[jt]s",
"**/?(*.)+(test).[jt]sx"
],
"extends": ["plugin:testing-library/react"],
"parserOptions": {
"project": "./tsconfig.tests.json"
},
"rules": {
"testing-library/prefer-user-event": "error",
"testing-library/no-wait-for-multiple-assertions": "off"
"testing-library/no-wait-for-multiple-assertions": "off",
"local-rules/require-using-disposable": "error"
}
}
],
Expand Down
8 changes: 3 additions & 5 deletions config/version.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,9 @@ switch (process.argv[2]) {
}

case "verify": {
const { ApolloClient, InMemoryCache } = require(path.join(
distRoot,
"core",
"core.cjs"
));
const { ApolloClient, InMemoryCache } = require(
path.join(distRoot, "core", "core.cjs")
);

// Though this may seem like overkill, verifying that ApolloClient is
// constructible in Node.js is actually pretty useful, too!
Expand Down
Empty file.
Empty file.
7 changes: 7 additions & 0 deletions eslint-local-rules/fixtures/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"strict": true,
"target": "esnext"
},
"include": ["file.ts", "react.tsx"]
}
14 changes: 14 additions & 0 deletions eslint-local-rules/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require("ts-node").register({
transpileOnly: true,
compilerOptions: {
// we need this to be nodenext in the tsconfig, because
// @typescript-eslint/utils only seems to export ESM
// in TypeScript's eyes, but it totally works
module: "commonjs",
moduleResolution: "node",
},
});

module.exports = {
"require-using-disposable": require("./require-using-disposable").rule,
};
5 changes: 5 additions & 0 deletions eslint-local-rules/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"scripts": {
"test": "node -r ts-node/register/transpile-only --no-warnings --test --watch *.test.ts"
}
}
31 changes: 31 additions & 0 deletions eslint-local-rules/require-using-disposable.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { rule } from "./require-using-disposable";
import { ruleTester } from "./testSetup";

ruleTester.run("require-using-disposable", rule, {
valid: [
`
function foo(): Disposable {}
using bar = foo()
`,
`
function foo(): AsyncDisposable {}
await using bar = foo()
`,
],
invalid: [
{
code: `
function foo(): Disposable {}
const bar = foo()
`,
errors: [{ messageId: "missingUsing" }],
},
{
code: `
function foo(): AsyncDisposable {}
const bar = foo()
`,
errors: [{ messageId: "missingAwaitUsing" }],
},
],
});
63 changes: 63 additions & 0 deletions eslint-local-rules/require-using-disposable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { ESLintUtils } from "@typescript-eslint/utils";
import ts from "typescript";
import * as utils from "ts-api-utils";

export const rule = ESLintUtils.RuleCreator.withoutDocs({
create(context) {
return {
VariableDeclaration(node) {
for (const declarator of node.declarations) {
if (!declarator.init) continue;
const services = ESLintUtils.getParserServices(context);
const type = services.getTypeAtLocation(declarator.init);
for (const typePart of parts(type)) {
if (!utils.isObjectType(typePart) || !typePart.symbol) {
continue;
}
if (
// bad check, but will do for now
// in the future, we should check for a `[Symbol.disposable]` property
// but I have no idea how to do that right now
typePart.symbol.escapedName === "Disposable" &&
node.kind != "using"
) {
context.report({
messageId: "missingUsing",
node: declarator,
});
}
if (
// similarly bad check
typePart.symbol.escapedName === "AsyncDisposable" &&
node.kind != "await using"
) {
context.report({
messageId: "missingAwaitUsing",
node: declarator,
});
}
}
}
},
};
},
meta: {
messages: {
missingUsing:
"Disposables should be allocated with `using <disposable>`.",
missingAwaitUsing:
"AsyncDisposables should be allocated with `await using <disposable>`.",
},
type: "suggestion",
schema: [],
},
defaultOptions: [],
});

function parts(type: ts.Type): ts.Type[] {
return type.isUnion()
? utils.unionTypeParts(type).flatMap(parts)
: type.isIntersection()
? utils.intersectionTypeParts(type).flatMap(parts)
: [type];
}
15 changes: 15 additions & 0 deletions eslint-local-rules/testSetup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { RuleTester } from "@typescript-eslint/rule-tester";
import nodeTest from "node:test";

RuleTester.it = nodeTest.it;
RuleTester.itOnly = nodeTest.only;
RuleTester.describe = nodeTest.describe;
RuleTester.afterAll = nodeTest.after;

export const ruleTester = new RuleTester({
parser: "@typescript-eslint/parser",
parserOptions: {
project: "./tsconfig.json",
tsconfigRootDir: __dirname + "/fixtures",
},
});
7 changes: 7 additions & 0 deletions eslint-local-rules/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "@tsconfig/node20/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"noEmit": true
}
}
Loading

0 comments on commit dc4baed

Please sign in to comment.