11import {
22 AppNotInstalledError ,
33 DeviceNotFoundError ,
4+ ElementReference ,
45 HarnessPlatformRunner ,
56} from '@react-native-harness/platforms' ;
67import {
@@ -10,7 +11,8 @@ import {
1011} from './config.js' ;
1112import * as simctl from './xcrun/simctl.js' ;
1213import * as devicectl from './xcrun/devicectl.js' ;
13- import { getDeviceName } from './utils.js' ;
14+ import * as idb from './idb.js' ;
15+ import { getDeviceName , parseUiHierarchy , getElementByPath , getUiHierarchy , findElementsByTestId } from './utils.js' ;
1416import { tmpdir } from 'node:os' ;
1517import { join } from 'node:path' ;
1618import { randomUUID } from 'node:crypto' ;
@@ -44,20 +46,11 @@ export const getAppleSimulatorPlatformInstance = async (
4446 throw new Error ( 'Simulator is not booted' ) ;
4547 }
4648
47- const isAvailable = await simctl . isAppInstalled ( udid , config . bundleId ) ;
48-
49- if ( ! isAvailable ) {
50- throw new AppNotInstalledError (
51- config . bundleId ,
52- getDeviceName ( config . device )
53- ) ;
54- }
55-
5649 return {
5750 startApp : async ( ) => {
5851 await simctl . startApp ( udid , config . bundleId ) ;
5952 } ,
60- restartApp : async ( ) => {
53+ restartApp : async ( ) => {
6154 await simctl . stopApp ( udid , config . bundleId ) ;
6255 await simctl . startApp ( udid , config . bundleId ) ;
6356 } ,
@@ -69,31 +62,50 @@ export const getAppleSimulatorPlatformInstance = async (
6962 } ,
7063 queries : {
7164 getUiHierarchy : async ( ) => {
72- throw new Error ( 'Not implemented yet' ) ;
65+ return await getUiHierarchy ( udid ) ;
7366 } ,
74- findByTestId : async ( ) => {
75- throw new Error ( 'Not implemented yet' ) ;
67+ findByTestId : async ( testId : string ) => {
68+ const hierarchy = await getUiHierarchy ( udid ) ;
69+ const matches = findElementsByTestId ( hierarchy , testId ) ;
70+ if ( matches . length === 0 ) {
71+ throw new Error ( `Element with testID "${ testId } " not found` ) ;
72+ }
73+ return { id : matches [ 0 ] . path . join ( '.' ) } ;
7674 } ,
77- findAllByTestId : async ( ) => {
78- throw new Error ( 'Not implemented yet' ) ;
75+ findAllByTestId : async ( testId : string ) => {
76+ const hierarchy = await getUiHierarchy ( udid ) ;
77+ const matches = findElementsByTestId ( hierarchy , testId ) ;
78+ return matches . map ( ( match ) => ( { id : match . path . join ( '.' ) } ) ) ;
7979 } ,
8080 } ,
8181 actions : {
82- tap : async ( ) => {
83- throw new Error ( 'Not implemented yet' ) ;
82+ tap : async ( x : number , y : number ) => {
83+ await idb . tap ( udid , x , y ) ;
8484 } ,
85- inputText : async ( ) => {
86- throw new Error ( 'Not implemented yet' ) ;
85+ inputText : async ( text : string ) => {
86+ await idb . inputText ( udid , text ) ;
8787 } ,
88- tapElement : async ( ) => {
89- throw new Error ( 'Not implemented yet' ) ;
88+ tapElement : async ( element : ElementReference ) => {
89+ const hierarchy = await getUiHierarchy ( udid ) ;
90+ const uiElement = getElementByPath ( hierarchy , element . id ) ;
91+
92+ if ( ! uiElement ) {
93+ throw new Error (
94+ `Element with identifier "${ element . id } " not found in UI hierarchy.`
95+ ) ;
96+ }
97+
98+ const centerX = uiElement . rect . x + uiElement . rect . width / 2 ;
99+ const centerY = uiElement . rect . y + uiElement . rect . height / 2 ;
100+
101+ await idb . tap ( udid , centerX , centerY ) ;
90102 } ,
91103 screenshot : async ( ) => {
92104 const tempPath = join (
93105 tmpdir ( ) ,
94106 `harness-screenshot-${ randomUUID ( ) } .png`
95107 ) ;
96- await simctl . screenshot ( udid , tempPath ) ;
108+ await idb . screenshot ( udid , tempPath ) ;
97109 return { path : tempPath } ;
98110 } ,
99111 } ,
@@ -120,6 +132,11 @@ export const getApplePhysicalDevicePlatformInstance = async (
120132 ) ;
121133 }
122134
135+ const getUiHierarchy = async ( ) => {
136+ const json = await idb . getUiHierarchy ( deviceId ) ;
137+ return parseUiHierarchy ( json ) ;
138+ } ;
139+
123140 return {
124141 startApp : async ( ) => {
125142 await devicectl . startApp ( deviceId , config . bundleId ) ;
@@ -135,28 +152,50 @@ export const getApplePhysicalDevicePlatformInstance = async (
135152 await devicectl . stopApp ( deviceId , config . bundleId ) ;
136153 } ,
137154 queries : {
138- getUiHierarchy : async ( ) => {
139- throw new Error ( 'Not implemented yet' ) ;
155+ getUiHierarchy,
156+ findByTestId : async ( testId : string ) => {
157+ const hierarchy = await getUiHierarchy ( ) ;
158+ const matches = findElementsByTestId ( hierarchy , testId ) ;
159+ if ( matches . length === 0 ) {
160+ throw new Error ( `Element with testID "${ testId } " not found` ) ;
161+ }
162+ return { id : matches [ 0 ] . path . join ( '.' ) } ;
140163 } ,
141- findByTestId : async ( ) => {
142- throw new Error ( 'Not implemented yet' ) ;
143- } ,
144- findAllByTestId : async ( ) => {
145- throw new Error ( 'Not implemented yet' ) ;
164+ findAllByTestId : async ( testId : string ) => {
165+ const hierarchy = await getUiHierarchy ( ) ;
166+ const matches = findElementsByTestId ( hierarchy , testId ) ;
167+ return matches . map ( ( match ) => ( { id : match . path . join ( '.' ) } ) ) ;
146168 } ,
147169 } ,
148170 actions : {
149- tap : async ( ) => {
150- throw new Error ( 'Not implemented yet' ) ;
171+ tap : async ( x : number , y : number ) => {
172+ await idb . tap ( deviceId , x , y ) ;
151173 } ,
152- inputText : async ( ) => {
153- throw new Error ( 'Not implemented yet' ) ;
174+ inputText : async ( text : string ) => {
175+ await idb . inputText ( deviceId , text ) ;
154176 } ,
155- tapElement : async ( ) => {
156- throw new Error ( 'Not implemented yet' ) ;
177+ tapElement : async ( element : ElementReference ) => {
178+ const hierarchy = await getUiHierarchy ( ) ;
179+ const uiElement = getElementByPath ( hierarchy , element . id ) ;
180+
181+ if ( ! uiElement ) {
182+ throw new Error (
183+ `Element with identifier "${ element . id } " not found in UI hierarchy.`
184+ ) ;
185+ }
186+
187+ const centerX = uiElement . rect . x + uiElement . rect . width / 2 ;
188+ const centerY = uiElement . rect . y + uiElement . rect . height / 2 ;
189+
190+ await idb . tap ( deviceId , centerX , centerY ) ;
157191 } ,
158192 screenshot : async ( ) => {
159- throw new Error ( 'Screenshot is not supported on physical iOS devices' ) ;
193+ const tempPath = join (
194+ tmpdir ( ) ,
195+ `harness-screenshot-${ randomUUID ( ) } .png`
196+ ) ;
197+ await idb . screenshot ( deviceId , tempPath ) ;
198+ return { path : tempPath } ;
160199 } ,
161200 } ,
162201 } ;
0 commit comments