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.
- package/changelog.md +185 -71
- package/dist/errors/generateDocs.js +100 -0
- package/dist/errors/index.js +243 -0
- package/dist/errors/registry.js +483 -0
- package/dist/errors-index.json +506 -0
- package/dist/globals.d.ts +62 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.jsx +5 -8
- package/dist/logger.js +1 -1
- package/dist/native/androidProject/app/build.gradle.kts +142 -7
- package/dist/native/androidProject/app/src/main/AndroidManifest.xml +4 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +2 -2
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/BarcodeDetector.kt +3 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/CameraSessionManager.kt +3 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/FrameworkServerUtils.kt +43 -8
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/BridgeUtilsTest.kt +181 -1
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/CameraUtilsTest.kt +156 -236
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FileSizeRouterUtilsTest.kt +471 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FileUtilsTest.kt +222 -226
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FrameworkServerUtilsTest.kt +300 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/IntentUtilsTest.kt +244 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/MetricsMonitorTest.kt +359 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/NativeBridgeTest.kt +108 -63
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/NotificationUtilsTest.kt +329 -385
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/OfflineCacheServiceTest.kt +302 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/PerfEventBufferTest.kt +133 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/UtilsKtTest.kt +152 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/WebCacheManagerTest.kt +145 -324
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/BarcodeDetectorTest.kt +132 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/HoldControllerTest.kt +155 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/TorchControllerTest.kt +150 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/VideoStreamStateMachineTest.kt +120 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/VideoStreamStateTest.kt +62 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/ViewfinderMapperTest.kt +143 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/ZoomControllerTest.kt +421 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/design/DesignTokensTest.kt +207 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/plugins/PluginBridgeTest.kt +368 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/security/FridaDetectorTest.kt +193 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/security/SecurityCheckManagerTest.kt +260 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/security/SecurityCheckSchedulerTest.kt +166 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/DeviceInfoUtilsTest.kt +167 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/DownloadUtilsTest.kt +197 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/NetworkMonitorTest.kt +271 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/PushNotificationUtilsTest.kt +119 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/SafeAreaUtilsTest.kt +108 -0
- package/dist/native/androidProject/build.gradle.kts +1 -0
- package/dist/native/androidProject/gradle/libs.versions.toml +26 -0
- package/dist/native/androidProject/settings.gradle.kts +11 -8
- package/dist/native/androidSetup.js +2 -2
- package/dist/native/bridge/WebBridge.d.ts +98 -0
- package/dist/native/bridge/WebBridge.js +40 -6
- package/dist/native/bridge/constants/NativeInterfaces.d.ts +391 -0
- package/dist/native/bridge/errors.d.ts +67 -0
- package/dist/native/bridge/errors.js +66 -151
- package/dist/native/bridge/hooks/index.d.ts +13 -0
- package/dist/native/bridge/hooks/index.js +0 -6
- package/dist/native/bridge/hooks/useAI.d.ts +1 -0
- package/dist/native/bridge/hooks/useAI.js +3 -1
- package/dist/native/bridge/hooks/useCamera.d.ts +86 -0
- package/dist/native/bridge/hooks/useCamera.js +35 -6
- package/dist/native/bridge/hooks/useDataProtection.d.ts +17 -0
- package/dist/native/bridge/hooks/useDeviceInfo.d.ts +34 -0
- package/dist/native/bridge/hooks/useDeviceInfo.js +5 -0
- package/dist/native/bridge/hooks/useFilePicker.d.ts +53 -0
- package/dist/native/bridge/hooks/useFilePicker.js +13 -0
- package/dist/native/bridge/hooks/useGoogleSignIn.d.ts +12 -0
- package/dist/native/bridge/hooks/useHapticFeedback.d.ts +76 -0
- package/dist/native/bridge/hooks/useHapticFeedback.js +6 -1
- package/dist/native/bridge/hooks/useIntent.d.ts +33 -0
- package/dist/native/bridge/hooks/useIntent.js +2 -0
- package/dist/native/bridge/hooks/useNativeTransition.d.ts +53 -0
- package/dist/native/bridge/hooks/useNativeTransition.js +12 -5
- package/dist/native/bridge/hooks/useNetworkStatus.d.ts +22 -0
- package/dist/native/bridge/hooks/useNetworkStatus.js +7 -0
- package/dist/native/bridge/hooks/useNotification.d.ts +83 -0
- package/dist/native/bridge/hooks/useNotification.js +81 -34
- package/dist/native/bridge/hooks/useSafeArea.d.ts +12 -0
- package/dist/native/bridge/hooks/useSafeArea.js +8 -0
- package/dist/native/bridge/hooks/useVideoStream.d.ts +67 -0
- package/dist/native/bridge/hooks/useVideoStream.js +38 -52
- package/dist/native/bridge/hooks/utils.d.ts +4 -0
- package/dist/native/bridge/hooks.d.ts +1 -0
- package/dist/native/bridge/perf/collectors/ApiCollector.d.ts +30 -0
- package/dist/native/bridge/perf/collectors/ApiCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/CacheCollector.d.ts +43 -0
- package/dist/native/bridge/perf/collectors/CacheCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/HookCollector.d.ts +37 -0
- package/dist/native/bridge/perf/collectors/HookCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/InsightsCollector.d.ts +103 -0
- package/dist/native/bridge/perf/collectors/InsightsCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/InteractionCollector.d.ts +53 -0
- package/dist/native/bridge/perf/collectors/InteractionCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/KeyboardCollector.d.ts +30 -0
- package/dist/native/bridge/perf/collectors/KeyboardCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/NavigationCollector.d.ts +37 -0
- package/dist/native/bridge/perf/collectors/NavigationCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/NetworkTimingCollector.d.ts +30 -0
- package/dist/native/bridge/perf/collectors/NetworkTimingCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/RenderCollector.d.ts +47 -0
- package/dist/native/bridge/perf/collectors/RenderCollector.js +8 -0
- package/dist/native/bridge/perf/collectors/ScrollCollector.d.ts +29 -0
- package/dist/native/bridge/perf/collectors/ScrollCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/ThreadCollector.d.ts +31 -0
- package/dist/native/bridge/perf/collectors/ThreadCollector.js +4 -0
- package/dist/native/bridge/perf/core/constants.d.ts +76 -0
- package/dist/native/bridge/perf/core/measure.d.ts +39 -0
- package/dist/native/bridge/perf/core/measure.js +4 -0
- package/dist/native/bridge/perf/core/session.d.ts +49 -0
- package/dist/native/bridge/perf/core/session.js +4 -0
- package/dist/native/bridge/perf/core/store.d.ts +37 -0
- package/dist/native/bridge/perf/core/store.js +4 -0
- package/dist/native/bridge/perf/core/summary.d.ts +60 -0
- package/dist/native/bridge/perf/core/traceExport.d.ts +10 -0
- package/dist/native/bridge/perf/core/waterfalls.d.ts +2 -0
- package/dist/native/bridge/perf/index.d.ts +63 -0
- package/dist/native/bridge/perf/index.js +4 -0
- package/dist/native/bridge/safeArea.d.ts +11 -0
- package/dist/native/bridge/safeArea.js +2 -0
- package/dist/native/bridge/useBaseHook.d.ts +137 -0
- package/dist/native/bridge/useBaseHook.js +34 -4
- package/dist/native/bridge/utils/CameraUtils.d.ts +94 -0
- package/dist/native/bridge/utils/CameraUtils.js +4 -0
- package/dist/native/bridge/utils/FileObjectConverter.d.ts +36 -0
- package/dist/native/bridge/utils/NativeBridge.d.ts +270 -0
- package/dist/native/bridge/utils/NativeBridge.js +10 -0
- package/dist/native/buildAndroid/index.js +1 -1
- package/dist/native/buildAppAndroid.js +9 -0
- package/dist/native/buildAppIos.js +16 -1
- package/dist/native/buildErrorFormat.js +57 -0
- package/dist/native/buildIos/config.js +15 -1
- package/dist/native/buildIos/index.js +1 -1
- package/dist/native/buildIos/plugins.js +10 -1
- package/dist/native/iosnativeWebView/CoreLogic/Package.swift +36 -0
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Camera/VideoStreamState.swift +1 -1
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Camera/VideoStreamStateMachine.swift +6 -4
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/BootTimingUtility.swift +3 -3
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/FrameworkServerUtils.swift +14 -9
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/NetworkMonitor.swift +11 -9
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/NotificationHandlerProvider.swift +1 -1
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/URLWhitelistManager.swift +7 -7
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/AnyCodableTests.swift +154 -0
- package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/BootTimingUtilityTests.swift +23 -5
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/CatalystConstantsTests.swift +192 -0
- package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/ConfigMappingTests.swift +1 -1
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/FrameworkServerUtilsErrorPathTests.swift +106 -0
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/FrameworkServerUtilsLoopbackTests.swift +243 -0
- package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/FrameworkServerUtilsTests.swift +1 -1
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/NetworkMonitorTests.swift +119 -0
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/NotificationHandlerProviderTests.swift +59 -0
- package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/NotificationHandlerTests.swift +1 -1
- package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/URLWhitelistManagerTests.swift +1 -1
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/VideoStreamStateMachineTests.swift +171 -0
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/VideoStreamStateTests.swift +91 -0
- package/dist/native/iosnativeWebView/Package.swift +10 -9
- package/dist/native/iosnativeWebView/Sources/CatalystNotifications/LocalNotificationHandler.swift +7 -2
- package/dist/native/iosnativeWebView/Sources/CatalystNotifications/NotificationManager.swift +1 -2
- package/dist/native/iosnativeWebView/Sources/CatalystNotifications/PushNotificationHandler.swift +5 -4
- package/dist/native/iosnativeWebView/Sources/Core/Camera/CameraSessionManager.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/HoldController.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/NativeCameraManager.swift +6 -5
- package/dist/native/iosnativeWebView/Sources/Core/Camera/ZoomController.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/PluginBridge.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +31 -30
- package/dist/native/iosnativeWebView/Sources/Core/Utils/CameraTransportUtils.swift +2 -1
- package/dist/native/iosnativeWebView/Sources/Core/Utils/DeviceInfoUtils.swift +1 -1
- package/dist/native/iosnativeWebView/Sources/Core/Utils/FilePickerHandler.swift +22 -19
- package/dist/native/iosnativeWebView/Sources/Core/Utils/ImageHandler.swift +40 -39
- package/dist/native/iosnativeWebView/Sources/Core/Utils/PerfEventBuffer.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/ResourceURLProtocol.swift +15 -15
- package/dist/native/iosnativeWebView/Sources/Core/Utils/WebKitConfig.swift +4 -3
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeCommandHandler.swift +6 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeDelegateHandler.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeFileHandler.swift +2 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeJavaScriptInterface.swift +2 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeMessageValidator.swift +3 -5
- package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +20 -13
- package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashView.swift +6 -5
- package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashViewModel.swift +14 -13
- package/dist/native/iosnativeWebView/Sources/Core/WebView/TransitionManager.swift +3 -3
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +9 -8
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewModel.swift +7 -6
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +24 -25
- package/dist/native/iosnativeWebView/iosnativeWebView/AppDelegate.swift +6 -1
- package/dist/native/iosnativeWebView/iosnativeWebView/ContentView.swift +1 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/iosnativeWebViewApp.swift +4 -0
- package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +2 -22
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeMessageValidatorTests.swift +1 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/CacheManagerTests.swift +1 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/FilePickerHandlerTests.swift +2 -1
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/NativeBridgeTests.swift +15 -9
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/WebViewTests.swift +1 -0
- package/dist/native/setupEmulatorIos.js +2 -2
- package/dist/native/terminalProgress.js +17 -17
- package/dist/router/ClientRouter.d.ts +8 -0
- package/dist/router/ClientRouter.js +13 -11
- package/dist/router/ServerRouter.d.ts +13 -0
- package/dist/router/ServerRouter.js +19 -7
- package/dist/scripts/build.js +14 -2
- package/dist/scripts/loadEnvironmentVariables.js +3 -2
- package/dist/scripts/plugins.js +1 -1
- package/dist/scripts/preServerInit.js +49 -3
- package/dist/scripts/preflight.js +111 -0
- package/dist/scripts/scriptUtils.js +46 -1
- package/dist/scripts/serve.js +7 -3
- package/dist/scripts/start.js +8 -3
- package/dist/server/expressServer.d.ts +1 -0
- package/dist/server/expressServer.js +247 -271
- package/dist/server/manifestCache.d.ts +2 -0
- package/dist/server/manifestCache.js +25 -33
- package/dist/server/renderer/ChunkExtractor.d.ts +44 -0
- package/dist/server/renderer/ChunkExtractor.js +113 -125
- package/dist/server/renderer/document/Body.d.ts +21 -0
- package/dist/server/renderer/document/Body.jsx +45 -45
- package/dist/server/renderer/document/FastRefresh.d.ts +3 -0
- package/dist/server/renderer/document/FastRefresh.jsx +7 -13
- package/dist/server/renderer/document/Head.d.ts +20 -0
- package/dist/server/renderer/document/Head.jsx +32 -32
- package/dist/server/renderer/document/index.d.ts +2 -0
- package/dist/server/renderer/document/index.jsx +2 -2
- package/dist/server/renderer/extract.d.ts +67 -0
- package/dist/server/renderer/extract.js +92 -89
- package/dist/server/renderer/handler.d.ts +2 -0
- package/dist/server/renderer/handler.jsx +401 -404
- package/dist/server/renderer/index.d.ts +2 -0
- package/dist/server/renderer/index.js +5 -8
- package/dist/server/renderer/render.d.ts +35 -0
- package/dist/server/renderer/render.js +25 -32
- package/dist/server/utils/botDetectionMiddleware.d.ts +1 -0
- package/dist/server/utils/botDetectionMiddleware.js +33 -23
- package/dist/server/utils/cjsRequire.d.ts +8 -0
- package/dist/server/utils/cjsRequire.js +2 -2
- package/dist/server/utils/userAgentUtil.d.ts +7 -0
- package/dist/server/utils/userAgentUtil.js +68 -71
- package/dist/server/utils/validator.d.ts +31 -0
- package/dist/server/utils/validator.js +133 -158
- package/dist/vite/inject-cache-key-plugin.js +3 -27
- package/dist/vite/moduleAliases.js +36 -0
- package/dist/vite/node-loader.mjs +2 -29
- package/dist/vite/resolveDevServerConfig.js +4 -1
- package/dist/vite/vite.config.js +2 -29
- package/dist/web-router/components/MetaTag.d.ts +4 -0
- package/dist/web-router/components/MetaTag.jsx +26 -28
- package/dist/web-router/components/MetaTag.test.jsx +100 -0
- package/dist/web-router/components/RouterDataProvider.d.ts +96 -0
- package/dist/web-router/components/RouterDataProvider.jsx +260 -259
- package/dist/web-router/components/RouterDataProvider.server.test.jsx +98 -0
- package/dist/web-router/components/RouterDataProvider.test.jsx +310 -0
- package/dist/web-router/components/Split.d.ts +74 -0
- package/dist/web-router/components/Split.jsx +147 -156
- package/dist/web-router/components/Split.server.test.jsx +52 -0
- package/dist/web-router/components/Split.test.jsx +167 -0
- package/dist/web-router/components/SplitInview.d.ts +22 -0
- package/dist/web-router/components/SplitInview.jsx +78 -89
- package/dist/web-router/components/SplitInview.test.jsx +112 -0
- package/dist/web-router/components/SsrRequestContext.d.ts +20 -0
- package/dist/web-router/components/SsrRequestContext.jsx +17 -7
- package/dist/web-router/components/SsrRequestContext.test.jsx +53 -0
- package/dist/web-router/context.d.ts +15 -0
- package/dist/web-router/context.jsx +5 -5
- package/dist/web-router/context.test.jsx +10 -0
- package/dist/web-router/utils/metaDataUtils.d.ts +24 -0
- package/dist/web-router/utils/metaDataUtils.jsx +86 -86
- package/dist/web-router/utils/metaDataUtils.test.jsx +188 -0
- package/dist/web-router/vitest.setup.js +12 -0
- package/mcp_v2/knowledge-base.json +115 -17
- package/mcp_v2/mcp.js +141 -3
- package/mcp_v2/package-lock.json +2031 -0
- package/mcp_v2/package.json +13 -2
- package/mcp_v2/scripts/copyErrorsIndex.cjs +34 -0
- package/mcp_v2/test/errors.test.ts +123 -0
- package/mcp_v2/test/github.test.ts +177 -0
- package/mcp_v2/tools/errors.d.ts +20 -0
- package/mcp_v2/tools/errors.js +63 -0
- package/mcp_v2/tools/github.d.ts +41 -0
- package/mcp_v2/tools/github.js +231 -0
- package/mcp_v2/vitest.config.mjs +22 -0
- package/package.json +60 -11
- package/test/errors/docsDrift.test.ts +113 -0
- package/test/errors/index.test.ts +200 -0
- package/test/errors/registry.test.ts +108 -0
- package/test/scripts/preflight.test.ts +118 -0
- package/test/server/ChunkExtractor.test.ts +97 -0
- package/test/server/extract.test.ts +128 -0
- package/test/server/fixtures/template/server/document.jsx +19 -0
- package/test/server/fixtures/template/server/index.js +10 -0
- package/test/server/fixtures/template/src/js/containers/App/index.jsx +33 -0
- package/test/server/fixtures/template/src/js/routes/index.jsx +72 -0
- package/test/server/fixtures/template/src/js/routes/utils.jsx +39 -0
- package/test/server/fixtures/template/src/js/store/index.js +19 -0
- package/test/server/handler.assets.test.ts +330 -0
- package/test/server/handler.test.ts +208 -0
- package/test/server/manifestCache.test.ts +71 -0
- package/test/server/render.test.ts +71 -0
- package/test/server/scriptUtils.test.ts +103 -0
- package/test/server/userAgentUtil.test.ts +48 -0
- package/test/server/validator.test.ts +225 -0
- package/test/vite/resolveDevServerConfig.test.js +4 -1
- package/tsconfig.test.json +23 -0
- package/vitest.config.ts +93 -0
- package/dist/caching.js +0 -1
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/DeviceInfoUtilsTest.kt +0 -187
- package/dist/native/bridge/standardizedHooks.js +0 -611
- package/dist/router.js +0 -1
- package/dist/scripts/validator.js +0 -149
- package/dist/web-router/hooks.jsx +0 -72
- package/dist/web-router/types/components/MetaTag.d.ts +0 -2
- package/dist/web-router/types/components/RouterDataProvider.d.ts +0 -95
- package/dist/web-router/types/context.d.ts +0 -14
- package/dist/web-router/types/hooks.d.ts +0 -1
- package/dist/web-router/types/index.d.ts +0 -3
- package/dist/web-router/types/utils/metaDataUtils.d.ts +0 -40
- /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Constants/CatalystConstants.swift +0 -0
- /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Constants/ConfigConstants.swift +0 -0
- /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Notifications/NotificationModels.swift +0 -0
- /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Protocols/NotificationHandlerProtocol.swift +0 -0
- /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/NullNotificationHandler.swift +0 -0
|
@@ -87,6 +87,11 @@ fun getLocalIpAddress(): String {
|
|
|
87
87
|
plugins {
|
|
88
88
|
alias(libs.plugins.android.application)
|
|
89
89
|
alias(libs.plugins.jetbrains.kotlin.android)
|
|
90
|
+
jacoco
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
jacoco {
|
|
94
|
+
toolVersion = libs.versions.jacoco.get()
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
android {
|
|
@@ -215,18 +220,24 @@ android {
|
|
|
215
220
|
java.srcDirs("src/noFcm/java")
|
|
216
221
|
}
|
|
217
222
|
// AI has no source-set swap — CatalystAIBridge self-registers via ServiceLoader
|
|
218
|
-
// when
|
|
223
|
+
// when catalyst-ai is on the classpath.
|
|
219
224
|
}
|
|
220
225
|
}
|
|
221
226
|
}
|
|
222
227
|
|
|
223
228
|
configurations.all {
|
|
224
229
|
resolutionStrategy.eachDependency {
|
|
230
|
+
// kotlin-reflect is deliberately NOT forced here (unlike the
|
|
231
|
+
// stdlib artifacts below) — mockito-kotlin 6.x requires
|
|
232
|
+
// kotlin-reflect >=2.1.20, newer than this project's 2.0.21
|
|
233
|
+
// compiler version. kotlin-reflect is forward-compatible with
|
|
234
|
+
// older stdlib/compiler versions for the reflection use cases
|
|
235
|
+
// actually exercised in tests, so it resolves to whatever's
|
|
236
|
+
// requested (libs.kotlin.reflect, currently 2.1.20) instead.
|
|
225
237
|
if (requested.group == "org.jetbrains.kotlin" &&
|
|
226
238
|
(requested.name == "kotlin-stdlib" ||
|
|
227
239
|
requested.name == "kotlin-stdlib-jdk7" ||
|
|
228
|
-
requested.name == "kotlin-stdlib-jdk8"
|
|
229
|
-
requested.name == "kotlin-reflect")) {
|
|
240
|
+
requested.name == "kotlin-stdlib-jdk8")) {
|
|
230
241
|
useVersion("2.0.21")
|
|
231
242
|
}
|
|
232
243
|
}
|
|
@@ -238,6 +249,16 @@ dependencies {
|
|
|
238
249
|
implementation(libs.material)
|
|
239
250
|
implementation(libs.androidx.constraintlayout)
|
|
240
251
|
testImplementation(libs.junit)
|
|
252
|
+
// #413: Mockito for JVM unit tests (app/src/test). mockito-core 5.x
|
|
253
|
+
// defaults to the inline mock-maker, so Kotlin's final classes don't
|
|
254
|
+
// need extra `open` keywords. mockito-kotlin adds the idiomatic
|
|
255
|
+
// whenever()/mock<T>() DSL on top. kotlinx-coroutines-test matches
|
|
256
|
+
// the existing kotlinx-coroutines-android dependency's release line
|
|
257
|
+
// for testing suspend funs in NativeBridge/OfflineCacheService.
|
|
258
|
+
testImplementation(libs.mockito.core)
|
|
259
|
+
testImplementation(libs.mockito.kotlin)
|
|
260
|
+
testImplementation(libs.kotlin.reflect)
|
|
261
|
+
testImplementation(libs.kotlinx.coroutines.test)
|
|
241
262
|
androidTestImplementation(libs.androidx.junit)
|
|
242
263
|
androidTestImplementation(libs.androidx.espresso.core)
|
|
243
264
|
implementation(libs.androidx.webkit)
|
|
@@ -252,11 +273,18 @@ dependencies {
|
|
|
252
273
|
implementation("io.ktor:ktor-server-sse:3.0.3")
|
|
253
274
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0")
|
|
254
275
|
|
|
255
|
-
// Native AI
|
|
256
|
-
|
|
257
|
-
|
|
276
|
+
// Native AI (catalyst-ai). The module is only ever on the classpath when
|
|
277
|
+
// ai.enabled=true -- that's the sole sync trigger (settings.gradle.kts
|
|
278
|
+
// guards on node_modules/catalyst-ai/android existing, and the auto-sync
|
|
279
|
+
// in buildAndroid/index.js only runs for ai.enabled). The app has no
|
|
280
|
+
// compile-time dependency on it: NativeBridge.kt discovers CatalystAIBridge
|
|
281
|
+
// at runtime via Class.forName + ServiceLoader. compileOnly is kept so the
|
|
282
|
+
// module still builds/type-checks when present; implementation adds it to
|
|
283
|
+
// the APK for runtime bundling.
|
|
284
|
+
if (project.findProject(":catalyst-ai") != null) {
|
|
285
|
+
compileOnly(project(":catalyst-ai"))
|
|
258
286
|
if (isAIEnabled()) {
|
|
259
|
-
implementation(project(":catalyst-
|
|
287
|
+
implementation(project(":catalyst-ai"))
|
|
260
288
|
}
|
|
261
289
|
}
|
|
262
290
|
|
|
@@ -284,6 +312,113 @@ dependencies {
|
|
|
284
312
|
}
|
|
285
313
|
}
|
|
286
314
|
|
|
315
|
+
// JVM unit test coverage (Tier 1). Mirrors the CatalystCoreLogic
|
|
316
|
+
// llvm-cov setup on iOS (#432): real, measured line coverage for
|
|
317
|
+
// app/src/test, not just a pass/fail count. Scoped to testDebugUnitTest —
|
|
318
|
+
// enableUnitTestCoverage=true on the debug build type (above) already
|
|
319
|
+
// makes Gradle emit the raw .exec coverage data; this task turns that
|
|
320
|
+
// into the human/CI-readable XML + HTML report.
|
|
321
|
+
tasks.register<JacocoReport>("jacocoTestReport") {
|
|
322
|
+
dependsOn("testDebugUnitTest")
|
|
323
|
+
group = "verification"
|
|
324
|
+
description = "Generates JVM unit test coverage report for app/src/test (Tier 1)."
|
|
325
|
+
|
|
326
|
+
reports {
|
|
327
|
+
xml.required.set(true)
|
|
328
|
+
html.required.set(true)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Excludes are split into two groups. The first is generated/non-authored
|
|
332
|
+
// code (R.class, BuildConfig, view/data binding scaffolding) — no
|
|
333
|
+
// coverage story either way, always excluded.
|
|
334
|
+
//
|
|
335
|
+
// The second is the Tier 2 classification below: files that build real
|
|
336
|
+
// Android Views, extend Activity/Fragment, or otherwise structurally
|
|
337
|
+
// require Robolectric or a real device/emulator to exercise (confirmed
|
|
338
|
+
// via a full-repo classification pass, not a guess from filenames —
|
|
339
|
+
// see PR description / issue for the per-file reasoning). These ARE
|
|
340
|
+
// real, authored logic — excluding them isn't "inflating" the number,
|
|
341
|
+
// it's making the gate honest: a Tier 1/Tier 2 coverage gate that
|
|
342
|
+
// includes Tier 2 in its denominator would fail any PR that touches
|
|
343
|
+
// WebView/Activity/View code regardless of how well-tested that PR's
|
|
344
|
+
// actual testable logic is. Mirrors the CoreLogic/UI split already
|
|
345
|
+
// accepted on iOS (#432).
|
|
346
|
+
//
|
|
347
|
+
// Each Tier 2 file's top-level class, Kt-file facade, and compiled
|
|
348
|
+
// lambda/inner classes are all excluded, so nothing structurally
|
|
349
|
+
// untestable is left counting against the gate. The one exception is
|
|
350
|
+
// NativeBridge, which has an already-tested companion object
|
|
351
|
+
// (parseAndValidateMessage, compiled separately as
|
|
352
|
+
// NativeBridge$Companion.class) — Gradle's Ant-style exclude() does
|
|
353
|
+
// not reliably support "!" negation, so instead of pattern-negating
|
|
354
|
+
// the companion out, its lambda exclude uses "$*$*" (two "$"
|
|
355
|
+
// segments), which Kotlin lambda class names always have and the
|
|
356
|
+
// single-"$" companion class name never does. See the comment next
|
|
357
|
+
// to that pattern below for the concrete example.
|
|
358
|
+
val generatedCodeFilter = listOf(
|
|
359
|
+
"**/R.class", "**/R$*.class",
|
|
360
|
+
"**/BuildConfig.*",
|
|
361
|
+
"**/Manifest*.*",
|
|
362
|
+
"**/*Test*.*",
|
|
363
|
+
"android/**/*.*",
|
|
364
|
+
"**/databinding/**",
|
|
365
|
+
"**/android/databinding/**",
|
|
366
|
+
"**/androidx/databinding/**",
|
|
367
|
+
"**/*_ViewBinding*.*",
|
|
368
|
+
"**/*\$ViewInjector*.*",
|
|
369
|
+
"**/*\$ViewBinder*.*",
|
|
370
|
+
"**/*_MembersInjector.class"
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
val tier2FrameworkBoundFilter = listOf(
|
|
374
|
+
// Real WebView/Activity construction, ViewBinding, real lifecycle.
|
|
375
|
+
// Each file's top-level class, Kt-file facade, and compiled lambda
|
|
376
|
+
// classes (Kotlin: OuterClass$methodName$N.class) are all excluded.
|
|
377
|
+
// None of these files has a companion object worth protecting
|
|
378
|
+
// (that only applies to NativeBridge, handled separately below),
|
|
379
|
+
// so a plain "$*" is safe here.
|
|
380
|
+
"**/CustomWebView.class", "**/CustomWebView\$*.class", "**/CustomWebViewKt.class",
|
|
381
|
+
"**/MainActivity.class", "**/MainActivity\$*.class",
|
|
382
|
+
"**/SplashActivity.class", "**/SplashActivity\$*.class",
|
|
383
|
+
"**/NativeCameraManager.class", "**/NativeCameraManager\$*.class",
|
|
384
|
+
"**/camera/CameraSessionManager.class", "**/camera/CameraSessionManager\$*.class",
|
|
385
|
+
"**/TransitionManager.class", "**/TransitionManager\$*.class",
|
|
386
|
+
"**/utils/KeyboardUtil.class", "**/utils/KeyboardUtil\$*.class",
|
|
387
|
+
"**/security/SecurityAlertUI.class", "**/security/SecurityAlertUI\$*.class",
|
|
388
|
+
"**/security/SecurityAlertHandler.class", "**/security/SecurityAlertHandler\$*.class",
|
|
389
|
+
"**/security/SecurityBottomSheet.class", "**/security/SecurityBottomSheet\$*.class",
|
|
390
|
+
// NativeBridge has an already-tested companion (parseAndValidateMessage,
|
|
391
|
+
// compiled as NativeBridge$Companion.class — one "$"). Kotlin lambda
|
|
392
|
+
// classes always carry two "$" segments (e.g.
|
|
393
|
+
// NativeBridge$downloadAndOpenFile$1.class), so "$*$*" excludes every
|
|
394
|
+
// lambda while a single-"$" glob would be needed to also exclude the
|
|
395
|
+
// companion — which this pattern does NOT match, by construction.
|
|
396
|
+
"**/NativeBridge.class", "**/NativeBridgeKt.class", "**/NativeBridge\$*\$*.class"
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
val debugTree = fileTree(layout.buildDirectory.dir("intermediates/javac/debug/compileDebugJavaWithJavac/classes")) {
|
|
400
|
+
exclude(generatedCodeFilter + tier2FrameworkBoundFilter)
|
|
401
|
+
}
|
|
402
|
+
val kotlinDebugTree = fileTree(layout.buildDirectory.dir("tmp/kotlin-classes/debug")) {
|
|
403
|
+
exclude(generatedCodeFilter + tier2FrameworkBoundFilter)
|
|
404
|
+
}
|
|
405
|
+
// Both noFcm/withFcm are listed (not just whichever isNotificationsEnabled()
|
|
406
|
+
// picked for this build) purely so the HTML report can resolve source
|
|
407
|
+
// lines for whichever one actually got compiled — listing the inactive
|
|
408
|
+
// one is harmless, Jacoco just won't find matching class files for it.
|
|
409
|
+
val sourceDirs = listOf(
|
|
410
|
+
"${project.projectDir}/src/main/java",
|
|
411
|
+
"${project.projectDir}/src/noFcm/java",
|
|
412
|
+
"${project.projectDir}/src/withFcm/java"
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
sourceDirectories.setFrom(files(sourceDirs))
|
|
416
|
+
classDirectories.setFrom(files(debugTree, kotlinDebugTree))
|
|
417
|
+
executionData.setFrom(fileTree(layout.buildDirectory) {
|
|
418
|
+
include("outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec")
|
|
419
|
+
})
|
|
420
|
+
}
|
|
421
|
+
|
|
287
422
|
// Task to verify local IP
|
|
288
423
|
tasks.register("printLocalIp") {
|
|
289
424
|
doLast {
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
6
6
|
<uses-permission android:name="android.permission.CAMERA" />
|
|
7
7
|
<uses-permission android:name="android.permission.VIBRATE" />
|
|
8
|
+
<!-- Runtime-guarded in NotificationUtils.showNotification (checkSelfPermission
|
|
9
|
+
|| SDK_INT < TIRAMISU); only requested/used when the notifications
|
|
10
|
+
feature is enabled at build time. -->
|
|
11
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
8
12
|
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
|
9
13
|
<queries>
|
|
10
14
|
<intent>
|
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt
CHANGED
|
@@ -27,7 +27,7 @@ import java.util.Properties
|
|
|
27
27
|
import java.util.ServiceLoader
|
|
28
28
|
import java.util.concurrent.atomic.AtomicBoolean
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
internal data class FilePickerOptions(
|
|
31
31
|
val mimeType: String = "*/*",
|
|
32
32
|
val multiple: Boolean = false,
|
|
33
33
|
val minFiles: Int? = null,
|
|
@@ -96,7 +96,7 @@ private fun JSONObject.optIntNullable(key: String): Int? =
|
|
|
96
96
|
private fun JSONObject.optLongNullable(key: String): Long? =
|
|
97
97
|
if (has(key) && !isNull(key)) getLong(key) else null
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
internal data class GoogleSignInOptions(
|
|
100
100
|
val clientId: String,
|
|
101
101
|
val nonce: String? = null,
|
|
102
102
|
val autoSelect: Boolean = false,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
package io.yourname.androidproject.camera
|
|
2
2
|
|
|
3
3
|
import android.util.Log
|
|
4
|
+
import androidx.annotation.OptIn
|
|
5
|
+
import androidx.camera.core.ExperimentalGetImage
|
|
4
6
|
import androidx.camera.core.ImageAnalysis
|
|
5
7
|
import androidx.camera.core.ImageProxy
|
|
6
8
|
import com.google.mlkit.vision.barcode.BarcodeScanner
|
|
@@ -72,6 +74,7 @@ class BarcodeDetector(
|
|
|
72
74
|
scanner = null
|
|
73
75
|
}
|
|
74
76
|
|
|
77
|
+
@OptIn(ExperimentalGetImage::class)
|
|
75
78
|
private fun processFrame(imageProxy: ImageProxy, activeScanner: BarcodeScanner) {
|
|
76
79
|
val mediaImage = imageProxy.image
|
|
77
80
|
if (mediaImage == null) {
|
|
@@ -7,7 +7,9 @@ import android.util.Size
|
|
|
7
7
|
import android.view.View
|
|
8
8
|
import android.hardware.camera2.CaptureRequest
|
|
9
9
|
import android.util.Range
|
|
10
|
+
import androidx.annotation.OptIn
|
|
10
11
|
import androidx.camera.camera2.interop.Camera2Interop
|
|
12
|
+
import androidx.camera.camera2.interop.ExperimentalCamera2Interop
|
|
11
13
|
import androidx.camera.core.Camera
|
|
12
14
|
import androidx.camera.core.CameraSelector
|
|
13
15
|
import androidx.camera.core.ImageAnalysis
|
|
@@ -152,6 +154,7 @@ class CameraSessionManager(
|
|
|
152
154
|
|
|
153
155
|
// ---- Internal ----
|
|
154
156
|
|
|
157
|
+
@OptIn(ExperimentalCamera2Interop::class)
|
|
155
158
|
private fun bindCamera() {
|
|
156
159
|
val future = ProcessCameraProvider.getInstance(activity)
|
|
157
160
|
future.addListener({
|
|
@@ -64,8 +64,23 @@ object FrameworkServerUtils {
|
|
|
64
64
|
// System prompt injected into every native AI request (built from attachmentComponents at initAI time)
|
|
65
65
|
private var nativeSystemPrompt: String = ""
|
|
66
66
|
|
|
67
|
-
// Coroutine scope for server operations
|
|
68
|
-
|
|
67
|
+
// Coroutine scope for server operations. A `var`, re-created at the
|
|
68
|
+
// top of startServer() rather than a `val` created once — stopServer()
|
|
69
|
+
// calls serverScope.cancel(), and a CoroutineScope is single-use: once
|
|
70
|
+
// cancelled it never accepts new coroutines again. With this as a
|
|
71
|
+
// `val`, any startServer() after a stop() launched startCleanupTask()
|
|
72
|
+
// into an already-cancelled scope, silently disabling periodic served-
|
|
73
|
+
// file cleanup for the rest of that server's lifetime. Found via a
|
|
74
|
+
// real start/stop/restart sequence in FrameworkServerUtilsTest.
|
|
75
|
+
// @Volatile: startServer()/stopServer() can run on different threads
|
|
76
|
+
// (this object is a singleton reached from NativeBridge's coroutine
|
|
77
|
+
// scope and from test code alike) — without it, a reassignment in
|
|
78
|
+
// startServer() has no guaranteed visibility to a reader on another
|
|
79
|
+
// thread (e.g. a coroutine already launched via serverScope.launch{}
|
|
80
|
+
// from a prior start), same class of bug as a non-volatile double-
|
|
81
|
+
// checked-locking field.
|
|
82
|
+
@Volatile
|
|
83
|
+
private var serverScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
|
69
84
|
|
|
70
85
|
data class ServedFile(
|
|
71
86
|
val file: File,
|
|
@@ -90,6 +105,14 @@ object FrameworkServerUtils {
|
|
|
90
105
|
}
|
|
91
106
|
|
|
92
107
|
return try {
|
|
108
|
+
// A cancelled CoroutineScope never accepts new coroutines —
|
|
109
|
+
// re-create it here so startCleanupTask() below (and any
|
|
110
|
+
// other serverScope.launch call during this server's
|
|
111
|
+
// lifetime) works correctly even after a prior stopServer().
|
|
112
|
+
if (!serverScope.isActive) {
|
|
113
|
+
serverScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
|
114
|
+
}
|
|
115
|
+
|
|
93
116
|
// Initialize cache directory
|
|
94
117
|
initializeCacheDirectory(context)
|
|
95
118
|
|
|
@@ -504,12 +527,24 @@ object FrameworkServerUtils {
|
|
|
504
527
|
call.response.header(HttpHeaders.AccessControlAllowMethods, "GET, OPTIONS")
|
|
505
528
|
call.response.header(HttpHeaders.AccessControlAllowHeaders, "*")
|
|
506
529
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
530
|
+
// respondText with a manually-built JSONObject, not
|
|
531
|
+
// call.respond(status, mapOf(...)) — that call needs a
|
|
532
|
+
// registered ContentNegotiation converter to serialize
|
|
533
|
+
// a raw Map, which this server never installs, so it
|
|
534
|
+
// was returning 406 Not Acceptable to every real HTTP
|
|
535
|
+
// client (confirmed via a real loopback request in
|
|
536
|
+
// FrameworkServerUtilsTest — no test caught this
|
|
537
|
+
// before because no test made a real request against
|
|
538
|
+
// this route). Matches the /ai/generate route's own
|
|
539
|
+
// JSONObject + respondText pattern elsewhere in this
|
|
540
|
+
// file, avoiding a new ContentNegotiation dependency
|
|
541
|
+
// for one endpoint.
|
|
542
|
+
val statusJson = JSONObject()
|
|
543
|
+
.put("status", "running")
|
|
544
|
+
.put("sessionId", sessionId)
|
|
545
|
+
.put("port", serverPort)
|
|
546
|
+
.put("servedFiles", servedFiles.size)
|
|
547
|
+
call.respondText(statusJson.toString(), ContentType.Application.Json, HttpStatusCode.OK)
|
|
513
548
|
}
|
|
514
549
|
|
|
515
550
|
// Handle OPTIONS preflight requests for CORS
|
package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/BridgeUtilsTest.kt
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
package io.yourname.androidproject
|
|
2
2
|
|
|
3
|
+
import android.webkit.WebView
|
|
3
4
|
import io.yourname.androidproject.utils.BridgeUtils
|
|
4
5
|
import org.json.JSONObject
|
|
5
6
|
import org.junit.Assert.*
|
|
6
7
|
import org.junit.Test
|
|
8
|
+
import org.mockito.kotlin.any
|
|
9
|
+
import org.mockito.kotlin.eq
|
|
10
|
+
import org.mockito.kotlin.isNull
|
|
11
|
+
import org.mockito.kotlin.mock
|
|
12
|
+
import org.mockito.kotlin.verify
|
|
7
13
|
|
|
8
14
|
/**
|
|
9
15
|
* Unit tests for BridgeUtils
|
|
@@ -12,8 +18,15 @@ import org.junit.Test
|
|
|
12
18
|
* - Bridge message formatting (3 tests)
|
|
13
19
|
* - Response serialization (2 tests)
|
|
14
20
|
* - Error message formatting (3 tests)
|
|
21
|
+
* - notifyWeb-family, emitPerfEvent, and log-family calls against a
|
|
22
|
+
* mocked WebView (extension — these were previously untested; only the
|
|
23
|
+
* pure string/JSON helpers above had coverage). Looper.myLooper() and
|
|
24
|
+
* Looper.getMainLooper() both return the SDK stub's default null in a
|
|
25
|
+
* JVM unit test, so `null != null` is false and every notifyWeb/
|
|
26
|
+
* emitPerfEvent call below takes the synchronous "already on main
|
|
27
|
+
* thread" branch directly — no WebView.post dispatch to account for.
|
|
15
28
|
*
|
|
16
|
-
* Total: 8 tests
|
|
29
|
+
* Total: 8 original + 12 extension tests
|
|
17
30
|
*
|
|
18
31
|
* Note: Tests focus on testable logic and string formatting without WebView
|
|
19
32
|
* following the same pattern as FileUtilsTest.kt
|
|
@@ -283,4 +296,171 @@ class BridgeUtilsTest {
|
|
|
283
296
|
assertTrue(kbFormatted.contains("."))
|
|
284
297
|
assertTrue(kbFormatted.endsWith("KB"))
|
|
285
298
|
}
|
|
299
|
+
|
|
300
|
+
// ============================================================
|
|
301
|
+
// CATEGORY 4: notifyWeb* against a mocked WebView (extension)
|
|
302
|
+
// ============================================================
|
|
303
|
+
|
|
304
|
+
@Test
|
|
305
|
+
fun `notifyWeb evaluates a null-data callback when data is null`() {
|
|
306
|
+
val webView: WebView = mock()
|
|
307
|
+
|
|
308
|
+
BridgeUtils.notifyWeb(webView, BridgeUtils.WebEvents.ON_CAMERA_CAPTURE, null)
|
|
309
|
+
|
|
310
|
+
verify(webView).evaluateJavascript(
|
|
311
|
+
eq("window.WebBridge.callback('ON_CAMERA_CAPTURE', null)"),
|
|
312
|
+
isNull()
|
|
313
|
+
)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
@Test
|
|
317
|
+
fun `notifyWeb quotes plain-string data as a JS string literal`() {
|
|
318
|
+
val webView: WebView = mock()
|
|
319
|
+
|
|
320
|
+
BridgeUtils.notifyWeb(webView, BridgeUtils.WebEvents.ON_INTENT_SUCCESS, "GRANTED")
|
|
321
|
+
|
|
322
|
+
verify(webView).evaluateJavascript(
|
|
323
|
+
eq("window.WebBridge.callback('ON_INTENT_SUCCESS', ${JSONObject.quote("GRANTED")})"),
|
|
324
|
+
isNull()
|
|
325
|
+
)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
@Test
|
|
329
|
+
fun `notifyWeb injects JSON-object-shaped data raw, not double-quoted`() {
|
|
330
|
+
val webView: WebView = mock()
|
|
331
|
+
|
|
332
|
+
BridgeUtils.notifyWeb(webView, BridgeUtils.WebEvents.ON_DEVICE_INFO_SUCCESS, """{"a":1}""")
|
|
333
|
+
|
|
334
|
+
verify(webView).evaluateJavascript(
|
|
335
|
+
eq("""window.WebBridge.callback('ON_DEVICE_INFO_SUCCESS', {"a":1})"""),
|
|
336
|
+
isNull()
|
|
337
|
+
)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
@Test
|
|
341
|
+
fun `notifyWeb swallows an exception from evaluateJavascript`() {
|
|
342
|
+
val webView: WebView = mock()
|
|
343
|
+
org.mockito.kotlin.whenever(webView.evaluateJavascript(any(), isNull()))
|
|
344
|
+
.thenThrow(RuntimeException("boom"))
|
|
345
|
+
|
|
346
|
+
// Does not propagate — notifyWeb catches and logs internally.
|
|
347
|
+
BridgeUtils.notifyWeb(webView, BridgeUtils.WebEvents.ON_CAMERA_ERROR, "x")
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
@Test
|
|
351
|
+
fun `notifyWebError defaults to a fallback message when errorMessage is null`() {
|
|
352
|
+
val webView: WebView = mock()
|
|
353
|
+
|
|
354
|
+
BridgeUtils.notifyWebError(webView, BridgeUtils.WebEvents.ON_CAMERA_ERROR, null)
|
|
355
|
+
|
|
356
|
+
verify(webView).evaluateJavascript(
|
|
357
|
+
eq("window.WebBridge.callback('ON_CAMERA_ERROR', ${JSONObject.quote("Unknown error occurred")})"),
|
|
358
|
+
isNull()
|
|
359
|
+
)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
@Test
|
|
363
|
+
fun `notifyWebSuccess defaults to SUCCESS when successMessage is not provided`() {
|
|
364
|
+
val webView: WebView = mock()
|
|
365
|
+
|
|
366
|
+
BridgeUtils.notifyWebSuccess(webView, BridgeUtils.WebEvents.ON_INTENT_SUCCESS)
|
|
367
|
+
|
|
368
|
+
verify(webView).evaluateJavascript(
|
|
369
|
+
eq("window.WebBridge.callback('ON_INTENT_SUCCESS', ${JSONObject.quote("SUCCESS")})"),
|
|
370
|
+
isNull()
|
|
371
|
+
)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
@Test
|
|
375
|
+
fun `notifyWebJson injects the JSON payload raw`() {
|
|
376
|
+
val webView: WebView = mock()
|
|
377
|
+
val payload = JSONObject().apply { put("ok", true) }
|
|
378
|
+
|
|
379
|
+
BridgeUtils.notifyWebJson(webView, BridgeUtils.WebEvents.ON_DEVICE_INFO_SUCCESS, payload)
|
|
380
|
+
|
|
381
|
+
verify(webView).evaluateJavascript(
|
|
382
|
+
eq("window.WebBridge.callback('ON_DEVICE_INFO_SUCCESS', ${payload})"),
|
|
383
|
+
isNull()
|
|
384
|
+
)
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ============================================================
|
|
388
|
+
// CATEGORY 5: emitPerfEvent / bridgeCallReceived / bridgeCallDispatched
|
|
389
|
+
// ============================================================
|
|
390
|
+
|
|
391
|
+
@Test
|
|
392
|
+
fun `emitPerfEvent is a no-op when PerfEventBuffer is disabled`() {
|
|
393
|
+
val webView: WebView = mock()
|
|
394
|
+
io.yourname.androidproject.utils.PerfEventBuffer.configure(enabled = false)
|
|
395
|
+
|
|
396
|
+
BridgeUtils.emitPerfEvent(webView, JSONObject().apply { put("type", "test") })
|
|
397
|
+
|
|
398
|
+
verify(webView, org.mockito.kotlin.never()).evaluateJavascript(any(), any())
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
@Test
|
|
402
|
+
fun `emitPerfEvent calls evaluateJavascript when enabled`() {
|
|
403
|
+
val webView: WebView = mock()
|
|
404
|
+
io.yourname.androidproject.utils.PerfEventBuffer.configure(enabled = true)
|
|
405
|
+
|
|
406
|
+
try {
|
|
407
|
+
BridgeUtils.emitPerfEvent(webView, JSONObject().apply { put("type", "test") })
|
|
408
|
+
verify(webView).evaluateJavascript(any(), isNull())
|
|
409
|
+
} finally {
|
|
410
|
+
io.yourname.androidproject.utils.PerfEventBuffer.configure(enabled = false)
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
@Test
|
|
415
|
+
fun `bridgeCallReceived and bridgeCallDispatched do not throw`() {
|
|
416
|
+
// BuildConfig.DEBUG's value in a JVM unit test depends on the build
|
|
417
|
+
// variant under test; either branch (no-op or delegate to
|
|
418
|
+
// PerfEventBuffer) should complete without throwing.
|
|
419
|
+
BridgeUtils.bridgeCallReceived("call-1", "getDeviceInfo")
|
|
420
|
+
BridgeUtils.bridgeCallDispatched("call-1")
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// ============================================================
|
|
424
|
+
// CATEGORY 6: log helpers
|
|
425
|
+
// ============================================================
|
|
426
|
+
|
|
427
|
+
@Test
|
|
428
|
+
fun `logDebug, logError, logInfo, and logWarning do not throw`() {
|
|
429
|
+
BridgeUtils.logDebug("TAG", "debug message")
|
|
430
|
+
BridgeUtils.logError("TAG", "error message")
|
|
431
|
+
BridgeUtils.logError("TAG", "error with throwable", RuntimeException("boom"))
|
|
432
|
+
BridgeUtils.logInfo("TAG", "info message")
|
|
433
|
+
BridgeUtils.logWarning("TAG", "warning message")
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// ============================================================
|
|
437
|
+
// CATEGORY 7: safeExecute
|
|
438
|
+
// ============================================================
|
|
439
|
+
|
|
440
|
+
@Test
|
|
441
|
+
fun `safeExecute runs the block and does not notify web on success`() {
|
|
442
|
+
val webView: WebView = mock()
|
|
443
|
+
var ran = false
|
|
444
|
+
|
|
445
|
+
BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_CAMERA_ERROR, "test op") {
|
|
446
|
+
ran = true
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
assertTrue(ran)
|
|
450
|
+
verify(webView, org.mockito.kotlin.never()).evaluateJavascript(any(), any())
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
@Test
|
|
454
|
+
fun `safeExecute catches an exception and notifies web with a formatted error`() {
|
|
455
|
+
val webView: WebView = mock()
|
|
456
|
+
|
|
457
|
+
BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_CAMERA_ERROR, "test op") {
|
|
458
|
+
throw RuntimeException("boom")
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
verify(webView).evaluateJavascript(
|
|
462
|
+
eq("window.WebBridge.callback('ON_CAMERA_ERROR', ${JSONObject.quote("Failed to test op: boom")})"),
|
|
463
|
+
isNull()
|
|
464
|
+
)
|
|
465
|
+
}
|
|
286
466
|
}
|