feat: add testing for the global configs

We want to make sure we aren't missing anything important.
This commit is contained in:
Naomi Carrigan 2024-09-25 22:31:08 -07:00
parent 2974c76c48
commit 70fcc1a49a
Signed by: naomi
SSH Key Fingerprint: SHA256:rca1iUI2OhAM6n4FIUaFcZcicmri0jgocqKiTTAfrt8
4 changed files with 383 additions and 18 deletions

View File

@ -8,7 +8,10 @@ export default [
"import/no-default-export": "off", "import/no-default-export": "off",
"import/namespace": "off", "import/namespace": "off",
"import/no-deprecated": "off", "import/no-deprecated": "off",
"@typescript-eslint/consistent-type-assertions": "off" "@typescript-eslint/consistent-type-assertions": "off",
"max-lines-per-function": "off",
"complexity": "off",
"max-nested-callbacks": "off"
}, },
}, },
]; ];

View File

@ -35,18 +35,15 @@ import { vitestRules } from "./rules/vitest.js";
import type { ESLint, Linter } from "eslint"; import type { ESLint, Linter } from "eslint";
const config: Array<Linter.Config> = [ const config: Array<Linter.Config> = [
// #region Typescript Config
{ {
files: [ "src/**/*.ts" ], files: [ "src/**/*.ts" ],
languageOptions: { languageOptions: {
globals: { globals: {
...globals.node, ...globals.node,
...globals.browser,
}, },
parser: parser, parser: parser,
parserOptions: { parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 11, ecmaVersion: 11,
project: true, project: true,
sourceType: "module", sourceType: "module",
@ -60,15 +57,10 @@ const config: Array<Linter.Config> = [
"deprecation": fixupPluginRules(deprecation), "deprecation": fixupPluginRules(deprecation),
"import": fixupPluginRules(importPlugin as ESLint.Plugin), "import": fixupPluginRules(importPlugin as ESLint.Plugin),
"jsdoc": jsdoc, "jsdoc": jsdoc,
// @ts-expect-error I'm not sure what's going on here, to be honest.
"playwright": fixupPluginRules(playwright),
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- No typedef means it's unsafe...
"react": react,
"sort-keys-fix": sortKeysFix as ESLint.Plugin, "sort-keys-fix": sortKeysFix as ESLint.Plugin,
// @ts-expect-error They haven't typedef this yet because it technically doesn't support eslint9 // @ts-expect-error They haven't typedef this yet because it technically doesn't support eslint9
"stylistic": fixupPluginRules(stylistic), "stylistic": fixupPluginRules(stylistic),
"unicorn": unicorn, "unicorn": unicorn,
"vitest": vitest,
}, },
rules: { rules: {
...eslintRules, ...eslintRules,
@ -81,11 +73,11 @@ const config: Array<Linter.Config> = [
...stylisticRules, ...stylisticRules,
...unicornRules, ...unicornRules,
...sortKeysFixRules, ...sortKeysFixRules,
...vitestRules,
...playwrightRules,
...reactRules,
}, },
}, },
// #endregion
// #region Vitest Config
{ {
files: [ "test/**/*.spec.ts" ], files: [ "test/**/*.spec.ts" ],
languageOptions: { languageOptions: {
@ -119,11 +111,94 @@ const config: Array<Linter.Config> = [
...stylisticRules, ...stylisticRules,
...unicornRules, ...unicornRules,
...sortKeysFixRules, ...sortKeysFixRules,
// Overrides },
"complexity": "off", },
"import/no-extraneous-dependencies": "error", // #endregion
"max-lines-per-function": "off",
"max-nested-callbacks": "off", // #region Playwright Config
{
files: [ "e2e/**/*.spec.ts" ],
languageOptions: {
globals: {
...globals.node,
},
parser: parser,
parserOptions: {
ecmaVersion: 11,
sourceType: "module",
},
},
plugins: {
// @ts-expect-error It's a config. It's just not the narrow config. SMH.
"@typescript-eslint": tslint,
"import": fixupPluginRules(importPlugin as ESLint.Plugin),
"jsdoc": jsdoc,
// @ts-expect-error I'm not actually sure what's going on here...
"playwright": playwright,
"sort-keys-fix": sortKeysFix as ESLint.Plugin,
// @ts-expect-error They haven't typedef this yet because it technically doesn't support eslint9
"stylistic": fixupPluginRules(stylistic),
"unicorn": unicorn,
},
rules: {
...eslintRules,
...disabledEslintRules,
...typescriptEslintRules,
...playwrightRules,
...importRules,
...jsdocRules,
...stylisticRules,
...unicornRules,
...sortKeysFixRules,
},
},
// #endregion
// #region React Config
{
files: [ "src/**/*.tsx" ],
languageOptions: {
globals: {
...globals.browser,
},
parser: parser,
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 11,
project: true,
sourceType: "module",
tsconfigRootDir: process.cwd(),
},
},
plugins: {
// @ts-expect-error It's a config. It's just not the narrow config. SMH.
"@typescript-eslint": tslint,
// @ts-expect-error They haven't typedef this yet because it technically doesn't support eslint9
"deprecation": fixupPluginRules(deprecation),
"import": fixupPluginRules(importPlugin as ESLint.Plugin),
"jsdoc": jsdoc,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- No typedef means it's unsafe...
"react": react,
"sort-keys-fix": sortKeysFix as ESLint.Plugin,
// @ts-expect-error They haven't typedef this yet because it technically doesn't support eslint9
"stylistic": fixupPluginRules(stylistic),
"unicorn": unicorn,
},
rules: {
...eslintRules,
...disabledEslintRules,
...typescriptEslintRules,
...typescriptEslintRulesWithTypes,
...importRules,
...jsdocRules,
...deprecationRules,
...stylisticRules,
...unicornRules,
...sortKeysFixRules,
...reactRules,
}, },
}, },
]; ];

268
test/config.spec.ts Normal file
View File

@ -0,0 +1,268 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { describe, expect, it } from "vitest";
import config from "../src/index.ts";
import { deprecationRules } from "../src/rules/deprecation.ts";
import { eslintRules } from "../src/rules/eslint.ts";
import { importRules } from "../src/rules/import.ts";
import { jsdocRules } from "../src/rules/jsdoc.ts";
import { playwrightRules } from "../src/rules/playwright.ts";
import { reactRules } from "../src/rules/react.ts";
import { sortKeysFixRules } from "../src/rules/sortKeysFix.ts";
import { stylisticRules } from "../src/rules/stylistic.ts";
import {
typescriptEslintRules,
typescriptEslintRulesWithTypes,
} from "../src/rules/typescriptEslint.ts";
import { unicornRules } from "../src/rules/unicorn.ts";
import { vitestRules } from "../src/rules/vitest.ts";
import type { Linter } from "eslint";
// #region Custom matcher
expect.extend({
toContainRules(received, expected) {
const missingRules: Array<string> = [];
const mismatchedRules: Array<string> = [];
for (const [ key, value ] of Object.entries(expected)) {
if (!(key in received)) {
missingRules.push(key);
} else if (!this.equals(received[key], value)) {
mismatchedRules.push(key);
}
}
const pass = missingRules.length === 0 && mismatchedRules.length === 0;
let message = "";
// eslint-disable-next-line no-negated-condition
if (!pass) {
message = `${message}Expected rules to contain all specified rules.\n`;
if (missingRules.length > 0) {
message = `${message}Missing rules: ${missingRules.join(", ")}\n`;
}
if (mismatchedRules.length > 0) {
message = `${message}Mismatched rules: ${mismatchedRules.join(", ")}\n`;
}
} else {
message = "Expected rules not to contain all specified rules";
}
return {
message: (): string => {
return message;
},
pass: pass,
};
},
});
// #endregion
// #region Helper function
const baseProperties = (
ruleset: Linter.Config<Linter.RulesRecord>,
isNoTypes: boolean,
): void => {
expect(ruleset?.plugins, "missing @typescript-eslint plugin").toHaveProperty(
"@typescript-eslint",
);
// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- This is a test.
isNoTypes
? expect(
ruleset?.plugins,
"should not have deprecation plugin",
).not.toHaveProperty("deprecation")
: expect(ruleset?.plugins, "missing deprecation plugin").toHaveProperty(
"deprecation",
);
expect(ruleset?.plugins, "missing import plugin").toHaveProperty("import");
expect(ruleset?.plugins, "missing jsdoc plugin").toHaveProperty("jsdoc");
expect(ruleset?.plugins, "missing sort-keys-fix plugin").toHaveProperty(
"sort-keys-fix",
);
expect(ruleset?.plugins, "missing stylistic plugin").toHaveProperty(
"stylistic",
);
expect(ruleset?.plugins, "missing unicorn plugin").toHaveProperty("unicorn");
expect(ruleset?.rules, "missing eslint rules").toContainRules(eslintRules);
expect(ruleset?.rules, "missing typescript-eslint rules").toContainRules(
typescriptEslintRules,
);
// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- This is a test.
isNoTypes
? expect(
ruleset?.rules,
"should not have deprecation rules",
).not.toContainRules(deprecationRules)
: expect(ruleset?.rules, "missing deprecation rules").toContainRules(
deprecationRules,
);
expect(ruleset?.rules, "missing import rules").toContainRules(importRules);
expect(ruleset?.rules, "missing jsdoc rules").toContainRules(jsdocRules);
expect(ruleset?.rules, "missing sort-keys-fix rules").toContainRules(
sortKeysFixRules,
);
expect(ruleset?.rules, "missing stylistic rules").toContainRules(
stylisticRules,
);
expect(ruleset?.rules, "missing unicorn rules").toContainRules(unicornRules);
// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- This is a test.
isNoTypes
? expect(
ruleset?.rules,
"should not have typescript-eslint rules with types",
).not.toContainRules(typescriptEslintRulesWithTypes)
: expect(
ruleset?.rules,
"missing typescript-eslint rules with types",
).toContainRules(typescriptEslintRulesWithTypes);
};
// #endregion
describe("global config", () => {
// #region Typescript
it("should apply the expected plugins for typescript", () => {
expect.assertions(26);
const ruleset = config.find((rule) => {
return rule?.files?.includes("src/**/*.ts");
});
expect(ruleset, "ruleset is not defined").toBeDefined();
expect(ruleset?.plugins, "ruleset does not have plugins").toBeDefined();
expect(
ruleset?.plugins,
"should not have vitest plugin",
).not.toHaveProperty("vitest");
expect(
ruleset?.plugins,
"should not have playwright plugin",
).not.toHaveProperty("playwright");
expect(ruleset?.plugins, "should not have react plugin").not.toHaveProperty(
"react",
);
expect(ruleset?.rules, "ruleset does not have rules").toBeDefined();
expect(ruleset?.rules, "should not have vitest rules").not.toContainRules(
vitestRules,
);
expect(
ruleset?.rules,
"should not have playwright rules",
).not.toContainRules(playwrightRules);
expect(ruleset?.rules, "should not have react rules").not.toContainRules(
reactRules,
);
baseProperties(ruleset as Linter.Config<Linter.RulesRecord>, false);
expect(
Object.keys(ruleset?.plugins ?? {}),
"should not have extraneous plugins",
).toHaveLength(7);
});
// #endregion
// #region Vitest
it("should apply the expected plugins for vitest files", () => {
expect.assertions(26);
const ruleset = config.find((rule) => {
return rule?.files?.includes("test/**/*.spec.ts");
});
expect(ruleset, "ruleset is not defined").toBeDefined();
expect(ruleset?.plugins, "ruleset does not have plugins").toBeDefined();
expect(ruleset?.plugins, "missing vitest plugin").toHaveProperty("vitest");
expect(
ruleset?.plugins,
"should not have playwright plugin",
).not.toHaveProperty("playwright");
expect(ruleset?.plugins, "should not have react plugin").not.toHaveProperty(
"react",
);
expect(ruleset?.rules, "ruleset does not have rules").toBeDefined();
expect(ruleset?.rules, "missing vitest rules").toContainRules(vitestRules);
expect(
ruleset?.rules,
"should not have playwright rules",
).not.toContainRules(playwrightRules);
expect(ruleset?.rules, "should not have react rules").not.toContainRules(
reactRules,
);
baseProperties(ruleset as Linter.Config<Linter.RulesRecord>, true);
expect(
Object.keys(ruleset?.plugins ?? {}),
"should not have extraneous plugins",
).toHaveLength(7);
});
// #endregion
// #region Playwright
it("should apply the expected plugins for playwright", () => {
expect.assertions(26);
const ruleset = config.find((rule) => {
return rule?.files?.includes("e2e/**/*.spec.ts");
});
expect(ruleset, "ruleset is not defined").toBeDefined();
expect(ruleset?.plugins, "ruleset does not have plugins").toBeDefined();
expect(
ruleset?.plugins,
"should not have vitest plugin",
).not.toHaveProperty("vitest");
expect(ruleset?.plugins, "missing playwright plugin").toHaveProperty(
"playwright",
);
expect(ruleset?.plugins, "should not have react plugin").not.toHaveProperty(
"react",
);
expect(ruleset?.rules, "ruleset does not have rules").toBeDefined();
expect(ruleset?.rules, "should not have vitest rules").not.toContainRules(
vitestRules,
);
expect(ruleset?.rules, "missing playwright rules").toContainRules(
playwrightRules,
);
expect(ruleset?.rules, "should not have react rules").not.toContainRules(
reactRules,
);
baseProperties(ruleset as Linter.Config<Linter.RulesRecord>, true);
expect(
Object.keys(ruleset?.plugins ?? {}),
"should not have extraneous plugins",
).toHaveLength(7);
});
// #endregion
// #region React
it("should apply the expected plugins for react", () => {
expect.assertions(26);
const ruleset = config.find((rule) => {
return rule?.files?.includes("src/**/*.tsx");
});
expect(ruleset, "ruleset is not defined").toBeDefined();
expect(ruleset?.plugins, "ruleset does not have plugins").toBeDefined();
expect(
ruleset?.plugins,
"should not have vitest plugin",
).not.toHaveProperty("vitest");
expect(
ruleset?.plugins,
"should not have playwright plugin",
).not.toHaveProperty("playwright");
expect(ruleset?.plugins, "missing react plugin").toHaveProperty("react");
expect(ruleset?.rules, "ruleset does not have rules").toBeDefined();
expect(ruleset?.rules, "should not have vitest rules").not.toContainRules(
vitestRules,
);
expect(
ruleset?.rules,
"should not have playwright rules",
).not.toContainRules(playwrightRules);
expect(ruleset?.rules, "missing react rules").toContainRules(reactRules);
baseProperties(ruleset as Linter.Config<Linter.RulesRecord>, false);
expect(
Object.keys(ruleset?.plugins ?? {}),
"should not have extraneous plugins",
).toHaveLength(8);
});
// #endregion
});

19
test/vitest.d.ts vendored Normal file
View File

@ -0,0 +1,19 @@
/// <reference types="vitest" />
import type { Assertion, AsymmetricMatchersContaining } from 'vitest';
interface CustomMatchers<R = unknown> {
toContainRules(expected: Record<string, unknown>): R;
}
declare module 'vitest' {
interface Assertion<T = any> extends CustomMatchers<T> {}
interface AsymmetricMatchersContaining extends CustomMatchers {}
}
declare module 'vitest' {
// @ts-expect-error We need to extend the TestAPI interface
export interface TestAPI {
extend: <T>(matchers: Record<string, (this: T, ...args: any[]) => any>) => void;
}
}