catalyst-core 0.3.0-beta.4 → 0.3.0-canary.1

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 (316) hide show
  1. package/changelog.md +80 -0
  2. package/dist/errors/generateDocs.js +100 -0
  3. package/dist/errors/index.js +243 -0
  4. package/dist/errors/registry.js +483 -0
  5. package/dist/errors-index.json +506 -0
  6. package/dist/globals.d.ts +62 -0
  7. package/dist/index.d.ts +5 -0
  8. package/dist/index.jsx +5 -8
  9. package/dist/logger.js +1 -1
  10. package/dist/native/androidProject/app/build.gradle.kts +142 -7
  11. package/dist/native/androidProject/app/src/main/AndroidManifest.xml +4 -0
  12. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +2 -2
  13. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/BarcodeDetector.kt +3 -0
  14. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/CameraSessionManager.kt +3 -0
  15. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/FrameworkServerUtils.kt +43 -8
  16. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/BridgeUtilsTest.kt +181 -1
  17. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/CameraUtilsTest.kt +156 -236
  18. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FileSizeRouterUtilsTest.kt +471 -0
  19. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FileUtilsTest.kt +222 -226
  20. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FrameworkServerUtilsTest.kt +300 -0
  21. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/IntentUtilsTest.kt +244 -0
  22. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/MetricsMonitorTest.kt +359 -0
  23. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/NativeBridgeTest.kt +108 -63
  24. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/NotificationUtilsTest.kt +329 -385
  25. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/OfflineCacheServiceTest.kt +302 -0
  26. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/PerfEventBufferTest.kt +133 -0
  27. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/UtilsKtTest.kt +152 -0
  28. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/WebCacheManagerTest.kt +145 -324
  29. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/BarcodeDetectorTest.kt +132 -0
  30. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/HoldControllerTest.kt +155 -0
  31. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/TorchControllerTest.kt +150 -0
  32. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/VideoStreamStateMachineTest.kt +120 -0
  33. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/VideoStreamStateTest.kt +62 -0
  34. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/ViewfinderMapperTest.kt +143 -0
  35. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/ZoomControllerTest.kt +421 -0
  36. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/design/DesignTokensTest.kt +207 -0
  37. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/plugins/PluginBridgeTest.kt +368 -0
  38. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/security/FridaDetectorTest.kt +193 -0
  39. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/security/SecurityCheckManagerTest.kt +260 -0
  40. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/security/SecurityCheckSchedulerTest.kt +166 -0
  41. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/DeviceInfoUtilsTest.kt +167 -0
  42. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/DownloadUtilsTest.kt +197 -0
  43. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/NetworkMonitorTest.kt +271 -0
  44. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/PushNotificationUtilsTest.kt +119 -0
  45. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/SafeAreaUtilsTest.kt +108 -0
  46. package/dist/native/androidProject/build.gradle.kts +1 -0
  47. package/dist/native/androidProject/gradle/libs.versions.toml +26 -0
  48. package/dist/native/androidProject/settings.gradle.kts +11 -8
  49. package/dist/native/androidSetup.js +2 -2
  50. package/dist/native/bridge/WebBridge.d.ts +98 -0
  51. package/dist/native/bridge/WebBridge.js +40 -6
  52. package/dist/native/bridge/constants/NativeInterfaces.d.ts +391 -0
  53. package/dist/native/bridge/errors.d.ts +67 -0
  54. package/dist/native/bridge/errors.js +66 -151
  55. package/dist/native/bridge/hooks/index.d.ts +13 -0
  56. package/dist/native/bridge/hooks/index.js +0 -6
  57. package/dist/native/bridge/hooks/useAI.d.ts +1 -0
  58. package/dist/native/bridge/hooks/useAI.js +3 -1
  59. package/dist/native/bridge/hooks/useCamera.d.ts +86 -0
  60. package/dist/native/bridge/hooks/useCamera.js +35 -6
  61. package/dist/native/bridge/hooks/useDataProtection.d.ts +17 -0
  62. package/dist/native/bridge/hooks/useDeviceInfo.d.ts +34 -0
  63. package/dist/native/bridge/hooks/useDeviceInfo.js +5 -0
  64. package/dist/native/bridge/hooks/useFilePicker.d.ts +53 -0
  65. package/dist/native/bridge/hooks/useFilePicker.js +13 -0
  66. package/dist/native/bridge/hooks/useGoogleSignIn.d.ts +12 -0
  67. package/dist/native/bridge/hooks/useHapticFeedback.d.ts +76 -0
  68. package/dist/native/bridge/hooks/useHapticFeedback.js +6 -1
  69. package/dist/native/bridge/hooks/useIntent.d.ts +33 -0
  70. package/dist/native/bridge/hooks/useIntent.js +2 -0
  71. package/dist/native/bridge/hooks/useNativeTransition.d.ts +53 -0
  72. package/dist/native/bridge/hooks/useNativeTransition.js +14 -7
  73. package/dist/native/bridge/hooks/useNetworkStatus.d.ts +22 -0
  74. package/dist/native/bridge/hooks/useNetworkStatus.js +7 -0
  75. package/dist/native/bridge/hooks/useNotification.d.ts +83 -0
  76. package/dist/native/bridge/hooks/useNotification.js +81 -34
  77. package/dist/native/bridge/hooks/useSafeArea.d.ts +12 -0
  78. package/dist/native/bridge/hooks/useSafeArea.js +8 -0
  79. package/dist/native/bridge/hooks/useVideoStream.d.ts +67 -0
  80. package/dist/native/bridge/hooks/useVideoStream.js +38 -52
  81. package/dist/native/bridge/hooks/utils.d.ts +4 -0
  82. package/dist/native/bridge/hooks.d.ts +1 -0
  83. package/dist/native/bridge/perf/collectors/ApiCollector.d.ts +30 -0
  84. package/dist/native/bridge/perf/collectors/ApiCollector.js +4 -0
  85. package/dist/native/bridge/perf/collectors/CacheCollector.d.ts +43 -0
  86. package/dist/native/bridge/perf/collectors/CacheCollector.js +4 -0
  87. package/dist/native/bridge/perf/collectors/HookCollector.d.ts +37 -0
  88. package/dist/native/bridge/perf/collectors/HookCollector.js +4 -0
  89. package/dist/native/bridge/perf/collectors/InsightsCollector.d.ts +103 -0
  90. package/dist/native/bridge/perf/collectors/InsightsCollector.js +4 -0
  91. package/dist/native/bridge/perf/collectors/InteractionCollector.d.ts +53 -0
  92. package/dist/native/bridge/perf/collectors/InteractionCollector.js +4 -0
  93. package/dist/native/bridge/perf/collectors/KeyboardCollector.d.ts +30 -0
  94. package/dist/native/bridge/perf/collectors/KeyboardCollector.js +4 -0
  95. package/dist/native/bridge/perf/collectors/NavigationCollector.d.ts +37 -0
  96. package/dist/native/bridge/perf/collectors/NavigationCollector.js +4 -0
  97. package/dist/native/bridge/perf/collectors/NetworkTimingCollector.d.ts +30 -0
  98. package/dist/native/bridge/perf/collectors/NetworkTimingCollector.js +4 -0
  99. package/dist/native/bridge/perf/collectors/RenderCollector.d.ts +47 -0
  100. package/dist/native/bridge/perf/collectors/RenderCollector.js +8 -0
  101. package/dist/native/bridge/perf/collectors/ScrollCollector.d.ts +29 -0
  102. package/dist/native/bridge/perf/collectors/ScrollCollector.js +4 -0
  103. package/dist/native/bridge/perf/collectors/ThreadCollector.d.ts +31 -0
  104. package/dist/native/bridge/perf/collectors/ThreadCollector.js +4 -0
  105. package/dist/native/bridge/perf/core/constants.d.ts +76 -0
  106. package/dist/native/bridge/perf/core/measure.d.ts +39 -0
  107. package/dist/native/bridge/perf/core/measure.js +4 -0
  108. package/dist/native/bridge/perf/core/session.d.ts +49 -0
  109. package/dist/native/bridge/perf/core/session.js +4 -0
  110. package/dist/native/bridge/perf/core/store.d.ts +37 -0
  111. package/dist/native/bridge/perf/core/store.js +4 -0
  112. package/dist/native/bridge/perf/core/summary.d.ts +60 -0
  113. package/dist/native/bridge/perf/core/traceExport.d.ts +10 -0
  114. package/dist/native/bridge/perf/core/waterfalls.d.ts +2 -0
  115. package/dist/native/bridge/perf/index.d.ts +63 -0
  116. package/dist/native/bridge/perf/index.js +4 -0
  117. package/dist/native/bridge/safeArea.d.ts +11 -0
  118. package/dist/native/bridge/safeArea.js +2 -0
  119. package/dist/native/bridge/useBaseHook.d.ts +137 -0
  120. package/dist/native/bridge/useBaseHook.js +34 -4
  121. package/dist/native/bridge/utils/CameraUtils.d.ts +94 -0
  122. package/dist/native/bridge/utils/CameraUtils.js +4 -0
  123. package/dist/native/bridge/utils/FileObjectConverter.d.ts +36 -0
  124. package/dist/native/bridge/utils/NativeBridge.d.ts +270 -0
  125. package/dist/native/bridge/utils/NativeBridge.js +10 -0
  126. package/dist/native/buildAndroid/index.js +1 -1
  127. package/dist/native/buildAppAndroid.js +9 -0
  128. package/dist/native/buildAppIos.js +16 -1
  129. package/dist/native/buildErrorFormat.js +57 -0
  130. package/dist/native/buildIos/config.js +15 -1
  131. package/dist/native/buildIos/index.js +1 -1
  132. package/dist/native/buildIos/plugins.js +10 -1
  133. package/dist/native/iosnativeWebView/CoreLogic/Package.swift +36 -0
  134. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Camera/VideoStreamState.swift +1 -1
  135. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Camera/VideoStreamStateMachine.swift +6 -4
  136. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/BootTimingUtility.swift +3 -3
  137. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/FrameworkServerUtils.swift +14 -9
  138. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/NetworkMonitor.swift +11 -9
  139. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/NotificationHandlerProvider.swift +1 -1
  140. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/URLWhitelistManager.swift +7 -7
  141. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/AnyCodableTests.swift +154 -0
  142. package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/BootTimingUtilityTests.swift +23 -5
  143. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/CatalystConstantsTests.swift +192 -0
  144. package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/ConfigMappingTests.swift +1 -1
  145. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/FrameworkServerUtilsErrorPathTests.swift +106 -0
  146. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/FrameworkServerUtilsLoopbackTests.swift +243 -0
  147. package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/FrameworkServerUtilsTests.swift +1 -1
  148. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/NetworkMonitorTests.swift +119 -0
  149. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/NotificationHandlerProviderTests.swift +59 -0
  150. package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/NotificationHandlerTests.swift +1 -1
  151. package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/URLWhitelistManagerTests.swift +1 -1
  152. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/VideoStreamStateMachineTests.swift +171 -0
  153. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/VideoStreamStateTests.swift +91 -0
  154. package/dist/native/iosnativeWebView/Package.swift +10 -9
  155. package/dist/native/iosnativeWebView/Sources/CatalystNotifications/LocalNotificationHandler.swift +7 -2
  156. package/dist/native/iosnativeWebView/Sources/CatalystNotifications/NotificationManager.swift +1 -2
  157. package/dist/native/iosnativeWebView/Sources/CatalystNotifications/PushNotificationHandler.swift +5 -4
  158. package/dist/native/iosnativeWebView/Sources/Core/Camera/CameraSessionManager.swift +1 -0
  159. package/dist/native/iosnativeWebView/Sources/Core/Camera/HoldController.swift +1 -0
  160. package/dist/native/iosnativeWebView/Sources/Core/Camera/NativeCameraManager.swift +6 -5
  161. package/dist/native/iosnativeWebView/Sources/Core/Camera/ZoomController.swift +1 -0
  162. package/dist/native/iosnativeWebView/Sources/Core/Plugins/PluginBridge.swift +1 -0
  163. package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +31 -30
  164. package/dist/native/iosnativeWebView/Sources/Core/Utils/CameraTransportUtils.swift +2 -1
  165. package/dist/native/iosnativeWebView/Sources/Core/Utils/DeviceInfoUtils.swift +1 -1
  166. package/dist/native/iosnativeWebView/Sources/Core/Utils/FilePickerHandler.swift +22 -19
  167. package/dist/native/iosnativeWebView/Sources/Core/Utils/ImageHandler.swift +40 -39
  168. package/dist/native/iosnativeWebView/Sources/Core/Utils/PerfEventBuffer.swift +1 -0
  169. package/dist/native/iosnativeWebView/Sources/Core/Utils/ResourceURLProtocol.swift +15 -15
  170. package/dist/native/iosnativeWebView/Sources/Core/Utils/WebKitConfig.swift +4 -3
  171. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeCommandHandler.swift +6 -1
  172. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeDelegateHandler.swift +1 -0
  173. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeFileHandler.swift +2 -1
  174. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeJavaScriptInterface.swift +2 -1
  175. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeMessageValidator.swift +3 -5
  176. package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +20 -13
  177. package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashView.swift +6 -5
  178. package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashViewModel.swift +14 -13
  179. package/dist/native/iosnativeWebView/Sources/Core/WebView/TransitionManager.swift +3 -3
  180. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +9 -8
  181. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewModel.swift +7 -6
  182. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +24 -25
  183. package/dist/native/iosnativeWebView/iosnativeWebView/AppDelegate.swift +6 -1
  184. package/dist/native/iosnativeWebView/iosnativeWebView/ContentView.swift +1 -0
  185. package/dist/native/iosnativeWebView/iosnativeWebView/iosnativeWebViewApp.swift +4 -0
  186. package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +2 -22
  187. package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeMessageValidatorTests.swift +1 -0
  188. package/dist/native/iosnativeWebView/iosnativeWebViewTests/CacheManagerTests.swift +1 -0
  189. package/dist/native/iosnativeWebView/iosnativeWebViewTests/FilePickerHandlerTests.swift +2 -1
  190. package/dist/native/iosnativeWebView/iosnativeWebViewTests/NativeBridgeTests.swift +15 -9
  191. package/dist/native/iosnativeWebView/iosnativeWebViewTests/WebViewTests.swift +1 -0
  192. package/dist/native/setupEmulatorIos.js +2 -2
  193. package/dist/native/terminalProgress.js +17 -17
  194. package/dist/router/ClientRouter.d.ts +8 -0
  195. package/dist/router/ClientRouter.js +13 -11
  196. package/dist/router/ServerRouter.d.ts +13 -0
  197. package/dist/router/ServerRouter.js +19 -7
  198. package/dist/scripts/build.js +14 -2
  199. package/dist/scripts/loadEnvironmentVariables.js +3 -2
  200. package/dist/scripts/plugins.js +1 -1
  201. package/dist/scripts/preServerInit.js +49 -3
  202. package/dist/scripts/preflight.js +111 -0
  203. package/dist/scripts/scriptUtils.js +46 -1
  204. package/dist/scripts/serve.js +7 -1
  205. package/dist/scripts/start.js +8 -1
  206. package/dist/server/expressServer.d.ts +1 -0
  207. package/dist/server/expressServer.js +247 -271
  208. package/dist/server/manifestCache.d.ts +2 -0
  209. package/dist/server/manifestCache.js +25 -33
  210. package/dist/server/renderer/ChunkExtractor.d.ts +44 -0
  211. package/dist/server/renderer/ChunkExtractor.js +113 -125
  212. package/dist/server/renderer/document/Body.d.ts +21 -0
  213. package/dist/server/renderer/document/Body.jsx +45 -45
  214. package/dist/server/renderer/document/FastRefresh.d.ts +3 -0
  215. package/dist/server/renderer/document/FastRefresh.jsx +7 -13
  216. package/dist/server/renderer/document/Head.d.ts +20 -0
  217. package/dist/server/renderer/document/Head.jsx +32 -32
  218. package/dist/server/renderer/document/index.d.ts +2 -0
  219. package/dist/server/renderer/document/index.jsx +2 -2
  220. package/dist/server/renderer/extract.d.ts +67 -0
  221. package/dist/server/renderer/extract.js +92 -89
  222. package/dist/server/renderer/handler.d.ts +2 -0
  223. package/dist/server/renderer/handler.jsx +401 -404
  224. package/dist/server/renderer/index.d.ts +2 -0
  225. package/dist/server/renderer/index.js +5 -8
  226. package/dist/server/renderer/render.d.ts +35 -0
  227. package/dist/server/renderer/render.js +33 -32
  228. package/dist/server/utils/botDetectionMiddleware.d.ts +1 -0
  229. package/dist/server/utils/botDetectionMiddleware.js +33 -23
  230. package/dist/server/utils/cjsRequire.d.ts +8 -0
  231. package/dist/server/utils/cjsRequire.js +2 -2
  232. package/dist/server/utils/userAgentUtil.d.ts +7 -0
  233. package/dist/server/utils/userAgentUtil.js +68 -71
  234. package/dist/server/utils/validator.d.ts +31 -0
  235. package/dist/server/utils/validator.js +133 -158
  236. package/dist/vite/inject-cache-key-plugin.js +3 -27
  237. package/dist/vite/moduleAliases.js +36 -0
  238. package/dist/vite/node-loader.mjs +2 -29
  239. package/dist/vite/resolveDevServerConfig.js +4 -1
  240. package/dist/vite/vite.config.js +13 -31
  241. package/dist/web-router/components/MetaTag.d.ts +4 -0
  242. package/dist/web-router/components/MetaTag.jsx +26 -28
  243. package/dist/web-router/components/MetaTag.test.jsx +100 -0
  244. package/dist/web-router/components/RouterDataProvider.d.ts +96 -0
  245. package/dist/web-router/components/RouterDataProvider.jsx +260 -259
  246. package/dist/web-router/components/RouterDataProvider.server.test.jsx +98 -0
  247. package/dist/web-router/components/RouterDataProvider.test.jsx +310 -0
  248. package/dist/web-router/components/Split.d.ts +74 -0
  249. package/dist/web-router/components/Split.jsx +147 -156
  250. package/dist/web-router/components/Split.server.test.jsx +52 -0
  251. package/dist/web-router/components/Split.test.jsx +167 -0
  252. package/dist/web-router/components/SplitInview.d.ts +22 -0
  253. package/dist/web-router/components/SplitInview.jsx +78 -89
  254. package/dist/web-router/components/SplitInview.test.jsx +112 -0
  255. package/dist/web-router/components/SsrRequestContext.d.ts +20 -0
  256. package/dist/web-router/components/SsrRequestContext.jsx +17 -7
  257. package/dist/web-router/components/SsrRequestContext.test.jsx +53 -0
  258. package/dist/web-router/context.d.ts +15 -0
  259. package/dist/web-router/context.jsx +5 -5
  260. package/dist/web-router/context.test.jsx +10 -0
  261. package/dist/web-router/utils/metaDataUtils.d.ts +24 -0
  262. package/dist/web-router/utils/metaDataUtils.jsx +86 -86
  263. package/dist/web-router/utils/metaDataUtils.test.jsx +188 -0
  264. package/dist/web-router/vitest.setup.js +12 -0
  265. package/mcp_v2/knowledge-base.json +52 -8
  266. package/mcp_v2/mcp.js +140 -3
  267. package/mcp_v2/package-lock.json +2031 -0
  268. package/mcp_v2/package.json +13 -2
  269. package/mcp_v2/scripts/copyErrorsIndex.cjs +34 -0
  270. package/mcp_v2/test/errors.test.ts +123 -0
  271. package/mcp_v2/test/github.test.ts +177 -0
  272. package/mcp_v2/tools/errors.d.ts +20 -0
  273. package/mcp_v2/tools/errors.js +63 -0
  274. package/mcp_v2/tools/github.d.ts +41 -0
  275. package/mcp_v2/tools/github.js +231 -0
  276. package/mcp_v2/vitest.config.mjs +22 -0
  277. package/package.json +60 -11
  278. package/test/errors/docsDrift.test.ts +113 -0
  279. package/test/errors/index.test.ts +200 -0
  280. package/test/errors/registry.test.ts +108 -0
  281. package/test/scripts/preflight.test.ts +121 -0
  282. package/test/server/ChunkExtractor.test.ts +97 -0
  283. package/test/server/extract.test.ts +128 -0
  284. package/test/server/fixtures/template/server/document.jsx +19 -0
  285. package/test/server/fixtures/template/server/index.js +10 -0
  286. package/test/server/fixtures/template/src/js/containers/App/index.jsx +33 -0
  287. package/test/server/fixtures/template/src/js/routes/index.jsx +72 -0
  288. package/test/server/fixtures/template/src/js/routes/utils.jsx +39 -0
  289. package/test/server/fixtures/template/src/js/store/index.js +19 -0
  290. package/test/server/handler.assets.test.ts +331 -0
  291. package/test/server/handler.test.ts +210 -0
  292. package/test/server/manifestCache.test.ts +71 -0
  293. package/test/server/render.test.ts +90 -0
  294. package/test/server/scriptUtils.test.ts +103 -0
  295. package/test/server/userAgentUtil.test.ts +48 -0
  296. package/test/server/validator.test.ts +228 -0
  297. package/test/vite/resolveDevServerConfig.test.js +4 -1
  298. package/tsconfig.test.json +23 -0
  299. package/vitest.config.ts +93 -0
  300. package/dist/caching.js +0 -1
  301. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/DeviceInfoUtilsTest.kt +0 -187
  302. package/dist/native/bridge/standardizedHooks.js +0 -611
  303. package/dist/router.js +0 -1
  304. package/dist/scripts/validator.js +0 -149
  305. package/dist/web-router/hooks.jsx +0 -72
  306. package/dist/web-router/types/components/MetaTag.d.ts +0 -2
  307. package/dist/web-router/types/components/RouterDataProvider.d.ts +0 -95
  308. package/dist/web-router/types/context.d.ts +0 -14
  309. package/dist/web-router/types/hooks.d.ts +0 -1
  310. package/dist/web-router/types/index.d.ts +0 -3
  311. package/dist/web-router/types/utils/metaDataUtils.d.ts +0 -24
  312. /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Constants/CatalystConstants.swift +0 -0
  313. /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Constants/ConfigConstants.swift +0 -0
  314. /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Notifications/NotificationModels.swift +0 -0
  315. /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Protocols/NotificationHandlerProtocol.swift +0 -0
  316. /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/NullNotificationHandler.swift +0 -0
