Commit e94f8191 authored by David Sveningsson's avatar David Sveningsson
Browse files

feat(rules): new rule `doctype-style` requiring doctype to be specific case

parent 8c53d40b
Pipeline #255866094 passed with stages
in 10 minutes and 43 seconds
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`docs/rules/doctype-style.md inline validation: correct 1`] = `Array []`;
exports[`docs/rules/doctype-style.md inline validation: incorrect 1`] = `
Array [
Object {
"errorCount": 1,
"filePath": "inline",
"messages": Array [
Object {
"column": 1,
"context": Object {
"style": "uppercase",
},
"line": 1,
"message": "DOCTYPE should be uppercase",
"offset": 0,
"ruleId": "doctype-style",
"selector": null,
"severity": 2,
"size": 10,
},
],
"source": "<!Doctype html>",
"warningCount": 0,
},
]
`;
import HtmlValidate from "../../../src/htmlvalidate";
const markup: { [key: string]: string } = {};
markup["incorrect"] = `<!Doctype html>`;
markup["correct"] = `<!DOCTYPE html>`;
describe("docs/rules/doctype-style.md", () => {
it("inline validation: incorrect", () => {
expect.assertions(1);
const htmlvalidate = new HtmlValidate({"rules":{"doctype-style":"error"}});
const report = htmlvalidate.validateString(markup["incorrect"]);
expect(report.results).toMatchSnapshot();
});
it("inline validation: correct", () => {
expect.assertions(1);
const htmlvalidate = new HtmlValidate({"rules":{"doctype-style":"error"}});
const report = htmlvalidate.validateString(markup["correct"]);
expect(report.results).toMatchSnapshot();
});
});
---
docType: rule
name: doctype-style
category: style
summary: Require a specific case for DOCTYPE
---
# Require a specific case for DOCTYPE (`doctype-style`)
While DOCTYPE is case-insensitive in the standard this rule requires it to be a specific style.
The standard consistently uses uppercase which is the default style for this rule.
Mixed case it not supported.
## Rule details
Examples of **incorrect** code for this rule:
<validate name="incorrect" rules="doctype-style">
<!Doctype html>
</validate>
Examples of **correct** code for this rule:
<validate name="correct" rules="doctype-style">
<!DOCTYPE html>
</validate>
## Options
This rule takes an optional object:
```javascript
{
"style": "uppercase",
}
```
### `style`
- `uppercase` requires DOCTYPE to be uppercase.
- `lowercase` requires DOCTYPE to be lowercase.
......@@ -99,7 +99,7 @@ Array [
"size": 12,
},
],
"source": "<!doctype html>
"source": "<!DOCTYPE html>
<!--[if lt IE 7]> <html class=\\"no-js ie6 oldie\\" lang=\\"en\\"> <![endif]-->
<!--[if IE 7]> <html class=\\"no-js ie7 oldie\\" lang=\\"en\\"> <![endif]-->
<!--[if IE 8]> <html class=\\"no-js ie8 oldie\\" lang=\\"en\\"> <![endif]-->
......
......@@ -13,6 +13,7 @@ const config: ConfigData = {
deprecated: "error",
"deprecated-rule": "warn",
"doctype-html": "error",
"doctype-style": "error",
"element-case": "error",
"element-name": "error",
"element-permitted-content": "error",
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rule doctype-style configured with lowercase should contain contextual documentation 1`] = `
Object {
"description": "While DOCTYPE is case-insensitive in the standard the current configuration requires it to be uppercase",
"url": "https://html-validate.org/rules/doctype-style.html",
}
`;
exports[`rule doctype-style configured with uppercase should contain contextual documentation 1`] = `
Object {
"description": "While DOCTYPE is case-insensitive in the standard the current configuration requires it to be uppercase",
"url": "https://html-validate.org/rules/doctype-style.html",
}
`;
exports[`rule doctype-style should contain documentation 1`] = `
Object {
"description": "While DOCTYPE is case-insensitive in the standard the current configuration requires a specific style.",
"url": "https://html-validate.org/rules/doctype-style.html",
}
`;
import HtmlValidate from "../htmlvalidate";
import "../matchers";
describe("rule doctype-style", () => {
let htmlvalidate: HtmlValidate;
describe("configured with uppercase", () => {
beforeAll(() => {
htmlvalidate = new HtmlValidate({
rules: { "doctype-style": ["error", { style: "uppercase" }] },
});
});
it("should not report when doctype is upper", () => {
expect.assertions(1);
const markup = "<!DOCTYPE html>";
const report = htmlvalidate.validateString(markup);
expect(report).toBeValid();
});
it("should report error when doctype is lowercase", () => {
expect.assertions(2);
const markup = "<!doctype html>";
const report = htmlvalidate.validateString(markup);
expect(report).toBeInvalid();
expect(report).toHaveError("doctype-style", "DOCTYPE should be uppercase");
});
it("should report error when doctype is mixed case", () => {
expect.assertions(2);
const markup = "<!DoCTyPe html>";
const report = htmlvalidate.validateString(markup);
expect(report).toBeInvalid();
expect(report).toHaveError("doctype-style", "DOCTYPE should be uppercase");
});
it("should contain contextual documentation", () => {
expect.assertions(1);
const context = {
style: "uppercase",
};
expect(htmlvalidate.getRuleDocumentation("doctype-style", null, context)).toMatchSnapshot();
});
});
describe("configured with lowercase", () => {
beforeAll(() => {
htmlvalidate = new HtmlValidate({
rules: { "doctype-style": ["error", { style: "lowercase" }] },
});
});
it("should not report when doctype is lowercase", () => {
expect.assertions(1);
const markup = "<!doctype html>";
const report = htmlvalidate.validateString(markup);
expect(report).toBeValid();
});
it("should report error when doctype is uppercase", () => {
expect.assertions(2);
const markup = "<!DOCTYPE html>";
const report = htmlvalidate.validateString(markup);
expect(report).toBeInvalid();
expect(report).toHaveError("doctype-style", "DOCTYPE should be lowercase");
});
it("should report error when doctype is mixed case", () => {
expect.assertions(2);
const markup = "<!DoCTyPe html>";
const report = htmlvalidate.validateString(markup);
expect(report).toBeInvalid();
expect(report).toHaveError("doctype-style", "DOCTYPE should be lowercase");
});
it("should contain contextual documentation", () => {
expect.assertions(1);
const context = {
style: "uppercase",
};
expect(htmlvalidate.getRuleDocumentation("doctype-style", null, context)).toMatchSnapshot();
});
});
it("should contain documentation", () => {
expect.assertions(1);
htmlvalidate = new HtmlValidate({
rules: { "doctype-style": ["error"] },
});
expect(htmlvalidate.getRuleDocumentation("doctype-style")).toMatchSnapshot();
});
});
import { DoctypeEvent } from "../event";
import { Rule, RuleDocumentation, ruleDocumentationUrl } from "../rule";
interface RuleContext {
style: "uppercase" | "lowercase";
}
interface RuleOptions {
style: "uppercase" | "lowercase";
}
const defaults: RuleOptions = {
style: "uppercase",
};
export default class DoctypeStyle extends Rule<RuleContext, RuleOptions> {
public constructor(options: Partial<RuleOptions>) {
super({ ...defaults, ...options });
}
public documentation(context?: RuleContext): RuleDocumentation {
const doc: RuleDocumentation = {
description: `While DOCTYPE is case-insensitive in the standard the current configuration requires a specific style.`,
url: ruleDocumentationUrl(__filename),
};
if (context) {
doc.description = `While DOCTYPE is case-insensitive in the standard the current configuration requires it to be ${context.style}`;
}
return doc;
}
public setup(): void {
this.on("doctype", (event: DoctypeEvent) => {
if (this.options.style === "uppercase" && event.tag !== "DOCTYPE") {
this.report(null, "DOCTYPE should be uppercase", event.location, this.options);
}
if (this.options.style === "lowercase" && event.tag !== "doctype") {
this.report(null, "DOCTYPE should be lowercase", event.location, this.options);
}
});
}
}
......@@ -12,6 +12,7 @@ import CloseOrder from "./close-order";
import Deprecated from "./deprecated";
import DeprecatedRule from "./deprecated-rule";
import DoctypeHtml from "./doctype-html";
import DoctypeStyle from "./doctype-style";
import ElementCase from "./element-case";
import ElementName from "./element-name";
import ElementPermittedContent from "./element-permitted-content";
......@@ -73,6 +74,7 @@ const bundledRules: Record<string, RuleConstructor<any, any>> = {
deprecated: Deprecated,
"deprecated-rule": DeprecatedRule,
"doctype-html": DoctypeHtml,
"doctype-style": DoctypeStyle,
"element-case": ElementCase,
"element-name": ElementName,
"element-permitted-content": ElementPermittedContent,
......
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Bug Showcase</title>
......
<!doctype html>
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js ie6 oldie" lang="en"> <![endif]-->
<!--[if IE 7]> <html class="no-js ie7 oldie" lang="en"> <![endif]-->
<!--[if IE 8]> <html class="no-js ie8 oldie" lang="en"> <![endif]-->
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment