@hot-updater/react-native 1.0.0-rc.17 → 1.0.0-rc.21

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 (240) hide show
  1. package/lib/commonjs/checkForUpdate.js +7 -9
  2. package/lib/commonjs/checkForUpdate.js.map +1 -1
  3. package/lib/commonjs/clientPlugin.js +7 -43
  4. package/lib/commonjs/clientPlugin.js.map +1 -1
  5. package/lib/commonjs/httpClient.js +7 -9
  6. package/lib/commonjs/httpClient.js.map +1 -1
  7. package/lib/commonjs/index.js +10 -0
  8. package/lib/commonjs/index.js.map +1 -1
  9. package/lib/commonjs/native.js +4 -4
  10. package/lib/commonjs/native.js.map +1 -1
  11. package/lib/commonjs/pluginHost.js +26 -190
  12. package/lib/commonjs/pluginHost.js.map +1 -1
  13. package/lib/commonjs/releaseCatalogCache.js +7 -59
  14. package/lib/commonjs/releaseCatalogCache.js.map +1 -1
  15. package/lib/commonjs/sdkVersion.js +1 -1
  16. package/lib/module/checkForUpdate.js +1 -3
  17. package/lib/module/checkForUpdate.js.map +1 -1
  18. package/lib/module/clientPlugin.js +1 -41
  19. package/lib/module/clientPlugin.js.map +1 -1
  20. package/lib/module/httpClient.js +1 -3
  21. package/lib/module/httpClient.js.map +1 -1
  22. package/lib/module/index.js +2 -0
  23. package/lib/module/index.js.map +1 -1
  24. package/lib/module/native.js +1 -1
  25. package/lib/module/native.js.map +1 -1
  26. package/lib/module/pluginHost.js +22 -187
  27. package/lib/module/pluginHost.js.map +1 -1
  28. package/lib/module/releaseCatalogCache.js +3 -54
  29. package/lib/module/releaseCatalogCache.js.map +1 -1
  30. package/lib/module/sdkVersion.js +1 -1
  31. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  32. package/lib/typescript/commonjs/clientPlugin.d.ts +1 -184
  33. package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -1
  34. package/lib/typescript/commonjs/httpClient.d.ts +1 -2
  35. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  36. package/lib/typescript/commonjs/index.d.ts +1 -0
  37. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  38. package/lib/typescript/commonjs/native.d.ts +1 -1
  39. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  40. package/lib/typescript/commonjs/pluginHost.d.ts +3 -28
  41. package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -1
  42. package/lib/typescript/commonjs/releaseCatalogCache.d.ts +1 -13
  43. package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
  44. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  45. package/lib/typescript/commonjs/types.d.ts +1 -1
  46. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  47. package/lib/typescript/commonjs/wrap.types.d.ts +2 -2
  48. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  49. package/lib/typescript/module/clientPlugin.d.ts +1 -184
  50. package/lib/typescript/module/clientPlugin.d.ts.map +1 -1
  51. package/lib/typescript/module/httpClient.d.ts +1 -2
  52. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  53. package/lib/typescript/module/index.d.ts +1 -0
  54. package/lib/typescript/module/index.d.ts.map +1 -1
  55. package/lib/typescript/module/native.d.ts +1 -1
  56. package/lib/typescript/module/native.d.ts.map +1 -1
  57. package/lib/typescript/module/pluginHost.d.ts +3 -28
  58. package/lib/typescript/module/pluginHost.d.ts.map +1 -1
  59. package/lib/typescript/module/releaseCatalogCache.d.ts +1 -13
  60. package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
  61. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  62. package/lib/typescript/module/types.d.ts +1 -1
  63. package/lib/typescript/module/types.d.ts.map +1 -1
  64. package/lib/typescript/module/wrap.types.d.ts +2 -2
  65. package/package.json +7 -16
  66. package/src/checkForUpdate.ts +4 -6
  67. package/src/clientPlugin.ts +17 -231
  68. package/src/httpClient.ts +4 -4
  69. package/src/index.ts +7 -0
  70. package/src/native.ts +1 -1
  71. package/src/pluginHost.ts +24 -241
  72. package/src/releaseCatalogCache.ts +7 -136
  73. package/src/sdkVersion.ts +1 -1
  74. package/src/types.ts +1 -1
  75. package/src/wrap.types.ts +2 -2
  76. package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +0 -46
  77. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +0 -2236
  78. package/android/src/test/java/com/hotupdater/HotUpdaterExceptionTest.kt +0 -220
  79. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +0 -264
  80. package/android/src/test/java/com/hotupdater/HotUpdaterRecoveryStateTest.kt +0 -97
  81. package/android/src/test/java/com/hotupdater/InstallIdentityServiceTest.kt +0 -71
  82. package/android/src/test/java/com/hotupdater/KeyValueStorageServiceTest.kt +0 -106
  83. package/android/src/test/java/com/hotupdater/NativeConfigUtilsTest.kt +0 -20
  84. package/android/src/test/java/com/hotupdater/NoBackupContext.kt +0 -11
  85. package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +0 -397
  86. package/android/src/test/java/com/hotupdater/PendingCrashRecoveryTest.kt +0 -44
  87. package/android/src/test/java/com/hotupdater/PreviousProcessExitTest.kt +0 -57
  88. package/android/src/test/java/com/hotupdater/RelativePathResolverTest.kt +0 -41
  89. package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +0 -118
  90. package/android/src/test/java/com/hotupdater/TarBrArchiveExtractorTest.kt +0 -206
  91. package/lib/commonjs/appReady.spec.js +0 -360
  92. package/lib/commonjs/appReady.spec.js.map +0 -1
  93. package/lib/commonjs/appReady.test-utils.js +0 -25
  94. package/lib/commonjs/appReady.test-utils.js.map +0 -1
  95. package/lib/commonjs/baseURL.js +0 -16
  96. package/lib/commonjs/baseURL.js.map +0 -1
  97. package/lib/commonjs/catalogCacheNative.spec.js +0 -31
  98. package/lib/commonjs/catalogCacheNative.spec.js.map +0 -1
  99. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +0 -936
  100. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  101. package/lib/commonjs/entryImportGraph.spec.js +0 -50
  102. package/lib/commonjs/entryImportGraph.spec.js.map +0 -1
  103. package/lib/commonjs/httpClient.spec.js +0 -171
  104. package/lib/commonjs/httpClient.spec.js.map +0 -1
  105. package/lib/commonjs/index.spec.js +0 -395
  106. package/lib/commonjs/index.spec.js.map +0 -1
  107. package/lib/commonjs/iosInterop.spec.js +0 -18
  108. package/lib/commonjs/iosInterop.spec.js.map +0 -1
  109. package/lib/commonjs/native.spec.js +0 -910
  110. package/lib/commonjs/native.spec.js.map +0 -1
  111. package/lib/commonjs/pluginHost.spec.js +0 -292
  112. package/lib/commonjs/pluginHost.spec.js.map +0 -1
  113. package/lib/commonjs/plugins/insights/eventId.js +0 -129
  114. package/lib/commonjs/plugins/insights/eventId.js.map +0 -1
  115. package/lib/commonjs/plugins/insights/eventId.spec.js +0 -47
  116. package/lib/commonjs/plugins/insights/eventId.spec.js.map +0 -1
  117. package/lib/commonjs/plugins/insights/index.js +0 -272
  118. package/lib/commonjs/plugins/insights/index.js.map +0 -1
  119. package/lib/commonjs/plugins/insights/insights.spec.js +0 -640
  120. package/lib/commonjs/plugins/insights/insights.spec.js.map +0 -1
  121. package/lib/commonjs/plugins/insights/sender.js +0 -128
  122. package/lib/commonjs/plugins/insights/sender.js.map +0 -1
  123. package/lib/commonjs/plugins/insights/sender.spec.js +0 -265
  124. package/lib/commonjs/plugins/insights/sender.spec.js.map +0 -1
  125. package/lib/commonjs/plugins/insights/state.js +0 -145
  126. package/lib/commonjs/plugins/insights/state.js.map +0 -1
  127. package/lib/commonjs/releaseCatalogCache.spec.js +0 -213
  128. package/lib/commonjs/releaseCatalogCache.spec.js.map +0 -1
  129. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +0 -64
  130. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  131. package/lib/commonjs/store.notification.spec.js +0 -84
  132. package/lib/commonjs/store.notification.spec.js.map +0 -1
  133. package/lib/commonjs/store.spec.js +0 -182
  134. package/lib/commonjs/store.spec.js.map +0 -1
  135. package/lib/commonjs/updateError.spec.js +0 -149
  136. package/lib/commonjs/updateError.spec.js.map +0 -1
  137. package/lib/commonjs/wrap.progress.spec.js +0 -195
  138. package/lib/commonjs/wrap.progress.spec.js.map +0 -1
  139. package/lib/commonjs/wrap.spec.js +0 -232
  140. package/lib/commonjs/wrap.spec.js.map +0 -1
  141. package/lib/module/appReady.spec.js +0 -359
  142. package/lib/module/appReady.spec.js.map +0 -1
  143. package/lib/module/appReady.test-utils.js +0 -19
  144. package/lib/module/appReady.test-utils.js.map +0 -1
  145. package/lib/module/baseURL.js +0 -11
  146. package/lib/module/baseURL.js.map +0 -1
  147. package/lib/module/catalogCacheNative.spec.js +0 -31
  148. package/lib/module/catalogCacheNative.spec.js.map +0 -1
  149. package/lib/module/checkForUpdate.releaseCatalog.spec.js +0 -935
  150. package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  151. package/lib/module/entryImportGraph.spec.js +0 -50
  152. package/lib/module/entryImportGraph.spec.js.map +0 -1
  153. package/lib/module/httpClient.spec.js +0 -171
  154. package/lib/module/httpClient.spec.js.map +0 -1
  155. package/lib/module/index.spec.js +0 -394
  156. package/lib/module/index.spec.js.map +0 -1
  157. package/lib/module/iosInterop.spec.js +0 -18
  158. package/lib/module/iosInterop.spec.js.map +0 -1
  159. package/lib/module/native.spec.js +0 -909
  160. package/lib/module/native.spec.js.map +0 -1
  161. package/lib/module/pluginHost.spec.js +0 -291
  162. package/lib/module/pluginHost.spec.js.map +0 -1
  163. package/lib/module/plugins/insights/eventId.js +0 -122
  164. package/lib/module/plugins/insights/eventId.js.map +0 -1
  165. package/lib/module/plugins/insights/eventId.spec.js +0 -47
  166. package/lib/module/plugins/insights/eventId.spec.js.map +0 -1
  167. package/lib/module/plugins/insights/index.js +0 -268
  168. package/lib/module/plugins/insights/index.js.map +0 -1
  169. package/lib/module/plugins/insights/insights.spec.js +0 -639
  170. package/lib/module/plugins/insights/insights.spec.js.map +0 -1
  171. package/lib/module/plugins/insights/sender.js +0 -123
  172. package/lib/module/plugins/insights/sender.js.map +0 -1
  173. package/lib/module/plugins/insights/sender.spec.js +0 -265
  174. package/lib/module/plugins/insights/sender.spec.js.map +0 -1
  175. package/lib/module/plugins/insights/state.js +0 -139
  176. package/lib/module/plugins/insights/state.js.map +0 -1
  177. package/lib/module/releaseCatalogCache.spec.js +0 -213
  178. package/lib/module/releaseCatalogCache.spec.js.map +0 -1
  179. package/lib/module/releaseCatalogCacheNativeContract.spec.js +0 -64
  180. package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  181. package/lib/module/store.notification.spec.js +0 -85
  182. package/lib/module/store.notification.spec.js.map +0 -1
  183. package/lib/module/store.spec.js +0 -181
  184. package/lib/module/store.spec.js.map +0 -1
  185. package/lib/module/updateError.spec.js +0 -149
  186. package/lib/module/updateError.spec.js.map +0 -1
  187. package/lib/module/wrap.progress.spec.js +0 -196
  188. package/lib/module/wrap.progress.spec.js.map +0 -1
  189. package/lib/module/wrap.spec.js +0 -233
  190. package/lib/module/wrap.spec.js.map +0 -1
  191. package/lib/typescript/commonjs/appReady.test-utils.d.ts +0 -9
  192. package/lib/typescript/commonjs/appReady.test-utils.d.ts.map +0 -1
  193. package/lib/typescript/commonjs/baseURL.d.ts +0 -4
  194. package/lib/typescript/commonjs/baseURL.d.ts.map +0 -1
  195. package/lib/typescript/commonjs/plugins/insights/eventId.d.ts +0 -18
  196. package/lib/typescript/commonjs/plugins/insights/eventId.d.ts.map +0 -1
  197. package/lib/typescript/commonjs/plugins/insights/index.d.ts +0 -40
  198. package/lib/typescript/commonjs/plugins/insights/index.d.ts.map +0 -1
  199. package/lib/typescript/commonjs/plugins/insights/sender.d.ts +0 -74
  200. package/lib/typescript/commonjs/plugins/insights/sender.d.ts.map +0 -1
  201. package/lib/typescript/commonjs/plugins/insights/state.d.ts +0 -46
  202. package/lib/typescript/commonjs/plugins/insights/state.d.ts.map +0 -1
  203. package/lib/typescript/module/appReady.test-utils.d.ts +0 -9
  204. package/lib/typescript/module/appReady.test-utils.d.ts.map +0 -1
  205. package/lib/typescript/module/baseURL.d.ts +0 -4
  206. package/lib/typescript/module/baseURL.d.ts.map +0 -1
  207. package/lib/typescript/module/plugins/insights/eventId.d.ts +0 -18
  208. package/lib/typescript/module/plugins/insights/eventId.d.ts.map +0 -1
  209. package/lib/typescript/module/plugins/insights/index.d.ts +0 -40
  210. package/lib/typescript/module/plugins/insights/index.d.ts.map +0 -1
  211. package/lib/typescript/module/plugins/insights/sender.d.ts +0 -74
  212. package/lib/typescript/module/plugins/insights/sender.d.ts.map +0 -1
  213. package/lib/typescript/module/plugins/insights/state.d.ts +0 -46
  214. package/lib/typescript/module/plugins/insights/state.d.ts.map +0 -1
  215. package/plugins/insights/package.json +0 -8
  216. package/src/appReady.spec.ts +0 -393
  217. package/src/appReady.test-utils.ts +0 -31
  218. package/src/baseURL.ts +0 -15
  219. package/src/catalogCacheNative.spec.ts +0 -63
  220. package/src/checkForUpdate.releaseCatalog.spec.ts +0 -966
  221. package/src/entryImportGraph.spec.ts +0 -65
  222. package/src/httpClient.spec.ts +0 -216
  223. package/src/index.spec.ts +0 -434
  224. package/src/iosInterop.spec.ts +0 -30
  225. package/src/native.spec.ts +0 -978
  226. package/src/pluginHost.spec.ts +0 -297
  227. package/src/plugins/insights/eventId.spec.ts +0 -60
  228. package/src/plugins/insights/eventId.ts +0 -171
  229. package/src/plugins/insights/index.ts +0 -367
  230. package/src/plugins/insights/insights.spec.ts +0 -732
  231. package/src/plugins/insights/sender.spec.ts +0 -287
  232. package/src/plugins/insights/sender.ts +0 -221
  233. package/src/plugins/insights/state.ts +0 -184
  234. package/src/releaseCatalogCache.spec.ts +0 -304
  235. package/src/releaseCatalogCacheNativeContract.spec.ts +0 -80
  236. package/src/store.notification.spec.ts +0 -102
  237. package/src/store.spec.ts +0 -215
  238. package/src/updateError.spec.ts +0 -144
  239. package/src/wrap.progress.spec.ts +0 -232
  240. package/src/wrap.spec.ts +0 -262
