diff --git a/src/matchers/__tests__/to-contain-element.test.tsx b/src/matchers/__tests__/to-contain-element.test.tsx new file mode 100644 index 000000000..27e5be1ec --- /dev/null +++ b/src/matchers/__tests__/to-contain-element.test.tsx @@ -0,0 +1,141 @@ +import * as React from 'react'; +import { View } from 'react-native'; +import { render, screen } from '../..'; +import '../extend-expect'; + +test('toContainElement() supports basic case', () => { + render( + + + + ); + + const parent = screen.getByTestId('parent'); + const child = screen.getByTestId('child'); + + expect(parent).toContainElement(child); + + expect(() => expect(parent).not.toContainElement(child)) + .toThrowErrorMatchingInlineSnapshot(` + "expect(container).not.toContainElement(element) + + + + contains: + + + " + `); +}); + +test('toContainElement() supports negative case', () => { + render( + <> + + + + ); + + const view1 = screen.getByTestId('view1'); + const view2 = screen.getByTestId('view2'); + + expect(view1).not.toContainElement(view2); + expect(view2).not.toContainElement(view1); + + expect(() => expect(view1).toContainElement(view2)) + .toThrowErrorMatchingInlineSnapshot(` + "expect(container).toContainElement(element) + + + + does not contain: + + + " + `); +}); + +test('toContainElement() handles null container', () => { + render(); + + const view = screen.getByTestId('view'); + + expect(() => expect(null).toContainElement(view)) + .toThrowErrorMatchingInlineSnapshot(` + "expect(received).toContainElement() + + received value must be a host element. + Received has value: null" + `); +}); + +test('toContainElement() handles null element', () => { + render(); + + const view = screen.getByTestId('view'); + + expect(view).not.toContainElement(null); + + expect(() => expect(view).toContainElement(null)) + .toThrowErrorMatchingInlineSnapshot(` + "expect(container).toContainElement(element) + + + + does not contain: + + null + " + `); +}); + +test('toContainElement() handles non-element container', () => { + render(); + + const view = screen.getByTestId('view'); + + expect(() => expect({ name: 'non-element' }).not.toContainElement(view)) + .toThrowErrorMatchingInlineSnapshot(` + "expect(received).not.toContainElement() + + received value must be a host element. + Received has type: object + Received has value: {"name": "non-element"}" + `); + + expect(() => expect(true).not.toContainElement(view)) + .toThrowErrorMatchingInlineSnapshot(` + "expect(received).not.toContainElement() + + received value must be a host element. + Received has type: boolean + Received has value: true" + `); +}); + +test('toContainElement() handles non-element element', () => { + render(); + + const view = screen.getByTestId('view'); + + expect(() => + // @ts-expect-error + expect(view).not.toContainElement({ name: 'non-element' }) + ).toThrowErrorMatchingInlineSnapshot(` + "expect(received).not.toContainElement() + + received value must be a host element. + Received has type: object + Received has value: {"name": "non-element"}" + `); +}); diff --git a/src/matchers/extend-expect.d.ts b/src/matchers/extend-expect.d.ts index 54e02213c..2a9a595c0 100644 --- a/src/matchers/extend-expect.d.ts +++ b/src/matchers/extend-expect.d.ts @@ -1,4 +1,5 @@ import type { StyleProp } from 'react-native'; +import type { ReactTestInstance } from 'react-test-renderer'; import type { TextMatch, TextMatchOptions } from '../matches'; import type { Style } from './to-have-style'; @@ -12,6 +13,7 @@ export interface JestNativeMatchers { toBePartiallyChecked(): R; toBeSelected(): R; toBeVisible(): R; + toContainElement(element: ReactTestInstance | null): R; toHaveDisplayValue(expectedValue: TextMatch, options?: TextMatchOptions): R; toHaveProp(name: string, expectedValue?: unknown): R; toHaveStyle(style: StyleProp