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
|
@@ -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
|
+
}
|