diff --git a/packages/native/src/lib/ElementAssertion.ts b/packages/native/src/lib/ElementAssertion.ts index 4abdb73..0c858c0 100644 --- a/packages/native/src/lib/ElementAssertion.ts +++ b/packages/native/src/lib/ElementAssertion.ts @@ -164,6 +164,42 @@ export class ElementAssertion extends Assertion { }); } + /** + * Check if the element has a specific property or a specific property value. + * + * @example + * ``` + * expect(element).toHaveProp("propName"); + * expect(element).toHaveProp("propName", "propValue"); + * ``` + * + * @param propName - The name of the prop to check for. + * @param value - The value of the prop to check for. + * @returns the assertion instance + */ + public toHaveProp(propName: string, value?: unknown): this { + const propValue: unknown = get(this.actual, `props.${propName}`, undefined); + const hasProp = propValue !== undefined; + const isPropEqual = value === undefined || propValue === value; + + const errorMessage = value === undefined + ? `Expected element ${this.toString()} to have prop '${propName}'.` + : `Expected element ${this.toString()} to have prop '${propName}' with value '${String(value)}'.`; + + const invertedErrorMessage = value === undefined + ? `Expected element ${this.toString()} NOT to have prop '${propName}'.` + : `Expected element ${this.toString()} NOT to have prop '${propName}' with value '${String(value)}'.`; + + const error = new AssertionError({ actual: this.actual, message: errorMessage }); + const invertedError = new AssertionError({ actual: this.actual, message: invertedErrorMessage }); + + return this.execute({ + assertWhen: hasProp && isPropEqual, + error, + invertedError, + }); + } + private isElementDisabled(element: ReactTestInstance): boolean { const { type } = element; const elementType = type.toString(); diff --git a/packages/native/test/lib/ElementAssertion.test.tsx b/packages/native/test/lib/ElementAssertion.test.tsx index b657d36..695db7d 100644 --- a/packages/native/test/lib/ElementAssertion.test.tsx +++ b/packages/native/test/lib/ElementAssertion.test.tsx @@ -317,4 +317,62 @@ describe("[Unit] ElementAssertion.test.ts", () => { }); }); }); + + describe(".toHaveProp", () => { + context("when the element contains the target prop", () => { + it("returns the assertion instance", () => { + const element = render( + , + ); + const test = new ElementAssertion(element.getByTestId("id")); + + expect(test.toHaveProp("accessibilityLabel")).toBe(test); + expect(() => test.not.toHaveProp("accessibilityLabel")) + .toThrowError(AssertionError) + .toHaveMessage("Expected element NOT to have prop 'accessibilityLabel'."); + }); + }); + + context("when the element does NOT contain the target prop", () => { + it("throws an error", () => { + const element = render( + , + ); + const test = new ElementAssertion(element.getByTestId("id")); + + expect(test.not.toHaveProp("accessibilityLabel")).toBeEqual(test); + expect(() => test.toHaveProp("accessibilityLabel")) + .toThrowError(AssertionError) + .toHaveMessage("Expected element to have prop 'accessibilityLabel'."); + }); + }); + + context("when the element contains the target prop with a specific value", () => { + it("returns the assertion instance", () => { + const element = render( + , + ); + const test = new ElementAssertion(element.getByTestId("id")); + + expect(test.toHaveProp("accessibilityLabel", "label")).toBe(test); + expect(() => test.not.toHaveProp("accessibilityLabel", "label")) + .toThrowError(AssertionError) + .toHaveMessage("Expected element NOT to have prop 'accessibilityLabel' with value 'label'."); + }); + }); + + context("when the element does NOT contain the target prop with a specific value", () => { + it("throws an error", () => { + const element = render( + , + ); + const test = new ElementAssertion(element.getByTestId("id")); + + expect(test.not.toHaveProp("accessibilityLabel", "label")).toBeEqual(test); + expect(() => test.toHaveProp("accessibilityLabel", "label")) + .toThrowError(AssertionError) + .toHaveMessage("Expected element to have prop 'accessibilityLabel' with value 'label'."); + }); + }); + }); });