@hot-updater/react-native 1.0.0-rc.2 → 1.0.0-rc.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (291) hide show
  1. package/android/proguard-rules.pro +17 -2
  2. package/android/src/main/java/com/hotupdater/BuiltInAssetResolver.kt +286 -0
  3. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +954 -837
  4. package/android/src/main/java/com/hotupdater/BundleMetadata.kt +15 -11
  5. package/android/src/main/java/com/hotupdater/FileManagerService.kt +45 -0
  6. package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +287 -22
  7. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +43 -31
  8. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +37 -54
  9. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryState.kt +64 -0
  10. package/android/src/main/java/com/hotupdater/InstallIdentityService.kt +37 -0
  11. package/android/src/main/java/com/hotupdater/KeyValueStorageService.kt +86 -0
  12. package/android/src/main/java/com/hotupdater/NetworkState.kt +31 -0
  13. package/android/src/main/java/com/hotupdater/NoBackupStorage.kt +17 -0
  14. package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +127 -50
  15. package/android/src/main/java/com/hotupdater/PreviousProcessExit.kt +89 -0
  16. package/android/src/main/java/com/hotupdater/ReactNativeValueConverters.kt +34 -0
  17. package/android/src/main/java/com/hotupdater/TarArchiveInputStream.kt +214 -375
  18. package/android/src/main/java/com/hotupdater/TarBrArchiveExtractor.kt +119 -0
  19. package/android/src/newarch/HotUpdater.kt +7 -9
  20. package/android/src/newarch/HotUpdaterModule.kt +109 -84
  21. package/android/src/oldarch/HotUpdater.kt +7 -9
  22. package/android/src/oldarch/HotUpdaterModule.kt +98 -74
  23. package/android/src/oldarch/HotUpdaterSpec.kt +4 -6
  24. package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
  25. package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
  26. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +950 -1112
  27. package/ios/HotUpdater/Internal/BundleMetadata.swift +8 -53
  28. package/ios/HotUpdater/Internal/FileManagerService.swift +48 -0
  29. package/ios/HotUpdater/Internal/FileUtilities.swift +64 -0
  30. package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +0 -1
  31. package/ios/HotUpdater/Internal/HotUpdater.mm +13 -45
  32. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +190 -73
  33. package/ios/HotUpdater/Internal/KeyValueStorageService.swift +71 -0
  34. package/ios/HotUpdater/Internal/NotificationExtension.swift +0 -2
  35. package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +182 -311
  36. package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +119 -71
  37. package/ios/HotUpdater/Internal/UpdateFailure.swift +390 -0
  38. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.h +16 -0
  39. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.mm +83 -0
  40. package/ios/HotUpdater/Recovery/HotUpdaterRecoveryHooks.mm +49 -0
  41. package/lib/commonjs/appReady.js +111 -0
  42. package/lib/commonjs/appReady.js.map +1 -0
  43. package/lib/commonjs/checkForUpdate.js +125 -70
  44. package/lib/commonjs/checkForUpdate.js.map +1 -1
  45. package/lib/commonjs/clientPlugin.js +13 -0
  46. package/lib/commonjs/clientPlugin.js.map +1 -0
  47. package/lib/commonjs/error.js +4 -13
  48. package/lib/commonjs/error.js.map +1 -1
  49. package/lib/commonjs/fetchJSON.js +5 -2
  50. package/lib/commonjs/fetchJSON.js.map +1 -1
  51. package/lib/commonjs/httpClient.js +55 -92
  52. package/lib/commonjs/httpClient.js.map +1 -1
  53. package/lib/commonjs/index.js +68 -20
  54. package/lib/commonjs/index.js.map +1 -1
  55. package/lib/commonjs/native.js +82 -62
  56. package/lib/commonjs/native.js.map +1 -1
  57. package/lib/commonjs/pluginHost.js +44 -0
  58. package/lib/commonjs/pluginHost.js.map +1 -0
  59. package/lib/commonjs/releaseCatalogCache.js +20 -61
  60. package/lib/commonjs/releaseCatalogCache.js.map +1 -1
  61. package/lib/commonjs/sdkVersion.js +1 -1
  62. package/lib/commonjs/sdkVersion.js.map +1 -1
  63. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  64. package/lib/commonjs/store.js +3 -10
  65. package/lib/commonjs/store.js.map +1 -1
  66. package/lib/commonjs/updateError.js +132 -0
  67. package/lib/commonjs/updateError.js.map +1 -0
  68. package/lib/commonjs/wrap.js +44 -27
  69. package/lib/commonjs/wrap.js.map +1 -1
  70. package/lib/module/appReady.js +103 -0
  71. package/lib/module/appReady.js.map +1 -0
  72. package/lib/module/checkForUpdate.js +118 -65
  73. package/lib/module/checkForUpdate.js.map +1 -1
  74. package/lib/module/clientPlugin.js +4 -0
  75. package/lib/module/clientPlugin.js.map +1 -0
  76. package/lib/module/error.js +4 -13
  77. package/lib/module/error.js.map +1 -1
  78. package/lib/module/fetchJSON.js +3 -1
  79. package/lib/module/fetchJSON.js.map +1 -1
  80. package/lib/module/httpClient.js +53 -90
  81. package/lib/module/httpClient.js.map +1 -1
  82. package/lib/module/index.js +55 -22
  83. package/lib/module/index.js.map +1 -1
  84. package/lib/module/native.js +74 -54
  85. package/lib/module/native.js.map +1 -1
  86. package/lib/module/pluginHost.js +35 -0
  87. package/lib/module/pluginHost.js.map +1 -0
  88. package/lib/module/releaseCatalogCache.js +16 -56
  89. package/lib/module/releaseCatalogCache.js.map +1 -1
  90. package/lib/module/sdkVersion.js +1 -1
  91. package/lib/module/sdkVersion.js.map +1 -1
  92. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  93. package/lib/module/store.js +3 -10
  94. package/lib/module/store.js.map +1 -1
  95. package/lib/module/updateError.js +126 -0
  96. package/lib/module/updateError.js.map +1 -0
  97. package/lib/module/wrap.js +45 -28
  98. package/lib/module/wrap.js.map +1 -1
  99. package/lib/typescript/commonjs/appReady.d.ts +23 -0
  100. package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
  101. package/lib/typescript/commonjs/checkForUpdate.d.ts +16 -4
  102. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  103. package/lib/typescript/commonjs/clientPlugin.d.ts +2 -0
  104. package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -0
  105. package/lib/typescript/commonjs/error.d.ts +4 -13
  106. package/lib/typescript/commonjs/error.d.ts.map +1 -1
  107. package/lib/typescript/commonjs/fetchJSON.d.ts +3 -0
  108. package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
  109. package/lib/typescript/commonjs/httpClient.d.ts +3 -32
  110. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  111. package/lib/typescript/commonjs/index.d.ts +7 -12
  112. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  113. package/lib/typescript/commonjs/native.d.ts +33 -23
  114. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  115. package/lib/typescript/commonjs/pluginHost.d.ts +4 -0
  116. package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
  117. package/lib/typescript/commonjs/releaseCatalogCache.d.ts +6 -14
  118. package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
  119. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  120. package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -1
  121. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +42 -47
  122. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/store.d.ts +1 -3
  124. package/lib/typescript/commonjs/store.d.ts.map +1 -1
  125. package/lib/typescript/commonjs/types.d.ts +1 -1
  126. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/updateError.d.ts +26 -0
  128. package/lib/typescript/commonjs/updateError.d.ts.map +1 -0
  129. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  130. package/lib/typescript/commonjs/wrap.types.d.ts +6 -7
  131. package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
  132. package/lib/typescript/module/appReady.d.ts +23 -0
  133. package/lib/typescript/module/appReady.d.ts.map +1 -0
  134. package/lib/typescript/module/checkForUpdate.d.ts +16 -4
  135. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  136. package/lib/typescript/module/clientPlugin.d.ts +2 -0
  137. package/lib/typescript/module/clientPlugin.d.ts.map +1 -0
  138. package/lib/typescript/module/error.d.ts +4 -13
  139. package/lib/typescript/module/error.d.ts.map +1 -1
  140. package/lib/typescript/module/fetchJSON.d.ts +3 -0
  141. package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
  142. package/lib/typescript/module/httpClient.d.ts +3 -32
  143. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  144. package/lib/typescript/module/index.d.ts +7 -12
  145. package/lib/typescript/module/index.d.ts.map +1 -1
  146. package/lib/typescript/module/native.d.ts +33 -23
  147. package/lib/typescript/module/native.d.ts.map +1 -1
  148. package/lib/typescript/module/pluginHost.d.ts +4 -0
  149. package/lib/typescript/module/pluginHost.d.ts.map +1 -0
  150. package/lib/typescript/module/releaseCatalogCache.d.ts +6 -14
  151. package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
  152. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  153. package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
  154. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +42 -47
  155. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  156. package/lib/typescript/module/store.d.ts +1 -3
  157. package/lib/typescript/module/store.d.ts.map +1 -1
  158. package/lib/typescript/module/types.d.ts +1 -1
  159. package/lib/typescript/module/types.d.ts.map +1 -1
  160. package/lib/typescript/module/updateError.d.ts +26 -0
  161. package/lib/typescript/module/updateError.d.ts.map +1 -0
  162. package/lib/typescript/module/wrap.d.ts.map +1 -1
  163. package/lib/typescript/module/wrap.types.d.ts +6 -7
  164. package/lib/typescript/module/wrap.types.d.ts.map +1 -1
  165. package/package.json +7 -4
  166. package/src/appReady.ts +149 -0
  167. package/src/checkForUpdate.ts +152 -89
  168. package/src/clientPlugin.ts +17 -0
  169. package/src/error.ts +4 -14
  170. package/src/fetchJSON.ts +5 -1
  171. package/src/httpClient.ts +78 -157
  172. package/src/index.ts +87 -29
  173. package/src/native.ts +109 -104
  174. package/src/pluginHost.ts +45 -0
  175. package/src/releaseCatalogCache.ts +26 -140
  176. package/src/sdkVersion.ts +1 -1
  177. package/src/specs/NativeHotUpdater.ts +41 -48
  178. package/src/store.ts +2 -23
  179. package/src/types.ts +1 -1
  180. package/src/updateError.ts +153 -0
  181. package/src/wrap.tsx +59 -28
  182. package/src/wrap.types.ts +6 -7
  183. package/android/src/main/java/com/hotupdater/DecompressService.kt +0 -89
  184. package/android/src/main/java/com/hotupdater/DecompressionStrategy.kt +0 -26
  185. package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +0 -117
  186. package/android/src/main/java/com/hotupdater/TarGzDecompressionStrategy.kt +0 -117
  187. package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +0 -192
  188. package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +0 -46
  189. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +0 -799
  190. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +0 -273
  191. package/android/src/test/java/com/hotupdater/NativeConfigUtilsTest.kt +0 -20
  192. package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +0 -111
  193. package/android/src/test/java/com/hotupdater/RelativePathResolverTest.kt +0 -41
  194. package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +0 -118
  195. package/android/src/test/java/com/hotupdater/TarArchiveInputStreamTest.kt +0 -68
  196. package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +0 -178
  197. package/ios/HotUpdater/Internal/DecompressService.swift +0 -112
  198. package/ios/HotUpdater/Internal/DecompressionStrategy.swift +0 -22
  199. package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +0 -370
  200. package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +0 -39
  201. package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +0 -59
  202. package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +0 -482
  203. package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +0 -56
  204. package/lib/commonjs/catalogCacheNative.spec.js +0 -31
  205. package/lib/commonjs/catalogCacheNative.spec.js.map +0 -1
  206. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +0 -604
  207. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  208. package/lib/commonjs/httpClient.spec.js +0 -102
  209. package/lib/commonjs/httpClient.spec.js.map +0 -1
  210. package/lib/commonjs/index.spec.js +0 -298
  211. package/lib/commonjs/index.spec.js.map +0 -1
  212. package/lib/commonjs/iosInterop.spec.js +0 -18
  213. package/lib/commonjs/iosInterop.spec.js.map +0 -1
  214. package/lib/commonjs/native.spec.js +0 -795
  215. package/lib/commonjs/native.spec.js.map +0 -1
  216. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +0 -218
  217. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  218. package/lib/commonjs/notifyAppReadyInsights.js +0 -133
  219. package/lib/commonjs/notifyAppReadyInsights.js.map +0 -1
  220. package/lib/commonjs/notifyAppReadyInsights.spec.js +0 -246
  221. package/lib/commonjs/notifyAppReadyInsights.spec.js.map +0 -1
  222. package/lib/commonjs/notifyAppReadyInsights.test-utils.js +0 -24
  223. package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +0 -1
  224. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +0 -117
  225. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +0 -1
  226. package/lib/commonjs/releaseCatalogCache.spec.js +0 -195
  227. package/lib/commonjs/releaseCatalogCache.spec.js.map +0 -1
  228. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +0 -64
  229. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  230. package/lib/commonjs/store.notification.spec.js +0 -76
  231. package/lib/commonjs/store.notification.spec.js.map +0 -1
  232. package/lib/commonjs/store.spec.js +0 -316
  233. package/lib/commonjs/store.spec.js.map +0 -1
  234. package/lib/commonjs/wrap.spec.js +0 -296
  235. package/lib/commonjs/wrap.spec.js.map +0 -1
  236. package/lib/module/catalogCacheNative.spec.js +0 -31
  237. package/lib/module/catalogCacheNative.spec.js.map +0 -1
  238. package/lib/module/checkForUpdate.releaseCatalog.spec.js +0 -603
  239. package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  240. package/lib/module/httpClient.spec.js +0 -102
  241. package/lib/module/httpClient.spec.js.map +0 -1
  242. package/lib/module/index.spec.js +0 -297
  243. package/lib/module/index.spec.js.map +0 -1
  244. package/lib/module/iosInterop.spec.js +0 -18
  245. package/lib/module/iosInterop.spec.js.map +0 -1
  246. package/lib/module/native.spec.js +0 -794
  247. package/lib/module/native.spec.js.map +0 -1
  248. package/lib/module/notifyAppReadyInsights.boundary.spec.js +0 -217
  249. package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  250. package/lib/module/notifyAppReadyInsights.js +0 -128
  251. package/lib/module/notifyAppReadyInsights.js.map +0 -1
  252. package/lib/module/notifyAppReadyInsights.spec.js +0 -245
  253. package/lib/module/notifyAppReadyInsights.spec.js.map +0 -1
  254. package/lib/module/notifyAppReadyInsights.test-utils.js +0 -18
  255. package/lib/module/notifyAppReadyInsights.test-utils.js.map +0 -1
  256. package/lib/module/notifyAppReadyInsights.transport.spec.js +0 -116
  257. package/lib/module/notifyAppReadyInsights.transport.spec.js.map +0 -1
  258. package/lib/module/releaseCatalogCache.spec.js +0 -195
  259. package/lib/module/releaseCatalogCache.spec.js.map +0 -1
  260. package/lib/module/releaseCatalogCacheNativeContract.spec.js +0 -64
  261. package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  262. package/lib/module/store.notification.spec.js +0 -77
  263. package/lib/module/store.notification.spec.js.map +0 -1
  264. package/lib/module/store.spec.js +0 -315
  265. package/lib/module/store.spec.js.map +0 -1
  266. package/lib/module/wrap.spec.js +0 -295
  267. package/lib/module/wrap.spec.js.map +0 -1
  268. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +0 -13
  269. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +0 -1
  270. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +0 -8
  271. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  272. package/lib/typescript/module/notifyAppReadyInsights.d.ts +0 -13
  273. package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +0 -1
  274. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +0 -8
  275. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  276. package/src/catalogCacheNative.spec.ts +0 -63
  277. package/src/checkForUpdate.releaseCatalog.spec.ts +0 -596
  278. package/src/httpClient.spec.ts +0 -132
  279. package/src/index.spec.ts +0 -331
  280. package/src/iosInterop.spec.ts +0 -30
  281. package/src/native.spec.ts +0 -855
  282. package/src/notifyAppReadyInsights.boundary.spec.ts +0 -234
  283. package/src/notifyAppReadyInsights.spec.ts +0 -243
  284. package/src/notifyAppReadyInsights.test-utils.ts +0 -31
  285. package/src/notifyAppReadyInsights.transport.spec.ts +0 -130
  286. package/src/notifyAppReadyInsights.ts +0 -194
  287. package/src/releaseCatalogCache.spec.ts +0 -282
  288. package/src/releaseCatalogCacheNativeContract.spec.ts +0 -80
  289. package/src/store.notification.spec.ts +0 -92
  290. package/src/store.spec.ts +0 -372
  291. package/src/wrap.spec.ts +0 -367
