@devicelab/maestro-runner-darwin-x64 1.1.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (562) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +166 -0
  3. package/THIRD_PARTY_LICENSES +186 -0
  4. package/bin/maestro-runner +0 -0
  5. package/drivers/android/appium-uiautomator2-server-debug-androidTest.apk +0 -0
  6. package/drivers/android/appium-uiautomator2-server-v9.11.1.apk +0 -0
  7. package/drivers/android/devicelab-android-driver-test.apk +0 -0
  8. package/drivers/android/devicelab-android-driver.apk +0 -0
  9. package/drivers/android/settings_apk-debug.apk +0 -0
  10. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunner/App.swift +11 -0
  11. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunner.xcodeproj/project.pbxproj +523 -0
  12. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  13. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/ATTRIBUTION.md +73 -0
  14. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/Bridging-Header.h +4 -0
  15. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/PixelDiff.swift +72 -0
  16. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/PrivateHeaders/XCTest/XCPointerEventPath.h +43 -0
  17. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/PrivateHeaders/XCTest/XCSynthesizedEventRecord.h +32 -0
  18. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/PrivateHeaders/XCTest/XCUIApplication+ActiveApp.h +23 -0
  19. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/PrivateHeaders/XCTest/XCUIDevice.h +30 -0
  20. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerObjCExceptionCatcher.h +11 -0
  21. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerObjCExceptionCatcher.m +16 -0
  22. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+Alert.swift +155 -0
  23. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+CommandExecution.swift +961 -0
  24. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+Environment.swift +37 -0
  25. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+Interaction.swift +1864 -0
  26. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+Lifecycle.swift +351 -0
  27. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+Media.swift +81 -0
  28. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+Models.swift +258 -0
  29. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+ScreenRecorder.swift +279 -0
  30. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+Snapshot.swift +703 -0
  31. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+SystemModal.swift +230 -0
  32. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+Transport.swift +137 -0
  33. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests+TvRemote.swift +185 -0
  34. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/RunnerTests.swift +197 -0
  35. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/SnapshotParams.h +28 -0
  36. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/SnapshotParams.m +49 -0
  37. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/SyntheticTyping.h +17 -0
  38. package/drivers/ios/DevicelabIOSRunner/DevicelabIOSRunnerUITests/SyntheticTyping.m +86 -0
  39. package/drivers/ios/DevicelabIOSRunner/PROTOCOL.md +258 -0
  40. package/drivers/ios/DevicelabIOSRunner/README.md +38 -0
  41. package/drivers/ios/DevicelabIOSRunner/project.yml +49 -0
  42. package/drivers/ios/WebDriverAgent/.github/ISSUE_TEMPLATE.md +4 -0
  43. package/drivers/ios/WebDriverAgent/.github/actions/xcode-setup/action.yml +32 -0
  44. package/drivers/ios/WebDriverAgent/.github/dependabot.yml +22 -0
  45. package/drivers/ios/WebDriverAgent/.github/workflows/functional-test.yml +62 -0
  46. package/drivers/ios/WebDriverAgent/.github/workflows/pr-title.yml +10 -0
  47. package/drivers/ios/WebDriverAgent/.github/workflows/publish.js.yml +105 -0
  48. package/drivers/ios/WebDriverAgent/.github/workflows/unit-test.yml +30 -0
  49. package/drivers/ios/WebDriverAgent/.github/workflows/wda-package.yml +107 -0
  50. package/drivers/ios/WebDriverAgent/.github/workflows/wda-tests.yml +221 -0
  51. package/drivers/ios/WebDriverAgent/.mocharc.js +4 -0
  52. package/drivers/ios/WebDriverAgent/.releaserc +45 -0
  53. package/drivers/ios/WebDriverAgent/CHANGELOG.md +1547 -0
  54. package/drivers/ios/WebDriverAgent/CONTRIBUTING.md +35 -0
  55. package/drivers/ios/WebDriverAgent/Configurations/IOSSettings.xcconfig +31 -0
  56. package/drivers/ios/WebDriverAgent/Configurations/IOSTestSettings.xcconfig +1 -0
  57. package/drivers/ios/WebDriverAgent/Configurations/TVOSSettings.xcconfig +31 -0
  58. package/drivers/ios/WebDriverAgent/Configurations/TVOSTestSettings.xcconfig +1 -0
  59. package/drivers/ios/WebDriverAgent/Fastlane/Fastfile +13 -0
  60. package/drivers/ios/WebDriverAgent/Gemfile +3 -0
  61. package/drivers/ios/WebDriverAgent/LICENSE +30 -0
  62. package/drivers/ios/WebDriverAgent/PrivateHeaders/AccessibilityUtilities/AXSettings.h +15 -0
  63. package/drivers/ios/WebDriverAgent/PrivateHeaders/MobileCoreServices/LSApplicationWorkspace.h +127 -0
  64. package/drivers/ios/WebDriverAgent/PrivateHeaders/TextInput/TIPreferencesController.h +45 -0
  65. package/drivers/ios/WebDriverAgent/PrivateHeaders/UIKitCore/UIKeyboardImpl.h +27 -0
  66. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/CDStructures.h +28 -0
  67. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/NSString-XCTAdditions.h +11 -0
  68. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/NSValue-XCTestAdditions.h +11 -0
  69. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/UIGestureRecognizer-RecordingAdditions.h +11 -0
  70. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/UILongPressGestureRecognizer-RecordingAdditions.h +11 -0
  71. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/UIPanGestureRecognizer-RecordingAdditions.h +11 -0
  72. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/UIPinchGestureRecognizer-RecordingAdditions.h +11 -0
  73. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/UISwipeGestureRecognizer-RecordingAdditions.h +11 -0
  74. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/UITapGestureRecognizer-RecordingAdditions.h +11 -0
  75. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCAXClient_iOS.h +55 -0
  76. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCActivityRecord.h +39 -0
  77. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCApplicationMonitor.h +44 -0
  78. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCApplicationMonitor_iOS.h +18 -0
  79. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCApplicationQuery.h +23 -0
  80. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCDebugLogDelegate-Protocol.h +11 -0
  81. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCDeviceEvent.h +27 -0
  82. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCEventGenerator.h +73 -0
  83. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCKeyMappingPath.h +36 -0
  84. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCKeyboardInputSolver.h +41 -0
  85. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCKeyboardKeyMap.h +101 -0
  86. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCKeyboardLayout.h +35 -0
  87. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCPointerEvent.h +30 -0
  88. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCPointerEventPath.h +43 -0
  89. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCSourceCodeRecording.h +42 -0
  90. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCSourceCodeTreeNode.h +84 -0
  91. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCSourceCodeTreeNodeEnumerator.h +18 -0
  92. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCSymbolicationRecord.h +29 -0
  93. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCSymbolicatorHolder.h +14 -0
  94. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCSynthesizedEventRecord.h +32 -0
  95. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTAXClient-Protocol.h +14 -0
  96. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTAsyncActivity-Protocol.h +16 -0
  97. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTAsyncActivity.h +23 -0
  98. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTAutomationTarget-Protocol.h +12 -0
  99. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTDarwinNotificationExpectation.h +23 -0
  100. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTElementSetTransformer-Protocol.h +18 -0
  101. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTKVOExpectation.h +28 -0
  102. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTMetric.h +29 -0
  103. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTNSNotificationExpectation.h +27 -0
  104. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTNSPredicateExpectation.h +24 -0
  105. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTNSPredicateExpectationObject-Protocol.h +15 -0
  106. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTRunnerAutomationSession.h +21 -0
  107. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTRunnerDaemonSession.h +98 -0
  108. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTRunnerIDESession.h +65 -0
  109. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTTestRunSession.h +25 -0
  110. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTTestRunSessionDelegate-Protocol.h +17 -0
  111. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTUIApplicationMonitor-Protocol.h +17 -0
  112. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTWaiter.h +55 -0
  113. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTWaiterDelegate-Protocol.h +16 -0
  114. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTWaiterDelegatePrivate-Protocol.h +12 -0
  115. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTWaiterManagement-Protocol.h +13 -0
  116. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTWaiterManager.h +28 -0
  117. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTest.h +34 -0
  118. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestCase.h +102 -0
  119. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestCaseRun.h +18 -0
  120. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestCaseSuite.h +19 -0
  121. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestConfiguration.h +66 -0
  122. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestContext.h +26 -0
  123. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestContextScope.h +19 -0
  124. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestDriver.h +38 -0
  125. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestDriverInterface-Protocol.h +14 -0
  126. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestExpectation.h +39 -0
  127. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestExpectationDelegate-Protocol.h +14 -0
  128. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestExpectationWaiter.h +17 -0
  129. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestLog.h +32 -0
  130. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestManager_IDEInterface-Protocol.h +36 -0
  131. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestManager_ManagerInterface-Protocol.h +54 -0
  132. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestManager_TestsInterface-Protocol.h +12 -0
  133. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestMisuseObserver.h +37 -0
  134. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestObservation-Protocol.h +20 -0
  135. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestObservationCenter.h +36 -0
  136. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestObserver.h +20 -0
  137. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestProbe.h +13 -0
  138. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestRun.h +39 -0
  139. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestSuite.h +48 -0
  140. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestSuiteRun.h +28 -0
  141. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCTestWaiter.h +14 -0
  142. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCUIApplication.h +60 -0
  143. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCUIApplicationImpl.h +38 -0
  144. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCUIApplicationProcess.h +85 -0
  145. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCUICoordinate.h +44 -0
  146. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCUIDevice.h +30 -0
  147. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCUIElement.h +74 -0
  148. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCUIElementAsynchronousHandlerWrapper.h +19 -0
  149. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCUIElementHitPointCoordinate.h +24 -0
  150. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCUIElementQuery.h +70 -0
  151. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCUIHitPointResult.h +20 -0
  152. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCUIRecorderNodeFinder.h +65 -0
  153. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCUIRecorderNodeFinderMatch.h +25 -0
  154. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCUIRecorderTimingMessage.h +20 -0
  155. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCUIRecorderUtilities.h +45 -0
  156. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCUIScreen.h +27 -0
  157. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/XCUIScreenDataSource-Protocol.h +19 -0
  158. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/_XCInternalTestRun.h +43 -0
  159. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/_XCKVOExpectationImplementation.h +32 -0
  160. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/_XCTDarwinNotificationExpectationImplementation.h +27 -0
  161. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/_XCTNSNotificationExpectationImplementation.h +30 -0
  162. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/_XCTNSPredicateExpectationImplementation.h +29 -0
  163. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/_XCTWaiterImpl.h +41 -0
  164. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/_XCTestCaseImplementation.h +70 -0
  165. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/_XCTestCaseInterruptionException.h +13 -0
  166. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/_XCTestExpectationImplementation.h +40 -0
  167. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/_XCTestImplementation.h +14 -0
  168. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/_XCTestObservationCenterImplementation.h +19 -0
  169. package/drivers/ios/WebDriverAgent/PrivateHeaders/XCTest/_XCTestSuiteImplementation.h +23 -0
  170. package/drivers/ios/WebDriverAgent/README.md +56 -0
  171. package/drivers/ios/WebDriverAgent/Scripts/build-webdriveragent.mjs +85 -0
  172. package/drivers/ios/WebDriverAgent/Scripts/build.sh +124 -0
  173. package/drivers/ios/WebDriverAgent/Scripts/ci/build-real.sh +24 -0
  174. package/drivers/ios/WebDriverAgent/Scripts/ci/build-sim.sh +15 -0
  175. package/drivers/ios/WebDriverAgent/Scripts/fetch-prebuilt-wda.mjs +69 -0
  176. package/drivers/ios/WebDriverAgent/Scripts/update-wda-version.mjs +42 -0
  177. package/drivers/ios/WebDriverAgent/WebDriverAgent.xcodeproj/project.pbxproj +4547 -0
  178. package/drivers/ios/WebDriverAgent/WebDriverAgent.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  179. package/drivers/ios/WebDriverAgent/WebDriverAgent.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  180. package/drivers/ios/WebDriverAgent/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/IntegrationApp.xcscheme +87 -0
  181. package/drivers/ios/WebDriverAgent/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/IntegrationTests_1.xcscheme +86 -0
  182. package/drivers/ios/WebDriverAgent/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/IntegrationTests_2.xcscheme +86 -0
  183. package/drivers/ios/WebDriverAgent/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/IntegrationTests_3.xcscheme +86 -0
  184. package/drivers/ios/WebDriverAgent/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/WebDriverAgentLib.xcscheme +125 -0
  185. package/drivers/ios/WebDriverAgent/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/WebDriverAgentLib_tvOS.xcscheme +95 -0
  186. package/drivers/ios/WebDriverAgent/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/WebDriverAgentRunner-nodebug.xcscheme +87 -0
  187. package/drivers/ios/WebDriverAgent/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/WebDriverAgentRunner.xcscheme +114 -0
  188. package/drivers/ios/WebDriverAgent/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/WebDriverAgentRunner_tvOS.xcscheme +123 -0
  189. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.h +100 -0
  190. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/FBXCElementSnapshotWrapper+Helpers.m +192 -0
  191. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/NSDictionary+FBUtf8SafeDictionary.h +39 -0
  192. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/NSDictionary+FBUtf8SafeDictionary.m +87 -0
  193. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/NSExpression+FBFormat.h +29 -0
  194. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/NSExpression+FBFormat.m +34 -0
  195. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/NSString+FBVisualLength.h +18 -0
  196. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/NSString+FBVisualLength.m +18 -0
  197. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/NSString+FBXMLSafeString.h +27 -0
  198. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/NSString+FBXMLSafeString.m +30 -0
  199. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCAXClient_iOS+FBSnapshotReqParams.h +25 -0
  200. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCAXClient_iOS+FBSnapshotReqParams.m +82 -0
  201. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCTIssue+FBPatcher.h +17 -0
  202. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCTIssue+FBPatcher.m +35 -0
  203. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBAlert.h +23 -0
  204. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBAlert.m +109 -0
  205. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBFocused.h +23 -0
  206. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.h +171 -0
  207. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +645 -0
  208. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBQuiescence.h +24 -0
  209. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBQuiescence.m +28 -0
  210. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBTouchAction.h +29 -0
  211. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBTouchAction.m +75 -0
  212. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBUIInterruptions.h +22 -0
  213. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplication+FBUIInterruptions.m +31 -0
  214. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplicationProcess+FBQuiescence.h +27 -0
  215. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIApplicationProcess+FBQuiescence.m +118 -0
  216. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHealthCheck.h +30 -0
  217. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHealthCheck.m +47 -0
  218. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.h +193 -0
  219. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBHelpers.m +388 -0
  220. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBRotation.h +38 -0
  221. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIDevice+FBRotation.m +68 -0
  222. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBAccessibility.h +29 -0
  223. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBAccessibility.m +50 -0
  224. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBCaching.h +19 -0
  225. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBCaching.m +36 -0
  226. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBClassChain.h +57 -0
  227. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBClassChain.m +98 -0
  228. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBCustomActions.h +27 -0
  229. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBCustomActions.m +127 -0
  230. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBFind.h +75 -0
  231. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBFind.m +133 -0
  232. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBForceTouch.h +38 -0
  233. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBForceTouch.m +52 -0
  234. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBIsVisible.h +28 -0
  235. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBIsVisible.m +78 -0
  236. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBMinMax.h +33 -0
  237. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBMinMax.m +75 -0
  238. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBPickerWheel.h +45 -0
  239. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBPickerWheel.m +58 -0
  240. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBResolve.h +37 -0
  241. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBResolve.m +51 -0
  242. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBScrolling.h +98 -0
  243. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBScrolling.m +342 -0
  244. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBSwiping.h +38 -0
  245. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBSwiping.m +56 -0
  246. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBTVFocuse.h +35 -0
  247. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBTVFocuse.m +71 -0
  248. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBTyping.h +64 -0
  249. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBTyping.m +199 -0
  250. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBUID.h +42 -0
  251. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBUID.m +85 -0
  252. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBUtilities.h +106 -0
  253. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBUtilities.m +170 -0
  254. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBVisibleFrame.h +35 -0
  255. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBVisibleFrame.m +52 -0
  256. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBWebDriverAttributes.h +32 -0
  257. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElement+FBWebDriverAttributes.m +294 -0
  258. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElementQuery+FBHelpers.h +28 -0
  259. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Categories/XCUIElementQuery+FBHelpers.m +46 -0
  260. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBAlertViewCommands.h +19 -0
  261. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBAlertViewCommands.m +130 -0
  262. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBCustomCommands.h +19 -0
  263. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBCustomCommands.m +633 -0
  264. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBDebugCommands.h +19 -0
  265. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBDebugCommands.m +81 -0
  266. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBElementCommands.h +19 -0
  267. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBElementCommands.m +705 -0
  268. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBFindElementCommands.h +18 -0
  269. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBFindElementCommands.m +186 -0
  270. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBOrientationCommands.h +19 -0
  271. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBOrientationCommands.m +185 -0
  272. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBScreenshotCommands.h +19 -0
  273. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBScreenshotCommands.m +40 -0
  274. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBSessionCommands.h +19 -0
  275. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBSessionCommands.m +594 -0
  276. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBTouchActionCommands.h +19 -0
  277. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBTouchActionCommands.m +41 -0
  278. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBTouchIDCommands.h +19 -0
  279. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBTouchIDCommands.m +30 -0
  280. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBUnknownCommands.h +19 -0
  281. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBUnknownCommands.m +39 -0
  282. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBVideoCommands.h +19 -0
  283. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Commands/FBVideoCommands.m +84 -0
  284. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/FBAlert.h +91 -0
  285. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/FBAlert.m +274 -0
  286. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Info.plist +26 -0
  287. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBCommandHandler.h +38 -0
  288. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBCommandStatus.h +80 -0
  289. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBCommandStatus.m +279 -0
  290. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBElement.h +95 -0
  291. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBElementCache.h +61 -0
  292. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBElementCache.m +100 -0
  293. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBElementUtils.h +76 -0
  294. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBElementUtils.m +153 -0
  295. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBExceptionHandler.h +29 -0
  296. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBExceptionHandler.m +58 -0
  297. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBExceptions.h +60 -0
  298. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBExceptions.m +24 -0
  299. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBHTTPStatusCodes.h +581 -0
  300. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBResponseJSONPayload.h +29 -0
  301. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBResponseJSONPayload.m +59 -0
  302. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBResponsePayload.h +74 -0
  303. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBResponsePayload.m +148 -0
  304. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBRoute.h +77 -0
  305. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBRoute.m +174 -0
  306. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBRouteRequest-Private.h +20 -0
  307. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBRouteRequest.h +39 -0
  308. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBRouteRequest.m +32 -0
  309. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBScreenRecordingContainer.h +56 -0
  310. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBScreenRecordingContainer.m +72 -0
  311. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBScreenRecordingPromise.h +29 -0
  312. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBScreenRecordingPromise.m +31 -0
  313. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBScreenRecordingRequest.h +39 -0
  314. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBScreenRecordingRequest.m +94 -0
  315. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBSession-Private.h +26 -0
  316. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBSession.h +141 -0
  317. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBSession.m +295 -0
  318. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBTCPSocket.h +65 -0
  319. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBTCPSocket.m +85 -0
  320. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBWebServer.h +52 -0
  321. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBWebServer.m +248 -0
  322. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBXCAccessibilityElement.h +34 -0
  323. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBXCAccessibilityElement.m +17 -0
  324. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBXCDeviceEvent.h +34 -0
  325. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBXCDeviceEvent.m +42 -0
  326. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBXCElementSnapshot.h +82 -0
  327. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBXCElementSnapshot.m +9 -0
  328. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBXCElementSnapshotWrapper.h +29 -0
  329. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Routing/FBXCElementSnapshotWrapper.m +114 -0
  330. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBAccessibilityTraits.h +20 -0
  331. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBAccessibilityTraits.m +61 -0
  332. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBActiveAppDetectionPoint.h +56 -0
  333. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBActiveAppDetectionPoint.m +87 -0
  334. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBAlertsMonitor.h +51 -0
  335. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBAlertsMonitor.m +94 -0
  336. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBBaseActionsSynthesizer.h +131 -0
  337. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBBaseActionsSynthesizer.m +165 -0
  338. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBCapabilities.h +45 -0
  339. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBCapabilities.m +25 -0
  340. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBClassChainQueryParser.h +99 -0
  341. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBClassChainQueryParser.m +666 -0
  342. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBConfiguration.h +403 -0
  343. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBConfiguration.m +711 -0
  344. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBDebugLogDelegateDecorator.h +26 -0
  345. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBDebugLogDelegateDecorator.m +49 -0
  346. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBElementHelpers.h +29 -0
  347. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBElementHelpers.m +21 -0
  348. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBElementTypeTransformer.h +45 -0
  349. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBElementTypeTransformer.m +149 -0
  350. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBErrorBuilder.h +64 -0
  351. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBErrorBuilder.m +75 -0
  352. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBFailureProofTestCase.h +19 -0
  353. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBFailureProofTestCase.m +68 -0
  354. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBImageProcessor.h +55 -0
  355. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBImageProcessor.m +170 -0
  356. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBImageUtils.h +25 -0
  357. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBImageUtils.m +80 -0
  358. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBKeyboard.h +37 -0
  359. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBKeyboard.m +123 -0
  360. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBLogger.h +32 -0
  361. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBLogger.m +47 -0
  362. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMacros.h +57 -0
  363. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMathUtils.h +36 -0
  364. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMathUtils.m +63 -0
  365. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMjpegServer.h +24 -0
  366. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBMjpegServer.m +151 -0
  367. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBNotificationsHelper.h +37 -0
  368. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBNotificationsHelper.m +29 -0
  369. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBPasteboard.h +40 -0
  370. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBPasteboard.m +172 -0
  371. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBProtocolHelpers.h +46 -0
  372. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBProtocolHelpers.m +144 -0
  373. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBReflectionUtils.h +23 -0
  374. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBReflectionUtils.m +31 -0
  375. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBRunLoopSpinner.h +77 -0
  376. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBRunLoopSpinner.m +94 -0
  377. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBRuntimeUtils.h +79 -0
  378. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBRuntimeUtils.m +112 -0
  379. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreen.h +22 -0
  380. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreen.m +21 -0
  381. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreenshot.h +44 -0
  382. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBScreenshot.m +227 -0
  383. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBSettings.h +48 -0
  384. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBSettings.m +42 -0
  385. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBTVNavigationTracker-Private.h +27 -0
  386. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBTVNavigationTracker.h +52 -0
  387. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBTVNavigationTracker.m +142 -0
  388. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBUnattachedAppLauncher.h +26 -0
  389. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBUnattachedAppLauncher.m +21 -0
  390. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBW3CActionsHelpers.h +42 -0
  391. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBW3CActionsHelpers.m +119 -0
  392. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBW3CActionsSynthesizer.h +19 -0
  393. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBW3CActionsSynthesizer.m +885 -0
  394. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBWebServerParams.h +22 -0
  395. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBWebServerParams.m +23 -0
  396. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBXCAXClientProxy.h +49 -0
  397. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBXCAXClientProxy.m +119 -0
  398. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBXCTestDaemonsProxy.h +45 -0
  399. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBXCTestDaemonsProxy.m +359 -0
  400. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBXCodeCompatibility.h +80 -0
  401. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBXCodeCompatibility.m +99 -0
  402. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBXMLGenerationOptions.h +45 -0
  403. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBXMLGenerationOptions.m +25 -0
  404. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBXPath-Private.h +56 -0
  405. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBXPath.h +59 -0
  406. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/FBXPath.m +979 -0
  407. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/LRUCache/LRUCache.h +66 -0
  408. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/LRUCache/LRUCache.m +146 -0
  409. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/LRUCache/LRUCacheNode.h +43 -0
  410. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/LRUCache/LRUCacheNode.m +51 -0
  411. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/NSPredicate+FBFormat.h +42 -0
  412. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/NSPredicate+FBFormat.m +69 -0
  413. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/XCTestPrivateSymbols.h +78 -0
  414. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/XCTestPrivateSymbols.m +107 -0
  415. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/XCUIApplicationProcessDelay.h +37 -0
  416. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Utilities/XCUIApplicationProcessDelay.m +83 -0
  417. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaAsyncSocket/GCDAsyncSocket.h +1220 -0
  418. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaAsyncSocket/GCDAsyncSocket.m +8812 -0
  419. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaAsyncSocket/GCDAsyncUdpSocket.h +1036 -0
  420. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaAsyncSocket/GCDAsyncUdpSocket.m +5868 -0
  421. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaHTTPServer/Categories/DDNumber.h +12 -0
  422. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaHTTPServer/Categories/DDNumber.m +88 -0
  423. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaHTTPServer/Categories/DDRange.h +56 -0
  424. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaHTTPServer/Categories/DDRange.m +106 -0
  425. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPConnection.h +107 -0
  426. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPConnection.m +2238 -0
  427. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPLogging.h +122 -0
  428. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPMessage.h +53 -0
  429. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPMessage.m +357 -0
  430. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPResponse.h +149 -0
  431. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPServer.h +126 -0
  432. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaHTTPServer/HTTPServer.m +372 -0
  433. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaHTTPServer/LICENSE +18 -0
  434. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaHTTPServer/Responses/HTTPDataResponse.h +13 -0
  435. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaHTTPServer/Responses/HTTPDataResponse.m +83 -0
  436. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaHTTPServer/Responses/HTTPErrorResponse.h +9 -0
  437. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/CocoaHTTPServer/Responses/HTTPErrorResponse.m +40 -0
  438. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/RoutingHTTPServer/HTTPResponseProxy.h +13 -0
  439. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/RoutingHTTPServer/HTTPResponseProxy.m +84 -0
  440. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/RoutingHTTPServer/LICENSE +19 -0
  441. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/RoutingHTTPServer/Route.h +18 -0
  442. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/RoutingHTTPServer/Route.m +11 -0
  443. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/RoutingHTTPServer/RouteRequest.h +16 -0
  444. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/RoutingHTTPServer/RouteRequest.m +50 -0
  445. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/RoutingHTTPServer/RouteResponse.h +20 -0
  446. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/RoutingHTTPServer/RouteResponse.m +66 -0
  447. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/RoutingHTTPServer/RoutingConnection.h +5 -0
  448. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/RoutingHTTPServer/RoutingConnection.m +142 -0
  449. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/RoutingHTTPServer/RoutingHTTPServer.h +55 -0
  450. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/Vendor/RoutingHTTPServer/RoutingHTTPServer.m +303 -0
  451. package/drivers/ios/WebDriverAgent/WebDriverAgentLib/WebDriverAgentLib.h +59 -0
  452. package/drivers/ios/WebDriverAgent/WebDriverAgentRunner/Info.plist +30 -0
  453. package/drivers/ios/WebDriverAgent/WebDriverAgentRunner/UITestingUITests.m +58 -0
  454. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/AppDelegate.h +14 -0
  455. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/AppDelegate.m +38 -0
  456. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/FBAlertViewController.h +14 -0
  457. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/FBAlertViewController.m +82 -0
  458. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/FBNavigationController.h +12 -0
  459. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/FBNavigationController.m +25 -0
  460. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/FBScrollViewController.h +13 -0
  461. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/FBScrollViewController.m +39 -0
  462. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/FBTableDataSource.h +16 -0
  463. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/FBTableDataSource.m +35 -0
  464. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/SceneDelegate.h +20 -0
  465. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/SceneDelegate.m +48 -0
  466. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/TouchSpotView.h +17 -0
  467. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/TouchSpotView.m +28 -0
  468. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/TouchViewController.h +23 -0
  469. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/TouchViewController.m +29 -0
  470. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/TouchableView.h +29 -0
  471. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/TouchableView.m +108 -0
  472. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/ViewController.h +12 -0
  473. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Classes/ViewController.m +88 -0
  474. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Info.plist +71 -0
  475. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/Resources/Base.lproj/Main.storyboard +620 -0
  476. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationApp/main.m +15 -0
  477. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBAlertTests.m +194 -0
  478. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBAutoAlertsHandlerTests.m +73 -0
  479. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBConfigurationTests.m +38 -0
  480. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBElementAttributeTests.m +264 -0
  481. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBElementSwipingTests.m +127 -0
  482. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBElementVisibilityTests.m +73 -0
  483. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBFailureProofTestCaseTests.m +41 -0
  484. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBForceTouchTests.m +85 -0
  485. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBImageProcessorTests.m +82 -0
  486. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBIntegrationTestCase.h +76 -0
  487. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBIntegrationTestCase.m +140 -0
  488. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBKeyboardTests.m +61 -0
  489. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBPasteboardTests.m +91 -0
  490. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBPickerWheelSelectTests.m +55 -0
  491. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBSafariAlertTests.m +73 -0
  492. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBScreenTests.m +31 -0
  493. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBScrollingTests.m +126 -0
  494. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBSessionIntegrationTests.m +131 -0
  495. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBTapTest.m +103 -0
  496. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBTestMacros.h +33 -0
  497. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBTypingTest.m +68 -0
  498. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBVideoRecordingTests.m +64 -0
  499. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBW3CMultiTouchActionsIntegrationTests.m +124 -0
  500. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBW3CTouchActionsIntegrationTests.m +491 -0
  501. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBW3CTypeActionsTests.m +184 -0
  502. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/FBXPathIntegrationTests.m +157 -0
  503. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/Info.plist +24 -0
  504. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCElementSnapshotHelperTests.m +211 -0
  505. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCElementSnapshotHitPointTests.m +30 -0
  506. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIApplicationHelperTests.m +162 -0
  507. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIDeviceHealthCheckTests.m +27 -0
  508. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIDeviceHelperTests.m +220 -0
  509. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIDeviceRotationTests.m +84 -0
  510. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIElementAttributesTests.m +248 -0
  511. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIElementFBFindTests.m +472 -0
  512. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/IntegrationTests/XCUIElementHelperIntegrationTests.m +51 -0
  513. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/Doubles/XCElementSnapshotDouble.h +16 -0
  514. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/Doubles/XCElementSnapshotDouble.m +110 -0
  515. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/Doubles/XCUIApplicationDouble.h +17 -0
  516. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/Doubles/XCUIApplicationDouble.m +66 -0
  517. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/Doubles/XCUIElementDouble.h +51 -0
  518. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/Doubles/XCUIElementDouble.m +104 -0
  519. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBClassChainTests.m +313 -0
  520. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBConfigurationTests.m +60 -0
  521. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBElementCacheTests.m +124 -0
  522. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBElementTypeTransformerTests.m +45 -0
  523. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBElementUtilitiesTests.m +36 -0
  524. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBErrorBuilderTests.m +61 -0
  525. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBExceptionHandlerTests.m +59 -0
  526. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBLRUCacheTests.m +127 -0
  527. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBMathUtilsTests.m +113 -0
  528. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBProtocolHelpersTests.m +81 -0
  529. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBRouteTests.m +129 -0
  530. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBRunLoopSpinnerTests.m +98 -0
  531. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBRuntimeUtilsTests.m +45 -0
  532. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBSDKVersionTests.m +66 -0
  533. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBSessionTests.m +67 -0
  534. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBXMLSafeStringTests.m +35 -0
  535. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/FBXPathTests.m +155 -0
  536. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/Info.plist +24 -0
  537. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/NSDictionaryFBUtf8SafeTests.m +34 -0
  538. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/NSExpressionFBFormatTests.m +60 -0
  539. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/NSPredicateFBFormatTests.m +72 -0
  540. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests/XCUIElementHelpersTests.m +50 -0
  541. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests_tvOS/Doubles/XCUIElementDouble.h +46 -0
  542. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests_tvOS/Doubles/XCUIElementDouble.m +80 -0
  543. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests_tvOS/FBTVNavigationTrackerTests.m +86 -0
  544. package/drivers/ios/WebDriverAgent/WebDriverAgentTests/UnitTests_tvOS/Info.plist +24 -0
  545. package/drivers/ios/WebDriverAgent/eslint.config.mjs +13 -0
  546. package/drivers/ios/WebDriverAgent/index.ts +7 -0
  547. package/drivers/ios/WebDriverAgent/lib/check-dependencies.ts +34 -0
  548. package/drivers/ios/WebDriverAgent/lib/constants.ts +18 -0
  549. package/drivers/ios/WebDriverAgent/lib/logger.ts +3 -0
  550. package/drivers/ios/WebDriverAgent/lib/no-session-proxy.ts +25 -0
  551. package/drivers/ios/WebDriverAgent/lib/types.ts +139 -0
  552. package/drivers/ios/WebDriverAgent/lib/utils.ts +342 -0
  553. package/drivers/ios/WebDriverAgent/lib/webdriveragent.ts +715 -0
  554. package/drivers/ios/WebDriverAgent/lib/xcodebuild.ts +488 -0
  555. package/drivers/ios/WebDriverAgent/package.json +102 -0
  556. package/drivers/ios/WebDriverAgent/test/functional/desired.ts +7 -0
  557. package/drivers/ios/WebDriverAgent/test/functional/helpers/simulator.ts +34 -0
  558. package/drivers/ios/WebDriverAgent/test/functional/webdriveragent-e2e-specs.ts +119 -0
  559. package/drivers/ios/WebDriverAgent/test/unit/utils-specs.ts +157 -0
  560. package/drivers/ios/WebDriverAgent/test/unit/webdriveragent-specs.ts +446 -0
  561. package/drivers/ios/WebDriverAgent/tsconfig.json +16 -0
  562. package/package.json +16 -0
