From 6fe3985f5f78344e6f0e21b49e4d46632ad8f26a Mon Sep 17 00:00:00 2001 From: Andrew Datsenko Date: Wed, 6 Aug 2025 07:01:37 -0700 Subject: [PATCH 1/7] Add tests for defaultSource and height (#53058) Summary: Changelog: [Internal] As title Reviewed By: rshest Differential Revision: D79566292 --- .../Libraries/Image/__tests__/Image-itest.js | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/react-native/Libraries/Image/__tests__/Image-itest.js b/packages/react-native/Libraries/Image/__tests__/Image-itest.js index d946941d444a..59777f2886b2 100644 --- a/packages/react-native/Libraries/Image/__tests__/Image-itest.js +++ b/packages/react-native/Libraries/Image/__tests__/Image-itest.js @@ -169,6 +169,44 @@ describe('', () => { ); }); }); + + describe('defaultSource', () => { + it('can provide a default image to display', () => { + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render( + , + ); + }); + + expect( + root.getRenderedOutput({props: ['defaultSource']}).toJSX(), + ).toEqual( + , + ); + }); + }); + + describe('height', () => { + it('provides height for image', () => { + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render(); + }); + + expect(root.getRenderedOutput({props: ['height']}).toJSX()).toEqual( + , + ); + }); + }); }); describe('ref', () => { From b854f1ac2bac24c869d84ada7fcc50cbe4e12463 Mon Sep 17 00:00:00 2001 From: Andrew Datsenko Date: Wed, 6 Aug 2025 07:01:37 -0700 Subject: [PATCH 2/7] Add on props (#53060) Summary: Changelog: [Internal] Add tests for Image props that notify of image loading progress. Reviewed By: rshest Differential Revision: D79596580 --- .../Libraries/Image/__tests__/Image-itest.js | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/packages/react-native/Libraries/Image/__tests__/Image-itest.js b/packages/react-native/Libraries/Image/__tests__/Image-itest.js index 59777f2886b2..ed8ae48b899a 100644 --- a/packages/react-native/Libraries/Image/__tests__/Image-itest.js +++ b/packages/react-native/Libraries/Image/__tests__/Image-itest.js @@ -207,6 +207,56 @@ describe('', () => { ); }); }); + + describe('loading progress', () => { + ( + [ + ['onError', 'fails to load'], + ['onLoadStart', 'start loading'], + ['onProgress', 'is loading'], + ['onLoad', 'loads successfully'], + ['onLoadEnd', 'ends loading'], + ] as const + ).forEach(([onProp, event]) => { + it(`${onProp} is called when image ${event}`, () => { + const onPropCallback = jest.fn(); + const ref = createRef(); + + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render( + { + onProp === 'onError' && onPropCallback(); + }} + onLoad={() => { + onProp === 'onLoad' && onPropCallback(); + }} + onLoadStart={() => { + onProp === 'onLoadStart' && onPropCallback(); + }} + onLoadEnd={() => { + onProp === 'onLoadEnd' && onPropCallback(); + }} + onProgress={() => { + onProp === 'onProgress' && onPropCallback(); + }} + />, + ); + }); + + expect(onPropCallback).toHaveBeenCalledTimes(0); + + const image = ensureInstance(ref.current, ReactNativeElement); + Fantom.dispatchNativeEvent(image, onProp, {}); + + expect(onPropCallback).toHaveBeenCalledTimes(1); + }); + }); + }); }); describe('ref', () => { From dd87b365d2061271eec6e94587c35ef66dde1df9 Mon Sep 17 00:00:00 2001 From: Andrew Datsenko Date: Wed, 6 Aug 2025 07:01:37 -0700 Subject: [PATCH 3/7] Add referrerPolicy tests (#53073) Summary: Changelog: [Internal] Add referrerPolicty for cross origin requests. Reviewed By: rshest Differential Revision: D79598735 --- .../Libraries/Image/__tests__/Image-itest.js | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/react-native/Libraries/Image/__tests__/Image-itest.js b/packages/react-native/Libraries/Image/__tests__/Image-itest.js index ed8ae48b899a..cd73f21af607 100644 --- a/packages/react-native/Libraries/Image/__tests__/Image-itest.js +++ b/packages/react-native/Libraries/Image/__tests__/Image-itest.js @@ -257,6 +257,37 @@ describe('', () => { }); }); }); + + describe('referrerPolicy', () => { + ( + [ + 'no-referrer', + 'no-referrer-when-downgrade', + 'origin', + 'origin-when-cross-origin', + 'same-origin', + 'strict-origin', + 'strict-origin-when-cross-origin', + 'unsafe-url', + ] as const + ).forEach(referrerPolicy => { + it(`${referrerPolicy} sets correct "Referrer-Policy" header`, () => { + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render( + , + ); + }); + + expect( + root.getRenderedOutput({props: ['source-header']}).toJSX(), + ).toEqual( + , + ); + }); + }); + }); }); describe('ref', () => { From 207900ce72a68c564aaed264ec2ab5775ef85652 Mon Sep 17 00:00:00 2001 From: Andrew Datsenko Date: Wed, 6 Aug 2025 07:01:37 -0700 Subject: [PATCH 4/7] Add resizeMode tests (#53061) Summary: Changelog: [Internal] Add tests for resizeMode prop Reviewed By: rshest Differential Revision: D79600137 --- .../Libraries/Image/__tests__/Image-itest.js | 40 +++++++++++++++++++ .../renderer/components/image/ImageProps.cpp | 22 ++++++++++ .../renderer/components/image/ImageProps.h | 2 +- 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/Image/__tests__/Image-itest.js b/packages/react-native/Libraries/Image/__tests__/Image-itest.js index cd73f21af607..ae74a839cc00 100644 --- a/packages/react-native/Libraries/Image/__tests__/Image-itest.js +++ b/packages/react-native/Libraries/Image/__tests__/Image-itest.js @@ -288,6 +288,46 @@ describe('', () => { }); }); }); + + describe('resizeMode', () => { + it('is set to "cover" by default', () => { + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render(); + }); + + expect(root.getRenderedOutput({props: ['resizeMode']}).toJSX()).toEqual( + , + ); + + Fantom.runTask(() => { + root.render(); + }); + + expect(root.getRenderedOutput({props: ['resizeMode']}).toJSX()).toEqual( + , + ); + }); + + (['stretch', 'contain', 'repeat', 'center'] as const).forEach( + resizeMode => { + it(`can be set to "${resizeMode}"`, () => { + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render( + , + ); + }); + + expect( + root.getRenderedOutput({props: ['resizeMode']}).toJSX(), + ).toEqual(); + }); + }, + ); + }); }); describe('ref', () => { diff --git a/packages/react-native/ReactCommon/react/renderer/components/image/ImageProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/image/ImageProps.cpp index 19d5501f09b0..dca045f67ce7 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/image/ImageProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/image/ImageProps.cpp @@ -304,8 +304,30 @@ SharedDebugStringConvertibleList ImageProps::getDebugProps() const { SharedDebugStringConvertibleList{ debugStringConvertibleItem( "blurRadius", blurRadius, imageProps.blurRadius), + debugStringConvertibleItem( + "resizeMode", + toString(resizeMode), + toString(imageProps.resizeMode)), }; } + +inline std::string toString(ImageResizeMode resizeMode) { + switch (resizeMode) { + case ImageResizeMode::Cover: + return "cover"; + case ImageResizeMode::Contain: + return "contain"; + case ImageResizeMode::Stretch: + return "stretch"; + case ImageResizeMode::Center: + return "center"; + case ImageResizeMode::Repeat: + return "repeat"; + case ImageResizeMode::None: + return "none"; + } +} + #endif } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/components/image/ImageProps.h b/packages/react-native/ReactCommon/react/renderer/components/image/ImageProps.h index 43cdc51ac5b7..1b753d7ffa0b 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/image/ImageProps.h +++ b/packages/react-native/ReactCommon/react/renderer/components/image/ImageProps.h @@ -34,7 +34,7 @@ class ImageProps final : public ViewProps { ImageSources sources{}; ImageSource defaultSource{}; ImageSource loadingIndicatorSource{}; - ImageResizeMode resizeMode{ImageResizeMode::Stretch}; + ImageResizeMode resizeMode{ImageResizeMode::Cover}; Float blurRadius{}; EdgeInsets capInsets{}; SharedColor tintColor{}; From 329439c552f26057ba96e0e6302bb1076a80310c Mon Sep 17 00:00:00 2001 From: Andrew Datsenko Date: Wed, 6 Aug 2025 07:01:37 -0700 Subject: [PATCH 5/7] Add source tests (#53074) Summary: Changelog: [Internal] Add tests for source prop of Image. Reviewed By: rshest Differential Revision: D79605165 --- .../Libraries/Image/__tests__/Image-itest.js | 101 ++++++++++++++++++ .../renderer/components/image/ImageProps.cpp | 2 +- 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/Image/__tests__/Image-itest.js b/packages/react-native/Libraries/Image/__tests__/Image-itest.js index ae74a839cc00..b95736aebbee 100644 --- a/packages/react-native/Libraries/Image/__tests__/Image-itest.js +++ b/packages/react-native/Libraries/Image/__tests__/Image-itest.js @@ -328,6 +328,107 @@ describe('', () => { }, ); }); + + describe('source', () => { + it('can be set to a local image', () => { + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render(); + }); + + expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual( + , + ); + }); + + it('can be set to a remote image', () => { + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render( + , + ); + }); + + expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual( + , + ); + }); + + it('can be set to a list of remote images', () => { + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render( + , + ); + }); + + expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual( + , + ); + }); + }); }); describe('ref', () => { diff --git a/packages/react-native/ReactCommon/react/renderer/components/image/ImageProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/image/ImageProps.cpp index dca045f67ce7..59a55d904f8b 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/image/ImageProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/image/ImageProps.cpp @@ -292,7 +292,7 @@ SharedDebugStringConvertibleList ImageProps::getDebugProps() const { sourcesList = sources[0].getDebugProps("source"); } else if (sources.size() > 1) { for (const auto& source : sources) { - std::string sourceName = "source@" + react::toString(source.scale) + "x"; + std::string sourceName = "source-" + react::toString(source.scale) + "x"; auto debugProps = source.getDebugProps(sourceName); sourcesList.insert( sourcesList.end(), debugProps.begin(), debugProps.end()); From da7cf0c3050fbc00812463b90503c5072142903a Mon Sep 17 00:00:00 2001 From: Andrew Datsenko Date: Wed, 6 Aug 2025 07:01:37 -0700 Subject: [PATCH 6/7] Add tests for src and srcSet (#53062) Summary: Changelog: [Internal] As title Reviewed By: rshest Differential Revision: D79642287 --- .../Libraries/Image/__tests__/Image-itest.js | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/packages/react-native/Libraries/Image/__tests__/Image-itest.js b/packages/react-native/Libraries/Image/__tests__/Image-itest.js index b95736aebbee..73548d1733c0 100644 --- a/packages/react-native/Libraries/Image/__tests__/Image-itest.js +++ b/packages/react-native/Libraries/Image/__tests__/Image-itest.js @@ -429,6 +429,127 @@ describe('', () => { ); }); }); + + describe('src', () => { + it('can be set to a remote image', () => { + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render( + , + ); + }); + + expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual( + , + ); + }); + + it('takes precedence over `source` prop', () => { + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render( + , + ); + }); + + expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual( + , + ); + }); + }); + + describe('srcSet', () => { + it('can be set to a list of remote images', () => { + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render( + , + ); + }); + + expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual( + , + ); + }); + + it('defaults to `1x` descriptor', () => { + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render( + , + ); + }); + + expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual( + , + ); + }); + + it('uses `src` for `1x` descriptor when provided', () => { + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render( + , + ); + }); + + expect(root.getRenderedOutput({props: ['source']}).toJSX()).toEqual( + , + ); + }); + }); }); describe('ref', () => { From caf310ea053734df0ca7f7560d7d7609d71dd97e Mon Sep 17 00:00:00 2001 From: Andrew Datsenko Date: Wed, 6 Aug 2025 07:01:37 -0700 Subject: [PATCH 7/7] Add test for width, style, testID and tintColor (#53088) Summary: Changelog: [Internal] As title Reviewed By: rshest Differential Revision: D79642765 --- .../Libraries/Image/__tests__/Image-itest.js | 73 +++++++++++++++++++ .../renderer/components/image/ImageProps.cpp | 2 + 2 files changed, 75 insertions(+) diff --git a/packages/react-native/Libraries/Image/__tests__/Image-itest.js b/packages/react-native/Libraries/Image/__tests__/Image-itest.js index 73548d1733c0..1b530f4cb35d 100644 --- a/packages/react-native/Libraries/Image/__tests__/Image-itest.js +++ b/packages/react-native/Libraries/Image/__tests__/Image-itest.js @@ -208,6 +208,20 @@ describe('', () => { }); }); + describe('width', () => { + it('provides width for image', () => { + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render(); + }); + + expect(root.getRenderedOutput({props: ['width']}).toJSX()).toEqual( + , + ); + }); + }); + describe('loading progress', () => { ( [ @@ -550,6 +564,65 @@ describe('', () => { ); }); }); + + describe('style', () => { + it('can be set', () => { + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render( + , + ); + }); + + expect(root.getRenderedOutput().toJSX()).toEqual( + , + ); + }); + }); + + describe('testID', () => { + it('can be set', () => { + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render(); + }); + + expect(root.getRenderedOutput({props: ['testID']}).toJSX()).toEqual( + , + ); + }); + }); + + describe('tintColor', () => { + it('can be set', () => { + const root = Fantom.createRoot(); + + Fantom.runTask(() => { + root.render(); + }); + + expect(root.getRenderedOutput({props: ['tintColor']}).toJSX()).toEqual( + , + ); + }); + }); }); describe('ref', () => { diff --git a/packages/react-native/ReactCommon/react/renderer/components/image/ImageProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/image/ImageProps.cpp index 59a55d904f8b..9fd8dbdaf5af 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/image/ImageProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/image/ImageProps.cpp @@ -308,6 +308,8 @@ SharedDebugStringConvertibleList ImageProps::getDebugProps() const { "resizeMode", toString(resizeMode), toString(imageProps.resizeMode)), + debugStringConvertibleItem( + "tintColor", toString(tintColor), toString(imageProps.tintColor)), }; }