diff --git a/package.json b/package.json
index c06fd0497..01b78a9db 100644
--- a/package.json
+++ b/package.json
@@ -82,6 +82,7 @@
"flow": "flow",
"copy-flowtypes": "cp typings/index.flow.js build",
"lint": "eslint src --cache",
+ "validate": "yarn lint && yarn typecheck && yarn test",
"prepublish": "yarn build",
"build:js": "babel src --out-dir build --extensions \".js,.ts,.jsx,.tsx\" --source-maps --ignore \"**/__tests__/**\"",
"build:js:watch": "yarn build:js --watch",
diff --git a/src/matchers/__tests__/to-have-style.test.tsx b/src/matchers/__tests__/to-have-style.test.tsx
new file mode 100644
index 000000000..46834cccd
--- /dev/null
+++ b/src/matchers/__tests__/to-have-style.test.tsx
@@ -0,0 +1,175 @@
+import React from 'react';
+import { StyleSheet, View, Pressable } from 'react-native';
+import { render } from '../..';
+import '../extend-expect';
+
+const styles = StyleSheet.create({
+ container: { borderBottomColor: 'white' },
+});
+
+test('toHaveStyle() handles basic cases', () => {
+ const screen = render(
+
+ );
+
+ const view = screen.getByTestId('view');
+ expect(view).toHaveStyle({ backgroundColor: 'blue' });
+ expect(view).toHaveStyle({ height: '100%' });
+ expect(view).toHaveStyle({ backgroundColor: 'blue', height: '100%' });
+ expect(view).toHaveStyle([{ backgroundColor: 'blue' }, { height: '100%' }]);
+
+ expect(view).toHaveStyle({ borderBottomColor: 'white' });
+ expect(view).toHaveStyle({ width: '50%' });
+ expect(view).toHaveStyle([[{ width: '50%' }]]);
+ expect(view).toHaveStyle({
+ transform: [{ scale: 2 }, { rotate: '45deg' }],
+ });
+
+ expect(view).not.toHaveStyle({ backgroundColor: 'red' });
+ expect(view).not.toHaveStyle({ height: '50%' });
+ expect(view).not.toHaveStyle({ backgroundColor: 'blue', height: '50%' });
+ expect(view).not.toHaveStyle([
+ { backgroundColor: 'blue' },
+ { height: '50%' },
+ ]);
+ expect(view).not.toHaveStyle({
+ transform: [{ scale: 2 }],
+ });
+ expect(view).not.toHaveStyle({
+ transform: [{ rotate: '45deg' }, { scale: 2 }],
+ });
+});
+
+test('toHaveStyle error messages', () => {
+ const screen = render(
+
+ );
+
+ const view = screen.getByTestId('view');
+ expect(() => expect(view).toHaveStyle({ backgroundColor: 'red' }))
+ .toThrowErrorMatchingInlineSnapshot(`
+ "expect(element).toHaveStyle()
+
+ - Expected
+ + Received
+
+ - backgroundColor: red;
+ + backgroundColor: blue;"
+ `);
+
+ expect(() =>
+ expect(view).toHaveStyle({
+ backgroundColor: 'blue',
+ transform: [{ scale: 1 }],
+ })
+ ).toThrowErrorMatchingInlineSnapshot(`
+ "expect(element).toHaveStyle()
+
+ - Expected
+ + Received
+
+ backgroundColor: blue;
+ transform: [
+ {
+ - "scale": 1
+ + "scale": 2
+ + },
+ + {
+ + "rotate": "45deg"
+ }
+ ];"
+ `);
+
+ expect(() => expect(view).not.toHaveStyle({ backgroundColor: 'blue' }))
+ .toThrowErrorMatchingInlineSnapshot(`
+ "expect(element).not.toHaveStyle()
+
+ Expected element not to have style:
+ backgroundColor: blue;
+ Received:
+ backgroundColor: blue;"
+ `);
+
+ expect(() => expect(view).toHaveStyle({ fontWeight: 'bold' }))
+ .toThrowErrorMatchingInlineSnapshot(`
+ "expect(element).toHaveStyle()
+
+ - Expected
+ + Received
+
+ - fontWeight: bold;"
+ `);
+
+ expect(() => expect(view).not.toHaveStyle({ backgroundColor: 'blue' }))
+ .toThrowErrorMatchingInlineSnapshot(`
+ "expect(element).not.toHaveStyle()
+
+ Expected element not to have style:
+ backgroundColor: blue;
+ Received:
+ backgroundColor: blue;"
+ `);
+});
+
+test('toHaveStyle() supports missing "style" prop', () => {
+ const screen = render();
+
+ const view = screen.getByTestId('view');
+ expect(view).not.toHaveStyle({ fontWeight: 'bold' });
+});
+
+test('toHaveStyle() supports undefined "transform" style', () => {
+ const screen = render(
+
+ );
+
+ const view = screen.getByTestId('view');
+ expect(() => expect(view).toHaveStyle({ transform: [{ scale: 1 }] }))
+ .toThrowErrorMatchingInlineSnapshot(`
+ "expect(element).toHaveStyle()
+
+ - Expected
+ + Received
+
+ - transform: [
+ - {
+ - "scale": 1
+ - }
+ - ];
+ + transform: undefined;"
+ `);
+});
+
+test('toHaveStyle() supports Pressable with function "style" prop', () => {
+ const screen = render(
+ ({ backgroundColor: 'blue' })} />
+ );
+
+ expect(screen.getByTestId('view')).toHaveStyle({ backgroundColor: 'blue' });
+});
diff --git a/src/matchers/extend-expect.d.ts b/src/matchers/extend-expect.d.ts
index acdb16d1d..54e02213c 100644
--- a/src/matchers/extend-expect.d.ts
+++ b/src/matchers/extend-expect.d.ts
@@ -1,4 +1,6 @@
+import type { StyleProp } from 'react-native';
import type { TextMatch, TextMatchOptions } from '../matches';
+import type { Style } from './to-have-style';
export interface JestNativeMatchers {
toBeOnTheScreen(): R;
@@ -12,6 +14,7 @@ export interface JestNativeMatchers {
toBeVisible(): R;
toHaveDisplayValue(expectedValue: TextMatch, options?: TextMatchOptions): R;
toHaveProp(name: string, expectedValue?: unknown): R;
+ toHaveStyle(style: StyleProp