2015-06-02 09:34:14 +03:00
|
|
|
|
/**
|
|
|
|
|
* @author Titus Wormer
|
2016-08-02 23:47:01 +03:00
|
|
|
|
* @copyright 2016 Titus Wormer
|
2015-08-17 14:48:19 +03:00
|
|
|
|
* @license MIT
|
2016-08-02 23:47:01 +03:00
|
|
|
|
* @module remark:lint
|
|
|
|
|
* @fileoverview Test suite for `remark-lint`.
|
2015-06-02 09:34:14 +03:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
/* eslint-disable max-params */
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
/* Dependencies. */
|
2015-06-02 09:34:14 +03:00
|
|
|
|
var path = require('path');
|
2016-08-02 23:47:01 +03:00
|
|
|
|
var test = require('tape');
|
|
|
|
|
var vfile = require('to-vfile');
|
|
|
|
|
var removePosition = require('unist-util-remove-position');
|
2015-12-26 16:25:46 +03:00
|
|
|
|
var remark = require('remark');
|
2016-08-22 11:52:41 +03:00
|
|
|
|
var lint = require('../packages/remark-lint');
|
2016-08-02 23:47:01 +03:00
|
|
|
|
var rules = require('../script/util/rules');
|
|
|
|
|
var rule = require('../script/util/rule');
|
|
|
|
|
|
|
|
|
|
/* Tests. */
|
|
|
|
|
test('core', function (t) {
|
|
|
|
|
t.test('should work', function (st) {
|
|
|
|
|
var doc = vfile('virtual.md');
|
|
|
|
|
|
|
|
|
|
st.plan(2);
|
|
|
|
|
|
|
|
|
|
doc.contents = [
|
|
|
|
|
'# A heading',
|
|
|
|
|
'',
|
|
|
|
|
'# Another main heading.',
|
|
|
|
|
'',
|
|
|
|
|
'<!--lint ignore-->',
|
|
|
|
|
'',
|
|
|
|
|
'# Another main heading.'
|
|
|
|
|
].join('\n');
|
|
|
|
|
|
|
|
|
|
remark()
|
2016-08-22 11:52:41 +03:00
|
|
|
|
.use(lint, {
|
|
|
|
|
noHeadingPunctuation: true,
|
|
|
|
|
noMultipleToplevelHeadings: true
|
|
|
|
|
})
|
2016-08-02 23:47:01 +03:00
|
|
|
|
.process(doc, function (err) {
|
|
|
|
|
st.ifErr(err, 'should not fail');
|
|
|
|
|
st.deepEqual(
|
|
|
|
|
doc.messages.map(String),
|
|
|
|
|
[
|
|
|
|
|
'virtual.md:3:1-3:24: Don’t add a trailing `.` to headings',
|
|
|
|
|
'virtual.md:3:1-3:24: Don’t use multiple top level headings (3:1)'
|
|
|
|
|
]);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.test('should support no options', function (st) {
|
|
|
|
|
st.plan(2);
|
|
|
|
|
|
|
|
|
|
remark().use(lint).process('.', function (err, file) {
|
|
|
|
|
st.ifErr(err, 'should not fail');
|
|
|
|
|
st.deepEqual(
|
|
|
|
|
file.messages.map(String),
|
2016-08-22 11:52:41 +03:00
|
|
|
|
[],
|
2016-08-02 23:47:01 +03:00
|
|
|
|
'should warn for missing new lines'
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.test('should support camel-cased rule-id’s', function (st) {
|
|
|
|
|
st.plan(2);
|
|
|
|
|
|
|
|
|
|
remark()
|
|
|
|
|
.use(lint, {finalNewline: false})
|
|
|
|
|
.process('.', function (err, file) {
|
|
|
|
|
st.ifErr(err, 'should not fail');
|
|
|
|
|
st.deepEqual(file.messages, [], 'should disable `final-newline`');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.test('should support dash-cased rule-id’s', function (st) {
|
|
|
|
|
st.plan(2);
|
|
|
|
|
|
|
|
|
|
remark()
|
|
|
|
|
.use(lint, {'final-newline': false})
|
|
|
|
|
.process('.', function (err, file) {
|
|
|
|
|
st.ifErr(err, 'should not fail');
|
|
|
|
|
st.deepEqual(file.messages, [], 'should disable `final-newline`');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2016-08-15 13:36:55 +03:00
|
|
|
|
t.test('should support a list with a severity', function (st) {
|
|
|
|
|
st.plan(3);
|
|
|
|
|
|
|
|
|
|
remark()
|
2016-08-22 11:52:41 +03:00
|
|
|
|
.use(lint, {finalNewline: [2]})
|
2016-08-15 13:36:55 +03:00
|
|
|
|
.process('.', function (err, file) {
|
|
|
|
|
st.ifErr(err, 'should not fail');
|
|
|
|
|
st.equal(
|
|
|
|
|
file.messages.join(),
|
|
|
|
|
'1:1: Missing newline character at end of file',
|
|
|
|
|
'should trigger fatally (1)'
|
|
|
|
|
);
|
|
|
|
|
st.equal(file.messages[0].fatal, true, 'should trigger fatally (2)');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2016-11-01 18:17:57 +03:00
|
|
|
|
t.test('should support a list with a string severity (`error`)', function (st) {
|
|
|
|
|
st.plan(3);
|
|
|
|
|
|
|
|
|
|
remark()
|
|
|
|
|
.use(lint, {finalNewline: ['error']})
|
|
|
|
|
.process('.', function (err, file) {
|
|
|
|
|
st.ifErr(err, 'should not fail');
|
|
|
|
|
st.equal(
|
|
|
|
|
file.messages.join(),
|
|
|
|
|
'1:1: Missing newline character at end of file',
|
|
|
|
|
'should trigger fatally (1)'
|
|
|
|
|
);
|
|
|
|
|
st.equal(file.messages[0].fatal, true, 'should trigger fatally (2)');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.test('should support a list with a string severity (`on`)', function (st) {
|
|
|
|
|
st.plan(3);
|
|
|
|
|
|
|
|
|
|
remark()
|
|
|
|
|
.use(lint, {finalNewline: ['on']})
|
|
|
|
|
.process('.', function (err, file) {
|
|
|
|
|
st.ifErr(err, 'should not fail');
|
|
|
|
|
st.equal(
|
|
|
|
|
file.messages.join(),
|
|
|
|
|
'1:1: Missing newline character at end of file',
|
|
|
|
|
'should message'
|
|
|
|
|
);
|
|
|
|
|
st.equal(file.messages[0].fatal, false, 'should *not* trigger fatally');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.test('should support a list with a string severity (`warn`)', function (st) {
|
|
|
|
|
st.plan(3);
|
|
|
|
|
|
|
|
|
|
remark()
|
|
|
|
|
.use(lint, {finalNewline: ['warn']})
|
|
|
|
|
.process('.', function (err, file) {
|
|
|
|
|
st.ifErr(err, 'should not fail');
|
|
|
|
|
st.equal(
|
|
|
|
|
file.messages.join(),
|
|
|
|
|
'1:1: Missing newline character at end of file',
|
|
|
|
|
'should message'
|
|
|
|
|
);
|
|
|
|
|
st.equal(file.messages[0].fatal, false, 'should *not* trigger fatally');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
t.test('should support a list with a string severity (`off`)', function (st) {
|
|
|
|
|
st.plan(2);
|
|
|
|
|
|
|
|
|
|
remark()
|
|
|
|
|
.use(lint, {finalNewline: ['off']})
|
|
|
|
|
.process('.', function (err, file) {
|
|
|
|
|
st.ifErr(err, 'should not fail');
|
|
|
|
|
st.deepEqual(file.messages, [], 'should disable `final-newline`');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2016-08-15 13:36:55 +03:00
|
|
|
|
t.test('should fail on invalid severities', function (st) {
|
|
|
|
|
st.throws(
|
|
|
|
|
function () {
|
|
|
|
|
remark().use(lint, {finalNewline: [3]});
|
|
|
|
|
},
|
|
|
|
|
/^Error: Invalid severity `3` for `final-newline`, expected 0, 1, or 2$/,
|
|
|
|
|
'should throw when too high'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
st.throws(
|
|
|
|
|
function () {
|
|
|
|
|
remark().use(lint, {finalNewline: [-1]});
|
|
|
|
|
},
|
|
|
|
|
/^Error: Invalid severity `-1` for `final-newline`, expected 0, 1, or 2$/,
|
|
|
|
|
'should throw too low'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
st.end();
|
|
|
|
|
});
|
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
t.end();
|
|
|
|
|
});
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
test('external rules', function (t) {
|
|
|
|
|
var tests = {
|
|
|
|
|
'literal external rules': [
|
|
|
|
|
require('remark-lint-no-url-trailing-slash')
|
|
|
|
|
],
|
|
|
|
|
'external rules by package name': [
|
|
|
|
|
'remark-lint-no-url-trailing-slash'
|
|
|
|
|
],
|
|
|
|
|
'external rules by prefixless name': [
|
|
|
|
|
'no-url-trailing-slash'
|
|
|
|
|
],
|
|
|
|
|
'external rules by absolute file-path': [
|
|
|
|
|
path.join(__dirname, 'local-external.js')
|
|
|
|
|
],
|
|
|
|
|
'external rules by relative file-path': [
|
2016-08-22 00:46:21 +03:00
|
|
|
|
'./' + path.relative(
|
2016-08-02 23:47:01 +03:00
|
|
|
|
process.cwd(),
|
|
|
|
|
path.join(__dirname, 'local-external.js')
|
|
|
|
|
)
|
|
|
|
|
],
|
|
|
|
|
'external rules without extension': [
|
|
|
|
|
path.join(__dirname, 'local-external')
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
t.plan(Object.keys(tests).length);
|
|
|
|
|
|
|
|
|
|
Object.keys(tests).forEach(function (label) {
|
|
|
|
|
t.test('should load ' + label, function (st) {
|
|
|
|
|
var doc = '[alpha](https://bravo.charlie/)\n';
|
|
|
|
|
|
|
|
|
|
st.plan(2);
|
|
|
|
|
|
|
|
|
|
remark()
|
|
|
|
|
.use(lint, {
|
2016-08-22 11:52:41 +03:00
|
|
|
|
external: tests[label],
|
|
|
|
|
trailingSlash: true
|
2016-08-02 23:47:01 +03:00
|
|
|
|
})
|
|
|
|
|
.process(doc, function (err, file) {
|
|
|
|
|
st.ifErr(err, 'should not fail');
|
|
|
|
|
st.deepEqual(
|
|
|
|
|
file.messages.map(String),
|
|
|
|
|
['1:1-1:32: Remove trailing slash (https://bravo.charlie)'],
|
|
|
|
|
'should warn for missing new lines'
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
test('rules', function (t) {
|
2016-08-22 11:52:41 +03:00
|
|
|
|
var all = rules(path.join(process.cwd(), 'packages', 'remark-lint'));
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
t.plan(all.length);
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
all.forEach(function (rulePath) {
|
|
|
|
|
var info = rule(rulePath);
|
|
|
|
|
|
|
|
|
|
t.test(info.ruleId, function (sst) {
|
|
|
|
|
assertRule(sst, info);
|
2015-06-02 09:34:14 +03:00
|
|
|
|
});
|
2016-08-02 23:47:01 +03:00
|
|
|
|
});
|
|
|
|
|
});
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
|
|
|
|
/**
|
2016-08-02 23:47:01 +03:00
|
|
|
|
* Assert a rule.
|
2015-06-02 09:34:14 +03:00
|
|
|
|
*
|
2016-08-02 23:47:01 +03:00
|
|
|
|
* @param {Test} t - Tape test.
|
|
|
|
|
* @param {Object} rule - Rule information.
|
2015-06-02 09:34:14 +03:00
|
|
|
|
*/
|
2016-08-02 23:47:01 +03:00
|
|
|
|
function assertRule(t, rule) {
|
|
|
|
|
var tests = rule.tests;
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
Object.keys(tests).forEach(function (setting) {
|
|
|
|
|
var fixture = tests[setting];
|
|
|
|
|
var config = JSON.parse(setting);
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
t.test(setting, function (st) {
|
|
|
|
|
Object.keys(fixture).forEach(function (name) {
|
|
|
|
|
st.test(name, function (sst) {
|
|
|
|
|
assertFixture(sst, rule, fixture[name], name, config);
|
|
|
|
|
});
|
|
|
|
|
});
|
2015-06-02 09:34:14 +03:00
|
|
|
|
});
|
2016-08-02 23:47:01 +03:00
|
|
|
|
});
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
t.end();
|
2015-06-02 09:34:14 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2016-08-02 23:47:01 +03:00
|
|
|
|
* Assert a fixture.
|
2015-06-02 09:34:14 +03:00
|
|
|
|
*
|
2016-08-02 23:47:01 +03:00
|
|
|
|
* @param {Test} t - Tape test.
|
|
|
|
|
* @param {Object} rule - Rule information.
|
|
|
|
|
* @param {Object} fixture - Fixture information.
|
|
|
|
|
* @param {string} basename - Name of fixture.
|
|
|
|
|
* @param {*} setting - Configuration.
|
2015-06-02 09:34:14 +03:00
|
|
|
|
*/
|
2016-08-02 23:47:01 +03:00
|
|
|
|
function assertFixture(t, rule, fixture, basename, setting) {
|
|
|
|
|
var ruleId = rule.ruleId;
|
|
|
|
|
var file = vfile(basename);
|
|
|
|
|
var expected = fixture.output;
|
2016-08-22 11:52:41 +03:00
|
|
|
|
var options = {};
|
2016-08-02 23:47:01 +03:00
|
|
|
|
var positionless = fixture.config.positionless;
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
options[ruleId] = setting;
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
file.contents = preprocess(fixture.input || '');
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
t.plan(positionless ? 1 : 2);
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-22 00:46:21 +03:00
|
|
|
|
try {
|
|
|
|
|
remark().use(lint, options).process(file, fixture.config);
|
|
|
|
|
} catch (err) {}
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
file.messages.forEach(function (message) {
|
|
|
|
|
if (message.ruleId !== ruleId) {
|
|
|
|
|
throw new Error(
|
|
|
|
|
'Expected `' + ruleId + '`, not `' +
|
|
|
|
|
message.ruleId + '` as `ruleId` for ' +
|
|
|
|
|
message
|
|
|
|
|
);
|
2015-06-02 09:34:14 +03:00
|
|
|
|
}
|
2016-08-02 23:47:01 +03:00
|
|
|
|
});
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
t.deepEqual(
|
|
|
|
|
normalize(file.messages),
|
|
|
|
|
expected,
|
|
|
|
|
'should equal with position'
|
|
|
|
|
);
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
if (!positionless) {
|
|
|
|
|
file.messages = [];
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-22 00:46:21 +03:00
|
|
|
|
try {
|
|
|
|
|
remark().use(function () {
|
|
|
|
|
return removePosition;
|
|
|
|
|
}).use(lint, options).process(file);
|
|
|
|
|
} catch (err) {}
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
t.deepEqual(
|
|
|
|
|
normalize(file.messages),
|
|
|
|
|
[],
|
|
|
|
|
'should equal without position'
|
|
|
|
|
);
|
|
|
|
|
}
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
file.messages = [];
|
2015-06-02 09:34:14 +03:00
|
|
|
|
}
|
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
/**
|
|
|
|
|
* Normalize a list of messages.
|
|
|
|
|
*
|
|
|
|
|
* @param {Array.<VFileMessage>} messages - List of warnings.
|
|
|
|
|
* @return {Array.<VFileMessage>} - Normalized warnings.
|
2015-06-02 09:34:14 +03:00
|
|
|
|
*/
|
2016-08-02 23:47:01 +03:00
|
|
|
|
function normalize(messages) {
|
|
|
|
|
return messages.map(function (message) {
|
|
|
|
|
var value = String(message);
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
return value.slice(value.indexOf(':') + 1);
|
|
|
|
|
});
|
|
|
|
|
}
|
2015-06-02 09:34:14 +03:00
|
|
|
|
|
2016-08-02 23:47:01 +03:00
|
|
|
|
function preprocess(value) {
|
|
|
|
|
return value.replace(/»/g, '\t').replace(/·/g, ' ');
|
|
|
|
|
}
|