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,300 @@
|
|
|
1
|
+
package io.yourname.androidproject
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.webkit.WebView
|
|
5
|
+
import io.yourname.androidproject.utils.FrameworkServerUtils
|
|
6
|
+
import org.junit.After
|
|
7
|
+
import org.junit.Assert.*
|
|
8
|
+
import org.junit.Before
|
|
9
|
+
import org.junit.Test
|
|
10
|
+
import org.mockito.kotlin.doReturn
|
|
11
|
+
import org.mockito.kotlin.mock
|
|
12
|
+
import java.io.IOException
|
|
13
|
+
import java.net.HttpURLConnection
|
|
14
|
+
import java.net.URL
|
|
15
|
+
import kotlin.io.path.createTempDirectory
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Loopback tests for FrameworkServerUtils, the Ktor-based localhost file
|
|
19
|
+
* server — previously entirely untested (0%). Ports iOS's
|
|
20
|
+
* FrameworkServerUtilsLoopbackTests.swift pattern: start the real
|
|
21
|
+
* embedded server (Ktor/Netty, real socket, real port from the
|
|
22
|
+
* 3000-3099 range) and make real HTTP requests against it via
|
|
23
|
+
* java.net.HttpURLConnection (no new test dependency needed — this repo
|
|
24
|
+
* has no OkHttp test dep, and the JDK's built-in client is enough for a
|
|
25
|
+
* loopback GET/POST).
|
|
26
|
+
*
|
|
27
|
+
* FrameworkServerUtils is a singleton `object` — server state
|
|
28
|
+
* (isServerRunning/serverPort/sessionId/servedFiles) is shared static
|
|
29
|
+
* state across the whole test JVM. Every test starts fresh via
|
|
30
|
+
* stopServer() in tearDown so state doesn't leak between tests or other
|
|
31
|
+
* test classes running in the same JVM.
|
|
32
|
+
*
|
|
33
|
+
* Not chased here: the /ai/stream and /ai/generate SSE routes (need a
|
|
34
|
+
* real nativeAiSupplier + Flow collection — a second, separate scope) and
|
|
35
|
+
* the 5-minute cleanup task's periodic timer path. This file covers
|
|
36
|
+
* lifecycle (start/stop/isRunning/getServerPort/getSessionId), file
|
|
37
|
+
* serving (add/copy/remove), and the /status, file-serving, and
|
|
38
|
+
* invalid-route HTTP endpoints — mirrors what iOS's loopback tests
|
|
39
|
+
* covered for the equivalent Swift class.
|
|
40
|
+
*/
|
|
41
|
+
class FrameworkServerUtilsTest {
|
|
42
|
+
|
|
43
|
+
private lateinit var cacheDir: java.io.File
|
|
44
|
+
private lateinit var context: Context
|
|
45
|
+
private lateinit var webView: WebView
|
|
46
|
+
|
|
47
|
+
@Before
|
|
48
|
+
fun setUp() {
|
|
49
|
+
// FrameworkServerUtils is a JVM-wide singleton object; stop it
|
|
50
|
+
// defensively before each test too, not just in tearDown — a
|
|
51
|
+
// prior test class (or an earlier test in this one, if tearDown
|
|
52
|
+
// somehow didn't run) leaving the server running would otherwise
|
|
53
|
+
// poison this class's isRunning()==false assumptions. Matches the
|
|
54
|
+
// defensive-stop pattern already used in FileSizeRouterUtilsTest.
|
|
55
|
+
if (FrameworkServerUtils.isRunning()) {
|
|
56
|
+
FrameworkServerUtils.stopServer()
|
|
57
|
+
}
|
|
58
|
+
cacheDir = createTempDirectory(prefix = "catalyst-framework-server-test").toFile()
|
|
59
|
+
context = mock { on { getCacheDir() } doReturn cacheDir }
|
|
60
|
+
webView = mock { on { getUrl() } doReturn "http://localhost:8080/index.html" }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@After
|
|
64
|
+
fun tearDown() {
|
|
65
|
+
if (FrameworkServerUtils.isRunning()) {
|
|
66
|
+
FrameworkServerUtils.stopServer()
|
|
67
|
+
}
|
|
68
|
+
cacheDir.deleteRecursively()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Waits briefly for Ktor/Netty's async bind to complete, mirroring the
|
|
72
|
+
* "server may not start in this sandbox" tolerance pattern used on
|
|
73
|
+
* iOS — returns false (caller should skip) if it never came up. */
|
|
74
|
+
private fun startServerAndWaitReady(): Boolean {
|
|
75
|
+
val started = FrameworkServerUtils.startServer(context, webView)
|
|
76
|
+
if (!started) return false
|
|
77
|
+
repeat(20) {
|
|
78
|
+
if (FrameworkServerUtils.isRunning() && FrameworkServerUtils.getServerPort() != 0) return true
|
|
79
|
+
Thread.sleep(100)
|
|
80
|
+
}
|
|
81
|
+
return FrameworkServerUtils.isRunning()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private fun get(path: String): Pair<Int, String> {
|
|
85
|
+
val port = FrameworkServerUtils.getServerPort()
|
|
86
|
+
val connection = URL("http://localhost:$port$path").openConnection() as HttpURLConnection
|
|
87
|
+
connection.connectTimeout = 5000
|
|
88
|
+
connection.readTimeout = 5000
|
|
89
|
+
connection.setRequestProperty("Accept", "*/*")
|
|
90
|
+
return try {
|
|
91
|
+
val code = connection.responseCode
|
|
92
|
+
val body = try {
|
|
93
|
+
connection.inputStream.bufferedReader().readText()
|
|
94
|
+
} catch (e: IOException) {
|
|
95
|
+
connection.errorStream?.bufferedReader()?.readText() ?: ""
|
|
96
|
+
}
|
|
97
|
+
code to body
|
|
98
|
+
} finally {
|
|
99
|
+
connection.disconnect()
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ============================================================
|
|
104
|
+
// Lifecycle
|
|
105
|
+
// ============================================================
|
|
106
|
+
|
|
107
|
+
@Test
|
|
108
|
+
fun `startServer starts the server and reports a port in the configured range`() {
|
|
109
|
+
assumeServerStarts {
|
|
110
|
+
assertTrue(FrameworkServerUtils.isRunning())
|
|
111
|
+
assertTrue(FrameworkServerUtils.getServerPort() in 3000..3099)
|
|
112
|
+
assertTrue(FrameworkServerUtils.getSessionId().isNotEmpty())
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@Test
|
|
117
|
+
fun `startServer is idempotent when already running`() {
|
|
118
|
+
assumeServerStarts {
|
|
119
|
+
val firstPort = FrameworkServerUtils.getServerPort()
|
|
120
|
+
val result = FrameworkServerUtils.startServer(context, webView)
|
|
121
|
+
assertTrue(result)
|
|
122
|
+
assertEquals(firstPort, FrameworkServerUtils.getServerPort())
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@Test
|
|
127
|
+
fun `stopServer stops the server and isRunning reports false`() {
|
|
128
|
+
assumeServerStarts {
|
|
129
|
+
FrameworkServerUtils.stopServer()
|
|
130
|
+
assertFalse(FrameworkServerUtils.isRunning())
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@Test
|
|
135
|
+
fun `isRunning is false before any server has started`() {
|
|
136
|
+
assertFalse(FrameworkServerUtils.isRunning())
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@Test
|
|
140
|
+
fun `a server restarted after stop can start again and serve a file`() {
|
|
141
|
+
// Regression test for the serverScope-never-recreated bug:
|
|
142
|
+
// serverScope was a `val`, so stopServer()'s serverScope.cancel()
|
|
143
|
+
// permanently killed it — a restarted server's startCleanupTask()
|
|
144
|
+
// then launched into an already-cancelled scope. This doesn't
|
|
145
|
+
// directly assert the cleanup task itself (its failure was
|
|
146
|
+
// silent — a cancelled scope just drops launched coroutines
|
|
147
|
+
// rather than throwing at the call site), but it does exercise
|
|
148
|
+
// the exact start -> stop -> start sequence that triggered it end
|
|
149
|
+
// to end, including a real file being served on the restarted
|
|
150
|
+
// instance, as the most direct available signal something didn't
|
|
151
|
+
// regress.
|
|
152
|
+
assumeServerStarts {
|
|
153
|
+
FrameworkServerUtils.stopServer()
|
|
154
|
+
assertFalse(FrameworkServerUtils.isRunning())
|
|
155
|
+
|
|
156
|
+
val restarted = FrameworkServerUtils.startServer(context, webView)
|
|
157
|
+
assertTrue(restarted)
|
|
158
|
+
assertTrue(FrameworkServerUtils.isRunning())
|
|
159
|
+
|
|
160
|
+
val tempFile = java.io.File(cacheDir, "after-restart.txt").apply { writeText("still works") }
|
|
161
|
+
val url = FrameworkServerUtils.addFileToServe(tempFile, "after-restart.txt", "text/plain")
|
|
162
|
+
assertNotNull(url)
|
|
163
|
+
|
|
164
|
+
val path = url!!.substringAfter("localhost:${FrameworkServerUtils.getServerPort()}")
|
|
165
|
+
val (code, body) = get(path)
|
|
166
|
+
assertEquals(200, code)
|
|
167
|
+
assertEquals("still works", body)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ============================================================
|
|
172
|
+
// addFileToServe / copyAndServeFile / removeServedFile (not-running guards)
|
|
173
|
+
// ============================================================
|
|
174
|
+
|
|
175
|
+
@Test
|
|
176
|
+
fun `addFileToServe returns null when the server is not running`() {
|
|
177
|
+
assertFalse(FrameworkServerUtils.isRunning())
|
|
178
|
+
val tempFile = java.io.File(cacheDir, "not-served.txt").apply { writeText("x") }
|
|
179
|
+
|
|
180
|
+
val result = FrameworkServerUtils.addFileToServe(tempFile, "not-served.txt", "text/plain")
|
|
181
|
+
|
|
182
|
+
assertNull(result)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
@Test
|
|
186
|
+
fun `copyAndServeFile returns null when the server is not running`() {
|
|
187
|
+
assertFalse(FrameworkServerUtils.isRunning())
|
|
188
|
+
val tempFile = java.io.File(cacheDir, "not-copied.txt").apply { writeText("x") }
|
|
189
|
+
|
|
190
|
+
val result = FrameworkServerUtils.copyAndServeFile(tempFile, "not-copied.txt", "text/plain")
|
|
191
|
+
|
|
192
|
+
assertNull(result)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@Test
|
|
196
|
+
fun `addFileToServe returns a well-formed URL once the server is running`() {
|
|
197
|
+
assumeServerStarts {
|
|
198
|
+
val tempFile = java.io.File(cacheDir, "served.txt").apply { writeText("hello") }
|
|
199
|
+
|
|
200
|
+
val url = FrameworkServerUtils.addFileToServe(tempFile, "served.txt", "text/plain")
|
|
201
|
+
|
|
202
|
+
assertNotNull(url)
|
|
203
|
+
assertTrue(url!!.startsWith("http://localhost:${FrameworkServerUtils.getServerPort()}/framework-${FrameworkServerUtils.getSessionId()}/file-"))
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ============================================================
|
|
208
|
+
// HTTP endpoints (real requests against the real embedded server)
|
|
209
|
+
// ============================================================
|
|
210
|
+
|
|
211
|
+
@Test
|
|
212
|
+
fun `GET status returns 200 with running JSON`() {
|
|
213
|
+
assumeServerStarts {
|
|
214
|
+
val (code, body) = get("/framework-${FrameworkServerUtils.getSessionId()}/status")
|
|
215
|
+
|
|
216
|
+
assertEquals(200, code)
|
|
217
|
+
assertTrue(body.contains("\"status\":\"running\""))
|
|
218
|
+
assertTrue(body.contains(FrameworkServerUtils.getSessionId()))
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
@Test
|
|
223
|
+
fun `GET a served file returns 200 with the real file content`() {
|
|
224
|
+
assumeServerStarts {
|
|
225
|
+
val content = "loopback content ${System.nanoTime()}"
|
|
226
|
+
val tempFile = java.io.File(cacheDir, "loopback.txt").apply { writeText(content) }
|
|
227
|
+
val url = FrameworkServerUtils.addFileToServe(tempFile, "loopback.txt", "text/plain")
|
|
228
|
+
assertNotNull(url)
|
|
229
|
+
|
|
230
|
+
val path = url!!.substringAfter("localhost:${FrameworkServerUtils.getServerPort()}")
|
|
231
|
+
val (code, body) = get(path)
|
|
232
|
+
|
|
233
|
+
assertEquals(200, code)
|
|
234
|
+
assertEquals(content, body)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
@Test
|
|
239
|
+
fun `GET an unknown fileId returns 404`() {
|
|
240
|
+
assumeServerStarts {
|
|
241
|
+
val (code, _) = get("/framework-${FrameworkServerUtils.getSessionId()}/file-does-not-exist")
|
|
242
|
+
assertEquals(404, code)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
@Test
|
|
247
|
+
fun `GET a served file whose physical file was deleted returns 404`() {
|
|
248
|
+
assumeServerStarts {
|
|
249
|
+
val tempFile = java.io.File(cacheDir, "vanish.txt").apply { writeText("will vanish") }
|
|
250
|
+
val url = FrameworkServerUtils.addFileToServe(tempFile, "vanish.txt", "text/plain")
|
|
251
|
+
assertNotNull(url)
|
|
252
|
+
tempFile.delete()
|
|
253
|
+
|
|
254
|
+
val path = url!!.substringAfter("localhost:${FrameworkServerUtils.getServerPort()}")
|
|
255
|
+
val (code, _) = get(path)
|
|
256
|
+
|
|
257
|
+
assertEquals(404, code)
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
@Test
|
|
262
|
+
fun `GET an invalid route returns 404`() {
|
|
263
|
+
assumeServerStarts {
|
|
264
|
+
val (code, _) = get("/framework-${FrameworkServerUtils.getSessionId()}/not-a-real-route")
|
|
265
|
+
assertEquals(404, code)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
@Test
|
|
270
|
+
fun `removeServedFile makes a subsequent request return 404`() {
|
|
271
|
+
assumeServerStarts {
|
|
272
|
+
val tempFile = java.io.File(cacheDir, "to-remove.txt").apply { writeText("bye") }
|
|
273
|
+
val url = FrameworkServerUtils.addFileToServe(tempFile, "to-remove.txt", "text/plain")
|
|
274
|
+
assertNotNull(url)
|
|
275
|
+
val path = url!!.substringAfter("localhost:${FrameworkServerUtils.getServerPort()}")
|
|
276
|
+
|
|
277
|
+
val (beforeCode, _) = get(path)
|
|
278
|
+
assertEquals(200, beforeCode)
|
|
279
|
+
|
|
280
|
+
val fileId = path.substringAfter("file-")
|
|
281
|
+
FrameworkServerUtils.removeServedFile(fileId)
|
|
282
|
+
|
|
283
|
+
val (afterCode, _) = get(path)
|
|
284
|
+
assertEquals(404, afterCode)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Runs `block` only if the real Ktor/Netty server actually came up in
|
|
289
|
+
// this sandbox; otherwise the test is silently skipped rather than
|
|
290
|
+
// failed — matches the "server may not start in this environment"
|
|
291
|
+
// tolerance pattern from the iOS loopback tests (network restrictions
|
|
292
|
+
// in some CI/sandbox environments can prevent binding a real socket).
|
|
293
|
+
private fun assumeServerStarts(block: () -> Unit) {
|
|
294
|
+
if (!startServerAndWaitReady()) {
|
|
295
|
+
org.junit.Assume.assumeTrue("Framework server failed to start in this environment — skipping", false)
|
|
296
|
+
return
|
|
297
|
+
}
|
|
298
|
+
block()
|
|
299
|
+
}
|
|
300
|
+
}
|
package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/IntentUtilsTest.kt
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
package io.yourname.androidproject
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.content.Intent
|
|
5
|
+
import android.content.pm.PackageManager
|
|
6
|
+
import android.content.pm.ResolveInfo
|
|
7
|
+
import io.yourname.androidproject.utils.IntentUtils
|
|
8
|
+
import org.junit.Assert.*
|
|
9
|
+
import org.junit.Test
|
|
10
|
+
import org.mockito.kotlin.doReturn
|
|
11
|
+
import org.mockito.kotlin.doThrow
|
|
12
|
+
import org.mockito.kotlin.mock
|
|
13
|
+
import org.mockito.kotlin.whenever
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Unit tests for IntentUtils, previously entirely untested (0%).
|
|
17
|
+
*
|
|
18
|
+
* parseIntentParams/validateFileUrl/createFilePickerIntent are pure (no
|
|
19
|
+
* Context/Activity) and covered directly. canHandleIntent/
|
|
20
|
+
* getAppsForMimeType/getDefaultAppForMimeType take an Activity but only
|
|
21
|
+
* touch its packageManager, so a mocked Activity + mocked PackageManager
|
|
22
|
+
* covers them without FileProvider. openFileWithSystemIntent/
|
|
23
|
+
* createShareIntent/openFileWithSpecificApp all call
|
|
24
|
+
* FileProvider.getUriForFile — a static method backed by a real
|
|
25
|
+
* ContentProvider registration that doesn't exist in a JVM unit test —
|
|
26
|
+
* left uncovered here rather than chased with a heavier mocking setup;
|
|
27
|
+
* matches the plan's "reachable without excessive mocking" scope.
|
|
28
|
+
*/
|
|
29
|
+
class IntentUtilsTest {
|
|
30
|
+
|
|
31
|
+
// ============================================================
|
|
32
|
+
// parseIntentParams
|
|
33
|
+
// ============================================================
|
|
34
|
+
|
|
35
|
+
@Test
|
|
36
|
+
fun `parseIntentParams splits fileUrl and mimeType on the pipe delimiter`() {
|
|
37
|
+
val (fileUrl, mimeType) = IntentUtils.parseIntentParams("https://example.com/file.pdf|application/pdf")
|
|
38
|
+
assertEquals("https://example.com/file.pdf", fileUrl)
|
|
39
|
+
assertEquals("application/pdf", mimeType)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Test
|
|
43
|
+
fun `parseIntentParams returns null mimeType when no pipe is present`() {
|
|
44
|
+
val (fileUrl, mimeType) = IntentUtils.parseIntentParams("https://example.com/file.pdf")
|
|
45
|
+
assertEquals("https://example.com/file.pdf", fileUrl)
|
|
46
|
+
assertNull(mimeType)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@Test
|
|
50
|
+
fun `parseIntentParams throws for null params`() {
|
|
51
|
+
assertThrows(IllegalArgumentException::class.java) {
|
|
52
|
+
IntentUtils.parseIntentParams(null)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@Test
|
|
57
|
+
fun `parseIntentParams throws for blank params`() {
|
|
58
|
+
assertThrows(IllegalArgumentException::class.java) {
|
|
59
|
+
IntentUtils.parseIntentParams(" ")
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@Test
|
|
64
|
+
fun `parseIntentParams throws when the fileUrl portion is empty`() {
|
|
65
|
+
assertThrows(IllegalArgumentException::class.java) {
|
|
66
|
+
IntentUtils.parseIntentParams("|application/pdf")
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@Test
|
|
71
|
+
fun `parseIntentParams treats a blank mimeType segment as null`() {
|
|
72
|
+
val (_, mimeType) = IntentUtils.parseIntentParams("https://example.com/file.pdf| ")
|
|
73
|
+
assertNull(mimeType)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ============================================================
|
|
77
|
+
// validateFileUrl
|
|
78
|
+
// ============================================================
|
|
79
|
+
|
|
80
|
+
@Test
|
|
81
|
+
fun `validateFileUrl accepts an https URL`() {
|
|
82
|
+
IntentUtils.validateFileUrl("https://example.com/file.pdf")
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@Test
|
|
86
|
+
fun `validateFileUrl accepts an http URL`() {
|
|
87
|
+
IntentUtils.validateFileUrl("http://example.com/file.pdf")
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@Test
|
|
91
|
+
fun `validateFileUrl throws for a blank URL`() {
|
|
92
|
+
val error = assertThrows(IllegalArgumentException::class.java) {
|
|
93
|
+
IntentUtils.validateFileUrl("")
|
|
94
|
+
}
|
|
95
|
+
assertEquals("File URL cannot be empty", error.message)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@Test
|
|
99
|
+
fun `validateFileUrl throws for a non-http(s) scheme`() {
|
|
100
|
+
val error = assertThrows(IllegalArgumentException::class.java) {
|
|
101
|
+
IntentUtils.validateFileUrl("file:///local/path.pdf")
|
|
102
|
+
}
|
|
103
|
+
assertEquals("Only remote URLs (http/https) are supported", error.message)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ============================================================
|
|
107
|
+
// createFilePickerIntent
|
|
108
|
+
//
|
|
109
|
+
// Intent is a real android.content.Intent (not mocked) constructed
|
|
110
|
+
// directly by the method under test. Its getters (action/type/
|
|
111
|
+
// categories/getBooleanExtra/getStringArrayExtra) are backed by the
|
|
112
|
+
// Android SDK's stub jar, which — even with
|
|
113
|
+
// testOptions.unitTests.isReturnDefaultValues=true — returns default
|
|
114
|
+
// values (null/false) rather than reflecting state actually set via
|
|
115
|
+
// setAction()/type=/putExtra() on the same stub instance. That's a
|
|
116
|
+
// structural limitation of testing raw Intent state in a plain JVM
|
|
117
|
+
// unit test (Robolectric provides a real Intent implementation for
|
|
118
|
+
// this; out of scope per this plan's Mockito-only ceiling). These
|
|
119
|
+
// tests exercise the method's branching logic (single vs.
|
|
120
|
+
// comma-separated MIME type path) and assert only that it completes
|
|
121
|
+
// and returns a non-null Intent, not the Intent's resulting state.
|
|
122
|
+
// ============================================================
|
|
123
|
+
|
|
124
|
+
@Test
|
|
125
|
+
fun `createFilePickerIntent returns a non-null Intent for a single MIME type`() {
|
|
126
|
+
val intent = IntentUtils.createFilePickerIntent("application/pdf")
|
|
127
|
+
assertNotNull(intent)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@Test
|
|
131
|
+
fun `createFilePickerIntent returns a non-null Intent when allowMultiple is set`() {
|
|
132
|
+
val intent = IntentUtils.createFilePickerIntent("image/*", allowMultiple = true)
|
|
133
|
+
assertNotNull(intent)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@Test
|
|
137
|
+
fun `createFilePickerIntent returns a non-null Intent for comma-separated MIME types`() {
|
|
138
|
+
val intent = IntentUtils.createFilePickerIntent("application/pdf,image/png")
|
|
139
|
+
assertNotNull(intent)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@Test
|
|
143
|
+
fun `createFilePickerIntent returns a non-null Intent for a trailing-comma single type`() {
|
|
144
|
+
val intent = IntentUtils.createFilePickerIntent("application/pdf,")
|
|
145
|
+
assertNotNull(intent)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ============================================================
|
|
149
|
+
// canHandleIntent / getAppsForMimeType / getDefaultAppForMimeType
|
|
150
|
+
// ============================================================
|
|
151
|
+
|
|
152
|
+
private fun activityWithPackageManager(packageManager: PackageManager): Activity {
|
|
153
|
+
return mock { on { getPackageManager() } doReturn packageManager }
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
@Test
|
|
157
|
+
fun `canHandleIntent returns true when queryIntentActivities finds a match`() {
|
|
158
|
+
val resolveInfo: ResolveInfo = mock()
|
|
159
|
+
val packageManager: PackageManager = mock {
|
|
160
|
+
on { queryIntentActivities(org.mockito.kotlin.any(), org.mockito.kotlin.eq(0)) } doReturn listOf(resolveInfo)
|
|
161
|
+
}
|
|
162
|
+
val activity = activityWithPackageManager(packageManager)
|
|
163
|
+
|
|
164
|
+
assertTrue(IntentUtils.canHandleIntent(activity, Intent(Intent.ACTION_VIEW)))
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@Test
|
|
168
|
+
fun `canHandleIntent returns false when no app can handle it`() {
|
|
169
|
+
val packageManager: PackageManager = mock {
|
|
170
|
+
on { queryIntentActivities(org.mockito.kotlin.any(), org.mockito.kotlin.eq(0)) } doReturn emptyList()
|
|
171
|
+
}
|
|
172
|
+
val activity = activityWithPackageManager(packageManager)
|
|
173
|
+
|
|
174
|
+
assertFalse(IntentUtils.canHandleIntent(activity, Intent(Intent.ACTION_VIEW)))
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
@Test
|
|
178
|
+
fun `canHandleIntent returns false when queryIntentActivities throws`() {
|
|
179
|
+
val packageManager: PackageManager = mock {
|
|
180
|
+
on { queryIntentActivities(org.mockito.kotlin.any(), org.mockito.kotlin.eq(0)) } doThrow RuntimeException("boom")
|
|
181
|
+
}
|
|
182
|
+
val activity = activityWithPackageManager(packageManager)
|
|
183
|
+
|
|
184
|
+
assertFalse(IntentUtils.canHandleIntent(activity, Intent(Intent.ACTION_VIEW)))
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
@Test
|
|
188
|
+
fun `getAppsForMimeType returns loaded labels for each resolved app`() {
|
|
189
|
+
val packageManager: PackageManager = mock()
|
|
190
|
+
val resolveInfo: ResolveInfo = mock {
|
|
191
|
+
on { loadLabel(packageManager) } doReturn "PDF Reader"
|
|
192
|
+
}
|
|
193
|
+
whenever(packageManager.queryIntentActivities(org.mockito.kotlin.any(), org.mockito.kotlin.eq(0)))
|
|
194
|
+
.thenReturn(listOf(resolveInfo))
|
|
195
|
+
val activity = activityWithPackageManager(packageManager)
|
|
196
|
+
|
|
197
|
+
val apps = IntentUtils.getAppsForMimeType(activity, "application/pdf")
|
|
198
|
+
|
|
199
|
+
assertEquals(listOf("PDF Reader"), apps)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
@Test
|
|
203
|
+
fun `getAppsForMimeType returns an empty list when resolution throws`() {
|
|
204
|
+
val packageManager: PackageManager = mock {
|
|
205
|
+
on { queryIntentActivities(org.mockito.kotlin.any(), org.mockito.kotlin.eq(0)) } doThrow RuntimeException("boom")
|
|
206
|
+
}
|
|
207
|
+
val activity = activityWithPackageManager(packageManager)
|
|
208
|
+
|
|
209
|
+
assertTrue(IntentUtils.getAppsForMimeType(activity, "application/pdf").isEmpty())
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
@Test
|
|
213
|
+
fun `getDefaultAppForMimeType returns the resolved package name`() {
|
|
214
|
+
val resolveInfo = android.content.pm.ResolveInfo().apply {
|
|
215
|
+
activityInfo = android.content.pm.ActivityInfo().apply { packageName = "com.example.pdf" }
|
|
216
|
+
}
|
|
217
|
+
val packageManager: PackageManager = mock {
|
|
218
|
+
on { resolveActivity(org.mockito.kotlin.any(), org.mockito.kotlin.eq(0)) } doReturn resolveInfo
|
|
219
|
+
}
|
|
220
|
+
val activity = activityWithPackageManager(packageManager)
|
|
221
|
+
|
|
222
|
+
assertEquals("com.example.pdf", IntentUtils.getDefaultAppForMimeType(activity, "application/pdf"))
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
@Test
|
|
226
|
+
fun `getDefaultAppForMimeType returns null when nothing resolves`() {
|
|
227
|
+
val packageManager: PackageManager = mock {
|
|
228
|
+
on { resolveActivity(org.mockito.kotlin.any(), org.mockito.kotlin.eq(0)) } doReturn null
|
|
229
|
+
}
|
|
230
|
+
val activity = activityWithPackageManager(packageManager)
|
|
231
|
+
|
|
232
|
+
assertNull(IntentUtils.getDefaultAppForMimeType(activity, "application/pdf"))
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
@Test
|
|
236
|
+
fun `getDefaultAppForMimeType returns null when resolution throws`() {
|
|
237
|
+
val packageManager: PackageManager = mock {
|
|
238
|
+
on { resolveActivity(org.mockito.kotlin.any(), org.mockito.kotlin.eq(0)) } doThrow RuntimeException("boom")
|
|
239
|
+
}
|
|
240
|
+
val activity = activityWithPackageManager(packageManager)
|
|
241
|
+
|
|
242
|
+
assertNull(IntentUtils.getDefaultAppForMimeType(activity, "application/pdf"))
|
|
243
|
+
}
|
|
244
|
+
}
|