Commit 48ad6da1 authored by David Sveningsson's avatar David Sveningsson

feat(rules): new rule `script-element`

parent f6afc0fd
Pipeline #118283034 passed with stages
in 10 minutes and 30 seconds
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`docs/rules/script-element.md inline validation: correct 1`] = `Array []`;
exports[`docs/rules/script-element.md inline validation: incorrect 1`] = `
Array [
Object {
"errorCount": 1,
"filePath": "inline",
"messages": Array [
Object {
"column": 26,
"context": undefined,
"line": 1,
"message": "End tag for <script> must not be omitted",
"offset": 25,
"ruleId": "script-element",
"selector": "script",
"severity": 2,
"size": 2,
},
],
"source": "<script src=\\"myscript.js\\"/>",
"warningCount": 0,
},
]
`;
import HtmlValidate from "../../../src/htmlvalidate";
const markup: { [key: string]: string } = {};
markup["incorrect"] = `<script src="myscript.js"/>`;
markup["correct"] = `<script src="myscript.js"></script>`;
describe("docs/rules/script-element.md", () => {
it("inline validation: incorrect", () => {
const htmlvalidate = new HtmlValidate({"rules":{"script-element":"error"}});
const report = htmlvalidate.validateString(markup["incorrect"]);
expect(report.results).toMatchSnapshot();
});
it("inline validation: correct", () => {
const htmlvalidate = new HtmlValidate({"rules":{"script-element":"error"}});
const report = htmlvalidate.validateString(markup["correct"]);
expect(report.results).toMatchSnapshot();
});
});
---
docType: rule
name: script-element
category: content-model
summary: Require end tag for `<script>`
---
# Require end tag for `<script>` element (`script-element`)
For legacy reasons the `<script>` element must include a `</script>` end tag even when using the `src` attribute.
## Rule details
Examples of **incorrect** code for this rule:
<validate name="incorrect" rules="script-element">
<script src="myscript.js"/>
</validate>
Examples of **correct** code for this rule:
<validate name="correct" rules="script-element">
<script src="myscript.js"></script>
</validate>
......@@ -4838,7 +4838,7 @@ exports[`HTML elements <samp> valid markup 1`] = `Array []`;
exports[`HTML elements <script> invalid markup 1`] = `
Array [
Object {
"errorCount": 3,
"errorCount": 4,
"filePath": "test-files/elements/script-invalid.html",
"messages": Array [
Object {
......@@ -4881,6 +4881,17 @@ Array [
"severity": 2,
"size": 2,
},
Object {
"column": 8,
"context": undefined,
"line": 8,
"message": "End tag for <script> must not be omitted",
"offset": 330,
"ruleId": "script-element",
"selector": "script:nth-child(3)",
"severity": 2,
"size": 2,
},
],
"source": "<!-- src attribute cannot be empty -->
<script src=\\"\\"></script>
......
......@@ -34,6 +34,7 @@ module.exports = {
"prefer-button": "error",
"prefer-native-element": "error",
"prefer-tbody": "error",
"script-element": "error",
"svg-focusable": "error",
"unrecognized-char-ref": "error",
void: "off",
......
......@@ -76,7 +76,7 @@ describe("Engine", () => {
config = Config.fromObject({
extends: ["html-validate:recommended"],
rules: {
deprecated: "off"
deprecated: "off",
},
});
config.init();
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`rule script-element should contain documentation 1`] = `
Object {
"description": "The end tag for \`<script>\` is a hard requirement and must never be omitted even when using the \`src\` attribute.",
"url": "https://html-validate.org/rules/script-element.html",
}
`;
import HtmlValidate from "../htmlvalidate";
import "../matchers";
describe("rule script-element", () => {
let htmlvalidate: HtmlValidate;
beforeAll(() => {
htmlvalidate = new HtmlValidate({
rules: { "script-element": "error" },
});
});
it("should not report when script element has end tag", () => {
const report = htmlvalidate.validateString("<script></script>");
expect(report).toBeValid();
});
it("should report when script element is self-closed", () => {
const report = htmlvalidate.validateString("<script/>");
expect(report).toHaveError(
"script-element",
"End tag for <script> must not be omitted"
);
});
it("should handle stray end tag", () => {
const report = htmlvalidate.validateString("</script>");
expect(report).toBeValid();
});
it("should handle missing end tag", () => {
const report = htmlvalidate.validateString("<script>");
expect(report).toBeValid();
});
it("should contain documentation", () => {
expect(
htmlvalidate.getRuleDocumentation("script-element")
).toMatchSnapshot();
});
});
import { NodeClosed } from "../dom";
import { TagCloseEvent } from "../event";
import { Rule, RuleDocumentation, ruleDocumentationUrl } from "../rule";
class ScriptElement extends Rule {
public documentation(): RuleDocumentation {
return {
description:
"The end tag for `<script>` is a hard requirement and must never be omitted even when using the `src` attribute.",
url: ruleDocumentationUrl(__filename),
};
}
public setup(): void {
this.on("tag:close", (event: TagCloseEvent) => {
const node = event.target; // The current element being closed.
if (!node || node.tagName !== "script") {
return;
}
if (node.closed !== NodeClosed.EndTag) {
this.report(node, `End tag for <${node.tagName}> must not be omitted`);
}
});
}
}
module.exports = ScriptElement;
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