@@ -1,909 +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
- getStorageItem: vi.fn(() => null),
16
- getManifest,
17
- getCrashHistory,
18
- getConstants: vi.fn(() => ({
19
- APP_VERSION: null,
20
- CHANNEL: "production",
21
- DEFAULT_CHANNEL: "production",
22
- FINGERPRINT_HASH: null,
23
- MIN_BUNDLE_ID: "min-bundle-id"
24
- })),
25
- notifyAppReady: vi.fn(),
26
- reload: vi.fn(),
27
- resetChannel: vi.fn(),
28
- setCohort: vi.fn(),
29
- setStorageItem: vi.fn(),
30
- setBundleURL: vi.fn(),
31
- switchChannel: vi.fn(),
32
- updateBundle: vi.fn()
33
- };
34
- });
35
- vi.mock("react-native", () => ({
36
- NativeEventEmitter: class {
37
- addListener() {
38
- return {
39
- remove: () => {}
40
- };
41
- }
42
- },
43
- Platform: {
44
- OS: "ios"
45
- }
46
- }));
47
- vi.mock("./specs/NativeHotUpdater", () => ({
48
- default: nativeModuleMock
49
- }));
50
- describe("notifyAppReady", () => {
51
- beforeEach(() => {
52
- vi.resetModules();
53
- nativeModuleMock.getActiveUpdateState.mockReset();
54
- nativeModuleMock.getActiveUpdateState.mockReturnValue({
55
- activeSelection: null,
56
- stableSelection: null,
57
- verificationPending: false,
58
- highestSeenCatalogs: {}
59
- });
60
- nativeModuleMock.notifyAppReady.mockReset();
61
- nativeModuleMock.getBaseURL.mockReset();
62
- nativeModuleMock.getBundleId.mockReset();
63
- nativeModuleMock.getCrashHistory.mockReset();
64
- nativeModuleMock.getConstants.mockReturnValue({
65
- APP_VERSION: null,
66
- CHANNEL: "production",
67
- DEFAULT_CHANNEL: "production",
68
- FINGERPRINT_HASH: null,
69
- MIN_BUNDLE_ID: "min-bundle-id"
70
- });
71
- nativeModuleMock.getBundleId.mockReturnValue("bundle-id");
72
- nativeModuleMock.getBaseURL.mockReturnValue(null);
73
- nativeModuleMock.getCrashHistory.mockReturnValue([]);
74
- nativeModuleMock.getCohort.mockReset();
75
- nativeModuleMock.getCohort.mockReturnValue("123");
76
- nativeModuleMock.getInstallId.mockReset();
77
- nativeModuleMock.getInstallId.mockReturnValue("install-id");
78
- nativeModuleMock.getManifest.mockReset();
79
- nativeModuleMock.getManifest.mockReturnValue({
80
- assets: {
81
- "index.android.bundle": {
82
- fileHash: "hash-123"
83
- }
84
- },
85
- bundleId: "bundle-id"
86
- });
87
- nativeModuleMock.resetChannel.mockReset();
88
- nativeModuleMock.setCohort.mockReset();
89
- nativeModuleMock.getStorageItem.mockReset();
90
- nativeModuleMock.getStorageItem.mockReturnValue(null);
91
- nativeModuleMock.setStorageItem.mockReset();
92
- nativeModuleMock.updateBundle.mockReset();
93
- });
94
- it("exposes active Release state without internal Catalog identity or history", async () => {
95
- const receipt = {
96
- bundleId: "bundle-123",
97
- releaseId: "release-123",
98
- kind: "BUNDLE",
99
- channel: "production",
100
- catalogId: "private-catalog",
101
- scopeKey: "private-scope",
102
- generation: 10,
103
- catalogHash: "private-hash",
104
- selectionContextHash: "private-context"
105
- };
106
- nativeModuleMock.getActiveUpdateState.mockReturnValue({
107
- activeSelection: receipt,
108
- stableSelection: receipt,
109
- verificationPending: true,
110
- highestSeenCatalogs: {
111
- "private-catalog|private-scope": {
112
- generation: 10,
113
- catalogHash: "private-hash"
114
- }
115
- }
116
- });
117
- const {
118
- getPublicActiveUpdateState
119
- } = await import("./native");
120
- const selection = {
121
- bundleId: "bundle-123",
122
- releaseId: "release-123",
123
- kind: "BUNDLE",
124
- channel: "production"
125
- };
126
- expect(getPublicActiveUpdateState()).toEqual({
127
- activeSelection: selection,
128
- stableSelection: selection,
129
- verificationPending: true
130
- });
131
- });
132
- it("tracks same-file promotions without changing artifact or crash identities", async () => {
133
- const state = {
134
- activeSelection: {
135
- kind: "BUNDLE",
136
- bundleId: "bundle-123",
137
- releaseId: "release-1"
138
- }
139
- };
140
- nativeModuleMock.getActiveUpdateState.mockImplementation(() => state);
141
- nativeModuleMock.getBundleId.mockReturnValue("bundle-123");
142
- nativeModuleMock.getManifest.mockReturnValue({
143
- assets: {}
144
- });
145
- nativeModuleMock.getCrashHistory.mockReturnValue(["bundle-failed"]);
146
- const {
147
- getUpdateId,
148
- getBundleId,
149
- getManifest,
150
- getCrashHistory
151
- } = await import("./native");
152
- expect(getUpdateId()).toBe("release-1");
153
- state.activeSelection.releaseId = "release-2";
154
- expect(getUpdateId()).toBe("release-2");
155
- expect(getBundleId()).toBe("bundle-123");
156
- expect(getManifest().bundleId).toBe("bundle-123");
157
- expect(getCrashHistory()).toEqual(["bundle-failed"]);
158
- });
159
- it.each([[null, "min-bundle-id"], ["legacy-bundle", "legacy-bundle"]])("preserves the fallback ID for native bundle %s", async (bundleId, expected) => {
160
- nativeModuleMock.getBundleId.mockReturnValue(bundleId);
161
- const {
162
- getUpdateId
163
- } = await import("./native");
164
- expect(getUpdateId()).toBe(expected);
165
- });
166
- it("reports a staged update while the manifest still identifies the running file", async () => {
167
- const runningSelection = {
168
- kind: "BUNDLE",
169
- bundleId: "running-file",
170
- releaseId: "running-update"
171
- };
172
- const state = {
173
- activeSelection: runningSelection,
174
- stableSelection: runningSelection
175
- };
176
- nativeModuleMock.getActiveUpdateState.mockImplementation(() => state);
177
- nativeModuleMock.getBundleId.mockReturnValue("running-file");
178
- nativeModuleMock.getManifest.mockReturnValue({
179
- assets: {},
180
- bundleId: "running-file"
181
- });
182
- const {
183
- getUpdateId,
184
- getBundleId,
185
- getManifest
186
- } = await import("./native");
187
- expect(getUpdateId()).toBe("running-update");
188
- state.activeSelection = {
189
- kind: "BUNDLE",
190
- bundleId: "staged-file",
191
- releaseId: "staged-update"
192
- };
193
- expect(getUpdateId()).toBe("staged-update");
194
- expect(getBundleId()).toBe("running-file");
195
- expect(getManifest().bundleId).toBe("running-file");
196
- });
197
- it("reports an explicit built-in rollback by its update ID", async () => {
198
- nativeModuleMock.getActiveUpdateState.mockReturnValue(JSON.stringify({
199
- activeSelection: {
200
- kind: "EMBEDDED",
201
- releaseId: "rollback-1",
202
- bundleId: "min-bundle-id"
203
- }
204
- }));
205
- nativeModuleMock.getBundleId.mockReturnValue(null);
206
- const {
207
- getUpdateId,
208
- getBundleId
209
- } = await import("./native");
210
- expect(getUpdateId()).toBe("rollback-1");
211
- expect(getBundleId()).toBe("min-bundle-id");
212
- });
213
- it("normalizes legacy PROMOTED launch reports to UPDATE_APPLIED", async () => {
214
- nativeModuleMock.notifyAppReady.mockReturnValue(JSON.stringify({
215
- fromBundleId: "bundle-123",
216
- status: "PROMOTED",
217
- toBundleId: "bundle-456"
218
- }));
219
- const {
220
- notifyAppReady
221
- } = await import("./native");
222
- expect(notifyAppReady()).toEqual({
223
- fromBundleId: "bundle-123",
224
- status: "UPDATE_APPLIED",
225
- toBundleId: "bundle-456"
226
- });
227
- expect(nativeModuleMock.notifyAppReady).toHaveBeenCalledWith();
228
- });
229
- it("returns RECOVERED launch reports with directional ids", async () => {
230
- nativeModuleMock.notifyAppReady.mockReturnValue({
231
- fromBundleId: "bundle-123",
232
- status: "RECOVERED",
233
- toBundleId: "bundle-122",
234
- updateStrategy: "appVersion"
235
- });
236
- const {
237
- notifyAppReady,
238
- readNotifyAppReady
239
- } = await import("./native");
240
- expect(notifyAppReady()).toEqual({
241
- fromBundleId: "bundle-123",
242
- status: "RECOVERED",
243
- toBundleId: "bundle-122"
244
- });
245
- expect(readNotifyAppReady()).toEqual({
246
- previousProcessExit: null,
247
- transition: {
248
- fromBundleId: "bundle-123",
249
- fromReleaseId: null,
250
- toBundleId: "bundle-122",
251
- toReleaseId: null,
252
- type: "RECOVERED",
253
- updateStrategy: "appVersion"
254
- },
255
- pending: false,
256
- result: {
257
- fromBundleId: "bundle-123",
258
- status: "RECOVERED",
259
- toBundleId: "bundle-122"
260
- }
261
- });
262
- });
263
- it("returns UNCHANGED for incomplete recovery payloads", async () => {
264
- nativeModuleMock.notifyAppReady.mockReturnValue({
265
- fromBundleId: "bundle-123",
266
- status: "RECOVERED",
267
- toBundleId: null,
268
- updateStrategy: "appVersion"
269
- });
270
- const {
271
- notifyAppReady,
272
- readNotifyAppReady
273
- } = await import("./native");
274
- expect(notifyAppReady()).toEqual({
275
- status: "UNCHANGED"
276
- });
277
- expect(readNotifyAppReady()).toEqual({
278
- previousProcessExit: null,
279
- transition: null,
280
- pending: false,
281
- result: {
282
- status: "UNCHANGED"
283
- }
284
- });
285
- });
286
- it("returns UNCHANGED when an applied launch report has no bundle ids", async () => {
287
- nativeModuleMock.notifyAppReady.mockReturnValue({
288
- status: "UPDATE_APPLIED"
289
- });
290
- const {
291
- notifyAppReady,
292
- readNotifyAppReady
293
- } = await import("./native");
294
- expect(notifyAppReady()).toEqual({
295
- status: "UNCHANGED"
296
- });
297
- expect(readNotifyAppReady()).toEqual({
298
- previousProcessExit: null,
299
- transition: null,
300
- pending: false,
301
- result: {
302
- status: "UNCHANGED"
303
- }
304
- });
305
- });
306
- it("normalizes malformed old-arch notifyAppReady payloads to UNCHANGED", async () => {
307
- nativeModuleMock.notifyAppReady.mockReturnValue("{");
308
- const {
309
- notifyAppReady,
310
- readNotifyAppReady
311
- } = await import("./native");
312
- expect(notifyAppReady()).toEqual({
313
- status: "UNCHANGED"
314
- });
315
- expect(readNotifyAppReady()).toEqual({
316
- previousProcessExit: null,
317
- transition: null,
318
- pending: false,
319
- result: {
320
- status: "UNCHANGED"
321
- }
322
- });
323
- });
324
- it("keeps the internal pending state out of the public result", async () => {
325
- nativeModuleMock.notifyAppReady.mockReturnValue({
326
- status: "PENDING"
327
- });
328
- const {
329
- notifyAppReady,
330
- readNotifyAppReady
331
- } = await import("./native");
332
- expect(notifyAppReady()).toEqual({
333
- status: "UNCHANGED"
334
- });
335
- expect(readNotifyAppReady()).toEqual({
336
- previousProcessExit: null,
337
- transition: null,
338
- pending: true,
339
- result: {
340
- status: "UNCHANGED"
341
- }
342
- });
343
- });
344
- it("returns the native bundle id when available", async () => {
345
- nativeModuleMock.getBundleId.mockReturnValue("bundle-123");
346
- const {
347
- getBundleId
348
- } = await import("./native");
349
- expect(getBundleId()).toBe("bundle-123");
350
- });
351
- it("throws when native SDK does not expose getBundleId", async () => {
352
- const nativeModule = nativeModuleMock;
353
- const originalGetBundleId = nativeModule.getBundleId;
354
- nativeModule.getBundleId = null;
355
- try {
356
- const {
357
- getBundleId
358
- } = await import("./native");
359
- expect(() => getBundleId()).toThrow("Native module is missing 'getBundleId()'");
360
- } finally {
361
- nativeModule.getBundleId = originalGetBundleId;
362
- }
363
- });
364
- it("falls back to MIN_BUNDLE_ID when native reports an empty bundle id", async () => {
365
- nativeModuleMock.getBundleId.mockReturnValue("");
366
- const {
367
- getBundleId
368
- } = await import("./native");
369
- expect(getBundleId()).toBe("min-bundle-id");
370
- });
371
- it("falls back to MIN_BUNDLE_ID when native bundle id is null", async () => {
372
- nativeModuleMock.getBundleId.mockReturnValue(null);
373
- const {
374
- getBundleId
375
- } = await import("./native");
376
- expect(getBundleId()).toBe("min-bundle-id");
377
- });
378
- it("falls back to MIN_BUNDLE_ID for legacy NIL_UUID bundle ids", async () => {
379
- nativeModuleMock.getBundleId.mockReturnValue("00000000-0000-0000-0000-000000000000");
380
- const {
381
- getBundleId
382
- } = await import("./native");
383
- expect(getBundleId()).toBe("min-bundle-id");
384
- });
385
- it("returns manifest from native objects", async () => {
386
- nativeModuleMock.getManifest.mockReturnValue({
387
- assets: {
388
- "assets/logo.png": {
389
- fileHash: "hash-logo",
390
- signature: "sig-logo"
391
- },
392
- "index.android.bundle": {
393
- fileHash: "hash-bundle"
394
- }
395
- },
396
- bundleId: "bundle-123"
397
- });
398
- const {
399
- getManifest
400
- } = await import("./native");
401
- expect(getManifest()).toEqual({
402
- assets: {
403
- "assets/logo.png": {
404
- fileHash: "hash-logo",
405
- signature: "sig-logo"
406
- },
407
- "index.android.bundle": {
408
- fileHash: "hash-bundle"
409
- }
410
- },
411
- bundleId: "bundle-123"
412
- });
413
- });
414
- it("normalizes legacy manifest asset entries from native objects", async () => {
415
- nativeModuleMock.getManifest.mockReturnValue({
416
- assets: {
417
- "assets/logo.png": "hash-logo"
418
- },
419
- bundleId: "bundle-123"
420
- });
421
- const {
422
- getManifest
423
- } = await import("./native");
424
- expect(getManifest()).toEqual({
425
- assets: {
426
- "assets/logo.png": {
427
- fileHash: "hash-logo"
428
- }
429
- },
430
- bundleId: "bundle-123"
431
- });
432
- });
433
- it("parses manifest from old-arch JSON payloads", async () => {
434
- nativeModuleMock.getManifest.mockReturnValue(JSON.stringify({
435
- assets: {
436
- "assets/logo.png": {
437
- fileHash: "hash-logo"
438
- }
439
- },
440
- bundleId: "bundle-123"
441
- }));
442
- const {
443
- getManifest
444
- } = await import("./native");
445
- expect(getManifest()).toEqual({
446
- assets: {
447
- "assets/logo.png": {
448
- fileHash: "hash-logo"
449
- }
450
- },
451
- bundleId: "bundle-123"
452
- });
453
- });
454
- it("normalizes legacy manifest asset entries from old-arch JSON payloads", async () => {
455
- nativeModuleMock.getManifest.mockReturnValue(JSON.stringify({
456
- assets: {
457
- "assets/logo.png": "hash-logo"
458
- },
459
- bundleId: "bundle-123"
460
- }));
461
- const {
462
- getManifest
463
- } = await import("./native");
464
- expect(getManifest()).toEqual({
465
- assets: {
466
- "assets/logo.png": {
467
- fileHash: "hash-logo"
468
- }
469
- },
470
- bundleId: "bundle-123"
471
- });
472
- });
473
- it("returns an empty-assets manifest for malformed payloads", async () => {
474
- nativeModuleMock.getManifest.mockReturnValue("{");
475
- const {
476
- getManifest
477
- } = await import("./native");
478
- expect(getManifest()).toEqual({
479
- assets: {},
480
- bundleId: "bundle-id"
481
- });
482
- });
483
- it("caches active bundle getters within a JS runtime", async () => {
484
- nativeModuleMock.getBundleId.mockReturnValue("bundle-123");
485
- nativeModuleMock.getManifest.mockReturnValue({
486
- assets: {
487
- "assets/logo.png": {
488
- fileHash: "hash-logo"
489
- }
490
- },
491
- bundleId: "bundle-123"
492
- });
493
- nativeModuleMock.getBaseURL.mockReturnValue("file:///bundle-123");
494
- const {
495
- getBaseURL,
496
- getBundleId,
497
- getManifest
498
- } = await import("./native");
499
- expect(getBundleId()).toBe("bundle-123");
500
- expect(getBundleId()).toBe("bundle-123");
501
- expect(nativeModuleMock.getBundleId).toHaveBeenCalledTimes(1);
502
- const firstManifest = getManifest();
503
- firstManifest.assets["assets/logo.png"] = {
504
- fileHash: "mutated-hash"
505
- };
506
- expect(getManifest()).toEqual({
507
- assets: {
508
- "assets/logo.png": {
509
- fileHash: "hash-logo"
510
- }
511
- },
512
- bundleId: "bundle-123"
513
- });
514
- expect(nativeModuleMock.getManifest).toHaveBeenCalledTimes(1);
515
- expect(getBaseURL()).toBe("file:///bundle-123");
516
- expect(getBaseURL()).toBe("file:///bundle-123");
517
- expect(nativeModuleMock.getBaseURL).toHaveBeenCalledTimes(1);
518
- });
519
- it("uses the launched bundle reported by native after updateBundle succeeds", async () => {
520
- nativeModuleMock.getBundleId.mockReturnValue("bundle-123");
521
- nativeModuleMock.getManifest.mockReturnValue({
522
- assets: {},
523
- bundleId: "bundle-123"
524
- });
525
- nativeModuleMock.getBaseURL.mockReturnValue("file:///bundle-123");
526
- nativeModuleMock.updateBundle.mockResolvedValue(true);
527
- const {
528
- getBaseURL,
529
- getBundleId,
530
- getManifest,
531
- updateBundle
532
- } = await import("./native");
533
- expect(getBundleId()).toBe("bundle-123");
534
- expect(getManifest()).toEqual({
535
- assets: {},
536
- bundleId: "bundle-123"
537
- });
538
- expect(getBaseURL()).toBe("file:///bundle-123");
539
- await updateBundle({
540
- assets: {},
541
- bundleId: "bundle-456",
542
- archiveUrl: "https://example.com/bundle.tar.br",
543
- manifestFileHash: "manifest-hash",
544
- manifestUrl: "https://example.com/manifest.json",
545
- status: "UPDATE"
546
- });
547
- expect(getBundleId()).toBe("bundle-123");
548
- expect(nativeModuleMock.updateBundle).toHaveBeenCalledWith(expect.objectContaining({
549
- archiveUrl: "https://example.com/bundle.tar.br"
550
- }));
551
- expect(getManifest()).toEqual({
552
- assets: {},
553
- bundleId: "bundle-123"
554
- });
555
- expect(getBaseURL()).toBe("file:///bundle-123");
556
- expect(nativeModuleMock.getBundleId).toHaveBeenCalledTimes(3);
557
- expect(nativeModuleMock.getManifest).toHaveBeenCalledTimes(2);
558
- expect(nativeModuleMock.getBaseURL).toHaveBeenCalledTimes(2);
559
- });
560
- it("reinstalls a session-installed bundle after native resets to built-in", async () => {
561
- nativeModuleMock.getConstants.mockReturnValue({
562
- APP_VERSION: null,
563
- CHANNEL: "production",
564
- DEFAULT_CHANNEL: "production",
565
- FINGERPRINT_HASH: null,
566
- MIN_BUNDLE_ID: "00000000-0000-0000-0000-000000000001"
567
- });
568
- nativeModuleMock.getBundleId.mockReturnValue("00000000-0000-0000-0000-000000000002");
569
- nativeModuleMock.updateBundle.mockResolvedValue(true);
570
- const {
571
- getBundleId,
572
- updateBundle
573
- } = await import("./native");
574
- await updateBundle({
575
- assets: {},
576
- bundleId: "00000000-0000-0000-0000-000000000003",
577
- manifestFileHash: "manifest-hash",
578
- manifestUrl: "https://example.com/manifest.json",
579
- status: "UPDATE"
580
- });
581
- nativeModuleMock.getBundleId.mockReturnValue("00000000-0000-0000-0000-000000000003");
582
- expect(getBundleId()).toBe("00000000-0000-0000-0000-000000000003");
583
- nativeModuleMock.getBundleId.mockReturnValue(null);
584
- await expect(updateBundle({
585
- assets: {},
586
- bundleId: "00000000-0000-0000-0000-000000000003",
587
- manifestFileHash: "manifest-hash",
588
- manifestUrl: "https://example.com/manifest.json",
589
- status: "UPDATE"
590
- })).resolves.toBe(true);
591
- expect(nativeModuleMock.updateBundle).toHaveBeenCalledTimes(2);
592
- });
593
- it("skips a session-installed bundle when native still reports it active", async () => {
594
- nativeModuleMock.getConstants.mockReturnValue({
595
- APP_VERSION: null,
596
- CHANNEL: "production",
597
- DEFAULT_CHANNEL: "production",
598
- FINGERPRINT_HASH: null,
599
- MIN_BUNDLE_ID: "00000000-0000-0000-0000-000000000001"
600
- });
601
- nativeModuleMock.getBundleId.mockReturnValue("00000000-0000-0000-0000-000000000002");
602
- nativeModuleMock.updateBundle.mockResolvedValue(true);
603
- const {
604
- updateBundle
605
- } = await import("./native");
606
- await updateBundle({
607
- assets: {},
608
- bundleId: "00000000-0000-0000-0000-000000000003",
609
- manifestFileHash: "manifest-hash",
610
- manifestUrl: "https://example.com/manifest.json",
611
- status: "UPDATE"
612
- });
613
- nativeModuleMock.getBundleId.mockReturnValue("00000000-0000-0000-0000-000000000003");
614
- await expect(updateBundle({
615
- assets: {},
616
- bundleId: "00000000-0000-0000-0000-000000000003",
617
- manifestFileHash: "manifest-hash",
618
- manifestUrl: "https://example.com/manifest.json",
619
- status: "UPDATE"
620
- })).resolves.toBe(true);
621
- expect(nativeModuleMock.updateBundle).toHaveBeenCalledTimes(1);
622
- });
623
- it("forwards manifest artifact parameters to native updateBundle", async () => {
624
- nativeModuleMock.getBundleId.mockReturnValue("bundle-123");
625
- nativeModuleMock.updateBundle.mockResolvedValue(true);
626
- const {
627
- updateBundle
628
- } = await import("./native");
629
- await updateBundle({
630
- bundleId: "bundle-789",
631
- assets: {
632
- "index.ios.bundle": {
633
- file: {
634
- compression: "br",
635
- url: "https://example.com/files/index.ios.bundle.br"
636
- },
637
- fileHash: "hash-next",
638
- patch: {
639
- algorithm: "bsdiff",
640
- baseBundleId: "bundle-123",
641
- baseFileHash: "hash-prev",
642
- patchFileHash: "hash-patch",
643
- patchUrl: "https://example.com/files/index.ios.bundle.bsdiff"
644
- }
645
- }
646
- },
647
- manifestFileHash: "sig:manifest",
648
- manifestUrl: "https://example.com/manifest.json",
649
- status: "UPDATE"
650
- });
651
- expect(nativeModuleMock.updateBundle).toHaveBeenCalledWith({
652
- bundleId: "bundle-789",
653
- assets: {
654
- "index.ios.bundle": {
655
- file: {
656
- compression: "br",
657
- url: "https://example.com/files/index.ios.bundle.br"
658
- },
659
- fileHash: "hash-next",
660
- patch: {
661
- algorithm: "bsdiff",
662
- baseBundleId: "bundle-123",
663
- baseFileHash: "hash-prev",
664
- patchFileHash: "hash-patch",
665
- patchUrl: "https://example.com/files/index.ios.bundle.bsdiff"
666
- }
667
- }
668
- },
669
- channel: undefined,
670
- manifestFileHash: "sig:manifest",
671
- manifestUrl: "https://example.com/manifest.json"
672
- });
673
- });
674
- it("resolves how native delivered a staged bundle", async () => {
675
- nativeModuleMock.getBundleId.mockReturnValue("bundle-123");
676
- nativeModuleMock.updateBundle.mockResolvedValue({
677
- delivery: "patch",
678
- patchFallback: true
679
- });
680
- const {
681
- stageBundle,
682
- updateBundle
683
- } = await import("./native");
684
- const params = {
685
- assets: {},
686
- bundleId: "bundle-789",
687
- manifestFileHash: "sig:manifest",
688
- manifestUrl: "https://example.com/manifest.json",
689
- status: "UPDATE"
690
- };
691
- await expect(stageBundle(params)).resolves.toEqual({
692
- delivery: "patch",
693
- patchFallback: true
694
- });
695
- // Native still reports bundle-123, so the same bundle downloads again.
696
- await expect(updateBundle(params)).resolves.toBe(true);
697
- nativeModuleMock.getBundleId.mockReturnValue("bundle-789");
698
- // Now native reports it: this runtime already staged it.
699
- await expect(stageBundle(params)).resolves.toBeNull();
700
- expect(nativeModuleMock.updateBundle).toHaveBeenCalledTimes(2);
701
- });
702
- it("passes on why the previous process exited from the launch report", async () => {
703
- nativeModuleMock.notifyAppReady.mockReturnValue({
704
- previousProcessExit: "LOW_MEMORY",
705
- status: "UNCHANGED"
706
- });
707
- const {
708
- readNotifyAppReady
709
- } = await import("./native");
710
- expect(readNotifyAppReady()).toEqual({
711
- previousProcessExit: "LOW_MEMORY",
712
- transition: null,
713
- pending: false,
714
- result: {
715
- status: "UNCHANGED"
716
- }
717
- });
718
- });
719
- it("invalidates cached bundle getters after resetChannel succeeds", async () => {
720
- nativeModuleMock.getConstants.mockReturnValue({
721
- APP_VERSION: null,
722
- CHANNEL: "beta",
723
- DEFAULT_CHANNEL: "production",
724
- FINGERPRINT_HASH: null,
725
- MIN_BUNDLE_ID: "min-bundle-id"
726
- });
727
- nativeModuleMock.getBundleId.mockReturnValue("bundle-beta");
728
- nativeModuleMock.getManifest.mockReturnValue({
729
- assets: {},
730
- bundleId: "bundle-beta"
731
- });
732
- nativeModuleMock.getBaseURL.mockReturnValue("file:///bundle-beta");
733
- nativeModuleMock.resetChannel.mockResolvedValue(true);
734
- const {
735
- getBaseURL,
736
- getBundleId,
737
- getManifest,
738
- resetChannel
739
- } = await import("./native");
740
- expect(getBundleId()).toBe("bundle-beta");
741
- expect(getManifest()).toEqual({
742
- assets: {},
743
- bundleId: "bundle-beta"
744
- });
745
- expect(getBaseURL()).toBe("file:///bundle-beta");
746
- nativeModuleMock.getBundleId.mockReturnValue(null);
747
- nativeModuleMock.getManifest.mockReturnValue({});
748
- nativeModuleMock.getBaseURL.mockReturnValue("");
749
- await expect(resetChannel()).resolves.toBe(true);
750
- expect(getBundleId()).toBe("min-bundle-id");
751
- expect(getManifest()).toEqual({
752
- assets: {},
753
- bundleId: "min-bundle-id"
754
- });
755
- expect(getBaseURL()).toBeNull();
756
- expect(nativeModuleMock.getBundleId).toHaveBeenCalledTimes(2);
757
- expect(nativeModuleMock.getManifest).toHaveBeenCalledTimes(2);
758
- expect(nativeModuleMock.getBaseURL).toHaveBeenCalledTimes(2);
759
- });
760
- it("delegates resetChannel to native even when exported channel constants already match", async () => {
761
- nativeModuleMock.getConstants.mockReturnValue({
762
- APP_VERSION: null,
763
- CHANNEL: "beta",
764
- DEFAULT_CHANNEL: "beta",
765
- FINGERPRINT_HASH: null,
766
- MIN_BUNDLE_ID: "min-bundle-id"
767
- });
768
- nativeModuleMock.resetChannel.mockResolvedValue(true);
769
- const {
770
- resetChannel
771
- } = await import("./native");
772
- await expect(resetChannel()).resolves.toBe(true);
773
- expect(nativeModuleMock.resetChannel).toHaveBeenCalledTimes(1);
774
- });
775
- it("parses crash history from legacy JSON payloads", async () => {
776
- nativeModuleMock.getCrashHistory.mockReturnValue(JSON.stringify(["bundle-1", "bundle-2"]));
777
- const {
778
- getCrashHistory
779
- } = await import("./native");
780
- expect(getCrashHistory()).toEqual(["bundle-1", "bundle-2"]);
781
- });
782
- it("falls back to an empty crash history for malformed payloads", async () => {
783
- nativeModuleMock.getCrashHistory.mockReturnValue("{");
784
- const {
785
- getCrashHistory
786
- } = await import("./native");
787
- expect(getCrashHistory()).toEqual([]);
788
- });
789
- it("passes normalized cohort overrides to native", async () => {
790
- const {
791
- setCohort
792
- } = await import("./native");
793
- setCohort(" QA-Group ");
794
- expect(nativeModuleMock.setCohort).toHaveBeenCalledWith("qa-group");
795
- });
796
- it("returns the most recently set cohort before native reads catch up", async () => {
797
- nativeModuleMock.getCohort.mockReturnValue("123");
798
- const {
799
- getCohort,
800
- setCohort
801
- } = await import("./native");
802
- setCohort(" QA-Group ");
803
- expect(getCohort()).toBe("qa-group");
804
- expect(nativeModuleMock.getCohort).not.toHaveBeenCalled();
805
- });
806
- it("throws when attempting to clear the cohort with an empty value", async () => {
807
- const {
808
- setCohort
809
- } = await import("./native");
810
- expect(() => setCohort("")).toThrow(INVALID_COHORT_ERROR_MESSAGE);
811
- expect(nativeModuleMock.setCohort).not.toHaveBeenCalled();
812
- });
813
- it("throws for invalid cohort overrides", async () => {
814
- const {
815
- setCohort
816
- } = await import("./native");
817
- expect(() => setCohort("Bad Cohort")).toThrow(INVALID_COHORT_ERROR_MESSAGE);
818
- expect(nativeModuleMock.setCohort).not.toHaveBeenCalled();
819
- });
820
- it("throws for cohort overrides longer than the limit", async () => {
821
- const {
822
- setCohort
823
- } = await import("./native");
824
- expect(() => setCohort("a".repeat(65))).toThrow(INVALID_COHORT_ERROR_MESSAGE);
825
- expect(nativeModuleMock.setCohort).not.toHaveBeenCalled();
826
- });
827
- it("returns the install id reported by native", async () => {
828
- nativeModuleMock.getInstallId.mockReturnValue("install-123");
829
- const {
830
- getInstallId
831
- } = await import("./native");
832
- expect(getInstallId()).toBe("install-123");
833
- });
834
- it("normalizes an omitted native fingerprint constant to null", async () => {
835
- nativeModuleMock.getConstants.mockReturnValue({
836
- APP_VERSION: null,
837
- CHANNEL: "production",
838
- DEFAULT_CHANNEL: "production",
839
- FINGERPRINT_HASH: undefined,
840
- MIN_BUNDLE_ID: "min-bundle-id"
841
- });
842
- const {
843
- getFingerprintHash
844
- } = await import("./native");
845
- expect(getFingerprintHash()).toBeNull();
846
- });
847
- it("throws when native SDK does not expose getInstallId", async () => {
848
- const nativeModule = nativeModuleMock;
849
- const originalGetInstallId = nativeModule.getInstallId;
850
- nativeModule.getInstallId = null;
851
- try {
852
- const {
853
- getInstallId
854
- } = await import("./native");
855
- expect(() => getInstallId()).toThrow("Native module is missing 'getInstallId()'");
856
- } finally {
857
- nativeModule.getInstallId = originalGetInstallId;
858
- }
859
- });
860
- it("reads and writes the native key-value store", async () => {
861
- nativeModuleMock.getStorageItem.mockImplementation(key => key === "plugins/example/state" ? "stored" : null);
862
- const {
863
- getStorageItem,
864
- setStorageItem
865
- } = await import("./native");
866
- expect(getStorageItem("plugins/example/state")).toBe("stored");
867
- expect(getStorageItem("plugins/example/missing")).toBeNull();
868
- setStorageItem("plugins/example/state", "next");
869
- setStorageItem("plugins/example/state", null);
870
- expect(nativeModuleMock.setStorageItem).toHaveBeenNthCalledWith(1, "plugins/example/state", "next");
871
- expect(nativeModuleMock.setStorageItem).toHaveBeenNthCalledWith(2, "plugins/example/state", null);
872
- });
873
- it.each(["getStorageItem", "setStorageItem"])("throws when native SDK does not expose %s", async name => {
874
- const nativeModule = nativeModuleMock;
875
- const original = nativeModule[name];
876
- nativeModule[name] = undefined;
877
- try {
878
- const {
879
- getStorageItem,
880
- setStorageItem
881
- } = await import("./native");
882
- expect(() => name === "getStorageItem" ? getStorageItem("key") : setStorageItem("key", "value")).toThrow(`Native module is missing '${name}()'`);
883
- } finally {
884
- nativeModule[name] = original;
885
- }
886
- });
887
- it("returns the cohort reported by native", async () => {
888
- nativeModuleMock.getCohort.mockReturnValue("qa-group");
889
- const {
890
- getCohort
891
- } = await import("./native");
892
- expect(getCohort()).toBe("qa-group");
893
- });
894
- it("normalizes the cohort reported by native", async () => {
895
- nativeModuleMock.getCohort.mockReturnValue(" QA-GROUP ");
896
- const {
897
- getCohort
898
- } = await import("./native");
899
- expect(getCohort()).toBe("qa-group");
900
- });
901
- it("throws when native reports an invalid cohort", async () => {
902
- nativeModuleMock.getCohort.mockReturnValue("1001");
903
- const {
904
- getCohort
905
- } = await import("./native");
906
- expect(() => getCohort()).toThrow(INVALID_COHORT_ERROR_MESSAGE);
907
- });
908
- });
909
- //# sourceMappingURL=native.spec.js.map