@terreno/rtk 0.11.0 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,326 @@
1
+ import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test";
2
+ import { act, renderHook, waitFor } from "@testing-library/react-native";
3
+ // ---------------------------------------------------------------------------
4
+ // Mutable refs that tests can tweak between runs
5
+ // ---------------------------------------------------------------------------
6
+ let mockBuildNumber = 42;
7
+ const mockUnwrap = mock(() => Promise.resolve({ status: "ok" }));
8
+ const mockTrigger = mock((..._args) => ({ unwrap: mockUnwrap }));
9
+ mock.module("./emptyApi", () => ({
10
+ useLazyGetVersionCheckQuery: () => [mockTrigger],
11
+ }));
12
+ // IsWeb is always true in tests (Platform.OS mocked as "web" in preload).
13
+ // We cannot change it per-test because bun snapshots module exports.
14
+ mock.module("./platform", () => ({
15
+ IsWeb: true,
16
+ }));
17
+ // AppState mock — capture listeners so tests can simulate transitions
18
+ let appStateListeners = [];
19
+ const mockRemove = mock(() => { });
20
+ const mockAddEventListener = mock((_event, handler) => {
21
+ appStateListeners.push(handler);
22
+ return { remove: mockRemove };
23
+ });
24
+ const mockOpenURL = mock(() => Promise.resolve(true));
25
+ mock.module("react-native", () => ({
26
+ AppState: {
27
+ addEventListener: mockAddEventListener,
28
+ currentState: "active",
29
+ },
30
+ Linking: { openURL: mockOpenURL },
31
+ Platform: { OS: "web" },
32
+ StyleSheet: { create: (s) => s },
33
+ }));
34
+ mock.module("expo-constants", () => ({
35
+ default: {
36
+ get expoConfig() {
37
+ return { extra: { buildNumber: mockBuildNumber } };
38
+ },
39
+ },
40
+ }));
41
+ // ---------------------------------------------------------------------------
42
+ // window.location.reload mock
43
+ // ---------------------------------------------------------------------------
44
+ const mockReload = mock(() => { });
45
+ // ---------------------------------------------------------------------------
46
+ // setInterval / clearInterval mocks (bun has no fake timers)
47
+ // ---------------------------------------------------------------------------
48
+ const originalSetInterval = globalThis.setInterval;
49
+ const originalClearInterval = globalThis.clearInterval;
50
+ let capturedIntervalMs;
51
+ let intervalIdCounter = 0;
52
+ const mockSetInterval = mock((_cb, ms) => {
53
+ capturedIntervalMs = ms;
54
+ return ++intervalIdCounter;
55
+ });
56
+ const mockClearInterval = mock((_id) => { });
57
+ // ---------------------------------------------------------------------------
58
+ // console capture
59
+ // ---------------------------------------------------------------------------
60
+ const debugCalls = [];
61
+ const originalDebug = console.debug;
62
+ const originalWarn = console.warn;
63
+ // Now import the hook (after all mock.module calls which are hoisted)
64
+ import { useUpgradeCheck } from "./useUpgradeCheck";
65
+ // Helper: flush microtasks (lets .then() chains resolve)
66
+ const flushPromises = () => new Promise((r) => setTimeout(r, 0));
67
+ // ---------------------------------------------------------------------------
68
+ // Setup / teardown
69
+ // ---------------------------------------------------------------------------
70
+ beforeEach(() => {
71
+ // Reset mutable refs
72
+ mockBuildNumber = 42;
73
+ appStateListeners = [];
74
+ // Clear call history and set default implementations
75
+ mockUnwrap.mockClear();
76
+ mockUnwrap.mockImplementation(() => Promise.resolve({ status: "ok" }));
77
+ mockTrigger.mockClear();
78
+ mockTrigger.mockImplementation((..._args) => ({ unwrap: mockUnwrap }));
79
+ mockRemove.mockClear();
80
+ mockAddEventListener.mockClear();
81
+ mockAddEventListener.mockImplementation((_event, handler) => {
82
+ appStateListeners.push(handler);
83
+ return { remove: mockRemove };
84
+ });
85
+ mockOpenURL.mockClear();
86
+ mockOpenURL.mockImplementation(() => Promise.resolve(true));
87
+ mockReload.mockClear();
88
+ // Mock window.location.reload
89
+ Object.defineProperty(globalThis, "window", {
90
+ configurable: true,
91
+ value: { location: { reload: mockReload } },
92
+ writable: true,
93
+ });
94
+ // Mock timers
95
+ globalThis.setInterval = mockSetInterval;
96
+ globalThis.clearInterval = mockClearInterval;
97
+ mockSetInterval.mockClear();
98
+ mockSetInterval.mockImplementation((_cb, ms) => {
99
+ capturedIntervalMs = ms;
100
+ return ++intervalIdCounter;
101
+ });
102
+ mockClearInterval.mockClear();
103
+ capturedIntervalMs = undefined;
104
+ // Console capture
105
+ debugCalls.length = 0;
106
+ console.debug = (...args) => {
107
+ debugCalls.push(args);
108
+ };
109
+ console.warn = () => { };
110
+ });
111
+ afterEach(() => {
112
+ console.debug = originalDebug;
113
+ console.warn = originalWarn;
114
+ globalThis.setInterval = originalSetInterval;
115
+ globalThis.clearInterval = originalClearInterval;
116
+ });
117
+ // ---------------------------------------------------------------------------
118
+ // Tests
119
+ // ---------------------------------------------------------------------------
120
+ describe("useUpgradeCheck", () => {
121
+ describe("mount behavior", () => {
122
+ it("returns default state before check resolves", () => {
123
+ // Make unwrap hang so state stays at defaults
124
+ mockUnwrap.mockImplementation(() => new Promise(() => { }));
125
+ const { result } = renderHook(() => useUpgradeCheck());
126
+ expect(result.current.isRequired).toBe(false);
127
+ expect(result.current.isWarning).toBe(false);
128
+ expect(result.current.requiredMessage).toBeUndefined();
129
+ expect(result.current.warningMessage).toBeUndefined();
130
+ expect(result.current.warningCheckCount).toBe(0);
131
+ });
132
+ it("skips check when buildNumber is undefined", () => {
133
+ mockBuildNumber = undefined;
134
+ renderHook(() => useUpgradeCheck());
135
+ expect(mockTrigger).not.toHaveBeenCalled();
136
+ });
137
+ it("triggers version check on mount with correct params", () => {
138
+ renderHook(() => useUpgradeCheck());
139
+ expect(mockTrigger).toHaveBeenCalledWith({ platform: "web", version: 42 });
140
+ });
141
+ });
142
+ describe("status handling", () => {
143
+ it("sets isRequired and requiredMessage when status is 'required'", async () => {
144
+ mockUnwrap.mockImplementation(() => Promise.resolve({ message: "Please update now", status: "required" }));
145
+ const { result } = renderHook(() => useUpgradeCheck());
146
+ await act(async () => {
147
+ await flushPromises();
148
+ });
149
+ await waitFor(() => {
150
+ expect(result.current.isRequired).toBe(true);
151
+ expect(result.current.requiredMessage).toBe("Please update now");
152
+ expect(result.current.isWarning).toBe(false);
153
+ });
154
+ });
155
+ it("sets isWarning, warningMessage, and increments warningCheckCount when status is 'warning'", async () => {
156
+ mockUnwrap.mockImplementation(() => Promise.resolve({ message: "New version available", status: "warning" }));
157
+ const { result } = renderHook(() => useUpgradeCheck());
158
+ await act(async () => {
159
+ await flushPromises();
160
+ });
161
+ await waitFor(() => {
162
+ expect(result.current.isWarning).toBe(true);
163
+ expect(result.current.warningMessage).toBe("New version available");
164
+ expect(result.current.warningCheckCount).toBe(1);
165
+ });
166
+ });
167
+ it("clears required/warning state when status is 'ok' after previous required", async () => {
168
+ // Start with "required"
169
+ mockUnwrap.mockImplementation(() => Promise.resolve({ message: "Update", status: "required" }));
170
+ const { result } = renderHook(() => useUpgradeCheck({ recheckOnForeground: true }));
171
+ await act(async () => {
172
+ await flushPromises();
173
+ });
174
+ await waitFor(() => {
175
+ expect(result.current.isRequired).toBe(true);
176
+ });
177
+ // Switch to "ok" and trigger via foreground recheck (more reliable than interval mock)
178
+ mockUnwrap.mockImplementation(() => Promise.resolve({ status: "ok" }));
179
+ await act(async () => {
180
+ // Simulate background → active transition to trigger runCheck
181
+ appStateListeners.forEach((listener) => {
182
+ listener("background");
183
+ });
184
+ });
185
+ await act(async () => {
186
+ appStateListeners.forEach((listener) => {
187
+ listener("active");
188
+ });
189
+ await flushPromises();
190
+ });
191
+ await waitFor(() => {
192
+ expect(result.current.isRequired).toBe(false);
193
+ expect(result.current.isWarning).toBe(false);
194
+ });
195
+ });
196
+ it("stores updateUrl from response", async () => {
197
+ mockUnwrap.mockImplementation(() => Promise.resolve({
198
+ message: "Update available",
199
+ status: "warning",
200
+ updateUrl: "https://example.com/update",
201
+ }));
202
+ const { result } = renderHook(() => useUpgradeCheck());
203
+ await act(async () => {
204
+ await flushPromises();
205
+ });
206
+ await waitFor(() => {
207
+ expect(result.current.isWarning).toBe(true);
208
+ });
209
+ });
210
+ });
211
+ describe("canUpdate", () => {
212
+ it("is true on web regardless of updateUrl", () => {
213
+ // IsWeb is always true in test env (Platform.OS: "web")
214
+ const { result } = renderHook(() => useUpgradeCheck());
215
+ expect(result.current.canUpdate).toBe(true);
216
+ });
217
+ });
218
+ describe("onUpdate", () => {
219
+ it("calls window.location.reload on web", () => {
220
+ const { result } = renderHook(() => useUpgradeCheck());
221
+ act(() => {
222
+ result.current.onUpdate();
223
+ });
224
+ expect(mockReload).toHaveBeenCalled();
225
+ });
226
+ });
227
+ describe("error handling", () => {
228
+ it("handles check failure gracefully", async () => {
229
+ mockUnwrap.mockImplementation(() => Promise.reject(new Error("Network error")));
230
+ const { result } = renderHook(() => useUpgradeCheck());
231
+ await act(async () => {
232
+ await flushPromises();
233
+ });
234
+ await waitFor(() => {
235
+ const failLog = debugCalls.find((args) => typeof args[0] === "string" && args[0].includes("Version check failed"));
236
+ expect(failLog).toBeDefined();
237
+ });
238
+ // State should remain at defaults
239
+ expect(result.current.isRequired).toBe(false);
240
+ expect(result.current.isWarning).toBe(false);
241
+ });
242
+ });
243
+ describe("polling", () => {
244
+ it("sets up interval when pollingIntervalMs is provided", () => {
245
+ renderHook(() => useUpgradeCheck({ pollingIntervalMs: 60000 }));
246
+ expect(mockSetInterval).toHaveBeenCalled();
247
+ expect(capturedIntervalMs).toBe(60000);
248
+ });
249
+ it("does not set up interval when pollingIntervalMs is omitted", () => {
250
+ renderHook(() => useUpgradeCheck());
251
+ expect(mockSetInterval).not.toHaveBeenCalled();
252
+ });
253
+ it("cleans up interval on unmount", () => {
254
+ const { unmount } = renderHook(() => useUpgradeCheck({ pollingIntervalMs: 60000 }));
255
+ unmount();
256
+ expect(mockClearInterval).toHaveBeenCalled();
257
+ });
258
+ it("increments warningCheckCount on each warning poll", async () => {
259
+ mockUnwrap.mockImplementation(() => Promise.resolve({ message: "Update", status: "warning" }));
260
+ const { result } = renderHook(() => useUpgradeCheck({ recheckOnForeground: true }));
261
+ await act(async () => {
262
+ await flushPromises();
263
+ });
264
+ await waitFor(() => {
265
+ expect(result.current.warningCheckCount).toBe(1);
266
+ });
267
+ // Simulate a foreground return to trigger another check
268
+ await act(async () => {
269
+ appStateListeners.forEach((listener) => {
270
+ listener("background");
271
+ });
272
+ });
273
+ await act(async () => {
274
+ appStateListeners.forEach((listener) => {
275
+ listener("active");
276
+ });
277
+ await flushPromises();
278
+ });
279
+ await waitFor(() => {
280
+ expect(result.current.warningCheckCount).toBe(2);
281
+ });
282
+ });
283
+ });
284
+ describe("foreground recheck", () => {
285
+ it("sets up AppState listener when recheckOnForeground is true", () => {
286
+ renderHook(() => useUpgradeCheck({ recheckOnForeground: true }));
287
+ expect(mockAddEventListener).toHaveBeenCalledWith("change", expect.any(Function));
288
+ });
289
+ it("does not set up AppState listener by default", () => {
290
+ renderHook(() => useUpgradeCheck());
291
+ expect(mockAddEventListener).not.toHaveBeenCalled();
292
+ });
293
+ it("triggers check on background to active transition", async () => {
294
+ renderHook(() => useUpgradeCheck({ recheckOnForeground: true }));
295
+ const initialCallCount = mockTrigger.mock.calls.length;
296
+ // Simulate going to background, then back to active
297
+ act(() => {
298
+ appStateListeners.forEach((listener) => {
299
+ listener("background");
300
+ });
301
+ });
302
+ act(() => {
303
+ appStateListeners.forEach((listener) => {
304
+ listener("active");
305
+ });
306
+ });
307
+ expect(mockTrigger.mock.calls.length).toBeGreaterThan(initialCallCount);
308
+ });
309
+ it("does not trigger extra check on active to active transition", () => {
310
+ renderHook(() => useUpgradeCheck({ recheckOnForeground: true }));
311
+ const initialCallCount = mockTrigger.mock.calls.length;
312
+ act(() => {
313
+ appStateListeners.forEach((listener) => {
314
+ listener("active");
315
+ });
316
+ });
317
+ expect(mockTrigger.mock.calls.length).toBe(initialCallCount);
318
+ });
319
+ it("cleans up AppState listener on unmount", () => {
320
+ const { unmount } = renderHook(() => useUpgradeCheck({ recheckOnForeground: true }));
321
+ unmount();
322
+ expect(mockRemove).toHaveBeenCalled();
323
+ });
324
+ });
325
+ });
326
+ //# sourceMappingURL=useUpgradeCheck.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useUpgradeCheck.test.js","sourceRoot":"","sources":["../src/useUpgradeCheck.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "@better-auth/expo": "^1.2.8",
4
4
  "@react-native-async-storage/async-storage": "2.2.0",