@@ -0,0 +1,1864 @@
1
+ import XCTest
2
+
3
+ extension RunnerTests {
4
+ struct TouchVisualizationFrame {
5
+ let x: Double
6
+ let y: Double
7
+ let referenceWidth: Double
8
+ let referenceHeight: Double
9
+ }
10
+
11
+ struct DragVisualizationFrame {
12
+ let x: Double
13
+ let y: Double
14
+ let x2: Double
15
+ let y2: Double
16
+ let referenceWidth: Double
17
+ let referenceHeight: Double
18
+ }
19
+
20
+ struct DragPoints {
21
+ let x: Double
22
+ let y: Double
23
+ let x2: Double
24
+ let y2: Double
25
+ }
26
+
27
+ struct SelectorElementMatch {
28
+ let element: XCUIElement?
29
+ let isAmbiguous: Bool
30
+ }
31
+
32
+ enum TextTypingRepairMode {
33
+ case none
34
+ case append
35
+ case replacement
36
+ }
37
+
38
+ enum TextEntryTiming {
39
+ static let focusTimeout: TimeInterval = 0.4
40
+ static let repairReadinessTimeout: TimeInterval = 1.0
41
+ static let readinessTimeout: TimeInterval = 2.0
42
+ static let hardwareKeyboardFallbackTimeout: TimeInterval = 0.35
43
+ static let pollInterval: TimeInterval = 0.02
44
+ static let warmupValueTimeout: TimeInterval = 0.4
45
+ static let verificationStabilityWindow: TimeInterval = 0.2
46
+ }
47
+
48
+ struct TextEntryResult {
49
+ let verified: Bool?
50
+ let repaired: Bool
51
+ let expectedText: String?
52
+ let observedText: String?
53
+ }
54
+
55
+ struct TextEntryTarget {
56
+ let element: XCUIElement?
57
+ let refreshPoint: CGPoint?
58
+ let prefersFocusedElement: Bool
59
+
60
+ func withElement(_ nextElement: XCUIElement?) -> TextEntryTarget {
61
+ guard let nextElement else {
62
+ return self
63
+ }
64
+ let frame = nextElement.frame
65
+ let point = frame.isEmpty ? refreshPoint : CGPoint(x: frame.midX, y: frame.midY)
66
+ return TextEntryTarget(
67
+ element: nextElement,
68
+ refreshPoint: point,
69
+ prefersFocusedElement: prefersFocusedElement
70
+ )
71
+ }
72
+ }
73
+
74
+ // MARK: - Navigation Gestures
75
+
76
+ func tapInAppBackControl(app: XCUIApplication) -> Bool {
77
+ #if os(macOS)
78
+ if let back = macOSNavigationBackElement(app: app) {
79
+ tapElementCenter(app: app, element: back)
80
+ return true
81
+ }
82
+ return false
83
+ #elseif os(tvOS)
84
+ _ = pressTvRemote(.menu)
85
+ return true
86
+ #else
87
+ let buttons = app.navigationBars.buttons.allElementsBoundByIndex
88
+ if let back = buttons.first(where: { $0.isHittable }) {
89
+ back.tap()
90
+ return true
91
+ }
92
+ return false
93
+ #endif
94
+ }
95
+
96
+ func performBackGesture(app: XCUIApplication) {
97
+ if pressTvRemote(.menu) {
98
+ return
99
+ }
100
+ performCoordinateBackGesture(app: app)
101
+ }
102
+
103
+ private func performCoordinateBackGesture(app: XCUIApplication) {
104
+ #if !os(tvOS)
105
+ let target = app.windows.firstMatch.exists ? app.windows.firstMatch : app
106
+ let start = target.coordinate(withNormalizedOffset: CGVector(dx: 0.05, dy: 0.5))
107
+ let end = target.coordinate(withNormalizedOffset: CGVector(dx: 0.8, dy: 0.5))
108
+ start.press(forDuration: 0.05, thenDragTo: end)
109
+ #endif
110
+ }
111
+
112
+ func performSystemBackAction(app: XCUIApplication) -> Bool {
113
+ #if os(macOS)
114
+ return false
115
+ #else
116
+ if pressTvRemote(.menu) {
117
+ return true
118
+ }
119
+ performBackGesture(app: app)
120
+ return true
121
+ #endif
122
+ }
123
+
124
+ func performAppSwitcherGesture(app: XCUIApplication) {
125
+ if pressTvRemote(.home) {
126
+ sleepFor(resolveTvRemoteDoublePressDelay())
127
+ _ = pressTvRemote(.home)
128
+ return
129
+ }
130
+ performCoordinateAppSwitcherGesture(app: app)
131
+ }
132
+
133
+ private func performCoordinateAppSwitcherGesture(app: XCUIApplication) {
134
+ #if !os(tvOS)
135
+ let target = app.windows.firstMatch.exists ? app.windows.firstMatch : app
136
+ let start = target.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.99))
137
+ let end = target.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.7))
138
+ start.press(forDuration: 0.6, thenDragTo: end)
139
+ #endif
140
+ }
141
+
142
+ func pressHomeButton() {
143
+ #if os(macOS)
144
+ return
145
+ #else
146
+ if pressTvRemote(.home) {
147
+ return
148
+ }
149
+ XCUIDevice.shared.press(.home)
150
+ #endif
151
+ }
152
+
153
+ func rotateDevice(to orientationName: String) -> Bool {
154
+ #if os(macOS) || os(tvOS)
155
+ return false
156
+ #else
157
+ switch orientationName {
158
+ case "portrait":
159
+ XCUIDevice.shared.orientation = .portrait
160
+ case "portrait-upside-down":
161
+ XCUIDevice.shared.orientation = .portraitUpsideDown
162
+ case "landscape-left":
163
+ XCUIDevice.shared.orientation = .landscapeLeft
164
+ case "landscape-right":
165
+ XCUIDevice.shared.orientation = .landscapeRight
166
+ default:
167
+ return false
168
+ }
169
+ sleepFor(0.2)
170
+ return true
171
+ #endif
172
+ }
173
+
174
+ // Maps XCUIDevice's appearance enum onto the wire strings the host uses.
175
+ //
176
+ // `unspecified` means no style is being forced rather than "dark", and it
177
+ // renders light, so it reports as light. That matches the reading the Android
178
+ // side gives for the auto/custom night-mode schedules: the only honest answer
179
+ // from the current state alone is "not currently dark".
180
+ func appearanceName(_ appearance: XCUIDevice.Appearance) -> String {
181
+ switch appearance {
182
+ case .dark:
183
+ return "dark"
184
+ default:
185
+ return "light"
186
+ }
187
+ }
188
+
189
+ // Returns nil for anything that is not a recognised appearance, so an
190
+ // unsupported value fails loudly instead of silently landing on a default.
191
+ func appearanceValue(_ name: String) -> XCUIDevice.Appearance? {
192
+ switch name.lowercased() {
193
+ case "dark":
194
+ return .dark
195
+ case "light":
196
+ return .light
197
+ default:
198
+ return nil
199
+ }
200
+ }
201
+
202
+ func findElement(app: XCUIApplication, text: String) -> XCUIElement? {
203
+ let predicate = NSPredicate(format: "label CONTAINS[c] %@ OR identifier CONTAINS[c] %@ OR value CONTAINS[c] %@", text, text, text)
204
+ let element = app.descendants(matching: .any).matching(predicate).firstMatch
205
+ return element.exists ? element : nil
206
+ }
207
+
208
+ func findElement(app: XCUIApplication, selectorKey: String, selectorValue: String) -> SelectorElementMatch {
209
+ let value = selectorValue.trimmingCharacters(in: .whitespacesAndNewlines)
210
+ guard !value.isEmpty else {
211
+ return SelectorElementMatch(element: nil, isAmbiguous: false)
212
+ }
213
+ let predicate: NSPredicate
214
+ switch selectorKey {
215
+ case "id":
216
+ predicate = NSPredicate(format: "identifier ==[c] %@", value)
217
+ case "label":
218
+ predicate = NSPredicate(format: "label ==[c] %@", value)
219
+ case "value":
220
+ predicate = NSPredicate(format: "value ==[c] %@", value)
221
+ case "text":
222
+ predicate = NSPredicate(format: "label ==[c] %@ OR identifier ==[c] %@ OR value ==[c] %@", value, value, value)
223
+ default:
224
+ return SelectorElementMatch(element: nil, isAmbiguous: false)
225
+ }
226
+
227
+ var matchedElement: XCUIElement?
228
+ let matches = app.descendants(matching: .any).matching(predicate).allElementsBoundByIndex
229
+ for element in matches where element.exists {
230
+ guard element.isHittable else {
231
+ continue
232
+ }
233
+ guard matchedElement == nil else {
234
+ return SelectorElementMatch(element: nil, isAmbiguous: true)
235
+ }
236
+ matchedElement = element
237
+ }
238
+ return SelectorElementMatch(element: matchedElement, isAmbiguous: false)
239
+ }
240
+
241
+ // MARK: - Liberal find-and-act
242
+ //
243
+ // The existing `findElement(selectorKey:selectorValue:)` above requires
244
+ // EXACT NSPredicate match + isHittable=true. That's too strict for many
245
+ // RN flows where target text is long (e.g. "What is Lorem Ipsum?") or
246
+ // non-hittable (StaticText headings). The Go-side snapshot+filter path
247
+ // is liberal (substring + case-insensitive + prefer-editable-inputs) but
248
+ // expensive (two HTTP round-trips: snapshot + tap, with 300-node cap).
249
+ //
250
+ // treeSignature builds a stable hash of the accessibility tree's
251
+ // geometry — the bits we care about for "is the UI ready for
252
+ // interaction": element type, identifier, label, and integer-rounded
253
+ // frame. Iterates depth-first, hashes each node into a Hasher, returns
254
+ // the final hash. Sub-pixel jitter from CALayer hairlines or display-
255
+ // link timing doesn't count as "still animating" (frames are rounded
256
+ // to int).
257
+ func treeSignature(of snapshot: XCUIElementSnapshot) -> UInt64 {
258
+ var hasher = Hasher()
259
+ func walk(_ s: XCUIElementSnapshot) {
260
+ hasher.combine(s.elementType.rawValue)
261
+ hasher.combine(s.identifier)
262
+ hasher.combine(s.label)
263
+ hasher.combine(Int(s.frame.origin.x))
264
+ hasher.combine(Int(s.frame.origin.y))
265
+ hasher.combine(Int(s.frame.size.width))
266
+ hasher.combine(Int(s.frame.size.height))
267
+ for child in s.children { walk(child) }
268
+ }
269
+ walk(snapshot)
270
+ return UInt64(bitPattern: Int64(hasher.finalize()))
271
+ }
272
+
273
+ // tryTapAlertButton handles the very common case of "tap a button in an
274
+ // active UIAlertController". Alerts present in a separate UIWindow, so
275
+ // their button frames don't translate to the main app's coordinate space —
276
+ // tapAt(x, y) using those frames misses the button. Tapping the
277
+ // XCUIElement directly lets XCTest do the window-coordinate math.
278
+ //
279
+ // Returns nil if no alert is present or no alert button matches; caller
280
+ // should then fall through to the main snapshot+tap path.
281
+ func tryTapAlertButton(
282
+ app: XCUIApplication,
283
+ selectorKey: String,
284
+ selectorValue: String
285
+ ) -> Response? {
286
+ let needle = selectorValue.trimmingCharacters(in: .whitespacesAndNewlines)
287
+ if needle.isEmpty {
288
+ return nil
289
+ }
290
+ let alertQuery = app.alerts
291
+ let alertCount = alertQuery.count
292
+ if alertCount == 0 {
293
+ return nil
294
+ }
295
+ let needleLower = needle.lowercased()
296
+
297
+ for i in 0..<alertCount {
298
+ let alert = alertQuery.element(boundBy: i)
299
+ if !alert.exists {
300
+ continue
301
+ }
302
+ // Score candidate buttons: 0=exact-on-target-attribute, 1=substring-on-target,
303
+ // 2=any-other-match. For selectorKey "id", target attribute is identifier;
304
+ // for "text"/"label", target is label.
305
+ var best: (XCUIElement, Int)? = nil
306
+ let buttons = alert.buttons.allElementsBoundByIndex
307
+ for button in buttons {
308
+ if !button.exists { continue }
309
+ let label = button.label.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
310
+ let identifier = button.identifier.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
311
+ var score: Int? = nil
312
+ switch selectorKey {
313
+ case "id":
314
+ if identifier == needleLower { score = 0 }
315
+ else if identifier.contains(needleLower) && !identifier.isEmpty { score = 1 }
316
+ else if label == needleLower { score = 0 }
317
+ else if label.contains(needleLower) && !label.isEmpty { score = 2 }
318
+ default: // "text", "label", anything else
319
+ if label == needleLower { score = 0 }
320
+ else if identifier == needleLower { score = 0 }
321
+ else if label.contains(needleLower) && !label.isEmpty { score = 1 }
322
+ else if identifier.contains(needleLower) && !identifier.isEmpty { score = 2 }
323
+ }
324
+ if let s = score {
325
+ if best == nil || s < best!.1 {
326
+ best = (button, s)
327
+ }
328
+ }
329
+ }
330
+ guard let (matchedButton, _) = best else { continue }
331
+ // Capture button frame (in screen coords) for response.
332
+ let frame = matchedButton.frame
333
+ let x = Double(frame.midX)
334
+ let y = Double(frame.midY)
335
+ let touchFrame = resolvedTouchVisualizationFrame(app: app, x: x, y: y)
336
+ let timing = measureGesture {
337
+ matchedButton.tap()
338
+ }
339
+ // Alert taps trigger a dismiss → underlying cascade (e.g. an
340
+ // alert "Discard" can kick off a navigation pop). There's a
341
+ // brief moment of "false stability" right after the alert
342
+ // dismisses but before the cascade animation actually starts.
343
+ // Wait for the tree to truly settle so the next tap (which
344
+ // will only do a pre-tap settle) doesn't get dropped by the
345
+ // gesture system mid-cascade.
346
+ waitForTreeStable(app: app, timeoutSeconds: 0.8)
347
+ return Response(
348
+ ok: true,
349
+ data: DataPayload(
350
+ message: "tapped alert button",
351
+ gestureStartUptimeMs: timing.gestureStartUptimeMs,
352
+ gestureEndUptimeMs: timing.gestureEndUptimeMs,
353
+ x: touchFrame.x,
354
+ y: touchFrame.y,
355
+ referenceWidth: touchFrame.referenceWidth,
356
+ referenceHeight: touchFrame.referenceHeight
357
+ )
358
+ )
359
+ }
360
+ return nil
361
+ }
362
+
363
+ // waitForTreeStable polls app.snapshot() until two consecutive
364
+ // snapshots produce the same tree signature, or the budget expires.
365
+ // Same idea as the awaitIdle command but inline — used by paths that
366
+ // need to wait for animation completion right where they are (e.g.
367
+ // tryTapAlertButton after dismissing an alert, before returning).
368
+ func waitForTreeStable(app: XCUIApplication, timeoutSeconds: TimeInterval) {
369
+ let started = Date()
370
+ var prevSig: UInt64? = nil
371
+ while Date().timeIntervalSince(started) < timeoutSeconds {
372
+ guard let snap = try? app.snapshot() else { return }
373
+ let sig = treeSignature(of: snap)
374
+ if let prev = prevSig, prev == sig {
375
+ return
376
+ }
377
+ prevSig = sig
378
+ }
379
+ }
380
+
381
+ // findLiberalMatch walks the XCUI tree, applies the same selector
382
+ // semantics as the Go-side (substring + case-insensitive + exact-match-
383
+ // preference + prefer-editable-inputs ranking), and returns the best
384
+ // match.
385
+ //
386
+ // The loop ALSO doubles as the animation-end gate: it walks the tree,
387
+ // and in the same pass computes a tree signature (hash of all node
388
+ // geometries). When two consecutive snapshots produce the same
389
+ // signature → the screen has settled → the match's frame is trustworthy
390
+ // and we can return.
391
+ //
392
+ // Why this is one loop and not "find then refresh-frame": doing it in
393
+ // one walk per iteration saves O(N) per iteration vs the previous
394
+ // two-pass design (one walk to find, separate walks to verify frame
395
+ // stability). It also ensures the matched element comes from the SAME
396
+ // snapshot we judged stable — no race window between "screen settled"
397
+ // and "use match from older snapshot".
398
+ //
399
+ // Budget: up to 600ms (~15 iters of ~40ms each). For a fully static
400
+ // screen the loop exits at iter 2 (~80ms). Mid-animation extends until
401
+ // tree stops changing or budget exhausted (then we tap at whatever
402
+ // frame we last saw, same fallback as before).
403
+
404
+ struct LiberalMatchResult {
405
+ let matched: XCUIElementSnapshot?
406
+ let walkedNodes: [SnapshotNode]
407
+ }
408
+
409
+ func findLiberalMatch(
410
+ app: XCUIApplication,
411
+ selectorKey: String,
412
+ selectorValue: String
413
+ ) -> LiberalMatchResult {
414
+ let needle = selectorValue.trimmingCharacters(in: .whitespacesAndNewlines)
415
+ if needle.isEmpty {
416
+ return LiberalMatchResult(matched: nil, walkedNodes: [])
417
+ }
418
+ let needleLower = needle.lowercased()
419
+
420
+ func typeRank(_ t: XCUIElement.ElementType) -> Int {
421
+ switch t {
422
+ case .textField, .secureTextField, .searchField, .textView: return 0
423
+ case .button, .link, .cell, .menuItem, .switch, .checkBox: return 1
424
+ default: return 2
425
+ }
426
+ }
427
+
428
+ func nodeMatchKind(label: String, identifier: String, valueText: String) -> Int? {
429
+ let labLower = label.lowercased()
430
+ let idLower = identifier.lowercased()
431
+ let valLower = valueText.lowercased()
432
+ switch selectorKey {
433
+ case "text":
434
+ if labLower == needleLower || valLower == needleLower || idLower == needleLower {
435
+ return 0
436
+ }
437
+ if labLower.contains(needleLower) || valLower.contains(needleLower) || idLower.contains(needleLower) {
438
+ return 1
439
+ }
440
+ return nil
441
+ case "id":
442
+ if idLower == needleLower { return 0 }
443
+ if idLower.contains(needleLower) { return 1 }
444
+ return nil
445
+ case "label":
446
+ if labLower == needleLower { return 0 }
447
+ if labLower.contains(needleLower) { return 1 }
448
+ return nil
449
+ default:
450
+ return nil
451
+ }
452
+ }
453
+
454
+ // walkOnce: one snapshot, one DFS — produces (match-candidates,
455
+ // tree-signature, flat node list) in a single pass. We accumulate
456
+ // hash bits into a Hasher as we visit each node and collect matches
457
+ // along the way.
458
+ func walkOnce(root: XCUIElementSnapshot) -> (XCUIElementSnapshot?, UInt64, [SnapshotNode]) {
459
+ var hasher = Hasher()
460
+ var allNodes: [SnapshotNode] = []
461
+ var matches: [(XCUIElementSnapshot, Int, Int)] = []
462
+
463
+ func walk(_ snap: XCUIElementSnapshot, depth: Int, parentIdx: Int?) {
464
+ // Tree-signature contribution: geometry + identity bits.
465
+ hasher.combine(snap.elementType.rawValue)
466
+ hasher.combine(snap.identifier)
467
+ hasher.combine(snap.label)
468
+ hasher.combine(Int(snap.frame.origin.x))
469
+ hasher.combine(Int(snap.frame.origin.y))
470
+ hasher.combine(Int(snap.frame.size.width))
471
+ hasher.combine(Int(snap.frame.size.height))
472
+
473
+ // Match-candidate collection + flat-node serialisation.
474
+ let label = snap.label.trimmingCharacters(in: .whitespacesAndNewlines)
475
+ let identifier = snap.identifier.trimmingCharacters(in: .whitespacesAndNewlines)
476
+ let valueText = String(describing: snap.value ?? "")
477
+ .trimmingCharacters(in: .whitespacesAndNewlines)
478
+ let nodeIdx = allNodes.count
479
+ allNodes.append(SnapshotNode(
480
+ index: nodeIdx,
481
+ type: elementTypeName(snap.elementType),
482
+ label: label.isEmpty ? nil : label,
483
+ identifier: identifier.isEmpty ? nil : identifier,
484
+ value: valueText.isEmpty ? nil : valueText,
485
+ placeholderValue: nil,
486
+ rect: snapshotRect(from: snap.frame),
487
+ enabled: snap.isEnabled,
488
+ focused: nil,
489
+ selected: snap.isSelected ? true : nil,
490
+ hittable: false,
491
+ depth: depth,
492
+ parentIndex: parentIdx,
493
+ hiddenContentAbove: nil,
494
+ hiddenContentBelow: nil
495
+ ))
496
+ if let kind = nodeMatchKind(label: label, identifier: identifier, valueText: valueText) {
497
+ if !snap.frame.isNull && !snap.frame.isEmpty {
498
+ matches.append((snap, kind, typeRank(snap.elementType)))
499
+ }
500
+ }
501
+ for child in snap.children {
502
+ walk(child, depth: depth + 1, parentIdx: nodeIdx)
503
+ }
504
+ }
505
+ walk(root, depth: 0, parentIdx: nil)
506
+
507
+ matches.sort { ($0.1, $0.2) < ($1.1, $1.2) }
508
+ let sig = UInt64(bitPattern: Int64(hasher.finalize()))
509
+ return (matches.first?.0, sig, allNodes)
510
+ }
511
+
512
+ let started = Date()
513
+ let timeout = 0.6 // 600ms budget for animation-end + find
514
+ var prevSig: UInt64? = nil
515
+ var lastMatch: XCUIElementSnapshot? = nil
516
+ var lastNodes: [SnapshotNode] = []
517
+
518
+ for _ in 0..<15 {
519
+ let root: XCUIElementSnapshot
520
+ do {
521
+ root = try app.snapshot()
522
+ } catch {
523
+ return LiberalMatchResult(matched: lastMatch, walkedNodes: lastNodes)
524
+ }
525
+ let (match, sig, nodes) = walkOnce(root: root)
526
+ lastMatch = match
527
+ lastNodes = nodes
528
+ // Two consecutive identical signatures = settled. Match's frame
529
+ // came from THIS snapshot, so it's trustworthy.
530
+ if let prev = prevSig, prev == sig {
531
+ return LiberalMatchResult(matched: match, walkedNodes: nodes)
532
+ }
533
+ prevSig = sig
534
+ if Date().timeIntervalSince(started) > timeout {
535
+ break
536
+ }
537
+ }
538
+ // Budget exhausted — return the last walk's data. Same fallback the
539
+ // previous code took on timeout.
540
+ return LiberalMatchResult(matched: lastMatch, walkedNodes: lastNodes)
541
+ }
542
+
543
+ func queryElement(app: XCUIApplication, selectorKey: String, selectorValue: String) -> Response {
544
+ let match = findElement(app: app, selectorKey: selectorKey, selectorValue: selectorValue)
545
+ if match.isAmbiguous {
546
+ return Response(ok: false, error: ErrorPayload(code: "AMBIGUOUS_MATCH", message: "selector matched multiple elements"))
547
+ }
548
+ guard let element = match.element else {
549
+ return Response(ok: true, data: DataPayload(found: false, nodes: []))
550
+ }
551
+
552
+ let label = element.label.trimmingCharacters(in: .whitespacesAndNewlines)
553
+ let identifier = element.identifier.trimmingCharacters(in: .whitespacesAndNewlines)
554
+ let valueText = String(describing: element.value ?? "")
555
+ .trimmingCharacters(in: .whitespacesAndNewlines)
556
+ let placeholder = element.placeholderValue?
557
+ .trimmingCharacters(in: .whitespacesAndNewlines)
558
+ let node = SnapshotNode(
559
+ index: 0,
560
+ type: elementTypeName(element.elementType),
561
+ label: label.isEmpty ? nil : label,
562
+ identifier: identifier.isEmpty ? nil : identifier,
563
+ value: valueText.isEmpty ? nil : valueText,
564
+ placeholderValue: (placeholder?.isEmpty ?? true) ? nil : placeholder,
565
+ rect: snapshotRect(from: element.frame),
566
+ enabled: element.isEnabled,
567
+ focused: nil,
568
+ selected: element.isSelected ? true : nil,
569
+ hittable: element.isHittable,
570
+ depth: 0,
571
+ parentIndex: nil,
572
+ hiddenContentAbove: nil,
573
+ hiddenContentBelow: nil
574
+ )
575
+ return Response(
576
+ ok: true,
577
+ data: DataPayload(
578
+ text: readableText(for: element),
579
+ found: true,
580
+ nodes: [node]
581
+ )
582
+ )
583
+ }
584
+
585
+ func readTextAt(app: XCUIApplication, x: Double, y: Double) -> String? {
586
+ let point = CGPoint(x: x, y: y)
587
+ let candidates = app.descendants(matching: .any).allElementsBoundByIndex
588
+ .filter { element in
589
+ element.exists && !element.frame.isEmpty && element.frame.contains(point)
590
+ }
591
+ .sorted { left, right in
592
+ let leftArea = max(1, left.frame.width * left.frame.height)
593
+ let rightArea = max(1, right.frame.width * right.frame.height)
594
+ if leftArea != rightArea {
595
+ return leftArea < rightArea
596
+ }
597
+ if left.frame.minY != right.frame.minY {
598
+ return left.frame.minY < right.frame.minY
599
+ }
600
+ if left.frame.minX != right.frame.minX {
601
+ return left.frame.minX < right.frame.minX
602
+ }
603
+ return left.elementType.rawValue < right.elementType.rawValue
604
+ }
605
+
606
+ for element in candidates where prefersExpandedTextRead(element) {
607
+ if let text = readableText(for: element) {
608
+ return text
609
+ }
610
+ }
611
+ for element in candidates {
612
+ if let text = readableText(for: element) {
613
+ return text
614
+ }
615
+ }
616
+ return nil
617
+ }
618
+
619
+ func clearTextInput(_ element: XCUIElement) {
620
+ #if !os(tvOS)
621
+ moveCaretToEnd(element: element)
622
+ #endif
623
+ let count = estimatedDeleteCount(for: element)
624
+ let deletes = String(repeating: XCUIKeyboardKey.delete.rawValue, count: count)
625
+ element.typeText(deletes)
626
+ }
627
+
628
+ func textInputAt(app: XCUIApplication, x: Double, y: Double) -> XCUIElement? {
629
+ let point = CGPoint(x: x, y: y)
630
+ var matched: XCUIElement?
631
+ let exceptionMessage = RunnerObjCExceptionCatcher.catchException({
632
+ // Type-filter via NSPredicate first — XCTest can evaluate this on
633
+ // the accessibility tree without iterating every descendant. The
634
+ // .any descendants + allElementsBoundByIndex form walks the entire
635
+ // tree O(n) — ~10s on dense screens, enough to time out the Go-side
636
+ // 10s callTimeout for inputText (NSPR/SPR Push-Input scenario).
637
+ let editablePredicate = NSPredicate(format:
638
+ "elementType == %d OR elementType == %d OR elementType == %d OR elementType == %d",
639
+ XCUIElement.ElementType.textField.rawValue,
640
+ XCUIElement.ElementType.secureTextField.rawValue,
641
+ XCUIElement.ElementType.searchField.rawValue,
642
+ XCUIElement.ElementType.textView.rawValue
643
+ )
644
+ let editableQuery = app.descendants(matching: .any).matching(editablePredicate)
645
+ // Fallback when no editable contains the point: prefer the only
646
+ // visible editable on screen. Common Maestro pattern: tap a button
647
+ // that navigates to an Input screen, then inputText — the button's
648
+ // old coords don't intersect the new screen's TextField, but the
649
+ // TextField is the obvious target.
650
+ let allEditables = editableQuery.allElementsBoundByIndex.filter { $0.exists }
651
+ let candidates = allEditables.filter { element in
652
+ let frame = element.frame
653
+ return !frame.isEmpty && frame.contains(point)
654
+ }
655
+ if candidates.isEmpty {
656
+ // No editable at the point. If exactly one editable is on screen,
657
+ // it's the intended target.
658
+ let onScreen = allEditables.filter { !$0.frame.isEmpty }
659
+ if onScreen.count == 1 {
660
+ matched = onScreen.first
661
+ }
662
+ return
663
+ }
664
+ let sorted = candidates
665
+ .sorted { left, right in
666
+ let leftArea = max(1, left.frame.width * left.frame.height)
667
+ let rightArea = max(1, right.frame.width * right.frame.height)
668
+ if leftArea != rightArea {
669
+ return leftArea < rightArea
670
+ }
671
+ if left.frame.minY != right.frame.minY {
672
+ return left.frame.minY < right.frame.minY
673
+ }
674
+ if left.frame.minX != right.frame.minX {
675
+ return left.frame.minX < right.frame.minX
676
+ }
677
+ return left.elementType.rawValue < right.elementType.rawValue
678
+ }
679
+ matched = sorted.first
680
+ })
681
+ if let exceptionMessage {
682
+ NSLog(
683
+ "AGENT_DEVICE_RUNNER_TEXT_INPUT_AT_POINT_IGNORED_EXCEPTION=%@",
684
+ exceptionMessage
685
+ )
686
+ return nil
687
+ }
688
+ return matched
689
+ }
690
+
691
+ func focusedTextInput(app: XCUIApplication) -> XCUIElement? {
692
+ var focused: XCUIElement?
693
+ let exceptionMessage = RunnerObjCExceptionCatcher.catchException({
694
+ let candidate = app
695
+ .descendants(matching: .any)
696
+ .matching(NSPredicate(format: "hasKeyboardFocus == 1"))
697
+ .firstMatch
698
+ guard candidate.exists else { return }
699
+
700
+ switch candidate.elementType {
701
+ case .textField, .secureTextField, .searchField, .textView:
702
+ focused = candidate
703
+ default:
704
+ return
705
+ }
706
+ })
707
+ if let exceptionMessage {
708
+ NSLog(
709
+ "AGENT_DEVICE_RUNNER_FOCUSED_INPUT_QUERY_IGNORED_EXCEPTION=%@",
710
+ exceptionMessage
711
+ )
712
+ return nil
713
+ }
714
+ return focused
715
+ }
716
+
717
+ func stabilizeTextInputBeforeTyping(app: XCUIApplication, target: XCUIElement?) -> XCUIElement? {
718
+ #if os(tvOS)
719
+ return target
720
+ #else
721
+ let latest = target
722
+ let deadline = Date().addingTimeInterval(TextEntryTiming.focusTimeout)
723
+ while Date() < deadline {
724
+ if let focused = focusedTextInput(app: app) {
725
+ return focused
726
+ }
727
+ sleepFor(TextEntryTiming.pollInterval)
728
+ }
729
+ return latest
730
+ #endif
731
+ }
732
+
733
+ func focusTextInputForTextEntry(app: XCUIApplication, x: Double?, y: Double?, identifier: String? = nil) -> TextEntryTarget {
734
+ // Local edit (not in upstream agent-device). When the caller supplies
735
+ // an `identifier` hint, resolve the editable element via the XCTest
736
+ // query DSL (predicate-based, indexed) instead of walking the full
737
+ // descendant tree via textInputAt's allElementsBoundByIndex. Saves
738
+ // ~1.4s per type call on dense screens. Falls back to the upstream
739
+ // x/y path when the identifier is empty or doesn't resolve to a text
740
+ // input.
741
+ if let identifier, !identifier.isEmpty {
742
+ var matched: XCUIElement?
743
+ _ = RunnerObjCExceptionCatcher.catchException({
744
+ // Composite predicate: identifier + element type filter. RN often
745
+ // gives the SAME testID to a wrapper Image (search icon) and the
746
+ // inner TextField — `firstMatch` on identifier alone can land on
747
+ // the wrong one. Constraining the predicate to editable types
748
+ // skips the icon directly without a slow descendant fallback.
749
+ let editablePredicate = NSPredicate(format:
750
+ "identifier == %@ AND (elementType == %d OR elementType == %d OR elementType == %d OR elementType == %d)",
751
+ identifier,
752
+ XCUIElement.ElementType.textField.rawValue,
753
+ XCUIElement.ElementType.secureTextField.rawValue,
754
+ XCUIElement.ElementType.searchField.rawValue,
755
+ XCUIElement.ElementType.textView.rawValue
756
+ )
757
+ let candidate = app.descendants(matching: .any).matching(editablePredicate).firstMatch
758
+ if candidate.exists {
759
+ matched = candidate
760
+ return
761
+ }
762
+ // Identifier didn't match any editable directly. Try matching the
763
+ // identifier loosely (any type) and look for an editable
764
+ // descendant — this catches RN's wrapper-view-with-inner-textinput
765
+ // pattern when both have different ids.
766
+ let looseMatch = app.descendants(matching: .any)
767
+ .matching(NSPredicate(format: "identifier == %@", identifier))
768
+ .firstMatch
769
+ guard looseMatch.exists else { return }
770
+ let typeFilter = NSPredicate(format:
771
+ "elementType == %d OR elementType == %d OR elementType == %d OR elementType == %d",
772
+ XCUIElement.ElementType.textField.rawValue,
773
+ XCUIElement.ElementType.secureTextField.rawValue,
774
+ XCUIElement.ElementType.searchField.rawValue,
775
+ XCUIElement.ElementType.textView.rawValue
776
+ )
777
+ let inner = looseMatch.descendants(matching: .any).matching(typeFilter).firstMatch
778
+ if inner.exists {
779
+ matched = inner
780
+ }
781
+ })
782
+ if let matched {
783
+ let frame = matched.frame
784
+ let refreshPoint: CGPoint?
785
+ if !frame.isEmpty {
786
+ _ = tapAt(app: app, x: frame.midX, y: frame.midY)
787
+ refreshPoint = CGPoint(x: frame.midX, y: frame.midY)
788
+ } else if let x, let y {
789
+ _ = tapAt(app: app, x: x, y: y)
790
+ refreshPoint = CGPoint(x: x, y: y)
791
+ } else {
792
+ refreshPoint = nil
793
+ }
794
+ // Local edit: skip stabilizeTextInputBeforeTyping (up to 400ms
795
+ // polling focusedTextInput) and waitForTextEntryReadiness (up to
796
+ // 2000ms polling keyboard visibility). Both exist for the
797
+ // x/y / unknown-target path, where the runner has to guess which
798
+ // element got tapped. We resolved the element by identifier and
799
+ // just tapped it — XCUI's typeText / synthetic-typing path will
800
+ // handle keyboard timing itself.
801
+ return TextEntryTarget(
802
+ element: matched,
803
+ refreshPoint: refreshPoint,
804
+ prefersFocusedElement: false
805
+ )
806
+ }
807
+ // Identifier didn't resolve — fall through to x/y / focused-element path.
808
+ }
809
+
810
+ guard let x, let y else {
811
+ let focused = waitForTextEntryReadiness(
812
+ app: app,
813
+ target: TextEntryTarget(
814
+ element: focusedTextInput(app: app),
815
+ refreshPoint: nil,
816
+ prefersFocusedElement: true
817
+ )
818
+ )
819
+ return TextEntryTarget(element: focused, refreshPoint: nil, prefersFocusedElement: true)
820
+ }
821
+
822
+ let target = textInputAt(app: app, x: x, y: y)
823
+ let requestedPoint = CGPoint(x: x, y: y)
824
+ if let target {
825
+ let frame = target.frame
826
+ if !frame.isEmpty {
827
+ _ = tapAt(app: app, x: frame.midX, y: frame.midY)
828
+ } else {
829
+ _ = tapAt(app: app, x: x, y: y)
830
+ }
831
+ } else {
832
+ _ = tapAt(app: app, x: x, y: y)
833
+ }
834
+ let stabilized = stabilizeTextInputBeforeTyping(app: app, target: target)
835
+ let element = waitForTextEntryReadiness(
836
+ app: app,
837
+ target: TextEntryTarget(
838
+ element: stabilized ?? target,
839
+ refreshPoint: requestedPoint,
840
+ prefersFocusedElement: false
841
+ )
842
+ ) ?? stabilized ?? target
843
+ return TextEntryTarget(
844
+ element: element,
845
+ refreshPoint: textEntryRefreshPoint(for: element) ?? requestedPoint,
846
+ prefersFocusedElement: false
847
+ )
848
+ }
849
+
850
+ func resolveTextEntryMode(_ command: Command) -> TextTypingRepairMode {
851
+ switch command.textEntryMode {
852
+ case "append":
853
+ return .append
854
+ case "replace":
855
+ return .replacement
856
+ default:
857
+ return command.clearFirst == true ? .replacement : .none
858
+ }
859
+ }
860
+
861
+ func typeTextReliably(
862
+ app: XCUIApplication,
863
+ target: TextEntryTarget,
864
+ text: String,
865
+ delaySeconds: Double,
866
+ repairMode: TextTypingRepairMode = .none
867
+ ) -> TextEntryResult {
868
+ guard !text.isEmpty else {
869
+ return TextEntryResult(verified: true, repaired: false, expectedText: "", observedText: "")
870
+ }
871
+ var activeTarget = target
872
+ let initialTarget = resolveTextEntryElement(app: app, target: activeTarget)
873
+ activeTarget = activeTarget.withElement(initialTarget)
874
+ let currentText = editableTextValue(for: initialTarget, treatingPlaceholderAsEmpty: true)
875
+ let initialText = repairMode == .append ? currentText : nil
876
+ let expectedText = expectedTextEntryValue(typedText: text, mode: repairMode, initialText: initialText)
877
+
878
+ if repairMode == .replacement {
879
+ guard let replacementTarget = initialTarget else {
880
+ return TextEntryResult(verified: nil, repaired: false, expectedText: expectedText, observedText: nil)
881
+ }
882
+ if currentText == nil || currentText?.isEmpty == false {
883
+ clearTextInput(replacementTarget)
884
+ activeTarget = activeTarget.withElement(replacementTarget)
885
+ }
886
+ }
887
+
888
+ func typeIntoCurrentTarget(_ value: String) -> XCUIElement? {
889
+ // Local edit (not in upstream agent-device). SecureTextField on RN
890
+ // iOS doesn't respond to coordinate-taps for focus transfer, AND
891
+ // its hasKeyboardFocus query throws or returns false. Two-part fix:
892
+ // 1. element.tap() to force OS first-responder onto the field
893
+ // 2. DLSendSyntheticTyping to inject characters at the HID layer
894
+ // For non-secure fields we keep upstream's app.typeText behaviour.
895
+ // We tried universalizing synthetic typing but it regressed Login
896
+ // flows — without the explicit pre-tap, synthetic events fire
897
+ // before the field has stable first-responder and characters land
898
+ // in the wrong field. app.typeText handles the focus dance
899
+ // implicitly and is more robust for non-secure fields.
900
+ let currentTarget = resolveTextEntryElement(app: app, target: activeTarget)
901
+ let isSecureField = currentTarget?.elementType == .secureTextField
902
+ if isSecureField, let secure = currentTarget {
903
+ _ = RunnerObjCExceptionCatcher.catchException({
904
+ secure.tap()
905
+ })
906
+ sleepFor(0.05)
907
+ var typingError: NSError?
908
+ let ok = DLSendSyntheticTyping(value, 240, &typingError)
909
+ if !ok {
910
+ NSLog(
911
+ "DEVICELAB_RUNNER_SYNTHETIC_TYPING_FAILED message=%@ fallback=app.typeText",
912
+ typingError?.localizedDescription ?? "(no error)"
913
+ )
914
+ app.typeText(value)
915
+ }
916
+ return currentTarget
917
+ }
918
+ app.typeText(value)
919
+ return currentTarget ?? resolveTextEntryElement(app: app, target: activeTarget)
920
+ }
921
+
922
+ func waitForWarmupValue(_ expectedValue: String?, target: TextEntryTarget) {
923
+ guard let expectedValue else {
924
+ sleepFor(TextEntryTiming.pollInterval)
925
+ return
926
+ }
927
+ let deadline = Date().addingTimeInterval(TextEntryTiming.warmupValueTimeout)
928
+ while Date() < deadline {
929
+ if editableTextValue(for: resolveTextEntryElement(app: app, target: target)) == expectedValue {
930
+ return
931
+ }
932
+ sleepFor(TextEntryTiming.pollInterval)
933
+ }
934
+ }
935
+
936
+ let characters = Array(text)
937
+ if delaySeconds > 0 && characters.count > 1 {
938
+ var typedTarget: XCUIElement?
939
+ for (index, character) in characters.enumerated() {
940
+ typedTarget = typeIntoCurrentTarget(String(character)) ?? typedTarget
941
+ if index + 1 < characters.count {
942
+ sleepFor(delaySeconds)
943
+ }
944
+ }
945
+ if repairMode == .none {
946
+ return TextEntryResult(verified: nil, repaired: false, expectedText: nil, observedText: nil)
947
+ }
948
+ let repairResult = repairTextEntryIfNeeded(
949
+ app: app,
950
+ target: activeTarget.withElement(typedTarget),
951
+ expectedText: expectedText,
952
+ repairMode: repairMode
953
+ )
954
+ return verifyTextEntry(
955
+ app: app,
956
+ target: activeTarget.withElement(typedTarget),
957
+ expectedText: expectedText,
958
+ repaired: repairResult.repaired
959
+ )
960
+ }
961
+
962
+ let typedTarget: XCUIElement?
963
+ if repairMode != .none && characters.count > 1 {
964
+ let firstCharacter = String(characters[0])
965
+ var firstTypedTarget = typeIntoCurrentTarget(firstCharacter)
966
+ activeTarget = activeTarget.withElement(firstTypedTarget)
967
+ let warmupExpectedText = expectedTextEntryValue(
968
+ typedText: firstCharacter,
969
+ mode: repairMode,
970
+ initialText: initialText
971
+ )
972
+ waitForWarmupValue(warmupExpectedText, target: activeTarget)
973
+ let remainingText = String(characters.dropFirst())
974
+ firstTypedTarget = typeIntoCurrentTarget(remainingText) ?? firstTypedTarget
975
+ typedTarget = firstTypedTarget
976
+ } else {
977
+ typedTarget = typeIntoCurrentTarget(text)
978
+ }
979
+ if repairMode == .none {
980
+ return TextEntryResult(verified: nil, repaired: false, expectedText: nil, observedText: nil)
981
+ }
982
+ let repairResult = repairTextEntryIfNeeded(
983
+ app: app,
984
+ target: activeTarget.withElement(typedTarget),
985
+ expectedText: expectedText,
986
+ repairMode: repairMode
987
+ )
988
+ return verifyTextEntry(
989
+ app: app,
990
+ target: activeTarget.withElement(typedTarget),
991
+ expectedText: expectedText,
992
+ repaired: repairResult.repaired
993
+ )
994
+ }
995
+
996
+ private func repairTextEntryIfNeeded(
997
+ app: XCUIApplication,
998
+ target: TextEntryTarget,
999
+ expectedText: String?,
1000
+ repairMode: TextTypingRepairMode
1001
+ ) -> TextEntryResult {
1002
+ #if os(iOS)
1003
+ guard let targetElement = resolveTextEntryElement(app: app, target: target) else {
1004
+ return TextEntryResult(verified: nil, repaired: false, expectedText: expectedText, observedText: nil)
1005
+ }
1006
+ guard let expectedText else {
1007
+ let observedText = editableTextValue(for: targetElement)
1008
+ return TextEntryResult(verified: nil, repaired: false, expectedText: nil, observedText: observedText)
1009
+ }
1010
+ guard shouldRepairTextEntry(
1011
+ app: app,
1012
+ target: target,
1013
+ expectedText: expectedText,
1014
+ repairMode: repairMode
1015
+ ) else {
1016
+ return verifyTextEntry(app: app, target: target, expectedText: expectedText, repaired: false)
1017
+ }
1018
+
1019
+ guard let repairTarget = resolveTextEntryElement(app: app, target: target) else {
1020
+ return TextEntryResult(verified: nil, repaired: false, expectedText: expectedText, observedText: nil)
1021
+ }
1022
+ let observedText = editableTextValue(for: repairTarget) ?? ""
1023
+ NSLog(
1024
+ "AGENT_DEVICE_RUNNER_REPAIR_TEXT_ENTRY expectedLength=%d observedLength=%d",
1025
+ expectedText.count,
1026
+ observedText.count
1027
+ )
1028
+ clearTextInput(repairTarget)
1029
+ app.typeText(expectedText)
1030
+ return verifyTextEntry(app: app, target: target, expectedText: expectedText, repaired: true)
1031
+ #else
1032
+ return TextEntryResult(verified: nil, repaired: false, expectedText: expectedText, observedText: nil)
1033
+ #endif
1034
+ }
1035
+
1036
+ private func verifyTextEntry(
1037
+ app: XCUIApplication,
1038
+ target: TextEntryTarget,
1039
+ expectedText: String?,
1040
+ repaired: Bool
1041
+ ) -> TextEntryResult {
1042
+ let targetElement = resolveTextEntryElement(app: app, target: target)
1043
+ guard let expectedText else {
1044
+ return TextEntryResult(
1045
+ verified: nil,
1046
+ repaired: repaired,
1047
+ expectedText: nil,
1048
+ observedText: editableTextValue(for: targetElement)
1049
+ )
1050
+ }
1051
+ guard let observedText = editableTextValue(for: targetElement) else {
1052
+ return TextEntryResult(verified: nil, repaired: repaired, expectedText: expectedText, observedText: nil)
1053
+ }
1054
+ guard observedText == expectedText else {
1055
+ return TextEntryResult(
1056
+ verified: false,
1057
+ repaired: repaired,
1058
+ expectedText: expectedText,
1059
+ observedText: observedText
1060
+ )
1061
+ }
1062
+ let stableDeadline = Date().addingTimeInterval(TextEntryTiming.verificationStabilityWindow)
1063
+ var latestObservedText = observedText
1064
+ while Date() < stableDeadline {
1065
+ sleepFor(TextEntryTiming.pollInterval)
1066
+ guard let nextObservedText = editableTextValue(for: resolveTextEntryElement(app: app, target: target)) else {
1067
+ return TextEntryResult(verified: nil, repaired: repaired, expectedText: expectedText, observedText: nil)
1068
+ }
1069
+ latestObservedText = nextObservedText
1070
+ guard nextObservedText == expectedText else {
1071
+ return TextEntryResult(
1072
+ verified: false,
1073
+ repaired: repaired,
1074
+ expectedText: expectedText,
1075
+ observedText: nextObservedText
1076
+ )
1077
+ }
1078
+ }
1079
+ return TextEntryResult(
1080
+ verified: true,
1081
+ repaired: repaired,
1082
+ expectedText: expectedText,
1083
+ observedText: latestObservedText
1084
+ )
1085
+ }
1086
+
1087
+ private func expectedTextEntryValue(
1088
+ typedText: String,
1089
+ mode: TextTypingRepairMode,
1090
+ initialText: String?
1091
+ ) -> String? {
1092
+ switch mode {
1093
+ case .none:
1094
+ return nil
1095
+ case .append:
1096
+ guard let initialText else {
1097
+ return nil
1098
+ }
1099
+ return initialText + typedText
1100
+ case .replacement:
1101
+ return typedText
1102
+ }
1103
+ }
1104
+
1105
+ private func shouldRepairTextEntry(
1106
+ app: XCUIApplication,
1107
+ target: TextEntryTarget,
1108
+ expectedText: String,
1109
+ repairMode: TextTypingRepairMode
1110
+ ) -> Bool {
1111
+ #if os(iOS)
1112
+ var latestObservedText: String?
1113
+ let deadline = Date().addingTimeInterval(TextEntryTiming.verificationStabilityWindow)
1114
+ repeat {
1115
+ guard let observedText = editableTextValue(for: resolveTextEntryElement(app: app, target: target)) else {
1116
+ return false
1117
+ }
1118
+ if observedText == expectedText {
1119
+ return false
1120
+ }
1121
+ latestObservedText = observedText
1122
+ if !isRepairableTextEntryMismatch(
1123
+ observedText: observedText,
1124
+ expectedText: expectedText,
1125
+ repairMode: repairMode
1126
+ ) {
1127
+ return false
1128
+ }
1129
+ sleepFor(TextEntryTiming.pollInterval)
1130
+ } while Date() < deadline
1131
+
1132
+ guard let latestObservedText else {
1133
+ return false
1134
+ }
1135
+ guard latestObservedText != expectedText else {
1136
+ return false
1137
+ }
1138
+ return isRepairableTextEntryMismatch(
1139
+ observedText: latestObservedText,
1140
+ expectedText: expectedText,
1141
+ repairMode: repairMode
1142
+ )
1143
+ #else
1144
+ return false
1145
+ #endif
1146
+ }
1147
+
1148
+ private func isRepairableTextEntryMismatch(
1149
+ observedText: String,
1150
+ expectedText: String,
1151
+ repairMode: TextTypingRepairMode
1152
+ ) -> Bool {
1153
+ guard observedText != expectedText else {
1154
+ return false
1155
+ }
1156
+ if repairMode == .replacement {
1157
+ return true
1158
+ }
1159
+ return observedText.isEmpty || isLikelyDroppedCharacterTextEntryMismatch(
1160
+ observedText: observedText,
1161
+ expectedText: expectedText
1162
+ )
1163
+ }
1164
+
1165
+ private func isLikelyDroppedCharacterTextEntryMismatch(observedText: String, expectedText: String) -> Bool {
1166
+ guard observedText.count < expectedText.count else {
1167
+ return false
1168
+ }
1169
+ let missingCharacterCount = expectedText.count - observedText.count
1170
+ guard missingCharacterCount <= max(2, expectedText.count / 4) else {
1171
+ return false
1172
+ }
1173
+ var expectedIndex = expectedText.startIndex
1174
+ for character in observedText {
1175
+ guard let matchIndex = expectedText[expectedIndex...].firstIndex(of: character) else {
1176
+ return false
1177
+ }
1178
+ expectedIndex = expectedText.index(after: matchIndex)
1179
+ }
1180
+ return true
1181
+ }
1182
+
1183
+ private func resolveTextEntryElement(app: XCUIApplication, target: TextEntryTarget) -> XCUIElement? {
1184
+ if target.prefersFocusedElement {
1185
+ if let focused = focusedTextInput(app: app) {
1186
+ return focused
1187
+ }
1188
+ if let element = target.element, element.exists {
1189
+ return element
1190
+ }
1191
+ } else {
1192
+ if let element = target.element, element.exists {
1193
+ return element
1194
+ }
1195
+ }
1196
+ if let refreshPoint = target.refreshPoint,
1197
+ let refreshed = textInputAt(app: app, x: refreshPoint.x, y: refreshPoint.y) {
1198
+ return refreshed
1199
+ }
1200
+ if let focused = focusedTextInput(app: app) {
1201
+ return focused
1202
+ }
1203
+ return nil
1204
+ }
1205
+
1206
+ private func waitForTextEntryReadiness(
1207
+ app: XCUIApplication,
1208
+ target: TextEntryTarget,
1209
+ timeout: TimeInterval = TextEntryTiming.readinessTimeout
1210
+ ) -> XCUIElement? {
1211
+ #if os(iOS)
1212
+ var latest = resolveTextEntryElement(app: app, target: target)
1213
+ let deadline = Date().addingTimeInterval(timeout)
1214
+ let hardwareKeyboardFallback = Date().addingTimeInterval(
1215
+ min(TextEntryTiming.hardwareKeyboardFallbackTimeout, timeout)
1216
+ )
1217
+ var sawSoftwareKeyboard = false
1218
+ while Date() < deadline {
1219
+ if let focused = focusedTextInput(app: app) {
1220
+ latest = focused
1221
+ if isKeyboardVisible(app: app) {
1222
+ return focused
1223
+ }
1224
+ }
1225
+ sawSoftwareKeyboard = sawSoftwareKeyboard || keyboardElementExists(app: app)
1226
+ if !sawSoftwareKeyboard && Date() >= hardwareKeyboardFallback && latest != nil {
1227
+ return latest
1228
+ }
1229
+ sleepFor(TextEntryTiming.pollInterval)
1230
+ }
1231
+ return focusedTextInput(app: app) ?? latest
1232
+ #else
1233
+ return resolveTextEntryElement(app: app, target: target)
1234
+ #endif
1235
+ }
1236
+
1237
+ private func textEntryRefreshPoint(for element: XCUIElement?) -> CGPoint? {
1238
+ guard let element else {
1239
+ return nil
1240
+ }
1241
+ let frame = element.frame
1242
+ guard !frame.isEmpty else {
1243
+ return nil
1244
+ }
1245
+ return CGPoint(x: frame.midX, y: frame.midY)
1246
+ }
1247
+
1248
+ func isKeyboardVisible(app: XCUIApplication) -> Bool {
1249
+ return visibleKeyboardFrame(app: app) != nil
1250
+ }
1251
+
1252
+ private func keyboardElementExists(app: XCUIApplication) -> Bool {
1253
+ #if os(iOS)
1254
+ var exists = false
1255
+ let exceptionMessage = RunnerObjCExceptionCatcher.catchException({
1256
+ exists = app.keyboards.firstMatch.exists
1257
+ })
1258
+ if let exceptionMessage {
1259
+ NSLog(
1260
+ "AGENT_DEVICE_RUNNER_KEYBOARD_EXISTS_IGNORED_EXCEPTION=%@",
1261
+ exceptionMessage
1262
+ )
1263
+ return false
1264
+ }
1265
+ return exists
1266
+ #else
1267
+ return false
1268
+ #endif
1269
+ }
1270
+
1271
+ func dismissKeyboard(app: XCUIApplication) -> (wasVisible: Bool, dismissed: Bool, visible: Bool) {
1272
+ let wasVisible = isKeyboardVisible(app: app)
1273
+ guard wasVisible else {
1274
+ return (wasVisible: false, dismissed: false, visible: false)
1275
+ }
1276
+
1277
+ #if os(tvOS)
1278
+ _ = pressTvRemote(.menu)
1279
+ sleepFor(0.2)
1280
+ let visible = isKeyboardVisible(app: app)
1281
+ return (wasVisible: true, dismissed: !visible, visible: visible)
1282
+ #else
1283
+ let keyboard = app.keyboards.firstMatch
1284
+ keyboard.swipeDown()
1285
+ sleepFor(0.2)
1286
+ if !isKeyboardVisible(app: app) {
1287
+ return (wasVisible: true, dismissed: true, visible: false)
1288
+ }
1289
+
1290
+ if tapKeyboardDismissControl(app: app) {
1291
+ sleepFor(0.2)
1292
+ let visible = isKeyboardVisible(app: app)
1293
+ return (wasVisible: true, dismissed: !visible, visible: visible)
1294
+ }
1295
+
1296
+ return (wasVisible: true, dismissed: false, visible: isKeyboardVisible(app: app))
1297
+ #endif
1298
+ }
1299
+
1300
+ private func tapKeyboardDismissControl(app: XCUIApplication) -> Bool {
1301
+ #if os(tvOS)
1302
+ return false
1303
+ #else
1304
+ guard let keyboardFrame = visibleKeyboardFrame(app: app) else {
1305
+ return false
1306
+ }
1307
+ for label in ["Hide keyboard", "Dismiss keyboard", "Done"] {
1308
+ let candidates = [
1309
+ app.keyboards.buttons[label],
1310
+ app.keyboards.keys[label],
1311
+ app.keyboards.toolbars.buttons[label],
1312
+ ]
1313
+ if let hittable = candidates.first(where: { $0.exists && $0.isHittable }) {
1314
+ hittable.tap()
1315
+ return true
1316
+ }
1317
+
1318
+ let toolbarButtonPredicate = NSPredicate(
1319
+ format: "label == %@ OR identifier == %@",
1320
+ label,
1321
+ label
1322
+ )
1323
+ let toolbarButtons = app.toolbars.buttons
1324
+ .matching(toolbarButtonPredicate)
1325
+ .allElementsBoundByIndex
1326
+ if let hittable = toolbarButtons.first(where: {
1327
+ $0.exists && $0.isHittable && isKeyboardAccessoryControl($0, keyboardFrame: keyboardFrame)
1328
+ }) {
1329
+ hittable.tap()
1330
+ return true
1331
+ }
1332
+ }
1333
+ return false
1334
+ #endif
1335
+ }
1336
+
1337
+ private func isKeyboardAccessoryControl(_ element: XCUIElement, keyboardFrame: CGRect) -> Bool {
1338
+ let frame = element.frame
1339
+ guard !frame.isEmpty && !keyboardFrame.isEmpty else {
1340
+ return false
1341
+ }
1342
+ return frame.intersects(keyboardFrame) || abs(frame.maxY - keyboardFrame.minY) <= 80
1343
+ }
1344
+
1345
+ private func moveCaretToEnd(element: XCUIElement) {
1346
+ #if os(tvOS)
1347
+ return
1348
+ #else
1349
+ let frame = element.frame
1350
+ guard !frame.isEmpty else {
1351
+ element.tap()
1352
+ return
1353
+ }
1354
+ let origin = element.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0))
1355
+ let target = origin.withOffset(
1356
+ CGVector(dx: max(2, frame.width - 4), dy: max(2, frame.height / 2))
1357
+ )
1358
+ target.tap()
1359
+ #endif
1360
+ }
1361
+
1362
+ private func estimatedDeleteCount(for element: XCUIElement) -> Int {
1363
+ let valueText = normalizedElementText(element.value)
1364
+ let base = valueText.isEmpty ? 24 : (valueText.count + 8)
1365
+ return max(24, min(120, base))
1366
+ }
1367
+
1368
+ private func normalizedElementText(_ value: Any?) -> String {
1369
+ String(describing: value ?? "")
1370
+ .trimmingCharacters(in: .whitespacesAndNewlines)
1371
+ }
1372
+
1373
+ private func editableTextValue(
1374
+ for element: XCUIElement?,
1375
+ treatingPlaceholderAsEmpty: Bool = false
1376
+ ) -> String? {
1377
+ guard let element else {
1378
+ return nil
1379
+ }
1380
+ switch element.elementType {
1381
+ case .textField, .searchField, .textView:
1382
+ let value = String(describing: element.value ?? "")
1383
+ if treatingPlaceholderAsEmpty && isPlaceholderValue(value, for: element) {
1384
+ return ""
1385
+ }
1386
+ return value
1387
+ case .secureTextField:
1388
+ return nil
1389
+ default:
1390
+ return nil
1391
+ }
1392
+ }
1393
+
1394
+ private func isPlaceholderValue(_ value: String, for element: XCUIElement) -> Bool {
1395
+ let normalizedValue = value.trimmingCharacters(in: .whitespacesAndNewlines)
1396
+ guard !normalizedValue.isEmpty else {
1397
+ return false
1398
+ }
1399
+ guard let placeholder = element.placeholderValue?.trimmingCharacters(in: .whitespacesAndNewlines),
1400
+ !placeholder.isEmpty else {
1401
+ return false
1402
+ }
1403
+ return normalizedValue == placeholder
1404
+ }
1405
+
1406
+ private func readableText(for element: XCUIElement) -> String? {
1407
+ let label = element.label.trimmingCharacters(in: .whitespacesAndNewlines)
1408
+ let identifier = element.identifier.trimmingCharacters(in: .whitespacesAndNewlines)
1409
+ let valueText = String(describing: element.value ?? "")
1410
+ .trimmingCharacters(in: .whitespacesAndNewlines)
1411
+ switch element.elementType {
1412
+ case .textField, .secureTextField, .searchField, .textView:
1413
+ if !valueText.isEmpty { return valueText }
1414
+ if !label.isEmpty { return label }
1415
+ return identifier.isEmpty ? nil : identifier
1416
+ default:
1417
+ if !label.isEmpty { return label }
1418
+ if !valueText.isEmpty { return valueText }
1419
+ return identifier.isEmpty ? nil : identifier
1420
+ }
1421
+ }
1422
+
1423
+ private func prefersExpandedTextRead(_ element: XCUIElement) -> Bool {
1424
+ switch element.elementType {
1425
+ case .textField, .secureTextField, .searchField, .textView:
1426
+ return true
1427
+ default:
1428
+ return false
1429
+ }
1430
+ }
1431
+
1432
+ func findScopeElement(app: XCUIApplication, scope: String) -> XCUIElement? {
1433
+ let predicate = NSPredicate(
1434
+ format: "label CONTAINS[c] %@ OR identifier CONTAINS[c] %@",
1435
+ scope,
1436
+ scope
1437
+ )
1438
+ let element = app.descendants(matching: .any).matching(predicate).firstMatch
1439
+ return element.exists ? element : nil
1440
+ }
1441
+
1442
+ func tapAt(app: XCUIApplication, x: Double, y: Double) -> RunnerInteractionOutcome {
1443
+ if let outcome = selectFocusedTvElement(app: app, point: CGPoint(x: x, y: y), action: "tap") {
1444
+ return outcome
1445
+ }
1446
+ return performCoordinateTap(app: app, x: x, y: y)
1447
+ }
1448
+
1449
+ func mouseClickAt(app: XCUIApplication, x: Double, y: Double, button: String) throws {
1450
+ #if os(macOS)
1451
+ let coordinate = interactionCoordinate(app: app, x: x, y: y)
1452
+ switch button {
1453
+ case "primary":
1454
+ coordinate.tap()
1455
+ case "secondary":
1456
+ coordinate.rightClick()
1457
+ case "middle":
1458
+ throw NSError(
1459
+ domain: "AgentDeviceRunner",
1460
+ code: 1,
1461
+ userInfo: [NSLocalizedDescriptionKey: "middle mouse button is not supported"]
1462
+ )
1463
+ default:
1464
+ throw NSError(
1465
+ domain: "AgentDeviceRunner",
1466
+ code: 1,
1467
+ userInfo: [NSLocalizedDescriptionKey: "unsupported mouse button: \(button)"]
1468
+ )
1469
+ }
1470
+ #elseif os(tvOS)
1471
+ throw NSError(
1472
+ domain: "AgentDeviceRunner",
1473
+ code: 1,
1474
+ userInfo: [NSLocalizedDescriptionKey: "mouseClick is not supported on tvOS"]
1475
+ )
1476
+ #else
1477
+ throw NSError(
1478
+ domain: "AgentDeviceRunner",
1479
+ code: 1,
1480
+ userInfo: [NSLocalizedDescriptionKey: "mouseClick is only supported on macOS"]
1481
+ )
1482
+ #endif
1483
+ }
1484
+
1485
+ func doubleTapAt(app: XCUIApplication, x: Double, y: Double) -> RunnerInteractionOutcome {
1486
+ if let outcome = selectFocusedTvElement(app: app, point: CGPoint(x: x, y: y), action: "double tap") {
1487
+ guard case .performed = outcome else { return outcome }
1488
+ sleepFor(0.1)
1489
+ _ = pressTvRemote(.select)
1490
+ return .performed
1491
+ }
1492
+ return performCoordinateDoubleTap(app: app, x: x, y: y)
1493
+ }
1494
+
1495
+ func longPressAt(app: XCUIApplication, x: Double, y: Double, duration: TimeInterval) -> RunnerInteractionOutcome {
1496
+ if let outcome = longSelectFocusedTvElement(app: app, point: CGPoint(x: x, y: y), duration: duration) {
1497
+ return outcome
1498
+ }
1499
+ return performCoordinateLongPress(app: app, x: x, y: y, duration: duration)
1500
+ }
1501
+
1502
+ func dragAt(
1503
+ app: XCUIApplication,
1504
+ x: Double,
1505
+ y: Double,
1506
+ x2: Double,
1507
+ y2: Double,
1508
+ holdDuration: TimeInterval,
1509
+ moveDuration: TimeInterval? = nil
1510
+ ) -> RunnerInteractionOutcome {
1511
+ // tvOS has no coordinate drag. Preserve the direction as a focus move.
1512
+ let dx = x2 - x
1513
+ let dy = y2 - y
1514
+ let button: TvRemoteButton = abs(dx) > abs(dy)
1515
+ ? (dx > 0 ? .right : .left)
1516
+ : (dy > 0 ? .down : .up)
1517
+ if pressTvRemote(button) {
1518
+ return .performed
1519
+ }
1520
+ return performCoordinateDrag(
1521
+ app: app, x: x, y: y, x2: x2, y2: y2,
1522
+ holdDuration: holdDuration, moveDuration: moveDuration)
1523
+ }
1524
+
1525
+ func keyboardAvoidingDragPoints(
1526
+ app: XCUIApplication,
1527
+ x: Double,
1528
+ y: Double,
1529
+ x2: Double,
1530
+ y2: Double
1531
+ ) -> DragPoints {
1532
+ let original = DragPoints(x: x, y: y, x2: x2, y2: y2)
1533
+ #if os(iOS)
1534
+ guard let keyboardFrame = visibleKeyboardFrame(app: app) else {
1535
+ return original
1536
+ }
1537
+ let minX = min(x, x2)
1538
+ let minY = min(y, y2)
1539
+ let gestureBounds = CGRect(
1540
+ x: CGFloat(minX),
1541
+ y: CGFloat(minY),
1542
+ width: CGFloat(max(abs(x2 - x), 1)),
1543
+ height: CGFloat(max(abs(y2 - y), 1))
1544
+ )
1545
+ guard gestureBounds.intersects(keyboardFrame) else {
1546
+ return original
1547
+ }
1548
+
1549
+ let window = app.windows.firstMatch
1550
+ let appFrame = window.exists && !window.frame.isEmpty ? window.frame : app.frame
1551
+ guard !appFrame.isEmpty else {
1552
+ return original
1553
+ }
1554
+
1555
+ let padding: Double = 12
1556
+ let targetMaxY = Double(keyboardFrame.minY) - padding
1557
+ let currentMaxY = max(y, y2)
1558
+ let shift = currentMaxY - targetMaxY
1559
+ guard shift > 0 else {
1560
+ return original
1561
+ }
1562
+
1563
+ let adjustedY = y - shift
1564
+ let adjustedY2 = y2 - shift
1565
+ guard min(adjustedY, adjustedY2) >= Double(appFrame.minY) + padding else {
1566
+ return original
1567
+ }
1568
+
1569
+ NSLog(
1570
+ "AGENT_DEVICE_RUNNER_KEYBOARD_AVOIDING_DRAG from=(%.1f,%.1f)->(%.1f,%.1f) adjusted=(%.1f,%.1f)->(%.1f,%.1f) keyboardMinY=%.1f",
1571
+ x,
1572
+ y,
1573
+ x2,
1574
+ y2,
1575
+ x,
1576
+ adjustedY,
1577
+ x2,
1578
+ adjustedY2,
1579
+ Double(keyboardFrame.minY)
1580
+ )
1581
+ return DragPoints(x: x, y: adjustedY, x2: x2, y2: adjustedY2)
1582
+ #else
1583
+ return original
1584
+ #endif
1585
+ }
1586
+
1587
+ func resolvedTouchVisualizationFrame(app: XCUIApplication, x: Double, y: Double) -> TouchVisualizationFrame {
1588
+ let appFrame = app.frame
1589
+ let referenceFrame = resolvedTouchReferenceFrame(app: app, appFrame: appFrame)
1590
+ let originX = appFrame.isEmpty ? referenceFrame.minX : appFrame.minX
1591
+ let originY = appFrame.isEmpty ? referenceFrame.minY : appFrame.minY
1592
+ return TouchVisualizationFrame(
1593
+ x: originX + x,
1594
+ y: originY + y,
1595
+ referenceWidth: referenceFrame.width,
1596
+ referenceHeight: referenceFrame.height
1597
+ )
1598
+ }
1599
+
1600
+ func resolvedDragVisualizationFrame(
1601
+ app: XCUIApplication,
1602
+ x: Double,
1603
+ y: Double,
1604
+ x2: Double,
1605
+ y2: Double
1606
+ ) -> DragVisualizationFrame {
1607
+ let start = resolvedTouchVisualizationFrame(app: app, x: x, y: y)
1608
+ let end = resolvedTouchVisualizationFrame(app: app, x: x2, y: y2)
1609
+ return DragVisualizationFrame(
1610
+ x: start.x,
1611
+ y: start.y,
1612
+ x2: end.x,
1613
+ y2: end.y,
1614
+ referenceWidth: start.referenceWidth,
1615
+ referenceHeight: start.referenceHeight
1616
+ )
1617
+ }
1618
+
1619
+ func resolvedTouchReferenceFrame(app: XCUIApplication, appFrame: CGRect) -> CGRect {
1620
+ let window = app.windows.firstMatch
1621
+ if window.exists {
1622
+ let windowFrame = window.frame
1623
+ if !windowFrame.isEmpty {
1624
+ return frameAvoidingKeyboard(app: app, frame: windowFrame)
1625
+ }
1626
+ }
1627
+ if !appFrame.isEmpty {
1628
+ return frameAvoidingKeyboard(app: app, frame: appFrame)
1629
+ }
1630
+ return CGRect(x: 0, y: 0, width: 0, height: 0)
1631
+ }
1632
+
1633
+ private func frameAvoidingKeyboard(app: XCUIApplication, frame: CGRect) -> CGRect {
1634
+ #if os(iOS)
1635
+ guard let keyboardFrame = visibleKeyboardFrame(app: app), !frame.isEmpty else {
1636
+ return frame
1637
+ }
1638
+ let intersection = frame.intersection(keyboardFrame)
1639
+ guard !intersection.isNull && intersection.height > 0 else {
1640
+ return frame
1641
+ }
1642
+ let keyboardCoverage = intersection.width / max(frame.width, 1)
1643
+ guard keyboardCoverage >= 0.5 else {
1644
+ return frame
1645
+ }
1646
+ let safeHeight = keyboardFrame.minY - frame.minY
1647
+ guard safeHeight >= frame.height * 0.25 else {
1648
+ return frame
1649
+ }
1650
+ return CGRect(x: frame.minX, y: frame.minY, width: frame.width, height: safeHeight)
1651
+ #else
1652
+ return frame
1653
+ #endif
1654
+ }
1655
+
1656
+ private func visibleKeyboardFrame(app: XCUIApplication) -> CGRect? {
1657
+ #if os(iOS)
1658
+ var frame: CGRect?
1659
+ let exceptionMessage = RunnerObjCExceptionCatcher.catchException({
1660
+ let keyboard = app.keyboards.firstMatch
1661
+ guard keyboard.exists else { return }
1662
+ let keyboardFrame = keyboard.frame
1663
+ guard !keyboardFrame.isEmpty else { return }
1664
+ frame = keyboardFrame
1665
+ })
1666
+ if let exceptionMessage {
1667
+ NSLog(
1668
+ "AGENT_DEVICE_RUNNER_KEYBOARD_FRAME_IGNORED_EXCEPTION=%@",
1669
+ exceptionMessage
1670
+ )
1671
+ return nil
1672
+ }
1673
+ return frame
1674
+ #else
1675
+ return nil
1676
+ #endif
1677
+ }
1678
+
1679
+ func runSeries(count: Int, pauseMs: Double, operation: (Int) -> Void) {
1680
+ let total = max(count, 1)
1681
+ let pause = max(pauseMs, 0)
1682
+ for idx in 0..<total {
1683
+ operation(idx)
1684
+ if idx < total - 1 && pause > 0 {
1685
+ sleepFor(pause / 1000.0)
1686
+ }
1687
+ }
1688
+ }
1689
+
1690
+ func swipe(app: XCUIApplication, direction: String) -> DragVisualizationFrame? {
1691
+ if performTvRemoteSwipeIfAvailable(direction: direction) {
1692
+ let frame = resolvedTouchReferenceFrame(app: app, appFrame: app.frame)
1693
+ let midX = frame.midX
1694
+ let midY = frame.midY
1695
+ return DragVisualizationFrame(
1696
+ x: midX,
1697
+ y: midY,
1698
+ x2: midX,
1699
+ y2: midY,
1700
+ referenceWidth: frame.width,
1701
+ referenceHeight: frame.height
1702
+ )
1703
+ }
1704
+ return nil
1705
+ }
1706
+
1707
+ private func performTvRemoteSwipeIfAvailable(direction: String) -> Bool {
1708
+ switch direction {
1709
+ case "up":
1710
+ return pressTvRemote(.up)
1711
+ case "down":
1712
+ return pressTvRemote(.down)
1713
+ case "left":
1714
+ return pressTvRemote(.left)
1715
+ case "right":
1716
+ return pressTvRemote(.right)
1717
+ default:
1718
+ return false
1719
+ }
1720
+ }
1721
+
1722
+ func pinch(app: XCUIApplication, scale: Double, x: Double?, y: Double?) -> RunnerInteractionOutcome {
1723
+ return performCoordinatePinch(app: app, scale: scale, x: x, y: y)
1724
+ }
1725
+
1726
+ private func performCoordinatePinch(app: XCUIApplication, scale: Double, x: Double?, y: Double?) -> RunnerInteractionOutcome {
1727
+ #if os(tvOS)
1728
+ return .unsupported("pinch is not supported on tvOS")
1729
+ #else
1730
+ let target = app.windows.firstMatch.exists ? app.windows.firstMatch : app
1731
+
1732
+ // Use double-tap + drag gesture for reliable map zoom
1733
+ // Zoom in (scale > 1): tap then drag UP
1734
+ // Zoom out (scale < 1): tap then drag DOWN
1735
+
1736
+ // Determine center point (use provided x/y or screen center)
1737
+ let centerX = x.map { $0 / target.frame.width } ?? 0.5
1738
+ let centerY = y.map { $0 / target.frame.height } ?? 0.5
1739
+ let center = target.coordinate(withNormalizedOffset: CGVector(dx: centerX, dy: centerY))
1740
+
1741
+ // Calculate drag distance based on scale (clamped to reasonable range)
1742
+ // Larger scale = more drag distance
1743
+ let dragAmount: CGFloat
1744
+ if scale > 1.0 {
1745
+ // Zoom in: drag up (negative Y direction in normalized coords)
1746
+ dragAmount = min(0.4, CGFloat(scale - 1.0) * 0.2)
1747
+ } else {
1748
+ // Zoom out: drag down (positive Y direction)
1749
+ dragAmount = min(0.4, CGFloat(1.0 - scale) * 0.4)
1750
+ }
1751
+
1752
+ let endY = scale > 1.0 ? (centerY - Double(dragAmount)) : (centerY + Double(dragAmount))
1753
+ let endPoint = target.coordinate(withNormalizedOffset: CGVector(dx: centerX, dy: max(0.1, min(0.9, endY))))
1754
+
1755
+ // Tap first (first tap of double-tap)
1756
+ center.tap()
1757
+
1758
+ // Immediately press and drag (second tap + drag)
1759
+ center.press(forDuration: 0.05, thenDragTo: endPoint)
1760
+ return .performed
1761
+ #endif
1762
+ }
1763
+
1764
+ private func interactionRoot(app: XCUIApplication) -> XCUIElement {
1765
+ let windows = app.windows.allElementsBoundByIndex
1766
+ if let window = windows.first(where: { $0.exists && !$0.frame.isEmpty }) {
1767
+ return window
1768
+ }
1769
+ return app
1770
+ }
1771
+
1772
+ private func performCoordinateTap(app: XCUIApplication, x: Double, y: Double) -> RunnerInteractionOutcome {
1773
+ #if os(tvOS)
1774
+ return .unsupported("coordinate tap is not supported on tvOS; move focus with swipe or scroll, then select the focused element")
1775
+ #else
1776
+ interactionCoordinate(app: app, x: x, y: y).tap()
1777
+ return .performed
1778
+ #endif
1779
+ }
1780
+
1781
+ private func performCoordinateDoubleTap(app: XCUIApplication, x: Double, y: Double) -> RunnerInteractionOutcome {
1782
+ #if os(tvOS)
1783
+ return .unsupported("coordinate double tap is not supported on tvOS; move focus with swipe or scroll, then select the focused element")
1784
+ #else
1785
+ interactionCoordinate(app: app, x: x, y: y).doubleTap()
1786
+ return .performed
1787
+ #endif
1788
+ }
1789
+
1790
+ private func performCoordinateLongPress(app: XCUIApplication, x: Double, y: Double, duration: TimeInterval) -> RunnerInteractionOutcome {
1791
+ #if os(tvOS)
1792
+ return .unsupported("coordinate long press is not supported on tvOS; move focus with swipe or scroll, then long-select the focused element")
1793
+ #else
1794
+ interactionCoordinate(app: app, x: x, y: y).press(forDuration: duration)
1795
+ return .performed
1796
+ #endif
1797
+ }
1798
+
1799
+ private func performCoordinateDrag(
1800
+ app: XCUIApplication,
1801
+ x: Double,
1802
+ y: Double,
1803
+ x2: Double,
1804
+ y2: Double,
1805
+ holdDuration: TimeInterval,
1806
+ moveDuration: TimeInterval? = nil
1807
+ ) -> RunnerInteractionOutcome {
1808
+ #if os(tvOS)
1809
+ return .unsupported("coordinate drag is not supported on tvOS")
1810
+ #else
1811
+ let start = interactionCoordinate(app: app, x: x, y: y)
1812
+ let end = interactionCoordinate(app: app, x: x2, y: y2)
1813
+ if let moveDuration, moveDuration > 0 {
1814
+ // XCUITest exposes drag speed as velocity, so derive it from the
1815
+ // requested movement time. The trailing hold lets the drop target
1816
+ // register the finger before release — releasing mid-motion reads
1817
+ // as a fling to reorder UIs.
1818
+ let distance = (CGFloat(x2 - x) * CGFloat(x2 - x) + CGFloat(y2 - y) * CGFloat(y2 - y)).squareRoot()
1819
+ let velocity = XCUIGestureVelocity(max(distance / CGFloat(moveDuration), 1))
1820
+ start.press(
1821
+ forDuration: holdDuration,
1822
+ thenDragTo: end,
1823
+ withVelocity: velocity,
1824
+ thenHoldForDuration: 0.25
1825
+ )
1826
+ } else {
1827
+ start.press(forDuration: holdDuration, thenDragTo: end)
1828
+ }
1829
+ return .performed
1830
+ #endif
1831
+ }
1832
+
1833
+ #if !os(tvOS)
1834
+ private func interactionCoordinate(app: XCUIApplication, x: Double, y: Double) -> XCUICoordinate {
1835
+ let root = interactionRoot(app: app)
1836
+ let origin = root.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0))
1837
+ let rootFrame = root.frame
1838
+ let offsetX = x - Double(rootFrame.origin.x)
1839
+ let offsetY = y - Double(rootFrame.origin.y)
1840
+ return origin.withOffset(CGVector(dx: offsetX, dy: offsetY))
1841
+ }
1842
+ #endif
1843
+
1844
+ private func tapElementCenter(app: XCUIApplication, element: XCUIElement) {
1845
+ let frame = element.frame
1846
+ if !frame.isEmpty {
1847
+ _ = tapAt(app: app, x: frame.midX, y: frame.midY)
1848
+ return
1849
+ }
1850
+ #if !os(tvOS)
1851
+ element.tap()
1852
+ #endif
1853
+ }
1854
+
1855
+ private func macOSNavigationBackElement(app: XCUIApplication) -> XCUIElement? {
1856
+ let predicate = NSPredicate(
1857
+ format: "identifier == %@ OR label == %@",
1858
+ "go back",
1859
+ "Back"
1860
+ )
1861
+ let element = app.descendants(matching: .any).matching(predicate).firstMatch
1862
+ return element.exists ? element : nil
1863
+ }
1864
+ }