summaryrefslogtreecommitdiff
path: root/eslint/rules/__tests__
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-04-29 00:37:56 +0200
committerMaze Winther <mazewinther@gmail.com>2026-05-02 03:27:23 +0200
commita9b9cf6bf50f08723896659634f2e6afc17bcd42 (patch)
tree9175b49ff21f4326872ac9e541d2765cc7994ebc /eslint/rules/__tests__
parentd6622dc6b3523a5658cd512b44f15f7a66fd540c (diff)
downloadopencut-a9b9cf6bf50f08723896659634f2e6afc17bcd42.tar.gz
chore: switch from biome to eslint + prettier; fix ton of lint issues
Diffstat (limited to 'eslint/rules/__tests__')
-rw-r--r--eslint/rules/__tests__/prefer-object-params.test.mjs112
1 files changed, 112 insertions, 0 deletions
diff --git a/eslint/rules/__tests__/prefer-object-params.test.mjs b/eslint/rules/__tests__/prefer-object-params.test.mjs
new file mode 100644
index 00000000..7f4bc947
--- /dev/null
+++ b/eslint/rules/__tests__/prefer-object-params.test.mjs
@@ -0,0 +1,112 @@
+import { afterAll, describe, it } from "bun:test";
+import { RuleTester } from "eslint";
+import tseslint from "typescript-eslint";
+import rule from "../prefer-object-params.mjs";
+
+RuleTester.describe = describe;
+RuleTester.it = it;
+RuleTester.itOnly = it.only;
+RuleTester.afterAll = afterAll;
+
+const ruleTester = new RuleTester({
+ languageOptions: {
+ parser: tseslint.parser,
+ parserOptions: {
+ ecmaVersion: "latest",
+ sourceType: "module",
+ ecmaFeatures: { jsx: true },
+ },
+ },
+});
+
+const error = { messageId: "preferObjectParams" };
+
+ruleTester.run("prefer-object-params", rule, {
+ valid: [
+ { code: "function f() {}" },
+ { code: "function f(a) {}" },
+ { code: "const f = (a) => a;" },
+ { code: "const f = function (a) { return a; };" },
+ { code: "function f({ a, b }) { return a + b; }" },
+ { code: "function f({ a, b }: { a: number; b: number }) { return a + b; }" },
+
+ // Direct callbacks: positional args are dictated by the caller.
+ { code: "[1, 2, 3].reduce((acc, cur) => acc + cur, 0);" },
+ { code: "arr.map((value, index) => value + index);" },
+ { code: "new Promise((resolve, reject) => {});" },
+ { code: "maybeFn?.((a, b) => a + b);" },
+
+ // Inline option-object callbacks: caller dictates the signature
+ // (e.g. zustand's `persist`, tanstack-query's `onSuccess`).
+ { code: "someAPI({ on: (a, b) => a + b });" },
+ {
+ code: "persist((set, get) => ({}), { migrate: (state, version) => state });",
+ },
+ { code: "new Foo({ handler: (a, b) => a + b });" },
+ { code: "maybeFn?.({ handler: (a, b) => a + b });" },
+
+ // Deeply-nested inline option callbacks still qualify: the entire
+ // option tree is supplied at the call site, so the caller dictates
+ // every signature inside it. Real example: better-auth's
+ // `betterAuth({ rateLimit: { customStorage: { set: (k, v) => ... }}})`.
+ { code: "someAPI({ outer: { handler: (a, b) => a + b } });" },
+
+ // Type predicates require a positional subject.
+ {
+ code: "function isFoo(x: unknown, ctx: Ctx): x is Foo { return true; }",
+ },
+ {
+ code: "const isFoo = (x: unknown, ctx: Ctx): x is Foo => true;",
+ },
+ ],
+ invalid: [
+ {
+ code: "function f(a, b) { return a + b; }",
+ errors: [error],
+ },
+ {
+ code: "const f = (a, b) => a + b;",
+ errors: [error],
+ },
+ {
+ code: "const f = function (a, b) { return a + b; };",
+ errors: [error],
+ },
+ {
+ code: "function f(a, b, c) { return a + b + c; }",
+ errors: [error],
+ },
+ {
+ code: "function formatTime(seconds: number, fps: number) { return seconds * fps; }",
+ errors: [error],
+ },
+ // A callback assigned to a name and passed indirectly is still a
+ // signature the author controls; the carve-out is intentionally narrow
+ // to direct callbacks.
+ {
+ code: "const cb = (a, b) => a + b; arr.reduce(cb, 0);",
+ errors: [error],
+ },
+ // Pre-declared option object: once the object is named, its callback
+ // signatures are the author's to evolve.
+ {
+ code: "const opts = { handler: (a, b) => a + b }; someAPI(opts);",
+ errors: [error],
+ },
+ // The error must point at the function's declaration line, so a
+ // `// eslint-disable-next-line` placed above the declaration (the
+ // conventional spot) suppresses the violation even when the
+ // signature spans multiple lines. Reporting at a parameter row
+ // instead leaves the directive inert and `reportUnusedDisableDirectives`
+ // then flags every suppressed call site.
+ {
+ code: [
+ "function f(",
+ "\ta: number,",
+ "\tb: number,",
+ ") { return a + b; }",
+ ].join("\n"),
+ errors: [{ messageId: "preferObjectParams", line: 1 }],
+ },
+ ],
+});