2020-09-12 12:11:14 -07:00
// @ts-check
2024-11-28 20:36:44 -08:00
import fs from "node:fs/promises" ;
import { createRequire } from "node:module" ;
const require = createRequire ( import . meta . url ) ;
import test from "ava" ;
2024-12-25 20:42:32 -08:00
import markdownIt from "markdown-it" ;
2024-12-03 19:58:28 -08:00
import { lint as lintAsync } from "markdownlint/async" ;
import { lint as lintPromise } from "markdownlint/promise" ;
import { lint as lintSync } from "markdownlint/sync" ;
2024-11-28 20:36:44 -08:00
import customRules from "./rules/rules.cjs" ;
import { newLineRe } from "../helpers/helpers.cjs" ;
import { _ _filename , importWithTypeJson } from "./esm-helpers.mjs" ;
const packageJson = await importWithTypeJson ( import . meta , "../package.json" ) ;
const { homepage , version } = packageJson ;
2020-09-12 12:11:14 -07:00
2024-12-25 20:42:32 -08:00
const markdownItFactory = ( ) => markdownIt ( { "html" : true } ) ;
2022-06-21 04:40:38 +00:00
test ( "customRulesV0" , ( t ) => new Promise ( ( resolve ) => {
2021-01-10 20:46:00 -08:00
t . plan ( 4 ) ;
2020-09-12 12:11:14 -07:00
const customRulesMd = "./test/custom-rules.md" ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2020-09-12 12:11:14 -07:00
const options = {
"customRules" : customRules . all ,
"files" : [ customRulesMd ] ,
2024-12-25 20:42:32 -08:00
markdownItFactory ,
2025-08-28 22:01:06 -07:00
// @ts-ignore
2020-09-12 12:11:14 -07:00
"resultVersion" : 0
} ;
2024-12-03 19:58:28 -08:00
lintAsync ( options , function callback ( err , actualResult ) {
2021-01-10 20:46:00 -08:00
t . falsy ( err ) ;
2020-09-12 12:11:14 -07:00
const expectedResult = { } ;
expectedResult [ customRulesMd ] = {
2024-10-01 22:41:10 -07:00
"any-blockquote-markdown-it" : [ 12 ] ,
"any-blockquote-micromark" : [ 12 ] ,
2020-09-12 12:11:14 -07:00
"every-n-lines" : [ 2 , 4 , 6 , 10 , 12 ] ,
"first-line" : [ 1 ] ,
"letters-E-X" : [ 3 , 7 ]
} ;
2021-01-10 20:46:00 -08:00
t . deepEqual ( actualResult , expectedResult , "Undetected issues." ) ;
2023-01-21 19:16:07 -08:00
// @ts-ignore
2020-09-12 12:11:14 -07:00
let actualMessage = actualResult . toString ( ) ;
let expectedMessage =
2024-10-01 22:41:10 -07:00
"./test/custom-rules.md: 12: any-blockquote-markdown-it" +
" Rule that reports an error for any blockquote\n" +
"./test/custom-rules.md: 12: any-blockquote-micromark" +
2020-09-12 12:11:14 -07:00
" Rule that reports an error for any blockquote\n" +
"./test/custom-rules.md: 2: every-n-lines" +
" Rule that reports an error every N lines\n" +
"./test/custom-rules.md: 4: every-n-lines" +
" Rule that reports an error every N lines\n" +
"./test/custom-rules.md: 6: every-n-lines" +
" Rule that reports an error every N lines\n" +
"./test/custom-rules.md: 10: every-n-lines" +
" Rule that reports an error every N lines\n" +
"./test/custom-rules.md: 12: every-n-lines" +
" Rule that reports an error every N lines\n" +
"./test/custom-rules.md: 1: first-line" +
" Rule that reports an error for the first line\n" +
"./test/custom-rules.md: 3: letters-E-X" +
" Rule that reports an error for lines with the letters 'EX'\n" +
"./test/custom-rules.md: 7: letters-E-X" +
" Rule that reports an error for lines with the letters 'EX'" ;
2021-01-10 20:46:00 -08:00
t . is ( actualMessage , expectedMessage , "Incorrect message (name)." ) ;
2020-09-12 12:11:14 -07:00
// @ts-ignore
actualMessage = actualResult . toString ( true ) ;
expectedMessage =
2024-10-01 22:41:10 -07:00
"./test/custom-rules.md: 12: any-blockquote-markdown-it" +
" Rule that reports an error for any blockquote\n" +
"./test/custom-rules.md: 12: any-blockquote-micromark" +
2020-09-12 12:11:14 -07:00
" Rule that reports an error for any blockquote\n" +
"./test/custom-rules.md: 2: every-n-lines" +
" Rule that reports an error every N lines\n" +
"./test/custom-rules.md: 4: every-n-lines" +
" Rule that reports an error every N lines\n" +
"./test/custom-rules.md: 6: every-n-lines" +
" Rule that reports an error every N lines\n" +
"./test/custom-rules.md: 10: every-n-lines" +
" Rule that reports an error every N lines\n" +
"./test/custom-rules.md: 12: every-n-lines" +
" Rule that reports an error every N lines\n" +
"./test/custom-rules.md: 1: first-line" +
" Rule that reports an error for the first line\n" +
"./test/custom-rules.md: 3: letter-E-letter-X" +
" Rule that reports an error for lines with the letters 'EX'\n" +
"./test/custom-rules.md: 7: letter-E-letter-X" +
" Rule that reports an error for lines with the letters 'EX'" ;
2021-01-10 20:46:00 -08:00
t . is ( actualMessage , expectedMessage , "Incorrect message (alias)." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2020-09-12 12:11:14 -07:00
2022-06-21 04:40:38 +00:00
test ( "customRulesV1" , ( t ) => new Promise ( ( resolve ) => {
2021-01-10 20:46:00 -08:00
t . plan ( 3 ) ;
2020-09-12 12:11:14 -07:00
const customRulesMd = "./test/custom-rules.md" ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2020-09-12 12:11:14 -07:00
const options = {
"customRules" : customRules . all ,
"files" : [ customRulesMd ] ,
2024-12-25 20:42:32 -08:00
markdownItFactory ,
2025-08-28 22:01:06 -07:00
// @ts-ignore
2020-09-12 12:11:14 -07:00
"resultVersion" : 1
} ;
2024-12-03 19:58:28 -08:00
lintAsync ( options , function callback ( err , actualResult ) {
2021-01-10 20:46:00 -08:00
t . falsy ( err ) ;
2020-09-12 12:11:14 -07:00
const expectedResult = { } ;
expectedResult [ customRulesMd ] = [
{ "lineNumber" : 12 ,
2024-10-01 22:41:10 -07:00
"ruleName" : "any-blockquote-markdown-it" ,
"ruleAlias" : "any-blockquote-markdown-it" ,
2020-09-12 12:11:14 -07:00
"ruleDescription" : "Rule that reports an error for any blockquote" ,
"ruleInformation" :
` ${ homepage } /blob/main/test/rules/any-blockquote.js ` ,
"errorDetail" : "Blockquote spans 1 line(s)." ,
2024-10-01 22:41:10 -07:00
"errorContext" : "> Blockquote" ,
"errorRange" : null } ,
{ "lineNumber" : 12 ,
"ruleName" : "any-blockquote-micromark" ,
"ruleAlias" : "any-blockquote-micromark" ,
"ruleDescription" : "Rule that reports an error for any blockquote" ,
"ruleInformation" :
` ${ homepage } /blob/main/test/rules/any-blockquote.js ` ,
"errorDetail" : "Blockquote spans 1 line(s)." ,
"errorContext" : "> Blockquote" ,
2020-09-12 12:11:14 -07:00
"errorRange" : null } ,
{ "lineNumber" : 2 ,
"ruleName" : "every-n-lines" ,
"ruleAlias" : "every-n-lines" ,
"ruleDescription" : "Rule that reports an error every N lines" ,
"ruleInformation" : null ,
"errorDetail" : "Line number 2" ,
"errorContext" : null ,
"errorRange" : null } ,
{ "lineNumber" : 4 ,
"ruleName" : "every-n-lines" ,
"ruleAlias" : "every-n-lines" ,
"ruleDescription" : "Rule that reports an error every N lines" ,
"ruleInformation" : null ,
"errorDetail" : "Line number 4" ,
"errorContext" : null ,
"errorRange" : null } ,
{ "lineNumber" : 6 ,
"ruleName" : "every-n-lines" ,
"ruleAlias" : "every-n-lines" ,
"ruleDescription" : "Rule that reports an error every N lines" ,
"ruleInformation" : null ,
"errorDetail" : "Line number 6" ,
"errorContext" : null ,
"errorRange" : null } ,
{ "lineNumber" : 10 ,
"ruleName" : "every-n-lines" ,
"ruleAlias" : "every-n-lines" ,
"ruleDescription" : "Rule that reports an error every N lines" ,
"ruleInformation" : null ,
"errorDetail" : "Line number 10" ,
"errorContext" : null ,
"errorRange" : null } ,
{ "lineNumber" : 12 ,
"ruleName" : "every-n-lines" ,
"ruleAlias" : "every-n-lines" ,
"ruleDescription" : "Rule that reports an error every N lines" ,
"ruleInformation" : null ,
"errorDetail" : "Line number 12" ,
"errorContext" : null ,
"errorRange" : null } ,
{ "lineNumber" : 1 ,
"ruleName" : "first-line" ,
"ruleAlias" : "first-line" ,
"ruleDescription" : "Rule that reports an error for the first line" ,
"ruleInformation" : null ,
"errorDetail" : null ,
"errorContext" : null ,
"errorRange" : null } ,
{ "lineNumber" : 3 ,
"ruleName" : "letters-E-X" ,
"ruleAlias" : "letter-E-letter-X" ,
"ruleDescription" :
"Rule that reports an error for lines with the letters 'EX'" ,
"ruleInformation" : ` ${ homepage } /blob/main/test/rules/letters-E-X.js ` ,
"errorDetail" : null ,
"errorContext" : "text" ,
"errorRange" : null } ,
{ "lineNumber" : 7 ,
"ruleName" : "letters-E-X" ,
"ruleAlias" : "letter-E-letter-X" ,
"ruleDescription" :
"Rule that reports an error for lines with the letters 'EX'" ,
"ruleInformation" : ` ${ homepage } /blob/main/test/rules/letters-E-X.js ` ,
"errorDetail" : null ,
"errorContext" : "text" ,
"errorRange" : null }
] ;
2021-01-10 20:46:00 -08:00
t . deepEqual ( actualResult , expectedResult , "Undetected issues." ) ;
2023-01-21 19:16:07 -08:00
// @ts-ignore
2020-09-12 12:11:14 -07:00
const actualMessage = actualResult . toString ( ) ;
const expectedMessage =
2024-10-01 22:41:10 -07:00
"./test/custom-rules.md: 12: any-blockquote-markdown-it/any-blockquote-markdown-it" +
" Rule that reports an error for any blockquote" +
" [Blockquote spans 1 line(s).] [Context: \"> Blockquote\"]\n" +
"./test/custom-rules.md: 12: any-blockquote-micromark/any-blockquote-micromark" +
2020-09-12 12:11:14 -07:00
" Rule that reports an error for any blockquote" +
2024-10-01 22:41:10 -07:00
" [Blockquote spans 1 line(s).] [Context: \"> Blockquote\"]\n" +
2020-09-12 12:11:14 -07:00
"./test/custom-rules.md: 2: every-n-lines/every-n-lines" +
" Rule that reports an error every N lines [Line number 2]\n" +
"./test/custom-rules.md: 4: every-n-lines/every-n-lines" +
" Rule that reports an error every N lines [Line number 4]\n" +
"./test/custom-rules.md: 6: every-n-lines/every-n-lines" +
" Rule that reports an error every N lines [Line number 6]\n" +
"./test/custom-rules.md: 10: every-n-lines/every-n-lines" +
" Rule that reports an error every N lines [Line number 10]\n" +
"./test/custom-rules.md: 12: every-n-lines/every-n-lines" +
" Rule that reports an error every N lines [Line number 12]\n" +
"./test/custom-rules.md: 1: first-line/first-line" +
" Rule that reports an error for the first line\n" +
"./test/custom-rules.md: 3: letters-E-X/letter-E-letter-X" +
" Rule that reports an error for lines with the letters 'EX'" +
" [Context: \"text\"]\n" +
"./test/custom-rules.md: 7: letters-E-X/letter-E-letter-X" +
" Rule that reports an error for lines with the letters 'EX'" +
" [Context: \"text\"]" ;
2021-01-10 20:46:00 -08:00
t . is ( actualMessage , expectedMessage , "Incorrect message." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2020-09-12 12:11:14 -07:00
2022-06-21 04:40:38 +00:00
test ( "customRulesV2" , ( t ) => new Promise ( ( resolve ) => {
2021-01-10 20:46:00 -08:00
t . plan ( 3 ) ;
2020-09-12 12:11:14 -07:00
const customRulesMd = "./test/custom-rules.md" ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2020-09-12 12:11:14 -07:00
const options = {
"customRules" : customRules . all ,
"files" : [ customRulesMd ] ,
2024-12-25 20:42:32 -08:00
markdownItFactory ,
2025-08-28 22:01:06 -07:00
// @ts-ignore
2020-09-12 12:11:14 -07:00
"resultVersion" : 2
} ;
2024-12-03 19:58:28 -08:00
lintAsync ( options , function callback ( err , actualResult ) {
2021-01-10 20:46:00 -08:00
t . falsy ( err ) ;
2020-09-12 12:11:14 -07:00
const expectedResult = { } ;
expectedResult [ customRulesMd ] = [
{ "lineNumber" : 12 ,
2024-10-01 22:41:10 -07:00
"ruleNames" : [ "any-blockquote-markdown-it" ] ,
"ruleDescription" : "Rule that reports an error for any blockquote" ,
"ruleInformation" :
` ${ homepage } /blob/main/test/rules/any-blockquote.js ` ,
"errorDetail" : "Blockquote spans 1 line(s)." ,
"errorContext" : "> Blockquote" ,
"errorRange" : null } ,
{ "lineNumber" : 12 ,
"ruleNames" : [ "any-blockquote-micromark" ] ,
2020-09-12 12:11:14 -07:00
"ruleDescription" : "Rule that reports an error for any blockquote" ,
"ruleInformation" :
` ${ homepage } /blob/main/test/rules/any-blockquote.js ` ,
"errorDetail" : "Blockquote spans 1 line(s)." ,
2024-10-01 22:41:10 -07:00
"errorContext" : "> Blockquote" ,
2020-09-12 12:11:14 -07:00
"errorRange" : null } ,
{ "lineNumber" : 2 ,
"ruleNames" : [ "every-n-lines" ] ,
"ruleDescription" : "Rule that reports an error every N lines" ,
"ruleInformation" : null ,
"errorDetail" : "Line number 2" ,
"errorContext" : null ,
"errorRange" : null } ,
{ "lineNumber" : 4 ,
"ruleNames" : [ "every-n-lines" ] ,
"ruleDescription" : "Rule that reports an error every N lines" ,
"ruleInformation" : null ,
"errorDetail" : "Line number 4" ,
"errorContext" : null ,
"errorRange" : null } ,
{ "lineNumber" : 6 ,
"ruleNames" : [ "every-n-lines" ] ,
"ruleDescription" : "Rule that reports an error every N lines" ,
"ruleInformation" : null ,
"errorDetail" : "Line number 6" ,
"errorContext" : null ,
"errorRange" : null } ,
{ "lineNumber" : 10 ,
"ruleNames" : [ "every-n-lines" ] ,
"ruleDescription" : "Rule that reports an error every N lines" ,
"ruleInformation" : null ,
"errorDetail" : "Line number 10" ,
"errorContext" : null ,
"errorRange" : null } ,
{ "lineNumber" : 12 ,
"ruleNames" : [ "every-n-lines" ] ,
"ruleDescription" : "Rule that reports an error every N lines" ,
"ruleInformation" : null ,
"errorDetail" : "Line number 12" ,
"errorContext" : null ,
"errorRange" : null } ,
{ "lineNumber" : 1 ,
"ruleNames" : [ "first-line" ] ,
"ruleDescription" : "Rule that reports an error for the first line" ,
"ruleInformation" : null ,
"errorDetail" : null ,
"errorContext" : null ,
"errorRange" : null } ,
{ "lineNumber" : 3 ,
"ruleNames" : [ "letters-E-X" , "letter-E-letter-X" , "contains-ex" ] ,
"ruleDescription" :
"Rule that reports an error for lines with the letters 'EX'" ,
"ruleInformation" : ` ${ homepage } /blob/main/test/rules/letters-E-X.js ` ,
"errorDetail" : null ,
"errorContext" : "text" ,
"errorRange" : null } ,
{ "lineNumber" : 7 ,
"ruleNames" : [ "letters-E-X" , "letter-E-letter-X" , "contains-ex" ] ,
"ruleDescription" :
"Rule that reports an error for lines with the letters 'EX'" ,
"ruleInformation" : ` ${ homepage } /blob/main/test/rules/letters-E-X.js ` ,
"errorDetail" : null ,
"errorContext" : "text" ,
"errorRange" : null }
] ;
2021-01-10 20:46:00 -08:00
t . deepEqual ( actualResult , expectedResult , "Undetected issues." ) ;
2023-01-21 19:16:07 -08:00
// @ts-ignore
2020-09-12 12:11:14 -07:00
const actualMessage = actualResult . toString ( ) ;
const expectedMessage =
2024-10-01 22:41:10 -07:00
"./test/custom-rules.md: 12: any-blockquote-markdown-it" +
" Rule that reports an error for any blockquote" +
" [Blockquote spans 1 line(s).] [Context: \"> Blockquote\"]\n" +
"./test/custom-rules.md: 12: any-blockquote-micromark" +
2020-09-12 12:11:14 -07:00
" Rule that reports an error for any blockquote" +
2024-10-01 22:41:10 -07:00
" [Blockquote spans 1 line(s).] [Context: \"> Blockquote\"]\n" +
2020-09-12 12:11:14 -07:00
"./test/custom-rules.md: 2: every-n-lines" +
" Rule that reports an error every N lines [Line number 2]\n" +
"./test/custom-rules.md: 4: every-n-lines" +
" Rule that reports an error every N lines [Line number 4]\n" +
"./test/custom-rules.md: 6: every-n-lines" +
" Rule that reports an error every N lines [Line number 6]\n" +
"./test/custom-rules.md: 10: every-n-lines" +
" Rule that reports an error every N lines [Line number 10]\n" +
"./test/custom-rules.md: 12: every-n-lines" +
" Rule that reports an error every N lines [Line number 12]\n" +
"./test/custom-rules.md: 1: first-line" +
" Rule that reports an error for the first line\n" +
"./test/custom-rules.md: 3: letters-E-X/letter-E-letter-X/contains-ex" +
" Rule that reports an error for lines with the letters 'EX'" +
" [Context: \"text\"]\n" +
"./test/custom-rules.md: 7: letters-E-X/letter-E-letter-X/contains-ex" +
" Rule that reports an error for lines with the letters 'EX'" +
" [Context: \"text\"]" ;
2021-01-10 20:46:00 -08:00
t . is ( actualMessage , expectedMessage , "Incorrect message." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2020-09-12 12:11:14 -07:00
2022-06-21 04:40:38 +00:00
test ( "customRulesConfig" , ( t ) => new Promise ( ( resolve ) => {
2021-01-10 20:46:00 -08:00
t . plan ( 2 ) ;
2020-09-12 12:11:14 -07:00
const customRulesMd = "./test/custom-rules.md" ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2020-09-12 12:11:14 -07:00
const options = {
"customRules" : customRules . all ,
"files" : [ customRulesMd ] ,
"config" : {
"blockquote" : true ,
"every-n-lines" : {
"n" : 3
} ,
"letters-e-x" : false
} ,
2024-12-25 20:42:32 -08:00
markdownItFactory ,
2025-08-28 22:01:06 -07:00
// @ts-ignore
2020-09-12 12:11:14 -07:00
"resultVersion" : 0
} ;
2024-12-03 19:58:28 -08:00
lintAsync ( options , function callback ( err , actualResult ) {
2021-01-10 20:46:00 -08:00
t . falsy ( err ) ;
2020-09-12 12:11:14 -07:00
const expectedResult = { } ;
expectedResult [ customRulesMd ] = {
2024-10-01 22:41:10 -07:00
"any-blockquote-markdown-it" : [ 12 ] ,
"any-blockquote-micromark" : [ 12 ] ,
2020-09-12 12:11:14 -07:00
"every-n-lines" : [ 3 , 6 , 12 ] ,
"first-line" : [ 1 ] ,
"letters-E-X" : [ 7 ]
} ;
2021-01-10 20:46:00 -08:00
t . deepEqual ( actualResult , expectedResult , "Undetected issues." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2020-09-12 12:11:14 -07:00
2022-06-21 04:40:38 +00:00
test ( "customRulesNpmPackage" , ( t ) => new Promise ( ( resolve ) => {
2021-01-10 20:46:00 -08:00
t . plan ( 2 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2020-09-12 12:11:14 -07:00
const options = {
2024-05-13 22:57:47 -07:00
"customRules" : [
require ( "./rules/npm" ) ,
require ( "markdownlint-rule-extended-ascii" )
] ,
2024-12-25 20:42:32 -08:00
markdownItFactory ,
2020-09-12 12:11:14 -07:00
"strings" : {
2024-05-13 22:57:47 -07:00
"string" : "# Text\n\n---\n\nText ✅\n"
2020-09-12 12:11:14 -07:00
} ,
2025-08-28 22:01:06 -07:00
// @ts-ignore
2020-09-12 12:11:14 -07:00
"resultVersion" : 0
} ;
2024-12-03 19:58:28 -08:00
lintAsync ( options , function callback ( err , actualResult ) {
2021-01-10 20:46:00 -08:00
t . falsy ( err ) ;
2020-09-12 12:11:14 -07:00
const expectedResult = { } ;
expectedResult . string = {
2024-05-13 22:57:47 -07:00
"extended-ascii" : [ 5 ] ,
2020-09-12 12:11:14 -07:00
"sample-rule" : [ 3 ]
} ;
2021-01-10 20:46:00 -08:00
// @ts-ignore
t . deepEqual ( actualResult , expectedResult , "Undetected issues." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2020-09-12 12:11:14 -07:00
2021-01-10 20:46:00 -08:00
test ( "customRulesBadProperty" , ( t ) => {
2024-03-09 16:17:50 -08:00
t . plan ( 30 ) ;
2022-06-08 22:10:27 -07:00
for ( const testCase of [
2020-09-12 12:11:14 -07:00
{
"propertyName" : "names" ,
"propertyValues" :
[ null , "string" , [ ] , [ null ] , [ "" ] , [ "string" , 10 ] ]
} ,
{
"propertyName" : "description" ,
"propertyValues" : [ null , 10 , "" , [ ] ]
} ,
{
"propertyName" : "information" ,
"propertyValues" : [ 10 , [ ] , "string" , "https://example.com" ]
} ,
2021-12-11 21:44:25 -08:00
{
"propertyName" : "asynchronous" ,
"propertyValues" : [ null , 10 , "" , [ ] ]
} ,
2020-09-12 12:11:14 -07:00
{
"propertyName" : "tags" ,
"propertyValues" :
[ null , "string" , [ ] , [ null ] , [ "" ] , [ "string" , 10 ] ]
} ,
2024-03-09 16:17:50 -08:00
{
"propertyName" : "parser" ,
"propertyValues" :
[ 10 , "string" , [ ] ]
} ,
2020-09-12 12:11:14 -07:00
{
"propertyName" : "function" ,
"propertyValues" : [ null , "string" , [ ] ]
}
2022-06-08 22:10:27 -07:00
] ) {
2020-09-12 12:11:14 -07:00
const { propertyName , propertyValues } = testCase ;
2022-06-08 22:10:27 -07:00
for ( const propertyValue of propertyValues ) {
2024-10-01 22:41:10 -07:00
const badRule = { ... customRules . firstLine } ;
2020-09-12 12:11:14 -07:00
badRule [ propertyName ] = propertyValue ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2020-09-12 12:11:14 -07:00
const options = {
"customRules" : [ badRule ]
} ;
2021-01-10 20:46:00 -08:00
t . throws (
2020-09-12 12:11:14 -07:00
function badRuleCall ( ) {
2024-12-03 19:58:28 -08:00
lintSync ( options ) ;
2020-09-12 12:11:14 -07:00
} ,
2021-01-10 20:46:00 -08:00
{
"message" :
2024-03-09 16:17:50 -08:00
` Property ' ${ propertyName } ' of custom rule at index 0 is incorrect: ' ${ propertyValue } '. `
2021-01-10 20:46:00 -08:00
} ,
2024-03-09 16:17:50 -08:00
` Did not get correct exception for property ' ${ propertyName } ' value ' ${ propertyValue } '. `
2020-09-12 12:11:14 -07:00
) ;
2022-06-08 22:10:27 -07:00
}
}
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
test ( "customRulesUsedNameName" , ( t ) => new Promise ( ( resolve ) => {
2021-01-10 20:46:00 -08:00
t . plan ( 4 ) ;
2024-12-03 19:58:28 -08:00
lintAsync ( {
/** @type {import("markdownlint").Rule[]} */
2020-09-12 12:11:14 -07:00
"customRules" : [
{
"names" : [ "name" , "NO-missing-SPACE-atx" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2020-09-12 12:11:14 -07:00
"function" : function noop ( ) { }
}
]
} , function callback ( err , result ) {
2021-01-10 20:46:00 -08:00
t . truthy ( err , "Did not get an error for duplicate name." ) ;
t . true ( err instanceof Error , "Error not instance of Error." ) ;
2023-01-21 19:16:07 -08:00
// @ts-ignore
2021-01-10 20:46:00 -08:00
t . is ( err . message ,
2020-09-12 12:11:14 -07:00
"Name 'NO-missing-SPACE-atx' of custom rule at index 0 is " +
"already used as a name or tag." ,
"Incorrect message for duplicate name." ) ;
2021-01-10 20:46:00 -08:00
t . true ( ! result , "Got result for duplicate name." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2020-09-12 12:11:14 -07:00
2022-06-21 04:40:38 +00:00
test ( "customRulesUsedNameTag" , ( t ) => new Promise ( ( resolve ) => {
2021-01-10 20:46:00 -08:00
t . plan ( 4 ) ;
2024-12-03 19:58:28 -08:00
lintAsync ( {
/** @type {import("markdownlint").Rule[]} */
2020-09-12 12:11:14 -07:00
"customRules" : [
{
"names" : [ "name" , "HtMl" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2020-09-12 12:11:14 -07:00
"function" : function noop ( ) { }
}
]
} , function callback ( err , result ) {
2021-01-10 20:46:00 -08:00
t . truthy ( err , "Did not get an error for duplicate name." ) ;
t . true ( err instanceof Error , "Error not instance of Error." ) ;
2023-01-21 19:16:07 -08:00
// @ts-ignore
2021-01-10 20:46:00 -08:00
t . is ( err . message ,
2020-09-12 12:11:14 -07:00
"Name 'HtMl' of custom rule at index 0 is already used as a name or tag." ,
"Incorrect message for duplicate name." ) ;
2021-01-10 20:46:00 -08:00
t . true ( ! result , "Got result for duplicate name." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2020-09-12 12:11:14 -07:00
2022-06-21 04:40:38 +00:00
test ( "customRulesUsedTagName" , ( t ) => new Promise ( ( resolve ) => {
2021-01-10 20:46:00 -08:00
t . plan ( 4 ) ;
2024-12-03 19:58:28 -08:00
lintAsync ( {
/** @type {import("markdownlint").Rule[]} */
2020-09-12 12:11:14 -07:00
"customRules" : [
{
"names" : [ "filler" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2020-09-12 12:11:14 -07:00
"function" : function noop ( ) { }
} ,
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" , "NO-missing-SPACE-atx" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2020-09-12 12:11:14 -07:00
"function" : function noop ( ) { }
}
]
} , function callback ( err , result ) {
2021-01-10 20:46:00 -08:00
t . truthy ( err , "Did not get an error for duplicate tag." ) ;
t . true ( err instanceof Error , "Error not instance of Error." ) ;
2023-01-21 19:16:07 -08:00
// @ts-ignore
2021-01-10 20:46:00 -08:00
t . is ( err . message ,
2020-09-12 12:11:14 -07:00
"Tag 'NO-missing-SPACE-atx' of custom rule at index 1 is " +
"already used as a name." ,
"Incorrect message for duplicate name." ) ;
2021-01-10 20:46:00 -08:00
t . true ( ! result , "Got result for duplicate tag." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2020-09-12 12:11:14 -07:00
2024-03-09 16:17:50 -08:00
test ( "customRulesParserUndefined" , ( t ) => {
t . plan ( 5 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2024-03-09 16:17:50 -08:00
const options = {
"customRules" : [
// @ts-ignore
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"function" :
( params ) => {
t . true ( Object . keys ( params ) . includes ( "tokens" ) ) ;
t . is ( Object . keys ( params . parsers ) . length , 1 ) ;
t . truthy ( params . parsers . markdownit ) ;
t . is ( Object . keys ( params . parsers . markdownit ) . length , 1 ) ;
t . truthy ( params . parsers . markdownit . tokens ) ;
}
}
] ,
2024-12-25 20:42:32 -08:00
markdownItFactory ,
2024-03-09 16:17:50 -08:00
"strings" : {
"string" : "# Heading\n"
}
} ;
2024-12-25 20:42:32 -08:00
return lintPromise ( options ) ;
2024-03-09 16:17:50 -08:00
} ) ;
test ( "customRulesParserNone" , ( t ) => {
t . plan ( 2 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2024-03-09 16:17:50 -08:00
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"parser" : "none" ,
"function" :
( params ) => {
t . false ( Object . keys ( params ) . includes ( "tokens" ) ) ;
t . is ( Object . keys ( params . parsers ) . length , 0 ) ;
}
}
] ,
"strings" : {
"string" : "# Heading\n"
}
} ;
2024-12-25 20:42:32 -08:00
return lintPromise ( options ) ;
2024-03-09 16:17:50 -08:00
} ) ;
test ( "customRulesParserMarkdownIt" , ( t ) => {
t . plan ( 5 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2024-03-09 16:17:50 -08:00
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"parser" : "markdownit" ,
"function" :
( params ) => {
t . false ( Object . keys ( params ) . includes ( "tokens" ) ) ;
t . is ( Object . keys ( params . parsers ) . length , 1 ) ;
t . truthy ( params . parsers . markdownit ) ;
t . is ( Object . keys ( params . parsers . markdownit ) . length , 1 ) ;
t . truthy ( params . parsers . markdownit . tokens ) ;
}
}
] ,
2024-12-25 20:42:32 -08:00
markdownItFactory ,
2024-03-09 16:17:50 -08:00
"strings" : {
"string" : "# Heading\n"
}
} ;
2024-12-25 20:42:32 -08:00
return lintPromise ( options ) ;
2024-03-09 16:17:50 -08:00
} ) ;
test ( "customRulesParserMicromark" , ( t ) => {
2024-10-01 22:41:10 -07:00
t . plan ( 5 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2024-03-09 16:17:50 -08:00
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"parser" : "micromark" ,
"function" :
2024-10-01 22:41:10 -07:00
( params ) => {
t . false ( Object . keys ( params ) . includes ( "tokens" ) ) ;
t . is ( Object . keys ( params . parsers ) . length , 1 ) ;
t . truthy ( params . parsers . micromark ) ;
t . is ( Object . keys ( params . parsers . micromark ) . length , 1 ) ;
t . truthy ( params . parsers . micromark . tokens ) ;
2024-03-09 16:17:50 -08:00
}
}
] ,
"strings" : {
"string" : "# Heading\n"
}
} ;
2024-12-25 20:42:32 -08:00
return lintPromise ( options ) ;
} ) ;
test ( "customRulesMarkdownItFactoryUndefined" , ( t ) => {
t . plan ( 1 ) ;
/** @type {import("markdownlint").Options} */
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"parser" : "markdownit" ,
"function" : ( ) => { }
}
] ,
"strings" : {
"string" : "# Heading\n"
}
} ;
t . throws (
( ) => lintSync ( options ) ,
{
"message" : "The option 'markdownItFactory' was required (due to the option 'customRules' including a rule requiring the 'markdown-it' parser), but 'markdownItFactory' was not set."
} ,
"No exception when markdownItFactory is undefined."
) ;
2024-03-09 16:17:50 -08:00
} ) ;
2024-12-27 21:22:14 -08:00
test ( "customRulesMarkdownItFactoryNotNeededSync" , ( t ) => {
t . plan ( 1 ) ;
/** @type {import("markdownlint").Options} */
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"parser" : "none" ,
"function" : ( ) => { }
}
] ,
"markdownItFactory" : ( ) => t . fail ( ) ,
"strings" : {
"string" : "# Heading\n"
}
} ;
t . pass ( ) ;
return lintSync ( options ) ;
} ) ;
test ( "customRulesMarkdownItFactoryNeededSync" , ( t ) => {
t . plan ( 1 ) ;
/** @type {import("markdownlint").Options} */
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"parser" : "markdownit" ,
"function" : ( ) => { }
}
] ,
"markdownItFactory" : ( ) => t . pass ( ) && markdownIt ( ) ,
"strings" : {
"string" : "# Heading\n"
}
} ;
return lintSync ( options ) ;
} ) ;
test ( "customRulesMarkdownItFactoryNotNeededAsync" , ( t ) => {
t . plan ( 1 ) ;
/** @type {import("markdownlint").Options} */
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"parser" : "none" ,
"function" : ( ) => { }
}
] ,
"markdownItFactory" : ( ) => t . fail ( ) ,
"strings" : {
"string" : "# Heading\n"
}
} ;
t . pass ( ) ;
return lintPromise ( options ) ;
} ) ;
test ( "customRulesMarkdownItFactoryNeededAsyncRunsSync" , ( t ) => {
t . plan ( 1 ) ;
/** @type {import("markdownlint").Options} */
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"parser" : "markdownit" ,
"function" : ( ) => { }
}
] ,
"markdownItFactory" : ( ) => t . pass ( ) && markdownIt ( ) ,
"strings" : {
"string" : "# Heading\n"
}
} ;
return lintPromise ( options ) ;
} ) ;
test ( "customRulesMarkdownItFactoryNeededAsyncRunsAsync" , ( t ) => {
t . plan ( 1 ) ;
/** @type {import("markdownlint").Options} */
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"parser" : "markdownit" ,
"function" : ( ) => { }
}
] ,
"markdownItFactory" : ( ) => t . pass ( ) && Promise . resolve ( markdownIt ( ) ) ,
"strings" : {
"string" : "# Heading\n"
}
} ;
return lintPromise ( options ) ;
} ) ;
test ( "customRulesMarkdownItFactoryNeededAsyncRunsAsyncWithImport" , ( t ) => {
t . plan ( 1 ) ;
/** @type {import("markdownlint").Options} */
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"parser" : "markdownit" ,
"function" : ( ) => { }
}
] ,
"markdownItFactory" : ( ) => import ( "markdown-it" ) . then ( ( module ) => t . pass ( ) && module . default ( ) ) ,
"strings" : {
"string" : "# Heading\n"
}
} ;
return lintPromise ( options ) ;
} ) ;
test ( "customRulesMarkdownItInstanceCanBeReusedSync" , ( t ) => {
t . plan ( 1 ) ;
const markdownItInstance = markdownItFactory ( ) ;
/** @type {import("markdownlint").Options} */
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"parser" : "markdownit" ,
"function" : ( ) => { }
}
] ,
"markdownItFactory" : ( ) => markdownItInstance ,
"strings" : {
"string" : "# Heading"
}
} ;
t . deepEqual ( lintSync ( options ) , lintSync ( options ) ) ;
} ) ;
test ( "customRulesMarkdownItInstanceCanBeReusedAsync" , async ( t ) => {
t . plan ( 1 ) ;
const markdownItInstance = markdownItFactory ( ) ;
/** @type {import("markdownlint").Options} */
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"parser" : "markdownit" ,
"function" : ( ) => { }
}
] ,
"markdownItFactory" : ( ) => Promise . resolve ( markdownItInstance ) ,
"strings" : {
"string" : "# Heading"
}
} ;
t . deepEqual ( await lintPromise ( options ) , await lintPromise ( options ) ) ;
} ) ;
2024-09-12 22:52:30 -07:00
test ( "customRulesMarkdownItParamsTokensSameObject" , ( t ) => {
2024-03-09 16:17:50 -08:00
t . plan ( 1 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2024-03-09 16:17:50 -08:00
const options = {
"customRules" : [
// @ts-ignore
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"function" :
( params ) => {
// @ts-ignore
t . is ( params . tokens , params . parsers . markdownit . tokens ) ;
}
}
] ,
2024-12-25 20:42:32 -08:00
markdownItFactory ,
2024-03-09 16:17:50 -08:00
"strings" : {
"string" : "# Heading\n"
}
} ;
2024-12-25 20:42:32 -08:00
return lintPromise ( options ) ;
2024-03-09 16:17:50 -08:00
} ) ;
2024-09-12 22:52:30 -07:00
test ( "customRulesMarkdownItTokensSnapshot" , ( t ) => {
t . plan ( 1 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2024-09-12 22:52:30 -07:00
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"parser" : "markdownit" ,
"function" :
( params ) => {
t . snapshot ( params . parsers . markdownit . tokens , "Unexpected tokens" ) ;
}
}
] ,
2024-12-25 20:42:32 -08:00
markdownItFactory ,
2024-09-12 22:52:30 -07:00
"noInlineConfig" : true
} ;
2024-10-01 22:41:10 -07:00
return fs
. readFile ( "./test/every-markdown-syntax.md" , "utf8" )
. then ( ( content ) => {
options . strings = { "content" : content . split ( newLineRe ) . join ( "\n" ) } ;
2024-12-25 20:42:32 -08:00
return lintPromise ( options ) ;
2024-10-01 22:41:10 -07:00
} ) ;
} ) ;
test ( "customRulesMicromarkTokensSnapshot" , ( t ) => {
t . plan ( 1 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2024-10-01 22:41:10 -07:00
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"parser" : "micromark" ,
"function" :
( params ) => {
t . snapshot ( params . parsers . micromark . tokens , "Unexpected tokens" ) ;
}
}
] ,
"noInlineConfig" : true
} ;
return fs
. readFile ( "./test/every-markdown-syntax.md" , "utf8" )
. then ( ( content ) => {
options . strings = { "content" : content . split ( newLineRe ) . join ( "\n" ) } ;
2024-12-25 20:42:32 -08:00
return lintPromise ( options ) ;
2024-10-01 22:41:10 -07:00
} ) ;
2024-09-12 22:52:30 -07:00
} ) ;
2023-07-11 22:17:53 -07:00
test ( "customRulesDefinitionStatic" , ( t ) => new Promise ( ( resolve ) => {
t . plan ( 2 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2023-07-11 22:17:53 -07:00
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"information" : new URL ( "https://example.com/information" ) ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2023-07-11 22:17:53 -07:00
"function" : ( params , onError ) => {
2024-03-09 16:17:50 -08:00
// @ts-ignore
2023-07-11 22:17:53 -07:00
const definition = options . customRules [ 0 ] ;
2023-07-12 21:58:36 -07:00
definition . names [ 0 ] = "changed" ;
2023-07-11 22:17:53 -07:00
definition . description = "changed" ;
2023-07-12 21:58:36 -07:00
definition . information . pathname = "changed" ;
2023-07-11 22:17:53 -07:00
onError ( {
"lineNumber" : 1
} ) ;
}
}
] ,
"strings" : {
"string" : "# Heading\n"
}
} ;
2024-12-03 19:58:28 -08:00
lintAsync ( options , ( err , actualResult ) => {
2023-07-11 22:17:53 -07:00
t . falsy ( err ) ;
const expectedResult = {
"string" : [
{
"lineNumber" : 1 ,
"ruleNames" : [ "name" ] ,
"ruleDescription" : "description" ,
"ruleInformation" : "https://example.com/information" ,
"errorDetail" : null ,
"errorContext" : null ,
"errorRange" : null ,
2025-09-11 20:34:24 -07:00
"fixInfo" : null ,
"severity" : "error"
2023-07-11 22:17:53 -07:00
}
]
} ;
t . deepEqual ( actualResult , expectedResult , "Undetected issues." ) ;
resolve ( ) ;
} ) ;
} ) ) ;
2022-06-21 04:40:38 +00:00
test ( "customRulesThrowForFile" , ( t ) => new Promise ( ( resolve ) => {
2021-01-10 20:46:00 -08:00
t . plan ( 4 ) ;
2020-09-12 12:11:14 -07:00
const exceptionMessage = "Test exception message" ;
2024-12-03 19:58:28 -08:00
lintAsync ( {
/** @type {import("markdownlint").Rule[]} */
2020-09-12 12:11:14 -07:00
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2020-09-12 12:11:14 -07:00
"function" : function throws ( ) {
throw new Error ( exceptionMessage ) ;
}
}
] ,
"files" : [ "./test/custom-rules.md" ]
} , function callback ( err , result ) {
2021-01-10 20:46:00 -08:00
t . truthy ( err , "Did not get an error for function thrown." ) ;
t . true ( err instanceof Error , "Error not instance of Error." ) ;
2023-01-21 19:16:07 -08:00
// @ts-ignore
2021-01-10 20:46:00 -08:00
t . is ( err . message , exceptionMessage ,
2020-09-12 12:11:14 -07:00
"Incorrect message for function thrown." ) ;
2021-01-10 20:46:00 -08:00
t . true ( ! result , "Got result for function thrown." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2020-09-12 12:11:14 -07:00
2021-01-10 20:46:00 -08:00
test ( "customRulesThrowForFileSync" , ( t ) => {
t . plan ( 1 ) ;
2020-09-12 12:11:14 -07:00
const exceptionMessage = "Test exception message" ;
2021-01-10 20:46:00 -08:00
t . throws (
2020-09-12 12:11:14 -07:00
function customRuleThrowsCall ( ) {
2024-12-03 19:58:28 -08:00
lintSync ( {
/** @type {import("markdownlint").Rule[]} */
2020-09-12 12:11:14 -07:00
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2020-09-12 12:11:14 -07:00
"function" : function throws ( ) {
throw new Error ( exceptionMessage ) ;
}
}
] ,
"files" : [ "./test/custom-rules.md" ]
} ) ;
} ,
2021-01-10 20:46:00 -08:00
{
"message" : exceptionMessage
} ,
2020-09-12 12:11:14 -07:00
"Did not get correct exception for function thrown."
) ;
} ) ;
2022-06-21 04:40:38 +00:00
test ( "customRulesThrowForString" , ( t ) => new Promise ( ( resolve ) => {
2021-01-10 20:46:00 -08:00
t . plan ( 4 ) ;
2020-09-12 12:11:14 -07:00
const exceptionMessage = "Test exception message" ;
2024-12-03 19:58:28 -08:00
lintAsync ( {
/** @type {import("markdownlint").Rule[]} */
2020-09-12 12:11:14 -07:00
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2020-09-12 12:11:14 -07:00
"function" : function throws ( ) {
throw new Error ( exceptionMessage ) ;
}
}
] ,
"strings" : {
"string" : "String"
}
} , function callback ( err , result ) {
2021-01-10 20:46:00 -08:00
t . truthy ( err , "Did not get an error for function thrown." ) ;
t . true ( err instanceof Error , "Error not instance of Error." ) ;
2023-01-21 19:16:07 -08:00
// @ts-ignore
2021-01-10 20:46:00 -08:00
t . is ( err . message , exceptionMessage ,
2020-09-12 12:11:14 -07:00
"Incorrect message for function thrown." ) ;
2021-01-10 20:46:00 -08:00
t . true ( ! result , "Got result for function thrown." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2021-11-30 22:35:19 -08:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2021-11-30 22:35:19 -08:00
test ( "customRulesThrowForStringSync" , ( t ) => {
t . plan ( 1 ) ;
const exceptionMessage = "Test exception message" ;
t . throws (
function customRuleThrowsCall ( ) {
2024-12-03 19:58:28 -08:00
lintSync ( {
/** @type {import("markdownlint").Rule[]} */
2021-11-30 22:35:19 -08:00
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2021-11-30 22:35:19 -08:00
"function" : function throws ( ) {
throw new Error ( exceptionMessage ) ;
}
}
] ,
"strings" : {
"string" : "String"
}
} ) ;
} ,
{
"message" : exceptionMessage
} ,
"Did not get correct exception for function thrown."
) ;
} ) ;
2022-06-21 04:40:38 +00:00
test ( "customRulesOnErrorNull" , ( t ) => new Promise ( ( resolve ) => {
2021-11-30 22:35:19 -08:00
t . plan ( 4 ) ;
2024-12-03 19:58:28 -08:00
lintAsync ( {
/** @type {import("markdownlint").Rule[]} */
2021-11-30 22:35:19 -08:00
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2021-11-30 22:35:19 -08:00
"function" : function onErrorNull ( params , onError ) {
2023-01-21 19:16:07 -08:00
// @ts-ignore
2021-11-30 22:35:19 -08:00
onError ( null ) ;
}
}
] ,
"strings" : {
"string" : "String"
}
} ,
function callback ( err , result ) {
t . truthy ( err , "Did not get an error for function thrown." ) ;
t . true ( err instanceof Error , "Error not instance of Error." ) ;
t . is (
2023-01-21 19:16:07 -08:00
// @ts-ignore
2021-11-30 22:35:19 -08:00
err . message ,
2023-10-07 19:32:29 -07:00
"Value of 'lineNumber' passed to onError by 'NAME' is incorrect for 'string'." ,
2021-11-30 22:35:19 -08:00
"Did not get correct exception for null object."
) ;
t . true ( ! result , "Got result for function thrown." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2020-09-12 12:11:14 -07:00
2021-11-30 22:35:19 -08:00
test ( "customRulesOnErrorNullSync" , ( t ) => {
2021-01-10 20:46:00 -08:00
t . plan ( 1 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2020-09-12 12:11:14 -07:00
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2020-09-12 12:11:14 -07:00
"function" : function onErrorNull ( params , onError ) {
2024-03-09 16:17:50 -08:00
// @ts-ignore
2020-09-12 12:11:14 -07:00
onError ( null ) ;
}
}
] ,
"strings" : {
"string" : "String"
}
} ;
2021-01-10 20:46:00 -08:00
t . throws (
2020-09-12 12:11:14 -07:00
function nullErrorCall ( ) {
2024-12-03 19:58:28 -08:00
lintSync ( options ) ;
2020-09-12 12:11:14 -07:00
} ,
2021-01-10 20:46:00 -08:00
{
2023-10-07 19:32:29 -07:00
"message" : "Value of 'lineNumber' passed to onError by 'NAME' is incorrect for 'string'."
2021-01-10 20:46:00 -08:00
} ,
2020-09-12 12:11:14 -07:00
"Did not get correct exception for null object."
) ;
} ) ;
2021-01-10 20:46:00 -08:00
test ( "customRulesOnErrorBad" , ( t ) => {
2023-07-11 21:44:45 -07:00
t . plan ( 25 ) ;
2022-06-08 22:10:27 -07:00
for ( const testCase of [
2020-09-12 12:11:14 -07:00
{
"propertyName" : "lineNumber" ,
"propertyValues" : [ null , "string" ]
} ,
{
"propertyName" : "detail" ,
"propertyValues" : [ 10 , [ ] ]
} ,
{
"propertyName" : "context" ,
"propertyValues" : [ 10 , [ ] ]
} ,
2023-07-11 21:44:45 -07:00
{
"propertyName" : "information" ,
"propertyValues" : [ 10 , [ ] , "string" , "https://example.com" ]
} ,
2020-09-12 12:11:14 -07:00
{
"propertyName" : "range" ,
"propertyValues" : [ 10 , [ ] , [ 10 ] , [ 10 , null ] , [ 10 , 11 , 12 ] ]
} ,
{
"propertyName" : "fixInfo" ,
"propertyValues" : [ 10 , "string" ]
} ,
{
"propertyName" : "fixInfo" ,
"subPropertyName" : "lineNumber" ,
"propertyValues" : [ null , "string" ]
} ,
{
"propertyName" : "fixInfo" ,
"subPropertyName" : "editColumn" ,
"propertyValues" : [ null , "string" ]
} ,
{
"propertyName" : "fixInfo" ,
"subPropertyName" : "deleteCount" ,
"propertyValues" : [ null , "string" ]
} ,
{
"propertyName" : "fixInfo" ,
"subPropertyName" : "insertText" ,
"propertyValues" : [ 10 , [ ] ]
}
2022-06-08 22:10:27 -07:00
] ) {
2020-09-12 12:11:14 -07:00
const { propertyName , subPropertyName , propertyValues } = testCase ;
2022-06-08 22:10:27 -07:00
for ( const propertyValue of propertyValues ) {
2020-09-12 12:11:14 -07:00
const badObject = {
"lineNumber" : 1
} ;
let propertyNames = null ;
if ( subPropertyName ) {
badObject [ propertyName ] = { } ;
badObject [ propertyName ] [ subPropertyName ] = propertyValue ;
propertyNames = ` ${ propertyName } . ${ subPropertyName } ` ;
} else {
badObject [ propertyName ] = propertyValue ;
propertyNames = propertyName ;
}
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2020-09-12 12:11:14 -07:00
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2020-09-12 12:11:14 -07:00
"function" : function onErrorBad ( params , onError ) {
onError ( badObject ) ;
}
}
] ,
"strings" : {
"string" : "String"
}
} ;
2021-01-10 20:46:00 -08:00
t . throws (
2020-09-12 12:11:14 -07:00
function badErrorCall ( ) {
2024-12-03 19:58:28 -08:00
lintSync ( options ) ;
2020-09-12 12:11:14 -07:00
} ,
2021-01-10 20:46:00 -08:00
{
"message" :
2023-10-07 19:32:29 -07:00
` Value of ' ${ propertyNames } ' passed to onError by 'NAME' is incorrect for 'string'. `
2021-01-10 20:46:00 -08:00
} ,
2020-09-12 12:11:14 -07:00
"Did not get correct exception for bad object."
) ;
2022-06-08 22:10:27 -07:00
}
}
2020-09-12 12:11:14 -07:00
} ) ;
2021-01-10 20:46:00 -08:00
test ( "customRulesOnErrorInvalid" , ( t ) => {
t . plan ( 17 ) ;
2022-06-08 22:10:27 -07:00
for ( const testCase of [
2020-09-12 12:11:14 -07:00
{
"propertyName" : "lineNumber" ,
"propertyValues" : [ - 1 , 0 , 3 , 4 ]
} ,
{
"propertyName" : "range" ,
"propertyValues" : [ [ 0 , 1 ] , [ 1 , 0 ] , [ 5 , 1 ] , [ 1 , 5 ] , [ 4 , 2 ] ]
} ,
{
"propertyName" : "fixInfo" ,
"subPropertyName" : "lineNumber" ,
"propertyValues" : [ - 1 , 0 , 3 , 4 ]
} ,
{
"propertyName" : "fixInfo" ,
"subPropertyName" : "editColumn" ,
"propertyValues" : [ 0 , 6 ]
} ,
{
"propertyName" : "fixInfo" ,
"subPropertyName" : "deleteCount" ,
"propertyValues" : [ - 2 , 5 ]
}
2022-06-08 22:10:27 -07:00
] ) {
2020-09-12 12:11:14 -07:00
const { propertyName , subPropertyName , propertyValues } = testCase ;
2022-06-08 22:10:27 -07:00
for ( const propertyValue of propertyValues ) {
2020-09-12 12:11:14 -07:00
const badObject = {
"lineNumber" : 1
} ;
let propertyNames = null ;
if ( subPropertyName ) {
badObject [ propertyName ] = { } ;
badObject [ propertyName ] [ subPropertyName ] = propertyValue ;
propertyNames = ` ${ propertyName } . ${ subPropertyName } ` ;
} else {
badObject [ propertyName ] = propertyValue ;
propertyNames = propertyName ;
}
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2020-09-12 12:11:14 -07:00
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2020-09-12 12:11:14 -07:00
"function" : function onErrorInvalid ( params , onError ) {
onError ( badObject ) ;
}
}
] ,
"strings" : {
"string" : "Text\ntext"
}
} ;
2021-01-10 20:46:00 -08:00
t . throws (
2020-09-12 12:11:14 -07:00
function invalidErrorCall ( ) {
2024-12-03 19:58:28 -08:00
lintSync ( options ) ;
2020-09-12 12:11:14 -07:00
} ,
2021-01-10 20:46:00 -08:00
{
"message" :
2023-10-07 19:32:29 -07:00
` Value of ' ${ propertyNames } ' passed to onError by 'NAME' is incorrect for 'string'. `
2021-01-10 20:46:00 -08:00
} ,
2020-09-12 12:11:14 -07:00
"Did not get correct exception for invalid object."
) ;
2022-06-08 22:10:27 -07:00
}
}
2020-09-12 12:11:14 -07:00
} ) ;
2021-01-10 20:46:00 -08:00
test ( "customRulesOnErrorValid" , ( t ) => {
t . plan ( 24 ) ;
2022-06-08 22:10:27 -07:00
for ( const testCase of [
2020-09-12 12:11:14 -07:00
{
"propertyName" : "lineNumber" ,
"propertyValues" : [ 1 , 2 ]
} ,
{
"propertyName" : "range" ,
"propertyValues" : [ [ 1 , 1 ] , [ 1 , 4 ] , [ 2 , 2 ] , [ 3 , 2 ] , [ 4 , 1 ] ]
} ,
{
"propertyName" : "fixInfo" ,
"subPropertyName" : "lineNumber" ,
"propertyValues" : [ 1 , 2 ]
} ,
{
"propertyName" : "fixInfo" ,
"subPropertyName" : "editColumn" ,
"propertyValues" : [ 1 , 2 , 4 , 5 ]
} ,
{
"propertyName" : "fixInfo" ,
"subPropertyName" : "deleteCount" ,
"propertyValues" : [ - 1 , 0 , 1 , 4 ]
} ,
{
"propertyName" : "fixInfo" ,
"subPropertyName" : "insertText" ,
"propertyValues" :
[ "" , "1" , "123456" , "\n" , "\nText" , "Text\n" , "\nText\n" ]
}
2022-06-08 22:10:27 -07:00
] ) {
2020-09-12 12:11:14 -07:00
const { propertyName , subPropertyName , propertyValues } = testCase ;
2022-06-08 22:10:27 -07:00
for ( const propertyValue of propertyValues ) {
2020-09-12 12:11:14 -07:00
const goodObject = {
"lineNumber" : 1
} ;
if ( subPropertyName ) {
goodObject [ propertyName ] = { } ;
goodObject [ propertyName ] [ subPropertyName ] = propertyValue ;
} else {
goodObject [ propertyName ] = propertyValue ;
}
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2020-09-12 12:11:14 -07:00
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2020-09-12 12:11:14 -07:00
"function" : function onErrorValid ( params , onError ) {
onError ( goodObject ) ;
}
}
] ,
"strings" : {
"string" : "Text\ntext"
}
} ;
2024-12-03 19:58:28 -08:00
lintSync ( options ) ;
2021-01-10 20:46:00 -08:00
t . truthy ( true ) ;
2022-06-08 22:10:27 -07:00
}
}
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
test ( "customRulesOnErrorLazy" , ( t ) => new Promise ( ( resolve ) => {
2021-01-10 20:46:00 -08:00
t . plan ( 2 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2020-09-12 12:11:14 -07:00
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2020-09-12 12:11:14 -07:00
"function" : function onErrorLazy ( params , onError ) {
onError ( {
"lineNumber" : 1 ,
"detail" : "" ,
"context" : "" ,
"range" : [ 1 , 1 ]
} ) ;
}
}
] ,
"strings" : {
"string" : "# Heading\n"
}
} ;
2024-12-03 19:58:28 -08:00
lintAsync ( options , function callback ( err , actualResult ) {
2021-01-10 20:46:00 -08:00
t . falsy ( err ) ;
2020-09-12 12:11:14 -07:00
const expectedResult = {
"string" : [
{
"lineNumber" : 1 ,
"ruleNames" : [ "name" ] ,
"ruleDescription" : "description" ,
"ruleInformation" : null ,
"errorDetail" : null ,
"errorContext" : null ,
2022-04-21 21:30:56 -07:00
"errorRange" : [ 1 , 1 ] ,
2025-09-11 20:34:24 -07:00
"fixInfo" : null ,
"severity" : "error"
2020-09-12 12:11:14 -07:00
}
]
} ;
2021-01-10 20:46:00 -08:00
t . deepEqual ( actualResult , expectedResult , "Undetected issues." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2020-09-12 12:11:14 -07:00
2022-06-21 04:40:38 +00:00
test ( "customRulesOnErrorModified" , ( t ) => new Promise ( ( resolve ) => {
2021-01-10 20:46:00 -08:00
t . plan ( 2 ) ;
2020-09-12 12:11:14 -07:00
const errorObject = {
"lineNumber" : 1 ,
"detail" : "detail" ,
"context" : "context" ,
2023-07-11 21:44:45 -07:00
"information" : new URL ( "https://example.com/information" ) ,
2020-09-12 12:11:14 -07:00
"range" : [ 1 , 2 ] ,
"fixInfo" : {
"editColumn" : 1 ,
"deleteCount" : 2 ,
"insertText" : "text"
}
} ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2020-09-12 12:11:14 -07:00
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2020-09-12 12:11:14 -07:00
"function" : function onErrorModified ( params , onError ) {
onError ( errorObject ) ;
errorObject . lineNumber = 2 ;
errorObject . detail = "changed" ;
errorObject . context = "changed" ;
2023-07-11 21:44:45 -07:00
errorObject . information = new URL ( "https://example.com/changed" ) ;
2020-09-12 12:11:14 -07:00
errorObject . range [ 1 ] = 3 ;
errorObject . fixInfo . editColumn = 2 ;
errorObject . fixInfo . deleteCount = 3 ;
errorObject . fixInfo . insertText = "changed" ;
}
}
] ,
"strings" : {
"string" : "# Heading\n"
2022-04-21 21:30:56 -07:00
}
2020-09-12 12:11:14 -07:00
} ;
2024-12-03 19:58:28 -08:00
lintAsync ( options , function callback ( err , actualResult ) {
2021-01-10 20:46:00 -08:00
t . falsy ( err ) ;
2020-09-12 12:11:14 -07:00
const expectedResult = {
"string" : [
{
"lineNumber" : 1 ,
"ruleNames" : [ "name" ] ,
"ruleDescription" : "description" ,
2023-07-11 21:44:45 -07:00
"ruleInformation" : "https://example.com/information" ,
2020-09-12 12:11:14 -07:00
"errorDetail" : "detail" ,
"errorContext" : "context" ,
"errorRange" : [ 1 , 2 ] ,
"fixInfo" : {
"editColumn" : 1 ,
"deleteCount" : 2 ,
"insertText" : "text"
2025-09-11 20:34:24 -07:00
} ,
"severity" : "error"
2020-09-12 12:11:14 -07:00
}
]
} ;
2021-01-10 20:46:00 -08:00
t . deepEqual ( actualResult , expectedResult , "Undetected issues." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2020-09-12 12:11:14 -07:00
2022-06-21 04:40:38 +00:00
test ( "customRulesOnErrorInvalidHandled" , ( t ) => new Promise ( ( resolve ) => {
2021-01-10 20:46:00 -08:00
t . plan ( 2 ) ;
2024-12-03 19:58:28 -08:00
lintAsync ( {
/** @type {import("markdownlint").Rule[]} */
2020-09-12 12:11:14 -07:00
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2020-09-12 12:11:14 -07:00
"function" : function onErrorInvalid ( params , onError ) {
onError ( {
2021-12-10 21:33:20 -08:00
"lineNumber" : 13
2020-09-12 12:11:14 -07:00
} ) ;
}
}
] ,
"strings" : {
"string" : "# Heading\n"
} ,
"handleRuleFailures" : true
} , function callback ( err , actualResult ) {
2021-01-10 20:46:00 -08:00
t . falsy ( err ) ;
2020-09-12 12:11:14 -07:00
const expectedResult = {
"string" : [
{
"lineNumber" : 1 ,
"ruleNames" : [ "name" ] ,
"ruleDescription" : "description" ,
"ruleInformation" : null ,
"errorDetail" : "This rule threw an exception: " +
2023-10-07 19:32:29 -07:00
"Value of 'lineNumber' passed to onError by 'NAME' is incorrect for 'string'." ,
2020-09-12 12:11:14 -07:00
"errorContext" : null ,
2022-04-21 21:30:56 -07:00
"errorRange" : null ,
2025-09-11 20:34:24 -07:00
"fixInfo" : null ,
"severity" : "error"
2020-09-12 12:11:14 -07:00
}
]
} ;
2021-01-10 20:46:00 -08:00
t . deepEqual ( actualResult , expectedResult , "Undetected issues." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2020-09-12 12:11:14 -07:00
2021-11-30 22:35:19 -08:00
test ( "customRulesOnErrorInvalidHandledSync" , ( t ) => {
t . plan ( 1 ) ;
2024-12-03 19:58:28 -08:00
const actualResult = lintSync ( {
/** @type {import("markdownlint").Rule[]} */
2021-11-30 22:35:19 -08:00
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2021-11-30 22:35:19 -08:00
"function" : function onErrorInvalid ( params , onError ) {
onError ( {
"lineNumber" : 13 ,
"detail" : "N/A"
} ) ;
}
}
] ,
"strings" : {
"string" : "# Heading\n"
} ,
"handleRuleFailures" : true
} ) ;
const expectedResult = {
"string" : [
{
"lineNumber" : 1 ,
"ruleNames" : [ "name" ] ,
"ruleDescription" : "description" ,
"ruleInformation" : null ,
"errorDetail" : "This rule threw an exception: " +
2023-10-07 19:32:29 -07:00
"Value of 'lineNumber' passed to onError by 'NAME' is incorrect for 'string'." ,
2021-11-30 22:35:19 -08:00
"errorContext" : null ,
2022-04-21 21:30:56 -07:00
"errorRange" : null ,
2025-09-11 20:34:24 -07:00
"fixInfo" : null ,
"severity" : "error"
2021-11-30 22:35:19 -08:00
}
]
} ;
t . deepEqual ( actualResult , expectedResult , "Undetected issues." ) ;
} ) ;
2024-09-29 18:11:41 -07:00
test ( "customRulesVersion" , ( t ) => new Promise ( ( resolve ) => {
t . plan ( 2 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2024-09-29 18:11:41 -07:00
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"parser" : "none" ,
"function" : function stringName ( params ) {
t . is ( params . version , version , "Incorrect version" ) ;
}
}
] ,
"files" : "doc/CustomRules.md"
} ;
2024-12-03 19:58:28 -08:00
lintAsync ( options , function callback ( err ) {
2024-09-29 18:11:41 -07:00
t . falsy ( err ) ;
resolve ( ) ;
} ) ;
} ) ) ;
2022-06-21 04:40:38 +00:00
test ( "customRulesFileName" , ( t ) => new Promise ( ( resolve ) => {
2021-01-10 20:46:00 -08:00
t . plan ( 2 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2020-09-12 12:11:14 -07:00
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2020-09-12 12:11:14 -07:00
"function" : function stringName ( params ) {
2021-01-10 20:46:00 -08:00
t . is ( params . name , "doc/CustomRules.md" , "Incorrect file name" ) ;
2020-09-12 12:11:14 -07:00
}
}
] ,
"files" : "doc/CustomRules.md"
} ;
2024-12-03 19:58:28 -08:00
lintAsync ( options , function callback ( err ) {
2021-01-10 20:46:00 -08:00
t . falsy ( err ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2020-09-12 12:11:14 -07:00
2022-06-21 04:40:38 +00:00
test ( "customRulesStringName" , ( t ) => new Promise ( ( resolve ) => {
2021-01-10 20:46:00 -08:00
t . plan ( 2 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2020-09-12 12:11:14 -07:00
const options = {
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2020-09-12 12:11:14 -07:00
"function" : function stringName ( params ) {
2021-01-10 20:46:00 -08:00
t . is ( params . name , "string" , "Incorrect string name" ) ;
2020-09-12 12:11:14 -07:00
}
}
] ,
"strings" : {
"string" : "# Heading"
}
} ;
2024-12-03 19:58:28 -08:00
lintAsync ( options , function callback ( err ) {
2021-01-10 20:46:00 -08:00
t . falsy ( err ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2020-09-12 12:11:14 -07:00
2023-07-11 21:44:45 -07:00
test ( "customRulesOnErrorInformationNotRuleNotError" , ( t ) => {
t . plan ( 1 ) ;
2024-12-03 19:58:28 -08:00
const actualResult = lintSync ( {
/** @type {import("markdownlint").Rule[]} */
2023-07-11 21:44:45 -07:00
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2023-07-11 21:44:45 -07:00
"function" : ( params , onError ) => {
onError ( {
"lineNumber" : 1
} ) ;
}
}
] ,
"strings" : {
"string" : "# Heading\n"
}
} ) ;
t . true ( actualResult . string [ 0 ] . ruleInformation === null , "Unexpected URL." ) ;
} ) ;
test ( "customRulesOnErrorInformationRuleNotError" , ( t ) => {
t . plan ( 1 ) ;
2024-12-03 19:58:28 -08:00
const actualResult = lintSync ( {
/** @type {import("markdownlint").Rule[]} */
2023-07-11 21:44:45 -07:00
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"information" : new URL ( "https://example.com/rule" ) ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2023-07-11 21:44:45 -07:00
"function" : ( params , onError ) => {
onError ( {
"lineNumber" : 1
} ) ;
}
}
] ,
"strings" : {
"string" : "# Heading\n"
}
} ) ;
t . is (
actualResult . string [ 0 ] . ruleInformation ,
"https://example.com/rule" ,
"Unexpected URL."
) ;
} ) ;
test ( "customRulesOnErrorInformationNotRuleError" , ( t ) => {
t . plan ( 1 ) ;
2024-12-03 19:58:28 -08:00
const actualResult = lintSync ( {
/** @type {import("markdownlint").Rule[]} */
2023-07-11 21:44:45 -07:00
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2023-07-11 21:44:45 -07:00
"function" : ( params , onError ) => {
onError ( {
"lineNumber" : 1 ,
"information" : new URL ( "https://example.com/error" )
} ) ;
}
}
] ,
"strings" : {
"string" : "# Heading\n"
}
} ) ;
t . is (
actualResult . string [ 0 ] . ruleInformation ,
"https://example.com/error" ,
"Unexpected URL."
) ;
} ) ;
test ( "customRulesOnErrorInformationRuleError" , ( t ) => {
t . plan ( 1 ) ;
2024-12-03 19:58:28 -08:00
const actualResult = lintSync ( {
/** @type {import("markdownlint").Rule[]} */
2023-07-11 21:44:45 -07:00
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"information" : new URL ( "https://example.com/rule" ) ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2023-07-11 21:44:45 -07:00
"function" : ( params , onError ) => {
onError ( {
"lineNumber" : 1 ,
"information" : new URL ( "https://example.com/error" )
} ) ;
}
}
] ,
"strings" : {
"string" : "# Heading\n"
}
} ) ;
t . is (
actualResult . string [ 0 ] . ruleInformation ,
"https://example.com/error" ,
"Unexpected URL."
) ;
} ) ;
test ( "customRulesOnErrorInformationRuleErrorUndefined" , ( t ) => {
t . plan ( 1 ) ;
2024-12-03 19:58:28 -08:00
const actualResult = lintSync ( {
/** @type {import("markdownlint").Rule[]} */
2023-07-11 21:44:45 -07:00
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"information" : new URL ( "https://example.com/rule" ) ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2023-07-11 21:44:45 -07:00
"function" : ( params , onError ) => {
onError ( {
"lineNumber" : 1 ,
"information" : undefined
} ) ;
}
}
] ,
"strings" : {
"string" : "# Heading\n"
}
} ) ;
t . is (
actualResult . string [ 0 ] . ruleInformation ,
"https://example.com/rule" ,
"Unexpected URL."
) ;
} ) ;
test ( "customRulesOnErrorInformationRuleErrorMultiple" , ( t ) => {
t . plan ( 6 ) ;
2024-12-03 19:58:28 -08:00
const actualResult = lintSync ( {
/** @type {import("markdownlint").Rule[]} */
2023-07-11 21:44:45 -07:00
"customRules" : [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"information" : new URL ( "https://example.com/rule" ) ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2023-07-11 21:44:45 -07:00
"function" : ( params , onError ) => {
onError ( {
"lineNumber" : 1 ,
"information" : new URL ( "https://example.com/errorA" )
} ) ;
onError ( {
"lineNumber" : 3 ,
"information" : new URL ( "https://example.com/errorB" )
} ) ;
onError ( {
"lineNumber" : 5
} ) ;
}
}
] ,
"strings" : {
"string" : "# Heading\n\nText\n\nText\n"
}
} ) ;
t . is (
actualResult . string [ 0 ] . lineNumber ,
1 ,
"Unexpected line number."
) ;
t . is (
actualResult . string [ 0 ] . ruleInformation ,
"https://example.com/errorA" ,
"Unexpected URL."
) ;
t . is (
actualResult . string [ 1 ] . lineNumber ,
3 ,
"Unexpected line number."
) ;
t . is (
actualResult . string [ 1 ] . ruleInformation ,
"https://example.com/errorB" ,
"Unexpected URL."
) ;
t . is (
actualResult . string [ 2 ] . lineNumber ,
5 ,
"Unexpected line number."
) ;
t . is (
actualResult . string [ 2 ] . ruleInformation ,
"https://example.com/rule" ,
"Unexpected URL."
) ;
} ) ;
2022-06-21 04:40:38 +00:00
test ( "customRulesDoc" , ( t ) => new Promise ( ( resolve ) => {
2021-01-10 20:46:00 -08:00
t . plan ( 2 ) ;
2024-12-03 19:58:28 -08:00
lintAsync ( {
2021-01-10 20:46:00 -08:00
"files" : "./doc/CustomRules.md" ,
2020-09-12 12:11:14 -07:00
"config" : {
"MD013" : { "line_length" : 200 }
}
} , function callback ( err , actual ) {
2021-01-10 20:46:00 -08:00
t . falsy ( err ) ;
const expected = { "./doc/CustomRules.md" : [ ] } ;
t . deepEqual ( actual , expected , "Unexpected issues." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2020-09-12 12:11:14 -07:00
2022-06-21 04:40:38 +00:00
test ( "customRulesLintJavaScript" , ( t ) => new Promise ( ( resolve ) => {
2021-01-10 20:46:00 -08:00
t . plan ( 2 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2020-09-12 12:11:14 -07:00
const options = {
"customRules" : customRules . lintJavaScript ,
2024-12-25 20:42:32 -08:00
"files" : "test/lint-javascript.md" ,
markdownItFactory
2020-09-12 12:11:14 -07:00
} ;
2024-12-03 19:58:28 -08:00
lintAsync ( options , ( err , actual ) => {
2021-01-10 20:46:00 -08:00
t . falsy ( err ) ;
2020-09-12 12:11:14 -07:00
const expected = {
"test/lint-javascript.md" : [
{
"lineNumber" : 12 ,
"ruleNames" : [ "lint-javascript" ] ,
"ruleDescription" : "Rule that lints JavaScript code" ,
"ruleInformation" : null ,
2024-04-20 21:23:06 -07:00
"errorDetail" : "'console' is not defined." ,
2020-09-12 12:11:14 -07:00
"errorContext" : "console.log(x);" ,
2022-04-21 21:30:56 -07:00
"errorRange" : null ,
2025-09-11 20:34:24 -07:00
"fixInfo" : null ,
"severity" : "error"
2020-09-12 12:11:14 -07:00
}
]
} ;
2021-01-10 20:46:00 -08:00
t . deepEqual ( actual , expected , "Unexpected issues." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2020-09-12 12:11:14 -07:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2021-12-10 21:33:20 -08:00
2022-06-21 04:40:38 +00:00
test ( "customRulesValidateJson" , ( t ) => new Promise ( ( resolve ) => {
2023-04-18 19:55:56 -07:00
t . plan ( 3 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2022-01-11 23:08:53 -08:00
const options = {
"customRules" : customRules . validateJson ,
2024-12-25 20:42:32 -08:00
"files" : "test/validate-json.md" ,
markdownItFactory
2022-01-11 23:08:53 -08:00
} ;
2024-12-03 19:58:28 -08:00
lintAsync ( options , ( err , actual ) => {
2022-01-11 23:08:53 -08:00
t . falsy ( err ) ;
const expected = {
"test/validate-json.md" : [
{
"lineNumber" : 22 ,
"ruleNames" : [ "validate-json" ] ,
"ruleDescription" : "Rule that validates JSON code" ,
"ruleInformation" : null ,
"errorContext" : null ,
2022-04-21 21:30:56 -07:00
"errorRange" : null ,
2025-09-11 20:34:24 -07:00
"fixInfo" : null ,
"severity" : "error"
2022-01-11 23:08:53 -08:00
}
]
} ;
2023-04-18 19:55:56 -07:00
t . true (
actual && ( actual [ "test/validate-json.md" ] [ 0 ] . errorDetail . length > 0 ) ,
"Missing errorDetail"
) ;
// @ts-ignore
delete actual [ "test/validate-json.md" ] [ 0 ] . errorDetail ;
2022-01-11 23:08:53 -08:00
t . deepEqual ( actual , expected , "Unexpected issues." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2022-01-11 23:08:53 -08:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2022-01-11 23:08:53 -08:00
2021-12-11 21:44:25 -08:00
test ( "customRulesAsyncThrowsInSyncContext" , ( t ) => {
t . plan ( 1 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2021-12-11 21:44:25 -08:00
const options = {
"customRules" : [
{
"names" : [ "name1" , "name2" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2021-12-11 21:44:25 -08:00
"asynchronous" : true ,
"function" : ( ) => { }
}
] ,
"strings" : {
"string" : "Unused"
}
} ;
t . throws (
2024-12-03 19:58:28 -08:00
( ) => lintSync ( options ) ,
2021-12-11 21:44:25 -08:00
{
"message" : "Custom rule name1/name2 at index 0 is asynchronous and " +
"can not be used in a synchronous context."
} ,
"Did not get correct exception for async rule in sync context."
) ;
} ) ;
2022-06-09 23:56:44 -07:00
test ( "customRulesParamsAreFrozen" , ( t ) => {
2024-10-11 20:33:29 -07:00
const assertParamsFrozen = ( params ) => {
const pending = [ params ] ;
let current = null ;
while ( ( current = pending . shift ( ) ) ) {
t . true ( Object . isFrozen ( current ) || ( current === params ) ) ;
for ( const name of Object . getOwnPropertyNames ( current ) ) {
const value = current [ name ] ;
if (
value &&
( typeof value === "object" ) &&
( name !== "parent" )
) {
pending . push ( value ) ;
}
}
}
} ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2022-06-09 23:56:44 -07:00
const options = {
"customRules" : [
{
2024-10-11 20:33:29 -07:00
"names" : [ "none" ] ,
2022-06-09 23:56:44 -07:00
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2024-10-11 20:33:29 -07:00
"function" : assertParamsFrozen
} ,
{
"names" : [ "markdownit" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"parser" : "markdownit" ,
"function" : assertParamsFrozen
} ,
{
"names" : [ "micromark" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
"parser" : "micromark" ,
"function" : assertParamsFrozen
2022-06-09 23:56:44 -07:00
}
] ,
2024-12-25 20:42:32 -08:00
"files" : [ "README.md" ] ,
markdownItFactory
2022-06-09 23:56:44 -07:00
} ;
2024-12-25 20:42:32 -08:00
return lintPromise ( options ) ;
2022-06-09 23:56:44 -07:00
} ) ;
2022-03-20 12:59:35 -07:00
test ( "customRulesParamsAreStable" , ( t ) => {
t . plan ( 4 ) ;
const config1 = { "value1" : 10 } ;
const config2 = { "value2" : 20 } ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2022-03-20 12:59:35 -07:00
const options = {
"config" : {
"MD010" : true ,
"name1" : config1 ,
"MD013" : { "line_length" : 200 } ,
"name2" : config2 ,
"MD033" : false
} ,
"customRules" : [
{
"names" : [ "name1" ] ,
"description" : "description1" ,
"tags" : [ "tag" ] ,
"asynchronous" : true ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2022-03-20 12:59:35 -07:00
"function" :
( params ) => {
2024-03-09 16:17:50 -08:00
const { config } = params ;
2022-03-20 12:59:35 -07:00
t . deepEqual (
2024-03-09 16:17:50 -08:00
config ,
2022-03-20 12:59:35 -07:00
config1 ,
2024-03-09 16:17:50 -08:00
` Unexpected config in sync path: ${ config } . `
2022-03-20 12:59:35 -07:00
) ;
return Promise . resolve ( ) . then ( ( ) => {
t . deepEqual (
2024-03-09 16:17:50 -08:00
config ,
2022-03-20 12:59:35 -07:00
config1 ,
2024-03-09 16:17:50 -08:00
` Unexpected config in async path: ${ config } . `
2022-03-20 12:59:35 -07:00
) ;
} ) ;
}
} ,
{
"names" : [ "name2" ] ,
"description" : "description2" ,
"tags" : [ "tag" ] ,
"asynchronous" : true ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2022-03-20 12:59:35 -07:00
"function" :
( params ) => {
const { config } = params ;
t . deepEqual (
config ,
config2 ,
` Unexpected config in sync path: ${ config } . `
) ;
return Promise . resolve ( ) . then ( ( ) => {
t . deepEqual (
config ,
config2 ,
` Unexpected config in async path: ${ config } . `
) ;
} ) ;
}
}
] ,
"strings" : {
"string" : "# Heading"
}
} ;
2024-12-25 20:42:32 -08:00
return lintPromise ( options ) ;
2022-03-20 12:59:35 -07:00
} ) ;
2021-12-11 21:44:25 -08:00
test ( "customRulesAsyncReadFiles" , ( t ) => {
t . plan ( 3 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2021-12-11 21:44:25 -08:00
const options = {
"customRules" : [
{
"names" : [ "name1" ] ,
"description" : "description1" ,
"information" : new URL ( "https://example.com/asyncRule1" ) ,
"tags" : [ "tag" ] ,
"asynchronous" : true ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2021-12-11 21:44:25 -08:00
"function" :
2024-11-28 20:36:44 -08:00
( params , onError ) => fs . readFile ( _ _filename ( import . meta ) , "utf8" ) . then (
2021-12-11 21:44:25 -08:00
( content ) => {
t . true ( content . length > 0 ) ;
onError ( {
"lineNumber" : 1 ,
"detail" : "detail1" ,
"context" : "context1" ,
"range" : [ 2 , 3 ]
} ) ;
}
)
} ,
{
"names" : [ "name2" ] ,
"description" : "description2" ,
"tags" : [ "tag" ] ,
"asynchronous" : true ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2021-12-11 21:44:25 -08:00
"function" :
2021-12-27 18:25:18 -08:00
async ( params , onError ) => {
2024-11-28 20:36:44 -08:00
const content = await fs . readFile ( _ _filename ( import . meta ) , "utf8" ) ;
2021-12-27 18:25:18 -08:00
t . true ( content . length > 0 ) ;
onError ( {
"lineNumber" : 1 ,
"detail" : "detail2" ,
"context" : "context2"
} ) ;
}
2021-12-11 21:44:25 -08:00
}
] ,
"strings" : {
"string" : "# Heading"
}
} ;
const expected = {
"string" : [
{
"lineNumber" : 1 ,
"ruleNames" : [ "MD047" , "single-trailing-newline" ] ,
"ruleDescription" : "Files should end with a single newline character" ,
2022-10-30 14:58:45 -07:00
"ruleInformation" : ` ${ homepage } /blob/v ${ version } /doc/md047.md ` ,
2021-12-11 21:44:25 -08:00
"errorDetail" : null ,
"errorContext" : null ,
2022-04-21 21:30:56 -07:00
"errorRange" : [ 9 , 1 ] ,
"fixInfo" : {
"editColumn" : 10 ,
"insertText" : "\n"
2025-09-11 20:34:24 -07:00
} ,
"severity" : "error"
2021-12-11 21:44:25 -08:00
} ,
{
"lineNumber" : 1 ,
"ruleNames" : [ "name1" ] ,
"ruleDescription" : "description1" ,
"ruleInformation" : "https://example.com/asyncRule1" ,
"errorDetail" : "detail1" ,
"errorContext" : "context1" ,
2022-04-21 21:30:56 -07:00
"errorRange" : [ 2 , 3 ] ,
2025-09-11 20:34:24 -07:00
"fixInfo" : null ,
"severity" : "error"
2021-12-11 21:44:25 -08:00
} ,
{
"lineNumber" : 1 ,
"ruleNames" : [ "name2" ] ,
"ruleDescription" : "description2" ,
"ruleInformation" : null ,
"errorDetail" : "detail2" ,
"errorContext" : "context2" ,
2022-04-21 21:30:56 -07:00
"errorRange" : null ,
2025-09-11 20:34:24 -07:00
"fixInfo" : null ,
"severity" : "error"
2021-12-11 21:44:25 -08:00
}
]
} ;
2024-12-03 19:58:28 -08:00
return lintPromise ( options )
2021-12-11 21:44:25 -08:00
. then ( ( actual ) => t . deepEqual ( actual , expected , "Unexpected issues." ) ) ;
} ) ;
test ( "customRulesAsyncIgnoresSyncReturn" , ( t ) => {
t . plan ( 1 ) ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Options} */
2021-12-11 21:44:25 -08:00
const options = {
"customRules" : [
{
"names" : [ "sync" ] ,
"description" : "description" ,
"information" : new URL ( "https://example.com/asyncRule" ) ,
"tags" : [ "tag" ] ,
"asynchronous" : false ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2021-12-11 21:44:25 -08:00
"function" : ( ) => new Promise ( ( ) => {
// Never resolves
} )
} ,
{
"names" : [ "async" ] ,
"description" : "description" ,
"information" : new URL ( "https://example.com/asyncRule" ) ,
"tags" : [ "tag" ] ,
"asynchronous" : true ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2021-12-11 21:44:25 -08:00
"function" : ( params , onError ) => new Promise ( ( resolve ) => {
onError ( { "lineNumber" : 1 } ) ;
2023-01-21 19:16:07 -08:00
resolve ( null ) ;
2021-12-11 21:44:25 -08:00
} )
}
] ,
"strings" : {
"string" : "# Heading"
}
} ;
const expected = {
"string" : [
{
"lineNumber" : 1 ,
"ruleNames" : [ "async" ] ,
"ruleDescription" : "description" ,
"ruleInformation" : "https://example.com/asyncRule" ,
"errorDetail" : null ,
"errorContext" : null ,
2022-04-21 21:30:56 -07:00
"errorRange" : null ,
2025-09-11 20:34:24 -07:00
"fixInfo" : null ,
"severity" : "error"
2021-12-11 21:44:25 -08:00
} ,
{
"lineNumber" : 1 ,
"ruleNames" : [ "MD047" , "single-trailing-newline" ] ,
"ruleDescription" : "Files should end with a single newline character" ,
2022-10-30 14:58:45 -07:00
"ruleInformation" : ` ${ homepage } /blob/v ${ version } /doc/md047.md ` ,
2021-12-11 21:44:25 -08:00
"errorDetail" : null ,
"errorContext" : null ,
2022-04-21 21:30:56 -07:00
"errorRange" : [ 9 , 1 ] ,
"fixInfo" : {
"editColumn" : 10 ,
"insertText" : "\n"
2025-09-11 20:34:24 -07:00
} ,
"severity" : "error"
2021-12-11 21:44:25 -08:00
}
]
} ;
2024-12-03 19:58:28 -08:00
return lintPromise ( options )
2021-12-11 21:44:25 -08:00
. then ( ( actual ) => t . deepEqual ( actual , expected , "Unexpected issues." ) ) ;
} ) ;
2021-12-10 21:33:20 -08:00
const errorMessage = "Custom error message." ;
const stringScenarios = [
[
"Files" ,
[ "./test/custom-rules.md" ] ,
null
] ,
[
"Strings" ,
null ,
{ "./test/custom-rules.md" : "# Heading\n" }
]
] ;
2022-06-08 22:10:27 -07:00
for ( const flavor of [
2021-12-10 21:33:20 -08:00
[
"customRulesThrowString" ,
( ) => {
throw errorMessage ;
}
] ,
[
"customRulesThrowError" ,
( ) => {
throw new Error ( errorMessage ) ;
}
]
2022-06-08 22:10:27 -07:00
] ) {
2021-12-10 21:33:20 -08:00
const [ name , func ] = flavor ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Rule[]} */
2021-12-10 21:33:20 -08:00
const customRule = [
{
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
// @ts-ignore
2021-12-10 21:33:20 -08:00
"function" : func
}
] ;
const expectedResult = {
"./test/custom-rules.md" : [
{
"lineNumber" : 1 ,
"ruleNames" : [ "name" ] ,
"ruleDescription" : "description" ,
"ruleInformation" : null ,
"errorDetail" : ` This rule threw an exception: ${ errorMessage } ` ,
"errorContext" : null ,
2022-04-21 21:30:56 -07:00
"errorRange" : null ,
2025-09-11 20:34:24 -07:00
"fixInfo" : null ,
"severity" : "error"
2021-12-10 21:33:20 -08:00
}
]
} ;
2022-06-08 22:10:27 -07:00
for ( const inputs of stringScenarios ) {
2021-12-10 21:33:20 -08:00
const [ subname , files , strings ] = inputs ;
2022-06-21 04:40:38 +00:00
test ( ` ${ name } ${ subname } UnhandledAsync ` , ( t ) => new Promise ( ( resolve ) => {
2021-12-10 21:33:20 -08:00
t . plan ( 4 ) ;
2024-12-03 19:58:28 -08:00
lintAsync ( {
2021-12-10 21:33:20 -08:00
// @ts-ignore
"customRules" : customRule ,
// @ts-ignore
files ,
// @ts-ignore
strings
} , function callback ( err , result ) {
t . truthy ( err , "Did not get an error for exception." ) ;
t . true ( err instanceof Error , "Error not instance of Error." ) ;
2023-01-21 19:16:07 -08:00
// @ts-ignore
2021-12-10 21:33:20 -08:00
t . is ( err . message , errorMessage , "Incorrect message for exception." ) ;
t . true ( ! result , "Got result for exception." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2021-12-10 21:33:20 -08:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2021-12-10 21:33:20 -08:00
2022-06-21 04:40:38 +00:00
test ( ` ${ name } ${ subname } HandledAsync ` , ( t ) => new Promise ( ( resolve ) => {
2021-12-10 21:33:20 -08:00
t . plan ( 2 ) ;
2024-12-03 19:58:28 -08:00
lintAsync ( {
2021-12-10 21:33:20 -08:00
// @ts-ignore
"customRules" : customRule ,
// @ts-ignore
files ,
// @ts-ignore
strings ,
"handleRuleFailures" : true
} , function callback ( err , actualResult ) {
t . falsy ( err ) ;
t . deepEqual ( actualResult , expectedResult , "Undetected issues." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2021-12-10 21:33:20 -08:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2021-12-10 21:33:20 -08:00
test ( ` ${ name } ${ subname } UnhandledSync ` , ( t ) => {
t . plan ( 1 ) ;
t . throws (
2024-12-03 19:58:28 -08:00
( ) => lintSync ( {
2021-12-10 21:33:20 -08:00
// @ts-ignore
"customRules" : customRule ,
// @ts-ignore
files ,
// @ts-ignore
strings
} ) ,
{
"message" : errorMessage
} ,
"Unexpected exception."
) ;
} ) ;
test ( ` ${ name } ${ subname } HandledSync ` , ( t ) => {
t . plan ( 1 ) ;
2024-12-03 19:58:28 -08:00
const actualResult = lintSync ( {
2021-12-10 21:33:20 -08:00
// @ts-ignore
"customRules" : customRule ,
// @ts-ignore
files ,
// @ts-ignore
strings ,
"handleRuleFailures" : true
} ) ;
t . deepEqual ( actualResult , expectedResult , "Undetected issues." ) ;
} ) ;
2022-06-08 22:10:27 -07:00
}
}
2021-12-11 21:44:25 -08:00
2022-06-08 22:10:27 -07:00
for ( const flavor of [
2021-12-11 21:44:25 -08:00
[
"customRulesAsyncExceptionString" ,
( ) => {
throw errorMessage ;
}
] ,
[
"customRulesAsyncExceptionError" ,
( ) => {
throw new Error ( errorMessage ) ;
}
] ,
[
"customRulesAsyncDeferredString" ,
2024-11-28 20:36:44 -08:00
( ) => fs . readFile ( _ _filename ( import . meta ) , "utf8" ) . then (
2021-12-11 21:44:25 -08:00
( ) => {
throw errorMessage ;
}
)
] ,
[
"customRulesAsyncDeferredError" ,
2024-11-28 20:36:44 -08:00
( ) => fs . readFile ( _ _filename ( import . meta ) , "utf8" ) . then (
2021-12-11 21:44:25 -08:00
( ) => {
throw new Error ( errorMessage ) ;
}
)
] ,
[
"customRulesAsyncRejectString" ,
( ) => Promise . reject ( errorMessage )
] ,
[
"customRulesAsyncRejectError" ,
( ) => Promise . reject ( new Error ( errorMessage ) )
]
2022-06-08 22:10:27 -07:00
] ) {
2021-12-11 21:44:25 -08:00
const [ name , func ] = flavor ;
2024-12-03 19:58:28 -08:00
/** @type {import("markdownlint").Rule} */
2021-12-11 21:44:25 -08:00
const customRule = {
"names" : [ "name" ] ,
"description" : "description" ,
"tags" : [ "tag" ] ,
2024-03-09 16:17:50 -08:00
"parser" : "none" ,
2021-12-11 21:44:25 -08:00
"asynchronous" : true ,
2024-03-09 16:17:50 -08:00
// @ts-ignore
2021-12-11 21:44:25 -08:00
"function" : func
} ;
2022-06-08 22:10:27 -07:00
for ( const inputs of stringScenarios ) {
2021-12-11 21:44:25 -08:00
const [ subname , files , strings ] = inputs ;
2022-06-21 04:40:38 +00:00
test ( ` ${ name } ${ subname } Unhandled ` , ( t ) => new Promise ( ( resolve ) => {
2021-12-11 21:44:25 -08:00
t . plan ( 4 ) ;
2024-12-03 19:58:28 -08:00
lintAsync ( {
2021-12-11 21:44:25 -08:00
// @ts-ignore
"customRules" : [ customRule ] ,
// @ts-ignore
files ,
// @ts-ignore
strings
} , function callback ( err , result ) {
t . truthy ( err , "Did not get an error for rejection." ) ;
t . true ( err instanceof Error , "Error not instance of Error." ) ;
2023-01-21 19:16:07 -08:00
// @ts-ignore
2021-12-11 21:44:25 -08:00
t . is ( err . message , errorMessage , "Incorrect message for rejection." ) ;
t . true ( ! result , "Got result for rejection." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2021-12-11 21:44:25 -08:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2021-12-11 21:44:25 -08:00
2022-06-21 04:40:38 +00:00
test ( ` ${ name } ${ subname } Handled ` , ( t ) => new Promise ( ( resolve ) => {
2021-12-11 21:44:25 -08:00
t . plan ( 2 ) ;
2024-12-03 19:58:28 -08:00
lintAsync ( {
2021-12-11 21:44:25 -08:00
// @ts-ignore
"customRules" : [ customRule ] ,
// @ts-ignore
files ,
// @ts-ignore
strings ,
"handleRuleFailures" : true
} , function callback ( err , actualResult ) {
t . falsy ( err ) ;
const expectedResult = {
"./test/custom-rules.md" : [
{
"lineNumber" : 1 ,
"ruleNames" : [ "name" ] ,
"ruleDescription" : "description" ,
"ruleInformation" : null ,
"errorDetail" : ` This rule threw an exception: ${ errorMessage } ` ,
"errorContext" : null ,
2022-04-21 21:30:56 -07:00
"errorRange" : null ,
2025-09-11 20:34:24 -07:00
"fixInfo" : null ,
"severity" : "error"
2021-12-11 21:44:25 -08:00
}
]
} ;
t . deepEqual ( actualResult , expectedResult , "Undetected issues." ) ;
2022-06-21 04:40:38 +00:00
resolve ( ) ;
2021-12-11 21:44:25 -08:00
} ) ;
2022-06-21 04:40:38 +00:00
} ) ) ;
2022-06-08 22:10:27 -07:00
}
}