vitest-stylelint-utils
Version:
Utilities for testing Stylelint plugins with Vitest
135 lines (133 loc) • 4.51 kB
JavaScript
import util from "node:util";
import stylelint from "stylelint";
import { describe, expect, it } from "vitest";
//#region src/get-test-rule.ts
const { lint } = stylelint;
/**
* Create a `testRule()` function with any specified plugins.
*/
function getTestRule(options = {}) {
return function testRule(schema) {
describe(`${schema.ruleName}`, () => {
const stylelintConfig = {
plugins: options.plugins || schema.plugins,
rules: { [schema.ruleName]: schema.config }
};
setupTestCases({
name: "accept",
cases: schema.accept,
schema,
comparisons(testCase) {
return async function() {
const stylelintOptions = {
code: testCase.code,
config: stylelintConfig,
customSyntax: schema.customSyntax,
codeFilename: schema.codeFilename
};
const output = await lint(stylelintOptions);
expect(output.results[0].warnings).toEqual([]);
expect(output.results[0].parseErrors).toEqual([]);
expect(output.results[0].invalidOptionWarnings).toEqual([]);
if (!schema.fix) return;
const outputAfterFix = await lint({
...stylelintOptions,
fix: true
});
const fixedCode = getOutputCss(outputAfterFix);
expect(fixedCode).toBe(testCase.code);
};
}
});
setupTestCases({
name: "reject",
cases: schema.reject,
schema,
comparisons(testCase) {
return async function() {
const stylelintOptions = {
code: testCase.code,
config: stylelintConfig,
customSyntax: schema.customSyntax,
codeFilename: schema.codeFilename
};
const outputAfterLint = await lint(stylelintOptions);
const actualWarnings = [...outputAfterLint.results[0].invalidOptionWarnings, ...outputAfterLint.results[0].warnings];
expect(outputAfterLint.results[0]).toMatchObject({ parseErrors: [] });
expect(actualWarnings).toHaveLength(testCase.warnings ? testCase.warnings.length : 1);
const warnings = testCase.warnings || [testCase];
warnings.forEach((warning, index) => {
expect(warning.message, "Expected \"reject\" test case to have a \"message\" property").toBeDefined();
const expectedWarning = {
text: warning.message,
line: warning.line,
column: warning.column,
endLine: warning.endLine,
endColumn: warning.endColumn
};
Object.entries(expectedWarning).forEach(([key, value]) => {
if (value === void 0) delete expectedWarning[key];
});
expect(actualWarnings[index]).toMatchObject(expectedWarning);
});
if (!schema.fix) return;
if (schema.fix && !testCase.fixed && testCase.fixed !== "" && !testCase.unfixable) throw new Error("If using { fix: true } in test schema, all reject cases must have { fixed: .. }");
const outputAfterFix = await lint({
...stylelintOptions,
fix: true
});
const fixedCode = getOutputCss(outputAfterFix);
if (testCase.unfixable) {
if (testCase.fixed) expect(fixedCode).toBe(testCase.fixed);
expect(fixedCode).toBe(testCase.code);
} else {
expect(fixedCode).toBe(testCase.fixed);
expect(fixedCode).not.toBe(testCase.code);
}
const outputAfterLintOnFixedCode = await lint({
...stylelintOptions,
code: fixedCode,
fix: testCase.unfixable
});
expect(outputAfterLintOnFixedCode.results[0]).toMatchObject({
warnings: outputAfterFix.results[0].warnings,
parseErrors: []
});
};
}
});
});
};
}
function setupTestCases({ name, cases, schema, comparisons }) {
if (cases && cases.length > 0) {
const testGroup = (() => {
if (schema.only) return describe.only;
if (schema.skip) return describe.skip;
return describe;
})();
testGroup(`${name}`, () => {
cases.forEach((testCase) => {
if (testCase) {
const spec = (() => {
if (testCase.only) return it.only;
if (testCase.skip) return it.skip;
return it;
})();
describe(`${util.inspect(schema.config)}`, () => {
describe(`${util.inspect(testCase.code)}`, () => {
spec(testCase.description || "no description", comparisons(testCase));
});
});
}
});
});
}
}
function getOutputCss(output) {
const result = output.results[0]._postcssResult;
if (result && result.root && result.opts) return result.root.toString(result.opts.syntax);
throw new TypeError("Invalid result");
}
//#endregion
export { getTestRule };