remark-lint/test.js

408 lines
11 KiB
JavaScript
Raw Normal View History

2021-08-12 15:01:59 +03:00
/**
* @typedef {import('unified').Plugin} Plugin
* @typedef {import('vfile-message').VFileMessage} VFileMessage
* @typedef {import('./script/util/rule.js').Check} Check
2023-05-16 12:46:34 +03:00
* @typedef {import('./script/util/rule.js').Rule} Rule
2021-08-12 15:01:59 +03:00
*/
2023-05-16 14:32:02 +03:00
import assert from 'node:assert/strict'
2021-08-12 15:11:25 +03:00
import path from 'node:path'
import process from 'node:process'
2023-05-16 14:32:02 +03:00
import test from 'node:test'
import url from 'node:url'
2021-08-10 21:28:49 +03:00
import {toVFile} from 'to-vfile'
import {removePosition} from 'unist-util-remove-position'
import {remark} from 'remark'
import remarkGfm from 'remark-gfm'
import {lintRule} from 'unified-lint-rule'
2021-08-10 18:29:09 +03:00
import {rules} from './script/util/rules.js'
import {rule} from './script/util/rule.js'
import {characters} from './script/characters.js'
import lint from './packages/remark-lint/index.js'
import noHeadingPunctuation from './packages/remark-lint-no-heading-punctuation/index.js'
import noMultipleToplevelHeadings from './packages/remark-lint-no-multiple-toplevel-headings/index.js'
import noUndefinedReferences from './packages/remark-lint-no-undefined-references/index.js'
2021-08-10 18:29:09 +03:00
import finalNewline from './packages/remark-lint-final-newline/index.js'
2018-05-18 13:30:21 +03:00
2021-08-11 14:05:30 +03:00
const own = {}.hasOwnProperty
2023-05-16 14:32:02 +03:00
test('core', async () => {
2021-08-12 15:01:59 +03:00
const doc = [
'# A heading',
'',
'# Another main heading.',
'',
'<!--lint ignore-->',
'',
'# Another main heading.'
].join('\n')
2021-11-29 21:36:39 +03:00
let file = await remark()
.use(noHeadingPunctuation)
.use(noMultipleToplevelHeadings)
.use(lint)
.process(toVFile({path: 'virtual.md', value: doc}))
2023-05-16 14:32:02 +03:00
assert.deepEqual(
2021-11-29 21:36:39 +03:00
asStrings(file.messages),
2021-08-12 15:01:59 +03:00
[
'virtual.md:3:1-3:24: Dont add a trailing `.` to headings',
'virtual.md:3:1-3:24: Dont use multiple top level headings (1:1)'
],
'should support `remark-lint` last'
)
2021-11-29 21:36:39 +03:00
file = await remark()
.use(lint)
.use(noHeadingPunctuation)
.use(noMultipleToplevelHeadings)
.process(toVFile({path: 'virtual.md', value: doc}))
2023-05-16 14:32:02 +03:00
assert.deepEqual(
2021-11-29 21:36:39 +03:00
asStrings(file.messages),
2021-08-12 15:01:59 +03:00
[
'virtual.md:3:1-3:24: Dont add a trailing `.` to headings',
'virtual.md:3:1-3:24: Dont use multiple top level headings (1:1)'
],
'should support `remark-lint` first'
2018-05-18 13:30:21 +03:00
)
2021-11-29 21:36:39 +03:00
file = await remark().use(lint).process('.')
2023-05-16 14:32:02 +03:00
assert.deepEqual(asStrings(file.messages), [], 'should support no rules')
2021-11-29 21:36:39 +03:00
file = await remark().use(finalNewline).process('')
2023-05-16 14:32:02 +03:00
assert.deepEqual(
asStrings(file.messages),
[],
'should support successful rules'
)
2021-11-29 21:36:39 +03:00
file = await remark().use(finalNewline, [2]).process('.')
2023-05-16 14:32:02 +03:00
assert.deepEqual(
2021-11-29 21:36:39 +03:00
file.messages.map((d) => JSON.parse(JSON.stringify(d))),
2021-08-12 15:01:59 +03:00
[
{
2023-11-09 16:45:02 +03:00
fatal: true,
2021-08-12 15:01:59 +03:00
message: 'Missing newline character at end of file',
2023-11-09 16:45:02 +03:00
name: '1:1',
2021-08-12 15:01:59 +03:00
reason: 'Missing newline character at end of file',
ruleId: 'final-newline',
2023-11-09 16:45:02 +03:00
source: 'remark-lint',
url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme'
2021-08-12 15:01:59 +03:00
}
],
'should support a list with a severity'
)
2021-11-29 21:36:39 +03:00
file = await remark().use(finalNewline, true).process('.')
2023-05-16 14:32:02 +03:00
assert.deepEqual(
2021-11-29 21:36:39 +03:00
asStrings(file.messages),
2021-08-12 15:01:59 +03:00
['1:1: Missing newline character at end of file'],
'should support a boolean (`true`)'
)
2021-11-29 21:36:39 +03:00
file = await remark().use(finalNewline, false).process('.')
2023-05-16 14:32:02 +03:00
assert.deepEqual(
2021-11-29 21:36:39 +03:00
asStrings(file.messages),
2021-08-12 15:01:59 +03:00
[],
'should support a boolean (`false`)'
)
2021-11-29 21:36:39 +03:00
file = await remark().use(finalNewline, [true]).process('.')
2023-05-16 14:32:02 +03:00
assert.deepEqual(
2021-11-29 21:36:39 +03:00
asStrings(file.messages),
2021-08-12 15:01:59 +03:00
['1:1: Missing newline character at end of file'],
'should support a list with a boolean severity (true, for on)'
)
2021-11-29 21:36:39 +03:00
file = await remark().use(finalNewline, [false]).process('.')
2023-05-16 14:32:02 +03:00
assert.deepEqual(
2021-11-29 21:36:39 +03:00
asStrings(file.messages),
2021-08-12 15:01:59 +03:00
[],
'should support a list with boolean severity (false, for off)'
)
2021-11-29 21:36:39 +03:00
file = await remark().use(finalNewline, ['error']).process('.')
2023-05-16 14:32:02 +03:00
assert.deepEqual(
2021-11-29 21:36:39 +03:00
file.messages.map((d) => JSON.parse(JSON.stringify(d))),
2021-08-12 15:01:59 +03:00
[
{
2023-11-09 16:45:02 +03:00
fatal: true,
2021-08-12 15:01:59 +03:00
message: 'Missing newline character at end of file',
2023-11-09 16:45:02 +03:00
name: '1:1',
2021-08-12 15:01:59 +03:00
reason: 'Missing newline character at end of file',
ruleId: 'final-newline',
2023-11-09 16:45:02 +03:00
source: 'remark-lint',
url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme'
2021-08-12 15:01:59 +03:00
}
],
'should support a list with string severity (`error`)'
)
2021-11-29 21:36:39 +03:00
file = await remark().use(finalNewline, ['on']).process('.')
2023-05-16 14:32:02 +03:00
assert.deepEqual(
2021-11-29 21:36:39 +03:00
file.messages.map((d) => JSON.parse(JSON.stringify(d))),
2021-08-12 15:01:59 +03:00
[
{
2023-11-09 16:45:02 +03:00
fatal: false,
2021-08-12 15:01:59 +03:00
message: 'Missing newline character at end of file',
2023-11-09 16:45:02 +03:00
name: '1:1',
2021-08-12 15:01:59 +03:00
reason: 'Missing newline character at end of file',
ruleId: 'final-newline',
2023-11-09 16:45:02 +03:00
source: 'remark-lint',
url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme'
2021-08-12 15:01:59 +03:00
}
],
'should support a list with string severity (`on`)'
)
2021-11-29 21:36:39 +03:00
file = await remark().use(finalNewline, ['warn']).process('.')
2023-05-16 14:32:02 +03:00
assert.deepEqual(
2021-11-29 21:36:39 +03:00
file.messages.map((d) => JSON.parse(JSON.stringify(d))),
2021-08-12 15:01:59 +03:00
[
{
2023-11-09 16:45:02 +03:00
fatal: false,
2021-08-12 15:01:59 +03:00
message: 'Missing newline character at end of file',
2023-11-09 16:45:02 +03:00
name: '1:1',
2021-08-12 15:01:59 +03:00
reason: 'Missing newline character at end of file',
ruleId: 'final-newline',
2023-11-09 16:45:02 +03:00
source: 'remark-lint',
url: 'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-final-newline#readme'
2021-08-12 15:01:59 +03:00
}
],
'should support a list with string severity (`warn`)'
)
2021-11-29 21:36:39 +03:00
file = await remark().use(finalNewline, ['off']).process('.')
2023-05-16 14:32:02 +03:00
assert.deepEqual(
2021-11-29 21:36:39 +03:00
asStrings(file.messages),
2021-08-12 15:01:59 +03:00
[],
'should support a list with string severity (`off`)'
)
2023-05-16 14:32:02 +03:00
assert.throws(
2021-08-12 15:01:59 +03:00
() => {
remark().use(finalNewline, [3]).freeze()
},
/^Error: Incorrect severity `3` for `final-newline`, expected 0, 1, or 2$/,
'should fail on incorrect severities (too high)'
)
2023-05-16 14:32:02 +03:00
assert.throws(
2021-08-12 15:01:59 +03:00
() => {
remark().use(finalNewline, [-1]).freeze()
},
/^Error: Incorrect severity `-1` for `final-newline`, expected 0, 1, or 2$/,
'should fail on incorrect severities (too low)'
)
file = await remark()
.use(noUndefinedReferences, {allow: [/^b\./i]})
.process(
toVFile({
path: 'virtual.md',
value: ['[foo][b.c]', '', '[bar][b]'].join('\n')
})
)
2023-05-16 14:32:02 +03:00
assert.deepEqual(
asStrings(file.messages),
['virtual.md:3:1-3:9: Found reference to undefined definition'],
'no-undefined-references allow option should work with native regex'
)
2021-11-29 21:36:39 +03:00
file = await remark()
.use(
lintRule('test:rule', (tree, file) => {
file.message('Test message')
}),
['warn']
)
.process('.')
2023-05-16 14:32:02 +03:00
assert.deepEqual(
2021-11-29 21:36:39 +03:00
file.messages.map((d) => JSON.parse(JSON.stringify(d))),
[
{
2023-11-09 16:45:02 +03:00
fatal: false,
message: 'Test message',
2023-11-09 16:45:02 +03:00
name: '1:1',
reason: 'Test message',
ruleId: 'rule',
2023-11-09 16:45:02 +03:00
source: 'test'
}
],
'should support string meta'
)
2018-05-18 13:30:21 +03:00
})
2015-06-02 09:34:14 +03:00
2021-08-11 14:05:30 +03:00
test('rules', async (t) => {
const root = path.join(process.cwd(), 'packages')
const all = rules(root)
let index = -1
while (++index < all.length) {
const basename = all[index]
const base = path.resolve(root, basename)
const info = rule(base)
2021-08-12 15:01:59 +03:00
const href = url.pathToFileURL(base).href + '/index.js'
/** @type {{default: Plugin}} */
2021-11-29 21:36:39 +03:00
const pluginMod = await import(href)
const fn = pluginMod.default
2021-08-11 14:05:30 +03:00
if (Object.keys(info.tests).length === 0) {
2023-05-16 14:32:02 +03:00
assert.ok(true, info.ruleId + ': no tests')
2021-08-11 14:05:30 +03:00
} else {
2023-05-16 14:32:02 +03:00
await t.test(info.ruleId, async () => {
const tests = info.tests
/** @type {string} */
let configuration
2021-08-11 14:05:30 +03:00
2023-05-16 14:32:02 +03:00
for (configuration in tests) {
if (own.call(tests, configuration)) {
const checks = tests[configuration]
/** @type {{config: unknown}} */
const {config} = JSON.parse(configuration)
2015-06-02 09:34:14 +03:00
2023-05-16 14:32:02 +03:00
/** @type {string} */
let name
2021-08-11 14:05:30 +03:00
2023-05-16 14:32:02 +03:00
for (name in checks) {
if (own.call(checks, name)) {
const basename = name
const check = checks[name]
2021-08-11 14:05:30 +03:00
2023-05-16 14:32:02 +03:00
await assertFixture(fn, info, check, basename, config)
}
}
2021-08-11 14:05:30 +03:00
}
}
2018-05-18 13:30:21 +03:00
})
2021-08-11 14:05:30 +03:00
}
}
2023-05-16 14:32:02 +03:00
})
2015-06-02 09:34:14 +03:00
2021-08-12 15:01:59 +03:00
/**
* @param {Plugin} rule
* @param {Rule} info
* @param {Check} fixture
* @param {string} basename
* @param {unknown} config
2021-08-12 15:01:59 +03:00
*/
2019-06-19 16:59:33 +03:00
/* eslint-disable-next-line max-params */
2023-05-16 14:32:02 +03:00
function assertFixture(rule, info, fixture, basename, config) {
2021-08-11 14:05:30 +03:00
const ruleId = info.ruleId
const file = toVFile(basename)
const expected = fixture.output
const positionless = fixture.positionless
2023-11-09 16:45:02 +03:00
// @ts-expect-error: to do: fix types.
let proc = remark().use(rule, config)
2021-08-10 21:28:49 +03:00
if (fixture.gfm) proc.use(remarkGfm)
2015-06-02 09:34:14 +03:00
2021-08-10 21:28:49 +03:00
file.value = preprocess(fixture.input || '')
2015-06-02 09:34:14 +03:00
try {
2018-05-18 13:30:21 +03:00
proc.runSync(proc.parse(file), file)
2018-10-23 10:40:18 +03:00
} catch (error) {
const exception = /** @type VFileMessage */ (error)
if (exception && exception.source !== 'remark-lint') {
throw exception
}
}
2021-08-11 14:05:30 +03:00
let index = -1
while (++index < file.messages.length) {
const message = file.messages[index]
if (message.ruleId !== ruleId) {
throw new Error(
2018-05-18 13:30:21 +03:00
'Expected `' +
ruleId +
'`, not `' +
message.ruleId +
'` as `ruleId` for ' +
message
)
2015-06-02 09:34:14 +03:00
}
const expectedUrl =
'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-' +
ruleId +
'#readme'
if (message.url !== expectedUrl) {
throw new Error(
'Expected `' +
expectedUrl +
'`, not `' +
message.url +
'` as `ruleId` for ' +
message
)
}
2021-08-11 14:05:30 +03:00
}
2018-05-18 13:30:21 +03:00
2023-05-16 14:32:02 +03:00
assert.deepEqual(
normalize(file.messages),
expected,
'should equal with position'
)
2015-06-02 09:34:14 +03:00
if (!positionless) {
2018-05-18 13:30:21 +03:00
file.messages = []
2021-08-12 15:01:59 +03:00
proc = remark()
.use(() => (tree) => removePosition(tree))
2023-11-09 16:45:02 +03:00
// @ts-expect-error: to do: fix types.
.use(rule, config)
2021-08-10 21:28:49 +03:00
if (fixture.gfm) proc.use(remarkGfm)
proc.processSync(file)
2015-06-02 09:34:14 +03:00
2023-05-16 14:32:02 +03:00
assert.deepEqual(
normalize(file.messages),
[],
'should equal without position'
)
}
2015-06-02 09:34:14 +03:00
}
2021-08-12 15:01:59 +03:00
/**
2021-11-25 10:48:30 +03:00
* @param {Array<VFileMessage>} messages
* @returns {Array<string>}
2021-08-12 15:01:59 +03:00
*/
function normalize(messages) {
2021-08-12 15:01:59 +03:00
return asStrings(messages).map((value) => value.slice(value.indexOf(':') + 1))
}
/**
2021-11-25 10:48:30 +03:00
* @param {Array<VFileMessage>} messages
* @returns {Array<string>}
2021-08-12 15:01:59 +03:00
*/
function asStrings(messages) {
2022-09-09 21:17:08 +03:00
return messages.map(String)
}
2015-06-02 09:34:14 +03:00
2021-08-12 15:01:59 +03:00
/**
* @param {string} value
* @returns {string}
*/
function preprocess(value) {
2021-08-11 14:05:30 +03:00
let index = -1
while (++index < characters.length) {
value = value.replace(characters[index].in, characters[index].out)
}
2018-05-18 13:30:21 +03:00
return value
}