From 1592d35e368d02d4c004ee672535b8b7ecd5f937 Mon Sep 17 00:00:00 2001 From: AntoineThibi <87643625+AntoineThibi@users.noreply.github.com> Date: Fri, 8 Sep 2023 11:23:19 +0200 Subject: [PATCH] feat: implement `toBeSelected` matcher (#1488) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: implement toBeSelected * refactor: tweaks * refactor: tweak tests * refactor: finishing touches --------- Co-authored-by: Maciej Jastrzębski --- src/helpers/accessiblity.ts | 7 ++ .../__tests__/to-be-selected.test.tsx | 96 +++++++++++++++++++ src/matchers/extend-expect.d.ts | 1 + src/matchers/extend-expect.ts | 2 + src/matchers/index.tsx | 1 + src/matchers/to-be-selected.ts | 24 +++++ 6 files changed, 131 insertions(+) create mode 100644 src/matchers/__tests__/to-be-selected.test.tsx create mode 100644 src/matchers/to-be-selected.ts diff --git a/src/helpers/accessiblity.ts b/src/helpers/accessiblity.ts index 7a4bb0e1..f3ac4de3 100644 --- a/src/helpers/accessiblity.ts +++ b/src/helpers/accessiblity.ts @@ -176,6 +176,13 @@ export function getAccessibilityCheckedState( return ariaChecked ?? accessibilityState?.checked; } +export function getAccessibilitySelectedState( + element: ReactTestInstance +): NonNullable { + const { accessibilityState, 'aria-selected': ariaSelected } = element.props; + return ariaSelected ?? accessibilityState?.selected ?? false; +} + export function getAccessibilityValue( element: ReactTestInstance ): AccessibilityValue | undefined { diff --git a/src/matchers/__tests__/to-be-selected.test.tsx b/src/matchers/__tests__/to-be-selected.test.tsx new file mode 100644 index 00000000..cbf1f46c --- /dev/null +++ b/src/matchers/__tests__/to-be-selected.test.tsx @@ -0,0 +1,96 @@ +import * as React from 'react'; +import { View } from 'react-native'; +import { render, screen } from '../..'; +import '../extend-expect'; + +test('.toBeSelected() basic case', () => { + render( + <> + + + + + + + ); + + expect(screen.getByTestId('selected')).toBeSelected(); + expect(screen.getByTestId('selected-aria')).toBeSelected(); + expect(screen.getByTestId('not-selected')).not.toBeSelected(); + expect(screen.getByTestId('not-selected-aria')).not.toBeSelected(); + expect(screen.getByTestId('default')).not.toBeSelected(); +}); + +test('.toBeSelected() error messages', () => { + render( + <> + + + + + + + ); + + expect(() => expect(screen.getByTestId('selected')).not.toBeSelected()) + .toThrowErrorMatchingInlineSnapshot(` + "expect(element).not.toBeSelected() + + Received element is selected + " + `); + + expect(() => expect(screen.getByTestId('selected-aria')).not.toBeSelected()) + .toThrowErrorMatchingInlineSnapshot(` + "expect(element).not.toBeSelected() + + Received element is selected + " + `); + + expect(() => expect(screen.getByTestId('not-selected')).toBeSelected()) + .toThrowErrorMatchingInlineSnapshot(` + "expect(element).toBeSelected() + + Received element is not selected + " + `); + + expect(() => expect(screen.getByTestId('not-selected-aria')).toBeSelected()) + .toThrowErrorMatchingInlineSnapshot(` + "expect(element).toBeSelected() + + Received element is not selected + " + `); + + expect(() => expect(screen.getByTestId('default')).toBeSelected()) + .toThrowErrorMatchingInlineSnapshot(` + "expect(element).toBeSelected() + + Received element is not selected + " + `); +}); diff --git a/src/matchers/extend-expect.d.ts b/src/matchers/extend-expect.d.ts index 781db2d3..6bc63bea 100644 --- a/src/matchers/extend-expect.d.ts +++ b/src/matchers/extend-expect.d.ts @@ -7,6 +7,7 @@ export interface JestNativeMatchers { toBeEmptyElement(): R; toBeEnabled(): R; toBePartiallyChecked(): R; + toBeSelected(): R; toBeVisible(): R; toHaveDisplayValue(expectedValue: TextMatch, options?: TextMatchOptions): R; toHaveProp(name: string, expectedValue?: unknown): R; diff --git a/src/matchers/extend-expect.ts b/src/matchers/extend-expect.ts index 25b5d3c1..87988a4a 100644 --- a/src/matchers/extend-expect.ts +++ b/src/matchers/extend-expect.ts @@ -5,6 +5,7 @@ import { toBeChecked } from './to-be-checked'; import { toBeDisabled, toBeEnabled } from './to-be-disabled'; import { toBeEmptyElement } from './to-be-empty-element'; import { toBePartiallyChecked } from './to-be-partially-checked'; +import { toBeSelected } from './to-be-selected'; import { toBeVisible } from './to-be-visible'; import { toHaveDisplayValue } from './to-have-display-value'; import { toHaveProp } from './to-have-prop'; @@ -17,6 +18,7 @@ expect.extend({ toBeEmptyElement, toBeEnabled, toBePartiallyChecked, + toBeSelected, toBeVisible, toHaveDisplayValue, toHaveProp, diff --git a/src/matchers/index.tsx b/src/matchers/index.tsx index 72718a18..16f5a6d9 100644 --- a/src/matchers/index.tsx +++ b/src/matchers/index.tsx @@ -7,3 +7,4 @@ export { toBeVisible } from './to-be-visible'; export { toHaveDisplayValue } from './to-have-display-value'; export { toHaveProp } from './to-have-prop'; export { toHaveTextContent } from './to-have-text-content'; +export { toBeSelected } from './to-be-selected'; diff --git a/src/matchers/to-be-selected.ts b/src/matchers/to-be-selected.ts new file mode 100644 index 00000000..3e35c4dd --- /dev/null +++ b/src/matchers/to-be-selected.ts @@ -0,0 +1,24 @@ +import { ReactTestInstance } from 'react-test-renderer'; +import { matcherHint } from 'jest-matcher-utils'; +import { getAccessibilitySelectedState } from '../helpers/accessiblity'; +import { checkHostElement, formatElement } from './utils'; + +export function toBeSelected( + this: jest.MatcherContext, + element: ReactTestInstance +) { + checkHostElement(element, toBeSelected, this); + + return { + pass: getAccessibilitySelectedState(element), + message: () => { + const is = this.isNot ? 'is' : 'is not'; + return [ + matcherHint(`${this.isNot ? '.not' : ''}.toBeSelected`, 'element', ''), + '', + `Received element ${is} selected`, + formatElement(element), + ].join('\n'); + }, + }; +}