Skip to content

Commit

Permalink
Introduce a no-empty-alt-text rule (#85)
Browse files Browse the repository at this point in the history
* Make sure that HTML that is inlined is supported

* Add detail

* Update the helpers to support multiple errors in one line

* add test support

* Add test cases

* Update configg

* Update export

* Add new rule

* add test cases and update docs

* Update test matchers

* Update src/rules/no-empty-string-alt.js

Co-authored-by: Ian Sanders <[email protected]>

* Update src/rules/no-empty-string-alt.js

Co-authored-by: Ian Sanders <[email protected]>

* Revert "Update src/rules/no-empty-string-alt.js"

This reverts commit 5b17abf.

Reverting because we are using this index.

* Fix Regex syntax

* Remove markdown syntax support

* Update doc to remove markdown syntax

* Add test case for multiple images in one line

Related:#85 (comment)

* Rename rule to no-empty-alt-text

* Add missing bracket

* Update README with the rule

* Update docs/rules/GH003-no-empty-alt-text.md

Co-authored-by: Ian Sanders <[email protected]>

* Update src/rules/no-empty-alt-text.js

Co-authored-by: Ian Sanders <[email protected]>

---------

Co-authored-by: Ian Sanders <[email protected]>
  • Loading branch information
khiga8 and iansan5653 authored Oct 9, 2023
1 parent c29f004 commit ccc7d23
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 3 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

Expand Down
27 changes: 27 additions & 0 deletions docs/rules/GH003-no-empty-alt-text.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# GH003 No Empty Alt Text

## 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 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.

This rule should be removed once this behavior is updated on GitHub's UI.

## Examples

### Incorrect 👎

```html
<img src="cat.png" alt="">
```

### Correct 👍

```html
<img src="mona.png" alt="Mona Lisa, the Octocat">
```
5 changes: 4 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ const gitHubCustomRules = require("./src/rules/index").rules;

module.exports = [...gitHubCustomRules];

const offByDefault = ["no-empty-alt-text"];

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) {
Expand Down
6 changes: 5 additions & 1 deletion src/rules/index.js
Original file line number Diff line number Diff line change
@@ -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-alt-text"),
],
};
40 changes: 40 additions & 0 deletions src/rules/no-empty-alt-text.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
module.exports = {
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-alt-text.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("<img")) ||
(token.type === "inline" &&
token.content.includes("<img") &&
token.children.some((child) => child.type === "html_inline"))
);
},
);

const htmlAltRegex = new RegExp(/alt=['"]['"]/, "gid");

for (const token of htmlTagsWithImages) {
const lineRange = token.map;
const lineNumber = token.lineNumber;
const lines = params.lines.slice(lineRange[0], lineRange[1]);

for (const [i, line] of lines.entries()) {
const matches = line.matchAll(htmlAltRegex);
for (const match of matches) {
const matchingContent = match[0];
const startIndex = match.indices[0][0];
onError({
lineNumber: lineNumber + i,
range: [startIndex + 1, matchingContent.length],
});
}
}
}
},
};
60 changes: 60 additions & 0 deletions test/no-empty-alt-text.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const noEmptyStringAltRule = require("../src/rules/no-empty-alt-text");
const runTest = require("./utils/run-test").runTest;

describe("GH003: No Empty Alt Text", () => {
describe("successes", () => {
test("html image", async () => {
const strings = [
'<img alt="A helpful description" src="https://user-images.githubusercontent.com/abcdef.png">',
"`<img alt='' src='image.png'>`", // code block
];

const results = await runTest(strings, noEmptyStringAltRule);
expect(results).toHaveLength(0);
});
});
describe("failures", () => {
test("HTML example", async () => {
const strings = [
'<img alt="" src="https://user-images.githubusercontent.com/abcdef.png">',
"<img alt='' src='https://user-images.githubusercontent.com/abcdef.png'>",
'<img src="cat.png" alt="" /> <img src="dog.png" alt="" />',
];

const results = await runTest(strings, noEmptyStringAltRule);

const failedRules = results
.map((result) => result.ruleNames)
.flat()
.filter((name) => !name.includes("GH"));

expect(failedRules).toHaveLength(4);
for (const rule of failedRules) {
expect(rule).toBe("no-empty-alt-text");
}
});

test("error message", async () => {
const strings = [
'<img alt="" src="https://user-images.githubusercontent.com/abcdef.png">',
'<img src="cat.png" alt="" /> <img src="dog.png" alt="" />',
];

const results = await runTest(strings, noEmptyStringAltRule);

expect(results[0].ruleDescription).toMatch(
"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]);
});
});
});
6 changes: 5 additions & 1 deletion test/usage.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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-alt-text"]);
});
});
describe("init method", () => {
Expand All @@ -17,6 +20,7 @@ describe("usage", () => {
"no-space-in-links": false,
"single-h1": true,
"no-emphasis-as-header": true,
"no-empty-alt-text": false,
"heading-increment": true,
"no-generic-link-text": true,
"ul-style": {
Expand Down

0 comments on commit ccc7d23

Please sign in to comment.