diff --git a/packages/native/src/lib/ElementAssertion.ts b/packages/native/src/lib/ElementAssertion.ts index f30d78b..bc804fc 100644 --- a/packages/native/src/lib/ElementAssertion.ts +++ b/packages/native/src/lib/ElementAssertion.ts @@ -121,6 +121,47 @@ export class ElementAssertion extends Assertion { }); } + /** + * Check if an element is contained within another element. + * + * @example + * ``` + * expect(parent).toContainElement(child); + * ``` + * + * @param element - The element to check for. + * @returns the assertion instance + */ + public toContainElement(element: ReactTestInstance): this { + const error = new AssertionError({ + actual: this.actual, + message: `Expected element ${this.toString()} to contain element ${instanceToString(element)}.`, + }); + const invertedError = new AssertionError({ + actual: this.actual, + message: `Expected element ${this.toString()} NOT to contain element ${instanceToString(element)}.`, + }); + + return this.execute({ + assertWhen: this.isElementContained(this.actual, element), + error, + invertedError, + }); + } + + private isElementContained(parentElement: ReactTestInstance, childElement: ReactTestInstance): boolean { + if (parentElement === null || childElement === null) { + return false; + } + + return ( + parentElement.findAll( + node => + node.type === childElement.type && node.props === childElement.props, + ).length > 0 + ); + } + private isElementDisabled(element: ReactTestInstance): boolean { const { type } = element; const elementType = type.toString(); diff --git a/packages/native/src/lib/helpers/helpers.ts b/packages/native/src/lib/helpers/helpers.ts index 631395c..6ab4db1 100644 --- a/packages/native/src/lib/helpers/helpers.ts +++ b/packages/native/src/lib/helpers/helpers.ts @@ -1,9 +1,27 @@ import { ReactTestInstance } from "react-test-renderer"; +/** + * Checks if a value is empty. + * + * @param value - The value to check. + * @returns `true` if the value is empty, `false` otherwise. + */ +export function isEmpty(value: unknown): boolean { + if (!value) { + return true; + } + + if (Array.isArray(value)) { + return value.length === 0; + } + + return false; +} + /** * Converts a ReactTestInstance to a string representation. * - * @param instance The ReactTestInstance to convert. + * @param instance - The ReactTestInstance to convert. * @returns A string representation of the instance. */ export function instanceToString(instance: ReactTestInstance | null): string { diff --git a/packages/native/test/lib/ElementAssertion.test.tsx b/packages/native/test/lib/ElementAssertion.test.tsx index 575046d..155ec3b 100644 --- a/packages/native/test/lib/ElementAssertion.test.tsx +++ b/packages/native/test/lib/ElementAssertion.test.tsx @@ -311,4 +311,64 @@ describe("[Unit] ElementAssertion.test.ts", () => { }); }); }); + + describe (".toContainElement", () => { + const element = render( + + + + + + , + ); + + const container = element.getByTestId("grandParentId"); + const containerElementAssertion = new ElementAssertion(container); + const parent = element.getByTestId("parentId"); + const parentElementAssertion = new ElementAssertion(parent); + const child = element.getByTestId("childId"); + const text = element.getByTestId("textId"); + const textElementAssertion = new ElementAssertion(text); + + context("when the element has children", () => { + context("and the target element is found in the children's element", () => { + it("returns the assertion instance", () => { + expect(containerElementAssertion.toContainElement(parent)).toBe(containerElementAssertion); + expect(containerElementAssertion.toContainElement(child)).toBe(containerElementAssertion); + expect(containerElementAssertion.toContainElement(text)).toBe(containerElementAssertion); + expect(parentElementAssertion.toContainElement(child)).toBe(parentElementAssertion); + }); + + it("throws an error for negative assertion", () => { + expect(() => containerElementAssertion.not.toContainElement(parent)) + .toThrowError(AssertionError) + .toHaveMessage("Expected element NOT to contain element ."); + expect(() => containerElementAssertion.not.toContainElement(text)) + .toThrowError(AssertionError) + .toHaveMessage("Expected element NOT to contain element ."); + }); + }); + + context("and the target element is NOT found in the children's element", () => { + it("throws an error", () => { + expect(() => parentElementAssertion.toContainElement(text)) + .toThrowError(AssertionError) + .toHaveMessage("Expected element to contain element ."); + }); + + it("returns the assertion instance for negative assertion", () => { + expect(parentElementAssertion.not.toContainElement(text)).toBeEqual(parentElementAssertion); + expect(parentElementAssertion.not.toContainElement(container)).toBeEqual(parentElementAssertion); + }); + }); + }); + + context("when the element does NOT have children", () => { + it("throws an error", () => { + expect(() => textElementAssertion.toContainElement(parent)) + .toThrowError(AssertionError) + .toHaveMessage("Expected element to contain element ."); + }); + }); + }); });