stylelint-tape
Version:
Test stylelint plugins
177 lines (166 loc) • 5.98 kB
JavaScript
import path from 'path';
import logUpdate from 'log-update';
import stylelint from 'stylelint';
const colors = {
reset: '\x1b[0m',
bold: '\x1b[1m',
dim: '\x1b[2m',
underline: '\x1b[4m',
blink: '\x1b[5m',
reverse: '\x1b[7m',
hidden: '\x1b[8m',
black: '\x1b[30m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
magenta: '\x1b[35m',
cyan: '\x1b[36m',
white: '\x1b[37m',
bgBlack: '\x1b[40m',
bgRed: '\x1b[41m',
bgGreen: '\x1b[42m',
bgYellow: '\x1b[43m',
bgBlue: '\x1b[44m',
bgMagenta: '\x1b[45m',
bgCyan: '\x1b[46m',
bgWhite: '\x1b[47m'
};
const getColorFn = key => string => `${colors[key]}${String(string).replace(colors.reset, `${colors.reset}${colors[key]}`)}${colors.reset}`;
const bold = getColorFn('bold');
const dim = getColorFn('dim');
const red = getColorFn('red');
const green = getColorFn('green');
const yellow = getColorFn('yellow');
const cyan = getColorFn('cyan');
const white = getColorFn('white');
const trim = string => String(string).trim().replace(/\s+/g, ' ');
const json = object => {
try {
// trimmed, stringified object with raw-formatted RegExp
return trim(JSON.stringify(object, (key, value) => value instanceof RegExp ? `__REGEX_START${value}__REGEX_END` : value, ' ')).replace(/"__REGEX_START([\W\w]+)__REGEX_END"/g, '$1').replace(/^\[\s*([\W\w]*)\s*\]$/, '$1');
} catch (error) {
return '';
}
};
const done = process.platform === 'win32' ? '√' : '✔';
const fail = process.platform === 'win32' ? '×' : '✖';
const wait = '…';
const cwd$1 = process.cwd();
var nodeOptions = {
cwd: cwd$1,
defaultArgs: [true]
};
async function stylelintTape(options, testsByRuleName) {
let errorsCount = 0;
const normalizedopts = Object.assign({}, nodeOptions, options === Object(options) ? options : {
plugin: options
});
normalizedopts.plugin = path.resolve(normalizedopts.cwd, normalizedopts.plugin || '');
for (const ruleName in testsByRuleName) {
const tests = testsByRuleName[ruleName];
for (const test of tests) {
const title = getTitleByTest(test);
// update log for pending test
logUpdate(`${dim(wait)} ${title}`);
// run test and update log with results
await runTest(ruleName, test, normalizedopts).then(() => {
logUpdate(`${green(done)} ${white(title)}`);
}, error => {
// update error count in case there is errors
errorsCount++;
logUpdate(`${red(fail)} ${bold(title)}\n ${yellow(trim(error.message))}`);
}).then(() => {
logUpdate.done();
});
}
}
if (errorsCount === 0) {
return Promise.resolve();
}
return Promise.reject(Error('Tests failed.'));
}
function runTest(ruleName, test, opts) {
return stylelint.lint({
code: test.source,
config: {
plugins: [path.resolve(opts.cwd, opts.plugin)],
rules: {
[ruleName]: test.args || opts.defaultArgs
}
},
fix: Boolean(test.expect)
}).then(results => {
const warnings = results.results.reduce((array, result) => {
return array.concat(result.warnings);
}, []);
if (typeof test.warnings === 'number' && test.warnings !== warnings.length) {
// throw when the warning length by number does not match
throw new Error(`Expected ${test.warnings} warnings\nReceived ${warnings.length} warnings`);
}
if (Array.isArray(test.warnings)) {
if (test.warnings.length !== warnings.length) {
// throw when the warning length by array does not match
throw new Error(`Expected ${test.warnings.length} warnings\nReceived ${warnings.length} warnings`);
}
test.warnings.forEach((warningEntry, warningIndex) => {
if (typeof warningEntry === 'string' && warningEntry !== warnings[warningIndex].text) {
// throw when the warning text does not match
throw new Error(`Expected warning: "${warningEntry}"\nReceived warning: "${warnings[warningIndex].text}"`);
}
Object.keys(Object(warningEntry)).forEach(warningKey => {
if (warnings[warningIndex][warningKey] === warningEntry[warningKey]) {
// throw when the warning key-value pair does not match
throw new Error(`Expected: "${warningKey}" as ${warnings[warningIndex][warningKey]}\nReceived: ${warningEntry[warningKey]}`);
}
});
});
}
if (test.expect && results.output !== test.expect) {
throw new Error(`Expected: ${test.expect}\nReceived: ${results.output}`);
}
});
}
function getWarningsByTest(test) {
if (test.warnings instanceof Array) {
return test.warnings.length;
}
if (typeof test.warnings === 'number') {
return test.warnings;
}
return 0;
}
function getargsByTest(test) {
if (test.args) {
return ` with ${green(json(test.args))}`;
}
return '';
}
function getTitleByTest(test) {
if ('title' in test) {
return test.title;
}
if ('expect' in test) {
return `${cyan(test.source)}${getargsByTest(test)} becomes ${cyan(test.expect)}`;
}
const numberOfWarnings = getWarningsByTest(test);
const warnings = numberOfWarnings === 1 ? 'warning' : 'warnings';
return `${cyan(test.source)}${getargsByTest(test)} reports ${yellow(String(numberOfWarnings))} ${warnings}`;
}
const cwd = process.cwd();
const optRegExp = /^--([\w-]+)$/;
// return default options with overrides from cli arguments
var cliOptions = process.argv.reduce((object, arg, index) => {
const last = process.argv[index - 1];
const key = optRegExp.test(last) && String(last).replace(optRegExp, '$1');
return Object.assign(object, key ? {
[key]: arg
} : null);
}, {
cwd,
tape: '.tape.js'
});
const tests = await import(path.resolve(cliOptions.cwd, cliOptions.tape));
stylelintTape(cliOptions, 'default' in tests ? tests.default : tests).then(process.exit.bind(process, 0), error => console.error(`${error.message}`) || process.exit(1));
//# sourceMappingURL=cli.mjs.map