catalyst-core 0.3.0-beta.5 → 0.3.0-canary.2

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 +185 -71
  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 +12 -5
  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 -3
  205. package/dist/scripts/start.js +8 -3
  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 +25 -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 +2 -29
  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 +115 -17
  266. package/mcp_v2/mcp.js +141 -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 +118 -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 +330 -0
  291. package/test/server/handler.test.ts +208 -0
  292. package/test/server/manifestCache.test.ts +71 -0
  293. package/test/server/render.test.ts +71 -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 +225 -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 -40
  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,143 @@
1
+ package io.yourname.androidproject.camera
2
+
3
+ import android.webkit.WebView
4
+ import org.json.JSONObject
5
+ import org.junit.Assert.assertNotNull
6
+ import org.junit.Assert.assertNull
7
+ import org.junit.Test
8
+ import org.mockito.kotlin.any
9
+ import org.mockito.kotlin.mock
10
+ import org.mockito.kotlin.verify
11
+
12
+ /**
13
+ * Unit tests for ViewfinderMapper (Android coverage batch 3), previously
14
+ * entirely untested (0/30 lines).
15
+ *
16
+ * Both functions are pure coordinate math with a single mockable
17
+ * collaborator call (WebView.getLocationOnScreen(IntArray)). All lines
18
+ * (the guard branches, the scale/offset math, and the success/failure
19
+ * paths of parseViewfinderRect) execute under test and are covered by
20
+ * jacoco -- but the *values* the math produces cannot be asserted here:
21
+ * android.graphics.RectF is a stub under this project's mockable
22
+ * android.jar (isReturnDefaultValues=true, confirmed empirically -- a
23
+ * `RectF(10f, 20f, 110f, 70f)` constructed in a test reads back left/top/
24
+ * right/bottom as 0.0, since the constructor body that assigns those
25
+ * public fields never runs). Per the PerfEventBufferTest/MetricsMonitorTest
26
+ * precedent of documenting and moving on rather than reaching for
27
+ * Robolectric, this file verifies null/non-null outcomes and observable
28
+ * collaborator interactions (webView.getLocationOnScreen being invoked)
29
+ * instead of exact coordinate values.
30
+ */
31
+ class ViewfinderMapperTest {
32
+
33
+ // ============================================================
34
+ // parseViewfinderRect
35
+ // ============================================================
36
+
37
+ @Test
38
+ fun `parseViewfinderRect returns a non-null pair and reads the WebView origin on well-formed JSON`() {
39
+ val json = JSONObject().apply {
40
+ put("x", 10.0)
41
+ put("y", 20.0)
42
+ put("width", 100.0)
43
+ put("height", 50.0)
44
+ }
45
+ val webView = mock<WebView>()
46
+
47
+ val result = ViewfinderMapper.parseViewfinderRect(json, webView)
48
+
49
+ assertNotNull(result)
50
+ verify(webView).getLocationOnScreen(any())
51
+ }
52
+
53
+ @Test
54
+ fun `parseViewfinderRect returns null when a required field is missing`() {
55
+ val json = JSONObject().apply {
56
+ put("x", 10.0)
57
+ put("y", 20.0)
58
+ // "width" and "height" deliberately omitted -- json.getDouble
59
+ // throws a JSONException, caught and mapped to null.
60
+ }
61
+ val webView = mock<WebView>()
62
+
63
+ val result = ViewfinderMapper.parseViewfinderRect(json, webView)
64
+
65
+ assertNull(result)
66
+ }
67
+
68
+ @Test
69
+ fun `parseViewfinderRect does not call getLocationOnScreen when JSON parsing fails first`() {
70
+ val json = JSONObject() // no fields at all
71
+ val webView = mock<WebView>()
72
+
73
+ ViewfinderMapper.parseViewfinderRect(json, webView)
74
+
75
+ // x is read before webView.getLocationOnScreen is called -- an
76
+ // empty JSON object throws on the very first getDouble("x").
77
+ org.mockito.Mockito.verifyNoInteractions(webView)
78
+ }
79
+
80
+ // ============================================================
81
+ // mapBarcodeToScreen
82
+ // ============================================================
83
+
84
+ @Test
85
+ fun `mapBarcodeToScreen returns null when previewWidth is zero`() {
86
+ val result = ViewfinderMapper.mapBarcodeToScreen(
87
+ boxLeft = 0, boxTop = 0, boxRight = 100, boxBottom = 100,
88
+ imageWidth = 640, imageHeight = 480,
89
+ previewWidth = 0f, previewHeight = 800f
90
+ )
91
+ assertNull(result)
92
+ }
93
+
94
+ @Test
95
+ fun `mapBarcodeToScreen returns null when previewHeight is zero`() {
96
+ val result = ViewfinderMapper.mapBarcodeToScreen(
97
+ boxLeft = 0, boxTop = 0, boxRight = 100, boxBottom = 100,
98
+ imageWidth = 640, imageHeight = 480,
99
+ previewWidth = 600f, previewHeight = 0f
100
+ )
101
+ assertNull(result)
102
+ }
103
+
104
+ @Test
105
+ fun `mapBarcodeToScreen returns null when imageWidth is zero`() {
106
+ val result = ViewfinderMapper.mapBarcodeToScreen(
107
+ boxLeft = 0, boxTop = 0, boxRight = 100, boxBottom = 100,
108
+ imageWidth = 0, imageHeight = 480,
109
+ previewWidth = 600f, previewHeight = 800f
110
+ )
111
+ assertNull(result)
112
+ }
113
+
114
+ @Test
115
+ fun `mapBarcodeToScreen returns null when imageHeight is zero`() {
116
+ val result = ViewfinderMapper.mapBarcodeToScreen(
117
+ boxLeft = 0, boxTop = 0, boxRight = 100, boxBottom = 100,
118
+ imageWidth = 640, imageHeight = 0,
119
+ previewWidth = 600f, previewHeight = 800f
120
+ )
121
+ assertNull(result)
122
+ }
123
+
124
+ @Test
125
+ fun `mapBarcodeToScreen returns non-null for valid non-square dimensions, exercising the FILL_CENTER scale math`() {
126
+ val result = ViewfinderMapper.mapBarcodeToScreen(
127
+ boxLeft = 100, boxTop = 100, boxRight = 200, boxBottom = 200,
128
+ imageWidth = 640, imageHeight = 480,
129
+ previewWidth = 400f, previewHeight = 400f
130
+ )
131
+ assertNotNull(result)
132
+ }
133
+
134
+ @Test
135
+ fun `mapBarcodeToScreen returns non-null when preview and image aspect ratios match`() {
136
+ val result = ViewfinderMapper.mapBarcodeToScreen(
137
+ boxLeft = 0, boxTop = 0, boxRight = 640, boxBottom = 480,
138
+ imageWidth = 640, imageHeight = 480,
139
+ previewWidth = 320f, previewHeight = 240f
140
+ )
141
+ assertNotNull(result)
142
+ }
143
+ }
@@ -0,0 +1,421 @@
1
+ package io.yourname.androidproject.camera
2
+
3
+ import android.animation.ValueAnimator
4
+ import android.content.Context
5
+ import androidx.camera.core.Camera
6
+ import androidx.camera.core.CameraControl
7
+ import androidx.camera.core.CameraInfo
8
+ import androidx.camera.core.ZoomState
9
+ import androidx.lifecycle.LiveData
10
+ import org.junit.After
11
+ import org.junit.Assert.assertEquals
12
+ import org.junit.Assert.assertFalse
13
+ import org.junit.Assert.assertTrue
14
+ import org.junit.Before
15
+ import org.junit.Test
16
+ import org.mockito.MockedStatic
17
+ import org.mockito.Mockito.mockStatic
18
+ import org.mockito.kotlin.any
19
+ import org.mockito.kotlin.argumentCaptor
20
+ import org.mockito.kotlin.doReturn
21
+ import org.mockito.kotlin.mock
22
+ import org.mockito.kotlin.verify
23
+ import org.mockito.kotlin.whenever
24
+
25
+ /**
26
+ * Unit tests for ZoomController (Android coverage batch 4), previously
27
+ * entirely untested (0/~79 lines).
28
+ *
29
+ * Constructor takes only a Context (to build a ScaleGestureDetector) plus
30
+ * a VideoStreamStateMachine and a callback -- all safe to construct
31
+ * because the mockable android.jar strips Android constructors to no-ops.
32
+ * The ScaleGestureDetector-driven pinch-to-zoom path (onTouchEvent /
33
+ * SimpleOnScaleGestureListener.onScale) is NOT exercised here: it requires
34
+ * a real ScaleGestureDetector to actually parse a MotionEvent stream and
35
+ * invoke the listener, which the mockable jar's stub GestureDetector does
36
+ * not do -- there is no seam to inject a fake detector (it's a private val
37
+ * built in the constructor). That gap mirrors this project's established
38
+ * precedent of leaving Android-internal event-dispatch machinery
39
+ * unexercised (see HoldControllerTest's Handler/Looper note) rather than
40
+ * pulling in Robolectric.
41
+ *
42
+ * Covered instead: applyZoomMultiplier() (no-camera / no-zoomState guards
43
+ * and the real clamp+setZoomRatio+callback path), onAutoZoomSuggestion()'s
44
+ * zoom-only-up guard, debounce/confirm-frame counting, and the animated
45
+ * commit branch, cancelZoomAnimation(), and currentMaxZoomRatio().
46
+ *
47
+ * ValueAnimator.ofFloat(...) returns null under the mockable jar (a static
48
+ * factory method, same shape as MetricsMonitorTest's
49
+ * Choreographer.getInstance() problem) -- mockStatic(ValueAnimator) is
50
+ * installed for the animated-commit tests and closed in tearDown(),
51
+ * stubbing ofFloat(...) to return a mocked ValueAnimator so
52
+ * onAutoZoomSuggestion()'s `.apply { ... start() }` block and the
53
+ * addUpdateListener(...) callback body are both exercisable by manually
54
+ * invoking the captured listener, mirroring TorchControllerTest's
55
+ * captured-Runnable pattern.
56
+ */
57
+ class ZoomControllerTest {
58
+
59
+ private lateinit var valueAnimatorMock: MockedStatic<ValueAnimator>
60
+ private val context = mock<Context>()
61
+ private val stateMachine = mock<VideoStreamStateMachine>()
62
+
63
+ private val zoomEvents = mutableListOf<Triple<Float, Float, Float>>()
64
+ private fun onZoomChanged(zoomLevel: Float, minZoom: Float, maxZoom: Float) {
65
+ zoomEvents.add(Triple(zoomLevel, minZoom, maxZoom))
66
+ }
67
+
68
+ private fun newController() = ZoomController(context, stateMachine, ::onZoomChanged)
69
+
70
+ @Before
71
+ fun setUp() {
72
+ zoomEvents.clear()
73
+ }
74
+
75
+ @After
76
+ fun tearDown() {
77
+ if (::valueAnimatorMock.isInitialized) {
78
+ valueAnimatorMock.close()
79
+ }
80
+ }
81
+
82
+ private fun mockCamera(zoomRatio: Float, minZoom: Float, maxZoom: Float): Camera {
83
+ val zoomState = mock<ZoomState> {
84
+ on { getZoomRatio() } doReturn zoomRatio
85
+ on { getMinZoomRatio() } doReturn minZoom
86
+ on { getMaxZoomRatio() } doReturn maxZoom
87
+ }
88
+ @Suppress("UNCHECKED_CAST")
89
+ val zoomStateLiveData = mock<LiveData<ZoomState>> {
90
+ on { getValue() } doReturn zoomState
91
+ }
92
+ val cameraInfo = mock<CameraInfo> {
93
+ on { getZoomState() } doReturn zoomStateLiveData
94
+ }
95
+ val cameraControl = mock<CameraControl>()
96
+ return mock {
97
+ on { getCameraInfo() } doReturn cameraInfo
98
+ on { getCameraControl() } doReturn cameraControl
99
+ }
100
+ }
101
+
102
+ // ============================================================
103
+ // applyZoomMultiplier
104
+ // ============================================================
105
+
106
+ @Test
107
+ fun `applyZoomMultiplier with no camera attached is a no-op`() {
108
+ val controller = newController()
109
+
110
+ controller.applyZoomMultiplier(2.0f)
111
+
112
+ assertTrue(zoomEvents.isEmpty())
113
+ }
114
+
115
+ @Test
116
+ fun `applyZoomMultiplier with a null zoomState value is a no-op`() {
117
+ val zoomStateLiveData = mock<LiveData<ZoomState>>()
118
+ whenever(zoomStateLiveData.value).thenReturn(null)
119
+ val cameraInfo = mock<CameraInfo> {
120
+ on { getZoomState() } doReturn zoomStateLiveData
121
+ }
122
+ val camera = mock<Camera> { on { getCameraInfo() } doReturn cameraInfo }
123
+ val controller = newController()
124
+ controller.attachCamera(camera)
125
+
126
+ controller.applyZoomMultiplier(2.0f)
127
+
128
+ assertTrue(zoomEvents.isEmpty())
129
+ }
130
+
131
+ @Test
132
+ fun `applyZoomMultiplier clamps to maxZoomRatio and fires the callback`() {
133
+ val camera = mockCamera(zoomRatio = 1.0f, minZoom = 1.0f, maxZoom = 5.0f)
134
+ val controller = newController()
135
+ controller.attachCamera(camera)
136
+
137
+ controller.applyZoomMultiplier(10.0f)
138
+
139
+ verify(camera.cameraControl).setZoomRatio(5.0f)
140
+ assertEquals(listOf(Triple(5.0f, 1.0f, 5.0f)), zoomEvents)
141
+ }
142
+
143
+ @Test
144
+ fun `applyZoomMultiplier clamps to minZoomRatio when below range`() {
145
+ val camera = mockCamera(zoomRatio = 1.0f, minZoom = 1.0f, maxZoom = 5.0f)
146
+ val controller = newController()
147
+ controller.attachCamera(camera)
148
+
149
+ controller.applyZoomMultiplier(0.1f)
150
+
151
+ verify(camera.cameraControl).setZoomRatio(1.0f)
152
+ assertEquals(listOf(Triple(1.0f, 1.0f, 5.0f)), zoomEvents)
153
+ }
154
+
155
+ @Test
156
+ fun `applyZoomMultiplier within range passes the multiplier through unchanged`() {
157
+ val camera = mockCamera(zoomRatio = 1.0f, minZoom = 1.0f, maxZoom = 5.0f)
158
+ val controller = newController()
159
+ controller.attachCamera(camera)
160
+
161
+ controller.applyZoomMultiplier(3.0f)
162
+
163
+ verify(camera.cameraControl).setZoomRatio(3.0f)
164
+ assertEquals(listOf(Triple(3.0f, 1.0f, 5.0f)), zoomEvents)
165
+ }
166
+
167
+ @Test
168
+ fun `setZoom delegates to applyZoomMultiplier`() {
169
+ val camera = mockCamera(zoomRatio = 1.0f, minZoom = 1.0f, maxZoom = 5.0f)
170
+ val controller = newController()
171
+ controller.attachCamera(camera)
172
+
173
+ controller.setZoom(2.0f)
174
+
175
+ verify(camera.cameraControl).setZoomRatio(2.0f)
176
+ assertEquals(listOf(Triple(2.0f, 1.0f, 5.0f)), zoomEvents)
177
+ }
178
+
179
+ // ============================================================
180
+ // detachCamera
181
+ // ============================================================
182
+
183
+ @Test
184
+ fun `detachCamera clears the camera so subsequent zoom calls are no-ops`() {
185
+ val camera = mockCamera(zoomRatio = 1.0f, minZoom = 1.0f, maxZoom = 5.0f)
186
+ val controller = newController()
187
+ controller.attachCamera(camera)
188
+
189
+ controller.detachCamera()
190
+ controller.applyZoomMultiplier(2.0f)
191
+
192
+ assertTrue(zoomEvents.isEmpty())
193
+ }
194
+
195
+ // ============================================================
196
+ // onAutoZoomSuggestion -- guards
197
+ // ============================================================
198
+
199
+ @Test
200
+ fun `onAutoZoomSuggestion with no camera attached returns false`() {
201
+ val controller = newController()
202
+
203
+ val result = controller.onAutoZoomSuggestion(3.0f)
204
+
205
+ assertFalse(result)
206
+ assertTrue(zoomEvents.isEmpty())
207
+ }
208
+
209
+ @Test
210
+ fun `onAutoZoomSuggestion ignores a suggestion that would zoom out`() {
211
+ val camera = mockCamera(zoomRatio = 3.0f, minZoom = 1.0f, maxZoom = 8.0f)
212
+ val controller = newController()
213
+ controller.attachCamera(camera)
214
+
215
+ val result = controller.onAutoZoomSuggestion(2.0f)
216
+
217
+ assertFalse(result)
218
+ assertTrue(zoomEvents.isEmpty())
219
+ }
220
+
221
+ @Test
222
+ fun `onAutoZoomSuggestion ignores a suggestion equal to the current ratio`() {
223
+ val camera = mockCamera(zoomRatio = 3.0f, minZoom = 1.0f, maxZoom = 8.0f)
224
+ val controller = newController()
225
+ controller.attachCamera(camera)
226
+
227
+ val result = controller.onAutoZoomSuggestion(3.0f)
228
+
229
+ assertFalse(result)
230
+ }
231
+
232
+ // ============================================================
233
+ // onAutoZoomSuggestion -- debounce
234
+ // ============================================================
235
+
236
+ @Test
237
+ fun `onAutoZoomSuggestion requires two consecutive matching frames before committing`() {
238
+ val camera = mockCamera(zoomRatio = 1.0f, minZoom = 1.0f, maxZoom = 8.0f)
239
+ val controller = newController()
240
+ controller.attachCamera(camera)
241
+
242
+ val firstFrame = controller.onAutoZoomSuggestion(4.0f)
243
+ assertFalse("first frame should only start debounce, not commit", firstFrame)
244
+ assertTrue(zoomEvents.isEmpty())
245
+ }
246
+
247
+ @Test
248
+ fun `onAutoZoomSuggestion resets debounce when the target changes beyond tolerance`() {
249
+ val camera = mockCamera(zoomRatio = 1.0f, minZoom = 1.0f, maxZoom = 8.0f)
250
+ val controller = newController()
251
+ controller.attachCamera(camera)
252
+
253
+ controller.onAutoZoomSuggestion(4.0f)
254
+ // Jumps well outside SUGGESTION_TOLERANCE (0.1f) -- restarts the
255
+ // debounce counter at 1 rather than confirming.
256
+ val secondFrame = controller.onAutoZoomSuggestion(6.0f)
257
+
258
+ assertFalse(secondFrame)
259
+ }
260
+
261
+ @Test
262
+ fun `onAutoZoomSuggestion resets debounce after a zoom-out-guard rejection`() {
263
+ val camera = mockCamera(zoomRatio = 3.0f, minZoom = 1.0f, maxZoom = 8.0f)
264
+ val controller = newController()
265
+ controller.attachCamera(camera)
266
+
267
+ // First build up one confirm frame toward 5.0.
268
+ controller.onAutoZoomSuggestion(5.0f)
269
+ // Now a rejected (zoom-out) suggestion resets consecutiveSuggestionCount to 0.
270
+ controller.onAutoZoomSuggestion(2.0f)
271
+
272
+ valueAnimatorMock = mockStatic(ValueAnimator::class.java)
273
+ val animator = mock<ValueAnimator>()
274
+ valueAnimatorMock.`when`<ValueAnimator> { ValueAnimator.ofFloat(any(), any()) }.thenReturn(animator)
275
+
276
+ // Suggesting 5.0 again should require a fresh two-frame confirm,
277
+ // not just one more (proving the counter was actually reset).
278
+ val secondAttemptFirstFrame = controller.onAutoZoomSuggestion(5.0f)
279
+ assertFalse("counter should have been reset by the rejected suggestion", secondAttemptFirstFrame)
280
+ }
281
+
282
+ // ============================================================
283
+ // onAutoZoomSuggestion -- confirmed / animated commit
284
+ // ============================================================
285
+
286
+ @Test
287
+ fun `onAutoZoomSuggestion commits and animates after enough consecutive matching frames`() {
288
+ val camera = mockCamera(zoomRatio = 1.0f, minZoom = 1.0f, maxZoom = 8.0f)
289
+ val controller = newController()
290
+ controller.attachCamera(camera)
291
+
292
+ valueAnimatorMock = mockStatic(ValueAnimator::class.java)
293
+ val animator = mock<ValueAnimator>()
294
+ valueAnimatorMock.`when`<ValueAnimator> { ValueAnimator.ofFloat(1.0f, 4.0f) }.thenReturn(animator)
295
+
296
+ controller.onAutoZoomSuggestion(4.0f) // frame 1 -- debounce only
297
+ val committed = controller.onAutoZoomSuggestion(4.0f) // frame 2 -- confirmed
298
+
299
+ assertTrue(committed)
300
+ verify(animator).duration = 300L
301
+ verify(animator).start()
302
+
303
+ val listenerCaptor = argumentCaptor<ValueAnimator.AnimatorUpdateListener>()
304
+ verify(animator).addUpdateListener(listenerCaptor.capture())
305
+
306
+ // Manually invoke the captured update listener, as TorchControllerTest
307
+ // does for its captured Runnable -- there's no real animation loop
308
+ // ticking a mocked ValueAnimator in a JVM unit test.
309
+ val tickingAnimator = mock<ValueAnimator> { on { getAnimatedValue() } doReturn 2.5f }
310
+ listenerCaptor.firstValue.onAnimationUpdate(tickingAnimator)
311
+
312
+ verify(camera.cameraControl).setZoomRatio(2.5f)
313
+ assertTrue(zoomEvents.contains(Triple(2.5f, 1.0f, 8.0f)))
314
+ }
315
+
316
+ @Test
317
+ fun `onAutoZoomSuggestion cancels a prior in-flight animator before starting a new one`() {
318
+ val camera = mockCamera(zoomRatio = 1.0f, minZoom = 1.0f, maxZoom = 8.0f)
319
+ val controller = newController()
320
+ controller.attachCamera(camera)
321
+
322
+ valueAnimatorMock = mockStatic(ValueAnimator::class.java)
323
+ val firstAnimator = mock<ValueAnimator>()
324
+ val secondAnimator = mock<ValueAnimator>()
325
+ valueAnimatorMock.`when`<ValueAnimator> { ValueAnimator.ofFloat(any(), any()) }
326
+ .thenReturn(firstAnimator, secondAnimator)
327
+
328
+ controller.onAutoZoomSuggestion(4.0f)
329
+ controller.onAutoZoomSuggestion(4.0f) // commits firstAnimator
330
+
331
+ controller.onAutoZoomSuggestion(7.0f)
332
+ controller.onAutoZoomSuggestion(7.0f) // commits secondAnimator, should cancel firstAnimator first
333
+
334
+ verify(firstAnimator).cancel()
335
+ }
336
+
337
+ // ============================================================
338
+ // cancelZoomAnimation
339
+ // ============================================================
340
+
341
+ @Test
342
+ fun `cancelZoomAnimation with no active animator does not throw`() {
343
+ val controller = newController()
344
+
345
+ controller.cancelZoomAnimation()
346
+ }
347
+
348
+ @Test
349
+ fun `cancelZoomAnimation cancels an in-flight animator and resets debounce state`() {
350
+ val camera = mockCamera(zoomRatio = 1.0f, minZoom = 1.0f, maxZoom = 8.0f)
351
+ val controller = newController()
352
+ controller.attachCamera(camera)
353
+
354
+ valueAnimatorMock = mockStatic(ValueAnimator::class.java)
355
+ val animator = mock<ValueAnimator>()
356
+ valueAnimatorMock.`when`<ValueAnimator> { ValueAnimator.ofFloat(any(), any()) }.thenReturn(animator)
357
+
358
+ controller.onAutoZoomSuggestion(4.0f)
359
+ controller.onAutoZoomSuggestion(4.0f) // commits and starts `animator`
360
+
361
+ controller.cancelZoomAnimation()
362
+
363
+ verify(animator).cancel()
364
+
365
+ // Debounce state was reset -- a fresh suggestion needs two frames again.
366
+ val nextFirstFrame = controller.onAutoZoomSuggestion(4.0f)
367
+ assertFalse(nextFirstFrame)
368
+ }
369
+
370
+ @Test
371
+ fun `detachCamera also cancels any in-flight zoom animation`() {
372
+ val camera = mockCamera(zoomRatio = 1.0f, minZoom = 1.0f, maxZoom = 8.0f)
373
+ val controller = newController()
374
+ controller.attachCamera(camera)
375
+
376
+ valueAnimatorMock = mockStatic(ValueAnimator::class.java)
377
+ val animator = mock<ValueAnimator>()
378
+ valueAnimatorMock.`when`<ValueAnimator> { ValueAnimator.ofFloat(any(), any()) }.thenReturn(animator)
379
+
380
+ controller.onAutoZoomSuggestion(4.0f)
381
+ controller.onAutoZoomSuggestion(4.0f)
382
+
383
+ controller.detachCamera()
384
+
385
+ verify(animator).cancel()
386
+ }
387
+
388
+ // ============================================================
389
+ // currentMaxZoomRatio
390
+ // ============================================================
391
+
392
+ @Test
393
+ fun `currentMaxZoomRatio returns the default 8x when no camera is attached`() {
394
+ val controller = newController()
395
+
396
+ assertEquals(8.0f, controller.currentMaxZoomRatio(), 0.001f)
397
+ }
398
+
399
+ @Test
400
+ fun `currentMaxZoomRatio returns the default 8x when zoomState value is null`() {
401
+ val zoomStateLiveData = mock<LiveData<ZoomState>>()
402
+ whenever(zoomStateLiveData.value).thenReturn(null)
403
+ val cameraInfo = mock<CameraInfo> {
404
+ on { getZoomState() } doReturn zoomStateLiveData
405
+ }
406
+ val camera = mock<Camera> { on { getCameraInfo() } doReturn cameraInfo }
407
+ val controller = newController()
408
+ controller.attachCamera(camera)
409
+
410
+ assertEquals(8.0f, controller.currentMaxZoomRatio(), 0.001f)
411
+ }
412
+
413
+ @Test
414
+ fun `currentMaxZoomRatio returns the attached camera's maxZoomRatio`() {
415
+ val camera = mockCamera(zoomRatio = 1.0f, minZoom = 1.0f, maxZoom = 12.5f)
416
+ val controller = newController()
417
+ controller.attachCamera(camera)
418
+
419
+ assertEquals(12.5f, controller.currentMaxZoomRatio(), 0.001f)
420
+ }
421
+ }