@@ -0,0 +1,300 @@
1
+ package io.yourname.androidproject
2
+
3
+ import android.content.Context
4
+ import android.webkit.WebView
5
+ import io.yourname.androidproject.utils.FrameworkServerUtils
6
+ import org.junit.After
7
+ import org.junit.Assert.*
8
+ import org.junit.Before
9
+ import org.junit.Test
10
+ import org.mockito.kotlin.doReturn
11
+ import org.mockito.kotlin.mock
12
+ import java.io.IOException
13
+ import java.net.HttpURLConnection
14
+ import java.net.URL
15
+ import kotlin.io.path.createTempDirectory
16
+
17
+ /**
18
+ * Loopback tests for FrameworkServerUtils, the Ktor-based localhost file
19
+ * server — previously entirely untested (0%). Ports iOS's
20
+ * FrameworkServerUtilsLoopbackTests.swift pattern: start the real
21
+ * embedded server (Ktor/Netty, real socket, real port from the
22
+ * 3000-3099 range) and make real HTTP requests against it via
23
+ * java.net.HttpURLConnection (no new test dependency needed — this repo
24
+ * has no OkHttp test dep, and the JDK's built-in client is enough for a
25
+ * loopback GET/POST).
26
+ *
27
+ * FrameworkServerUtils is a singleton `object` — server state
28
+ * (isServerRunning/serverPort/sessionId/servedFiles) is shared static
29
+ * state across the whole test JVM. Every test starts fresh via
30
+ * stopServer() in tearDown so state doesn't leak between tests or other
31
+ * test classes running in the same JVM.
32
+ *
33
+ * Not chased here: the /ai/stream and /ai/generate SSE routes (need a
34
+ * real nativeAiSupplier + Flow collection — a second, separate scope) and
35
+ * the 5-minute cleanup task's periodic timer path. This file covers
36
+ * lifecycle (start/stop/isRunning/getServerPort/getSessionId), file
37
+ * serving (add/copy/remove), and the /status, file-serving, and
38
+ * invalid-route HTTP endpoints — mirrors what iOS's loopback tests
39
+ * covered for the equivalent Swift class.
40
+ */
41
+ class FrameworkServerUtilsTest {
42
+
43
+ private lateinit var cacheDir: java.io.File
44
+ private lateinit var context: Context
45
+ private lateinit var webView: WebView
46
+
47
+ @Before
48
+ fun setUp() {
49
+ // FrameworkServerUtils is a JVM-wide singleton object; stop it
50
+ // defensively before each test too, not just in tearDown — a
51
+ // prior test class (or an earlier test in this one, if tearDown
52
+ // somehow didn't run) leaving the server running would otherwise
53
+ // poison this class's isRunning()==false assumptions. Matches the
54
+ // defensive-stop pattern already used in FileSizeRouterUtilsTest.
55
+ if (FrameworkServerUtils.isRunning()) {
56
+ FrameworkServerUtils.stopServer()
57
+ }
58
+ cacheDir = createTempDirectory(prefix = "catalyst-framework-server-test").toFile()
59
+ context = mock { on { getCacheDir() } doReturn cacheDir }
60
+ webView = mock { on { getUrl() } doReturn "http://localhost:8080/index.html" }
61
+ }
62
+
63
+ @After
64
+ fun tearDown() {
65
+ if (FrameworkServerUtils.isRunning()) {
66
+ FrameworkServerUtils.stopServer()
67
+ }
68
+ cacheDir.deleteRecursively()
69
+ }
70
+
71
+ /** Waits briefly for Ktor/Netty's async bind to complete, mirroring the
72
+ * "server may not start in this sandbox" tolerance pattern used on
73
+ * iOS — returns false (caller should skip) if it never came up. */
74
+ private fun startServerAndWaitReady(): Boolean {
75
+ val started = FrameworkServerUtils.startServer(context, webView)
76
+ if (!started) return false
77
+ repeat(20) {
78
+ if (FrameworkServerUtils.isRunning() && FrameworkServerUtils.getServerPort() != 0) return true
79
+ Thread.sleep(100)
80
+ }
81
+ return FrameworkServerUtils.isRunning()
82
+ }
83
+
84
+ private fun get(path: String): Pair<Int, String> {
85
+ val port = FrameworkServerUtils.getServerPort()
86
+ val connection = URL("http://localhost:$port$path").openConnection() as HttpURLConnection
87
+ connection.connectTimeout = 5000
88
+ connection.readTimeout = 5000
89
+ connection.setRequestProperty("Accept", "*/*")
90
+ return try {
91
+ val code = connection.responseCode
92
+ val body = try {
93
+ connection.inputStream.bufferedReader().readText()
94
+ } catch (e: IOException) {
95
+ connection.errorStream?.bufferedReader()?.readText() ?: ""
96
+ }
97
+ code to body
98
+ } finally {
99
+ connection.disconnect()
100
+ }
101
+ }
102
+
103
+ // ============================================================
104
+ // Lifecycle
105
+ // ============================================================
106
+
107
+ @Test
108
+ fun `startServer starts the server and reports a port in the configured range`() {
109
+ assumeServerStarts {
110
+ assertTrue(FrameworkServerUtils.isRunning())
111
+ assertTrue(FrameworkServerUtils.getServerPort() in 3000..3099)
112
+ assertTrue(FrameworkServerUtils.getSessionId().isNotEmpty())
113
+ }
114
+ }
115
+
116
+ @Test
117
+ fun `startServer is idempotent when already running`() {
118
+ assumeServerStarts {
119
+ val firstPort = FrameworkServerUtils.getServerPort()
120
+ val result = FrameworkServerUtils.startServer(context, webView)
121
+ assertTrue(result)
122
+ assertEquals(firstPort, FrameworkServerUtils.getServerPort())
123
+ }
124
+ }
125
+
126
+ @Test
127
+ fun `stopServer stops the server and isRunning reports false`() {
128
+ assumeServerStarts {
129
+ FrameworkServerUtils.stopServer()
130
+ assertFalse(FrameworkServerUtils.isRunning())
131
+ }
132
+ }
133
+
134
+ @Test
135
+ fun `isRunning is false before any server has started`() {
136
+ assertFalse(FrameworkServerUtils.isRunning())
137
+ }
138
+
139
+ @Test
140
+ fun `a server restarted after stop can start again and serve a file`() {
141
+ // Regression test for the serverScope-never-recreated bug:
142
+ // serverScope was a `val`, so stopServer()'s serverScope.cancel()
143
+ // permanently killed it — a restarted server's startCleanupTask()
144
+ // then launched into an already-cancelled scope. This doesn't
145
+ // directly assert the cleanup task itself (its failure was
146
+ // silent — a cancelled scope just drops launched coroutines
147
+ // rather than throwing at the call site), but it does exercise
148
+ // the exact start -> stop -> start sequence that triggered it end
149
+ // to end, including a real file being served on the restarted
150
+ // instance, as the most direct available signal something didn't
151
+ // regress.
152
+ assumeServerStarts {
153
+ FrameworkServerUtils.stopServer()
154
+ assertFalse(FrameworkServerUtils.isRunning())
155
+
156
+ val restarted = FrameworkServerUtils.startServer(context, webView)
157
+ assertTrue(restarted)
158
+ assertTrue(FrameworkServerUtils.isRunning())
159
+
160
+ val tempFile = java.io.File(cacheDir, "after-restart.txt").apply { writeText("still works") }
161
+ val url = FrameworkServerUtils.addFileToServe(tempFile, "after-restart.txt", "text/plain")
162
+ assertNotNull(url)
163
+
164
+ val path = url!!.substringAfter("localhost:${FrameworkServerUtils.getServerPort()}")
165
+ val (code, body) = get(path)
166
+ assertEquals(200, code)
167
+ assertEquals("still works", body)
168
+ }
169
+ }
170
+
171
+ // ============================================================
172
+ // addFileToServe / copyAndServeFile / removeServedFile (not-running guards)
173
+ // ============================================================
174
+
175
+ @Test
176
+ fun `addFileToServe returns null when the server is not running`() {
177
+ assertFalse(FrameworkServerUtils.isRunning())
178
+ val tempFile = java.io.File(cacheDir, "not-served.txt").apply { writeText("x") }
179
+
180
+ val result = FrameworkServerUtils.addFileToServe(tempFile, "not-served.txt", "text/plain")
181
+
182
+ assertNull(result)
183
+ }
184
+
185
+ @Test
186
+ fun `copyAndServeFile returns null when the server is not running`() {
187
+ assertFalse(FrameworkServerUtils.isRunning())
188
+ val tempFile = java.io.File(cacheDir, "not-copied.txt").apply { writeText("x") }
189
+
190
+ val result = FrameworkServerUtils.copyAndServeFile(tempFile, "not-copied.txt", "text/plain")
191
+
192
+ assertNull(result)
193
+ }
194
+
195
+ @Test
196
+ fun `addFileToServe returns a well-formed URL once the server is running`() {
197
+ assumeServerStarts {
198
+ val tempFile = java.io.File(cacheDir, "served.txt").apply { writeText("hello") }
199
+
200
+ val url = FrameworkServerUtils.addFileToServe(tempFile, "served.txt", "text/plain")
201
+
202
+ assertNotNull(url)
203
+ assertTrue(url!!.startsWith("http://localhost:${FrameworkServerUtils.getServerPort()}/framework-${FrameworkServerUtils.getSessionId()}/file-"))
204
+ }
205
+ }
206
+
207
+ // ============================================================
208
+ // HTTP endpoints (real requests against the real embedded server)
209
+ // ============================================================
210
+
211
+ @Test
212
+ fun `GET status returns 200 with running JSON`() {
213
+ assumeServerStarts {
214
+ val (code, body) = get("/framework-${FrameworkServerUtils.getSessionId()}/status")
215
+
216
+ assertEquals(200, code)
217
+ assertTrue(body.contains("\"status\":\"running\""))
218
+ assertTrue(body.contains(FrameworkServerUtils.getSessionId()))
219
+ }
220
+ }
221
+
222
+ @Test
223
+ fun `GET a served file returns 200 with the real file content`() {
224
+ assumeServerStarts {
225
+ val content = "loopback content ${System.nanoTime()}"
226
+ val tempFile = java.io.File(cacheDir, "loopback.txt").apply { writeText(content) }
227
+ val url = FrameworkServerUtils.addFileToServe(tempFile, "loopback.txt", "text/plain")
228
+ assertNotNull(url)
229
+
230
+ val path = url!!.substringAfter("localhost:${FrameworkServerUtils.getServerPort()}")
231
+ val (code, body) = get(path)
232
+
233
+ assertEquals(200, code)
234
+ assertEquals(content, body)
235
+ }
236
+ }
237
+
238
+ @Test
239
+ fun `GET an unknown fileId returns 404`() {
240
+ assumeServerStarts {
241
+ val (code, _) = get("/framework-${FrameworkServerUtils.getSessionId()}/file-does-not-exist")
242
+ assertEquals(404, code)
243
+ }
244
+ }
245
+
246
+ @Test
247
+ fun `GET a served file whose physical file was deleted returns 404`() {
248
+ assumeServerStarts {
249
+ val tempFile = java.io.File(cacheDir, "vanish.txt").apply { writeText("will vanish") }
250
+ val url = FrameworkServerUtils.addFileToServe(tempFile, "vanish.txt", "text/plain")
251
+ assertNotNull(url)
252
+ tempFile.delete()
253
+
254
+ val path = url!!.substringAfter("localhost:${FrameworkServerUtils.getServerPort()}")
255
+ val (code, _) = get(path)
256
+
257
+ assertEquals(404, code)
258
+ }
259
+ }
260
+
261
+ @Test
262
+ fun `GET an invalid route returns 404`() {
263
+ assumeServerStarts {
264
+ val (code, _) = get("/framework-${FrameworkServerUtils.getSessionId()}/not-a-real-route")
265
+ assertEquals(404, code)
266
+ }
267
+ }
268
+
269
+ @Test
270
+ fun `removeServedFile makes a subsequent request return 404`() {
271
+ assumeServerStarts {
272
+ val tempFile = java.io.File(cacheDir, "to-remove.txt").apply { writeText("bye") }
273
+ val url = FrameworkServerUtils.addFileToServe(tempFile, "to-remove.txt", "text/plain")
274
+ assertNotNull(url)
275
+ val path = url!!.substringAfter("localhost:${FrameworkServerUtils.getServerPort()}")
276
+
277
+ val (beforeCode, _) = get(path)
278
+ assertEquals(200, beforeCode)
279
+
280
+ val fileId = path.substringAfter("file-")
281
+ FrameworkServerUtils.removeServedFile(fileId)
282
+
283
+ val (afterCode, _) = get(path)
284
+ assertEquals(404, afterCode)
285
+ }
286
+ }
287
+
288
+ // Runs `block` only if the real Ktor/Netty server actually came up in
289
+ // this sandbox; otherwise the test is silently skipped rather than
290
+ // failed — matches the "server may not start in this environment"
291
+ // tolerance pattern from the iOS loopback tests (network restrictions
292
+ // in some CI/sandbox environments can prevent binding a real socket).
293
+ private fun assumeServerStarts(block: () -> Unit) {
294
+ if (!startServerAndWaitReady()) {
295
+ org.junit.Assume.assumeTrue("Framework server failed to start in this environment — skipping", false)
296
+ return
297
+ }
298
+ block()
299
+ }
300
+ }
@@ -0,0 +1,244 @@
1
+ package io.yourname.androidproject
2
+
3
+ import android.app.Activity
4
+ import android.content.Intent
5
+ import android.content.pm.PackageManager
6
+ import android.content.pm.ResolveInfo
7
+ import io.yourname.androidproject.utils.IntentUtils
8
+ import org.junit.Assert.*
9
+ import org.junit.Test
10
+ import org.mockito.kotlin.doReturn
11
+ import org.mockito.kotlin.doThrow
12
+ import org.mockito.kotlin.mock
13
+ import org.mockito.kotlin.whenever
14
+
15
+ /**
16
+ * Unit tests for IntentUtils, previously entirely untested (0%).
17
+ *
18
+ * parseIntentParams/validateFileUrl/createFilePickerIntent are pure (no
19
+ * Context/Activity) and covered directly. canHandleIntent/
20
+ * getAppsForMimeType/getDefaultAppForMimeType take an Activity but only
21
+ * touch its packageManager, so a mocked Activity + mocked PackageManager
22
+ * covers them without FileProvider. openFileWithSystemIntent/
23
+ * createShareIntent/openFileWithSpecificApp all call
24
+ * FileProvider.getUriForFile — a static method backed by a real
25
+ * ContentProvider registration that doesn't exist in a JVM unit test —
26
+ * left uncovered here rather than chased with a heavier mocking setup;
27
+ * matches the plan's "reachable without excessive mocking" scope.
28
+ */
29
+ class IntentUtilsTest {
30
+
31
+ // ============================================================
32
+ // parseIntentParams
33
+ // ============================================================
34
+
35
+ @Test
36
+ fun `parseIntentParams splits fileUrl and mimeType on the pipe delimiter`() {
37
+ val (fileUrl, mimeType) = IntentUtils.parseIntentParams("https://example.com/file.pdf|application/pdf")
38
+ assertEquals("https://example.com/file.pdf", fileUrl)
39
+ assertEquals("application/pdf", mimeType)
40
+ }
41
+
42
+ @Test
43
+ fun `parseIntentParams returns null mimeType when no pipe is present`() {
44
+ val (fileUrl, mimeType) = IntentUtils.parseIntentParams("https://example.com/file.pdf")
45
+ assertEquals("https://example.com/file.pdf", fileUrl)
46
+ assertNull(mimeType)
47
+ }
48
+
49
+ @Test
50
+ fun `parseIntentParams throws for null params`() {
51
+ assertThrows(IllegalArgumentException::class.java) {
52
+ IntentUtils.parseIntentParams(null)
53
+ }
54
+ }
55
+
56
+ @Test
57
+ fun `parseIntentParams throws for blank params`() {
58
+ assertThrows(IllegalArgumentException::class.java) {
59
+ IntentUtils.parseIntentParams(" ")
60
+ }
61
+ }
62
+
63
+ @Test
64
+ fun `parseIntentParams throws when the fileUrl portion is empty`() {
65
+ assertThrows(IllegalArgumentException::class.java) {
66
+ IntentUtils.parseIntentParams("|application/pdf")
67
+ }
68
+ }
69
+
70
+ @Test
71
+ fun `parseIntentParams treats a blank mimeType segment as null`() {
72
+ val (_, mimeType) = IntentUtils.parseIntentParams("https://example.com/file.pdf| ")
73
+ assertNull(mimeType)
74
+ }
75
+
76
+ // ============================================================
77
+ // validateFileUrl
78
+ // ============================================================
79
+
80
+ @Test
81
+ fun `validateFileUrl accepts an https URL`() {
82
+ IntentUtils.validateFileUrl("https://example.com/file.pdf")
83
+ }
84
+
85
+ @Test
86
+ fun `validateFileUrl accepts an http URL`() {
87
+ IntentUtils.validateFileUrl("http://example.com/file.pdf")
88
+ }
89
+
90
+ @Test
91
+ fun `validateFileUrl throws for a blank URL`() {
92
+ val error = assertThrows(IllegalArgumentException::class.java) {
93
+ IntentUtils.validateFileUrl("")
94
+ }
95
+ assertEquals("File URL cannot be empty", error.message)
96
+ }
97
+
98
+ @Test
99
+ fun `validateFileUrl throws for a non-http(s) scheme`() {
100
+ val error = assertThrows(IllegalArgumentException::class.java) {
101
+ IntentUtils.validateFileUrl("file:///local/path.pdf")
102
+ }
103
+ assertEquals("Only remote URLs (http/https) are supported", error.message)
104
+ }
105
+
106
+ // ============================================================
107
+ // createFilePickerIntent
108
+ //
109
+ // Intent is a real android.content.Intent (not mocked) constructed
110
+ // directly by the method under test. Its getters (action/type/
111
+ // categories/getBooleanExtra/getStringArrayExtra) are backed by the
112
+ // Android SDK's stub jar, which — even with
113
+ // testOptions.unitTests.isReturnDefaultValues=true — returns default
114
+ // values (null/false) rather than reflecting state actually set via
115
+ // setAction()/type=/putExtra() on the same stub instance. That's a
116
+ // structural limitation of testing raw Intent state in a plain JVM
117
+ // unit test (Robolectric provides a real Intent implementation for
118
+ // this; out of scope per this plan's Mockito-only ceiling). These
119
+ // tests exercise the method's branching logic (single vs.
120
+ // comma-separated MIME type path) and assert only that it completes
121
+ // and returns a non-null Intent, not the Intent's resulting state.
122
+ // ============================================================
123
+
124
+ @Test
125
+ fun `createFilePickerIntent returns a non-null Intent for a single MIME type`() {
126
+ val intent = IntentUtils.createFilePickerIntent("application/pdf")
127
+ assertNotNull(intent)
128
+ }
129
+
130
+ @Test
131
+ fun `createFilePickerIntent returns a non-null Intent when allowMultiple is set`() {
132
+ val intent = IntentUtils.createFilePickerIntent("image/*", allowMultiple = true)
133
+ assertNotNull(intent)
134
+ }
135
+
136
+ @Test
137
+ fun `createFilePickerIntent returns a non-null Intent for comma-separated MIME types`() {
138
+ val intent = IntentUtils.createFilePickerIntent("application/pdf,image/png")
139
+ assertNotNull(intent)
140
+ }
141
+
142
+ @Test
143
+ fun `createFilePickerIntent returns a non-null Intent for a trailing-comma single type`() {
144
+ val intent = IntentUtils.createFilePickerIntent("application/pdf,")
145
+ assertNotNull(intent)
146
+ }
147
+
148
+ // ============================================================
149
+ // canHandleIntent / getAppsForMimeType / getDefaultAppForMimeType
150
+ // ============================================================
151
+
152
+ private fun activityWithPackageManager(packageManager: PackageManager): Activity {
153
+ return mock { on { getPackageManager() } doReturn packageManager }
154
+ }
155
+
156
+ @Test
157
+ fun `canHandleIntent returns true when queryIntentActivities finds a match`() {
158
+ val resolveInfo: ResolveInfo = mock()
159
+ val packageManager: PackageManager = mock {
160
+ on { queryIntentActivities(org.mockito.kotlin.any(), org.mockito.kotlin.eq(0)) } doReturn listOf(resolveInfo)
161
+ }
162
+ val activity = activityWithPackageManager(packageManager)
163
+
164
+ assertTrue(IntentUtils.canHandleIntent(activity, Intent(Intent.ACTION_VIEW)))
165
+ }
166
+
167
+ @Test
168
+ fun `canHandleIntent returns false when no app can handle it`() {
169
+ val packageManager: PackageManager = mock {
170
+ on { queryIntentActivities(org.mockito.kotlin.any(), org.mockito.kotlin.eq(0)) } doReturn emptyList()
171
+ }
172
+ val activity = activityWithPackageManager(packageManager)
173
+
174
+ assertFalse(IntentUtils.canHandleIntent(activity, Intent(Intent.ACTION_VIEW)))
175
+ }
176
+
177
+ @Test
178
+ fun `canHandleIntent returns false when queryIntentActivities throws`() {
179
+ val packageManager: PackageManager = mock {
180
+ on { queryIntentActivities(org.mockito.kotlin.any(), org.mockito.kotlin.eq(0)) } doThrow RuntimeException("boom")
181
+ }
182
+ val activity = activityWithPackageManager(packageManager)
183
+
184
+ assertFalse(IntentUtils.canHandleIntent(activity, Intent(Intent.ACTION_VIEW)))
185
+ }
186
+
187
+ @Test
188
+ fun `getAppsForMimeType returns loaded labels for each resolved app`() {
189
+ val packageManager: PackageManager = mock()
190
+ val resolveInfo: ResolveInfo = mock {
191
+ on { loadLabel(packageManager) } doReturn "PDF Reader"
192
+ }
193
+ whenever(packageManager.queryIntentActivities(org.mockito.kotlin.any(), org.mockito.kotlin.eq(0)))
194
+ .thenReturn(listOf(resolveInfo))
195
+ val activity = activityWithPackageManager(packageManager)
196
+
197
+ val apps = IntentUtils.getAppsForMimeType(activity, "application/pdf")
198
+
199
+ assertEquals(listOf("PDF Reader"), apps)
200
+ }
201
+
202
+ @Test
203
+ fun `getAppsForMimeType returns an empty list when resolution throws`() {
204
+ val packageManager: PackageManager = mock {
205
+ on { queryIntentActivities(org.mockito.kotlin.any(), org.mockito.kotlin.eq(0)) } doThrow RuntimeException("boom")
206
+ }
207
+ val activity = activityWithPackageManager(packageManager)
208
+
209
+ assertTrue(IntentUtils.getAppsForMimeType(activity, "application/pdf").isEmpty())
210
+ }
211
+
212
+ @Test
213
+ fun `getDefaultAppForMimeType returns the resolved package name`() {
214
+ val resolveInfo = android.content.pm.ResolveInfo().apply {
215
+ activityInfo = android.content.pm.ActivityInfo().apply { packageName = "com.example.pdf" }
216
+ }
217
+ val packageManager: PackageManager = mock {
218
+ on { resolveActivity(org.mockito.kotlin.any(), org.mockito.kotlin.eq(0)) } doReturn resolveInfo
219
+ }
220
+ val activity = activityWithPackageManager(packageManager)
221
+
222
+ assertEquals("com.example.pdf", IntentUtils.getDefaultAppForMimeType(activity, "application/pdf"))
223
+ }
224
+
225
+ @Test
226
+ fun `getDefaultAppForMimeType returns null when nothing resolves`() {
227
+ val packageManager: PackageManager = mock {
228
+ on { resolveActivity(org.mockito.kotlin.any(), org.mockito.kotlin.eq(0)) } doReturn null
229
+ }
230
+ val activity = activityWithPackageManager(packageManager)
231
+
232
+ assertNull(IntentUtils.getDefaultAppForMimeType(activity, "application/pdf"))
233
+ }
234
+
235
+ @Test
236
+ fun `getDefaultAppForMimeType returns null when resolution throws`() {
237
+ val packageManager: PackageManager = mock {
238
+ on { resolveActivity(org.mockito.kotlin.any(), org.mockito.kotlin.eq(0)) } doThrow RuntimeException("boom")
239
+ }
240
+ val activity = activityWithPackageManager(packageManager)
241
+
242
+ assertNull(IntentUtils.getDefaultAppForMimeType(activity, "application/pdf"))
243
+ }
244
+ }