5
5
  "@reduxjs/toolkit": "^2.11.1",
6
- "@terreno/ui": "0.11.0",
6
+ "@terreno/ui": "0.11.2",
7
7
  "async-mutex": "^0.5.0",
8
8
  "axios": "^1.13.2",
9
9
  "axios-retry": "^4.5.0",
@@ -67,5 +67,5 @@
67
67
  "test:coverage": "bun run ../scripts/check-coverage.ts"
68
68
  },
69
69
  "types": "dist/index.d.ts",
70
- "version": "0.11.0"
70
+ "version": "0.11.2"
71
71
  }
@@ -1,4 +1,4 @@
1
- import {afterEach, beforeEach, describe, expect, it, mock} from "bun:test";
1
+ import {afterEach, beforeEach, describe, expect, it} from "bun:test";
2
2
 
3
3
  import {
4
4
  AUTH_DEBUG,
@@ -172,57 +172,5 @@ describe("logAuth / logSocket", () => {
172
172
  });
173
173
  });
174
174
 
175
- describe("expo tunnel warning", () => {
176
- it("warns when expoGoConfig.debuggerHost contains exp.direct", async () => {
177
- const errorCalls: unknown[][] = [];
178
- const originalError = console.error;
179
- console.error = (...args: unknown[]): void => {
180
- errorCalls.push(args);
181
- };
182
- mock.module("expo-constants", () => ({
183
- default: {
184
- expoConfig: {extra: {}},
185
- expoGoConfig: {debuggerHost: "abc.exp.direct"},
186
- },
187
- }));
188
- try {
189
- const testId = `${Date.now()}-${Math.random()}`;
190
- await import(`./constants?case=${testId}`);
191
- const warning = errorCalls.find((args) =>
192
- args.some((v) => typeof v === "string" && v.includes("Expo Tunnel is not currently"))
193
- );
194
- expect(warning).toBeDefined();
195
- } finally {
196
- console.error = originalError;
197
- mock.module("expo-constants", () => ({default: {expoConfig: {extra: {}}}}));
198
- }
199
- });
200
- });
201
-
202
- describe("AUTH_DEBUG enabled path", () => {
203
- it("logs debug messages from logAuth when AUTH_DEBUG is true on module load", async () => {
204
- const debugCalls: unknown[][] = [];
205
- const originalDebug = console.debug;
206
- console.debug = (...args: unknown[]): void => {
207
- debugCalls.push(args);
208
- };
209
- mock.module("expo-constants", () => ({
210
- default: {expoConfig: {extra: {AUTH_DEBUG: "true", WEBSOCKETS_DEBUG: "true"}}},
211
- }));
212
- try {
213
- const testId = `${Date.now()}-${Math.random()}`;
214
- const loaded = (await import(`./constants?case=${testId}`)) as typeof import("./constants");
215
- expect(loaded.AUTH_DEBUG).toBe(true);
216
- const preLength = debugCalls.length;
217
- loaded.logAuth("hello");
218
- expect(debugCalls.length).toBe(preLength + 1);
219
- loaded.logSocket(undefined, "ws on");
220
- expect(debugCalls.some((args) => args[0] === "[websocket]" && args[1] === "ws on")).toBe(
221
- true
222
- );
223
- } finally {
224
- console.debug = originalDebug;
225
- mock.module("expo-constants", () => ({default: {expoConfig: {extra: {}}}}));
226
- }
227
- });
228
- });
175
+ // Mock.module tests (expo tunnel warning, AUTH_DEBUG) moved to
176
+ // src/isolated/constants.isolated.ts to avoid coverage tracking interference.
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Isolated tests for constants.ts paths that require mock.module.
3
+ *
4
+ * These live in isolated/ so that mock.module calls do not interfere
5
+ * with coverage tracking in the main constants.test.ts file.
6
+ */
7
+ import {describe, expect, it, mock} from "bun:test";
8
+
9
+ describe("expo tunnel warning", () => {
10
+ it("warns when expoGoConfig.debuggerHost contains exp.direct", async () => {
11
+ const errorCalls: unknown[][] = [];
12
+ const originalError = console.error;
13
+ console.error = (...args: unknown[]): void => {
14
+ errorCalls.push(args);
15
+ };
16
+ mock.module("expo-constants", () => ({
17
+ default: {
18
+ expoConfig: {extra: {}},
19
+ expoGoConfig: {debuggerHost: "abc.exp.direct"},
20
+ },
21
+ }));
22
+ try {
23
+ const testId = `${Date.now()}-${Math.random()}`;
24
+ await import(`../constants?case=${testId}`);
25
+ const warning = errorCalls.find((args) =>
26
+ args.some((v) => typeof v === "string" && v.includes("Expo Tunnel is not currently"))
27
+ );
28
+ expect(warning).toBeDefined();
29
+ } finally {
30
+ console.error = originalError;
31
+ mock.module("expo-constants", () => ({default: {expoConfig: {extra: {}}}}));
32
+ }
33
+ });
34
+ });
35
+
36
+ describe("AUTH_DEBUG enabled path", () => {
37
+ it("logs debug messages from logAuth when AUTH_DEBUG is true on module load", async () => {
38
+ const debugCalls: unknown[][] = [];
39
+ const originalDebug = console.debug;
40
+ console.debug = (...args: unknown[]): void => {
41
+ debugCalls.push(args);
42
+ };
43
+ mock.module("expo-constants", () => ({
44
+ default: {expoConfig: {extra: {AUTH_DEBUG: "true", WEBSOCKETS_DEBUG: "true"}}},
45
+ }));
46
+ try {
47
+ const testId = `${Date.now()}-${Math.random()}`;
48
+ const loaded = (await import(`../constants?case=${testId}`)) as typeof import("../constants");
49
+ expect(loaded.AUTH_DEBUG).toBe(true);
50
+ const preLength = debugCalls.length;
51
+ loaded.logAuth("hello");
52
+ expect(debugCalls.length).toBe(preLength + 1);
53
+ loaded.logSocket(undefined, "ws on");
54
+ expect(debugCalls.some((args) => args[0] === "[websocket]" && args[1] === "ws on")).toBe(
55
+ true
56
+ );
57
+ } finally {
58
+ console.debug = originalDebug;
59
+ mock.module("expo-constants", () => ({default: {expoConfig: {extra: {}}}}));
60
+ }
61
+ });
62
+ });
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Isolated tests for useUpgradeCheck mobile-only paths.
3
+ *
4
+ * These run in a separate bun process because they need IsWeb=false,
5
+ * which cannot be changed per-test (bun snapshots module exports).
6
+ */
7
+ import {afterEach, beforeEach, describe, expect, it, mock} from "bun:test";
8
+ import {act, renderHook, waitFor} from "@testing-library/react-native";
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Mutable refs
12
+ // ---------------------------------------------------------------------------
13
+ let mockBuildNumber: number | undefined = 42;
14
+
15
+ interface MockVersionCheckResponse {
16
+ message?: string;
17
+ status: "ok" | "warning" | "required";
18
+ updateUrl?: string;
19
+ }
20
+ const mockUnwrap = mock(
21
+ (): Promise<MockVersionCheckResponse> =>
22
+ Promise.resolve({status: "warning", updateUrl: "https://example.com/update"})
23
+ );
24
+ const mockTrigger = mock((..._args: unknown[]) => ({unwrap: mockUnwrap}));
25
+
26
+ mock.module("../emptyApi", () => ({
27
+ useLazyGetVersionCheckQuery: () => [mockTrigger],
28
+ }));
29
+
30
+ // IsWeb = false so we exercise the mobile onUpdate paths
31
+ mock.module("../platform", () => ({
32
+ IsWeb: false,
33
+ }));
34
+
35
+ let appStateListeners: Array<(state: string) => void> = [];
36
+ const mockRemove = mock(() => {});
37
+ const mockAddEventListener = mock((_event: string, handler: (state: string) => void) => {
38
+ appStateListeners.push(handler);
39
+ return {remove: mockRemove};
40
+ });
41
+ const mockOpenURL = mock(() => Promise.resolve(true));
42
+
43
+ mock.module("react-native", () => ({
44
+ AppState: {
45
+ addEventListener: mockAddEventListener,
46
+ currentState: "active",
47
+ },
48
+ Linking: {openURL: mockOpenURL},
49
+ Platform: {OS: "ios"},
50
+ StyleSheet: {create: (s: unknown) => s},
51
+ }));
52
+
53
+ mock.module("expo-constants", () => ({
54
+ default: {
55
+ get expoConfig() {
56
+ return {extra: {buildNumber: mockBuildNumber}};
57
+ },
58
+ },
59
+ }));
60
+
61
+ mock.module("expo-secure-store", () => ({
62
+ deleteItemAsync: async () => {},
63
+ getItemAsync: async () => null,
64
+ setItemAsync: async () => {},
65
+ }));
66
+
67
+ mock.module("@react-native-async-storage/async-storage", () => ({
68
+ default: {
69
+ getItem: async () => null,
70
+ removeItem: async () => {},
71
+ setItem: async () => {},
72
+ },
73
+ }));
74
+
75
+ mock.module("expo-network", () => ({
76
+ getNetworkStateAsync: async () => ({isConnected: true}),
77
+ }));
78
+
79
+ // Import after all mock.module calls
80
+ import {useUpgradeCheck} from "../useUpgradeCheck";
81
+
82
+ const flushPromises = (): Promise<void> => new Promise((r) => setTimeout(r, 0));
83
+
84
+ // ---------------------------------------------------------------------------
85
+ // Setup / teardown
86
+ // ---------------------------------------------------------------------------
87
+ const warnCalls: unknown[][] = [];
88
+ const originalWarn = console.warn;
89
+ const originalDebug = console.debug;
90
+
91
+ beforeEach(() => {
92
+ mockBuildNumber = 42;
93
+ appStateListeners = [];
94
+
95
+ mockUnwrap.mockClear();
96
+ mockUnwrap.mockImplementation(() =>
97
+ Promise.resolve({status: "warning" as const, updateUrl: "https://example.com/update"})
98
+ );
99
+ mockTrigger.mockClear();
100
+ mockTrigger.mockImplementation((..._args: unknown[]) => ({unwrap: mockUnwrap}));
101
+ mockRemove.mockClear();
102
+ mockAddEventListener.mockClear();
103
+ mockAddEventListener.mockImplementation((_event: string, handler: (state: string) => void) => {
104
+ appStateListeners.push(handler);
105
+ return {remove: mockRemove};
106
+ });
107
+ mockOpenURL.mockClear();
108
+ mockOpenURL.mockImplementation(() => Promise.resolve(true));
109
+
110
+ warnCalls.length = 0;
111
+ console.warn = (...args: unknown[]): void => {
112
+ warnCalls.push(args);
113
+ };
114
+ console.debug = (): void => {};
115
+ });
116
+
117
+ afterEach(() => {
118
+ console.warn = originalWarn;
119
+ console.debug = originalDebug;
120
+ });
121
+
122
+ // ---------------------------------------------------------------------------
123
+ // Tests
124
+ // ---------------------------------------------------------------------------
125
+ describe("useUpgradeCheck (mobile)", () => {
126
+ it("canUpdate is true when updateUrl is set", async () => {
127
+ const {result} = renderHook(() => useUpgradeCheck());
128
+
129
+ await act(async () => {
130
+ await flushPromises();
131
+ });
132
+
133
+ await waitFor(() => {
134
+ expect(result.current.canUpdate).toBe(true);
135
+ });
136
+ });
137
+
138
+ it("canUpdate is false when no updateUrl", () => {
139
+ mockUnwrap.mockImplementation(() => new Promise(() => {}));
140
+ const {result} = renderHook(() => useUpgradeCheck());
141
+ expect(result.current.canUpdate).toBe(false);
142
+ });
143
+
144
+ it("onUpdate calls Linking.openURL with updateUrl", async () => {
145
+ const {result} = renderHook(() => useUpgradeCheck());
146
+
147
+ await act(async () => {
148
+ await flushPromises();
149
+ });
150
+
151
+ await waitFor(() => {
152
+ expect(result.current.canUpdate).toBe(true);
153
+ });
154
+
155
+ act(() => {
156
+ result.current.onUpdate();
157
+ });
158
+
159
+ expect(mockOpenURL).toHaveBeenCalledWith("https://example.com/update");
160
+ });
161
+
162
+ it("onUpdate logs warning when no updateUrl on mobile", () => {
163
+ mockUnwrap.mockImplementation(() => Promise.resolve({status: "ok" as const}));
164
+ const {result} = renderHook(() => useUpgradeCheck());
165
+
166
+ act(() => {
167
+ result.current.onUpdate();
168
+ });
169
+
170
+ const noUrlWarn = warnCalls.find(
171
+ (args) => typeof args[0] === "string" && args[0].includes("no update URL")
172
+ );
173
+ expect(noUrlWarn).toBeDefined();
174
+ });
175
+ });