Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(w3c/linter-rules/required-sections): required sections linter #3922

Merged
merged 19 commits into from
Dec 22, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion profiles/w3c.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const modules = [
import("../src/core/linter-rules/local-refs-exist.js"),
import("../src/core/linter-rules/no-headingless-sections.js"),
import("../src/core/linter-rules/no-unused-vars.js"),
import("../src/core/linter-rules/privsec-section.js"),
import("../src/w3c/linter-rules/required-sections.js"),
import("../src/core/linter-rules/wpt-tests-exist.js"),
import("../src/core/linter-rules/no-http-props.js"),
import("../src/core/linter-rules/a11y.js"),
Expand Down
2 changes: 1 addition & 1 deletion src/w3c/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const w3cLogo = {

const w3cDefaults = {
lint: {
"privsec-section": true,
"required-sections": true,
"wpt-tests-exist": false,
a11y: false,
},
Expand Down
57 changes: 57 additions & 0 deletions src/w3c/linter-rules/required-sections.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// @ts-check
/**
* Checks that there is a section that has at least privacy and/or
* security and considerations.
*
* The rule is "privacy" or "security", and "considerations", in any order,
* case-insensitive, multi-line check.
*
*/

import { getIntlData, norm, showError } from "../../core/utils.js";

const ruleName = "required-sections";
export const name = "w3c/linter-rules/required-sections";

const localizationStrings = {
en: {
msg(sectionTitle) {
return `W3C Recommendation track documents require a separate "${sectionTitle}" section.`;
},
hint(sectionTitle) {
return (
`Add a \`<section>\` with a "${sectionTitle}" header. ` +
"See the [Horizontal review guidelines](https://w3c.github.io/documentreview/#how_to_get_horizontal_review)."
marcoscaceres marked this conversation as resolved.
Show resolved Hide resolved
);
},
},
};
const l10n = getIntlData(localizationStrings);

function hasSection(headers, expectedText) {
const regex = new RegExp(expectedText, "im");
marcoscaceres marked this conversation as resolved.
Show resolved Hide resolved
const found = headers.some(({ textContent }) => {
const text = norm(textContent);
return regex.test(text);
});
return found;
}

const requiredSections = ["Privacy Considerations", "Security Considerations"];

export function run(conf) {
if (!conf.lint?.[ruleName] || !conf.isRecTrack) {
return;
}
// usually at end of the document
const headers = [
...document.querySelectorAll("h2, h3, h4, h5, h6"),
].reverse();
for (const section of requiredSections) {
if (!hasSection(headers, section)) {
const msg = l10n.msg(section);
const hint = l10n.hint(section);
showError(msg, name, { hint });
}
}
}
114 changes: 114 additions & 0 deletions tests/spec/w3c/linter-rules/required-sections-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
"use strict";

import {
errorFilters,
flushIframes,
makeRSDoc,
makeStandardOps,
} from "../../SpecHelper.js";

describe("w3c — required-sections", () => {
afterAll(() => {
flushIframes();
});

const linterErrors = errorFilters.filter(
"w3c/linter-rules/required-sections"
);

it("doesn't generate errors for non-rec-track that don't have the required sections", async () => {
const ops = makeStandardOps({
lint: { "required-sections": true },
specStatus: "ED",
});
const doc = await makeRSDoc(ops);
const errors = linterErrors(doc);
expect(errors).toHaveSize(0);
});

it("does nothing if disabled", async () => {
const ops = makeStandardOps({ lint: { "required-sections": true } });
marcoscaceres marked this conversation as resolved.
Show resolved Hide resolved
const doc = await makeRSDoc(ops);
const errors = linterErrors(doc);
expect(errors).toHaveSize(0);
});

it("generates errors when its a rec track document and both privacy and security sections are missing", async () => {
const ops = makeStandardOps({
lint: { "required-sections": true },
specStatus: "WD",
});
const doc = await makeRSDoc(ops);
const errors = linterErrors(doc);
expect(errors).toHaveSize(2);
});

it("generates an error if privacy section if present, but security is missing", async () => {
const body = `
<section>
<h2>Privacy Considerations</h2>
<p>This is a privacy section</p>
</section>
`;
const ops = makeStandardOps(
{
lint: { "required-sections": true },
specStatus: "WD",
},
body
);
const doc = await makeRSDoc(ops);
const errors = linterErrors(doc);
expect(errors).toHaveSize(1);
const [error] = errors;
expect(error.message).toContain(
'separate "Security Considerations" section'
);
});

it("generates an error if Security section if present, but privacy is missing", async () => {
const body = `
<section>
<h2>Security Considerations</h2>
<p>This is a security section</p>
</section>
`;
const ops = makeStandardOps(
{
lint: { "required-sections": true },
specStatus: "WD",
},
body
);
const doc = await makeRSDoc(ops);
const errors = linterErrors(doc);
expect(errors).toHaveSize(1);
const [error] = errors;
expect(error.message).toContain(
'separate "Privacy Considerations" section'
);
});

it("generates no errors when both the Privacy and Security considerations sections are present", async () => {
const body = `
<section>
<h2>Privacy Considerations</h2>
<p>This is a privacy section</p>
</section>
<section>
<h2>Security Considerations</h2>
<p>This is a security section</p>
</section>
`;
const ops = makeStandardOps(
{
lint: { "required-sections": true },
specStatus: "WD",
},
body
);
const doc = await makeRSDoc(ops);
const errors = linterErrors(doc);
expect(errors).toHaveSize(0);
});
});