diff --git a/.html-w3c-validatorrc.json b/.html-w3c-validatorrc.json new file mode 100644 index 0000000..3a91806 --- /dev/null +++ b/.html-w3c-validatorrc.json @@ -0,0 +1,3 @@ +{ + "files": ["./example/build/index.html", "./example/build/about.html"] +} diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..116caa1 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +fixtures diff --git a/example/package-lock.json b/example/package-lock.json index 93a3da9..14d97d2 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -36,6 +36,7 @@ "@types/html-validator": "5.0.3", "@types/mock-fs": "4.13.1", "@types/node": "20.4.9", + "@types/sinon": "10.0.16", "@typescript-eslint/eslint-plugin": "6.3.0", "@typescript-eslint/parser": "6.3.0", "editorconfig-checker": "5.1.1", @@ -55,6 +56,7 @@ "rimraf": "5.0.1", "semantic-release": "21.0.7", "serve": "14.2.0", + "sinon": "15.2.0", "typescript": "5.1.6" }, "engines": { diff --git a/package-lock.json b/package-lock.json index 37d42bb..66b4709 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "@types/html-validator": "5.0.3", "@types/mock-fs": "4.13.1", "@types/node": "20.4.9", + "@types/sinon": "10.0.16", "@typescript-eslint/eslint-plugin": "6.3.0", "@typescript-eslint/parser": "6.3.0", "editorconfig-checker": "5.1.1", @@ -48,6 +49,7 @@ "rimraf": "5.0.1", "semantic-release": "21.0.7", "serve": "14.2.0", + "sinon": "15.2.0", "typescript": "5.1.6" }, "engines": { @@ -1693,6 +1695,50 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "dev": true + }, "node_modules/@swc/cli": { "version": "0.1.62", "resolved": "https://registry.npmjs.org/@swc/cli/-/cli-0.1.62.tgz", @@ -2031,6 +2077,21 @@ "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, + "node_modules/@types/sinon": { + "version": "10.0.16", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.16.tgz", + "integrity": "sha512-j2Du5SYpXZjJVJtXBokASpPRj+e2z+VUhCPHmM6WMfe3dpHu6iVKJMU6AiBcMp/XTAYnEj6Wc1trJUWwZ0QaAQ==", + "dev": true, + "dependencies": { + "@types/sinonjs__fake-timers": "*" + } + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz", + "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.3.0.tgz", @@ -4080,6 +4141,15 @@ "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", "dev": true }, + "node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -6523,6 +6593,12 @@ "node": "*" } }, + "node_modules/just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, "node_modules/keyv": { "version": "4.5.3", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", @@ -6838,6 +6914,12 @@ "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", "dev": true }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true + }, "node_modules/lodash.isfunction": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", @@ -7489,6 +7571,43 @@ "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==", "dev": true }, + "node_modules/nise": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", + "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "@sinonjs/fake-timers": "^10.0.2", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, + "node_modules/nise/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/nise/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/nise/node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "dependencies": { + "isarray": "0.0.1" + } + }, "node_modules/node-emoji": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", @@ -12781,6 +12900,24 @@ "node": ">=4" } }, + "node_modules/sinon": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.2.0.tgz", + "integrity": "sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^10.3.0", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.4", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -13539,6 +13676,15 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", diff --git a/package.json b/package.json index 1376314..2c7b3b6 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "@types/html-validator": "5.0.3", "@types/mock-fs": "4.13.1", "@types/node": "20.4.9", + "@types/sinon": "10.0.16", "@typescript-eslint/eslint-plugin": "6.3.0", "@typescript-eslint/parser": "6.3.0", "editorconfig-checker": "5.1.1", @@ -85,6 +86,7 @@ "rimraf": "5.0.1", "semantic-release": "21.0.7", "serve": "14.2.0", + "sinon": "15.2.0", "typescript": "5.1.6" } } diff --git a/src/HTMLValidatorCommand.ts b/src/HTMLValidatorCommand.ts index 4e7095a..b8c2146 100644 --- a/src/HTMLValidatorCommand.ts +++ b/src/HTMLValidatorCommand.ts @@ -17,9 +17,9 @@ import { isExistingPath } from './utils/isExistingPath.js' export const CONFIG_FILE_NAME = '.html-w3c-validatorrc.json' -const severities = ['error', 'warning', 'info'] as const +export const SEVERITIES = ['error', 'warning', 'info'] as const -export type Severity = (typeof severities)[number] +export type Severity = (typeof SEVERITIES)[number] interface Config { urls?: string[] @@ -113,22 +113,19 @@ export class HTMLValidatorCommand extends Command { `Invalid config file at "${configPath}". Please add URLs or files.` ) } - const configSeverities: Severity[] = config.severities ?? [ - 'warning', - 'error' - ] - for (const severity of configSeverities) { - if (!severities.includes(severity)) { + const severities: Severity[] = config.severities ?? ['warning', 'error'] + for (const severity of severities) { + if (!SEVERITIES.includes(severity)) { throw new Error( - `Invalid config file at "${configPath}". Please add valid severities (${severities.join( + `Invalid config file at "${configPath}". Please add valid severities (${SEVERITIES.join( ', ' )}).` ) } } - if (configSeverities.length === 0) { + if (severities.length === 0) { throw new Error( - `Invalid config file at "${configPath}". Please add valid severities (${severities.join( + `Invalid config file at "${configPath}". Please add valid severities (${SEVERITIES.join( ', ' )}).` ) @@ -169,8 +166,8 @@ export class HTMLValidatorCommand extends Command { } const hasErrors = result.messages.some((message) => { return ( - configSeverities.includes(message.type as Severity) || - configSeverities.includes(message.subType as Severity) + severities.includes(message.type as Severity) || + severities.includes(message.subType as Severity) ) }) if (!hasErrors) { diff --git a/src/__test__/HTMLValidatorCommand.test.ts b/src/__test__/HTMLValidatorCommand.test.ts index 5031ae8..2ede0a2 100644 --- a/src/__test__/HTMLValidatorCommand.test.ts +++ b/src/__test__/HTMLValidatorCommand.test.ts @@ -1,23 +1,377 @@ import test from 'node:test' import assert from 'node:assert/strict' +import path from 'node:path' +import { PassThrough } from 'node:stream' +import sinon from 'sinon' import { execa } from 'execa' +import { table } from 'table' +import chalk from 'chalk' +import logSymbols from 'log-symbols' import { cli } from '../cli.js' -import { HTMLValidatorCommand } from '../HTMLValidatorCommand.js' +import { + HTMLValidatorCommand, + CONFIG_FILE_NAME, + SEVERITIES +} from '../HTMLValidatorCommand.js' + +const FIXTURES_PATH = path.join(process.cwd(), 'src', '__test__', 'fixtures') await test('html-w3c-validator', async (t) => { + t.afterEach(() => { + sinon.restore() + }) + await t.test('should be instance of the command', async () => { const command = cli.process([]) assert(command instanceof HTMLValidatorCommand) }) - await t.test('succeeds and validate the html correctly', async () => { - const exampleURL = new URL('../../example', import.meta.url) - process.chdir(exampleURL.pathname) - await execa('rimraf', ['node_modules']) - await execa('npm', ['install']) - const { exitCode } = await execa('npm', ['run', 'test:html-w3c-validator']) - assert.strictEqual(exitCode, 0) + await t.test( + 'succeeds and validate the html correctly (example)', + async () => { + const exampleURL = new URL('../../example', import.meta.url) + process.chdir(exampleURL.pathname) + await execa('rimraf', ['node_modules']) + await execa('npm', ['install']) + const { exitCode } = await execa('npm', [ + 'run', + 'test:html-w3c-validator' + ]) + assert.strictEqual(exitCode, 0) + } + ) + + await t.test( + 'succeeds and validate the html correctly (example without working directory)', + async () => { + const logs: string[] = [] + sinon.stub(console, 'log').value((log: string) => { + logs.push(log) + }) + const consoleLogSpy = sinon.spy(console, 'log') + const stream = new PassThrough() + const exitCode = await cli.run([], { + stdin: process.stdin, + stdout: stream, + stderr: stream + }) + stream.end() + assert.strictEqual(exitCode, 0) + assert.strictEqual( + consoleLogSpy.calledWith( + logSymbols.success, + './example/build/index.html' + ), + true, + logs.join('\n') + ) + assert.strictEqual( + consoleLogSpy.calledWith( + logSymbols.success, + './example/build/about.html' + ), + true, + logs.join('\n') + ) + } + ) + + await t.test( + 'succeeds and validate the html correctly (fixture)', + async () => { + const workingDirectory = path.join(FIXTURES_PATH, 'success') + const logs: string[] = [] + sinon.stub(console, 'log').value((log: string) => { + logs.push(log) + }) + const consoleLogSpy = sinon.spy(console, 'log') + const stream = new PassThrough() + const exitCode = await cli.run( + [`--current-working-directory=${workingDirectory}`], + { + stdin: process.stdin, + stdout: stream, + stderr: stream + } + ) + stream.end() + assert.strictEqual(exitCode, 0) + assert.strictEqual( + consoleLogSpy.calledWith(logSymbols.success, './build/index.html'), + true, + logs.join('\n') + ) + assert.strictEqual( + consoleLogSpy.calledWith(logSymbols.success, './build/about.html'), + true, + logs.join('\n') + ) + } + ) + + await t.test('fails with not found config', async () => { + const workingDirectory = path.join(FIXTURES_PATH, 'error-config-not-found') + const configPath = path.join(workingDirectory, CONFIG_FILE_NAME) + const errors: string[] = [] + sinon.stub(console, 'error').value((error: string) => { + errors.push(error) + }) + const consoleErrorSpy = sinon.spy(console, 'error') + const stream = new PassThrough() + const exitCode = await cli.run( + [`--current-working-directory=${workingDirectory}`], + { + stdin: process.stdin, + stdout: stream, + stderr: stream + } + ) + stream.end() + assert.strictEqual(exitCode, 1) + assert.strictEqual( + consoleErrorSpy.calledWith( + chalk.bold.red('Error:') + + ` No config file found at ${configPath}. Please create "${CONFIG_FILE_NAME}".` + ), + true, + errors.join('\n') + ) + }) + + await t.test('fails with invalid JSON config', async () => { + const workingDirectory = path.join( + FIXTURES_PATH, + 'error-config-invalid-json' + ) + const configPath = path.join(workingDirectory, CONFIG_FILE_NAME) + const errors: string[] = [] + sinon.stub(console, 'error').value((error: string) => { + errors.push(error) + }) + const consoleErrorSpy = sinon.spy(console, 'error') + const stream = new PassThrough() + const exitCode = await cli.run( + [`--current-working-directory=${workingDirectory}`], + { + stdin: process.stdin, + stdout: stream, + stderr: stream + } + ) + stream.end() + assert.strictEqual(exitCode, 1) + assert.strictEqual( + consoleErrorSpy.calledWith( + chalk.bold.red('Error:') + + ` Invalid config file at "${configPath}". Please check the JSON syntax.` + ), + true, + errors.join('\n') + ) + }) + + await t.test('fails with invalid URLs config', async () => { + const workingDirectory = path.join( + FIXTURES_PATH, + 'error-config-invalid-urls' + ) + const configPath = path.join(workingDirectory, CONFIG_FILE_NAME) + const errors: string[] = [] + sinon.stub(console, 'error').value((error: string) => { + errors.push(error) + }) + const consoleErrorSpy = sinon.spy(console, 'error') + const stream = new PassThrough() + const exitCode = await cli.run( + [`--current-working-directory=${workingDirectory}`], + { + stdin: process.stdin, + stdout: stream, + stderr: stream + } + ) + stream.end() + assert.strictEqual(exitCode, 1) + assert.strictEqual( + consoleErrorSpy.calledWith( + chalk.bold.red('Error:') + + ` Invalid config file at "${configPath}". Please include an array of URLs.` + ), + true, + errors.join('\n') + ) + }) + + await t.test('fails with invalid files config', async () => { + const workingDirectory = path.join( + FIXTURES_PATH, + 'error-config-invalid-files' + ) + const configPath = path.join(workingDirectory, CONFIG_FILE_NAME) + const errors: string[] = [] + sinon.stub(console, 'error').value((error: string) => { + errors.push(error) + }) + const consoleErrorSpy = sinon.spy(console, 'error') + const stream = new PassThrough() + const exitCode = await cli.run( + [`--current-working-directory=${workingDirectory}`], + { + stdin: process.stdin, + stdout: stream, + stderr: stream + } + ) + stream.end() + assert.strictEqual(exitCode, 1) + assert.strictEqual( + consoleErrorSpy.calledWith( + chalk.bold.red('Error:') + + ` Invalid config file at "${configPath}". Please include an array of files.` + ), + true, + errors.join('\n') + ) + }) + + await t.test('fails with invalid files and urls config', async () => { + const workingDirectory = path.join( + FIXTURES_PATH, + 'error-config-invalid-files-and-urls' + ) + const configPath = path.join(workingDirectory, CONFIG_FILE_NAME) + const errors: string[] = [] + sinon.stub(console, 'error').value((error: string) => { + errors.push(error) + }) + const consoleErrorSpy = sinon.spy(console, 'error') + const stream = new PassThrough() + const exitCode = await cli.run( + [`--current-working-directory=${workingDirectory}`], + { + stdin: process.stdin, + stdout: stream, + stderr: stream + } + ) + stream.end() + assert.strictEqual(exitCode, 1) + assert.strictEqual( + consoleErrorSpy.calledWith( + chalk.bold.red('Error:') + + ` Invalid config file at "${configPath}". Please add URLs or files.` + ), + true, + errors.join('\n') + ) + }) + + await t.test('fails with invalid severities config', async () => { + const workingDirectory = path.join( + FIXTURES_PATH, + 'error-config-invalid-severities' + ) + const configPath = path.join(workingDirectory, CONFIG_FILE_NAME) + const errors: string[] = [] + sinon.stub(console, 'error').value((error: string) => { + errors.push(error) + }) + const consoleErrorSpy = sinon.spy(console, 'error') + const stream = new PassThrough() + const exitCode = await cli.run( + [`--current-working-directory=${workingDirectory}`], + { + stdin: process.stdin, + stdout: stream, + stderr: stream + } + ) + stream.end() + assert.strictEqual(exitCode, 1) + assert.strictEqual( + consoleErrorSpy.calledWith( + chalk.bold.red('Error:') + + ` Invalid config file at "${configPath}". Please add valid severities (${SEVERITIES.join( + ', ' + )}).` + ), + true, + errors.join('\n') + ) + }) + + await t.test('fails with invalid empty severities config', async () => { + const workingDirectory = path.join( + FIXTURES_PATH, + 'error-config-invalid-severities-empty' + ) + const configPath = path.join(workingDirectory, CONFIG_FILE_NAME) + const errors: string[] = [] + sinon.stub(console, 'error').value((error: string) => { + errors.push(error) + }) + const consoleErrorSpy = sinon.spy(console, 'error') + const stream = new PassThrough() + const exitCode = await cli.run( + [`--current-working-directory=${workingDirectory}`], + { + stdin: process.stdin, + stdout: stream, + stderr: stream + } + ) + stream.end() + assert.strictEqual(exitCode, 1) + assert.strictEqual( + consoleErrorSpy.calledWith( + chalk.bold.red('Error:') + + ` Invalid config file at "${configPath}". Please add valid severities (${SEVERITIES.join( + ', ' + )}).` + ), + true, + errors.join('\n') + ) + }) + + await t.test('fails with invalid files paths to check', async () => { + const workingDirectory = path.join( + FIXTURES_PATH, + 'error-invalid-files-paths-to-check' + ) + const htmlPath = path.resolve(workingDirectory, 'index.html') + const errors: string[] = [] + sinon.stub(console, 'error').value((error: string) => { + errors.push(error) + }) + const consoleErrorSpy = sinon.spy(console, 'error') + const stream = new PassThrough() + const exitCode = await cli.run( + [`--current-working-directory=${workingDirectory}`], + { + stdin: process.stdin, + stdout: stream, + stderr: stream + } + ) + stream.end() + assert.strictEqual(exitCode, 1) + const messagesTable = [ + [`No file found at "${htmlPath}". Please check the path.`] + ] + assert.strictEqual( + consoleErrorSpy.calledWith( + chalk.bold.red('Error:') + ' HTML validation (W3C) failed!' + ), + true, + errors.join('\n') + ) + assert.strictEqual( + consoleErrorSpy.calledWith(table(messagesTable)), + true, + errors.join('\n') + ) }) }) diff --git a/src/__test__/fixtures/error-config-invalid-files-and-urls/.html-w3c-validatorrc.json b/src/__test__/fixtures/error-config-invalid-files-and-urls/.html-w3c-validatorrc.json new file mode 100644 index 0000000..bc3240a --- /dev/null +++ b/src/__test__/fixtures/error-config-invalid-files-and-urls/.html-w3c-validatorrc.json @@ -0,0 +1,4 @@ +{ + "urls": [], + "files": [] +} diff --git a/src/__test__/fixtures/error-config-invalid-files/.html-w3c-validatorrc.json b/src/__test__/fixtures/error-config-invalid-files/.html-w3c-validatorrc.json new file mode 100644 index 0000000..4895148 --- /dev/null +++ b/src/__test__/fixtures/error-config-invalid-files/.html-w3c-validatorrc.json @@ -0,0 +1,3 @@ +{ + "files": "Invalid" +} diff --git a/src/__test__/fixtures/error-config-invalid-json/.html-w3c-validatorrc.json b/src/__test__/fixtures/error-config-invalid-json/.html-w3c-validatorrc.json new file mode 100644 index 0000000..8ffda68 --- /dev/null +++ b/src/__test__/fixtures/error-config-invalid-json/.html-w3c-validatorrc.json @@ -0,0 +1 @@ +Invalid JSON diff --git a/src/__test__/fixtures/error-config-invalid-severities-empty/.html-w3c-validatorrc.json b/src/__test__/fixtures/error-config-invalid-severities-empty/.html-w3c-validatorrc.json new file mode 100644 index 0000000..698033e --- /dev/null +++ b/src/__test__/fixtures/error-config-invalid-severities-empty/.html-w3c-validatorrc.json @@ -0,0 +1,4 @@ +{ + "files": ["./index.html"], + "severities": [] +} diff --git a/src/__test__/fixtures/error-config-invalid-severities-empty/index.html b/src/__test__/fixtures/error-config-invalid-severities-empty/index.html new file mode 100644 index 0000000..a4f1260 --- /dev/null +++ b/src/__test__/fixtures/error-config-invalid-severities-empty/index.html @@ -0,0 +1,9 @@ + + + + + + Home + + + diff --git a/src/__test__/fixtures/error-config-invalid-severities/.html-w3c-validatorrc.json b/src/__test__/fixtures/error-config-invalid-severities/.html-w3c-validatorrc.json new file mode 100644 index 0000000..4d31b58 --- /dev/null +++ b/src/__test__/fixtures/error-config-invalid-severities/.html-w3c-validatorrc.json @@ -0,0 +1,4 @@ +{ + "files": ["./index.html"], + "severities": ["errors-invalid"] +} diff --git a/src/__test__/fixtures/error-config-invalid-severities/index.html b/src/__test__/fixtures/error-config-invalid-severities/index.html new file mode 100644 index 0000000..a4f1260 --- /dev/null +++ b/src/__test__/fixtures/error-config-invalid-severities/index.html @@ -0,0 +1,9 @@ + + + + + + Home + + + diff --git a/src/__test__/fixtures/error-config-invalid-urls/.html-w3c-validatorrc.json b/src/__test__/fixtures/error-config-invalid-urls/.html-w3c-validatorrc.json new file mode 100644 index 0000000..ae90f38 --- /dev/null +++ b/src/__test__/fixtures/error-config-invalid-urls/.html-w3c-validatorrc.json @@ -0,0 +1,3 @@ +{ + "urls": "Invalid" +} diff --git a/src/__test__/fixtures/error-config-not-found/.gitkeep b/src/__test__/fixtures/error-config-not-found/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/__test__/fixtures/error-invalid-files-paths-to-check/.html-w3c-validatorrc.json b/src/__test__/fixtures/error-invalid-files-paths-to-check/.html-w3c-validatorrc.json new file mode 100644 index 0000000..9e21d03 --- /dev/null +++ b/src/__test__/fixtures/error-invalid-files-paths-to-check/.html-w3c-validatorrc.json @@ -0,0 +1,3 @@ +{ + "files": ["./index.html"] +} diff --git a/src/__test__/fixtures/success/.html-w3c-validatorrc.json b/src/__test__/fixtures/success/.html-w3c-validatorrc.json new file mode 100644 index 0000000..a4a2392 --- /dev/null +++ b/src/__test__/fixtures/success/.html-w3c-validatorrc.json @@ -0,0 +1,3 @@ +{ + "files": ["./build/index.html", "./build/about.html"] +} diff --git a/src/__test__/fixtures/success/build/about.html b/src/__test__/fixtures/success/build/about.html new file mode 100644 index 0000000..e393d8f --- /dev/null +++ b/src/__test__/fixtures/success/build/about.html @@ -0,0 +1,9 @@ + + + + + + About + + + diff --git a/src/__test__/fixtures/success/build/index.html b/src/__test__/fixtures/success/build/index.html new file mode 100644 index 0000000..a4f1260 --- /dev/null +++ b/src/__test__/fixtures/success/build/index.html @@ -0,0 +1,9 @@ + + + + + + Home + + +