From 2e86fbbdceddf5de35bd8508fedc710d6917473e Mon Sep 17 00:00:00 2001 From: ACR1209 Date: Fri, 30 May 2025 13:27:13 -0500 Subject: [PATCH 1/6] feat(native): add toHaveStyle matcher --- packages/native/src/lib/ElementAssertion.ts | 28 ++++ packages/native/src/lib/helpers/styles.ts | 8 + packages/native/src/lib/helpers/types.ts | 19 +++ .../native/test/lib/ElementAssertion.test.tsx | 147 ++++++++++++++++++ 4 files changed, 202 insertions(+) create mode 100644 packages/native/src/lib/helpers/styles.ts create mode 100644 packages/native/src/lib/helpers/types.ts diff --git a/packages/native/src/lib/ElementAssertion.ts b/packages/native/src/lib/ElementAssertion.ts index 0c858c0..7fa16dd 100644 --- a/packages/native/src/lib/ElementAssertion.ts +++ b/packages/native/src/lib/ElementAssertion.ts @@ -3,6 +3,8 @@ import { get } from "dot-prop-immutable"; import { ReactTestInstance } from "react-test-renderer"; import { instanceToString, isEmpty } from "./helpers/helpers"; +import { getFlattenedStyle } from "./helpers/styles"; +import { AssertiveStyle } from "./helpers/types"; export class ElementAssertion extends Assertion { public constructor(actual: ReactTestInstance) { @@ -200,6 +202,32 @@ export class ElementAssertion extends Assertion { }); } + public toHaveStyle(style: AssertiveStyle): this { + const stylesOnElement: AssertiveStyle = get(this.actual, "props.style", {}); + + const flattenedElementStyle = getFlattenedStyle(stylesOnElement); + const flattenedStyle = getFlattenedStyle(style); + + const hasStyle = Object.keys(flattenedStyle) + .every(key => flattenedElementStyle[key] === flattenedStyle[key]); + + const error = new AssertionError({ + actual: this.actual, + message: `Expected element ${this.toString()} to have style ${JSON.stringify(flattenedStyle)}.`, + }); + + const invertedError = new AssertionError({ + actual: this.actual, + message: `Expected element ${this.toString()} NOT to have style ${JSON.stringify(flattenedStyle)}.`, + }); + + return this.execute({ + assertWhen: hasStyle, + error, + invertedError, + }); + } + private isElementDisabled(element: ReactTestInstance): boolean { const { type } = element; const elementType = type.toString(); diff --git a/packages/native/src/lib/helpers/styles.ts b/packages/native/src/lib/helpers/styles.ts new file mode 100644 index 0000000..091983d --- /dev/null +++ b/packages/native/src/lib/helpers/styles.ts @@ -0,0 +1,8 @@ +import { StyleSheet } from "react-native"; + +import { AssertiveStyle, StyleObject } from "./types"; + +export function getFlattenedStyle(style: AssertiveStyle): StyleObject { + const flattenedStyle = StyleSheet.flatten(style); + return flattenedStyle ? (flattenedStyle as StyleObject) : {}; +} diff --git a/packages/native/src/lib/helpers/types.ts b/packages/native/src/lib/helpers/types.ts new file mode 100644 index 0000000..f2263f9 --- /dev/null +++ b/packages/native/src/lib/helpers/types.ts @@ -0,0 +1,19 @@ +import { ImageStyle, StyleProp, TextStyle, ViewStyle } from "react-native"; + +/** + * Type representing a style that can be applied to a React Native component. + * It can be a style for text, view, or image components. + */ +export type Style = TextStyle | ViewStyle | ImageStyle; + +/** + * Type for a style prop that can be applied to a React Native component. + * It can be a single style or an array of styles. + */ +export type AssertiveStyle = StyleProp