GithubHelp home page GithubHelp logo

dom-accessibility-api's Introduction

dom-accessibility-api

npm version Build Status Azure DevOps coverage

Computes the accessible name or description of a given DOM Element. https://w3c.github.io/accname/ implemented in JavaScript for testing.

$ yarn add dom-accessibility-api
import {
	computeAccessibleName,
	computeAccessibleDescription,
} from "dom-accessibility-api";

I'm not an editor of any of the referenced specs (nor very experience with using them) so if you got any insights, something catches your eye please open an issue.

Supported environments

WARNING: Only active node versions are supported. Inactive node versions can stop working in a SemVer MINOR release.

ie 11
edge >= 14
firefox >= 52
chrome >= 49
safari >= 10
node 10.0

progress

Using https://github.com/web-platform-tests/wpt. Be sure to init submodules when cloning. See the test readme for more info about the test setup.

browser (Chrome)

153/159

jsdom

report 138/159 passing of which 15 are due `::before { content }`, one might be a wrong test, 5 are pathological
  web-platform-tests
    accname
      ✓ [expected fail] description_1.0_combobox-focusable-manual.html
      ✓ [expected fail] description_from_content_of_describedby_element-manual.html
      ✓ description_link-with-label-manual.html
      ✓ description_test_case_557-manual.html
      ✓ description_test_case_664-manual.html
      ✓ description_test_case_665-manual.html
      ✓ description_test_case_666-manual.html
      ✓ description_test_case_772-manual.html
      ✓ description_test_case_773-manual.html
      ✓ description_test_case_774-manual.html
      ✓ description_test_case_838-manual.html
      ✓ description_test_case_broken_reference-manual.html
      ✓ description_test_case_one_valid_reference-manual.html
      ✓ description_title-same-element-manual.html
      ✓ name_1.0_combobox-focusable-alternative-manual.html
      ✓ name_1.0_combobox-focusable-manual.html
      ✓ name_checkbox-label-embedded-combobox-manual.html
      ✓ name_checkbox-label-embedded-listbox-manual.html
      ✓ name_checkbox-label-embedded-menu-manual.html
      ✓ name_checkbox-label-embedded-select-manual.html
      ✓ name_checkbox-label-embedded-slider-manual.html
      ✓ name_checkbox-label-embedded-spinbutton-manual.html
      ✓ name_checkbox-label-embedded-textbox-manual.html
      ✓ name_checkbox-label-multiple-label-alternative-manual.html
      ✓ name_checkbox-label-multiple-label-manual.html
      ✓ name_checkbox-title-manual.html
      ✓ name_file-label-embedded-combobox-manual.html
      ✓ name_file-label-embedded-menu-manual.html
      ✓ name_file-label-embedded-select-manual.html
      ✓ name_file-label-embedded-slider-manual.html
      ✓ name_file-label-embedded-spinbutton-manual.html
      ✓ [expected fail] name_file-label-inline-block-elements-manual.html
      ✓ [expected fail] name_file-label-inline-block-styles-manual.html
      ✓ name_file-label-inline-hidden-elements-manual.html
      ✓ name_file-label-owned-combobox-manual.html
      ✓ name_file-label-owned-combobox-owned-listbox-manual.html
      ✓ name_file-title-manual.html
      ✓ name_from_content-manual.html
      ✓ name_from_content_of_label-manual.html
      ✓ name_from_content_of_labelledby_element-manual.html
      ✓ name_from_content_of_labelledby_elements_one_of_which_is_hidden-manual.html
      ✓ name_heading-combobox-focusable-alternative-manual.html
      ✓ name_image-title-manual.html
      ✓ name_link-mixed-content-manual.html
      ✓ name_link-with-label-manual.html
      ✓ name_password-label-embedded-combobox-manual.html
      ✓ name_password-label-embedded-menu-manual.html
      ✓ name_password-label-embedded-select-manual.html
      ✓ name_password-label-embedded-slider-manual.html
      ✓ name_password-label-embedded-spinbutton-manual.html
      ✓ name_password-title-manual.html
      ✓ name_radio-label-embedded-combobox-manual.html
      ✓ name_radio-label-embedded-menu-manual.html
      ✓ name_radio-label-embedded-select-manual.html
      ✓ name_radio-label-embedded-slider-manual.html
      ✓ name_radio-label-embedded-spinbutton-manual.html
      ✓ name_radio-title-manual.html
      ✓ name_test_case_539-manual.html
      ✓ name_test_case_540-manual.html
      ✓ name_test_case_541-manual.html
      ✓ name_test_case_543-manual.html
      ✓ name_test_case_544-manual.html
      ✓ name_test_case_545-manual.html
      ✓ name_test_case_546-manual.html
      ✓ name_test_case_547-manual.html
      ✓ name_test_case_548-manual.html
      ✓ name_test_case_549-manual.html
      ✓ name_test_case_550-manual.html
      ✓ name_test_case_551-manual.html
      ✓ [expected fail] name_test_case_552-manual.html
      ✓ [expected fail] name_test_case_553-manual.html
      ✓ name_test_case_556-manual.html
      ✓ name_test_case_557-manual.html
      ✓ name_test_case_558-manual.html
      ✓ name_test_case_559-manual.html
      ✓ name_test_case_560-manual.html
      ✓ name_test_case_561-manual.html
      ✓ name_test_case_562-manual.html
      ✓ name_test_case_563-manual.html
      ✓ name_test_case_564-manual.html
      ✓ name_test_case_565-manual.html
      ✓ name_test_case_566-manual.html
      ✓ name_test_case_596-manual.html
      ✓ name_test_case_597-manual.html
      ✓ name_test_case_598-manual.html
      ✓ name_test_case_599-manual.html
      ✓ name_test_case_600-manual.html
      ✓ name_test_case_601-manual.html
      ✓ name_test_case_602-manual.html
      ✓ name_test_case_603-manual.html
      ✓ name_test_case_604-manual.html
      ✓ name_test_case_605-manual.html
      ✓ name_test_case_606-manual.html
      ✓ name_test_case_607-manual.html
      ✓ name_test_case_608-manual.html
      ✓ name_test_case_609-manual.html
      ✓ name_test_case_610-manual.html
      ✓ name_test_case_611-manual.html
      ✓ name_test_case_612-manual.html
      ✓ name_test_case_613-manual.html
      ✓ name_test_case_614-manual.html
      ✓ name_test_case_615-manual.html
      ✓ name_test_case_616-manual.html
      ✓ name_test_case_617-manual.html
      ✓ name_test_case_618-manual.html
      ✓ name_test_case_619-manual.html
      ✓ name_test_case_620-manual.html
      ✓ name_test_case_621-manual.html
      ✓ [expected fail] name_test_case_659-manual.html
      ✓ [expected fail] name_test_case_660-manual.html
      ✓ [expected fail] name_test_case_661-manual.html
      ✓ [expected fail] name_test_case_662-manual.html
      ✓ [expected fail] name_test_case_663a-manual.html
      ✓ name_test_case_721-manual.html
      ✓ name_test_case_723-manual.html
      ✓ name_test_case_724-manual.html
      ✓ name_test_case_725-manual.html
      ✓ name_test_case_726-manual.html
      ✓ name_test_case_727-manual.html
      ✓ name_test_case_728-manual.html
      ✓ name_test_case_729-manual.html
      ✓ name_test_case_730-manual.html
      ✓ name_test_case_731-manual.html
      ✓ name_test_case_733-manual.html
      ✓ name_test_case_734-manual.html
      ✓ name_test_case_735-manual.html
      ✓ name_test_case_736-manual.html
      ✓ name_test_case_737-manual.html
      ✓ name_test_case_738-manual.html
      ✓ name_test_case_739-manual.html
      ✓ name_test_case_740-manual.html
      ✓ name_test_case_741-manual.html
      ✓ name_test_case_742-manual.html
      ✓ name_test_case_743-manual.html
      ✓ name_test_case_744-manual.html
      ✓ name_test_case_745-manual.html
      ✓ name_test_case_746-manual.html
      ✓ name_test_case_747-manual.html
      ✓ name_test_case_748-manual.html
      ✓ name_test_case_749-manual.html
      ✓ name_test_case_750-manual.html
      ✓ name_test_case_751-manual.html
      ✓ name_test_case_752-manual.html
      ✓ [expected fail] name_test_case_753-manual.html
      ✓ [expected fail] name_test_case_754-manual.html
      ✓ [expected fail] name_test_case_755-manual.html
      ✓ [expected fail] name_test_case_756-manual.html
      ✓ [expected fail] name_test_case_757-manual.html
      ✓ [expected fail] name_test_case_758-manual.html
      ✓ [expected fail] name_test_case_759-manual.html
      ✓ [expected fail] name_test_case_760-manual.html
      ✓ [expected fail] name_test_case_761-manual.html
      ✓ [expected fail] name_test_case_762-manual.html
      ✓ name_text-label-embedded-combobox-manual.html
      ✓ name_text-label-embedded-menu-manual.html
      ✓ name_text-label-embedded-select-manual.html
      ✓ name_text-label-embedded-slider-manual.html
      ✓ name_text-label-embedded-spinbutton-manual.html
      ✓ name_text-title-manual.html

