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