From fcd17531eb35961137988c0c87ba97ae46019350 Mon Sep 17 00:00:00 2001 From: Wiktor Date: Wed, 13 Sep 2023 18:32:53 +0200 Subject: [PATCH 1/4] feat: toBeExpanded matcher --- src/helpers/accessiblity.ts | 7 ++ .../__tests__/to-be-expanded.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-expanded.tsx | 28 ++++++ 6 files changed, 135 insertions(+) create mode 100644 src/matchers/__tests__/to-be-expanded.test.tsx create mode 100644 src/matchers/to-be-expanded.tsx diff --git a/src/helpers/accessiblity.ts b/src/helpers/accessiblity.ts index ee440bc37..608a48fba 100644 --- a/src/helpers/accessiblity.ts +++ b/src/helpers/accessiblity.ts @@ -219,3 +219,10 @@ export function isElementSelected( const { accessibilityState, 'aria-selected': ariaSelected } = element.props; return ariaSelected ?? accessibilityState?.selected ?? false; } + +export function isElementExpanded( + element: ReactTestInstance +): NonNullable { + const { accessibilityState, 'aria-expanded': ariaExpanded } = element.props; + return ariaExpanded ?? accessibilityState?.expanded ?? false; +} diff --git a/src/matchers/__tests__/to-be-expanded.test.tsx b/src/matchers/__tests__/to-be-expanded.test.tsx new file mode 100644 index 000000000..07b891f75 --- /dev/null +++ b/src/matchers/__tests__/to-be-expanded.test.tsx @@ -0,0 +1,96 @@ +import * as React from 'react'; +import { View } from 'react-native'; +import { render, screen } from '../..'; +import '../extend-expect'; + +test('toBeExpanded() basic case', () => { + render( + <> + + + + + + + ); + + expect(screen.getByTestId('expanded')).toBeExpanded(); + expect(screen.getByTestId('expanded-aria')).toBeExpanded(); + expect(screen.getByTestId('not-expanded')).not.toBeExpanded(); + expect(screen.getByTestId('not-expanded-aria')).not.toBeExpanded(); + expect(screen.getByTestId('default')).not.toBeExpanded(); +}); + +test('toBeExpanded() error messages', () => { + render( + <> + + + + + + + ); + + expect(() => expect(screen.getByTestId('expanded')).not.toBeExpanded()) + .toThrowErrorMatchingInlineSnapshot(` + "expect(element).not.toBeExpanded() + + Received element is expanded: + " + `); + + expect(() => expect(screen.getByTestId('expanded-aria')).not.toBeExpanded()) + .toThrowErrorMatchingInlineSnapshot(` + "expect(element).not.toBeExpanded() + + Received element is expanded: + " + `); + + expect(() => expect(screen.getByTestId('not-expanded')).toBeExpanded()) + .toThrowErrorMatchingInlineSnapshot(` + "expect(element).toBeExpanded() + + Received element is not expanded: + " + `); + + expect(() => expect(screen.getByTestId('not-expanded-aria')).toBeExpanded()) + .toThrowErrorMatchingInlineSnapshot(` + "expect(element).toBeExpanded() + + Received element is not expanded: + " + `); + + expect(() => expect(screen.getByTestId('default')).toBeExpanded()) + .toThrowErrorMatchingInlineSnapshot(` + "expect(element).toBeExpanded() + + Received element is not expanded: + " + `); +}); diff --git a/src/matchers/extend-expect.d.ts b/src/matchers/extend-expect.d.ts index 2a9a595c0..3efa454f2 100644 --- a/src/matchers/extend-expect.d.ts +++ b/src/matchers/extend-expect.d.ts @@ -10,6 +10,7 @@ export interface JestNativeMatchers { toBeBusy(): R; toBeEmptyElement(): R; toBeEnabled(): R; + toBeExpanded(): R; toBePartiallyChecked(): R; toBeSelected(): R; toBeVisible(): R; diff --git a/src/matchers/extend-expect.ts b/src/matchers/extend-expect.ts index aaad5ba32..4a419d15c 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 { toBeBusy } from './to-be-busy'; import { toBeEmptyElement } from './to-be-empty-element'; +import { toBeExpanded } from './to-be-expanded'; import { toBePartiallyChecked } from './to-be-partially-checked'; import { toBeSelected } from './to-be-selected'; import { toBeVisible } from './to-be-visible'; @@ -21,6 +22,7 @@ expect.extend({ toBeBusy, toBeEmptyElement, toBeEnabled, + toBeExpanded, toBePartiallyChecked, toBeSelected, toBeVisible, diff --git a/src/matchers/index.tsx b/src/matchers/index.tsx index 9b4db7f6c..a4689a371 100644 --- a/src/matchers/index.tsx +++ b/src/matchers/index.tsx @@ -2,6 +2,7 @@ export { toBeBusy } from './to-be-busy'; export { toBeChecked } from './to-be-checked'; export { toBeDisabled, toBeEnabled } from './to-be-disabled'; export { toBeEmptyElement } from './to-be-empty-element'; +export { toBeExpanded } from './to-be-expanded'; export { toBeOnTheScreen } from './to-be-on-the-screen'; export { toBePartiallyChecked } from './to-be-partially-checked'; export { toBeSelected } from './to-be-selected'; diff --git a/src/matchers/to-be-expanded.tsx b/src/matchers/to-be-expanded.tsx new file mode 100644 index 000000000..1fbb8060d --- /dev/null +++ b/src/matchers/to-be-expanded.tsx @@ -0,0 +1,28 @@ +import { ReactTestInstance } from 'react-test-renderer'; +import { matcherHint } from 'jest-matcher-utils'; +import { isElementExpanded } from '../helpers/accessiblity'; +import { checkHostElement, formatElement } from './utils'; + +export function toBeExpanded( + this: jest.MatcherContext, + element: ReactTestInstance +) { + checkHostElement(element, toBeExpanded, this); + + return { + pass: isElementExpanded(element), + message: () => { + const matcher = matcherHint( + `${this.isNot ? '.not' : ''}.toBeExpanded`, + 'element', + '' + ); + return [ + matcher, + '', + `Received element is ${this.isNot ? '' : 'not '}expanded:`, + formatElement(element), + ].join('\n'); + }, + }; +} From 450fd5bed0d3589be57a9eea8776b95ff88583f9 Mon Sep 17 00:00:00 2001 From: Wiktor Date: Mon, 18 Sep 2023 20:15:12 +0200 Subject: [PATCH 2/4] feat: toBeCollapsed matcher --- src/helpers/accessiblity.ts | 11 +++ .../__tests__/to-be-collapsed.test.tsx | 97 +++++++++++++++++++ src/matchers/extend-expect.d.ts | 1 + src/matchers/extend-expect.ts | 2 + src/matchers/index.tsx | 1 + src/matchers/to-be-collapsed.tsx | 28 ++++++ 6 files changed, 140 insertions(+) create mode 100644 src/matchers/__tests__/to-be-collapsed.test.tsx create mode 100644 src/matchers/to-be-collapsed.tsx diff --git a/src/helpers/accessiblity.ts b/src/helpers/accessiblity.ts index 608a48fba..2e0c272b3 100644 --- a/src/helpers/accessiblity.ts +++ b/src/helpers/accessiblity.ts @@ -226,3 +226,14 @@ export function isElementExpanded( const { accessibilityState, 'aria-expanded': ariaExpanded } = element.props; return ariaExpanded ?? accessibilityState?.expanded ?? false; } + +export function isElementCollapsed( + element: ReactTestInstance +): NonNullable { + const { accessibilityState, 'aria-expanded': ariaExpanded } = element.props; + return ariaExpanded !== undefined + ? !ariaExpanded + : accessibilityState !== undefined + ? !accessibilityState.expanded + : false; +} diff --git a/src/matchers/__tests__/to-be-collapsed.test.tsx b/src/matchers/__tests__/to-be-collapsed.test.tsx new file mode 100644 index 000000000..c46100002 --- /dev/null +++ b/src/matchers/__tests__/to-be-collapsed.test.tsx @@ -0,0 +1,97 @@ +import * as React from 'react'; +import { View } from 'react-native'; +import { render, screen } from '../..'; +import '../extend-expect'; + +test('toBeCollapsed() basic case', () => { + render( + <> + + + + + + + ); + + expect(screen.getByTestId('expanded')).not.toBeCollapsed(); + expect(screen.getByTestId('expanded-aria')).not.toBeCollapsed(); + expect(screen.getByTestId('not-expanded')).toBeCollapsed(); + expect(screen.getByTestId('not-expanded-aria')).toBeCollapsed(); + expect(screen.getByTestId('default')).not.toBeCollapsed(); +}); + +test('toBeCollapsed() error messages', () => { + render( + <> + + + + + + + ); + + expect(() => expect(screen.getByTestId('expanded')).toBeCollapsed()) + .toThrowErrorMatchingInlineSnapshot(` + "expect(element).toBeCollapsed() + + Received element is not collapsed: + " + `); + + expect(() => expect(screen.getByTestId('expanded-aria')).toBeCollapsed()) + .toThrowErrorMatchingInlineSnapshot(` + "expect(element).toBeCollapsed() + + Received element is not collapsed: + " + `); + + expect(() => expect(screen.getByTestId('not-expanded')).not.toBeCollapsed()) + .toThrowErrorMatchingInlineSnapshot(` + "expect(element).not.toBeCollapsed() + + Received element is collapsed: + " + `); + + expect(() => + expect(screen.getByTestId('not-expanded-aria')).not.toBeCollapsed() + ).toThrowErrorMatchingInlineSnapshot(` + "expect(element).not.toBeCollapsed() + + Received element is collapsed: + " + `); + + expect(() => expect(screen.getByTestId('default')).toBeCollapsed()) + .toThrowErrorMatchingInlineSnapshot(` + "expect(element).toBeCollapsed() + + Received element is not collapsed: + " + `); +}); diff --git a/src/matchers/extend-expect.d.ts b/src/matchers/extend-expect.d.ts index 3efa454f2..dd8bfa173 100644 --- a/src/matchers/extend-expect.d.ts +++ b/src/matchers/extend-expect.d.ts @@ -6,6 +6,7 @@ import type { Style } from './to-have-style'; export interface JestNativeMatchers { toBeOnTheScreen(): R; toBeChecked(): R; + toBeCollapsed(): R; toBeDisabled(): R; toBeBusy(): R; toBeEmptyElement(): R; diff --git a/src/matchers/extend-expect.ts b/src/matchers/extend-expect.ts index 4a419d15c..c262f3d1c 100644 --- a/src/matchers/extend-expect.ts +++ b/src/matchers/extend-expect.ts @@ -2,6 +2,7 @@ import { toBeOnTheScreen } from './to-be-on-the-screen'; import { toBeChecked } from './to-be-checked'; +import { toBeCollapsed } from './to-be-collapsed'; import { toBeDisabled, toBeEnabled } from './to-be-disabled'; import { toBeBusy } from './to-be-busy'; import { toBeEmptyElement } from './to-be-empty-element'; @@ -18,6 +19,7 @@ import { toHaveTextContent } from './to-have-text-content'; expect.extend({ toBeOnTheScreen, toBeChecked, + toBeCollapsed, toBeDisabled, toBeBusy, toBeEmptyElement, diff --git a/src/matchers/index.tsx b/src/matchers/index.tsx index a4689a371..66a4a0942 100644 --- a/src/matchers/index.tsx +++ b/src/matchers/index.tsx @@ -1,5 +1,6 @@ export { toBeBusy } from './to-be-busy'; export { toBeChecked } from './to-be-checked'; +export { toBeCollapsed } from './to-be-collapsed'; export { toBeDisabled, toBeEnabled } from './to-be-disabled'; export { toBeEmptyElement } from './to-be-empty-element'; export { toBeExpanded } from './to-be-expanded'; diff --git a/src/matchers/to-be-collapsed.tsx b/src/matchers/to-be-collapsed.tsx new file mode 100644 index 000000000..857ab1b9d --- /dev/null +++ b/src/matchers/to-be-collapsed.tsx @@ -0,0 +1,28 @@ +import { ReactTestInstance } from 'react-test-renderer'; +import { matcherHint } from 'jest-matcher-utils'; +import { isElementCollapsed } from '../helpers/accessiblity'; +import { checkHostElement, formatElement } from './utils'; + +export function toBeCollapsed( + this: jest.MatcherContext, + element: ReactTestInstance +) { + checkHostElement(element, toBeCollapsed, this); + + return { + pass: isElementCollapsed(element), + message: () => { + const matcher = matcherHint( + `${this.isNot ? '.not' : ''}.toBeCollapsed`, + 'element', + '' + ); + return [ + matcher, + '', + `Received element is ${this.isNot ? '' : 'not '}collapsed:`, + formatElement(element), + ].join('\n'); + }, + }; +} From fcfed40d9c607ae2e31128fcfce54150e8ad8ec6 Mon Sep 17 00:00:00 2001 From: Wiktor Date: Tue, 19 Sep 2023 10:02:22 +0200 Subject: [PATCH 3/4] chore: shorten syntax for isElementCollapsed check --- src/helpers/accessiblity.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/helpers/accessiblity.ts b/src/helpers/accessiblity.ts index 2e0c272b3..f03f23e85 100644 --- a/src/helpers/accessiblity.ts +++ b/src/helpers/accessiblity.ts @@ -231,9 +231,5 @@ export function isElementCollapsed( element: ReactTestInstance ): NonNullable { const { accessibilityState, 'aria-expanded': ariaExpanded } = element.props; - return ariaExpanded !== undefined - ? !ariaExpanded - : accessibilityState !== undefined - ? !accessibilityState.expanded - : false; + return (ariaExpanded ?? accessibilityState?.expanded) === false; } From c6d182f87f4aebed48dd2c1268ca930830da0ab2 Mon Sep 17 00:00:00 2001 From: Maciej Jastrzebski Date: Tue, 19 Sep 2023 12:10:57 +0200 Subject: [PATCH 4/4] refactor: clean up --- src/helpers/accessiblity.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/helpers/accessiblity.ts b/src/helpers/accessiblity.ts index f03f23e85..7419d3356 100644 --- a/src/helpers/accessiblity.ts +++ b/src/helpers/accessiblity.ts @@ -213,11 +213,11 @@ export function isElementBusy( return ariaBusy ?? accessibilityState?.busy ?? false; } -export function isElementSelected( +export function isElementCollapsed( element: ReactTestInstance -): NonNullable { - const { accessibilityState, 'aria-selected': ariaSelected } = element.props; - return ariaSelected ?? accessibilityState?.selected ?? false; +): NonNullable { + const { accessibilityState, 'aria-expanded': ariaExpanded } = element.props; + return (ariaExpanded ?? accessibilityState?.expanded) === false; } export function isElementExpanded( @@ -227,9 +227,9 @@ export function isElementExpanded( return ariaExpanded ?? accessibilityState?.expanded ?? false; } -export function isElementCollapsed( +export function isElementSelected( element: ReactTestInstance -): NonNullable { - const { accessibilityState, 'aria-expanded': ariaExpanded } = element.props; - return (ariaExpanded ?? accessibilityState?.expanded) === false; +): NonNullable { + const { accessibilityState, 'aria-selected': ariaSelected } = element.props; + return ariaSelected ?? accessibilityState?.selected ?? false; }