From 8fc7ccdb6f7abbc5d6ae3389e3f722efefebcfc0 Mon Sep 17 00:00:00 2001 From: Peter Abbondanzo Date: Wed, 13 May 2026 22:05:26 -0700 Subject: [PATCH] Add Image.getSize device coverage (#56746) Summary: Add an RNTester PlatformTest case that calls Image.getSize against a small PNG, a large JPEG, and an EXIF-rotated JPEG, then verifies the dimensions reported by native image metadata. Extend the existing Image Maestro flow to open the new test and wait for the pass result, so the same flow can run against Android and iOS RNTester builds. Changelog: [Internal][Added] - Add RNTester device coverage for Image.getSize dimensions Reviewed By: christophpurrer Differential Revision: D104535939 --- packages/rn-tester/.maestro/image.yml | 29 +++-- ...RNTesterPlatformTestMinimizedResultView.js | 9 +- .../RNTesterPlatformTestResultView.js | 4 +- .../RNTesterPlatformTestResultsText.js | 39 +++---- .../js/examples/Image/ImageExample.js | 109 +++++++++++++++++- 5 files changed, 156 insertions(+), 34 deletions(-) diff --git a/packages/rn-tester/.maestro/image.yml b/packages/rn-tester/.maestro/image.yml index f963352f4b51..c344b84ab7eb 100644 --- a/packages/rn-tester/.maestro/image.yml +++ b/packages/rn-tester/.maestro/image.yml @@ -1,12 +1,27 @@ appId: ${APP_ID} # iOS: com.meta.RNTester.localDevelopment | Android: com.facebook.react.uiapp --- -- launchApp -- assertVisible: "Components" -- scrollUntilVisible: - element: - id: "Image" - direction: DOWN - speed: 40 +- runFlow: ./helpers/launch-app-and-search.yml +- inputText: + text: "Image" +- assertVisible: + id: "Image" - tapOn: id: "Image" - assertVisible: "Plain Network Image with `source` prop." +- assertVisible: + id: "example_search" +- tapOn: + id: "example_search" +- inputText: + text: "Image.getSize" +- hideKeyboard +- tapOn: + id: "platform-test-results" +- assertVisible: "Results" +- extendedWaitUntil: + visible: "Image.getSize resolves source dimensions" + timeout: 30000 +- assertVisible: + id: "platform-test-pass-count-1" +- assertVisible: + id: "platform-test-fail-count-0" diff --git a/packages/rn-tester/js/examples/Experimental/PlatformTest/RNTesterPlatformTestMinimizedResultView.js b/packages/rn-tester/js/examples/Experimental/PlatformTest/RNTesterPlatformTestMinimizedResultView.js index e09b24d33e8b..d4cf0f50e115 100644 --- a/packages/rn-tester/js/examples/Experimental/PlatformTest/RNTesterPlatformTestMinimizedResultView.js +++ b/packages/rn-tester/js/examples/Experimental/PlatformTest/RNTesterPlatformTestMinimizedResultView.js @@ -33,9 +33,12 @@ export default function RNTesterPlatformTestMinimizedResultView({ style, }: Props): React.MixedElement { return ( - + - + - + diff --git a/packages/rn-tester/js/examples/Experimental/PlatformTest/RNTesterPlatformTestResultView.js b/packages/rn-tester/js/examples/Experimental/PlatformTest/RNTesterPlatformTestResultView.js index 0b814621eb69..b71ce8beb50e 100644 --- a/packages/rn-tester/js/examples/Experimental/PlatformTest/RNTesterPlatformTestResultView.js +++ b/packages/rn-tester/js/examples/Experimental/PlatformTest/RNTesterPlatformTestResultView.js @@ -275,7 +275,7 @@ export default function RNTesterPlatformTestResultView( Results {filteredNotice} - + - + - + {numPass} Pass - {' '} - + {numFail} Fail {numSkipped > 0 ? ( - <> - {' '} - - {numSkipped} Skipped - - + + {numSkipped} Skipped + ) : null} {numError > 0 ? ( - <> - {' '} - - {numError} Error - - + + {numError} Error + ) : null} {numPending > 0 ? ( - <> - {' '} - - {numPending} Pending - - + + {numPending} Pending + ) : null} ); @@ -75,4 +69,7 @@ const styles = StyleSheet.create({ skippedText: { color: 'blue', }, + statText: { + marginEnd: 8, + }, }); diff --git a/packages/rn-tester/js/examples/Image/ImageExample.js b/packages/rn-tester/js/examples/Image/ImageExample.js index 1147e6bf7d1c..a628ed492c22 100644 --- a/packages/rn-tester/js/examples/Image/ImageExample.js +++ b/packages/rn-tester/js/examples/Image/ImageExample.js @@ -11,10 +11,12 @@ 'use strict'; import type {RNTesterModuleExample} from '../../types/RNTesterTypes'; +import type {PlatformTestComponentBaseProps} from '../Experimental/PlatformTest/RNTesterPlatformTestTypes'; import type {ImageProps, LayoutChangeEvent} from 'react-native'; import RNTesterButton from '../../components/RNTesterButton'; import RNTesterText from '../../components/RNTesterText'; +import RNTesterPlatformTest from '../Experimental/PlatformTest/RNTesterPlatformTest'; import ImageCapInsetsExample from './ImageCapInsetsExample'; import * as React from 'react'; import {useEffect, useState} from 'react'; @@ -567,7 +569,7 @@ class OnPartialLoadExample extends React.Component< = [ + { + expectedHeight: 492, + expectedWidth: 960, + uri: IMAGE1, + name: 'large PNG', + }, + { + expectedHeight: 3000, + expectedWidth: 4500, + uri: 'https://www.facebook.com/assets/react_native_oss_tests/large-image@1x.jpg', + name: 'large JPEG with density 2', + }, + { + expectedHeight: 1200, + expectedWidth: 1800, + // Rotated 90 degrees counter-clockwise + uri: 'https://www.facebook.com/assets/react_native_oss_tests/exif-6@1x.jpg', + name: 'EXIF rotated JPEG', + }, +]; + +function getImageSize(uri: string): Promise<{height: number, width: number}> { + return new Promise((resolve, reject) => { + Image.getSize(uri, (width, height) => resolve({height, width}), reject); + }); +} + +function ImageGetSizePlatformTest(props: PlatformTestComponentBaseProps) { + const {harness} = props; + const asyncTest = harness.useAsyncTest( + 'Image.getSize resolves source dimensions', + 30000, + ); + + useEffect(() => { + let cancelled = false; + + Promise.all( + GET_SIZE_TEST_IMAGES.map(image => + getImageSize(image.uri).then(size => ({image, size})), + ), + ) + .then(results => { + if (cancelled) { + return; + } + + for (const result of results) { + asyncTest.step(({assert_equals}) => { + assert_equals( + result.size.width, + result.image.expectedWidth, + `${result.image.name} width`, + ); + assert_equals( + result.size.height, + result.image.expectedHeight, + `${result.image.name} height`, + ); + }); + } + }) + .catch((error: unknown) => { + if (!cancelled) { + asyncTest.step(({assert_true}) => { + assert_true(false, `Image.getSize failed: ${String(error)}`); + }); + } + }) + .finally(() => { + if (!cancelled) { + asyncTest.done(); + } + }); + + return () => { + cancelled = true; + }; + }, [asyncTest]); + + return ( + + Calling Image.getSize for {GET_SIZE_TEST_IMAGES.length} remote images. + + ); +} + const styles = StyleSheet.create({ base: { width: 64, @@ -1564,6 +1659,18 @@ exports.examples = [ return ; }, }, + { + title: 'Image.getSize', + render: function (): React.Node { + return ( + + ); + }, + }, { title: 'MultipleSourcesExample', description: