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,132 @@
1
+ package io.yourname.androidproject.camera
2
+
3
+ import androidx.camera.core.ImageProxy
4
+ import com.google.mlkit.vision.barcode.BarcodeScanner
5
+ import com.google.mlkit.vision.barcode.common.Barcode
6
+ import org.junit.Assert.assertEquals
7
+ import org.junit.Assert.assertNotNull
8
+ import org.junit.Test
9
+ import org.mockito.kotlin.mock
10
+ import org.mockito.kotlin.verify
11
+ import org.mockito.kotlin.whenever
12
+
13
+ /**
14
+ * Unit tests for BarcodeDetector (Android coverage batch 3), previously
15
+ * entirely untested (0/52 lines).
16
+ *
17
+ * Scope: close() (scanner null-safe close/clear) and the private
18
+ * processFrame() early-return branch (imageProxy.image == null -> closes
19
+ * and returns) are exercised directly/indirectly. resolveFormats() and
20
+ * formatName() (companion) are pure and exercised via their observable
21
+ * effects/return values.
22
+ *
23
+ * processFrame()'s null-image early-return branch (imageProxy.image ==
24
+ * null -> imageProxy.close(); return) is reachable via reflection on the
25
+ * private method without needing a real InputImage/ML Kit Task, and is
26
+ * exercised that way below.
27
+ *
28
+ * Deliberately out of scope: buildImageAnalysis() itself is NOT invoked --
29
+ * it calls BarcodeScanning.getClient(...) and ImageAnalysis.Builder()...
30
+ * .build(), both of which are static/builder factories that construct real
31
+ * ML Kit / CameraX platform objects rather than mockable interfaces (per
32
+ * project rule 3: builders that delegate to real platform objects are out
33
+ * of scope). Likewise the non-null-image path through processFrame()
34
+ * (InputImage.fromMediaImage(...) then
35
+ * activeScanner.process(image).addOnSuccessListener{...}) is not
36
+ * exercised: InputImage.fromMediaImage is itself a static factory over a
37
+ * real android.media.Image, and BarcodeScanner.process() returns a real ML
38
+ * Kit Task<List<Barcode>> whose Task machinery isn't a simple mockable
39
+ * single-call the way ImageProxy is -- driving it would require faking ML
40
+ * Kit's Task/Executor internals, not just this class's collaborators.
41
+ */
42
+ class BarcodeDetectorTest {
43
+
44
+ private val zoomController = mock<ZoomController>()
45
+
46
+ // ============================================================
47
+ // formatName (companion) -- pure, exhaustive over declared formats
48
+ // ============================================================
49
+
50
+ @Test
51
+ fun `formatName maps every documented barcode format constant to its label`() {
52
+ assertEquals("QR", BarcodeDetector.formatName(Barcode.FORMAT_QR_CODE))
53
+ assertEquals("EAN_13", BarcodeDetector.formatName(Barcode.FORMAT_EAN_13))
54
+ assertEquals("EAN_8", BarcodeDetector.formatName(Barcode.FORMAT_EAN_8))
55
+ assertEquals("CODE_128", BarcodeDetector.formatName(Barcode.FORMAT_CODE_128))
56
+ assertEquals("CODE_39", BarcodeDetector.formatName(Barcode.FORMAT_CODE_39))
57
+ assertEquals("DATA_MATRIX", BarcodeDetector.formatName(Barcode.FORMAT_DATA_MATRIX))
58
+ assertEquals("PDF417", BarcodeDetector.formatName(Barcode.FORMAT_PDF417))
59
+ assertEquals("AZTEC", BarcodeDetector.formatName(Barcode.FORMAT_AZTEC))
60
+ assertEquals("UPC_A", BarcodeDetector.formatName(Barcode.FORMAT_UPC_A))
61
+ assertEquals("UPC_E", BarcodeDetector.formatName(Barcode.FORMAT_UPC_E))
62
+ }
63
+
64
+ @Test
65
+ fun `formatName falls back to UNKNOWN for an unrecognized format`() {
66
+ assertEquals("UNKNOWN", BarcodeDetector.formatName(-999))
67
+ }
68
+
69
+ // ============================================================
70
+ // close()
71
+ // ============================================================
72
+
73
+ @Test
74
+ fun `close before any scanner was built does not throw`() {
75
+ val detector = BarcodeDetector(zoomController) { _, _, _ -> }
76
+ detector.close()
77
+ }
78
+
79
+ @Test
80
+ fun `close is safe to call twice`() {
81
+ val detector = BarcodeDetector(zoomController) { _, _, _ -> }
82
+ detector.close()
83
+ detector.close()
84
+ }
85
+
86
+ // ============================================================
87
+ // suppressResults flag
88
+ // ============================================================
89
+
90
+ @Test
91
+ fun `suppressResults defaults to false and is settable`() {
92
+ val detector = BarcodeDetector(zoomController) { _, _, _ -> }
93
+ assertEquals(false, detector.suppressResults)
94
+ detector.suppressResults = true
95
+ assertEquals(true, detector.suppressResults)
96
+ }
97
+
98
+ // ============================================================
99
+ // Construction sanity
100
+ // ============================================================
101
+
102
+ @Test
103
+ fun `constructing BarcodeDetector does not throw`() {
104
+ val detector = BarcodeDetector(zoomController) { _, _, _ -> }
105
+ assertNotNull(detector)
106
+ }
107
+
108
+ // ============================================================
109
+ // processFrame (private) -- reached via reflection since
110
+ // buildImageAnalysis()'s analyzer lambda isn't otherwise reachable
111
+ // without a real ImageAnalysis pipeline.
112
+ // ============================================================
113
+
114
+ @Test
115
+ fun `processFrame closes the imageProxy and returns early when the image is null`() {
116
+ val detector = BarcodeDetector(zoomController) { _, _, _ -> }
117
+ val imageProxy = mock<ImageProxy>()
118
+ whenever(imageProxy.image).thenReturn(null)
119
+ val scanner = mock<BarcodeScanner>()
120
+
121
+ val method = BarcodeDetector::class.java.getDeclaredMethod(
122
+ "processFrame",
123
+ ImageProxy::class.java,
124
+ BarcodeScanner::class.java
125
+ )
126
+ method.isAccessible = true
127
+ method.invoke(detector, imageProxy, scanner)
128
+
129
+ verify(imageProxy).close()
130
+ org.mockito.Mockito.verifyNoInteractions(scanner)
131
+ }
132
+ }
@@ -0,0 +1,155 @@
1
+ package io.yourname.androidproject.camera
2
+
3
+ import org.junit.Assert.assertEquals
4
+ import org.junit.Assert.assertFalse
5
+ import org.junit.Assert.assertNull
6
+ import org.junit.Assert.assertTrue
7
+ import org.junit.Test
8
+ import org.mockito.kotlin.mock
9
+ import org.mockito.kotlin.verify
10
+ import org.mockito.kotlin.whenever
11
+
12
+ /**
13
+ * Unit tests for HoldController (Android coverage batch 3), previously
14
+ * entirely untested (0/28 lines).
15
+ *
16
+ * Scope: isNewValue() (pure AtomicReference compare-and-set logic) and
17
+ * reset() (clears state, no Handler dispatch required to observe its
18
+ * effect via barcodeDetector.suppressResults / lastDetectedValue) are
19
+ * covered directly. startHold()'s call into
20
+ * stateMachine.transition(HOLD) and barcodeDetector.suppressResults=true
21
+ * are also observable without the Handler ever firing.
22
+ *
23
+ * Deliberately out of scope, per this project's established
24
+ * PerfEventBufferTest precedent (documented there and required reading for
25
+ * this file): HoldController constructs a real
26
+ * Handler(Looper.getMainLooper()) at field-init time. There is no real
27
+ * Looper in a JVM unit test, so handler.postDelayed(...)'s posted runnable
28
+ * (the "hold ended, resume streaming" callback) never fires here -- the
29
+ * resumeRunnable's *body* (barcodeDetector.suppressResults = false,
30
+ * stateMachine.transition(STREAMING)) is not exercised or asserted on.
31
+ * Only the parts of startHold()/reset() that run synchronously before/
32
+ * around the postDelayed/removeCallbacks calls are covered.
33
+ */
34
+ class HoldControllerTest {
35
+
36
+ private val stateMachine = mock<VideoStreamStateMachine>()
37
+ private val barcodeDetector = mock<BarcodeDetector>()
38
+
39
+ private fun newController() = HoldController(stateMachine, barcodeDetector)
40
+
41
+ // ============================================================
42
+ // startHold()
43
+ // ============================================================
44
+
45
+ @Test
46
+ fun `startHold suppresses barcode results when the state transition succeeds`() {
47
+ whenever(stateMachine.transition(VideoStreamState.HOLD)).thenReturn(true)
48
+
49
+ val controller = newController()
50
+ controller.startHold()
51
+
52
+ // barcodeDetector is a Mockito mock -- its "suppressResults" setter
53
+ // is intercepted rather than writing a real backing field, so the
54
+ // observable effect is the setter invocation, not a later getter
55
+ // read (which would just return the default/stubbed value).
56
+ verify(barcodeDetector).suppressResults = true
57
+ verify(stateMachine).transition(VideoStreamState.HOLD)
58
+ }
59
+
60
+ @Test
61
+ fun `startHold does not suppress results when the state transition fails`() {
62
+ whenever(stateMachine.transition(VideoStreamState.HOLD)).thenReturn(false)
63
+
64
+ val controller = newController()
65
+ controller.startHold()
66
+
67
+ // Early return on a failed transition -- suppressResults is never
68
+ // touched.
69
+ org.mockito.Mockito.verifyNoInteractions(barcodeDetector)
70
+ }
71
+
72
+ @Test
73
+ fun `startHold called twice cancels the previously scheduled runnable`() {
74
+ whenever(stateMachine.transition(VideoStreamState.HOLD)).thenReturn(true)
75
+
76
+ val controller = newController()
77
+ controller.startHold()
78
+ // Second call should not throw even though a runnable is already
79
+ // pending -- exercises the resumeRunnable?.let { handler.removeCallbacks(it) }
80
+ // branch with a non-null previous runnable.
81
+ controller.startHold()
82
+
83
+ verify(barcodeDetector, org.mockito.kotlin.times(2)).suppressResults = true
84
+ }
85
+
86
+ // ============================================================
87
+ // reset()
88
+ // ============================================================
89
+
90
+ @Test
91
+ fun `reset clears suppressResults and lastDetectedValue`() {
92
+ whenever(stateMachine.transition(VideoStreamState.HOLD)).thenReturn(true)
93
+
94
+ val controller = newController()
95
+ controller.isNewValue("abc123")
96
+ controller.startHold()
97
+
98
+ controller.reset()
99
+
100
+ // suppressResults is set true by startHold(), then false again by
101
+ // reset() -- verify the final call rather than reading back a
102
+ // mocked getter (see the startHold tests above for why).
103
+ verify(barcodeDetector).suppressResults = false
104
+ assertNull(controller.lastDetectedValue)
105
+ }
106
+
107
+ @Test
108
+ fun `reset with no pending hold does not throw`() {
109
+ val controller = newController()
110
+ controller.reset()
111
+ assertNull(controller.lastDetectedValue)
112
+ }
113
+
114
+ // ============================================================
115
+ // isNewValue()
116
+ // ============================================================
117
+
118
+ @Test
119
+ fun `isNewValue returns true for the first value seen and updates lastDetectedValue`() {
120
+ val controller = newController()
121
+
122
+ val result = controller.isNewValue("abc123")
123
+
124
+ assertTrue(result)
125
+ assertEquals("abc123", controller.lastDetectedValue)
126
+ }
127
+
128
+ @Test
129
+ fun `isNewValue returns false for a repeat of the last detected value`() {
130
+ val controller = newController()
131
+
132
+ controller.isNewValue("abc123")
133
+ val result = controller.isNewValue("abc123")
134
+
135
+ assertFalse(result)
136
+ assertEquals("abc123", controller.lastDetectedValue)
137
+ }
138
+
139
+ @Test
140
+ fun `isNewValue returns true when the value differs from the last detected one`() {
141
+ val controller = newController()
142
+
143
+ controller.isNewValue("abc123")
144
+ val result = controller.isNewValue("xyz789")
145
+
146
+ assertTrue(result)
147
+ assertEquals("xyz789", controller.lastDetectedValue)
148
+ }
149
+
150
+ @Test
151
+ fun `lastDetectedValue is null before any detection`() {
152
+ val controller = newController()
153
+ assertNull(controller.lastDetectedValue)
154
+ }
155
+ }
@@ -0,0 +1,150 @@
1
+ package io.yourname.androidproject.camera
2
+
3
+ import android.content.Context
4
+ import androidx.camera.core.Camera
5
+ import androidx.camera.core.CameraControl
6
+ import com.google.common.util.concurrent.ListenableFuture
7
+ import org.junit.Assert.assertEquals
8
+ import org.junit.Assert.assertNotNull
9
+ import org.junit.Assert.assertTrue
10
+ import org.junit.Test
11
+ import org.mockito.kotlin.any
12
+ import org.mockito.kotlin.argumentCaptor
13
+ import org.mockito.kotlin.doReturn
14
+ import org.mockito.kotlin.mock
15
+ import org.mockito.kotlin.verify
16
+ import org.mockito.kotlin.whenever
17
+
18
+ /**
19
+ * Unit tests for TorchController (Android coverage batch 3), previously
20
+ * entirely untested (0/26 lines).
21
+ *
22
+ * Scope: attachCamera/detachCamera (pure field assignment) and setTorch's
23
+ * three branches (no camera attached, front-camera guard, and the actual
24
+ * enableTorch(...).addListener(...) call on a mocked Camera/CameraControl/
25
+ * ListenableFuture) are all covered without needing a real CameraX
26
+ * pipeline -- Camera/CameraControl are interfaces and ListenableFuture is
27
+ * a plain interface too, all mockable. notifyReset() is pure (invokes the
28
+ * callback, logs).
29
+ *
30
+ * ContextCompat.getMainExecutor(context) is called with a mocked Context;
31
+ * per DownloadUtilsTest/OfflineCacheServiceTest precedent, no
32
+ * mockStatic(ContextCompat) is needed here -- getMainExecutor() on a
33
+ * plain mocked Context under the mockable android.jar
34
+ * (isReturnDefaultValues=true / Robolectric not in use) still needs a
35
+ * Looper to resolve on a real device, but in this project's JVM-mock
36
+ * setup, ContextCompat.getMainExecutor() ultimately just wraps
37
+ * context.getMainLooper(); a mocked Context's getMainLooper() returns
38
+ * null under the mockable jar's default-value stubbing. Since the
39
+ * listener is added to the ListenableFuture's addListener(Runnable,
40
+ * Executor) directly and we intercept that call on a mocked
41
+ * ListenableFuture (never a real Guava future backed by the executor),
42
+ * the executor value itself is never actually invoked/dereferenced here
43
+ * -- only that setTorch reaches the addListener call and, when we
44
+ * manually invoke the captured Runnable, that onTorchChanged fires.
45
+ */
46
+ class TorchControllerTest {
47
+
48
+ private val context = mock<Context>()
49
+
50
+ // ============================================================
51
+ // attachCamera / detachCamera
52
+ // ============================================================
53
+
54
+ @Test
55
+ fun `attachCamera then detachCamera does not throw`() {
56
+ val callback = mutableListOf<Boolean>()
57
+ val controller = TorchController(context) { callback.add(it) }
58
+ val camera = mock<Camera>()
59
+
60
+ controller.attachCamera(camera, "back")
61
+ controller.detachCamera()
62
+
63
+ assertNotNull(controller)
64
+ }
65
+
66
+ // ============================================================
67
+ // setTorch
68
+ // ============================================================
69
+
70
+ @Test
71
+ fun `setTorch with no camera attached does not throw and does not invoke callback`() {
72
+ val callback = mutableListOf<Boolean>()
73
+ val controller = TorchController(context) { callback.add(it) }
74
+
75
+ controller.setTorch(true)
76
+
77
+ assertTrue(callback.isEmpty())
78
+ }
79
+
80
+ @Test
81
+ fun `setTorch on a front-facing camera is ignored`() {
82
+ val callback = mutableListOf<Boolean>()
83
+ val controller = TorchController(context) { callback.add(it) }
84
+ val camera = mock<Camera>()
85
+ controller.attachCamera(camera, "front")
86
+
87
+ controller.setTorch(true)
88
+
89
+ assertTrue(callback.isEmpty())
90
+ org.mockito.Mockito.verifyNoInteractions(camera)
91
+ }
92
+
93
+ @Test
94
+ fun `setTorch on a back-facing camera enables torch and fires the callback once the future resolves`() {
95
+ val callback = mutableListOf<Boolean>()
96
+ val controller = TorchController(context) { callback.add(it) }
97
+ val camera = mock<Camera>()
98
+ val cameraControl = mock<CameraControl>()
99
+ @Suppress("UNCHECKED_CAST")
100
+ val future = mock<ListenableFuture<Void>>()
101
+ whenever(camera.cameraControl).doReturn(cameraControl)
102
+ whenever(cameraControl.enableTorch(true)).doReturn(future)
103
+ controller.attachCamera(camera, "back")
104
+
105
+ controller.setTorch(true)
106
+
107
+ // Capture the Runnable passed to addListener and invoke it
108
+ // manually -- there's no real Looper/Executor running the
109
+ // listener for us in a JVM unit test.
110
+ val runnableCaptor = argumentCaptor<Runnable>()
111
+ verify(future).addListener(runnableCaptor.capture(), any())
112
+ runnableCaptor.firstValue.run()
113
+
114
+ assertEquals(listOf(true), callback)
115
+ }
116
+
117
+ @Test
118
+ fun `setTorch(false) disables torch`() {
119
+ val callback = mutableListOf<Boolean>()
120
+ val controller = TorchController(context) { callback.add(it) }
121
+ val camera = mock<Camera>()
122
+ val cameraControl = mock<CameraControl>()
123
+ val future = mock<ListenableFuture<Void>>()
124
+ whenever(camera.cameraControl).doReturn(cameraControl)
125
+ whenever(cameraControl.enableTorch(false)).doReturn(future)
126
+ controller.attachCamera(camera, "back")
127
+
128
+ controller.setTorch(false)
129
+
130
+ val runnableCaptor = argumentCaptor<Runnable>()
131
+ verify(future).addListener(runnableCaptor.capture(), any())
132
+ runnableCaptor.firstValue.run()
133
+
134
+ assertEquals(listOf(false), callback)
135
+ }
136
+
137
+ // ============================================================
138
+ // notifyReset
139
+ // ============================================================
140
+
141
+ @Test
142
+ fun `notifyReset invokes the callback with false`() {
143
+ val callback = mutableListOf<Boolean>()
144
+ val controller = TorchController(context) { callback.add(it) }
145
+
146
+ controller.notifyReset()
147
+
148
+ assertEquals(listOf(false), callback)
149
+ }
150
+ }
@@ -0,0 +1,120 @@
1
+ package io.yourname.androidproject.camera
2
+
3
+ import org.junit.Assert.assertEquals
4
+ import org.junit.Assert.assertFalse
5
+ import org.junit.Assert.assertTrue
6
+ import org.junit.Test
7
+
8
+ /**
9
+ * Unit tests for VideoStreamStateMachine, ported from iOS's
10
+ * VideoStreamStateMachineTests.swift. Android's implementation uses
11
+ * CopyOnWriteArrayList + @Synchronized rather than iOS's NSLock +
12
+ * weak-listener boxing, so there's no weak-dealloc-compaction case to
13
+ * port — everything else (valid/invalid transitions, listener
14
+ * notify/no-notify, removeListener) carries over directly.
15
+ */
16
+ class VideoStreamStateMachineTest {
17
+
18
+ private class RecordingListener : VideoStreamStateListener {
19
+ val events = mutableListOf<Pair<VideoStreamState, VideoStreamState>>()
20
+ override fun onStateChanged(prev: VideoStreamState, next: VideoStreamState) {
21
+ events.add(prev to next)
22
+ }
23
+ }
24
+
25
+ @Test
26
+ fun `a valid transition updates state and returns true`() {
27
+ val machine = VideoStreamStateMachine()
28
+
29
+ val result = machine.transition(VideoStreamState.STARTING)
30
+
31
+ assertTrue(result)
32
+ assertEquals(VideoStreamState.STARTING, machine.state)
33
+ }
34
+
35
+ @Test
36
+ fun `an invalid transition leaves state unchanged and returns false`() {
37
+ val machine = VideoStreamStateMachine()
38
+
39
+ // IDLE -> STREAMING is not a valid direct transition.
40
+ val result = machine.transition(VideoStreamState.STREAMING)
41
+
42
+ assertFalse(result)
43
+ assertEquals(VideoStreamState.IDLE, machine.state)
44
+ }
45
+
46
+ @Test
47
+ fun `isActive reflects the current state`() {
48
+ val machine = VideoStreamStateMachine()
49
+ assertFalse(machine.isActive)
50
+
51
+ machine.transition(VideoStreamState.STARTING)
52
+ assertFalse(machine.isActive)
53
+
54
+ machine.transition(VideoStreamState.STREAMING)
55
+ assertTrue(machine.isActive)
56
+ }
57
+
58
+ @Test
59
+ fun `a listener receives onStateChanged on a valid transition`() {
60
+ val machine = VideoStreamStateMachine()
61
+ val listener = RecordingListener()
62
+ machine.addListener(listener)
63
+
64
+ machine.transition(VideoStreamState.STARTING)
65
+
66
+ assertEquals(listOf(VideoStreamState.IDLE to VideoStreamState.STARTING), listener.events)
67
+ }
68
+
69
+ @Test
70
+ fun `a listener does not fire on an invalid transition`() {
71
+ val machine = VideoStreamStateMachine()
72
+ val listener = RecordingListener()
73
+ machine.addListener(listener)
74
+
75
+ machine.transition(VideoStreamState.STREAMING) // invalid from IDLE
76
+
77
+ assertTrue(listener.events.isEmpty())
78
+ }
79
+
80
+ @Test
81
+ fun `removeListener stops further notifications`() {
82
+ val machine = VideoStreamStateMachine()
83
+ val listener = RecordingListener()
84
+ machine.addListener(listener)
85
+ machine.transition(VideoStreamState.STARTING)
86
+ assertEquals(1, listener.events.size)
87
+
88
+ machine.removeListener(listener)
89
+ machine.transition(VideoStreamState.STREAMING)
90
+
91
+ // No new event recorded after removal.
92
+ assertEquals(1, listener.events.size)
93
+ }
94
+
95
+ @Test
96
+ fun `addListener does not register the same listener twice`() {
97
+ val machine = VideoStreamStateMachine()
98
+ val listener = RecordingListener()
99
+ machine.addListener(listener)
100
+ machine.addListener(listener)
101
+
102
+ machine.transition(VideoStreamState.STARTING)
103
+
104
+ assertEquals(1, listener.events.size)
105
+ }
106
+
107
+ @Test
108
+ fun `multiple listeners all receive the same transition event`() {
109
+ val machine = VideoStreamStateMachine()
110
+ val listenerA = RecordingListener()
111
+ val listenerB = RecordingListener()
112
+ machine.addListener(listenerA)
113
+ machine.addListener(listenerB)
114
+
115
+ machine.transition(VideoStreamState.STARTING)
116
+
117
+ assertEquals(1, listenerA.events.size)
118
+ assertEquals(1, listenerB.events.size)
119
+ }
120
+ }
@@ -0,0 +1,62 @@
1
+ package io.yourname.androidproject.camera
2
+
3
+ import org.junit.Assert.assertEquals
4
+ import org.junit.Assert.assertFalse
5
+ import org.junit.Assert.assertTrue
6
+ import org.junit.Test
7
+
8
+ /**
9
+ * Exhaustive truth-table tests for VideoStreamState, ported directly from
10
+ * iOS's VideoStreamStateTests.swift (CoreLogic/Tests/CatalystCoreLogicTests)
11
+ * — this Kotlin enum is a byte-for-byte match of the Swift one (same
12
+ * states, same canTransitionTo table, same isActive derivation), so the
13
+ * same 6x6 truth table applies unchanged.
14
+ */
15
+ class VideoStreamStateTest {
16
+
17
+ private val allStates = VideoStreamState.values().toList()
18
+
19
+ // Every valid transition documented in VideoStreamState.kt's own
20
+ // header comment, expressed as (from, to) pairs.
21
+ private val validTransitions = setOf(
22
+ VideoStreamState.IDLE to VideoStreamState.STARTING,
23
+ VideoStreamState.STARTING to VideoStreamState.STREAMING,
24
+ VideoStreamState.STARTING to VideoStreamState.IDLE,
25
+ VideoStreamState.STREAMING to VideoStreamState.HOLD,
26
+ VideoStreamState.HOLD to VideoStreamState.STREAMING,
27
+ VideoStreamState.STREAMING to VideoStreamState.FLIPPING,
28
+ VideoStreamState.FLIPPING to VideoStreamState.STREAMING,
29
+ VideoStreamState.STREAMING to VideoStreamState.STOPPING,
30
+ VideoStreamState.FLIPPING to VideoStreamState.STOPPING,
31
+ VideoStreamState.HOLD to VideoStreamState.STOPPING,
32
+ VideoStreamState.STOPPING to VideoStreamState.IDLE
33
+ )
34
+
35
+ @Test
36
+ fun `canTransitionTo matches the documented transition table for all 36 state pairs`() {
37
+ for (from in allStates) {
38
+ for (to in allStates) {
39
+ val expected = validTransitions.contains(from to to)
40
+ assertEquals(
41
+ "canTransitionTo($from -> $to) expected $expected",
42
+ expected,
43
+ from.canTransitionTo(to)
44
+ )
45
+ }
46
+ }
47
+ }
48
+
49
+ @Test
50
+ fun `isActive is true for STREAMING, HOLD, and FLIPPING`() {
51
+ assertTrue(VideoStreamState.STREAMING.isActive)
52
+ assertTrue(VideoStreamState.HOLD.isActive)
53
+ assertTrue(VideoStreamState.FLIPPING.isActive)
54
+ }
55
+
56
+ @Test
57
+ fun `isActive is false for IDLE, STARTING, and STOPPING`() {
58
+ assertFalse(VideoStreamState.IDLE.isActive)
59
+ assertFalse(VideoStreamState.STARTING.isActive)
60
+ assertFalse(VideoStreamState.STOPPING.isActive)
61
+ }
62
+ }