Repository: ymrl/a11y-visualizer
Pull request #28 (2024-04-28 14:18:05)

:bug: handle aria-hidden svg image as image


Patch:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 49bd276..17f373f 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -25,14 +25,14 @@ jobs:
       - name: Install dependencies
         run: npm ci
 
+      - name: Release
+        run: npm run release -- ${{ github.event.inputs.release_type }}
+
       - name: Git config
         run: |
           git config --global user.name 'github-actions[bot]'
           git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com'
 
-      - name: Release
-        run: npm run release -- ${{ github.event.inputs.release_type }}
-
       - name: Push changes
         uses: ad-m/github-push-action@v0.6.0
         with:
@@ -42,7 +42,7 @@ jobs:
 
       - name: Get version
         id: get_version
-        run: echo "::set-output name=VERSION::$(jq -r .version package.json)"
+        run: echo "::set-output name=VERSION::$(node -e \"console.log(require('./package.json').version)\")"
 
       - name: Create Release
         id: create_release
diff --git a/manifest.json b/manifest.json
index 70024e6..5bd4e31 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,7 +1,7 @@
 {
   "manifest_version": 3,
   "name": "Accessibility Visualizer",
-  "version": "0.4.0",
+  "version": "0.3.0",
   "action": {
     "default_popup": "src/popup/index.html"
   },
diff --git a/package-lock.json b/package-lock.json
index 2ebfbe2..d000fc1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "a11y-visualizer",
-  "version": "0.4.0",
+  "version": "0.3.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "a11y-visualizer",
-      "version": "0.4.0",
+      "version": "0.3.0",
       "dependencies": {
         "dom-accessibility-api": "^0.6.3",
         "i18next": "^23.10.1",
diff --git a/package.json b/package.json
index f263465..49f1bd3 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "a11y-visualizer",
   "private": true,
-  "version": "0.4.0",
+  "version": "0.3.0",
   "type": "module",
   "scripts": {
     "dev": "vite",
diff --git a/src/content/dom/collectElements.ts b/src/content/dom/collectElements.ts
index 1107f5b..d604b04 100644
--- a/src/content/dom/collectElements.ts
+++ b/src/content/dom/collectElements.ts
@@ -3,53 +3,20 @@ import { getPositionBaseElement } from "./getPositionBaseElement";
 import { isHidden } from "./isHidden";
 import { getElementPosition } from "./getElementPosition";
 import { globalTips } from "./tips/globalTips";
-import { ariaHiddenTips } from "./tips/ariaHiddenTips";
-import { headingTips } from "./tips/headingTips";
-import { linkTips } from "./tips/linkTips";
-import { buttonTips } from "./tips/buttonTips";
-import { formTips } from "./tips/formTips";
-import { imageTips } from "./tips/imageTips";
+import { AriraHiddenSelectors, ariaHiddenTips } from "./tips/ariaHiddenTips";
+import { HeadingSelectors, headingTips } from "./tips/headingTips";
+import { LinkSelectors, linkTips } from "./tips/linkTips";
+import { ButtonSelectors, buttonTips } from "./tips/buttonTips";
+import { FormSelectors, formTips } from "./tips/formTips";
+import { imageTips, isImage, ImageSelectors } from "./tips/imageTips";
 
 const Selector = [
-  // images
-  "img",
-  "svg",
-  '[role="img"]',
-
-  // form controls
-  "input:not([type='hidden'])",
-  "textarea",
-  "select",
-  "button",
-  '[role="button"]',
-  '[role="checkbox"]',
-  '[role="combobox"]',
-  '[role="radio"]',
-  '[role="searchbox"]',
-  '[role="slider"]',
-  '[role="spinbutton"]',
-  '[role="switch"]',
-  '[role="textbox"]',
-  '[role="menuitemcheckbox"]',
-  '[role="menuitemradio"]',
-  "label",
-
-  // links
-  "a",
-  "map area",
-  '[role="link"]',
-
-  // headings
-  "h1",
-  "h2",
-  "h3",
-  "h4",
-  "h5",
-  "h6",
-  '[role="heading"]',
-
-  // aria-hidden
-  '[aria-hidden="true"]',
+  ...(ImageSelectors || []),
+  ...(FormSelectors || []),
+  ...(LinkSelectors || []),
+  ...(HeadingSelectors || []),
+  ...(ButtonSelectors || []),
+  ...(AriraHiddenSelectors || []),
 ].join(",");
 
 export const collectElements = (
@@ -102,7 +69,7 @@ export const collectElements = (
           ...getElementPosition(el, w, offsetX, offsetY),
           hidden: isHidden(el),
           categories: [
-            images.length > 0 ? "image" : "",
+            isImage(el) ? "image" : "",
             forms.length > 0 ? "formControl" : "",
             buttons.length > 0 ? "button" : "",
             links.length > 0 ? "link" : "",
diff --git a/src/content/dom/tips/ariaHiddenTips.ts b/src/content/dom/tips/ariaHiddenTips.ts
index 8374dc3..71b6586 100644
--- a/src/content/dom/tips/ariaHiddenTips.ts
+++ b/src/content/dom/tips/ariaHiddenTips.ts
@@ -1,5 +1,7 @@
 import { ElementTip } from "../../types";
 import { isAriaHidden } from "../isAriaHidden";
 
+export const AriraHiddenSelectors = ['[aria-hidden="true"]'] as const;
+
 export const ariaHiddenTips = (el: Element): ElementTip[] =>
   isAriaHidden(el) ? [{ type: "warning", content: "messages.ariaHidden" }] : [];
diff --git a/src/content/dom/tips/buttonTips.ts b/src/content/dom/tips/buttonTips.ts
index 215bc66..44ced19 100644
--- a/src/content/dom/tips/buttonTips.ts
+++ b/src/content/dom/tips/buttonTips.ts
@@ -3,6 +3,15 @@ import { ElementTip } from "../../types";
 import { isAriaHidden } from "../isAriaHidden";
 import { isFocusable } from "../isFocusable";
 
+export const ButtonSelectors = [
+  "button",
+  '[role="button"]',
+  'input[type="button"]',
+  'input[type="submit"]',
+  'input[type="reset"]',
+  'input[type="image"]',
+] as const;
+
 export const buttonTips = (el: Element): ElementTip[] => {
   const result: ElementTip[] = [];
   const tagName = el.tagName.toLowerCase();
diff --git a/src/content/dom/tips/formTips.ts b/src/content/dom/tips/formTips.ts
index 99c573d..4282577 100644
--- a/src/content/dom/tips/formTips.ts
+++ b/src/content/dom/tips/formTips.ts
@@ -2,6 +2,23 @@ import { computeAccessibleName } from "dom-accessibility-api";
 import { ElementTip } from "../../types";
 import { isAriaHidden, isFocusable, isHidden } from "../index";
 
+export const FormSelectors = [
+  "input:not([type='hidden'])",
+  "textarea",
+  "select",
+  '[role="checkbox"]',
+  '[role="combobox"]',
+  '[role="radio"]',
+  '[role="searchbox"]',
+  '[role="slider"]',
+  '[role="spinbutton"]',
+  '[role="switch"]',
+  '[role="textbox"]',
+  '[role="menuitemcheckbox"]',
+  '[role="menuitemradio"]',
+  "label",
+] as const;
+
 const LABELABLE_SELECTOR = [
   "button",
   "input:not([type='hidden'])",
diff --git a/src/content/dom/tips/headingTips.ts b/src/content/dom/tips/headingTips.ts
index 522fb33..a70154c 100644
--- a/src/content/dom/tips/headingTips.ts
+++ b/src/content/dom/tips/headingTips.ts
@@ -2,6 +2,16 @@ import { computeAccessibleName } from "dom-accessibility-api";
 import { ElementTip } from "../../types";
 import { isAriaHidden } from "../isAriaHidden";
 
+export const HeadingSelectors = [
+  "h1",
+  "h2",
+  "h3",
+  "h4",
+  "h5",
+  "h6",
+  '[role="heading"]',
+] as const;
+
 export const headingTips = (el: Element): ElementTip[] => {
   const result: ElementTip[] = [];
   const tagName = el.tagName.toLowerCase();
diff --git a/src/content/dom/tips/imageTips.ts b/src/content/dom/tips/imageTips.ts
index aada76d..511f062 100644
--- a/src/content/dom/tips/imageTips.ts
+++ b/src/content/dom/tips/imageTips.ts
@@ -2,11 +2,20 @@ import { computeAccessibleName } from "dom-accessibility-api";
 import { ElementTip } from "../../types";
 import { isAriaHidden } from "../index";
 
+export const ImageSelectors = ["img", "svg", '[role="img"]'] as const;
+
+export const isImage = (el: Element): boolean => {
+  const tagName = el.tagName.toLowerCase();
+  return (
+    tagName === "img" || tagName === "svg" || el.getAttribute("role") === "img"
+  );
+};
+
 export const imageTips = (el: Element): ElementTip[] => {
   const result: ElementTip[] = [];
   const tagName = el.tagName.toLowerCase();
   const roleAttr = el.getAttribute("role") || "";
-  if (tagName === "img" || roleAttr === "img" || tagName === "svg") {
+  if (isImage(el)) {
     const name = computeAccessibleName(el);
     if (name) {
       result.push({ type: "name", content: name });
diff --git a/src/content/dom/tips/linkTips.ts b/src/content/dom/tips/linkTips.ts
index b296625..ec3660d 100644
--- a/src/content/dom/tips/linkTips.ts
+++ b/src/content/dom/tips/linkTips.ts
@@ -2,6 +2,9 @@ import { computeAccessibleName } from "dom-accessibility-api";
 import { ElementTip } from "../../types";
 import { isAriaHidden } from "../isAriaHidden";
 import { isFocusable } from "../isFocusable";
+
+export const LinkSelectors = ["a", "area", '[role="link"]'] as const;
+
 export const linkTips = (el: Element): ElementTip[] => {
   const result: ElementTip[] = [];
   const tagName = el.tagName.toLowerCase();


Test patch:
diff --git a/src/content/dom/tips/imageTips.test.ts b/src/content/dom/tips/imageTips.test.ts
index 00198cf..086ad19 100644
--- a/src/content/dom/tips/imageTips.test.ts
+++ b/src/content/dom/tips/imageTips.test.ts
@@ -1,5 +1,35 @@
 import { describe, test, expect, afterEach } from "vitest";
-import { imageTips } from "./imageTips";
+import { imageTips, isImage } from "./imageTips";
+
+describe("isImage()", () => {
+  test("div", () => {
+    const element = document.createElement("div");
+    expect(isImage(element)).toBe(false);
+  });
+  test("img", () => {
+    const element = document.createElement("img");
+    expect(isImage(element)).toBe(true);
+  });
+  test("svg", () => {
+    const element = document.createElement("svg");
+    expect(isImage(element)).toBe(true);
+  });
+  test("role=img", () => {
+    const element = document.createElement("div");
+    element.setAttribute("role", "img");
+    expect(isImage(element)).toBe(true);
+  });
+  test("role=button", () => {
+    const element = document.createElement("div");
+    element.setAttribute("role", "button");
+    expect(isImage(element)).toBe(false);
+  });
+  test("svg role=presentation", () => {
+    const element = document.createElement("svg");
+    element.setAttribute("role", "presentation");
+    expect(isImage(element)).toBe(true);
+  });
+});
 
 describe("imageTips()", () => {
   afterEach(() => {