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

feat(rules): new rule `no-multiple-main`

parent d2eff14b
Pipeline #203711417 passed with stages
in 9 minutes and 16 seconds
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`docs/rules/no-multiple-main.md inline validation: correct 1`] = `Array []`;
exports[`docs/rules/no-multiple-main.md inline validation: incorrect 1`] = `
Array [
Object {
"errorCount": 1,
"filePath": "inline",
"messages": Array [
Object {
"column": 2,
"context": undefined,
"line": 2,
"message": "Multiple <main> elements present in document",
"offset": 18,
"ruleId": "no-multiple-main",
"selector": "main:nth-child(2)",
"severity": 2,
"size": 4,
},
],
"source": "<main>foo</main>
<main>bar</main>",
"warningCount": 0,
},
]
`;
import HtmlValidate from "../../../src/htmlvalidate";
const markup: { [key: string]: string } = {};
markup["incorrect"] = `<main>foo</main>
<main>bar</main>`;
markup["correct"] = `<main>foo</main>
<main hidden>bar</main>`;
describe("docs/rules/no-multiple-main.md", () => {
it("inline validation: incorrect", () => {
expect.assertions(1);
const htmlvalidate = new HtmlValidate({"rules":{"no-multiple-main":"error"}});
const report = htmlvalidate.validateString(markup["incorrect"]);
expect(report.results).toMatchSnapshot();
});
it("inline validation: correct", () => {
expect.assertions(1);
const htmlvalidate = new HtmlValidate({"rules":{"no-multiple-main":"error"}});
const report = htmlvalidate.validateString(markup["correct"]);
expect(report.results).toMatchSnapshot();
});
});
---
docType: rule
name: no-multiple-main
category: content-model
summary: Disallow multiple `<main>`
---
# Disallows multiple `<main>` elements in the same document (`no-multiple-main`)
HTML5 [disallows][whatwg] multiple visible `<main>` element in the same document.
Multiple `<main>` can be present but at most one can be visible and the others must be hidden using the `hidden` attribute.
[whatwg]: https://html.spec.whatwg.org/multipage/grouping-content.html#the-main-element
## Rule details
Examples of **incorrect** code for this rule:
<validate name="incorrect" rules="no-multiple-main">
<main>foo</main>
<main>bar</main>
</validate>
Examples of **correct** code for this rule:
<validate name="correct" rules="no-multiple-main">
<main>foo</main>
<main hidden>bar</main>
</validate>
......@@ -32,6 +32,7 @@ const config: ConfigData = {
"no-dup-id": "error",
"no-implicit-close": "error",
"no-inline-style": "error",
"no-multiple-main": "error",
"no-raw-characters": "error",
"no-redundant-for": "error",
"no-redundant-role": "error",
......
......@@ -18,6 +18,7 @@ const config: ConfigData = {
"no-deprecated-attr": "error",
"no-dup-attr": "error",
"no-dup-id": "error",
"no-multiple-main": "error",
"no-raw-characters": ["error", { relaxed: true }],
"script-element": "error",
"unrecognized-char-ref": "error",
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rule no-multiple-main should contain documentation 1`] = `
Object {
"description": "Only a single visible \`<main>\` element can be present at in a document at a time.
Multiple \`<main>\` can be present in the DOM as long the others are hidden using the HTML5 \`hidden\` attribute.",
"url": "https://html-validate.org/rules/no-multiple-main.html",
}
`;
......@@ -36,6 +36,7 @@ import NoDupId from "./no-dup-id";
import NoImplicitClose from "./no-implicit-close";
import NoInlineStyle from "./no-inline-style";
import NoMissingReferences from "./no-missing-references";
import NoMultipleMain from "./no-multiple-main";
import NoRawCharacters from "./no-raw-characters";
import NoRedundantFor from "./no-redundant-for";
import NoRedundantRole from "./no-redundant-role";
......@@ -94,6 +95,7 @@ const bundledRules: Record<string, RuleConstructor<any, any>> = {
"no-implicit-close": NoImplicitClose,
"no-inline-style": NoInlineStyle,
"no-missing-references": NoMissingReferences,
"no-multiple-main": NoMultipleMain,
"no-raw-characters": NoRawCharacters,
"no-redundant-for": NoRedundantFor,
"no-redundant-role": NoRedundantRole,
......
import HtmlValidate from "../htmlvalidate";
import { processAttribute } from "../transform/mocks/attribute";
import "../matchers";
describe("rule no-multiple-main", () => {
let htmlvalidate: HtmlValidate;
beforeAll(() => {
htmlvalidate = new HtmlValidate({
rules: { "no-multiple-main": "error" },
});
});
it("should not report when <main> is missing", () => {
expect.assertions(1);
const markup = "<p></p>";
const report = htmlvalidate.validateString(markup);
expect(report).toBeValid();
});
it("should not report when single <main> is present", () => {
expect.assertions(1);
const markup = "<main></main>";
const report = htmlvalidate.validateString(markup);
expect(report).toBeValid();
});
it("should not report when other <main> are hidden", () => {
expect.assertions(1);
const markup = "<main>a</main><main hidden>b</main>";
const report = htmlvalidate.validateString(markup, {
processAttribute,
});
expect(report).toBeValid();
});
it("should not report when all <main> are hidden", () => {
expect.assertions(1);
const markup = "<main hidden>a</main><main hidden>b</main>";
const report = htmlvalidate.validateString(markup, {
processAttribute,
});
expect(report).toBeValid();
});
it("should report when multiple <main> are visible", () => {
expect.assertions(2);
const markup = "<main>a</main><main>b</main>";
const report = htmlvalidate.validateString(markup);
expect(report).toBeInvalid();
expect(report).toHaveError("no-multiple-main", "Multiple <main> elements present in document");
});
it("should contain documentation", () => {
expect.assertions(1);
expect(htmlvalidate.getRuleDocumentation("no-multiple-main")).toMatchSnapshot();
});
});
import { DOMReadyEvent } from "../event";
import { Rule, RuleDocumentation, ruleDocumentationUrl } from "../rule";
export default class NoMultipleMain extends Rule {
public documentation(): RuleDocumentation {
return {
description: [
"Only a single visible `<main>` element can be present at in a document at a time.",
"",
"Multiple `<main>` can be present in the DOM as long the others are hidden using the HTML5 `hidden` attribute.",
].join("\n"),
url: ruleDocumentationUrl(__filename),
};
}
public setup(): void {
this.on("dom:ready", (event: DOMReadyEvent) => {
const { document } = event;
const main = document.querySelectorAll("main").filter((cur) => !cur.hasAttribute("hidden"));
main.shift(); /* ignore the first occurrence */
/* report all other occurrences */
for (const elem of main) {
this.report(elem, "Multiple <main> elements present in document");
}
});
}
}
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