diff --git a/src/helpers/__tests__/getTextContent.test.tsx b/src/helpers/__tests__/getTextContent.test.tsx index e2f937c7d..76a9bd56c 100644 --- a/src/helpers/__tests__/getTextContent.test.tsx +++ b/src/helpers/__tests__/getTextContent.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Text } from 'react-native'; import render from '../../render'; -import { getTextContent } from '../getTextContent'; +import { getTextContent } from '../text-content'; test('getTextContent with simple content', () => { const view = render(Hello world); diff --git a/src/helpers/matchers/matchTextContent.ts b/src/helpers/matchers/matchTextContent.ts index 4c025e6ac..182cb1469 100644 --- a/src/helpers/matchers/matchTextContent.ts +++ b/src/helpers/matchers/matchTextContent.ts @@ -1,6 +1,6 @@ import type { ReactTestInstance } from 'react-test-renderer'; import { matches, TextMatch, TextMatchOptions } from '../../matches'; -import { getTextContent } from '../getTextContent'; +import { getTextContent } from '../text-content'; /** * Matches the given node's text content against string or regex matcher. diff --git a/src/helpers/getTextContent.ts b/src/helpers/text-content.ts similarity index 100% rename from src/helpers/getTextContent.ts rename to src/helpers/text-content.ts diff --git a/src/matchers/__tests__/to-have-text-content.test.tsx b/src/matchers/__tests__/to-have-text-content.test.tsx new file mode 100644 index 000000000..71e7b628b --- /dev/null +++ b/src/matchers/__tests__/to-have-text-content.test.tsx @@ -0,0 +1,84 @@ +/// + +import * as React from 'react'; +import { View, Text } from 'react-native'; +import { render, screen } from '../..'; +import '../extend-expect'; + +test('toHaveTextContent() example test', () => { + render( + + Hello World + + ); + + const view = screen.getByTestId('view'); + expect(view).toHaveTextContent('Hello World'); + expect(view).not.toHaveTextContent('Hello there'); +}); + +test('toHaveTextContent() handles positive test cases', () => { + render(Hello World); + + const text = screen.getByTestId('text'); + expect(text).toHaveTextContent('Hello World'); + expect(text).toHaveTextContent('Hello', { exact: false }); + expect(text).toHaveTextContent(/Hello World/); +}); + +test('toHaveTextContent() does exact match by default', () => { + render(Hello World); + + const text = screen.getByTestId('text'); + expect(text).toHaveTextContent('Hello World'); + expect(text).not.toHaveTextContent('Hello'); + expect(text).not.toHaveTextContent('World'); +}); + +test('toHaveTextContent() handles nested text', () => { + render( + + Hello React + + ); + + const text = screen.getByTestId('text'); + expect(text).toHaveTextContent('Hello React'); +}); + +test('toHaveTextContent() negative test cases', () => { + render(Hello World); + + const text = screen.getByTestId('text'); + expect(text).not.toHaveTextContent(/Hello React/); + expect(() => expect(text).toHaveTextContent(/Hello React/)) + .toThrowErrorMatchingInlineSnapshot(` + "expect(element).toHaveTextContent() + + Expected element to have text content: + /Hello React/ + Received: + Hello World" + `); + + expect(text).not.toHaveTextContent('Yellow', { exact: false }); + expect(() => expect(text).toHaveTextContent('Yellow', { exact: false })) + .toThrowErrorMatchingInlineSnapshot(` + "expect(element).toHaveTextContent() + + Expected element to have text content: + Yellow + Received: + Hello World" + `); +}); + +test('toHaveTextContent() on null element', () => { + expect(() => expect(null).toHaveTextContent('Hello World')) + .toThrowErrorMatchingInlineSnapshot(` + "expect(received).toHaveTextContent() + + received value must be a host element. + Received has value: null" + `); +}); diff --git a/src/matchers/extend-expect.d.ts b/src/matchers/extend-expect.d.ts index 16a31109e..d10520755 100644 --- a/src/matchers/extend-expect.d.ts +++ b/src/matchers/extend-expect.d.ts @@ -1,9 +1,10 @@ -import { TextMatch, TextMatchOptions } from '../matches'; +import type { TextMatch, TextMatchOptions } from '../matches'; export interface JestNativeMatchers { toBeOnTheScreen(): R; toBeEmptyElement(): R; toHaveDisplayValue(expectedValue: TextMatch, options?: TextMatchOptions): R; + toHaveTextContent(expectedText: TextMatch, options?: TextMatchOptions): R; } // Implicit Jest global `expect`. diff --git a/src/matchers/extend-expect.ts b/src/matchers/extend-expect.ts index bf6b1bac5..188deaa6b 100644 --- a/src/matchers/extend-expect.ts +++ b/src/matchers/extend-expect.ts @@ -3,9 +3,11 @@ import { toBeOnTheScreen } from './to-be-on-the-screen'; import { toBeEmptyElement } from './to-be-empty-element'; import { toHaveDisplayValue } from './to-have-display-value'; +import { toHaveTextContent } from './to-have-text-content'; expect.extend({ toBeOnTheScreen, toBeEmptyElement, toHaveDisplayValue, + toHaveTextContent, }); diff --git a/src/matchers/to-have-text-content.tsx b/src/matchers/to-have-text-content.tsx new file mode 100644 index 000000000..373bfbff6 --- /dev/null +++ b/src/matchers/to-have-text-content.tsx @@ -0,0 +1,35 @@ +import type { ReactTestInstance } from 'react-test-renderer'; +import { matcherHint } from 'jest-matcher-utils'; +import { getTextContent } from '../helpers/text-content'; +import { TextMatch, TextMatchOptions, matches } from '../matches'; +import { checkHostElement, formatMessage } from './utils'; + +export function toHaveTextContent( + this: jest.MatcherContext, + element: ReactTestInstance, + expectedText: TextMatch, + options?: TextMatchOptions +) { + checkHostElement(element, toHaveTextContent, this); + + const text = getTextContent(element); + + return { + pass: matches(expectedText, text, options?.normalizer, options?.exact), + message: () => { + return [ + formatMessage( + matcherHint( + `${this.isNot ? '.not' : ''}.toHaveTextContent`, + 'element', + '' + ), + `Expected element ${this.isNot ? 'not to' : 'to'} have text content`, + expectedText, + 'Received', + text + ), + ].join('\n'); + }, + }; +}