diff --git a/README.md b/README.md
index a6d28cf..398c53b 100644
--- a/README.md
+++ b/README.md
@@ -81,11 +81,12 @@ clear to read and to maintain.
- [`toBePartiallyChecked`](#tobepartiallychecked)
- [`toHaveRole`](#tohaverole)
- [`toHaveErrorMessage`](#tohaveerrormessage)
- - [`toHaveSelection`](#tohaveselection)
+ - [`toBePressed`](#tobepressed)
- [Deprecated matchers](#deprecated-matchers)
- [`toBeEmpty`](#tobeempty)
- [`toBeInTheDOM`](#tobeinthedom)
- [`toHaveDescription`](#tohavedescription)
+ - [`toHaveSelection`](#tohaveselection)
- [Inspiration](#inspiration)
- [Other Solutions](#other-solutions)
- [Guiding Principles](#guiding-principles)
@@ -1306,6 +1307,46 @@ expect(timeInput).toHaveErrorMessage(expect.stringContaining('Invalid time')) //
expect(timeInput).not.toHaveErrorMessage('Pikachu!')
```
+
+
+### `toBePressed`
+
+This allows to check whether given element is
+[pressed](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-pressed).
+
+It accepts elements with explicit or implicit `button` role and valid
+`aria-pressed` attribute of `"true"` or `"false"`.
+
+```typescript
+toBePressed()
+```
+
+#### Examples
+
+```html
+
+
+
+
+
+
+Pressed span
+Released span
+```
+
+##### Using DOM Testing Library
+
+```javascript
+screen.getByRole('button', {name: 'Pressed'}).toBePressed()
+screen.getByRole('button', {name: 'Released'}).not.toBePressed()
+
+screen.getByRole('button', {name: 'Pressed input button'}).toBePressed()
+screen.getByRole('button', {name: 'Released input button'}).not.toBePressed()
+
+screen.getByRole('button', {name: 'Pressed span'}).toBePressed()
+screen.getByRole('button', {name: 'Released span'}).not.toBePressed()
+```
+
## Deprecated matchers
### `toBeEmpty`
diff --git a/src/__tests__/to-be-pressed.js b/src/__tests__/to-be-pressed.js
new file mode 100644
index 0000000..fc9649e
--- /dev/null
+++ b/src/__tests__/to-be-pressed.js
@@ -0,0 +1,89 @@
+import {render} from './helpers/test-utils'
+
+describe('.toBePressed', () => {
+ test('handles button element', () => {
+ const {queryByTestId} = render(`
+
+
+ `)
+
+ expect(queryByTestId('button-pressed')).toBePressed()
+ expect(queryByTestId('button-not-pressed')).not.toBePressed()
+ })
+
+ test('handles element with role="button"', () => {
+ const {queryByTestId} = render(`
+
+
+ `)
+
+ expect(queryByTestId('button-pressed')).toBePressed()
+ expect(queryByTestId('button-not-pressed')).not.toBePressed()
+ })
+
+ test('handles input with button type', () => {
+ const {queryByTestId} = render(`
+
+
+ `)
+
+ expect(queryByTestId('button-pressed')).toBePressed()
+ expect(queryByTestId('button-not-pressed')).not.toBePressed()
+ })
+
+ test('throw an error when pressable element is not pressed but expected to be', () => {
+ const {queryByTestId} = render(`
+
+ `)
+
+ expect(() => expect(queryByTestId('button')).toBePressed())
+ .toThrowErrorMatchingInlineSnapshot(`
+ expect(>element>).toBePressed()>
+
+ Expected element to have:
+ aria-pressed="true">
+ Received:
+ aria-pressed="false">
+ `)
+ })
+
+ test('throw an error when pressable element is pressed but expected not to be', () => {
+ const {queryByTestId} = render(`
+
+ `)
+
+ expect(() => expect(queryByTestId('button')).not.toBePressed())
+ .toThrowErrorMatchingInlineSnapshot(`
+ expect(>element>).not.toBePressed()>
+
+ Expected element to have:
+ aria-pressed="false">
+ Received:
+ aria-pressed="true">
+ `)
+ })
+
+ test('throw an error when pressable element has invalid aria-pressed attribute', () => {
+ const {queryByTestId} = render(`
+
+ `)
+
+ expect(() =>
+ expect(queryByTestId('button')).toBePressed(),
+ ).toThrowErrorMatchingInlineSnapshot(
+ `Only button or input with type="button" or element with role="button" and a valid aria-pressed attribute can be used with .toBePressed()`,
+ )
+ })
+
+ test('throws an error when element is not a button', () => {
+ const {queryByTestId} = render(
+ ``,
+ )
+
+ expect(() =>
+ expect(queryByTestId('span')).toBePressed(),
+ ).toThrowErrorMatchingInlineSnapshot(
+ `Only button or input with type="button" or element with role="button" and a valid aria-pressed attribute can be used with .toBePressed()`,
+ )
+ })
+})
diff --git a/src/matchers.js b/src/matchers.js
index ed534e2..9f5a72d 100644
--- a/src/matchers.js
+++ b/src/matchers.js
@@ -25,3 +25,4 @@ export {toBePartiallyChecked} from './to-be-partially-checked'
export {toHaveDescription} from './to-have-description'
export {toHaveErrorMessage} from './to-have-errormessage'
export {toHaveSelection} from './to-have-selection'
+export {toBePressed} from './to-be-pressed'
diff --git a/src/to-be-pressed.js b/src/to-be-pressed.js
new file mode 100644
index 0000000..8f1aa54
--- /dev/null
+++ b/src/to-be-pressed.js
@@ -0,0 +1,50 @@
+import {checkHtmlElement, getMessage} from './utils'
+
+export function toBePressed(element) {
+ checkHtmlElement(element, toBePressed, this)
+
+ const roles = (element.getAttribute('role') || '')
+ .split(' ')
+ .map(role => role.trim())
+
+ const isButton =
+ element.tagName.toLowerCase() === 'button' ||
+ (element.tagName.toLowerCase() === 'input' && element.type === 'button') ||
+ roles.includes('button')
+
+ const pressedAttribute = element.getAttribute('aria-pressed')
+
+ const isValidAriaElement =
+ pressedAttribute === 'true' || pressedAttribute === 'false'
+
+ if (!isButton || !isValidAriaElement) {
+ return {
+ pass: false,
+ message: () =>
+ `Only button or input with type="button" or element with role="button" and a valid aria-pressed attribute can be used with .toBePressed()`,
+ }
+ }
+
+ const isPressed = pressedAttribute === 'true'
+
+ return {
+ pass: isButton && isPressed,
+
+ message: () => {
+ const matcher = this.utils.matcherHint(
+ `${this.isNot ? '.not' : ''}.toBePressed`,
+ 'element',
+ '',
+ )
+
+ return getMessage(
+ this,
+ matcher,
+ `Expected element to have`,
+ `aria-pressed="${this.isNot ? 'false' : 'true'}"`,
+ `Received`,
+ `aria-pressed="${pressedAttribute}"`,
+ )
+ },
+ }
+}
diff --git a/types/matchers.d.ts b/types/matchers.d.ts
index 8bf2ad5..b7c7e6a 100755
--- a/types/matchers.d.ts
+++ b/types/matchers.d.ts
@@ -761,6 +761,36 @@ declare namespace matchers {
* [testing-library/jest-dom#tohaveselection](https://github.com/testing-library/jest-dom#tohaveselection)
*/
toHaveSelection(selection?: string): R
+ /*
+ * @description
+ * This allows to check whether given element has been [pressed](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-pressed)
+ *
+ * It accepts elements with explicit or implicit `button` role and valid `aria-pressed`
+ * attribute of `"true"` or `"false"`.
+ *
+ * @example
+ *
+ *
+ *
+ *
+ *
+ *
+ * Pressed span
+ * Released span
+ *
+ * screen.getByRole('button', { name: 'Pressed' }).toBePressed();
+ * screen.getByRole('button', { name: 'Released' }).not.toBePressed();
+ *
+ * screen.getByRole('button', { name: 'Pressed input button' }).toBePressed();
+ * screen.getByRole('button', { name: 'Released input button' }).not.toBePressed();
+ *
+ * screen.getByRole('button', { name: 'Pressed span' }).toBePressed();
+ * screen.getByRole('button', { name: 'Released span' }).not.toBePressed();
+ *
+ * @see
+ * [testing-library/jest-dom#tobepressed](https://github.com/testing-library/jest-dom#tobepressed)
+ */
+ toBePressed(): R
}
}