From 0533b6506aaf4513be5b2529d9349b7055555152 Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Thu, 5 Oct 2023 22:01:54 -0400 Subject: [PATCH 01/22] Make sure that HTML that is inlined is supported --- src/rules/no-default-alt-text.js | 14 +++++++++++--- test/accessibility-rules.test.js | 2 +- test/example.md | 2 ++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/rules/no-default-alt-text.js b/src/rules/no-default-alt-text.js index 9eae5d0..c5f03ea 100644 --- a/src/rules/no-default-alt-text.js +++ b/src/rules/no-default-alt-text.js @@ -23,7 +23,12 @@ module.exports = { function: function GH001(params, onError) { const htmlTagsWithImages = params.parsers.markdownit.tokens.filter( (token) => { - return token.type === "html_block" && token.content.includes(" child.type === "html_inline")) + ); }, ); const inlineImages = params.parsers.markdownit.tokens.filter( @@ -36,12 +41,15 @@ module.exports = { const lineRange = token.map; const lineNumber = token.lineNumber; const lines = params.lines.slice(lineRange[0], lineRange[1]); - for (let i = 0; i < lines.length; i++) { const line = lines[i]; let matches; if (token.type === "inline") { - matches = line.matchAll(markdownAltRegex); + if (token.children.some((child) => child.type === "html_inline")) { + matches = line.matchAll(htmlAltRegex); + } else { + matches = line.matchAll(markdownAltRegex); + } } else { matches = line.matchAll(htmlAltRegex); } diff --git a/test/accessibility-rules.test.js b/test/accessibility-rules.test.js index ef0e72a..e41e7bf 100644 --- a/test/accessibility-rules.test.js +++ b/test/accessibility-rules.test.js @@ -26,7 +26,7 @@ describe("when A11y rules applied", () => { .map((failure) => failure.ruleNames) .flat(); - expect(failuresForExampleFile).toHaveLength(1); + expect(failuresForExampleFile).toHaveLength(3); expect(failureNames).toContain("no-default-alt-text"); }); }); diff --git a/test/example.md b/test/example.md index 69ea19c..61e0808 100644 --- a/test/example.md +++ b/test/example.md @@ -1,3 +1,5 @@ # Example Violations ![Screen Shot 2022-06-26 at 7 41 30 PM](https://user-images.githubusercontent.com/abcdef.png) + +imageImage \ No newline at end of file From 1a5e09eec9e6f52e8323226381247c42952e56f9 Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Thu, 5 Oct 2023 22:07:27 -0400 Subject: [PATCH 02/22] Add detail --- src/rules/no-default-alt-text.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rules/no-default-alt-text.js b/src/rules/no-default-alt-text.js index c5f03ea..3254561 100644 --- a/src/rules/no-default-alt-text.js +++ b/src/rules/no-default-alt-text.js @@ -59,6 +59,7 @@ module.exports = { onError({ lineNumber: lineNumber + i, range: [startIndex + 1, altText.length], + detail: `Flagged alt: ${altText}`, }); } } From 0f707f9d6e48e41d4c0c6c397c25cee69f4457d5 Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Fri, 6 Oct 2023 09:52:39 -0400 Subject: [PATCH 03/22] Update the helpers to support multiple errors in one line --- test/no-default-alt-text.test.js | 10 ++-------- test/no-generic-link-text.test.js | 5 +---- test/utils/run-test.js | 6 ++++-- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/test/no-default-alt-text.test.js b/test/no-default-alt-text.test.js index feca45f..de98999 100644 --- a/test/no-default-alt-text.test.js +++ b/test/no-default-alt-text.test.js @@ -11,10 +11,7 @@ describe("GH001: No Default Alt Text", () => { ]; const results = await runTest(strings, altTextRule); - - for (const result of results) { - expect(result).not.toBeDefined(); - } + expect(results.length).toBe(0); }); test("html image", async () => { const strings = [ @@ -22,10 +19,7 @@ describe("GH001: No Default Alt Text", () => { ]; const results = await runTest(strings, altTextRule); - - for (const result of results) { - expect(result).not.toBeDefined(); - } + expect(results.length).toBe(0); }); }); describe("failures", () => { diff --git a/test/no-generic-link-text.test.js b/test/no-generic-link-text.test.js index 16ac44a..ac11941 100644 --- a/test/no-generic-link-text.test.js +++ b/test/no-generic-link-text.test.js @@ -17,10 +17,7 @@ describe("GH002: No Generic Link Text", () => { ]; const results = await runTest(strings, noGenericLinkTextRule); - - for (const result of results) { - expect(result).not.toBeDefined(); - } + expect(results.length).toBe(0); }); }); describe("failures", () => { diff --git a/test/utils/run-test.js b/test/utils/run-test.js index a70e283..b3a6aa1 100644 --- a/test/utils/run-test.js +++ b/test/utils/run-test.js @@ -11,7 +11,7 @@ async function runTest(strings, rule, ruleConfig) { customRules: [rule], }; - return await Promise.all( + const results = await Promise.all( strings.map((variation) => { const thisTestConfig = { ...config, @@ -21,11 +21,13 @@ async function runTest(strings, rule, ruleConfig) { return new Promise((resolve, reject) => { markdownlint(thisTestConfig, (err, result) => { if (err) reject(err); - resolve(result[0][0]); + resolve(result[0]); }); }); }), ); + + return results.flat(); } exports.runTest = runTest; From 8cf33ccfa6cb992844a079a648248842f603a766 Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Fri, 6 Oct 2023 09:58:55 -0400 Subject: [PATCH 04/22] add test support --- test/no-default-alt-text.test.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/no-default-alt-text.test.js b/test/no-default-alt-text.test.js index de98999..314a781 100644 --- a/test/no-default-alt-text.test.js +++ b/test/no-default-alt-text.test.js @@ -71,6 +71,17 @@ describe("GH001: No Default Alt Text", () => { } }); + test("flags multiple consecutive inline images", async () => { + const strings = ['imageImage']; + const results = await runTest(strings, altTextRule); + expect(results).toHaveLength(2); + + expect(results[0].errorRange).toEqual([11, 5]); + expect(results[0].errorDetail).toEqual("Flagged alt: image"); + expect(results[1].errorRange).toEqual([28, 5]); + expect(results[1].errorDetail).toEqual("Flagged alt: Image"); + }); + test("error message", async () => { const strings = [ "![Screen Shot 2022-06-26 at 7 41 30 PM](https://user-images.githubusercontent.com/abcdef.png)", From 488fbb4ccd85c13a7e099916e4e08f63b320716a Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Thu, 5 Oct 2023 19:43:27 -0400 Subject: [PATCH 05/22] Add test cases --- docs/rules/GH003-no-empty-string-alt.md | 35 +++++++++++ test/no-empty-string-alt.test.js | 84 +++++++++++++++++++++++++ test/usage.test.js | 6 +- 3 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 docs/rules/GH003-no-empty-string-alt.md create mode 100644 test/no-empty-string-alt.test.js diff --git a/docs/rules/GH003-no-empty-string-alt.md b/docs/rules/GH003-no-empty-string-alt.md new file mode 100644 index 0000000..774adfd --- /dev/null +++ b/docs/rules/GH003-no-empty-string-alt.md @@ -0,0 +1,35 @@ +# GH003 No empty string alt + +## Rule details + +This rule is _off_ by default and is only applicable for GitHub rendered markdown. + +Currently, all images on github.com are automatically wrapped in an anchor tag. + +As a result, images that are intentionally marked as decorative (via `alt=""``) end up as a link without an accessible name. This is confusing for assistive technology users. + +This rule can be enabled to enforce that the alt attribute is always set to descriptive text. + +This rule should be removed once this behavior is updated on GitHub's UI. + +## Examples + +### Incorrect 👎 + +```md +![""](cat.png) +``` + +```html + +``` + +### Correct 👍 + +```md +![Mona Lisa](mona.png) +``` + +```html +Mona Lisa, the Octocat +``` diff --git a/test/no-empty-string-alt.test.js b/test/no-empty-string-alt.test.js new file mode 100644 index 0000000..4d106f9 --- /dev/null +++ b/test/no-empty-string-alt.test.js @@ -0,0 +1,84 @@ +const noEmptyStringAltRule = require("../src/rules/no-empty-string-alt"); +const runTest = require("./utils/run-test").runTest; + +describe("GH003: No Empty String Alt", () => { + describe("successes", () => { + test("inline", async () => { + const strings = [ + "![Chart with a single root node reading 'Example'](https://user-images.githubusercontent.com/abcdef.png)", + ]; + + const results = await runTest(strings, noEmptyStringAltRule); + + for (const result of results) { + expect(result).not.toBeDefined(); + } + }); + test("html image", async () => { + const strings = [ + 'A helpful description', + ]; + + const results = await runTest(strings, noEmptyStringAltRule); + + for (const result of results) { + expect(result).not.toBeDefined(); + } + }); + }); + describe("failures", () => { + test("markdown example", async () => { + const strings = [ + "![''](https://user-images.githubusercontent.com/abcdef.png)", + '![""](https://user-images.githubusercontent.com/abcdef.png)', + ]; + + const results = await runTest(strings, noEmptyStringAltRule); + + const failedRules = results + .map((result) => result.ruleNames) + .flat() + .filter((name) => !name.includes("GH")); + + expect(failedRules).toHaveLength(2); + for (const rule of failedRules) { + expect(rule).toBe("no-empty-string-alt"); + } + }); + + test("HTML example", async () => { + const strings = [ + '', + "", + ]; + + const results = await runTest(strings, noEmptyStringAltRule); + + const failedRules = results + .map((result) => result.ruleNames) + .flat() + .filter((name) => !name.includes("GH")); + + expect(failedRules).toHaveLength(2); + for (const rule of failedRules) { + expect(rule).toBe("no-empty-string-alt"); + } + }); + + test("error message", async () => { + const strings = [ + "![''](https://user-images.githubusercontent.com/abcdef.png)", + '', + ]; + + const results = await runTest(strings, noEmptyStringAltRule); + + expect(results[0].ruleDescription).toMatch( + "Please provide an alternative text for the image.", + ); + expect(results[1].ruleDescription).toMatch( + "Please provide an alternative text for the image.", + ); + }); + }); +}); diff --git a/test/usage.test.js b/test/usage.test.js index 24cd235..fde884e 100644 --- a/test/usage.test.js +++ b/test/usage.test.js @@ -4,8 +4,11 @@ describe("usage", () => { describe("default export", () => { test("custom rules on default export", () => { const rules = githubMarkdownLint; - expect(rules).toHaveLength(2); + expect(rules).toHaveLength(3); + expect(rules[0].names).toEqual(["GH001", "no-default-alt-text"]); + expect(rules[1].names).toEqual(["GH002", "no-generic-link-text"]); + expect(rules[2].names).toEqual(["GH003", "no-empty-string-alt"]); }); }); describe("init method", () => { @@ -17,6 +20,7 @@ describe("usage", () => { "no-space-in-links": false, "single-h1": true, "no-emphasis-as-header": true, + "no-empty-string-alt": false, "heading-increment": true, "no-generic-link-text": true, "ul-style": { From 111d3ac16c2965eac41ef450e9f960e2c79a31d0 Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Thu, 5 Oct 2023 19:43:51 -0400 Subject: [PATCH 06/22] Update configg --- index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index c16a4ff..d846595 100644 --- a/index.js +++ b/index.js @@ -6,8 +6,11 @@ const gitHubCustomRules = require("./src/rules/index").rules; module.exports = [...gitHubCustomRules]; +const offByDefault = ["no-empty-string-alt"]; + for (const rule of gitHubCustomRules) { - base[rule.names[1]] = true; + const ruleName = rule.names[1]; + base[ruleName] = offByDefault.includes(ruleName) ? false : true; } module.exports.init = function init(consumerConfig) { From 012612f22a3b809cda5d9d14e1844a2a62fa8638 Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Thu, 5 Oct 2023 19:44:13 -0400 Subject: [PATCH 07/22] Update export --- src/rules/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/rules/index.js b/src/rules/index.js index c6c9664..c17e488 100644 --- a/src/rules/index.js +++ b/src/rules/index.js @@ -1,3 +1,7 @@ module.exports = { - rules: [require("./no-default-alt-text"), require("./no-generic-link-text")], + rules: [ + require("./no-default-alt-text"), + require("./no-generic-link-text"), + require("./no-empty-string-alt"), + ], }; From 09d02ffce5e90be163982925cf13824d9f506bd7 Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Thu, 5 Oct 2023 21:01:23 -0400 Subject: [PATCH 08/22] Add new rule --- src/rules/no-empty-string-alt.js | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/rules/no-empty-string-alt.js diff --git a/src/rules/no-empty-string-alt.js b/src/rules/no-empty-string-alt.js new file mode 100644 index 0000000..c7f2d77 --- /dev/null +++ b/src/rules/no-empty-string-alt.js @@ -0,0 +1,44 @@ +module.exports = { + names: ["GH003", "no-empty-string-alt"], + description: "Please provide an alternative text for the image.", + information: new URL( + "https://github.com/github/markdownlint-github/blob/main/docs/rules/GH003-no-empty-string-alt.md", + ), + tags: ["accessibility", "images"], + function: function GH003(params, onError) { + const htmlTagsWithImages = params.parsers.markdownit.tokens.filter( + (token) => { + return token.type === "html_block" && token.content.includes(" + token.type === "inline" && + token.children.some((child) => child.type === "image"), + ); + + const htmlAltRegex = new RegExp(/alt=""|alt=''/i); + const markdownAltRegex = new RegExp(/!\[""\]|!\[''\]/i); + + for (const token of [...htmlTagsWithImages, ...inlineImages]) { + const lineRange = token.map; + const lineNumber = token.lineNumber; + const lines = params.lines.slice(lineRange[0], lineRange[1]); + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + let fails; + if (token.type === "inline") { + fails = markdownAltRegex.test(line); + } else { + fails = htmlAltRegex.test(line); + } + if (fails) { + onError({ + lineNumber: lineNumber + i, + }); + } + } + } + }, +}; From ec366050d99368f74897e9fa9449629a55bb6a92 Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Thu, 5 Oct 2023 22:24:30 -0400 Subject: [PATCH 09/22] add test cases and update docs --- docs/rules/GH003-no-empty-string-alt.md | 4 ++-- src/rules/no-empty-string-alt.js | 22 +++++++++++++++------- test/no-empty-string-alt.test.js | 6 ++++++ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/docs/rules/GH003-no-empty-string-alt.md b/docs/rules/GH003-no-empty-string-alt.md index 774adfd..4ef23ae 100644 --- a/docs/rules/GH003-no-empty-string-alt.md +++ b/docs/rules/GH003-no-empty-string-alt.md @@ -2,11 +2,11 @@ ## Rule details -This rule is _off_ by default and is only applicable for GitHub rendered markdown. +⚠️ This rule is _off_ by default and is only applicable for GitHub rendered markdown. Currently, all images on github.com are automatically wrapped in an anchor tag. -As a result, images that are intentionally marked as decorative (via `alt=""``) end up as a link without an accessible name. This is confusing for assistive technology users. +As a result, images that are intentionally marked as decorative (via `alt=""``) end up rendering as a link without an accessible name. This is confusing and inaccessible for assistive technology users. This rule can be enabled to enforce that the alt attribute is always set to descriptive text. diff --git a/src/rules/no-empty-string-alt.js b/src/rules/no-empty-string-alt.js index c7f2d77..4d79ef9 100644 --- a/src/rules/no-empty-string-alt.js +++ b/src/rules/no-empty-string-alt.js @@ -8,7 +8,12 @@ module.exports = { function: function GH003(params, onError) { const htmlTagsWithImages = params.parsers.markdownit.tokens.filter( (token) => { - return token.type === "html_block" && token.content.includes(" child.type === "html_inline")) + ); }, ); const inlineImages = params.parsers.markdownit.tokens.filter( @@ -17,8 +22,8 @@ module.exports = { token.children.some((child) => child.type === "image"), ); - const htmlAltRegex = new RegExp(/alt=""|alt=''/i); - const markdownAltRegex = new RegExp(/!\[""\]|!\[''\]/i); + const htmlAltRegex = new RegExp(/alt=""|alt=''/, "gid"); + const markdownAltRegex = new RegExp(/!\[""\]|!\[''\]/, "gid"); for (const token of [...htmlTagsWithImages, ...inlineImages]) { const lineRange = token.map; @@ -27,15 +32,18 @@ module.exports = { for (let i = 0; i < lines.length; i++) { const line = lines[i]; - let fails; + let matches; if (token.type === "inline") { - fails = markdownAltRegex.test(line); + matches = line.matchAll(markdownAltRegex); } else { - fails = htmlAltRegex.test(line); + matches = line.matchAll(htmlAltRegex); } - if (fails) { + for (const match of matches) { + const matchingContent = match[0]; + const startIndex = match.indices[0][0]; onError({ lineNumber: lineNumber + i, + range: [startIndex + 1, matchingContent.length], }); } } diff --git a/test/no-empty-string-alt.test.js b/test/no-empty-string-alt.test.js index 4d106f9..6ef849e 100644 --- a/test/no-empty-string-alt.test.js +++ b/test/no-empty-string-alt.test.js @@ -6,6 +6,8 @@ describe("GH003: No Empty String Alt", () => { test("inline", async () => { const strings = [ "![Chart with a single root node reading 'Example'](https://user-images.githubusercontent.com/abcdef.png)", + "`![''](image.png)`", // code block + '`![""](image.png)`', // code block ]; const results = await runTest(strings, noEmptyStringAltRule); @@ -17,6 +19,7 @@ describe("GH003: No Empty String Alt", () => { test("html image", async () => { const strings = [ 'A helpful description', + "` { expect(results[0].ruleDescription).toMatch( "Please provide an alternative text for the image.", ); + expect(results[0].errorRange).toEqual([1, 5]); + expect(results[1].ruleDescription).toMatch( "Please provide an alternative text for the image.", ); + expect(results[1].errorRange).toEqual([6, 6]); }); }); }); From 88e18520f21db4276b3a1038bb3fde498d734ed9 Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Fri, 6 Oct 2023 10:06:37 -0400 Subject: [PATCH 10/22] Update test matchers --- test/no-empty-string-alt.test.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/test/no-empty-string-alt.test.js b/test/no-empty-string-alt.test.js index 6ef849e..e3355f5 100644 --- a/test/no-empty-string-alt.test.js +++ b/test/no-empty-string-alt.test.js @@ -11,10 +11,7 @@ describe("GH003: No Empty String Alt", () => { ]; const results = await runTest(strings, noEmptyStringAltRule); - - for (const result of results) { - expect(result).not.toBeDefined(); - } + expect(results).toHaveLength(0); }); test("html image", async () => { const strings = [ @@ -23,10 +20,7 @@ describe("GH003: No Empty String Alt", () => { ]; const results = await runTest(strings, noEmptyStringAltRule); - - for (const result of results) { - expect(result).not.toBeDefined(); - } + expect(results).toHaveLength(0); }); }); describe("failures", () => { From e725906a4da93f6bd48546e6a28aeb1fec06b46a Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Fri, 6 Oct 2023 16:37:18 -0400 Subject: [PATCH 11/22] Update src/rules/no-empty-string-alt.js Co-authored-by: Ian Sanders --- src/rules/no-empty-string-alt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules/no-empty-string-alt.js b/src/rules/no-empty-string-alt.js index 4d79ef9..73f8d08 100644 --- a/src/rules/no-empty-string-alt.js +++ b/src/rules/no-empty-string-alt.js @@ -22,7 +22,7 @@ module.exports = { token.children.some((child) => child.type === "image"), ); - const htmlAltRegex = new RegExp(/alt=""|alt=''/, "gid"); + const htmlAltRegex = /alt=['"]['"]/gid; const markdownAltRegex = new RegExp(/!\[""\]|!\[''\]/, "gid"); for (const token of [...htmlTagsWithImages, ...inlineImages]) { From 5b17abfbc297834a94c3c91a5f18a523b7a3a8aa Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Fri, 6 Oct 2023 16:37:27 -0400 Subject: [PATCH 12/22] Update src/rules/no-empty-string-alt.js Co-authored-by: Ian Sanders --- src/rules/no-empty-string-alt.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rules/no-empty-string-alt.js b/src/rules/no-empty-string-alt.js index 73f8d08..429c261 100644 --- a/src/rules/no-empty-string-alt.js +++ b/src/rules/no-empty-string-alt.js @@ -30,8 +30,7 @@ module.exports = { const lineNumber = token.lineNumber; const lines = params.lines.slice(lineRange[0], lineRange[1]); - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; + for (const line of lines) { let matches; if (token.type === "inline") { matches = line.matchAll(markdownAltRegex); From ff25a342526e13c85456b43b76f87fcb7118e2be Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Fri, 6 Oct 2023 16:46:19 -0400 Subject: [PATCH 13/22] Revert "Update src/rules/no-empty-string-alt.js" This reverts commit 5b17abfbc297834a94c3c91a5f18a523b7a3a8aa. Reverting because we are using this index. --- src/rules/no-empty-string-alt.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rules/no-empty-string-alt.js b/src/rules/no-empty-string-alt.js index 429c261..73f8d08 100644 --- a/src/rules/no-empty-string-alt.js +++ b/src/rules/no-empty-string-alt.js @@ -30,7 +30,8 @@ module.exports = { const lineNumber = token.lineNumber; const lines = params.lines.slice(lineRange[0], lineRange[1]); - for (const line of lines) { + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; let matches; if (token.type === "inline") { matches = line.matchAll(markdownAltRegex); From 9d75081a3d8263412f33a70305c238a187aaa5b7 Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Fri, 6 Oct 2023 16:47:18 -0400 Subject: [PATCH 14/22] Fix Regex syntax --- src/rules/no-empty-string-alt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules/no-empty-string-alt.js b/src/rules/no-empty-string-alt.js index 73f8d08..f5dc68f 100644 --- a/src/rules/no-empty-string-alt.js +++ b/src/rules/no-empty-string-alt.js @@ -22,7 +22,7 @@ module.exports = { token.children.some((child) => child.type === "image"), ); - const htmlAltRegex = /alt=['"]['"]/gid; + const htmlAltRegex = new RegExp(/alt=['"]['"]/, "gid"); const markdownAltRegex = new RegExp(/!\[""\]|!\[''\]/, "gid"); for (const token of [...htmlTagsWithImages, ...inlineImages]) { From 00fc5ddefe1b02403230baccb055fdd15e698d93 Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Fri, 6 Oct 2023 16:50:04 -0400 Subject: [PATCH 15/22] Remove markdown syntax support --- src/rules/no-empty-string-alt.js | 15 ++----------- test/no-empty-string-alt.test.js | 37 +------------------------------- 2 files changed, 3 insertions(+), 49 deletions(-) diff --git a/src/rules/no-empty-string-alt.js b/src/rules/no-empty-string-alt.js index f5dc68f..f437652 100644 --- a/src/rules/no-empty-string-alt.js +++ b/src/rules/no-empty-string-alt.js @@ -16,28 +16,17 @@ module.exports = { ); }, ); - const inlineImages = params.parsers.markdownit.tokens.filter( - (token) => - token.type === "inline" && - token.children.some((child) => child.type === "image"), - ); const htmlAltRegex = new RegExp(/alt=['"]['"]/, "gid"); - const markdownAltRegex = new RegExp(/!\[""\]|!\[''\]/, "gid"); - for (const token of [...htmlTagsWithImages, ...inlineImages]) { + for (const token of htmlTagsWithImages) { const lineRange = token.map; const lineNumber = token.lineNumber; const lines = params.lines.slice(lineRange[0], lineRange[1]); for (let i = 0; i < lines.length; i++) { const line = lines[i]; - let matches; - if (token.type === "inline") { - matches = line.matchAll(markdownAltRegex); - } else { - matches = line.matchAll(htmlAltRegex); - } + const matches = line.matchAll(htmlAltRegex); for (const match of matches) { const matchingContent = match[0]; const startIndex = match.indices[0][0]; diff --git a/test/no-empty-string-alt.test.js b/test/no-empty-string-alt.test.js index e3355f5..a75bc62 100644 --- a/test/no-empty-string-alt.test.js +++ b/test/no-empty-string-alt.test.js @@ -3,16 +3,6 @@ const runTest = require("./utils/run-test").runTest; describe("GH003: No Empty String Alt", () => { describe("successes", () => { - test("inline", async () => { - const strings = [ - "![Chart with a single root node reading 'Example'](https://user-images.githubusercontent.com/abcdef.png)", - "`![''](image.png)`", // code block - '`![""](image.png)`', // code block - ]; - - const results = await runTest(strings, noEmptyStringAltRule); - expect(results).toHaveLength(0); - }); test("html image", async () => { const strings = [ 'A helpful description', @@ -24,25 +14,6 @@ describe("GH003: No Empty String Alt", () => { }); }); describe("failures", () => { - test("markdown example", async () => { - const strings = [ - "![''](https://user-images.githubusercontent.com/abcdef.png)", - '![""](https://user-images.githubusercontent.com/abcdef.png)', - ]; - - const results = await runTest(strings, noEmptyStringAltRule); - - const failedRules = results - .map((result) => result.ruleNames) - .flat() - .filter((name) => !name.includes("GH")); - - expect(failedRules).toHaveLength(2); - for (const rule of failedRules) { - expect(rule).toBe("no-empty-string-alt"); - } - }); - test("HTML example", async () => { const strings = [ '', @@ -64,7 +35,6 @@ describe("GH003: No Empty String Alt", () => { test("error message", async () => { const strings = [ - "![''](https://user-images.githubusercontent.com/abcdef.png)", '', ]; @@ -73,12 +43,7 @@ describe("GH003: No Empty String Alt", () => { expect(results[0].ruleDescription).toMatch( "Please provide an alternative text for the image.", ); - expect(results[0].errorRange).toEqual([1, 5]); - - expect(results[1].ruleDescription).toMatch( - "Please provide an alternative text for the image.", - ); - expect(results[1].errorRange).toEqual([6, 6]); + expect(results[0].errorRange).toEqual([6, 6]); }); }); }); From 0ba6e3cd44589a21b6c1aef5c7dc4deba439cfb4 Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Fri, 6 Oct 2023 16:50:30 -0400 Subject: [PATCH 16/22] Update doc to remove markdown syntax --- docs/rules/GH003-no-empty-string-alt.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/rules/GH003-no-empty-string-alt.md b/docs/rules/GH003-no-empty-string-alt.md index 4ef23ae..6d6b36f 100644 --- a/docs/rules/GH003-no-empty-string-alt.md +++ b/docs/rules/GH003-no-empty-string-alt.md @@ -16,20 +16,12 @@ This rule should be removed once this behavior is updated on GitHub's UI. ### Incorrect 👎 -```md -![""](cat.png) -``` - ```html ``` ### Correct 👍 -```md -![Mona Lisa](mona.png) -``` - ```html Mona Lisa, the Octocat ``` From 8f80d4d8368154ad4c9a0e1b244aff285f9c84c0 Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Fri, 6 Oct 2023 16:55:06 -0400 Subject: [PATCH 17/22] Add test case for multiple images in one line Related:https://github.com/github/markdownlint-github/pull/85#discussion_r1348919044 --- test/no-empty-string-alt.test.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test/no-empty-string-alt.test.js b/test/no-empty-string-alt.test.js index a75bc62..0ae2a95 100644 --- a/test/no-empty-string-alt.test.js +++ b/test/no-empty-string-alt.test.js @@ -18,6 +18,7 @@ describe("GH003: No Empty String Alt", () => { const strings = [ '', "", + ' ', ]; const results = await runTest(strings, noEmptyStringAltRule); @@ -27,7 +28,7 @@ describe("GH003: No Empty String Alt", () => { .flat() .filter((name) => !name.includes("GH")); - expect(failedRules).toHaveLength(2); + expect(failedRules).toHaveLength(4); for (const rule of failedRules) { expect(rule).toBe("no-empty-string-alt"); } @@ -36,6 +37,7 @@ describe("GH003: No Empty String Alt", () => { test("error message", async () => { const strings = [ '', + ' ', ]; const results = await runTest(strings, noEmptyStringAltRule); @@ -44,6 +46,15 @@ describe("GH003: No Empty String Alt", () => { "Please provide an alternative text for the image.", ); expect(results[0].errorRange).toEqual([6, 6]); + + expect(results[1].ruleDescription).toMatch( + "Please provide an alternative text for the image.", + ); + expect(results[1].errorRange).toEqual([20, 6]); + expect(results[2].ruleDescription).toMatch( + "Please provide an alternative text for the image.", + ); + expect(results[2].errorRange).toEqual([49, 6]); }); }); }); From f2d977498194b03bcd4a6c1cf1264834f148dcf3 Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Fri, 6 Oct 2023 16:57:42 -0400 Subject: [PATCH 18/22] Rename rule to no-empty-alt-text --- ...03-no-empty-string-alt.md => GH003-no-empty-alt-text.md} | 2 +- index.js | 2 +- src/rules/index.js | 2 +- src/rules/{no-empty-string-alt.js => no-empty-alt-text.js} | 4 ++-- ...o-empty-string-alt.test.js => no-empty-alt-text.test.js} | 6 +++--- test/usage.test.js | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) rename docs/rules/{GH003-no-empty-string-alt.md => GH003-no-empty-alt-text.md} (96%) rename src/rules/{no-empty-string-alt.js => no-empty-alt-text.js} (94%) rename test/{no-empty-string-alt.test.js => no-empty-alt-text.test.js} (91%) diff --git a/docs/rules/GH003-no-empty-string-alt.md b/docs/rules/GH003-no-empty-alt-text.md similarity index 96% rename from docs/rules/GH003-no-empty-string-alt.md rename to docs/rules/GH003-no-empty-alt-text.md index 6d6b36f..5a4f73a 100644 --- a/docs/rules/GH003-no-empty-string-alt.md +++ b/docs/rules/GH003-no-empty-alt-text.md @@ -1,4 +1,4 @@ -# GH003 No empty string alt +# GH003 No Empty Alt Text ## Rule details diff --git a/index.js b/index.js index d846595..06ba60c 100644 --- a/index.js +++ b/index.js @@ -6,7 +6,7 @@ const gitHubCustomRules = require("./src/rules/index").rules; module.exports = [...gitHubCustomRules]; -const offByDefault = ["no-empty-string-alt"]; +const offByDefault = ["no-empty-alt-text"]; for (const rule of gitHubCustomRules) { const ruleName = rule.names[1]; diff --git a/src/rules/index.js b/src/rules/index.js index c17e488..08fd9e6 100644 --- a/src/rules/index.js +++ b/src/rules/index.js @@ -2,6 +2,6 @@ module.exports = { rules: [ require("./no-default-alt-text"), require("./no-generic-link-text"), - require("./no-empty-string-alt"), + require("./no-empty-alt-text"), ], }; diff --git a/src/rules/no-empty-string-alt.js b/src/rules/no-empty-alt-text.js similarity index 94% rename from src/rules/no-empty-string-alt.js rename to src/rules/no-empty-alt-text.js index f437652..d28279b 100644 --- a/src/rules/no-empty-string-alt.js +++ b/src/rules/no-empty-alt-text.js @@ -1,8 +1,8 @@ module.exports = { - names: ["GH003", "no-empty-string-alt"], + names: ["GH003", "no-empty-alt-text"], description: "Please provide an alternative text for the image.", information: new URL( - "https://github.com/github/markdownlint-github/blob/main/docs/rules/GH003-no-empty-string-alt.md", + "https://github.com/github/markdownlint-github/blob/main/docs/rules/GH003-no-empty-alt-text.md", ), tags: ["accessibility", "images"], function: function GH003(params, onError) { diff --git a/test/no-empty-string-alt.test.js b/test/no-empty-alt-text.test.js similarity index 91% rename from test/no-empty-string-alt.test.js rename to test/no-empty-alt-text.test.js index 0ae2a95..5f0a671 100644 --- a/test/no-empty-string-alt.test.js +++ b/test/no-empty-alt-text.test.js @@ -1,7 +1,7 @@ -const noEmptyStringAltRule = require("../src/rules/no-empty-string-alt"); +const noEmptyStringAltRule = require("../src/rules/no-empty-alt-text"); const runTest = require("./utils/run-test").runTest; -describe("GH003: No Empty String Alt", () => { +describe("GH003: No Empty Alt Text", () => { describe("successes", () => { test("html image", async () => { const strings = [ @@ -30,7 +30,7 @@ describe("GH003: No Empty String Alt", () => { expect(failedRules).toHaveLength(4); for (const rule of failedRules) { - expect(rule).toBe("no-empty-string-alt"); + expect(rule).toBe("no-empty-alt-text"); } }); diff --git a/test/usage.test.js b/test/usage.test.js index fde884e..a87d10b 100644 --- a/test/usage.test.js +++ b/test/usage.test.js @@ -8,7 +8,7 @@ describe("usage", () => { expect(rules[0].names).toEqual(["GH001", "no-default-alt-text"]); expect(rules[1].names).toEqual(["GH002", "no-generic-link-text"]); - expect(rules[2].names).toEqual(["GH003", "no-empty-string-alt"]); + expect(rules[2].names).toEqual(["GH003", "no-empty-alt-text"]); }); }); describe("init method", () => { @@ -20,7 +20,7 @@ describe("usage", () => { "no-space-in-links": false, "single-h1": true, "no-emphasis-as-header": true, - "no-empty-string-alt": false, + "no-empty-alt-text": false, "heading-increment": true, "no-generic-link-text": true, "ul-style": { From 6bf95bb259bb69792a5a8f8b8994f78b9a40b4e2 Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Fri, 6 Oct 2023 16:58:40 -0400 Subject: [PATCH 19/22] Add missing bracket --- test/no-empty-alt-text.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/no-empty-alt-text.test.js b/test/no-empty-alt-text.test.js index 5f0a671..0b6fd43 100644 --- a/test/no-empty-alt-text.test.js +++ b/test/no-empty-alt-text.test.js @@ -6,7 +6,7 @@ describe("GH003: No Empty Alt Text", () => { test("html image", async () => { const strings = [ 'A helpful description', - "``", // code block ]; const results = await runTest(strings, noEmptyStringAltRule); From f11f8001171cf9075389d3f5e4c8316907849520 Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Fri, 6 Oct 2023 16:59:17 -0400 Subject: [PATCH 20/22] Update README with the rule --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 073b602..3bc08be 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ The following are custom rules defined in this plugin. * [**GH001** _no-default-alt-text_](./docs/rules/GH001-no-default-alt-text.md) * [**GH002** _no-generic-link-text_](./docs/rules/GH002-no-generic-link-text.md) +* [**GH002** _no-empty-alt-text_](./docs/rules/GH002-no-empty-alt-text.md) See [`markdownlint` rules](https://github.com/DavidAnson/markdownlint#rules--aliases) for documentation on rules pulled in from `markdownlint`. From c60b8a7def99014d94c7709dde1c92de7d617c43 Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Mon, 9 Oct 2023 11:02:08 -0400 Subject: [PATCH 21/22] Update docs/rules/GH003-no-empty-alt-text.md Co-authored-by: Ian Sanders --- docs/rules/GH003-no-empty-alt-text.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rules/GH003-no-empty-alt-text.md b/docs/rules/GH003-no-empty-alt-text.md index 5a4f73a..dc73ef6 100644 --- a/docs/rules/GH003-no-empty-alt-text.md +++ b/docs/rules/GH003-no-empty-alt-text.md @@ -6,7 +6,7 @@ Currently, all images on github.com are automatically wrapped in an anchor tag. -As a result, images that are intentionally marked as decorative (via `alt=""``) end up rendering as a link without an accessible name. This is confusing and inaccessible for assistive technology users. +As a result, images that are intentionally marked as decorative (via `alt=""`) end up rendering as a link without an accessible name. This is confusing and inaccessible for assistive technology users. This rule can be enabled to enforce that the alt attribute is always set to descriptive text. From 0927174f67cf2b1ad8eba0ff3f1c2bcb3e20a148 Mon Sep 17 00:00:00 2001 From: Kate Higa <16447748+khiga8@users.noreply.github.com> Date: Mon, 9 Oct 2023 11:02:17 -0400 Subject: [PATCH 22/22] Update src/rules/no-empty-alt-text.js Co-authored-by: Ian Sanders --- src/rules/no-empty-alt-text.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rules/no-empty-alt-text.js b/src/rules/no-empty-alt-text.js index d28279b..02cab26 100644 --- a/src/rules/no-empty-alt-text.js +++ b/src/rules/no-empty-alt-text.js @@ -24,8 +24,7 @@ module.exports = { const lineNumber = token.lineNumber; const lines = params.lines.slice(lineRange[0], lineRange[1]); - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; + for (const [i, line] of lines.entries()) { const matches = line.matchAll(htmlAltRegex); for (const match of matches) { const matchingContent = match[0];