@volley/vwr-loader 1.0.5 → 1.1.0

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 (60) hide show
  1. package/README.md +21 -0
  2. package/dist/amplitudeFlagFetcher.d.ts +4 -2
  3. package/dist/amplitudeFlagFetcher.d.ts.map +1 -1
  4. package/dist/amplitudeFlagFetcher.js +26 -19
  5. package/dist/amplitudeFlagFetcher.js.map +1 -1
  6. package/dist/cli.js +213 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/envDefaults.d.ts +3 -0
  9. package/dist/envDefaults.d.ts.map +1 -1
  10. package/dist/envDefaults.js +16 -4
  11. package/dist/envDefaults.js.map +1 -1
  12. package/dist/getDeviceId.d.ts +2 -40
  13. package/dist/getDeviceId.d.ts.map +1 -1
  14. package/dist/getDeviceId.js +31 -18
  15. package/dist/getDeviceId.js.map +1 -1
  16. package/dist/getEnvironment.d.ts +1 -1
  17. package/dist/getEnvironment.d.ts.map +1 -1
  18. package/dist/getEnvironment.js +15 -7
  19. package/dist/getEnvironment.js.map +1 -1
  20. package/dist/getShellVersion.d.ts +2 -19
  21. package/dist/getShellVersion.d.ts.map +1 -1
  22. package/dist/getShellVersion.js +41 -6
  23. package/dist/getShellVersion.js.map +1 -1
  24. package/dist/index.html +1 -1
  25. package/dist/loadVwr.d.ts.map +1 -1
  26. package/dist/loadVwr.js +8 -6
  27. package/dist/loadVwr.js.map +1 -1
  28. package/dist/main.js +1 -1
  29. package/dist/main.js.map +1 -1
  30. package/dist/types.d.ts +87 -0
  31. package/dist/types.d.ts.map +1 -0
  32. package/dist/types.js +5 -0
  33. package/dist/types.js.map +1 -0
  34. package/dist/vwrConfig.d.ts +2 -0
  35. package/dist/vwrConfig.d.ts.map +1 -1
  36. package/dist/vwrConfig.js +148 -119
  37. package/dist/vwrConfig.js.map +1 -1
  38. package/package.json +16 -5
  39. package/src/amplitudeFlagFetcher.test.ts +36 -59
  40. package/src/amplitudeFlagFetcher.ts +31 -22
  41. package/src/envDefaults.ts +23 -4
  42. package/src/getDeviceId.test.ts +89 -27
  43. package/src/getDeviceId.ts +35 -58
  44. package/src/getEnvironment.test.ts +479 -0
  45. package/src/getEnvironment.ts +16 -8
  46. package/src/getShellVersion.test.ts +123 -4
  47. package/src/getShellVersion.ts +46 -21
  48. package/src/loadVwr.ts +13 -6
  49. package/src/main.ts +3 -1
  50. package/src/types.ts +88 -0
  51. package/src/vite-env.d.ts +3 -0
  52. package/src/vwrConfig.test.ts +2 -2
  53. package/src/vwrConfig.ts +202 -199
  54. package/eslint.config.mjs +0 -23
  55. package/scripts/build.js +0 -2
  56. package/scripts/build.ts +0 -207
  57. package/tsconfig.eslint.json +0 -16
  58. package/tsconfig.json +0 -17
  59. package/vite.config.ts +0 -24
  60. package/vitest.config.ts +0 -8
