Skip to content

Latest commit

 

History

History
277 lines (220 loc) · 7.64 KB

Rule_Tester.mdx

File metadata and controls

277 lines (220 loc) · 7.64 KB
id sidebar_label
rule-tester
rule-tester

import CodeBlock from '@theme/CodeBlock';

@typescript-eslint/rule-tester

A utility for testing ESLint rules

This is a fork of ESLint's built-in RuleTester to provide some better types and additional features for testing TypeScript rules.

Usage

For non-type-aware rules you can test them as follows:

import { RuleTester } from '@typescript-eslint/rule-tester';
import rule from '../src/rules/my-rule.ts';

const ruleTester = new RuleTester();

ruleTester.run('my-rule', rule, {
  valid: [
    // valid tests can be a raw string,
    'const x = 1;',
    // or they can be an object
    {
      code: 'const y = 2;',
      options: [{ ruleOption: true }],
    },

    // you can enable JSX parsing by passing parserOptions.ecmaFeatures.jsx = true
    {
      code: 'const z = <div />;',
      parserOptions: {
        ecmaFeatures: {
          jsx: true,
        },
      },
    },
  ],
  invalid: [
    // invalid tests must always be an object
    {
      code: 'const a = 1;',
      // invalid tests must always specify the expected errors
      errors: [
        {
          messageId: 'ruleMessage',
          // If applicable - it's recommended that you also assert the data in
          // addition to the messageId so that you can ensure the correct message
          // is generated
          data: {
            placeholder1: 'a',
          },
        },
      ],
    },

    // fixers can be tested using the output parameter
    {
      code: 'const b = 1;',
      output: 'const c = 1;',
      errors: [
        /* ... */
      ],
    },
    // passing `output = null` will enforce the code is NOT changed
    {
      code: 'const c = 1;',
      output: null,
      errors: [
        /* ... */
      ],
    },

    // suggestions can be tested via errors
    {
      code: 'const d = 1;',
      output: null,
      errors: [
        {
          messageId: 'suggestionError',
          suggestions: [
            {
              messageId: 'suggestionOne',
              output: 'const e = 1;',
            },
          ],
        },
      ],
    },
    // passing `suggestions = null` will enforce there are NO suggestions
    {
      code: 'const d = 1;',
      output: null,
      errors: [
        {
          messageId: 'noSuggestionError',
          suggestions: null,
        },
      ],
    },
  ],
});

Type-Aware Testing

Type-aware rules can be tested in almost exactly the same way, except you need to create some files on disk. We require files on disk due to a limitation with TypeScript in that it requires physical files on disk to initialize the project. We suggest creating a fixture folder nearby that contains three files:

  1. file.ts - this should be an empty file.
  2. react.tsx - this should be an empty file.
  3. tsconfig.json - this should be the config to use for your test, for example:
    {
      "compilerOptions": {
        "strict": true
      },
      "include": ["file.ts", "react.tsx"]
    }

:::caution It's important to note that both file.ts and react.tsx must both be empty files! The rule tester will automatically use the string content from your tests - the empty files are just there for initialization. :::

You can then test your rule by providing the type-aware config:

const ruleTester = new RuleTester({
  // Added lines start
  parserOptions: {
    tsconfigRootDir: './path/to/your/folder/fixture',
    project: './tsconfig.json',
  },
  // Added lines end
});

With that config the parser will automatically run in type-aware mode and you can write tests just like before.

Test Dependency Constraints

Sometimes it's desirable to test your rule against multiple versions of a dependency to ensure backwards and forwards compatibility. With backwards-compatibility testing there comes a complication in that some tests may not be compatible with an older version of a dependency. For example - if you're testing against an older version of TypeScript, certain features might cause a parser error!

import DependencyConstraint from '!!raw-loader!../../packages/rule-tester/src/types/DependencyConstraint.ts';

{DependencyConstraint}

The RuleTester allows you to apply dependency constraints at either an individual test or constructor level.

const ruleTester = new RuleTester({
  // Added lines start
  dependencyConstraints: {
    // none of the tests will run unless `my-dependency` matches the semver range `>=1.2.3`
    'my-dependency': '1.2.3',
    // you can also provide granular semver ranges
    'my-granular-dep': {
      // none of the tests will run unless `my-granular-dep` matches the semver range `~3.2.1`
      range: '~3.2.1',
    },
  },
  // Added lines end
});

ruleTester.run('my-rule', rule, {
  valid: [
    {
      code: 'const y = 2;',
      // Added lines start
      dependencyConstraints: {
        // this test won't run unless BOTH dependencies match the given ranges
        first: '1.2.3',
        second: '3.2.1',
      },
      // Added lines end
    },
  ],
  invalid: [
    /* ... */
  ],
});

All dependencies provided in the dependencyConstraints object must match their given ranges in order for a test to not be skipped.

With Specific Frameworks

ESLint's RuleTester relies on some global hooks for tests. If they aren't available globally, your tests will fail with an error like:

Error: Missing definition for `afterAll` - you must set one using `RuleTester.afterAll` or there must be one defined globally as `afterAll`.

:::tip Be sure to set RuleTester's static properties before calling new RuleTester(...) for the first time. :::

Mocha

Consider setting up RuleTester's static properties in a mochaGlobalSetup fixture:

import * as mocha from 'mocha';
import { RuleTester } from '@typescript-eslint/rule-tester';

RuleTester.afterAll = mocha.after;

Vitest

Consider setting up RuleTester's static properties in a setupFiles script:

import * as vitest from 'vitest';
import { RuleTester } from '@typescript-eslint/rule-tester';

RuleTester.afterAll = vitest.afterAll;

// If you are not using vitest with globals: true (https://vitest.dev/config/#globals):
RuleTester.it = vitest.it;
RuleTester.itOnly = vitest.it.only;
RuleTester.describe = vitest.describe;

Node built-in test runner

Consider setting up RuleTester's static properties in a preloaded module using the --import or --require flag:

// setup.js
import * as test from 'node:test';
import { RuleTester } from '@typescript-eslint/rule-tester';

RuleTester.afterAll = test.afterAll;
RuleTester.describe = test.describe;
RuleTester.it = test.it;
RuleTester.itOnly = test.it.only;

Tests can then be run from the command line like so:

node --import setup.js --test

Options

RuleTester constructor options

import RuleTesterConfig from '!!raw-loader!../../packages/rule-tester/src/types/RuleTesterConfig.ts';

{RuleTesterConfig}

Valid test case options

import ValidTestCase from '!!raw-loader!../../packages/rule-tester/src/types/ValidTestCase.ts';

{ValidTestCase}

Invalid test case options

import InvalidTestCase from '!!raw-loader!../../packages/rule-tester/src/types/InvalidTestCase.ts';

{InvalidTestCase}