missing

  • visibility context (inherited but can reappear; currently reappearing wont't work)

dom-accessibility-api's People

Contributors

alonidiom avatar calebeby avatar ckundo avatar dependabot-preview[bot] avatar dependabot[bot] avatar eps1lon avatar geoffrich avatar github-actions[bot] avatar hiebj avatar jlp-craigmorten avatar juanca avatar marcosvega91 avatar matanbobi avatar nolanlawson avatar pablo-abc avatar renovate[bot] avatar ricardozv28 avatar semantic-release-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

dom-accessibility-api's Issues

Compile to es5

Could you change the ts compiler target to es5? We are using dom-testing-library on IE!

computeAccessibleName ignores text inside of shadow DOM

Thank you for writing this library! It's great to see a battle-tested solution for computing accessible names and descriptions. 🙂

I noticed, though, that when text is inside of the shadow DOM, it appears that computeAccessibleName ignores this text. Here is a CodePen to demonstrate.

Note that the Chrome Accessibility DevTools show the text inside of the shadow root as well as the text outside of the shadow root, since the browser uses both to compute the accessible name:

Screen Shot 2021-10-26 at 9 15 34 AM

In the case of open shadow roots (the most common kind), it should be possible to traverse shadow boundaries and collect the text inside of shadow roots.

Error in `exports` definition in `package.json`

dom-accessibility-api version: 0.4.2
node version: v13.11.0

Pulled this library by depending on @testing-library/react and then got an error trying to run my tests with mocha:

Error [ERR_INVALID_PACKAGE_TARGET]: Invalid "exports" main target "dist/index.js" defined in the package config /Users/...snip.../node_modules/dom-accessibility-api/package.json
    at resolveExportsTarget (internal/modules/cjs/loader.js:574:13)
    at resolveExportsTarget (internal/modules/cjs/loader.js:613:20)

After a bit of investigation, I believe that the issue is with the exports values in package.json missing a leading ./

Changing the exports to the following fixes my issue:

	"exports": {
		"import": "./dist/index.mjs",
		"require": "./dist/index.js"
	}

I have to admit I'm not too familiar with this setting but looking at https://nodejs.org/api/esm.html#esm_package_exports this seems correct.

Let me know if you need more details.

Elements with "option" role prefer "title" over the element content for accessible name

Hi there! 👋

I found some unexpected behavior while using React Testing Library, and found that it used this library for accessible name computation. I wanted to start a discussion to see if my understanding of the expected output was correct.

In short, it looks like option-roled elements with both text content AND a title attribute are preferring the title over the text content for the accessible name. Per the computation steps specs, it seems that the text content should be used before the "Tooltip attribute" (in this case, title).

Example HTML:

  <select>
    <option
    title="Title"  
  >
      Name
    </option>
  </select>

computeAccessibleName for the above is spitting out "Title", not "Name". By comparison, in Chrome's dev tools for the same case, it sets the accessible name as "Name".

Here's a sandbox showing the behavior.

Does my understanding of the expected output sound correct here? I'm somewhat new to this area, so wanted to make sure I wasn't off! 😅 I also haven't yet been able to trace where specifically in the code the difference is happening.

Thank you!

img elements with no alt attribute should maintain a role of img

Currently, getRole on an image that is missing an alt attribute returns null. this is incorrect.

given the following markup:

const imgEl = (new JSDOM(<img src='logo.png"'>`)).window.document.body.querySelector('img')

getRole(imgEl) should return img, but instead it returns null. (when adding an alt attribute, getRole return img correctly).

A completely missing alt attribute is notably different from alt="" or simply alt; adding an alt attribute with an empty value should remove the node from the AOM, whereas omitting it should not. using an empty value is a way for authors to instruct the browser to ignore an image.

Confirmed the above expected behavior in Chrome.

[related to #428]

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Edited/Blocked

These updates have been manually edited so Renovate will no longer make changes. To discard all commits and start over, click on a checkbox.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Ignored or Blocked

These are blocked by an existing closed PR and will not be recreated unless you click a checkbox below.

Detected dependencies

github-actions
.github/workflows/codeql-analysis.yml
  • actions/checkout v4
  • github/codeql-action v2
  • github/codeql-action v2
.github/workflows/release.yml
  • actions/checkout v4
  • actions/setup-node v3
  • changesets/action v1
npm
package.json
  • @babel/cli ^7.14.3
  • @babel/core ^7.14.3
  • @babel/plugin-proposal-class-properties ^7.13.0
  • @babel/preset-env ^7.14.4
  • @babel/preset-typescript ^7.13.0
  • @changesets/changelog-github ^0.4.0
  • @changesets/cli ^2.16.0
  • @testing-library/dom ^9.0.0
  • @types/jest ^29.0.0
  • @types/node 20.6.3
  • @typescript-eslint/eslint-plugin ^6.0.0
  • @typescript-eslint/parser ^6.0.0
  • concurrently ^8.0.0
  • cross-env ^7.0.3
  • cypress ^12.0.0
  • eslint ^7.27.0
  • eslint-plugin-jest ^27.0.0
  • jest ^29.0.0
  • jest-diff ^29.0.0
  • jest-environment-jsdom ^29.0.0
  • jest-junit ^16.0.0
  • js-yaml ^4.1.0
  • jsdom ^20.0.0
  • minimatch ^9.0.0
  • mocha ^10.0.0
  • mocha-sugar-free ^1.4.0
  • prettier ^3.0.0
  • q ^1.5.1
  • request ^2.88
  • request-promise-native ^1.0.9
  • rimraf ^5.0.0
  • serve ^14.0.0
  • typescript ^5.0.0
  • @types/node 20.6.3
tests/build/fixtures/ava-ts-node/package.json
  • @testing-library/react ^14.0.0
  • @types/jsdom ^20.0.0
  • @types/react ^18.0.0
  • @types/react-dom ^18.0.0
  • @types/testing-library__react ^10.0.1
  • ava ^5.0.0
  • jsdom ^20.0.0
  • react ^18.0.0
  • react-dom ^18.0.0
  • ts-node ^10.0.0
  • typescript ^5.0.0
tests/build/fixtures/kcd-rollup/package.json
  • @babel/runtime ^7.9.2
  • kcd-scripts ^14.0.0
tests/build/fixtures/node-es-modules/package.json
nvm
.nvmrc
  • node 18.18.0

  • Check this box to trigger a request for Renovate to run again on this repository

Support `list` and `listitem`

I tried to use dom testing library with getByRole('listitem', { name: 'text' })

I realized that querying a button by accessibility name works but the function computeAccessibleName when called on <span role='listitem'>text</span> returns empty string

Title attribute should not supercede text contents in accessible name computation on certain elements

According to https://w3c.github.io/html-aam/#button-element-accessible-name-computation , the title attribute of a <button> element should only be considered after the button element subtree. My interpretation of this is that when a button has both text content and a title, the text content should be used for the accessible name, not the title. If you look elsewhere within that spec, you'll see that the same is true for <a> elements. This is consistent with the way the accessible name is reported in the devtools of both Chrome and Firefox. If you construct such a button and inspect it in the Accessibility tab of Chrome's devtools for instance, you'll see that in the "Name" section the title is farther down the list than the "Contents", and that the title gets crossed out when Contents are present.

In terms of the other spec, https://www.w3.org/TR/accname-1.2 , my impression is that title is to be considered a "Tooltip attribute" as referenced in step 2I, rather than an "attribute which defines a text alternative" as referenced in step 2D.

Errors when using with rollup + svelte

Hi I use Rollup and svelte for my project and this liberary (as a dependancy of "@testing-library#jest-dom") causes roll-up to complain:

First a Circular dependency on module basis. It isn't circular when you look at the functions so a minor moving of the getLocalName or hasAnyConcreteRoles would do the trick

(!) Circular dependency
node_modules/dom-accessibility-api/dist/util.mjs -> node_modules/dom-accessibility-api/dist/getRole.mjs -> node_modules/dom-accessibility-api/dist/util.mjs

Second an error about the package.json, basically you would need to add the package.json to the export class in it's own file.

[rollup-plugin-svelte] The following packages did not export their `package.json` file so we could not check the "svelte" field. If you had difficulties importing svelte components from a package, then please contact the author and ask them to export the package.json file.

I have no clue if you find this at all helpful, but if nobody provides you this feedback, you will never know that Svelte + Rollup doesn't like your package ;)

Which version of the spec is this package supporting?

Apologies if the repo says and have missed it, but was curious what version of spec(s) this package is aiming to support.

E.g. in the description https://w3c.github.io/accname/ is listed which links to the working draft for 1.2 of accname.

Is the aim "bleeding edge" or latest stable?


As a tenuously related aside, I was curious on how might feel about updating this package to consume https://github.com/A11yance/aria-query for some of mapping population, noticed there are a number of roles that are missing from this package and outsourcing might reduce maintaince overhead (notice you're active in that repo as well @eps1lon!)

For the latest list of HTML roles see https://www.w3.org/TR/html-aam-1.0/#html-element-role-mappings (or https://www.w3.org/TR/core-aam-1.2/#mapping_role_table for the full "core" list).

Implicit role mapping for <th> element

Currently the getRole(element: Element): string | null method returns "columnheader" for the th element which isn't always the correct choice for the element depending on ancestory and attributes.

REF: https://github.com/eps1lon/dom-accessibility-api/blob/main/sources/getRole.ts#L62


According to html-aria:

role=columnheader, rowheader or cell if the ancestor table element is exposed as a role=table

role=columnheader, rowheader or gridcell if the ancestor table element is exposed as a role=grid or treegrid

No corresponding role if the ancestor table element is not exposed as a role=table, grid or treegrid

Here I appreciate hands may be tied less the scope of getRole() is expanded to start exploring parent elements.


According to html-aam:

If scope="row" then map th to rowheader
If scope="col" then map th to columnheader

Which does appear to be more achievable for this library as we have similar precedent for mapping roles from a combination of local name and attribute value(s) in the implicit role switch statement flow.

Propose that the th logic is moved out of the localNameToRoleMappings map and it's role is instead calculated dynamically as:

  1. rowheader if scope="row"
  2. columnheader if scope="col"
  3. TBD if otherwise, i.e. if scope is one of rowgroup, colgroup, or missing (REF: https://www.w3.org/TR/2011/WD-html5-author-20110809/the-th-element.html#attr-th-scope-auto)

As an aside, also raised w3c/html-aria#495 to query why html-aria does not mention the implicit role logic when the scope attribute is present.

A link with div content but no text element returns "" as the name

Possible duplicate of #908 . I believe this is a regression from #893 .

Sample HTML:
<a href="/users/Ram"><div title="Ram"></div></a>

Sandbox: https://codesandbox.io/s/intelligent-khayyam-04l055?file=/src/index.test.js

To see the differing behaviors between 0.5.6 and 0.5.16, change the dom-accessibility-api version from 0.5.16 to 0.5.14 or earlier and do a hard refresh (ctrl+shift+r in firefox).

Working stack (0.5.6):

computeElementTextAlternative(), accessible-name-and-description.ts:531
computeElementTextAlternative(), accessible-name-and-description.ts:597
computeTextAlternative(), accessible-name-and-description.ts:370
childNodes(), accessible-name-and-description.ts:369
computeMiscTextAlternative(), accessible-name-and-description.ts:651
computeTextAlternative(), accessible-name-and-description.ts:682
computeAccessibleName(), accessible-name.ts:40
anonymous(), role.js:150
filter(), role.js:144
allQuery(), query-helpers.js:87
query(), query-helpers.js:130
getAllByRole(), users.test.js:37

Non-working stack (0.5.16):

computeElementTextAlternative(), accessible-name-and-description.ts:552
computeElementTextAlternative(), accessible-name-and-description.ts:626
computeTextAlternative(), accessible-name-and-description.ts:372
forEach(), accessible-name-and-description.ts:371
computeMiscTextAlternative(), accessible-name-and-description.ts:686
computeTextAlternative(), accessible-name-and-description.ts:721
computeAccessibleName(), accessible-name.ts:40
anonymous(), role.js:183
filter(), role.js:178
allQuery(), query-helpers.js:74
query(), query-helpers.js:109
getAllByRole(), users.test.js:54

Explanation:
2F -> get child node titles -> 2D -> fallback (null in 0.5.16, title of node in 0.5.6).

Reading through the spec, so I could be very wrong:

  • <a href="/users/Ram"> -> Get name from content (2F)
  • <div title="Ram"> -> Check inner content; if null or empty, then return 2I (the title="Ram").

elements with no accessible name should return null

Hi there, noticed that an element with no accessible name returns an empty string. empty string is a valid value for an alt attribute for an img element, and it behaves differently than a missing (null) accessible name. when an empty string is present, the node is removed from the AOM, whereas a null value the node is still present.

would you consider returning null when there is no accessible name calculated, and returning empty string when the accessible name is set to ""?

computeAccessibleName() get null string instead of actual value

aria-label is existed when check from dev tool, but computeAccessibleName($optionItem[0]) get null string instead of actual value. Which make me confused.

cy.get('div.ap-option-item').should('have.attr', 'aria-label','DropdownItemfortesting');
cy.get('div.ap-option-item').then(($optionItem)=>{
const radioAccessibleName = computeAccessibleName($optionItem[0])
expect(radioAccessibleName).to.equal('DropdownItemfortesting')
})

computeAccessibleDescription does not include aria-label text

This issue was originally reported here testing-library/jest-dom#500, but it was noted that the underlying issue is here in the computeAccessibleDescription method.

  • @testing-library/jest-dom: "5.16.5"
  • @testing-library/react: "13.4.0"
  • node version: 18

Relevant code or config:

  it("includes svg aria-label in description text", () => {
    render(
      <>
        <label htmlFor="input-id">input</label>
        <input id="input-id" aria-describedby="description-id" />
        <div id="description-id">
          <svg role="img" aria-label="error" /> message
        </div>
      </>
    );

    expect(screen.getByLabelText("input")).toHaveAccessibleDescription("error message");
  });

What you did:

Screen readers include the svg aria-label in the accessible description, but computeAccessibleDescription (via toHaveAccessibleDescription in jest-dom) does not.

What happened:

    Expected element to have accessible description:
      error message
    Received:
      message

Reproduction:

https://codesandbox.io/s/react-testing-library-demo-forked-45xzdn?file=/src/__tests__/toHaveAccessibleDescription.js

Problem description:

aria-label contents are not included in the accessible description.

Suggested solution:

aria-label contents should be included in accessible descriptions.

Aria-description doesn't work

Hi, it looks like aria-description isn't looked at for the computeAccessibleDescription computation. I'm not sure why the aria-description attribute isn't mentioned in the spec here, but it is described here: https://w3c.github.io/aria/#aria-description.

It feels like something that should be factored in even though the accname spec doesn't mention it

`getAccessibleName` fails with `Maximum call stack size exceeded`

In version 0.5.15 of dom-accessibility-api, our tests started failing with RangeError: Maximum call stack size exceeded. Previously, we had been using version 0.5.14, but I had updated our lock file to get the latest transitive dependency versions.

The full stack trace is

RangeError: Maximum call stack size exceeded
    at CSSStyleDeclaration.get [as cssText] (https://59jbmi.csb.app/static/js/jsdom-16.3.0.min.js:30:82799)
    at CSSStyleDeclaration._setProperty (https://59jbmi.csb.app/static/js/jsdom-16.3.0.min.js:30:82082)
    at CSSStyleDeclaration.set (https://59jbmi.csb.app/static/js/jsdom-16.3.0.min.js:30:140717)
    at CSSStyleDeclaration.<anonymous> (https://59jbmi.csb.app/static/js/jsdom-16.3.0.min.js:30:112985)
    at Array.forEach (<anonymous>)
    at CSSStyleDeclaration.<anonymous> (https://59jbmi.csb.app/static/js/jsdom-16.3.0.min.js:30:112910)
    at CSSStyleDeclaration.set [as font] (https://59jbmi.csb.app/static/js/jsdom-16.3.0.min.js:30:145022)
    at CSSStyleDeclaration.setProperty (https://59jbmi.csb.app/static/js/jsdom-16.3.0.min.js:30:81653)
    at https://59jbmi.csb.app/static/js/jsdom-16.3.0.min.js:37:49908
    at CSSStyleDeclaration.forEach (<anonymous>)
    at https://59jbmi.csb.app/static/js/jsdom-16.3.0.min.js:37:49869
    at https://59jbmi.csb.app/static/js/jsdom-16.3.0.min.js:37:1620704
    at Array.forEach (<anonymous>)
    at handleSheet (https://59jbmi.csb.app/static/js/jsdom-16.3.0.min.js:37:1620484)
    at exports.forEachMatchingSheetRuleOfElement (https://59jbmi.csb.app/static/js/jsdom-16.3.0.min.js:37:1620808)
    at Window.getComputedStyle (https://59jbmi.csb.app/static/js/jsdom-16.3.0.min.js:37:49816)
    at isHidden (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:22:15)
    at computeTextAlternative (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:275:21)
    at eval (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:143:20)
    at Array.forEach (<anonymous>)
    at computeMiscTextAlternative (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:142:16)
    at computeTextAlternative (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:339:31)
    at eval (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:282:16)
    at Array.map (<anonymous>)
    at computeTextAlternative (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:281:28)
    at eval (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:143:20)
    at Array.forEach (<anonymous>)
    at computeMiscTextAlternative (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:142:16)
    at computeTextAlternative (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:339:31)
    at eval (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:282:16)
    at Array.map (<anonymous>)
    at computeTextAlternative (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:281:28)
    at eval (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:143:20)
    at Array.forEach (<anonymous>)
    at computeMiscTextAlternative (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:142:16)
    at computeTextAlternative (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:339:31)
    at eval (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:282:16)
    at Array.map (<anonymous>)
    at computeTextAlternative (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:281:28)
    at eval (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:143:20)
    at Array.forEach (<anonymous>)
    at computeMiscTextAlternative (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:142:16)
    at computeTextAlternative (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:339:31)
    at eval (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:282:16)
    at Array.map (<anonymous>)
    at computeTextAlternative (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:281:28)
    at eval (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:143:20)
    at Array.forEach (<anonymous>)
    at computeMiscTextAlternative (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:142:16)
    at computeTextAlternative (https://59jbmi.csb.app/node_modules/dom-accessibility-api/dist/accessible-name-and-description.mjs:339:31)

See a reproduction of this issue at https://codesandbox.io/s/typescript-jest-test-template-forked-59jbmi?file=/src/index.ts
If you change the version back to 0.5.14, save and then refresh, you'll see that the test passes.

Expose consultedNodes

Hi, is there a way to use the values of consultedNodes when calling computeAccessibleName or computeAccessibleDescription? Maybe we could add 2nd copies of those methods with different names that return an object { name/description: '...', consultedNodes: ... } to avoid breaking changes?

`computeAccessibleName` won't compute button name from child title

Elements with the "button" role support determining their accessible name from their content, and it's possible for internal elements to have no text content but instead specify their accessible name via a "title" attribute. For example:

<button>
    <span title="Name from child title"></span>
</button>

This can come up, for example, when using internal elements that display an icon.

Unfortunately, calling computeAccessibleName on such a button returns an empty string, whereas it should return 'Name from child title'. The correct title can be viewed, for example, in the accessibility tree of Chrome devtools:

image
image

Calling computeAccessibleName on the inner element returns the correct accessible name.

I've set up a replication in Codepen: dom-accessibility-api accessible name bug reproduction

Accessible name incorrect when <slot> involved

I want to use this package to determine the accessible name of an element containing a slot (or when the node used to determine accessible name contains a slot, e.g. with aria-labelledby). Currently computeAccessibleName does not return the same accessible name the browser uses when called on an element containing a slot. I've created a GitHub repo demonstrating the issue.

The demo creates two custom elements, each containing a button with a slot. The second element (custom-button-with-default) sets a default value for the slot, while the first (custom-button) does not. The test code invokes computeAccessibleName on the <button> within each custom element's shadow root.

The test cases are as follows:

  • When called on the button within custom-button, this package returns an empty string while Firefox/Chrome determines the accessible name to be "Custom name"
  • When called on the button within the first custom-button-with-default (slot content is set), this package returns "Default name" while Firefox/Chrome determines the accessible name to be "Custom name"
  • When called on the button within the second custom-button-with-default (slot content is not set), this package returns "Default name" while Firefox/Chrome determine the accessible name to be "Default name"

Below is an image how Firefox displays the accessibility tree. You can also clone and run my demo locally.
Image of accessibility tree from demo page in firefox

This is an interesting one, since I couldn't find a corresponding WPT and the spec doesn't cover this. I have an issue out on the accessible name spec to clarify this. Either way, there does seem to be a mismatch between how this package determines the accessible name and how browsers do (at least Chrome and Firefox, I'm not able to test Safari).

The root cause of the issue seems to be related to how slots work. The "Custom name" text passed to the custom-button does not become a child node of the slot, but rather an assigned node. This package (and the spec) only consider child nodes.

const slot = document.querySelector('custom-button').shadowRoot.querySelector('slot')
console.log(slot.childNodes)        // NodeList []
console.log(slot.assignedNodes())   // Array [ #text "Custom name" ]

To accurately determine the accessible name with slotted content, assigned nodes should be considered as well as child nodes. We'd also need to be careful of the case where default content is provided to the slot.

I'd be happy to take a crack at implementing this if you agree that it's an issue.

`computeAccessibleName` won't compute combobox name from self referencing aria-labelledby attr

When a div element role is combobox and the element has an aria-labelledby attribute that references itself, computeAccessibleName is failing to return the accessible name.

Role combobox supports name from author https://www.w3.org/TR/wai-aria/#namefromauthor

According to 2B of https://www.w3.org/TR/accname-1.2/#mapping_additional_nd_te

if computing a name, and the current node has an aria-labelledby attribute that contains at least one valid IDREF, and the current node is not already part of an aria-labelledby traversal, process its IDREFs in the order they occur

Please correct if I misunderstand but my interpretation is it should be returning the name. I note the name is seen in Chrome dev tools in the accessibility pane.

combobox-labelledby-self

When role is combobox the following test cases demonstrate it failing to return an accessible name when the element refers to itself using aria-labelledby.

Test cases

test.each([
[
		`<div data-test id="el1" role="combobox" aria-labelledby="el1">I reference myself for my name</div>`,
		"I reference myself for my name",
	],
	[
		`
		<div data-test id="el1" role="combobox" aria-labelledby="label">I reference another element for my name</div>
		<div id="label" role="presentation">I'm prohibited a name</div>
		`,
		"I'm prohibited a name",
	],
	[
		`<div data-test id="el1" role="combobox" aria-label="I use aria-label for my name"></div>`,
		"I use aria-label for my name",
	],
])(`misc #%#`, (markup, expectedAccessibleName) => {
	expect(markup).toRenderIntoDocumentAccessibleName(expectedAccessibleName);
});

Test output

misc #0

    expected <div
      aria-labelledby="el1"
      data-test=""
      id="el1"
      role="combobox"
    >
      I reference myself for my name
    </div> to have accessible name 'I reference myself for my name' but got ''
    - Expected
    + Received

    - I reference myself for my name

      496 |             ],
      497 |     ])(`misc #%#`, (markup, expectedAccessibleName) => {
    > 498 |             expect(markup).toRenderIntoDocumentAccessibleName(expectedAccessibleName);
          |                            ^
      499 |     });
      500 |
      501 | test("text nodes are not concatenated by space", () => {

      at toRenderIntoDocumentAccessibleName (__tests__/accessible-name.js:498:18)

At a quick glance this would appear to be because the logic is skipping to "step 2E" for role combobox, ultimately returning an empty string on line 649 of sources/accessible-name-and-description.ts
return isHTMLInputElement(current) ? current.value : "";

Error: Not Implemented: window.computedStyle(elt, pseudoElt)

Possibly fixed by: #380, not sure

JSDOM has released an update which no longer allows a second argument to getComputedStyle, as described here:

(first line of changelog 16.4.0 and code changes shown just below that):
https://app.renovatebot.com/package-diff?name=jsdom&from=16.3.0&to=16.4.0

dom-accessibility-api unfortunately calls that function with two arguments

As a result, the console is flooded with warnings like:

Error: Not implemented: window.computedStyle(elt, pseudoElt)
          at module.exports (/project_repo/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17)
          at getComputedStyle (/project_repo/node_modules/jsdom/lib/jsdom/browser/Window.js:657:7)
          at computeMiscTextAlternative (/project_repo/node_modules/dom-accessibility-api/sources/accessible-name-and-description.ts:332:25)
          at computeTextAlternative (/project_repo/node_modules/dom-accessibility-api/sources/accessible-name-and-description.ts:603:11)
          at computeTextAlternative (/project_repo/node_modules/dom-accessibility-api/sources/accessible-name-and-description.ts:634:3)
          at computeAccessibleName (/project_repo/node_modules/dom-accessibility-api/sources/accessible-name.ts:40:9)
          at /project_repo/node_modules/@testing-library/dom/dist/queries/role.js:110:82
          at Array.filter (<anonymous>)
          at queryAllByRole (/project_repo/node_modules/@testing-library/dom/dist/queries/role.js:104:6)
          at /project_repo/node_modules/@testing-library/dom/dist/query-helpers.js:64:17 undefined

You can see the offending lines of code in dom-accessibility-api here:
https://github.com/eps1lon/dom-accessibility-api/blob/main/sources/accessible-name-and-description.ts#L340
https://github.com/eps1lon/dom-accessibility-api/blob/main/sources/accessible-name-and-description.ts#L367

Which would probably be fixed by making a small change to this line:
https://github.com/eps1lon/dom-accessibility-api/blob/main/sources/accessible-name-and-description.ts#L324

Sorry I am not more familiar with this repo or I would take a stab at making the right change.

Versions:
"@testing-library/dom": "7.21.1",
..... using: "dom-accessibility-api": "0.4.7"

Close if fixed already.

Thank you!

Performance to compute accessible name

To be short, the actual code is very slow to compute accessible names.

I'm not sure if I read well the CI, I didn't read in the test code but I saw 159 passing (13s) so if it run only one time the function per test, it means it takes 80ms on average to compute the text.

Related issue in dom-testing-library.
testing-library/dom-testing-library#698 (comment)

Thanks a lot for all your work here, I will try to look how we can optimize the performance here but I will be in vacation next week.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.