iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🧪

Snapshot Testing Your Custom ESLint Config

に公開

What is this

This is a summary of how to test whether the ESLint Config you created works as expected.

ESLint provides a vast number of rules, and when plugins are added, that number becomes enormous. Since writing all rule settings yourself requires significant effort, it is common to adopt configs (presets) provided by various plugins and add rule settings as needed. Furthermore, there are cases where different settings are applied only to specific file formats or within certain directories. In such situations, the overall picture of the configuration becomes extremely complex, making it very difficult to verify visually if it is working as expected.

In this article, I will introduce a snapshot testing method that helps solve these issues.

What is Snapshot Testing

Snapshot testing is a testing method that records the output of a certain function or component during test execution and verifies whether any differences have occurred by comparing it with that record in subsequent test runs. Visual Regression Testing (VRT) is one type of this, and while it is often used for testing UI components, it can also be applied to testing configuration files like in this case.

Snapshot Testing in ESLint Config

The ESLint CLI provides a --print-config option. When this is added, the configuration details applied during execution are output in JSON format. Not only are the setting values for various rules listed flatly, but it also includes setting values such as settings and languageOptions.

Execution example
npx eslint --print-config ./target.js

# The ESLint configuration applied to target.js is output in JSON format
{
  "settings": {...},
  "languageOptions": {...},
  "plugins": [...],
  "rules": {...},
}

Since linting itself is not performed at this time, the content of the file passed to the --print-config option can be empty.

Also, if you save this JSON data as a text file, it becomes a snapshot of the current configuration, allowing you to verify whether the configuration of the created ESLint Config is as expected.

Snapshot save example
npx eslint --print-config ./target.js > ./snapshot.json

Furthermore, in subsequent test runs, you can detect configuration changes by comparing the differences with the saved snapshot.

Snapshot Test Design

Jest or Vitest provides a function called toMatchSnapshot()[1], which makes it easy to implement snapshot tests.

On the other hand, for ESLint, executing it via the CLI is inconvenient when running on a test runner. ESLint provides a Node.js API intended for use in plugins and test runners, and you can implement snapshot tests by utilizing this.

https://eslint.org/docs/latest/integrate/nodejs-api

Implementation

1. File/Directory Structure

Assume the following file/directory structure.

.
├── target.js        # File to apply the Linter to
├── eslint.config.js # The ESLint Config file to be tested
└── snapshot.test.js # Test code

2. Create an instance of the ESLint class

First, implement the logic to create an instance of the ESLint class using the ESLint Node.js API.

import { loadESLint } from 'eslint';

const DefaultESLint = await loadESLint({
  useFlatConfig: true,
});

const eslint = new DefaultESLint({
  cwd: import.meta.dirname,
});

Use the loadESLint() method to retrieve the ESLint class. By setting the useFlatConfig option to true, you get a class compatible with Flat Config, and by setting it to false, you get a class compatible with the eslintrc format. The default value is true.

Then, when creating an instance from the retrieved class, specify the cwd option to define the directory where eslint.config.js should be loaded.

3. Retrieve ESLint configuration details

Use the eslint.calculateConfigForFile(filePath) method to retrieve the ESLint configuration details applied to the file passed as an argument.

const config = eslint.calculateConfigForFile('./target.js');

Similar to the CLI's --print-config, you specify a single file path for the calculateConfigForFile() method argument. This retrieves the ESLint configuration applied to ./target.js.

4. Implement the snapshot test

Use the toMatchSnapshot() method of Jest or Vitest mentioned earlier to save the retrieved configuration as a snapshot test.

snapshot.test.js
import { loadESLint } from 'eslint';

test('should match ESLint Configuration snapshot', async () => {
  const DefaultESLint = await loadESLint({
    useFlatConfig: true,
  });
  const eslint = new DefaultESLint({
    cwd: import.meta.dirname,
  });
  const config = eslint.calculateConfigForFile('./target.js');

  expect(config).toMatchSnapshot();
});

Since ESLint's Flat Config is based on ES Modules, the test code should also be implemented as ES Modules.

5. Run the tests

Finally, run the tests.

Jest
NODE_OPTIONS="$NODE_OPTIONS --experimental-vm-modules" npx jest
Vitest
# Vitest supports ES Modules, so no environment variables are needed.
npx vitest

Upon the first execution, a snapshot file will be generated as follows:

 .
+├── __snapshots__/
+   └── snapshot.test.js.snap
 ├── target.js
 ├── eslint.config.js
 └── snapshot.test.js
snapshot.test.js.snap
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should match ESLint Configuration snapshot 1`] = `
{
  "languageOptions": {
    "ecmaVersion": "latest",
    "parser": "espree@9.6.1",
    "parserOptions": {
      "ecmaVersion": 2023,
      "sourceType": "module",
    },
    "sourceType": "module",
  },
  "plugins": [
    "@",
    "import",
    "promise",
  ],
  "processor": undefined,
  "rules": {
    "accessor-pairs": [0],
    ...
  },
  "settings": {
    ...
  },
}
`;

In subsequent test runs, the configuration will be compared with the saved snapshot, and the results will be output as logs. This completes the snapshot implementation.

Conclusion

ESLint Config can often become very complex, and it is easy to fall into a situation where Lint errors are not detected as expected even though you thought you configured it correctly, causing the codebase to become messy without noticing. By introducing snapshot testing, you can continuously verify whether the ESLint Config is correctly written, helping to maintain code quality.

References

脚注
  1. Vitest is a test runner with high compatibility with Jest, so most Jest features are also available in Vitest. ↩︎

Discussion