From 1c8905d42daf91e22d2de786f3ea94b3536bc70c Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 24 Jun 2024 14:32:27 +0200 Subject: [PATCH] Refactor code-style --- index.d.ts | 4 +- index.js | 5 +- lib/index.js | 30 +-- package.json | 35 +++- test.js | 510 +++++++++++++++++++++++++-------------------------- 5 files changed, 301 insertions(+), 283 deletions(-) diff --git a/index.d.ts b/index.d.ts index 5fedfb3..c1ee837 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/ban-types, @typescript-eslint/consistent-type-definitions */ - import type {Options as DeadOrAliveOptions} from 'dead-or-alive' export {default} from './lib/index.js' @@ -14,7 +12,7 @@ export interface Options { * `deadOrAliveOptions.findUrls` is always off as further URLs are not used * by `remark-lint-no-dead-urls`. */ - deadOrAliveOptions?: DeadOrAliveOptions | null | undefined + deadOrAliveOptions?: Readonly | null | undefined /** * Check relative values relative to this URL * (optional, example: `'https://example.com/from'`). diff --git a/index.js b/index.js index a49a278..6a4c25d 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,2 @@ -/** - * @typedef {import('./lib/index.js').Options} Options - */ - +// Note: types exposed from `index.d.ts`. export {default} from './lib/index.js' diff --git a/lib/index.js b/lib/index.js index 27c0615..e3c8747 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,11 +1,7 @@ /** - * @typedef {import('mdast').Nodes} Nodes - * @typedef {import('mdast').Resource} Resource - * @typedef {import('mdast').Root} Root - * - * @typedef {import('remark-lint-no-dead-urls').Options} Options - * - * @typedef {import('vfile').VFile} VFile + * @import {Nodes, Resource, Root} from 'mdast' + * @import {Options} from 'remark-lint-no-dead-urls' + * @import {VFile} from 'vfile' */ /** @@ -13,12 +9,15 @@ * Resource nodes. */ -import {ok as assert} from 'devlop' import {deadOrAlive} from 'dead-or-alive' +import {ok as assert} from 'devlop' import isOnline from 'is-online' import {lintRule} from 'unified-lint-rule' import {visit} from 'unist-util-visit' +/** @type {Readonly} */ +const emptyOptions = {} +const defaultSkipUrlPatterns = [/^(?!https?)/i] const remarkLintNoDeadUrls = lintRule( { origin: 'remark-lint:no-dead-urls', @@ -26,7 +25,6 @@ const remarkLintNoDeadUrls = lintRule( }, rule ) -const defaultSkipUrlPatterns = [/^(?!https?)/i] export default remarkLintNoDeadUrls @@ -59,11 +57,11 @@ async function rule(tree, file, options) { /** @type {Map>} */ const nodesByUrl = new Map() const online = await isOnline() - const settings = options || {} + const settings = options || emptyOptions const skipUrlPatterns = settings.skipUrlPatterns - ? settings.skipUrlPatterns.map((d) => - typeof d === 'string' ? new RegExp(d) : d - ) + ? settings.skipUrlPatterns.map(function (d) { + return typeof d === 'string' ? new RegExp(d) : d + }) : [...defaultSkipUrlPatterns] if (settings.skipLocalhost) { @@ -125,7 +123,11 @@ async function rule(tree, file, options) { const url = new URL(value, from).href - if (skipUrlPatterns.some((skipPattern) => skipPattern.test(url))) { + if ( + skipUrlPatterns.some(function (skipPattern) { + return skipPattern.test(url) + }) + ) { return } diff --git a/package.json b/package.json index 1ad4237..7b0da70 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,14 @@ { "name": "remark-lint-no-dead-urls", "version": "1.1.0", - "description": "Ensure that URLs in your Markdown are alive", + "description": "remark-lint rule to warn when URLs are dead", "license": "MIT", "keywords": [ "lint", "markdown", - "remark", - "remark-lint", "remark-lint-rule", + "remark-lint", + "remark", "rule" ], "repository": "remarkjs/remark-lint-no-dead-urls", @@ -58,9 +58,9 @@ "build": "tsc --build --clean && tsc --build && type-coverage", "format": "remark . --frail --output --quiet && prettier . --log-level warn --write && xo --fix", "prepack": "npm run build && npm run format", - "test": "npm run build && npm run format && npm run test-coverage", "test-api": "node --conditions development test.js", - "test-coverage": "c8 --100 --reporter lcov npm run test-api" + "test-coverage": "c8 --100 --reporter lcov npm run test-api", + "test": "npm run build && npm run format && npm run test-coverage" }, "prettier": { "bracketSpacing": false, @@ -82,6 +82,31 @@ "strict": true }, "xo": { + "overrides": [ + { + "files": [ + "**/*.d.ts" + ], + "rules": { + "@typescript-eslint/array-type": [ + "error", + { + "default": "generic" + } + ], + "@typescript-eslint/ban-types": [ + "error", + { + "extendDefaults": true + } + ], + "@typescript-eslint/consistent-type-definitions": [ + "error", + "interface" + ] + } + } + ], "prettier": true } } diff --git a/test.js b/test.js index 2a43802..a5dc4bd 100644 --- a/test.js +++ b/test.js @@ -1,25 +1,33 @@ import assert from 'node:assert/strict' import test from 'node:test' -import {remark} from 'remark' import remarkLintNoDeadUrls from 'remark-lint-no-dead-urls' +import {remark} from 'remark' import {MockAgent, getGlobalDispatcher, setGlobalDispatcher} from 'undici' import {compareMessage} from 'vfile-sort' -test('works', async () => { - const globalDispatcher = getGlobalDispatcher() - const mockAgent = new MockAgent() - mockAgent.enableNetConnect(/(?=a)b/) - setGlobalDispatcher(mockAgent) - const a = mockAgent.get('https://exists.com') - a.intercept({path: '/'}).reply(200, 'ok') - a.intercept({path: '/does/not/'}).reply(404, 'nok') - - mockAgent - .get('https://does-not-exists.com') - .intercept({path: '/'}) - .reply(404, 'nok') - - const file = await remark().use(remarkLintNoDeadUrls).process(` +test('remark-lint-no-dead-urls', async function (t) { + await t.test('should expose the public api', async function () { + assert.deepEqual( + Object.keys(await import('remark-lint-no-dead-urls')).sort(), + ['default'] + ) + }) + + await t.test('should work', async function () { + const globalDispatcher = getGlobalDispatcher() + const mockAgent = new MockAgent() + mockAgent.enableNetConnect(/(?=a)b/) + setGlobalDispatcher(mockAgent) + const interceptable = mockAgent.get('https://exists.com') + interceptable.intercept({path: '/'}).reply(200, 'ok') + interceptable.intercept({path: '/does/not/'}).reply(404, 'nok') + + mockAgent + .get('https://does-not-exists.com') + .intercept({path: '/'}) + .reply(404, 'nok') + + const document = ` # Title Here is a [good link](https://exists.com). @@ -27,35 +35,33 @@ Here is a [good link](https://exists.com). Here is a [bad link](https://exists.com/does/not/). Here is another [bad link](https://does-not-exists.com). - `) +` - await mockAgent.close() - await setGlobalDispatcher(globalDispatcher) + const file = await remark().use(remarkLintNoDeadUrls).process(document) - file.messages.sort(compareMessage) + await mockAgent.close() + await setGlobalDispatcher(globalDispatcher) - assert.deepEqual( - file.messages.map((d) => d.reason), - [ - 'Unexpected dead URL `https://exists.com/does/not/`, expected live URL', - 'Unexpected dead URL `https://does-not-exists.com/`, expected live URL' - ] - ) -}) + file.messages.sort(compareMessage) -test('works w/o URLs', async () => { - const file = await remark().use(remarkLintNoDeadUrls).process(` -# Title + assert.deepEqual(file.messages.map(String), [ + '6:11-6:51: Unexpected dead URL `https://exists.com/does/not/`, expected live URL', + '8:17-8:56: Unexpected dead URL `https://does-not-exists.com/`, expected live URL' + ]) + }) + + await t.test('should work w/o URLs', async function () { + const document = `# Title No URLs in here. -`) +` + const file = await remark().use(remarkLintNoDeadUrls).process(document) - assert.equal(file.messages.length, 0) -}) + assert.equal(file.messages.length, 0) + }) -test('ignores URLs relative to the current URL normally', async () => { - const file = await remark().use(remarkLintNoDeadUrls).process(` -[](a.md) + await t.test('should normally ignore relative URLs', async function () { + const document = `[](a.md) [](/b.md) [](./c.md) [](../d.md) @@ -65,52 +71,48 @@ test('ignores URLs relative to the current URL normally', async () => { [](/h:i) [](?j:k) [](#l:m) -`) +` + const file = await remark().use(remarkLintNoDeadUrls).process(document) - assert.equal(file.messages.length, 0) -}) + assert.equal(file.messages.length, 0) + }) + + await t.test('should checks full URLs', async function () { + const globalDispatcher = getGlobalDispatcher() + const mockAgent = new MockAgent() + mockAgent.enableNetConnect(/(?=a)b/) + setGlobalDispatcher(mockAgent) -test('checks full URLs normally', async () => { - const globalDispatcher = getGlobalDispatcher() - const mockAgent = new MockAgent() - mockAgent.enableNetConnect(/(?=a)b/) - setGlobalDispatcher(mockAgent) - - const file = await remark().use(remarkLintNoDeadUrls, { - // Note: `[]` to overwrite the default only-http check in `skipUrlPatterns`. - skipUrlPatterns: [] - }).process(` -[](http://a.com) + const document = `[](http://a.com) [](https://b.com) [](C:\\Documents\\c.md) [](file:///Users/tilde/d.js) -`) - - await mockAgent.close() - await setGlobalDispatcher(globalDispatcher) - - file.messages.sort(compareMessage) - - assert.deepEqual( - file.messages.map((d) => d.reason), - [ - 'Unexpected dead URL `http://a.com/`, expected live URL', - 'Unexpected dead URL `https://b.com/`, expected live URL', - 'Unexpected dead URL `c:\\Documents\\c.md`, expected live URL', - 'Unexpected dead URL `file:///Users/tilde/d.js`, expected live URL' - ] - ) -}) +` + const file = await remark() + // Note: `[]` to overwrite the default only-http check in `skipUrlPatterns`. + .use(remarkLintNoDeadUrls, {skipUrlPatterns: []}) + .process(document) + + await mockAgent.close() + await setGlobalDispatcher(globalDispatcher) + + file.messages.sort(compareMessage) + + assert.deepEqual(file.messages.map(String), [ + '1:1-1:17: Unexpected dead URL `http://a.com/`, expected live URL', + '2:1-2:18: Unexpected dead URL `https://b.com/`, expected live URL', + '3:1-3:22: Unexpected dead URL `c:\\Documents\\c.md`, expected live URL', + '4:1-4:29: Unexpected dead URL `file:///Users/tilde/d.js`, expected live URL' + ]) + }) -test('checks relative URLs w/ `from`', async () => { - const globalDispatcher = getGlobalDispatcher() - const mockAgent = new MockAgent() - mockAgent.enableNetConnect(/(?=a)b/) - setGlobalDispatcher(mockAgent) + await t.test('should check relative URLs w/ `from`', async function () { + const globalDispatcher = getGlobalDispatcher() + const mockAgent = new MockAgent() + mockAgent.enableNetConnect(/(?=a)b/) + setGlobalDispatcher(mockAgent) - const file = await remark().use(remarkLintNoDeadUrls, { - from: 'https://example.com/from/folder' - }).process(` + const document = ` [](a.md) [](/b.md) [](./c.md) @@ -118,113 +120,114 @@ test('checks relative URLs w/ `from`', async () => { [](#e) [](?f) [](//g.com) -`) - - await mockAgent.close() - await setGlobalDispatcher(globalDispatcher) - - file.messages.sort(compareMessage) - - assert.deepEqual( - file.messages.map((d) => d.reason), - [ - 'Unexpected dead URL `https://example.com/from/a.md`, expected live URL', - 'Unexpected dead URL `https://example.com/b.md`, expected live URL', - 'Unexpected dead URL `https://example.com/from/c.md`, expected live URL', - 'Unexpected dead URL `https://example.com/d.md`, expected live URL', - 'Unexpected dead URL `https://example.com/from/folder#e`, expected live URL', - 'Unexpected dead URL `https://example.com/from/folder?f`, expected live URL', - 'Unexpected dead URL `https://g.com/`, expected live URL' - ] - ) -}) - -test('checks relative URLs w/ `meta.origin`, `meta.pathname`', async () => { - const globalDispatcher = getGlobalDispatcher() - const mockAgent = new MockAgent() - mockAgent.enableNetConnect(/(?=a)b/) - setGlobalDispatcher(mockAgent) - - const file = await remark() - .use(remarkLintNoDeadUrls) - .process({ - data: {meta: {origin: 'https://example.com', pathname: '/from/folder'}}, - value: '[](a.md)' - }) - - await mockAgent.close() - await setGlobalDispatcher(globalDispatcher) - - file.messages.sort(compareMessage) +` + + const file = await remark() + .use(remarkLintNoDeadUrls, {from: 'https://example.com/from/folder'}) + .process(document) + + await mockAgent.close() + await setGlobalDispatcher(globalDispatcher) + + file.messages.sort(compareMessage) + + assert.deepEqual(file.messages.map(String), [ + '2:1-2:9: Unexpected dead URL `https://example.com/from/a.md`, expected live URL', + '3:1-3:10: Unexpected dead URL `https://example.com/b.md`, expected live URL', + '4:1-4:11: Unexpected dead URL `https://example.com/from/c.md`, expected live URL', + '5:1-5:12: Unexpected dead URL `https://example.com/d.md`, expected live URL', + '6:1-6:7: Unexpected dead URL `https://example.com/from/folder#e`, expected live URL', + '7:1-7:7: Unexpected dead URL `https://example.com/from/folder?f`, expected live URL', + '8:1-8:12: Unexpected dead URL `https://g.com/`, expected live URL' + ]) + }) - assert.deepEqual( - file.messages.map((d) => d.reason), - ['Unexpected dead URL `https://example.com/from/a.md`, expected live URL'] + await t.test( + 'should check relative URLs w/ `meta.origin`, `meta.pathname`', + async function () { + const globalDispatcher = getGlobalDispatcher() + const mockAgent = new MockAgent() + mockAgent.enableNetConnect(/(?=a)b/) + setGlobalDispatcher(mockAgent) + + const document = '[](a.md)' + const file = await remark() + .use(remarkLintNoDeadUrls) + .process({ + data: { + meta: {origin: 'https://example.com', pathname: '/from/folder'} + }, + value: document + }) + + await mockAgent.close() + await setGlobalDispatcher(globalDispatcher) + + file.messages.sort(compareMessage) + + assert.deepEqual(file.messages.map(String), [ + '1:1-1:9: Unexpected dead URL `https://example.com/from/a.md`, expected live URL' + ]) + } ) -}) -test('works with definitions and images', async () => { - const globalDispatcher = getGlobalDispatcher() - const mockAgent = new MockAgent() - mockAgent.enableNetConnect(/(?=a)b/) - setGlobalDispatcher(mockAgent) + await t.test('should check definitions, images', async function () { + const globalDispatcher = getGlobalDispatcher() + const mockAgent = new MockAgent() + mockAgent.enableNetConnect(/(?=a)b/) + setGlobalDispatcher(mockAgent) - const file = await remark().use(remarkLintNoDeadUrls).process(` + const document = ` ![image](https://example.com/a) [link](https://example.com/b) [definition]: https://example.com/c - `) +` + const file = await remark().use(remarkLintNoDeadUrls).process(document) - await mockAgent.close() - await setGlobalDispatcher(globalDispatcher) + await mockAgent.close() + await setGlobalDispatcher(globalDispatcher) - file.messages.sort(compareMessage) + file.messages.sort(compareMessage) - assert.deepEqual( - file.messages.map((d) => d.reason), - [ - 'Unexpected dead URL `https://example.com/a`, expected live URL', - 'Unexpected dead URL `https://example.com/b`, expected live URL', - 'Unexpected dead URL `https://example.com/c`, expected live URL' - ] - ) -}) + assert.deepEqual(file.messages.map(String), [ + '2:1-2:32: Unexpected dead URL `https://example.com/a`, expected live URL', + '4:1-4:30: Unexpected dead URL `https://example.com/b`, expected live URL', + '6:1-6:36: Unexpected dead URL `https://example.com/c`, expected live URL' + ]) + }) -test('skips URLs with unsupported protocols', async () => { - const globalDispatcher = getGlobalDispatcher() - const mockAgent = new MockAgent() - mockAgent.enableNetConnect(/(?=a)b/) - setGlobalDispatcher(mockAgent) + await t.test('should skip URLs w/ unknown protocols', async function () { + const globalDispatcher = getGlobalDispatcher() + const mockAgent = new MockAgent() + mockAgent.enableNetConnect(/(?=a)b/) + setGlobalDispatcher(mockAgent) - const file = await remark().use(remarkLintNoDeadUrls).process(` + const document = ` [a](mailto:me@me.com) [b](ftp://path/to/file.txt) [c](flopper://a/b/c) -`) +` + const file = await remark().use(remarkLintNoDeadUrls).process(document) - await mockAgent.close() - await setGlobalDispatcher(globalDispatcher) + await mockAgent.close() + await setGlobalDispatcher(globalDispatcher) - file.messages.sort(compareMessage) + file.messages.sort(compareMessage) - assert.deepEqual( - file.messages.map((d) => d.reason), - [] - ) -}) + assert.deepEqual(file.messages.map(String), []) + }) -test('ignores localhost when skipLocalhost enabled', async () => { - const globalDispatcher = getGlobalDispatcher() - const mockAgent = new MockAgent() - mockAgent.enableNetConnect(/(?=a)b/) - setGlobalDispatcher(mockAgent) + await t.test('should ignore localhost w/ `skipLocalhost`', async function () { + const globalDispatcher = getGlobalDispatcher() + const mockAgent = new MockAgent() + mockAgent.enableNetConnect(/(?=a)b/) + setGlobalDispatcher(mockAgent) - const file = await remark().use(remarkLintNoDeadUrls, {skipLocalhost: true}) - .process(` + const document = ` * [a](http://localhost) * [b](http://localhost/alex/test) * [c](http://localhost:3000) @@ -232,130 +235,123 @@ test('ignores localhost when skipLocalhost enabled', async () => { * [e](http://127.0.0.1) * [f](http://127.0.0.1:3000) * [g](http://example.com) -`) +` + const file = await remark() + .use(remarkLintNoDeadUrls, {skipLocalhost: true}) + .process(document) - await mockAgent.close() - await setGlobalDispatcher(globalDispatcher) + await mockAgent.close() + await setGlobalDispatcher(globalDispatcher) - file.messages.sort(compareMessage) + file.messages.sort(compareMessage) - assert.deepEqual( - file.messages.map((d) => d.reason), - ['Unexpected dead URL `http://example.com/`, expected live URL'] - ) -}) - -test('skipUrlPatterns for content', async () => { - const globalDispatcher = getGlobalDispatcher() - const mockAgent = new MockAgent() - mockAgent.enableNetConnect(/(?=a)b/) - setGlobalDispatcher(mockAgent) + assert.deepEqual(file.messages.map(String), [ + '8:3-8:26: Unexpected dead URL `http://example.com/`, expected live URL' + ]) + }) - const file = await remark().use(remarkLintNoDeadUrls, { - skipUrlPatterns: [/^http:\/\/aaa\.com/, '^http://bbb\\.com'] - }).process(` -[a](http://aaa.com) -[b](http://aaa.com/somePath) -[c](http://aaa.com/somePath?withQuery=wow) -[d](http://bbb.com/somePath/maybe) -`) + await t.test('should support anchors', async function () { + const globalDispatcher = getGlobalDispatcher() + const mockAgent = new MockAgent() + mockAgent.enableNetConnect(/(?=a)b/) + setGlobalDispatcher(mockAgent) + const site = mockAgent.get('https://example.com') - await mockAgent.close() - await setGlobalDispatcher(globalDispatcher) + site.intercept({path: '/'}).reply(200, '

hi

', { + headers: {'Content-Type': 'text/html'} + }) - file.messages.sort(compareMessage) + const document = ` +[a](https://example.com#exists) +[b](https://example.com#does-not-exist) +` + const file = await remark().use(remarkLintNoDeadUrls).process(document) - assert.deepEqual( - file.messages.map((d) => d.reason), - [] - ) -}) + await mockAgent.close() + await setGlobalDispatcher(globalDispatcher) -test('should support anchors', async () => { - const globalDispatcher = getGlobalDispatcher() - const mockAgent = new MockAgent() - mockAgent.enableNetConnect(/(?=a)b/) - setGlobalDispatcher(mockAgent) - const site = mockAgent.get('https://example.com') + file.messages.sort(compareMessage) - site.intercept({path: '/'}).reply(200, '

hi

', { - headers: {'Content-Type': 'text/html'} + assert.deepEqual(file.messages.map(String), [ + '3:1-3:40: Unexpected dead URL `https://example.com/#does-not-exist`, expected live URL' + ]) }) - const file = await remark().use(remarkLintNoDeadUrls).process(` -[a](https://example.com#exists) -[b](https://example.com#does-not-exist) - `) - - await mockAgent.close() - await setGlobalDispatcher(globalDispatcher) + await t.test('should support `skipUrlPatterns`', async function () { + const globalDispatcher = getGlobalDispatcher() + const mockAgent = new MockAgent() + mockAgent.enableNetConnect(/(?=a)b/) + setGlobalDispatcher(mockAgent) - file.messages.sort(compareMessage) + const document = ` +[a](http://aaa.com) +[b](http://aaa.com/somePath) +[c](http://aaa.com/somePath?withQuery=wow) +[d](http://bbb.com/somePath/maybe) +` + const file = await remark() + .use(remarkLintNoDeadUrls, { + skipUrlPatterns: [/^http:\/\/aaa\.com/, '^http://bbb\\.com'] + }) + .process(document) - assert.deepEqual( - file.messages.map((d) => d.reason), - [ - 'Unexpected dead URL `https://example.com/#does-not-exist`, expected live URL' - ] - ) -}) + await mockAgent.close() + await setGlobalDispatcher(globalDispatcher) -test('should support `deadOrAlive` options', async () => { - const globalDispatcher = getGlobalDispatcher() - const mockAgent = new MockAgent() - mockAgent.enableNetConnect(/(?=a)b/) - setGlobalDispatcher(mockAgent) - const site = mockAgent.get('https://example.com') + file.messages.sort(compareMessage) - site.intercept({path: '/'}).reply(200, '

hi

', { - headers: {'Content-Type': 'text/html'} + assert.deepEqual(file.messages.map(String), []) }) - const file = await remark().use(remarkLintNoDeadUrls, { - deadOrAliveOptions: {checkAnchor: false} - }).process(` -[b](https://example.com#does-not-exist) - `) + await t.test('should support `deadOrAlive` options', async function () { + const globalDispatcher = getGlobalDispatcher() + const mockAgent = new MockAgent() + mockAgent.enableNetConnect(/(?=a)b/) + setGlobalDispatcher(mockAgent) + const site = mockAgent.get('https://example.com') - await mockAgent.close() - await setGlobalDispatcher(globalDispatcher) + site.intercept({path: '/'}).reply(200, '

hi

', { + headers: {'Content-Type': 'text/html'} + }) - file.messages.sort(compareMessage) + const document = `[b](https://example.com#does-not-exist)` + const file = await remark() + .use(remarkLintNoDeadUrls, {deadOrAliveOptions: {checkAnchor: false}}) + .process(document) - assert.deepEqual( - file.messages.map((d) => d.reason), - [] - ) -}) + await mockAgent.close() + await setGlobalDispatcher(globalDispatcher) -test('should support redirects', async () => { - const globalDispatcher = getGlobalDispatcher() - const mockAgent = new MockAgent() - mockAgent.enableNetConnect(/(?=a)b/) - setGlobalDispatcher(mockAgent) - const site = mockAgent.get('https://example.com') + file.messages.sort(compareMessage) - site.intercept({path: '/from'}).reply(301, '', { - headers: {Location: '/to'} + assert.deepEqual(file.messages.map(String), []) }) - site.intercept({path: '/to'}).reply(200, 'ok', { - headers: {'Content-Type': 'text/html'} - }) + await t.test('should support redirects', async function () { + const globalDispatcher = getGlobalDispatcher() + const mockAgent = new MockAgent() + mockAgent.enableNetConnect(/(?=a)b/) + setGlobalDispatcher(mockAgent) + const site = mockAgent.get('https://example.com') + + site.intercept({path: '/from'}).reply(301, '', { + headers: {Location: '/to'} + }) - const file = await remark().use(remarkLintNoDeadUrls).process(` -[a](https://example.com/from) - `) + site.intercept({path: '/to'}).reply(200, 'ok', { + headers: {'Content-Type': 'text/html'} + }) - await mockAgent.close() - await setGlobalDispatcher(globalDispatcher) + const document = `[a](https://example.com/from)` + const file = await remark().use(remarkLintNoDeadUrls).process(document) - file.messages.sort(compareMessage) + await mockAgent.close() + await setGlobalDispatcher(globalDispatcher) - assert.deepEqual( - file.messages.map((d) => d.reason), - [ - 'Unexpected redirecting URL `https://example.com/from`, expected final URL `https://example.com/to`' - ] - ) + file.messages.sort(compareMessage) + + assert.deepEqual(file.messages.map(String), [ + '1:1-1:30: Unexpected redirecting URL `https://example.com/from`, expected final URL `https://example.com/to`' + ]) + }) })