UNPKG

@jhae/stylelint-config-verifier

Version:

A Stylelint configuration tester for Jest that helps you verify your defined rules.

143 lines (142 loc) 4.65 kB
import stylelint from 'stylelint'; /** * The `ConfigVerifier` class verifies the rules of a Stylelint configuration. It runs Stylelint for a given test case * and compares the linter result with the expected result. The test case contains the code that should be linted and * the expected result. The expected result contains the expected error status, messages, and severities. * * @example * ```javascript * new ConfigVerifier().verify( * 'at-rule-disallowed-list', * { * name: 'Disallow @debug rule', * code: '@debug "";', * expect: { * errored: true, * messages: ['Unexpected at-rule "debug"'], * severities: ['error'], * }, * }, * { * name: 'Allow @use rule', * code: '@use "test.scss";', * }, * ); * ``` */ export class ConfigVerifier { configFile; /** * The default test case expectation * * This expectation occurs if Stylelint reports no problems and is used if no expectation was defined in a test case. * * @type {TestCaseExpectation} * * @internal */ defaultExpectation = { errored: false, messages: [], severities: [], }; /** * Creates a new `ConfigVerifier` object. * * @param {string | undefined} configFile - The path to the Stylelint config file whose rules should be verified */ constructor(configFile) { this.configFile = configFile; } /** * Verifies a rule configuration. * * @param {string} ruleName - The name of the rule * @param {TestCase[]} testCases - The test cases */ verify(ruleName, ...testCases) { describe(`Rule '${ruleName}'`, () => { test.each(testCases)('$name', async (testCase) => { const warnings = this.getWarnings(ruleName, await this.getLinterResult(testCase)); const { expect: expectation = this.defaultExpectation } = testCase; expect(this.getErrored(warnings)).toBe(expectation.errored); expect(this.getMessages(warnings)).toStrictEqual(expectation.messages.map((message) => `${message} (${ruleName})`)); expect(this.getSeverities(warnings)).toStrictEqual(expectation.severities); }); }); } /** * Runs Stylelint for the given test case and returns a Promise that resolves to the linter result. * * @internal * * @param {TestCase} testCase - The test case * * @returns {Promise<LinterResult>} A Promise that resolves to the linter result * * @throws {Error} If both `file` and `code` are defined or undefined */ getLinterResult({ file, code }) { if ([file, code].filter((value) => value === undefined).length !== 1) { throw new Error('Though both "file" and "code" are optional, you must have one and cannot have both.'); } return stylelint.lint({ configFile: this.configFile, files: file, code, }); } /** * Returns the warnings for a rule from the given linter result. * * @internal * * @param {string} ruleName - The name of the rule * @param {LinterResult} linterResult - The linter result * * @returns {Warning[]} The warnings for the rule */ getWarnings(ruleName, { results: lintResults }) { return lintResults .map(({ warnings }) => warnings) .reduce((previous, current) => previous.concat(current), []) .filter((warning) => warning.rule === ruleName); } /** * Returns true if the given lint warnings contain an error, otherwise false. * * @internal * * @param {Warning[]} warnings - The lint warnings * * @returns {boolean} True if the warnings contain an error, otherwise false */ getErrored(warnings) { return this.getSeverities(warnings).some((severity) => severity === 'error'); } /** * Returns the messages of the given lint warnings. * * @internal * * @param {Warning[]} warnings - The lint warnings * * @returns {string[]} The messages of the warnings */ getMessages(warnings) { return warnings.map(({ text }) => text); } /** * Returns the severities of the given lint warnings. * * @internal * * @param {Warning[]} warnings - The lint warnings * * @returns {Severity[]} The severities of the warnings */ getSeverities(warnings) { return warnings.map(({ severity }) => severity); } } export default ConfigVerifier;