Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ariperkkio/extend-to-be-announced
Custom Jest/Vitest matcher for validating ARIA Live Regions
https://github.com/ariperkkio/extend-to-be-announced
a11y accessibility aria-live jest vitest
Last synced: 3 months ago
JSON representation
Custom Jest/Vitest matcher for validating ARIA Live Regions
- Host: GitHub
- URL: https://github.com/ariperkkio/extend-to-be-announced
- Owner: AriPerkkio
- Created: 2021-02-17T17:41:15.000Z (almost 4 years ago)
- Default Branch: master
- Last Pushed: 2024-06-10T06:39:25.000Z (8 months ago)
- Last Synced: 2024-06-11T17:35:22.210Z (8 months ago)
- Topics: a11y, accessibility, aria-live, jest, vitest
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/extend-to-be-announced
- Size: 874 KB
- Stars: 8
- Watchers: 3
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
Awesome Lists containing this project
README
# extend-to-be-announced
[![version](https://img.shields.io/npm/v/extend-to-be-announced)](https://www.npmjs.com/package/extend-to-be-announced)
[Motivation](#Motivation) | [Installation](#installation) | [Usage](#usage) | [Support](#support)
> Utility for asserting [ARIA live regions](https://www.w3.org/TR/wai-aria-1.2/#dfn-live-region).
`extend-to-be-announced` is a matcher extender for [Jest](https://jestjs.io/) and [Vitest](https://vitest.dev/). It makes validating ARIA live regions extremely easy. Internally it is utilizing [`aria-live-capture`](https://github.com/AriPerkkio/aria-live-capture) for announcement detection.
For Storybook integration see [`storybook-addon-aria-live`](https://github.com/AriPerkkio/storybook-addon-aria-live).
```js
test('live region is announced', () => {
const region = document.createElement('div');
region.setAttribute('role', 'status');document.body.appendChild(region);
region.textContent = 'Loading';expect('Loading').toBeAnnounced('polite');
});
```## Motivation
Validating ARIA live regions with [`@testing-library`](https://testing-library.com/) and [`jest-dom`](https://github.com/testing-library/jest-dom) requires developers to consider implementation details.
Current solutions are prone to false positives.In test below it is not clearly visible that `Loading...` is not actually announced.
Assistive technologies are only expected to announce **updates** of ARIA live regions - not the initial content.```js
render(Loading...);const liveRegion = screen.getByRole('status');
// Loading is not be announced by assistive technologies ❌
// Content of live region has not updated. This is it's initial text content.
expect(liveRegion).toHaveTextContent('Loading...');
```Instead developers should check that messages are rendered into existing ARIA Live regions.
```js
);
const { rerender } = render(// Live region should be present
const liveRegion = screen.getByRole('status');// Live region should initially be empty
expect(liveRegion).toBeEmptyDOMElement();// Update content of the live region
rerender(Loading...);// Loading is announced by assistive technologies ✅
expect(liveRegion).toHaveTextContent('Loading...');
````toBeAnnounced` can be used to hide such implementation detail from tests.
```js
);
const { rerender } = render(rerender(
Loading...);expect('Loading...').toBeAnnounced('polite');
```## Installation
`extend-to-be-announced` should be included in development dependencies.
```bash
npm install --save-dev extend-to-be-announced
```## Usage
### Test setup
There are out-of-the-box setups for Vitest and Jest.
#### Vitest
Import registration entrypoint in your [test setup](https://vitest.dev/config/#setupfiles).
```js
import 'extend-to-be-announced/vitest';
```For setting up registration options use `register(options)` method instead.
```js
import { register } from 'extend-to-be-announced/vitest/register';register({
/** Indicates whether live regions inside `ShadowRoot`s should be tracked. Defaults to false. */
includeShadowDom: true,
});
```#### Jest
Import registration entrypoint in your [test setup](https://jestjs.io/docs/en/configuration.html#setupfilesafterenv-array).
```js
import 'extend-to-be-announced/jest';
```For setting up registration options use `register(options)` method instead.
```js
import { register } from 'extend-to-be-announced/jest/register';register({
/** Indicates whether live regions inside `ShadowRoot`s should be tracked. Defaults to false. */
includeShadowDom: true,
});
```Note that you'll need to add ESM dependencies of this package to your Jest config's `transformIgnorePatterns`. For example with `pnpm`:
```js
transformIgnorePatterns: ['/node_modules/.pnpm/(?!(aria-live-capture)@)'],
```### Typescript
This package utilizes [Typescript's `exports` support](https://www.typescriptlang.org/docs/handbook/esm-node.html#packagejson-exports-imports-and-self-referencing) for type declarations. You'll need to set `"moduleResolution": "node16"` or `"moduleResolution": "nodenext"` in your `tsconfig.json` in order to have typings picked properly. For legacy setups where certain fields of `tsconfig.json` cannot be modified, such as `create-react-app`, there is a work-around entrypoint for `jest`.
```json
{
"compilerOptions": {
"moduleResolution": "node16" // Or nodenext
}
}
```### Assertions
#### toBeAnnounced
Assert whether given message was announced by assistive technologies.
Accepts string or regexp as matcher value.```js
expect('Loading...').toBeAnnounced();
expect(/loading/i).toBeAnnounced();
expect('Error occured...').toBeAnnounced();
expect(/error occured/i).toBeAnnounced();
```Politeness setting of announcement can be optionally asserted.
```js
expect('Loading...').toBeAnnounced('polite');
expect('Error occured...').toBeAnnounced('assertive');
```##### Examples
```html
Render#1 |
Render#2 |Loading
PASS ✅ | expect('Loading').toBeAnnounced('polite');
``````html
Render#1 |Error
PASS ✅ | expect('Error').toBeAnnounced('assertive');
``````html
Render#1 |
Render#2 |Error
PASS ✅ | expect('Error').toBeAnnounced();
``````html
Render#1 |Loading
FAIL ❌ | expect('Loading').toBeAnnounced();
``````html
Render#1 |
Render#2 |Loading
FAIL ❌ | expect('Loading').toBeAnnounced();
```With `register({ includeShadowDom: true })`:
```html
Render#1 |
| #shadow-root
|
|
|
Render#2 |
| #shadow-root
|Loading
|
|
PASS ✅ | expect('Loading').toBeAnnounced()
```### Utilities
#### getAnnouncements
Get all announcements as `Map`.
```js
import { getAnnouncements } from 'extend-to-be-announced';
getAnnouncements();> Map {
> "Status message" => "polite",
> "Alert message" => "assertive",
> }
```#### clearAnnouncements
Clear all captured announcements.
```js
import { clearAnnouncements } from 'extend-to-be-announced';
clearAnnouncements();
```## Support
| Feature | Status |
| :-------------: | :----: |
| `role` | ✅ |
| `aria-live` | ✅ |
| `aria-atomic` | ❌ 👷 |
| `aria-busy` | ❌ |
| `aria-relevant` | ❌ |