@@ -0,0 +1,479 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
2
+
3
+ import { type Environment, getEnvironment } from "./getEnvironment"
4
+
5
+ describe("getEnvironment", () => {
6
+ const mockLogger = {
7
+ info: vi.fn(),
8
+ warn: vi.fn(),
9
+ error: vi.fn(),
10
+ }
11
+
12
+ beforeEach(() => {
13
+ vi.clearAllMocks()
14
+ mockLogger.info.mockClear()
15
+ mockLogger.warn.mockClear()
16
+ mockLogger.error.mockClear()
17
+ // Clean up window properties
18
+ delete (window as any).Capacitor
19
+ })
20
+
21
+ afterEach(() => {
22
+ vi.restoreAllMocks()
23
+ delete (window as any).Capacitor
24
+ })
25
+
26
+ describe("Fire TV - Native Environment Detection", () => {
27
+ it("should read environment from native BuildConfig for Fire TV", async () => {
28
+ // Setup Capacitor plugin mock
29
+ Object.defineProperty(window, "Capacitor", {
30
+ value: {
31
+ Plugins: {
32
+ DeviceInfo: {
33
+ getNativeShellAppEnvironment: vi
34
+ .fn()
35
+ .mockResolvedValue({
36
+ environment: "development",
37
+ }),
38
+ },
39
+ },
40
+ },
41
+ writable: true,
42
+ configurable: true,
43
+ })
44
+
45
+ const result = await getEnvironment(
46
+ "FIRE_TV",
47
+ undefined,
48
+ mockLogger
49
+ )
50
+
51
+ expect(result.environment).toBe("dev")
52
+ expect(result.source).toBe("native")
53
+ expect(mockLogger.info).toHaveBeenCalledWith(
54
+ expect.stringContaining(
55
+ 'Environment: "dev" (source: native BuildConfig.ENVIRONMENT="development")'
56
+ )
57
+ )
58
+ })
59
+
60
+ it("should map 'production' to 'prod'", async () => {
61
+ Object.defineProperty(window, "Capacitor", {
62
+ value: {
63
+ Plugins: {
64
+ DeviceInfo: {
65
+ getNativeShellAppEnvironment: vi
66
+ .fn()
67
+ .mockResolvedValue({
68
+ environment: "production",
69
+ }),
70
+ },
71
+ },
72
+ },
73
+ writable: true,
74
+ configurable: true,
75
+ })
76
+
77
+ const result = await getEnvironment(
78
+ "FIRE_TV",
79
+ undefined,
80
+ mockLogger
81
+ )
82
+
83
+ expect(result.environment).toBe("prod")
84
+ expect(result.source).toBe("native")
85
+ })
86
+
87
+ it("should map 'staging' to 'staging'", async () => {
88
+ Object.defineProperty(window, "Capacitor", {
89
+ value: {
90
+ Plugins: {
91
+ DeviceInfo: {
92
+ getNativeShellAppEnvironment: vi
93
+ .fn()
94
+ .mockResolvedValue({
95
+ environment: "staging",
96
+ }),
97
+ },
98
+ },
99
+ },
100
+ writable: true,
101
+ configurable: true,
102
+ })
103
+
104
+ const result = await getEnvironment(
105
+ "FIRE_TV",
106
+ undefined,
107
+ mockLogger
108
+ )
109
+
110
+ expect(result.environment).toBe("staging")
111
+ expect(result.source).toBe("native")
112
+ })
113
+
114
+ it("should map legacy 'qa' to 'staging'", async () => {
115
+ Object.defineProperty(window, "Capacitor", {
116
+ value: {
117
+ Plugins: {
118
+ DeviceInfo: {
119
+ getNativeShellAppEnvironment: vi
120
+ .fn()
121
+ .mockResolvedValue({
122
+ environment: "qa",
123
+ }),
124
+ },
125
+ },
126
+ },
127
+ writable: true,
128
+ configurable: true,
129
+ })
130
+
131
+ const result = await getEnvironment(
132
+ "FIRE_TV",
133
+ undefined,
134
+ mockLogger
135
+ )
136
+
137
+ expect(result.environment).toBe("staging")
138
+ expect(result.source).toBe("native")
139
+ })
140
+
141
+ it("should handle native env already in vwr format (dev)", async () => {
142
+ Object.defineProperty(window, "Capacitor", {
143
+ value: {
144
+ Plugins: {
145
+ DeviceInfo: {
146
+ getNativeShellAppEnvironment: vi
147
+ .fn()
148
+ .mockResolvedValue({
149
+ environment: "dev",
150
+ }),
151
+ },
152
+ },
153
+ },
154
+ writable: true,
155
+ configurable: true,
156
+ })
157
+
158
+ const result = await getEnvironment(
159
+ "FIRE_TV",
160
+ undefined,
161
+ mockLogger
162
+ )
163
+
164
+ expect(result.environment).toBe("dev")
165
+ expect(result.source).toBe("native")
166
+ })
167
+
168
+ it("should fallback to dev when native returns unknown value", async () => {
169
+ Object.defineProperty(window, "Capacitor", {
170
+ value: {
171
+ Plugins: {
172
+ DeviceInfo: {
173
+ getNativeShellAppEnvironment: vi
174
+ .fn()
175
+ .mockResolvedValue({
176
+ environment: "unknown-env",
177
+ }),
178
+ },
179
+ },
180
+ },
181
+ writable: true,
182
+ configurable: true,
183
+ })
184
+
185
+ const result = await getEnvironment(
186
+ "FIRE_TV",
187
+ undefined,
188
+ mockLogger
189
+ )
190
+
191
+ expect(result.environment).toBe("dev")
192
+ expect(result.source).toBe("native")
193
+ })
194
+
195
+ it("should fallback to build-time when native plugin unavailable", async () => {
196
+ // No Capacitor plugin available
197
+ const result = await getEnvironment(
198
+ "FIRE_TV",
199
+ "staging",
200
+ mockLogger
201
+ )
202
+
203
+ expect(result.environment).toBe("staging")
204
+ expect(result.source).toBe("build-time")
205
+ expect(mockLogger.warn).toHaveBeenCalledWith(
206
+ expect.stringContaining(
207
+ "Failed to read environment from native, falling back to build-time"
208
+ )
209
+ )
210
+ })
211
+
212
+ it("should fallback to build-time when native plugin throws error", async () => {
213
+ Object.defineProperty(window, "Capacitor", {
214
+ value: {
215
+ Plugins: {
216
+ DeviceInfo: {
217
+ getNativeShellAppEnvironment: vi
218
+ .fn()
219
+ .mockRejectedValue(
220
+ new Error("Native plugin error")
221
+ ),
222
+ },
223
+ },
224
+ },
225
+ writable: true,
226
+ configurable: true,
227
+ })
228
+
229
+ const result = await getEnvironment("FIRE_TV", "prod", mockLogger)
230
+
231
+ expect(result.environment).toBe("prod")
232
+ expect(result.source).toBe("build-time")
233
+ expect(mockLogger.error).toHaveBeenCalled()
234
+ expect(mockLogger.warn).toHaveBeenCalledWith(
235
+ expect.stringContaining("falling back to build-time")
236
+ )
237
+ })
238
+
239
+ it("should fallback to build-time when native returns empty", async () => {
240
+ Object.defineProperty(window, "Capacitor", {
241
+ value: {
242
+ Plugins: {
243
+ DeviceInfo: {
244
+ getNativeShellAppEnvironment: vi
245
+ .fn()
246
+ .mockResolvedValue({
247
+ environment: "",
248
+ }),
249
+ },
250
+ },
251
+ },
252
+ writable: true,
253
+ configurable: true,
254
+ })
255
+
256
+ const result = await getEnvironment("FIRE_TV", "dev", mockLogger)
257
+
258
+ expect(result.environment).toBe("dev")
259
+ expect(result.source).toBe("build-time")
260
+ expect(mockLogger.error).toHaveBeenCalledWith(
261
+ expect.stringContaining(
262
+ "DeviceInfo.getNativeShellAppEnvironment returned empty"
263
+ )
264
+ )
265
+ })
266
+
267
+ it("should fallback to default when both native and build-time fail", async () => {
268
+ // No Capacitor plugin, no build-time env
269
+ const result = await getEnvironment(
270
+ "FIRE_TV",
271
+ undefined,
272
+ mockLogger
273
+ )
274
+
275
+ expect(result.environment).toBe("dev")
276
+ expect(result.source).toBe("default")
277
+ expect(mockLogger.warn).toHaveBeenCalledWith(
278
+ expect.stringContaining(
279
+ 'Environment: "dev" (source: default fallback'
280
+ )
281
+ )
282
+ })
283
+
284
+ it("should be case-insensitive for Fire TV platform", async () => {
285
+ Object.defineProperty(window, "Capacitor", {
286
+ value: {
287
+ Plugins: {
288
+ DeviceInfo: {
289
+ getNativeShellAppEnvironment: vi
290
+ .fn()
291
+ .mockResolvedValue({
292
+ environment: "production",
293
+ }),
294
+ },
295
+ },
296
+ },
297
+ writable: true,
298
+ configurable: true,
299
+ })
300
+
301
+ const result1 = await getEnvironment(
302
+ "fire_tv",
303
+ undefined,
304
+ mockLogger
305
+ )
306
+ const result2 = await getEnvironment(
307
+ "FIRE_TV",
308
+ undefined,
309
+ mockLogger
310
+ )
311
+
312
+ expect(result1.environment).toBe("prod")
313
+ expect(result2.environment).toBe("prod")
314
+ })
315
+ })
316
+
317
+ describe("Non-Fire TV Platforms - Build-time Environment", () => {
318
+ const platforms = ["SAMSUNG_TV", "LG_TV", "MOBILE", "WEB"]
319
+
320
+ platforms.forEach((platform) => {
321
+ describe(`${platform}`, () => {
322
+ it("should use build-time environment when provided", async () => {
323
+ const result = await getEnvironment(
324
+ platform,
325
+ "staging",
326
+ mockLogger
327
+ )
328
+
329
+ expect(result.environment).toBe("staging")
330
+ expect(result.source).toBe("build-time")
331
+ expect(mockLogger.info).toHaveBeenCalledWith(
332
+ expect.stringContaining(
333
+ 'Environment: "staging" (source: build-time CLI injection)'
334
+ )
335
+ )
336
+ })
337
+
338
+ it("should use build-time 'prod' environment", async () => {
339
+ const result = await getEnvironment(
340
+ platform,
341
+ "prod",
342
+ mockLogger
343
+ )
344
+
345
+ expect(result.environment).toBe("prod")
346
+ expect(result.source).toBe("build-time")
347
+ })
348
+
349
+ it("should use build-time 'dev' environment", async () => {
350
+ const result = await getEnvironment(
351
+ platform,
352
+ "dev",
353
+ mockLogger
354
+ )
355
+
356
+ expect(result.environment).toBe("dev")
357
+ expect(result.source).toBe("build-time")
358
+ })
359
+
360
+ it("should use build-time 'local' environment", async () => {
361
+ const result = await getEnvironment(
362
+ platform,
363
+ "local",
364
+ mockLogger
365
+ )
366
+
367
+ expect(result.environment).toBe("local")
368
+ expect(result.source).toBe("build-time")
369
+ })
370
+
371
+ it("should fallback to default when build-time not provided", async () => {
372
+ const result = await getEnvironment(
373
+ platform,
374
+ undefined,
375
+ mockLogger
376
+ )
377
+
378
+ expect(result.environment).toBe("dev")
379
+ expect(result.source).toBe("default")
380
+ expect(mockLogger.warn).toHaveBeenCalledWith(
381
+ expect.stringContaining(
382
+ 'Environment: "dev" (source: default fallback'
383
+ )
384
+ )
385
+ })
386
+
387
+ it("should fallback to default when build-time is invalid", async () => {
388
+ const result = await getEnvironment(
389
+ platform,
390
+ "invalid-env",
391
+ mockLogger
392
+ )
393
+
394
+ expect(result.environment).toBe("dev")
395
+ expect(result.source).toBe("default")
396
+ })
397
+ })
398
+ })
399
+ })
400
+
401
+ describe("Environment Validation", () => {
402
+ it("should accept valid environments", async () => {
403
+ const validEnvs: Environment[] = ["local", "dev", "staging", "prod"]
404
+
405
+ for (const env of validEnvs) {
406
+ const result = await getEnvironment("WEB", env, mockLogger)
407
+ expect(result.environment).toBe(env)
408
+ expect(result.source).toBe("build-time")
409
+ }
410
+ })
411
+
412
+ it("should reject invalid environments", async () => {
413
+ const invalidEnvs = [
414
+ "development",
415
+ "production",
416
+ "qa",
417
+ "test",
418
+ "",
419
+ "random",
420
+ ]
421
+
422
+ for (const env of invalidEnvs) {
423
+ const result = await getEnvironment("WEB", env, mockLogger)
424
+ expect(result.environment).toBe("dev")
425
+ expect(result.source).toBe("default")
426
+ }
427
+ })
428
+ })
429
+
430
+ describe("Logging", () => {
431
+ it("should log native environment source", async () => {
432
+ Object.defineProperty(window, "Capacitor", {
433
+ value: {
434
+ Plugins: {
435
+ DeviceInfo: {
436
+ getNativeShellAppEnvironment: vi
437
+ .fn()
438
+ .mockResolvedValue({
439
+ environment: "production",
440
+ }),
441
+ },
442
+ },
443
+ },
444
+ writable: true,
445
+ configurable: true,
446
+ })
447
+
448
+ await getEnvironment("FIRE_TV", undefined, mockLogger)
449
+
450
+ expect(mockLogger.info).toHaveBeenCalledWith(
451
+ '[Shell] Environment: "prod" (source: native BuildConfig.ENVIRONMENT="production")'
452
+ )
453
+ })
454
+
455
+ it("should log build-time environment source", async () => {
456
+ await getEnvironment("SAMSUNG_TV", "staging", mockLogger)
457
+
458
+ expect(mockLogger.info).toHaveBeenCalledWith(
459
+ '[Shell] Environment: "staging" (source: build-time CLI injection)'
460
+ )
461
+ })
462
+
463
+ it("should warn when falling back to default", async () => {
464
+ await getEnvironment("LG_TV", undefined, mockLogger)
465
+
466
+ expect(mockLogger.warn).toHaveBeenCalledWith(
467
+ '[Shell] Environment: "dev" (source: default fallback - no native or build-time env found)'
468
+ )
469
+ })
470
+
471
+ it("should warn when native fails and falls back", async () => {
472
+ await getEnvironment("FIRE_TV", "prod", mockLogger)
473
+
474
+ expect(mockLogger.warn).toHaveBeenCalledWith(
475
+ "[Shell] Failed to read environment from native, falling back to build-time"
476
+ )
477
+ })
478
+ })
479
+ })
@@ -19,7 +19,7 @@ export interface EnvironmentResult {
19
19
  * This ensures production APKs cannot accidentally run dev VWR,
20
20
  * since the native Android build variant determines the environment.
21
21
  *
22
- * @param platform - Platform identifier ('FIRE_TV', 'SAMSUNG_TV', 'LG_TV', 'MOBILE', 'WEB')
22
+ * @param platform - Platform identifier ('FIRE_TV', 'SAMSUNG_TV', 'LG_TV', 'ANDROID_MOBILE', 'IOS_MOBILE', 'WEB')
23
23
  * @param buildTimeEnv - Optional build-time injected environment (from CLI)
24
24
  * @param logger - Optional logger for reporting
25
25
  * @returns Environment result with source information for logging
@@ -48,13 +48,16 @@ export async function getEnvironment(
48
48
  }
49
49
 
50
50
  // Build-time injection (CLI --env flag)
51
- if (buildTimeEnv && isValidEnvironment(buildTimeEnv)) {
52
- logger.info(
53
- `[Shell] Environment: "${buildTimeEnv}" (source: build-time CLI injection)`
54
- )
55
- return {
56
- environment: buildTimeEnv as Environment,
57
- source: "build-time",
51
+ if (buildTimeEnv) {
52
+ const normalizedBuildTimeEnv = buildTimeEnv.toLowerCase()
53
+ if (isValidEnvironment(normalizedBuildTimeEnv)) {
54
+ logger.info(
55
+ `[Shell] Environment: "${normalizedBuildTimeEnv}" (source: build-time CLI injection)`
56
+ )
57
+ return {
58
+ environment: normalizedBuildTimeEnv as Environment,
59
+ source: "build-time",
60
+ }
58
61
  }
59
62
  }
60
63
 
@@ -110,12 +113,17 @@ async function getFireTVEnvironment(logger: Logger): Promise<string | null> {
110
113
  * - "dev"
111
114
  * - "staging"
112
115
  * - "prod"
116
+ *
117
+ * Note: Legacy "qa" values are mapped to "staging".
113
118
  */
114
119
  function mapNativeEnvironment(nativeEnv: string): Environment {
115
120
  const normalized = nativeEnv.toLowerCase()
116
121
  switch (normalized) {
117
122
  case "development":
118
123
  return "dev"
124
+ case "qa":
125
+ // Legacy: map qa to staging
126
+ return "staging"
119
127
  case "staging":
120
128
  return "staging"
121
129
  case "production":
@@ -220,11 +220,130 @@ describe("getShellVersion", () => {
220
220
  })
221
221
  })
222
222
 
223
- describe("Mobile", () => {
224
- it("returns unknown (not yet implemented)", async () => {
225
- const version = await getShellVersion("MOBILE")
223
+ describe("Mobile (iOS)", () => {
224
+ afterEach(() => {
225
+ delete (window as any).iosAppContext
226
+ })
227
+
228
+ it("returns version from iosAppContext.appVersion", async () => {
229
+ ;(window as any).iosAppContext = {
230
+ appVersion: "2.1.0",
231
+ }
232
+
233
+ const version = await getShellVersion("IOS_MOBILE")
234
+
235
+ expect(version).toBe("2.1.0")
236
+ })
237
+
238
+ it("returns unknown when iosAppContext is not available", async () => {
239
+ const consoleWarnSpy = vi
240
+ .spyOn(console, "warn")
241
+ .mockImplementation(() => {})
242
+
243
+ const version = await getShellVersion("IOS_MOBILE")
244
+
245
+ expect(version).toBe("unknown")
246
+
247
+ consoleWarnSpy.mockRestore()
248
+ })
249
+
250
+ it("returns unknown when appVersion is empty", async () => {
251
+ const consoleWarnSpy = vi
252
+ .spyOn(console, "warn")
253
+ .mockImplementation(() => {})
254
+
255
+ ;(window as any).iosAppContext = {
256
+ appVersion: " ",
257
+ }
258
+
259
+ const version = await getShellVersion("IOS_MOBILE")
260
+
261
+ expect(version).toBe("unknown")
262
+
263
+ consoleWarnSpy.mockRestore()
264
+ })
265
+
266
+ it("is case insensitive", async () => {
267
+ ;(window as any).iosAppContext = {
268
+ appVersion: "2.2.0",
269
+ }
270
+
271
+ const version = await getShellVersion("ios_mobile")
272
+
273
+ expect(version).toBe("2.2.0")
274
+ })
275
+ })
276
+
277
+ describe("Mobile (Android)", () => {
278
+ afterEach(() => {
279
+ delete (window as any).androidAppContext
280
+ })
281
+
282
+ it("returns version from androidAppContext.appVersion", async () => {
283
+ ;(window as any).androidAppContext = {
284
+ appVersion: "3.1.0",
285
+ }
286
+
287
+ const version = await getShellVersion("ANDROID_MOBILE")
288
+
289
+ expect(version).toBe("3.1.0")
290
+ })
291
+
292
+ it("returns unknown when androidAppContext is not available", async () => {
293
+ const consoleWarnSpy = vi
294
+ .spyOn(console, "warn")
295
+ .mockImplementation(() => {})
296
+
297
+ const version = await getShellVersion("ANDROID_MOBILE")
298
+
299
+ expect(version).toBe("unknown")
300
+
301
+ consoleWarnSpy.mockRestore()
302
+ })
303
+
304
+ it("returns unknown when appVersion is empty", async () => {
305
+ const consoleWarnSpy = vi
306
+ .spyOn(console, "warn")
307
+ .mockImplementation(() => {})
308
+
309
+ ;(window as any).androidAppContext = {
310
+ appVersion: "",
311
+ }
312
+
313
+ const version = await getShellVersion("ANDROID_MOBILE")
226
314
 
227
315
  expect(version).toBe("unknown")
316
+
317
+ consoleWarnSpy.mockRestore()
318
+ })
319
+
320
+ it("is case insensitive", async () => {
321
+ ;(window as any).androidAppContext = {
322
+ appVersion: "3.2.0",
323
+ }
324
+
325
+ const version = await getShellVersion("android_mobile")
326
+
327
+ expect(version).toBe("3.2.0")
328
+ })
329
+
330
+ it("prefers iOS context when both are available", async () => {
331
+ ;(window as any).iosAppContext = {
332
+ appVersion: "1.0.0-ios",
333
+ }
334
+ ;(window as any).androidAppContext = {
335
+ appVersion: "1.0.0-android",
336
+ }
337
+
338
+ // When calling with IOS_MOBILE, should get iOS version
339
+ const iosVersion = await getShellVersion("IOS_MOBILE")
340
+ expect(iosVersion).toBe("1.0.0-ios")
341
+
342
+ // When calling with ANDROID_MOBILE, should still check iOS first (matching SDK behavior)
343
+ const androidVersion = await getShellVersion("ANDROID_MOBILE")
344
+ expect(androidVersion).toBe("1.0.0-ios")
345
+
346
+ delete (window as any).iosAppContext
228
347
  })
229
348
  })
230
349
 
@@ -268,7 +387,7 @@ describe("getShellVersion", () => {
268
387
  expect(version).toBe("unknown")
269
388
  expect(consoleWarnSpy).toHaveBeenCalledWith(
270
389
  "[Shell] Failed to get shell version:",
271
- expect.any(Error)
390
+ { error: expect.any(Error) }
272
391
  )
273
392
 
274
393
  consoleWarnSpy.mockRestore()