UNPKG

stylelint

Version:
296 lines (252 loc) 10.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = function (equalityCheck) { return function (rule, schema) { var alreadyHadOnlyTest = !!onlyTest; if (schema.accept) { schema.accept.forEach(_lodash2.default.partial(checkCaseForOnly, "accept")); } if (schema.reject) { schema.reject.forEach(_lodash2.default.partial(checkCaseForOnly, "reject")); } if (onlyTest) { schema = _lodash2.default.assign(_lodash2.default.omit(schema, ["accept", "reject"]), _defineProperty({ skipBasicChecks: true }, onlyTest.type, [onlyTest.case])); } if (!alreadyHadOnlyTest) { process.nextTick(function () { processGroup(rule, schema, equalityCheck); }); } }; }; var _postcss = require("postcss"); var _postcss2 = _interopRequireDefault(_postcss); var _postcssScss = require("postcss-scss"); var _postcssScss2 = _interopRequireDefault(_postcssScss); var _postcssLess = require("postcss-less"); var _postcssLess2 = _interopRequireDefault(_postcssLess); var _sugarss = require("sugarss"); var _sugarss2 = _interopRequireDefault(_sugarss); var _lodash = require("lodash"); var _lodash2 = _interopRequireDefault(_lodash); var _normalizeRuleSettings = require("../normalizeRuleSettings"); var _normalizeRuleSettings2 = _interopRequireDefault(_normalizeRuleSettings); var _disableRanges = require("../disableRanges"); var _disableRanges2 = _interopRequireDefault(_disableRanges); var _basicChecks = require("./basicChecks"); var _basicChecks2 = _interopRequireDefault(_basicChecks); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } /** * Create a stylelint rule testing function. * * Pass in an `equalityCheck` function. Given some information, * this checker should use Whatever Test Runner to perform * equality checks. * * `equalityCheck` should accept two arguments: * - `processCss` {Promise}: A Promise that resolves with an array of * comparisons that you need to check (documented below). * - `context` {object}: An object that contains additional information * you may need: * - `caseDescription` {string}: A description of the test case as a whole. * Will look like this: * > rule: value-list-comma-space-before * > config: "always-single-line" * > code: "a { background-size: 0 ,0;\n}" * - `comparisonCount` {number}: The number of comparisons that * will need to be performed (e.g. useful for tape). * - `completeAssertionDescription` {string}: While each individual * comparison may have its own description, this is a description * of the whole assertion (e.g. useful for Mocha). * - `only` {boolean}: If `true`, the test runner should only run this * test case (e.g. `test.only` in tape, `describe.only` in Mocha). * * `processCss` is a Promsie that resolves with an array of comparisons. * Each comparison has the following properties: * - `actual` {any}: Some actual value. * - `expected` {any}: Some expected value. * - `description` {string}: A (possibly empty) description of the comparison. * * Within `equalityCheck`, you need to ensure that you: * - Set up the test case. * - When `processCss` resolves, loop through every comparison. * - For each comparison, make an assertion checking that `actual === expected`. * * The `testRule` function that you get has a simple signature: * `testRule(rule, testGroupDescription)`. * * `rule` is just the rule that you are testing (a function). * * `testGroupDescription` is an object fitting the following schema. * * Required properties: * - `ruleName` {string}: The name of the rule. Used in descriptions. * - `config` {any}: The rule's configuration for this test group. * Should match the format you'd use in `.stylelintrc`. * - `accept` {array}: An array of objects describing test cases that * should not violate the rule. Each object has these properties: * - `code` {string}: The source CSS to check. * - `description` {[string]}: An optional description of the case. * - `reject` {array}: An array of objects describing test cases that * should violate the rule once. Each object has these properties: * - `code` {string}: The source CSS to check. * - `message` {string}: The message of the expected violation. * - `line` {[number]}: The expected line number of the violation. * If this is left out, the line won't be checked. * - `column` {[number]}: The expected column number of the violation. * If this is left out, the column won't be checked. * - `description` {[string]}: An optional description of the case. * * Optional properties: * - `syntax` {"css"|"scss"|"less"|"sugarss"}: Defaults to `"css"`. * - `skipBasicChecks` {boolean}: Defaults to `false`. If `true`, a * few rudimentary checks (that should almost always be included) * will not be performed. * - `preceedingPlugins` {array}: An array of PostCSS plugins that * should be run before the CSS is tested. * * @param {function} equalityCheck - Described above * @return {function} testRule - Decsribed above */ var onlyTest = void 0; function checkCaseForOnly(caseType, testCase) { if (!testCase.only) { return; } if (onlyTest) { throw new Error("Cannot use `only` on multiple test cases"); } onlyTest = { case: testCase, type: caseType }; } function processGroup(rule, schema, equalityCheck) { var ruleName = schema.ruleName; var ruleOptions = (0, _normalizeRuleSettings2.default)(schema.config); var rulePrimaryOptions = ruleOptions[0]; var ruleSecondaryOptions = ruleOptions[1]; var printableConfig = rulePrimaryOptions ? JSON.stringify(rulePrimaryOptions) : ""; if (printableConfig && ruleSecondaryOptions) { printableConfig += ", " + JSON.stringify(ruleSecondaryOptions); } function createCaseDescription(code) { var text = "\n> rule: " + ruleName + "\n"; text += "> config: " + printableConfig + "\n"; text += "> code: " + JSON.stringify(code) + "\n"; return text; } // Process the code through the rule and return // the PostCSS LazyResult promise function postcssProcess(code) { var postcssProcessOptions = {}; switch (schema.syntax) { case "scss": postcssProcessOptions.syntax = _postcssScss2.default; break; case "less": postcssProcessOptions.syntax = _postcssLess2.default; break; case "sugarss": postcssProcessOptions.syntax = _sugarss2.default; break; } var processor = (0, _postcss2.default)(); processor.use(_disableRanges2.default); if (schema.preceedingPlugins) { schema.preceedingPlugins.forEach(processor.use); } return processor.use(rule(rulePrimaryOptions, ruleSecondaryOptions)).process(code, postcssProcessOptions); } // Apply the basic positive checks unless // explicitly told not to var passingTestCases = schema.skipBasicChecks ? schema.accept : _basicChecks2.default.concat(schema.accept); if (passingTestCases && passingTestCases.length) { passingTestCases.forEach(function (acceptedCase) { if (!acceptedCase) { return; } var assertionDescription = spaceJoin(acceptedCase.description, "should be accepted"); var resultPromise = postcssProcess(acceptedCase.code).then(function (postcssResult) { var warnings = postcssResult.warnings(); return [{ expected: 0, actual: warnings.length, description: assertionDescription }]; }).catch(function (err) { return console.log(err.stack); }); // eslint-disable-line no-console equalityCheck(resultPromise, { comparisonCount: 1, caseDescription: createCaseDescription(acceptedCase.code), completeAssertionDescription: assertionDescription }); }); } if (schema.reject && schema.reject.length) { schema.reject.forEach(function (rejectedCase) { var completeAssertionDescription = "should register one warning"; var comparisonCount = 1; if (rejectedCase.line) { comparisonCount++; completeAssertionDescription += " on line " + rejectedCase.line; } if (rejectedCase.column !== undefined) { comparisonCount++; completeAssertionDescription += " on column " + rejectedCase.column; } if (rejectedCase.message) { comparisonCount++; completeAssertionDescription += " with message \"" + rejectedCase.message + "\""; } var resultPromise = postcssProcess(rejectedCase.code).then(function (postcssResult) { var warnings = postcssResult.warnings(); var warning = warnings[0]; var comparisons = [{ expected: 1, actual: warnings.length, description: spaceJoin(rejectedCase.description, "should register one warning") }]; if (rejectedCase.line) { comparisons.push({ expected: rejectedCase.line, actual: _lodash2.default.get(warning, "line"), description: spaceJoin(rejectedCase.description, "should warn on line " + rejectedCase.line) }); } if (rejectedCase.column !== undefined) { comparisons.push({ expected: rejectedCase.column, actual: _lodash2.default.get(warning, "column"), description: spaceJoin(rejectedCase.description, "should warn on column " + rejectedCase.column) }); } if (rejectedCase.message) { comparisons.push({ expected: rejectedCase.message, actual: _lodash2.default.get(warning, "text"), description: spaceJoin(rejectedCase.description, "should warn with message " + rejectedCase.message) }); } return comparisons; }).catch(function (err) { return console.log(err.stack); }); // eslint-disable-line no-console equalityCheck(resultPromise, { comparisonCount: comparisonCount, completeAssertionDescription: completeAssertionDescription, caseDescription: createCaseDescription(rejectedCase.code), only: rejectedCase.only }); }); } } function spaceJoin() { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return _lodash2.default.compact(args).join(" "); }