@@ -1,794 +0,0 @@
1
- "use strict";
2
-
3
- import { INVALID_COHORT_ERROR_MESSAGE } from "@hot-updater/core";
4
- import { beforeEach, describe, expect, it, vi } from "vitest";
5
- const nativeModuleMock = vi.hoisted(() => {
6
- const getManifest = vi.fn();
7
- const getCrashHistory = vi.fn(() => []);
8
- return {
9
- getActiveUpdateState: vi.fn(),
10
- clearCrashHistory: vi.fn(() => true),
11
- getBaseURL: vi.fn(() => null),
12
- getBundleId: vi.fn(() => "bundle-id"),
13
- getCohort: vi.fn(() => "123"),
14
- getInstallId: vi.fn(() => "install-id"),
15
- getUserId: vi.fn(() => null),
16
- getUsername: vi.fn(() => null),
17
- getManifest,
18
- getCrashHistory,
19
- getConstants: vi.fn(() => ({
20
- APP_VERSION: null,
21
- CHANNEL: "production",
22
- DEFAULT_CHANNEL: "production",
23
- FINGERPRINT_HASH: null,
24
- MIN_BUNDLE_ID: "min-bundle-id"
25
- })),
26
- notifyAppReady: vi.fn(),
27
- reload: vi.fn(),
28
- resetChannel: vi.fn(),
29
- setCohort: vi.fn(),
30
- setUser: vi.fn(),
31
- setBundleURL: vi.fn(),
32
- switchChannel: vi.fn(),
33
- updateBundle: vi.fn()
34
- };
35
- });
36
- vi.mock("react-native", () => ({
37
- NativeEventEmitter: class {
38
- addListener() {
39
- return {
40
- remove: () => {}
41
- };
42
- }
43
- },
44
- Platform: {
45
- OS: "ios"
46
- }
47
- }));
48
- vi.mock("./specs/NativeHotUpdater", () => ({
49
- default: nativeModuleMock
50
- }));
51
- describe("notifyAppReady", () => {
52
- beforeEach(() => {
53
- vi.resetModules();
54
- nativeModuleMock.notifyAppReady.mockReset();
55
- nativeModuleMock.getBaseURL.mockReset();
56
- nativeModuleMock.getBundleId.mockReset();
57
- nativeModuleMock.getCrashHistory.mockReset();
58
- nativeModuleMock.getConstants.mockReturnValue({
59
- APP_VERSION: null,
60
- CHANNEL: "production",
61
- DEFAULT_CHANNEL: "production",
62
- FINGERPRINT_HASH: null,
63
- MIN_BUNDLE_ID: "min-bundle-id"
64
- });
65
- nativeModuleMock.getBundleId.mockReturnValue("bundle-id");
66
- nativeModuleMock.getBaseURL.mockReturnValue(null);
67
- nativeModuleMock.getCrashHistory.mockReturnValue([]);
68
- nativeModuleMock.getCohort.mockReset();
69
- nativeModuleMock.getCohort.mockReturnValue("123");
70
- nativeModuleMock.getInstallId.mockReset();
71
- nativeModuleMock.getInstallId.mockReturnValue("install-id");
72
- nativeModuleMock.getManifest.mockReset();
73
- nativeModuleMock.getManifest.mockReturnValue({
74
- assets: {
75
- "index.android.bundle": {
76
- fileHash: "hash-123"
77
- }
78
- },
79
- bundleId: "bundle-id"
80
- });
81
- nativeModuleMock.resetChannel.mockReset();
82
- nativeModuleMock.setCohort.mockReset();
83
- nativeModuleMock.setUser.mockReset();
84
- nativeModuleMock.updateBundle.mockReset();
85
- });
86
- it("exposes active Release state without internal Catalog identity or history", async () => {
87
- const receipt = {
88
- bundleId: "bundle-123",
89
- releaseId: "release-123",
90
- kind: "BUNDLE",
91
- channel: "production",
92
- catalogId: "private-catalog",
93
- scopeKey: "private-scope",
94
- generation: 10,
95
- catalogHash: "private-hash",
96
- selectionContextHash: "private-context"
97
- };
98
- nativeModuleMock.getActiveUpdateState.mockReturnValue({
99
- activeSelection: receipt,
100
- stableSelection: receipt,
101
- verificationPending: true,
102
- highestSeenCatalogs: {
103
- "private-catalog|private-scope": {
104
- generation: 10,
105
- catalogHash: "private-hash"
106
- }
107
- }
108
- });
109
- const {
110
- getPublicActiveUpdateState
111
- } = await import("./native");
112
- const selection = {
113
- bundleId: "bundle-123",
114
- releaseId: "release-123",
115
- kind: "BUNDLE",
116
- channel: "production"
117
- };
118
- expect(getPublicActiveUpdateState()).toEqual({
119
- activeSelection: selection,
120
- stableSelection: selection,
121
- verificationPending: true
122
- });
123
- });
124
- it("normalizes legacy PROMOTED launch reports to UPDATE_APPLIED", async () => {
125
- nativeModuleMock.notifyAppReady.mockReturnValue(JSON.stringify({
126
- fromBundleId: "bundle-123",
127
- status: "PROMOTED",
128
- toBundleId: "bundle-456"
129
- }));
130
- const {
131
- notifyAppReady
132
- } = await import("./native");
133
- expect(notifyAppReady()).toEqual({
134
- fromBundleId: "bundle-123",
135
- status: "UPDATE_APPLIED",
136
- toBundleId: "bundle-456"
137
- });
138
- expect(nativeModuleMock.notifyAppReady).toHaveBeenCalledWith();
139
- });
140
- it("returns RECOVERED launch reports with directional ids", async () => {
141
- nativeModuleMock.notifyAppReady.mockReturnValue({
142
- fromBundleId: "bundle-123",
143
- status: "RECOVERED",
144
- toBundleId: "bundle-122",
145
- updateStrategy: "appVersion"
146
- });
147
- const {
148
- notifyAppReady,
149
- readNotifyAppReady
150
- } = await import("./native");
151
- expect(notifyAppReady()).toEqual({
152
- fromBundleId: "bundle-123",
153
- status: "RECOVERED",
154
- toBundleId: "bundle-122"
155
- });
156
- expect(readNotifyAppReady()).toEqual({
157
- insightsEvent: {
158
- fromBundleId: "bundle-123",
159
- toBundleId: "bundle-122",
160
- type: "RECOVERED",
161
- updateStrategy: "appVersion"
162
- },
163
- pending: false,
164
- result: {
165
- fromBundleId: "bundle-123",
166
- status: "RECOVERED",
167
- toBundleId: "bundle-122"
168
- }
169
- });
170
- });
171
- it("returns UNCHANGED for incomplete recovery payloads", async () => {
172
- nativeModuleMock.notifyAppReady.mockReturnValue({
173
- fromBundleId: "bundle-123",
174
- status: "RECOVERED",
175
- toBundleId: null,
176
- updateStrategy: "appVersion"
177
- });
178
- const {
179
- notifyAppReady,
180
- readNotifyAppReady
181
- } = await import("./native");
182
- expect(notifyAppReady()).toEqual({
183
- status: "UNCHANGED"
184
- });
185
- expect(readNotifyAppReady()).toEqual({
186
- insightsEvent: null,
187
- pending: false,
188
- result: {
189
- status: "UNCHANGED"
190
- }
191
- });
192
- });
193
- it("returns UNCHANGED when automatic insights metadata is incomplete", async () => {
194
- nativeModuleMock.notifyAppReady.mockReturnValue({
195
- status: "UPDATE_APPLIED"
196
- });
197
- const {
198
- notifyAppReady,
199
- readNotifyAppReady
200
- } = await import("./native");
201
- expect(notifyAppReady()).toEqual({
202
- status: "UNCHANGED"
203
- });
204
- expect(readNotifyAppReady()).toEqual({
205
- insightsEvent: null,
206
- pending: false,
207
- result: {
208
- status: "UNCHANGED"
209
- }
210
- });
211
- });
212
- it("normalizes malformed old-arch notifyAppReady payloads to UNCHANGED", async () => {
213
- nativeModuleMock.notifyAppReady.mockReturnValue("{");
214
- const {
215
- notifyAppReady,
216
- readNotifyAppReady
217
- } = await import("./native");
218
- expect(notifyAppReady()).toEqual({
219
- status: "UNCHANGED"
220
- });
221
- expect(readNotifyAppReady()).toEqual({
222
- insightsEvent: null,
223
- pending: false,
224
- result: {
225
- status: "UNCHANGED"
226
- }
227
- });
228
- });
229
- it("keeps the internal pending state out of the public result", async () => {
230
- nativeModuleMock.notifyAppReady.mockReturnValue({
231
- status: "PENDING"
232
- });
233
- const {
234
- notifyAppReady,
235
- readNotifyAppReady
236
- } = await import("./native");
237
- expect(notifyAppReady()).toEqual({
238
- status: "UNCHANGED"
239
- });
240
- expect(readNotifyAppReady()).toEqual({
241
- insightsEvent: null,
242
- pending: true,
243
- result: {
244
- status: "UNCHANGED"
245
- }
246
- });
247
- });
248
- it("returns the native bundle id when available", async () => {
249
- nativeModuleMock.getBundleId.mockReturnValue("bundle-123");
250
- const {
251
- getBundleId
252
- } = await import("./native");
253
- expect(getBundleId()).toBe("bundle-123");
254
- });
255
- it("throws when native SDK does not expose getBundleId", async () => {
256
- const nativeModule = nativeModuleMock;
257
- const originalGetBundleId = nativeModule.getBundleId;
258
- nativeModule.getBundleId = null;
259
- try {
260
- const {
261
- getBundleId
262
- } = await import("./native");
263
- expect(() => getBundleId()).toThrow("Native module is missing 'getBundleId()'");
264
- } finally {
265
- nativeModule.getBundleId = originalGetBundleId;
266
- }
267
- });
268
- it("falls back to MIN_BUNDLE_ID when native reports an empty bundle id", async () => {
269
- nativeModuleMock.getBundleId.mockReturnValue("");
270
- const {
271
- getBundleId
272
- } = await import("./native");
273
- expect(getBundleId()).toBe("min-bundle-id");
274
- });
275
- it("falls back to MIN_BUNDLE_ID when native bundle id is null", async () => {
276
- nativeModuleMock.getBundleId.mockReturnValue(null);
277
- const {
278
- getBundleId
279
- } = await import("./native");
280
- expect(getBundleId()).toBe("min-bundle-id");
281
- });
282
- it("falls back to MIN_BUNDLE_ID for legacy NIL_UUID bundle ids", async () => {
283
- nativeModuleMock.getBundleId.mockReturnValue("00000000-0000-0000-0000-000000000000");
284
- const {
285
- getBundleId
286
- } = await import("./native");
287
- expect(getBundleId()).toBe("min-bundle-id");
288
- });
289
- it("returns manifest from native objects", async () => {
290
- nativeModuleMock.getManifest.mockReturnValue({
291
- assets: {
292
- "assets/logo.png": {
293
- fileHash: "hash-logo",
294
- signature: "sig-logo"
295
- },
296
- "index.android.bundle": {
297
- fileHash: "hash-bundle"
298
- }
299
- },
300
- bundleId: "bundle-123"
301
- });
302
- const {
303
- getManifest
304
- } = await import("./native");
305
- expect(getManifest()).toEqual({
306
- assets: {
307
- "assets/logo.png": {
308
- fileHash: "hash-logo",
309
- signature: "sig-logo"
310
- },
311
- "index.android.bundle": {
312
- fileHash: "hash-bundle"
313
- }
314
- },
315
- bundleId: "bundle-123"
316
- });
317
- });
318
- it("normalizes legacy manifest asset entries from native objects", async () => {
319
- nativeModuleMock.getManifest.mockReturnValue({
320
- assets: {
321
- "assets/logo.png": "hash-logo"
322
- },
323
- bundleId: "bundle-123"
324
- });
325
- const {
326
- getManifest
327
- } = await import("./native");
328
- expect(getManifest()).toEqual({
329
- assets: {
330
- "assets/logo.png": {
331
- fileHash: "hash-logo"
332
- }
333
- },
334
- bundleId: "bundle-123"
335
- });
336
- });
337
- it("parses manifest from old-arch JSON payloads", async () => {
338
- nativeModuleMock.getManifest.mockReturnValue(JSON.stringify({
339
- assets: {
340
- "assets/logo.png": {
341
- fileHash: "hash-logo"
342
- }
343
- },
344
- bundleId: "bundle-123"
345
- }));
346
- const {
347
- getManifest
348
- } = await import("./native");
349
- expect(getManifest()).toEqual({
350
- assets: {
351
- "assets/logo.png": {
352
- fileHash: "hash-logo"
353
- }
354
- },
355
- bundleId: "bundle-123"
356
- });
357
- });
358
- it("normalizes legacy manifest asset entries from old-arch JSON payloads", async () => {
359
- nativeModuleMock.getManifest.mockReturnValue(JSON.stringify({
360
- assets: {
361
- "assets/logo.png": "hash-logo"
362
- },
363
- bundleId: "bundle-123"
364
- }));
365
- const {
366
- getManifest
367
- } = await import("./native");
368
- expect(getManifest()).toEqual({
369
- assets: {
370
- "assets/logo.png": {
371
- fileHash: "hash-logo"
372
- }
373
- },
374
- bundleId: "bundle-123"
375
- });
376
- });
377
- it("returns an empty-assets manifest for malformed payloads", async () => {
378
- nativeModuleMock.getManifest.mockReturnValue("{");
379
- const {
380
- getManifest
381
- } = await import("./native");
382
- expect(getManifest()).toEqual({
383
- assets: {},
384
- bundleId: "bundle-id"
385
- });
386
- });
387
- it("caches active bundle getters within a JS runtime", async () => {
388
- nativeModuleMock.getBundleId.mockReturnValue("bundle-123");
389
- nativeModuleMock.getManifest.mockReturnValue({
390
- assets: {
391
- "assets/logo.png": {
392
- fileHash: "hash-logo"
393
- }
394
- },
395
- bundleId: "bundle-123"
396
- });
397
- nativeModuleMock.getBaseURL.mockReturnValue("file:///bundle-123");
398
- const {
399
- getBaseURL,
400
- getBundleId,
401
- getManifest
402
- } = await import("./native");
403
- expect(getBundleId()).toBe("bundle-123");
404
- expect(getBundleId()).toBe("bundle-123");
405
- expect(nativeModuleMock.getBundleId).toHaveBeenCalledTimes(1);
406
- const firstManifest = getManifest();
407
- firstManifest.assets["assets/logo.png"] = {
408
- fileHash: "mutated-hash"
409
- };
410
- expect(getManifest()).toEqual({
411
- assets: {
412
- "assets/logo.png": {
413
- fileHash: "hash-logo"
414
- }
415
- },
416
- bundleId: "bundle-123"
417
- });
418
- expect(nativeModuleMock.getManifest).toHaveBeenCalledTimes(1);
419
- expect(getBaseURL()).toBe("file:///bundle-123");
420
- expect(getBaseURL()).toBe("file:///bundle-123");
421
- expect(nativeModuleMock.getBaseURL).toHaveBeenCalledTimes(1);
422
- });
423
- it("uses the launched bundle reported by native after updateBundle succeeds", async () => {
424
- nativeModuleMock.getBundleId.mockReturnValue("bundle-123");
425
- nativeModuleMock.getManifest.mockReturnValue({
426
- assets: {},
427
- bundleId: "bundle-123"
428
- });
429
- nativeModuleMock.getBaseURL.mockReturnValue("file:///bundle-123");
430
- nativeModuleMock.updateBundle.mockResolvedValue(true);
431
- const {
432
- getBaseURL,
433
- getBundleId,
434
- getManifest,
435
- updateBundle
436
- } = await import("./native");
437
- expect(getBundleId()).toBe("bundle-123");
438
- expect(getManifest()).toEqual({
439
- assets: {},
440
- bundleId: "bundle-123"
441
- });
442
- expect(getBaseURL()).toBe("file:///bundle-123");
443
- await updateBundle({
444
- bundleId: "bundle-456",
445
- fileHash: null,
446
- fileUrl: "https://example.com/bundle.zip",
447
- status: "UPDATE"
448
- });
449
- expect(getBundleId()).toBe("bundle-123");
450
- expect(getManifest()).toEqual({
451
- assets: {},
452
- bundleId: "bundle-123"
453
- });
454
- expect(getBaseURL()).toBe("file:///bundle-123");
455
- expect(nativeModuleMock.getBundleId).toHaveBeenCalledTimes(3);
456
- expect(nativeModuleMock.getManifest).toHaveBeenCalledTimes(2);
457
- expect(nativeModuleMock.getBaseURL).toHaveBeenCalledTimes(2);
458
- });
459
- it("reinstalls a session-installed bundle after native resets to built-in", async () => {
460
- nativeModuleMock.getConstants.mockReturnValue({
461
- APP_VERSION: null,
462
- CHANNEL: "production",
463
- DEFAULT_CHANNEL: "production",
464
- FINGERPRINT_HASH: null,
465
- MIN_BUNDLE_ID: "00000000-0000-0000-0000-000000000001"
466
- });
467
- nativeModuleMock.getBundleId.mockReturnValue("00000000-0000-0000-0000-000000000002");
468
- nativeModuleMock.updateBundle.mockResolvedValue(true);
469
- const {
470
- getBundleId,
471
- updateBundle
472
- } = await import("./native");
473
- await updateBundle({
474
- bundleId: "00000000-0000-0000-0000-000000000003",
475
- fileHash: null,
476
- fileUrl: "https://example.com/bundle.zip",
477
- status: "UPDATE"
478
- });
479
- nativeModuleMock.getBundleId.mockReturnValue("00000000-0000-0000-0000-000000000003");
480
- expect(getBundleId()).toBe("00000000-0000-0000-0000-000000000003");
481
- nativeModuleMock.getBundleId.mockReturnValue(null);
482
- await expect(updateBundle({
483
- bundleId: "00000000-0000-0000-0000-000000000003",
484
- fileHash: null,
485
- fileUrl: "https://example.com/bundle.zip",
486
- status: "UPDATE"
487
- })).resolves.toBe(true);
488
- expect(nativeModuleMock.updateBundle).toHaveBeenCalledTimes(2);
489
- });
490
- it("skips a session-installed bundle when native still reports it active", async () => {
491
- nativeModuleMock.getConstants.mockReturnValue({
492
- APP_VERSION: null,
493
- CHANNEL: "production",
494
- DEFAULT_CHANNEL: "production",
495
- FINGERPRINT_HASH: null,
496
- MIN_BUNDLE_ID: "00000000-0000-0000-0000-000000000001"
497
- });
498
- nativeModuleMock.getBundleId.mockReturnValue("00000000-0000-0000-0000-000000000002");
499
- nativeModuleMock.updateBundle.mockResolvedValue(true);
500
- const {
501
- updateBundle
502
- } = await import("./native");
503
- await updateBundle({
504
- bundleId: "00000000-0000-0000-0000-000000000003",
505
- fileHash: null,
506
- fileUrl: "https://example.com/bundle.zip",
507
- status: "UPDATE"
508
- });
509
- nativeModuleMock.getBundleId.mockReturnValue("00000000-0000-0000-0000-000000000003");
510
- await expect(updateBundle({
511
- bundleId: "00000000-0000-0000-0000-000000000003",
512
- fileHash: null,
513
- fileUrl: "https://example.com/bundle.zip",
514
- status: "UPDATE"
515
- })).resolves.toBe(true);
516
- expect(nativeModuleMock.updateBundle).toHaveBeenCalledTimes(1);
517
- });
518
- it("forwards manifest artifact parameters to native updateBundle", async () => {
519
- nativeModuleMock.getBundleId.mockReturnValue("bundle-123");
520
- nativeModuleMock.updateBundle.mockResolvedValue(true);
521
- const {
522
- updateBundle
523
- } = await import("./native");
524
- await updateBundle({
525
- bundleId: "bundle-789",
526
- changedAssets: {
527
- "index.ios.bundle": {
528
- file: {
529
- compression: "br",
530
- url: "https://example.com/files/index.ios.bundle.br"
531
- },
532
- fileHash: "hash-next",
533
- patch: {
534
- algorithm: "bsdiff",
535
- baseBundleId: "bundle-123",
536
- baseFileHash: "hash-prev",
537
- patchFileHash: "hash-patch",
538
- patchUrl: "https://example.com/files/index.ios.bundle.bsdiff"
539
- }
540
- }
541
- },
542
- fileHash: "sig:archive",
543
- fileUrl: "https://example.com/bundle.zip",
544
- manifestFileHash: "sig:manifest",
545
- manifestUrl: "https://example.com/manifest.json",
546
- status: "UPDATE"
547
- });
548
- expect(nativeModuleMock.updateBundle).toHaveBeenCalledWith({
549
- bundleId: "bundle-789",
550
- changedAssets: {
551
- "index.ios.bundle": {
552
- file: {
553
- compression: "br",
554
- url: "https://example.com/files/index.ios.bundle.br"
555
- },
556
- fileHash: "hash-next",
557
- patch: {
558
- algorithm: "bsdiff",
559
- baseBundleId: "bundle-123",
560
- baseFileHash: "hash-prev",
561
- patchFileHash: "hash-patch",
562
- patchUrl: "https://example.com/files/index.ios.bundle.bsdiff"
563
- }
564
- }
565
- },
566
- channel: undefined,
567
- fileHash: "sig:archive",
568
- fileUrl: "https://example.com/bundle.zip",
569
- manifestFileHash: "sig:manifest",
570
- manifestUrl: "https://example.com/manifest.json"
571
- });
572
- });
573
- it("invalidates cached bundle getters after resetChannel succeeds", async () => {
574
- nativeModuleMock.getConstants.mockReturnValue({
575
- APP_VERSION: null,
576
- CHANNEL: "beta",
577
- DEFAULT_CHANNEL: "production",
578
- FINGERPRINT_HASH: null,
579
- MIN_BUNDLE_ID: "min-bundle-id"
580
- });
581
- nativeModuleMock.getBundleId.mockReturnValue("bundle-beta");
582
- nativeModuleMock.getManifest.mockReturnValue({
583
- assets: {},
584
- bundleId: "bundle-beta"
585
- });
586
- nativeModuleMock.getBaseURL.mockReturnValue("file:///bundle-beta");
587
- nativeModuleMock.resetChannel.mockResolvedValue(true);
588
- const {
589
- getBaseURL,
590
- getBundleId,
591
- getManifest,
592
- resetChannel
593
- } = await import("./native");
594
- expect(getBundleId()).toBe("bundle-beta");
595
- expect(getManifest()).toEqual({
596
- assets: {},
597
- bundleId: "bundle-beta"
598
- });
599
- expect(getBaseURL()).toBe("file:///bundle-beta");
600
- nativeModuleMock.getBundleId.mockReturnValue(null);
601
- nativeModuleMock.getManifest.mockReturnValue({});
602
- nativeModuleMock.getBaseURL.mockReturnValue("");
603
- await expect(resetChannel()).resolves.toBe(true);
604
- expect(getBundleId()).toBe("min-bundle-id");
605
- expect(getManifest()).toEqual({
606
- assets: {},
607
- bundleId: "min-bundle-id"
608
- });
609
- expect(getBaseURL()).toBeNull();
610
- expect(nativeModuleMock.getBundleId).toHaveBeenCalledTimes(2);
611
- expect(nativeModuleMock.getManifest).toHaveBeenCalledTimes(2);
612
- expect(nativeModuleMock.getBaseURL).toHaveBeenCalledTimes(2);
613
- });
614
- it("delegates resetChannel to native even when exported channel constants already match", async () => {
615
- nativeModuleMock.getConstants.mockReturnValue({
616
- APP_VERSION: null,
617
- CHANNEL: "beta",
618
- DEFAULT_CHANNEL: "beta",
619
- FINGERPRINT_HASH: null,
620
- MIN_BUNDLE_ID: "min-bundle-id"
621
- });
622
- nativeModuleMock.resetChannel.mockResolvedValue(true);
623
- const {
624
- resetChannel
625
- } = await import("./native");
626
- await expect(resetChannel()).resolves.toBe(true);
627
- expect(nativeModuleMock.resetChannel).toHaveBeenCalledTimes(1);
628
- });
629
- it("parses crash history from legacy JSON payloads", async () => {
630
- nativeModuleMock.getCrashHistory.mockReturnValue(JSON.stringify(["bundle-1", "bundle-2"]));
631
- const {
632
- getCrashHistory
633
- } = await import("./native");
634
- expect(getCrashHistory()).toEqual(["bundle-1", "bundle-2"]);
635
- });
636
- it("falls back to an empty crash history for malformed payloads", async () => {
637
- nativeModuleMock.getCrashHistory.mockReturnValue("{");
638
- const {
639
- getCrashHistory
640
- } = await import("./native");
641
- expect(getCrashHistory()).toEqual([]);
642
- });
643
- it("passes normalized cohort overrides to native", async () => {
644
- const {
645
- setCohort
646
- } = await import("./native");
647
- setCohort(" QA-Group ");
648
- expect(nativeModuleMock.setCohort).toHaveBeenCalledWith("qa-group");
649
- });
650
- it("returns the most recently set cohort before native reads catch up", async () => {
651
- nativeModuleMock.getCohort.mockReturnValue("123");
652
- const {
653
- getCohort,
654
- setCohort
655
- } = await import("./native");
656
- setCohort(" QA-Group ");
657
- expect(getCohort()).toBe("qa-group");
658
- expect(nativeModuleMock.getCohort).not.toHaveBeenCalled();
659
- });
660
- it("throws when attempting to clear the cohort with an empty value", async () => {
661
- const {
662
- setCohort
663
- } = await import("./native");
664
- expect(() => setCohort("")).toThrow(INVALID_COHORT_ERROR_MESSAGE);
665
- expect(nativeModuleMock.setCohort).not.toHaveBeenCalled();
666
- });
667
- it("throws for invalid cohort overrides", async () => {
668
- const {
669
- setCohort
670
- } = await import("./native");
671
- expect(() => setCohort("Bad Cohort")).toThrow(INVALID_COHORT_ERROR_MESSAGE);
672
- expect(nativeModuleMock.setCohort).not.toHaveBeenCalled();
673
- });
674
- it("throws for cohort overrides longer than the limit", async () => {
675
- const {
676
- setCohort
677
- } = await import("./native");
678
- expect(() => setCohort("a".repeat(65))).toThrow(INVALID_COHORT_ERROR_MESSAGE);
679
- expect(nativeModuleMock.setCohort).not.toHaveBeenCalled();
680
- });
681
- it("returns the install id reported by native", async () => {
682
- nativeModuleMock.getInstallId.mockReturnValue("install-123");
683
- const {
684
- getInstallId
685
- } = await import("./native");
686
- expect(getInstallId()).toBe("install-123");
687
- });
688
- it("normalizes an omitted native fingerprint constant to null", async () => {
689
- nativeModuleMock.getConstants.mockReturnValue({
690
- APP_VERSION: null,
691
- CHANNEL: "production",
692
- DEFAULT_CHANNEL: "production",
693
- FINGERPRINT_HASH: undefined,
694
- MIN_BUNDLE_ID: "min-bundle-id"
695
- });
696
- const {
697
- getFingerprintHash
698
- } = await import("./native");
699
- expect(getFingerprintHash()).toBeNull();
700
- });
701
- it("throws when native SDK does not expose getInstallId", async () => {
702
- const nativeModule = nativeModuleMock;
703
- const originalGetInstallId = nativeModule.getInstallId;
704
- nativeModule.getInstallId = null;
705
- try {
706
- const {
707
- getInstallId
708
- } = await import("./native");
709
- expect(() => getInstallId()).toThrow("Native module is missing 'getInstallId()'");
710
- } finally {
711
- nativeModule.getInstallId = originalGetInstallId;
712
- }
713
- });
714
- it("passes nullable user identity through to native", async () => {
715
- const {
716
- setUser
717
- } = await import("./native");
718
- setUser({
719
- userId: "user-123",
720
- username: "alice"
721
- });
722
- setUser({
723
- userId: 42,
724
- username: "bob"
725
- });
726
- setUser({});
727
- setUser(null);
728
- expect(nativeModuleMock.setUser).toHaveBeenNthCalledWith(1, "user-123", "alice");
729
- expect(nativeModuleMock.setUser).toHaveBeenNthCalledWith(2, "42", "bob");
730
- expect(nativeModuleMock.setUser).toHaveBeenNthCalledWith(3, null, null);
731
- expect(nativeModuleMock.setUser).toHaveBeenNthCalledWith(4, null, null);
732
- });
733
- it("reads persisted user identity from native when available", async () => {
734
- nativeModuleMock.getUserId.mockReturnValue("user-123");
735
- nativeModuleMock.getUsername.mockReturnValue("alice");
736
- const {
737
- getPersistedUserIdentity
738
- } = await import("./native");
739
- expect(getPersistedUserIdentity()).toEqual({
740
- userId: "user-123",
741
- username: "alice"
742
- });
743
- });
744
- it("throws when native SDK does not expose persisted user identity getters", async () => {
745
- const nativeModule = nativeModuleMock;
746
- const originalGetUserId = nativeModule.getUserId;
747
- nativeModule.getUserId = null;
748
- try {
749
- const {
750
- getPersistedUserIdentity
751
- } = await import("./native");
752
- expect(() => getPersistedUserIdentity()).toThrow("Native module is missing 'getUserId()' or 'getUsername()'");
753
- } finally {
754
- nativeModule.getUserId = originalGetUserId;
755
- }
756
- });
757
- it("throws when native SDK does not expose setUser", async () => {
758
- const nativeModule = nativeModuleMock;
759
- const originalSetUser = nativeModule.setUser;
760
- nativeModule.setUser = null;
761
- try {
762
- const {
763
- setUser
764
- } = await import("./native");
765
- expect(() => setUser({
766
- userId: "user-123"
767
- })).toThrow("Native module is missing 'setUser()'");
768
- } finally {
769
- nativeModule.setUser = originalSetUser;
770
- }
771
- });
772
- it("returns the cohort reported by native", async () => {
773
- nativeModuleMock.getCohort.mockReturnValue("qa-group");
774
- const {
775
- getCohort
776
- } = await import("./native");
777
- expect(getCohort()).toBe("qa-group");
778
- });
779
- it("normalizes the cohort reported by native", async () => {
780
- nativeModuleMock.getCohort.mockReturnValue(" QA-GROUP ");
781
- const {
782
- getCohort
783
- } = await import("./native");
784
- expect(getCohort()).toBe("qa-group");
785
- });
786
- it("throws when native reports an invalid cohort", async () => {
787
- nativeModuleMock.getCohort.mockReturnValue("1001");
788
- const {
789
- getCohort
790
- } = await import("./native");
791
- expect(() => getCohort()).toThrow(INVALID_COHORT_ERROR_MESSAGE);
792
- });
793
- });
794
- //# sourceMappingURL=native.spec.js.map