diff --git a/src/matchers/__tests__/to-be-empty-element.test.tsx b/src/matchers/__tests__/to-be-empty-element.test.tsx
new file mode 100644
index 000000000..c9e3867b5
--- /dev/null
+++ b/src/matchers/__tests__/to-be-empty-element.test.tsx
@@ -0,0 +1,71 @@
+import React from 'react';
+import { View } from 'react-native';
+import { render, screen } from '../..';
+import '../extend-expect';
+
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+function DoNotRenderChildren({ children }: { children: React.ReactNode }) {
+ // Intentionally do not render children.
+ return null;
+}
+
+test('toBeEmptyElement()', () => {
+ render(
+
+
+
+ );
+
+ const empty = screen.getByTestId('empty');
+ expect(empty).toBeEmptyElement();
+ expect(() => expect(empty).not.toBeEmptyElement())
+ .toThrowErrorMatchingInlineSnapshot(`
+ "expect(element).not.toBeEmptyElement()
+
+ Received:
+ (no elements)"
+ `);
+
+ const notEmpty = screen.getByTestId('not-empty');
+ expect(notEmpty).not.toBeEmptyElement();
+ expect(() => expect(notEmpty).toBeEmptyElement())
+ .toThrowErrorMatchingInlineSnapshot(`
+ "expect(element).toBeEmptyElement()
+
+ Received:
+ "
+ `);
+});
+
+test('toBeEmptyElement() ignores composite-only children', () => {
+ render(
+
+
+
+
+
+ );
+
+ const view = screen.getByTestId('view');
+ expect(view).toBeEmptyElement();
+ expect(() => expect(view).not.toBeEmptyElement())
+ .toThrowErrorMatchingInlineSnapshot(`
+ "expect(element).not.toBeEmptyElement()
+
+ Received:
+ (no elements)"
+ `);
+});
+
+test('toBeEmptyElement() on null element', () => {
+ expect(() => {
+ expect(null).toBeEmptyElement();
+ }).toThrowErrorMatchingInlineSnapshot(`
+ "expect(received).toBeEmptyElement()
+
+ received value must be a host element.
+ Received has value: null"
+ `);
+});
diff --git a/src/matchers/__tests__/utils.test.tsx b/src/matchers/__tests__/utils.test.tsx
index 0e210459d..f73eb3ce1 100644
--- a/src/matchers/__tests__/utils.test.tsx
+++ b/src/matchers/__tests__/utils.test.tsx
@@ -8,7 +8,7 @@ function fakeMatcher() {
}
test('formatElement', () => {
- expect(formatElement(null)).toMatchInlineSnapshot(`"null"`);
+ expect(formatElement(null)).toMatchInlineSnapshot(`" null"`);
});
test('checkHostElement allows host element', () => {
diff --git a/src/matchers/extend-expect.d.ts b/src/matchers/extend-expect.d.ts
index 0fcec96c9..435d4c509 100644
--- a/src/matchers/extend-expect.d.ts
+++ b/src/matchers/extend-expect.d.ts
@@ -1,8 +1,6 @@
-import { TextMatch, TextMatchOptions } from '../matches';
-
export interface JestNativeMatchers {
toBeOnTheScreen(): R;
- toHaveTextContent(text: TextMatch, options?: TextMatchOptions): R;
+ toBeEmptyElement(): R;
}
// Implicit Jest global `expect`.
diff --git a/src/matchers/extend-expect.ts b/src/matchers/extend-expect.ts
index 1a998d336..dc7744189 100644
--- a/src/matchers/extend-expect.ts
+++ b/src/matchers/extend-expect.ts
@@ -1,7 +1,9 @@
///
import { toBeOnTheScreen } from './to-be-on-the-screen';
+import { toBeEmptyElement } from './to-be-empty-element';
expect.extend({
toBeOnTheScreen,
+ toBeEmptyElement,
});
diff --git a/src/matchers/index.tsx b/src/matchers/index.tsx
index 8d1540b14..34adad661 100644
--- a/src/matchers/index.tsx
+++ b/src/matchers/index.tsx
@@ -1 +1,2 @@
export { toBeOnTheScreen } from './to-be-on-the-screen';
+export { toBeEmptyElement } from './to-be-empty-element';
diff --git a/src/matchers/to-be-empty-element.tsx b/src/matchers/to-be-empty-element.tsx
new file mode 100644
index 000000000..867b71392
--- /dev/null
+++ b/src/matchers/to-be-empty-element.tsx
@@ -0,0 +1,29 @@
+import { ReactTestInstance } from 'react-test-renderer';
+import { matcherHint, RECEIVED_COLOR } from 'jest-matcher-utils';
+import { getHostChildren } from '../helpers/component-tree';
+import { checkHostElement, formatElementArray } from './utils';
+
+export function toBeEmptyElement(
+ this: jest.MatcherContext,
+ element: ReactTestInstance
+) {
+ checkHostElement(element, toBeEmptyElement, this);
+
+ const hostChildren = getHostChildren(element);
+
+ return {
+ pass: hostChildren.length === 0,
+ message: () => {
+ return [
+ matcherHint(
+ `${this.isNot ? '.not' : ''}.toBeEmptyElement`,
+ 'element',
+ ''
+ ),
+ '',
+ 'Received:',
+ `${RECEIVED_COLOR(formatElementArray(hostChildren))}`,
+ ].join('\n');
+ },
+ };
+}
diff --git a/src/matchers/utils.tsx b/src/matchers/utils.tsx
index ef5b10f91..f6808b09c 100644
--- a/src/matchers/utils.tsx
+++ b/src/matchers/utils.tsx
@@ -69,7 +69,7 @@ export function checkHostElement(
*/
export function formatElement(element: ReactTestInstance | null) {
if (element == null) {
- return 'null';
+ return ' null';
}
return redent(
@@ -92,6 +92,14 @@ export function formatElement(element: ReactTestInstance | null) {
);
}
+export function formatElementArray(elements: ReactTestInstance[]) {
+ if (elements.length === 0) {
+ return ' (no elements)';
+ }
+
+ return redent(elements.map(formatElement).join('\n'), 2);
+}
+
export function formatMessage(
matcher: string,
expectedLabel: string,