For more complex checks you may want to make sure that your validation and fix logic is what you would expect it to be.

We recommend using mock-fs along with our defineTestCheck utility to mock the file system. This combination will test your checks end-to-end to ensure that your checks never create unexpected changes.

Example check

Here’s an example of a check that reads a file from disk, has dynamically returned messages, and auto-fix functionality.

ensure-license.ts
import { defineCheck, json, diff } from 'commonality';

const ensureLicense = (license: string = 'MIT') => {
  return {
    name: 'my-team/ensure-license',
    level: 'error',
    validate: async (ctx) => {
      const packageJson = await json(ctx.package.path, 'package.json').get();

      // Since we return multiple messages we should test for each scenario
      if (!packageJson || !packageJson.license) {
        return {
          message: 'Package.json must have a license',
          path: 'package.json',
          suggestion: diff(
            { name: packageJson.name },
            { name: packageJson.name, license },
          ),
        };
      }


      if(packageJson.license !== license) {
        return {
          message: `Package.json license must be ${license}`,
          path: 'package.json',
          suggestion: diff(
            { name: packageJson.name, license: packageJson.license },
            { name: packageJson.name, license },
          ),
        };
      }

      return true
    },
    fix: async (ctx) => {
      // We'll want to test that this file gets updated correctly on disk
      await json(ctx.package.path, 'package.json').merge({
        license,
      });
    },
  };
};

Example test

We can test that the check accurately reads the file, returns the correct validation result, and that the auto-fix function correctly updates the file on disk.

ensure-license.test.ts
import { ensureLicense } from './ensure-license';
import { defineTestCheck, json } from 'commonality';
import mockFs from 'mock-fs';

describe('ensureLicense', () => {
  describe('validate', () => {
    test('returns a message if package.json does not have a license property', () => {
      mockFs({
        'package.json': JSON.stringify({
          name: 'my-pkg',
        }),
      });

      const check = defineTestCheck(ensureLicense('MIT'));

      const result = await check.validate();

      expect(result.message).toEqual(`Package.json must have a license`)
      expect(result.path).toEqual(`package.json`)
      expect(result.suggestion).toMatchSnapshot();
    });

    test('returns a message if package.json does not contain a matching license', () => {
      mockFs({
        'package.json': JSON.stringify({
          name: 'my-pkg',
          license: 'ISC',
        }),
      });

      const check = defineTestCheck(ensureLicense('MIT'));

      const result = await check.validate();

      expect(result.message).toEqual(`Package.json license must be MIT`)
      expect(result.path).toEqual(`package.json`)
      expect(result.suggestion).toMatchSnapshot();
    });

    test('returns true if package.json does contain a matching license', () => {
      mockFs({
        'package.json': JSON.stringify({
          name: 'my-pkg',
          license: 'MIT',
        }),
      });

      const check = defineTestCheck(ensureLicense('MIT'));

      const result = await check.validate();

      expect(result).toEqual(true);
    });
  });

  describe('fix', () => {
    test('updates the package.json file with the correct license', () => {
      mockFs({
        'package.json': JSON.stringify({
          name: 'my-pkg',
        }),
      });

      const check = defineTestCheck(ensureLicense('MIT'));

      await check.fix();

      // You can re-use our helpers in tests to get the contents of files
      const packageJson = await json('package.json').get();

      expect(packageJson).toEqual({
        name: 'my-pkg',
        license: 'MIT',
      });
    });
  });
});