remark-lint/test.js

375 lines
10 KiB
JavaScript
Raw Normal View History

2021-08-12 15:01:59 +03:00
/**
2023-11-11 19:47:26 +03:00
* @typedef {import('unified').PluggableList} PluggableList
* @typedef {import('unified').Plugin<[unknown]>} Plugin
*
* @typedef {import('./script/info.js').Check} Check
* @typedef {import('./script/info.js').PluginInfo} PluginInfo
2021-08-12 15:01:59 +03:00
*/
2023-05-16 14:32:02 +03:00
import assert from 'node:assert/strict'
import test from 'node:test'
2023-12-13 18:54:37 +03:00
import {controlPictures} from 'control-pictures'
2021-08-10 21:28:49 +03:00
import {remark} from 'remark'
import remarkGfm from 'remark-gfm'
2023-11-11 19:47:26 +03:00
import remarkLint from 'remark-lint'
import remarkLintFinalNewline from 'remark-lint-final-newline'
import remarkLintNoHeadingPunctuation from 'remark-lint-no-heading-punctuation'
import remarkLintNoMultipleToplevelHeadings from 'remark-lint-no-multiple-toplevel-headings'
import remarkLintNoUndefinedReferences from 'remark-lint-no-undefined-references'
import remarkMdx from 'remark-mdx'
import {lintRule} from 'unified-lint-rule'
2023-11-11 19:47:26 +03:00
import {removePosition} from 'unist-util-remove-position'
import {VFile} from 'vfile'
import {plugins} from './script/info.js'
2018-05-18 13:30:21 +03:00
2023-11-11 19:47:26 +03:00
test('remark-lint', async function (t) {
await t.test('should expose the public api', async function () {
assert.deepEqual(Object.keys(await import('remark-lint')).sort(), [
'default'
])
})
2021-08-11 14:05:30 +03:00
2021-08-12 15:01:59 +03:00
const doc = [
'# A heading',
'',
'# Another main heading.',
'',
'<!--lint ignore-->',
'',
'# Another main heading.'
].join('\n')
2023-11-11 19:47:26 +03:00
await t.test('should support `remark-lint` last', async function () {
const file = await remark()
.use(remarkLintNoHeadingPunctuation)
.use(remarkLintNoMultipleToplevelHeadings)
.use(remarkLint)
.process({path: 'virtual.md', value: doc})
2021-11-29 21:36:39 +03:00
2023-11-11 19:47:26 +03:00
assert.deepEqual(file.messages.map(String), [
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)'
2023-11-11 19:47:26 +03:00
])
})
2023-11-11 19:47:26 +03:00
await t.test('should support `remark-lint` first', async function () {
const file = await remark()
.use(remarkLint)
.use(remarkLintNoHeadingPunctuation)
.use(remarkLintNoMultipleToplevelHeadings)
.process({path: 'virtual.md', value: doc})
2021-11-29 21:36:39 +03:00
2023-11-11 19:47:26 +03:00
assert.deepEqual(file.messages.map(String), [
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)'
2023-11-11 19:47:26 +03:00
])
})
2023-11-11 19:47:26 +03:00
await t.test('should support no rules', async function () {
const file = await remark().use(remarkLint).process('.')
2023-11-11 19:47:26 +03:00
assert.deepEqual(file.messages, [])
})
2021-11-29 21:36:39 +03:00
2023-11-11 19:47:26 +03:00
await t.test('should support successful rules', async function () {
const file = await remark().use(remarkLintFinalNewline).process('')
2021-11-29 21:36:39 +03:00
2023-11-11 19:47:26 +03:00
assert.deepEqual(file.messages, [])
})
2021-11-29 21:36:39 +03:00
2023-11-11 19:47:26 +03:00
await t.test('should support a list with a severity', async function () {
const file = await remark().use(remarkLintFinalNewline, [2]).process('.')
2023-11-11 19:47:26 +03:00
assert.deepEqual(file.messages.map(jsonClone), [
2021-08-12 15:01:59 +03:00
{
column: 2,
2023-11-09 16:45:02 +03:00
fatal: true,
line: 1,
2021-08-12 15:01:59 +03:00
message: 'Missing newline character at end of file',
name: '1:2',
place: {column: 2, line: 1, offset: 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
}
2023-11-11 19:47:26 +03:00
])
})
2021-08-12 15:01:59 +03:00
2023-11-11 19:47:26 +03:00
await t.test('should support a boolean (`true`)', async function () {
const file = await remark().use(remarkLintFinalNewline, true).process('.')
2021-11-29 21:36:39 +03:00
2023-11-11 19:47:26 +03:00
assert.deepEqual(file.messages.map(String), [
'1:2: Missing newline character at end of file'
2023-11-11 19:47:26 +03:00
])
})
2021-08-12 15:01:59 +03:00
2023-11-11 19:47:26 +03:00
await t.test('should support a boolean (`false`)', async function () {
const file = await remark().use(remarkLintFinalNewline, false).process('.')
2021-11-29 21:36:39 +03:00
2023-11-11 19:47:26 +03:00
assert.deepEqual(file.messages, [])
})
2021-08-12 15:01:59 +03:00
2023-11-11 19:47:26 +03:00
await t.test(
'should support a list with a boolean severity (true, for on)',
async function () {
const file = await remark()
.use(remarkLintFinalNewline, [true])
.process('.')
2021-11-29 21:36:39 +03:00
2023-11-11 19:47:26 +03:00
assert.deepEqual(file.messages.map(String), [
'1:2: Missing newline character at end of file'
2023-11-11 19:47:26 +03:00
])
}
2021-08-12 15:01:59 +03:00
)
2023-11-11 19:47:26 +03:00
await t.test(
'should support a list with boolean severity (false, for off)',
async function () {
const file = await remark()
.use(remarkLintFinalNewline, [false])
.process('.')
2021-11-29 21:36:39 +03:00
2023-11-11 19:47:26 +03:00
assert.deepEqual(file.messages, [])
}
2021-08-12 15:01:59 +03:00
)
2023-11-11 19:47:26 +03:00
await t.test(
'should support a list with string severity (`error`)',
async function () {
const file = await remark()
.use(remarkLintFinalNewline, ['error'])
.process('.')
assert.deepEqual(file.messages.map(jsonClone), [
{
column: 2,
2023-11-11 19:47:26 +03:00
fatal: true,
line: 1,
2023-11-11 19:47:26 +03:00
message: 'Missing newline character at end of file',
name: '1:2',
place: {column: 2, line: 1, offset: 1},
2023-11-11 19:47:26 +03:00
reason: 'Missing newline character at end of file',
ruleId: 'final-newline',
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
)
2023-11-11 19:47:26 +03:00
await t.test(
'should support a list with string severity (`on`)',
async function () {
const file = await remark()
.use(remarkLintFinalNewline, ['on'])
.process('.')
assert.deepEqual(file.messages.map(jsonClone), [
{
column: 2,
2023-11-11 19:47:26 +03:00
fatal: false,
line: 1,
2023-11-11 19:47:26 +03:00
message: 'Missing newline character at end of file',
name: '1:2',
place: {column: 2, line: 1, offset: 1},
2023-11-11 19:47:26 +03:00
reason: 'Missing newline character at end of file',
ruleId: 'final-newline',
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
)
2023-11-11 19:47:26 +03:00
await t.test(
'should support a list with string severity (`warn`)',
async function () {
const file = await remark()
.use(remarkLintFinalNewline, ['warn'])
.process('.')
assert.deepEqual(file.messages.map(jsonClone), [
{
column: 2,
2023-11-11 19:47:26 +03:00
fatal: false,
line: 1,
2023-11-11 19:47:26 +03:00
message: 'Missing newline character at end of file',
name: '1:2',
place: {column: 2, line: 1, offset: 1},
2023-11-11 19:47:26 +03:00
reason: 'Missing newline character at end of file',
ruleId: 'final-newline',
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
)
2023-11-11 19:47:26 +03:00
await t.test(
'should support a list with string severity (`off`)',
async function () {
const file = await remark()
.use(remarkLintFinalNewline, ['off'])
.process('.')
2021-11-29 21:36:39 +03:00
2023-11-11 19:47:26 +03:00
assert.deepEqual(file.messages, [])
}
2021-08-12 15:01:59 +03:00
)
2023-11-11 19:47:26 +03:00
await t.test(
'should fail on incorrect severities (too high)',
async function () {
assert.throws(function () {
remark().use(remarkLintFinalNewline, [3]).freeze()
}, /^Error: Incorrect severity `3` for `final-newline`, expected 0, 1, or 2$/)
}
2021-08-12 15:01:59 +03:00
)
2023-11-11 19:47:26 +03:00
await t.test(
'should fail on incorrect severities (too low)',
async function () {
assert.throws(function () {
remark().use(remarkLintFinalNewline, [-1]).freeze()
}, /^Error: Incorrect severity `-1` for `final-newline`, expected 0, 1, or 2$/)
}
2021-08-12 15:01:59 +03:00
)
2023-11-11 19:47:26 +03:00
await t.test(
'should support regex as options (remark-lint-no-undefined-references)',
async function () {
const file = await remark()
.use(remarkLintNoUndefinedReferences, {allow: [/^b\./i]})
.process({
path: 'virtual.md',
value: ['[foo][b.c]', '', '[bar][b]'].join('\n')
})
assert.deepEqual(file.messages.map(String), [
'virtual.md:3:1-3:9: Found reference to undefined definition'
])
}
)
2023-11-11 19:47:26 +03:00
await t.test(
'should support meta as a string (unified-lint-rule)',
async function () {
const file = await remark()
.use(
lintRule('test:rule', function (_, file) {
file.message('Test message')
}),
['warn']
)
.process('.')
assert.deepEqual(file.messages.map(jsonClone), [
{
fatal: false,
message: 'Test message',
name: '1:1',
reason: 'Test message',
ruleId: 'rule',
source: 'test'
}
])
}
)
2018-05-18 13:30:21 +03:00
})
2015-06-02 09:34:14 +03:00
2023-11-11 19:47:26 +03:00
test('plugins', async function (t) {
for (const plugin of plugins) {
await t.test(plugin.name, async function () {
await assertPlugin(plugin)
})
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
/**
2023-11-11 19:47:26 +03:00
* @param {PluginInfo} info
* Info.
* @returns {Promise<undefined>}
* Nothing.
2021-08-12 15:01:59 +03:00
*/
2023-11-11 19:47:26 +03:00
async function assertPlugin(info) {
/** @type {{default: Plugin}} */
const pluginMod = await import(info.name)
const plugin = pluginMod.default
for (const check of info.checks) {
await assertCheck(plugin, info, check)
}
2023-11-11 19:47:26 +03:00
}
2023-11-11 19:47:26 +03:00
/**
* @param {Plugin} plugin
* Plugin.
* @param {PluginInfo} info
* info.
* @param {Check} check
* Check.
* @returns {Promise<undefined>}
* Nothing.
*/
async function assertCheck(plugin, info, check) {
/** @type {{config: unknown}} */
const {config} = JSON.parse(check.configuration)
/** @type {PluggableList} */
const extras = []
2023-12-13 18:54:37 +03:00
const value = controlPictures(check.input)
if (check.gfm) {
extras.push(remarkGfm)
}
if (check.mdx) {
extras.push(remarkMdx)
}
2023-11-11 19:47:26 +03:00
const file = await remark()
.use(plugin, config)
.use(extras)
.process(new VFile({path: check.name, value}))
for (const message of file.messages) {
assert.equal(message.ruleId, info.ruleId)
assert.equal(
message.url,
'https://github.com/remarkjs/remark-lint/tree/main/packages/remark-lint-' +
2023-11-11 19:47:26 +03:00
info.ruleId +
'#readme'
)
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(
2023-11-11 19:47:26 +03:00
file.messages.map(String).map(function (value) {
return value.slice(value.indexOf(':') + 1)
}),
check.output
2023-05-16 14:32:02 +03:00
)
2015-06-02 09:34:14 +03:00
2023-11-11 19:47:26 +03:00
if (!check.positionless) {
const file = await remark()
.use(function () {
return function (tree) {
removePosition(tree)
}
})
.use(plugin, config)
.use(extras)
.process(new VFile({path: check.name, value}))
2021-08-12 15:01:59 +03:00
2023-11-11 19:47:26 +03:00
assert.deepEqual(file.messages, [])
}
}
2015-06-02 09:34:14 +03:00
2021-08-12 15:01:59 +03:00
/**
2023-11-11 19:47:26 +03:00
* @param {unknown} d
* Value.
* @returns {unknown}
* Cloned value.
2021-08-12 15:01:59 +03:00
*/
2023-11-11 19:47:26 +03:00
function jsonClone(d) {
return JSON.parse(JSON.stringify(d))
}