Skip to content

feat(native): Add toBeEmptyElement #142

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Apr 25, 2025
40 changes: 33 additions & 7 deletions packages/native/src/lib/ElementAssertion.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import { Assertion, AssertionError } from "@assertive-ts/core";
import { get } from "dot-prop-immutable";
import { Children } from "react";
import { ReactTestInstance } from "react-test-renderer";

import { instanceToString } from "./helpers/helpers";

export class ElementAssertion extends Assertion<ReactTestInstance> {
public constructor(actual: ReactTestInstance) {
super(actual);
}

public override toString = (): string => {
if (this.actual === null) {
return "null";
}

return `<${this.actual.type.toString()} ... />`;
return instanceToString(this.actual);
};

/**
Expand All @@ -32,7 +31,7 @@ export class ElementAssertion extends Assertion<ReactTestInstance> {
});
const invertedError = new AssertionError({
actual: this.actual,
message: `Expected element ${this.toString()} to NOT be disabled.`,
message: `Expected element ${this.toString()} NOT to be disabled.`,
});

return this.execute({
Expand All @@ -58,7 +57,7 @@ export class ElementAssertion extends Assertion<ReactTestInstance> {
});
const invertedError = new AssertionError({
actual: this.actual,
message: `Expected element ${this.toString()} to NOT be enabled.`,
message: `Expected element ${this.toString()} NOT to be enabled.`,
});

return this.execute({
Expand All @@ -68,6 +67,33 @@ export class ElementAssertion extends Assertion<ReactTestInstance> {
});
}

/**
* Check if the element is empty.
*
* @example
* ```
* expect(element).toBeEmpty();
* ```
*
* @returns the assertion instance
*/
public toBeEmpty(): this {
const error = new AssertionError({
actual: this.actual,
message: `Expected element ${this.toString()} to be empty.`,
});
const invertedError = new AssertionError({
actual: this.actual,
message: `Expected element ${this.toString()} NOT to be empty.`,
});

return this.execute({
assertWhen: Children.count(this.actual.props.children) === 0,
error,
invertedError,
});
}

private isElementDisabled(element: ReactTestInstance): boolean {
const { type } = element;
const elementType = type.toString();
Expand Down
15 changes: 15 additions & 0 deletions packages/native/src/lib/helpers/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ReactTestInstance } from "react-test-renderer";

/**
* Converts a ReactTestInstance to a string representation.
*
* @param instance The ReactTestInstance to convert.
* @returns A string representation of the instance.
*/
export function instanceToString(instance: ReactTestInstance | null): string {
if (instance === null) {
return "null";
}

return `<${instance.type.toString()} ... />`;
}
43 changes: 37 additions & 6 deletions packages/native/test/lib/ElementAssertion.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { render } from "@testing-library/react-native";
import {
View,
TextInput,
Text,
} from "react-native";

import { ElementAssertion } from "../../src/lib/ElementAssertion";
Expand Down Expand Up @@ -34,7 +35,7 @@ describe("[Unit] ElementAssertion.test.ts", () => {
.toHaveMessage("Expected element <TextInput ... /> to be disabled.");
expect(() => test.not.toBeEnabled())
.toThrowError(AssertionError)
.toHaveMessage("Expected element <TextInput ... /> to NOT be enabled.");
.toHaveMessage("Expected element <TextInput ... /> NOT to be enabled.");
});
});
});
Expand All @@ -59,7 +60,7 @@ describe("[Unit] ElementAssertion.test.ts", () => {
.toHaveMessage("Expected element <View ... /> to be enabled.");
expect(() => parent.not.toBeDisabled())
.toThrowError(AssertionError)
.toHaveMessage("Expected element <View ... /> to NOT be disabled.");
.toHaveMessage("Expected element <View ... /> NOT to be disabled.");
});
});

Expand All @@ -83,13 +84,13 @@ describe("[Unit] ElementAssertion.test.ts", () => {
.toHaveMessage("Expected element <View ... /> to be disabled.");
expect(() => parent.not.toBeEnabled())
.toThrowError(AssertionError)
.toHaveMessage("Expected element <View ... /> to NOT be enabled.");
.toHaveMessage("Expected element <View ... /> NOT to be enabled.");
expect(() => child.toBeDisabled())
.toThrowError(AssertionError)
.toHaveMessage("Expected element <View ... /> to be disabled.");
expect(() => child.not.toBeEnabled())
.toThrowError(AssertionError)
.toHaveMessage("Expected element <View ... /> to NOT be enabled.");
.toHaveMessage("Expected element <View ... /> NOT to be enabled.");
});
});
});
Expand All @@ -114,7 +115,7 @@ describe("[Unit] ElementAssertion.test.ts", () => {
.toHaveMessage("Expected element <View ... /> to be enabled.");
expect(() => child.not.toBeDisabled())
.toThrowError(AssertionError)
.toHaveMessage("Expected element <View ... /> to NOT be disabled.");
.toHaveMessage("Expected element <View ... /> NOT to be disabled.");
});

it("returns error for parent element", () => {
Expand All @@ -124,9 +125,39 @@ describe("[Unit] ElementAssertion.test.ts", () => {
.toHaveMessage("Expected element <View ... /> to be disabled.");
expect(() => parent.not.toBeEnabled())
.toThrowError(AssertionError)
.toHaveMessage("Expected element <View ... /> to NOT be enabled.");
.toHaveMessage("Expected element <View ... /> NOT to be enabled.");
});
});
});
});

describe(".toBeEmpty", () => {
context("when the element is empty", () => {
it("returns the assertion instance", () => {
const element = render(<View testID="id" />);
const test = new ElementAssertion(element.getByTestId("id"));

expect(test.toBeEmpty()).toBe(test);
expect(() => test.not.toBeEmpty())
.toThrowError(AssertionError)
.toHaveMessage("Expected element <View ... /> NOT to be empty.");
});
});

context("when the element is NOT empty", () => {
it("throws an error", () => {
const element = render(
<View testID="id">
<Text>{"Not empty"}</Text>
</View>,
);
const test = new ElementAssertion(element.getByTestId("id"));

expect(test.not.toBeEmpty()).toBeEqual(test);
expect(() => test.toBeEmpty())
.toThrowError(AssertionError)
.toHaveMessage("Expected element <View ... /> to be empty.");
});
});
});
});