@workos-inc/authkit-nextjs 2.13.0 → 2.15.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.
- package/README.md +10 -3
- package/dist/esm/auth.js +20 -4
- package/dist/esm/auth.js.map +1 -1
- package/dist/esm/types/auth.d.ts +4 -2
- package/dist/esm/types/session.d.ts +1 -1
- package/dist/esm/types/validate-api-key.d.ts +1 -1
- package/dist/esm/types/workos.d.ts +1 -1
- package/dist/esm/workos.js +1 -1
- package/package.json +20 -21
- package/src/actions.spec.ts +14 -12
- package/src/auth.spec.ts +45 -29
- package/src/auth.ts +22 -2
- package/src/authkit-callback-route.spec.ts +31 -29
- package/src/components/authkit-provider.spec.tsx +31 -31
- package/src/components/button.spec.tsx +4 -6
- package/src/components/impersonation.spec.tsx +25 -25
- package/src/components/min-max-button.spec.tsx +2 -2
- package/src/components/tokenStore.spec.ts +21 -21
- package/src/components/useAccessToken.spec.tsx +73 -77
- package/src/components/useTokenClaims.spec.tsx +22 -22
- package/src/cookie.spec.ts +7 -8
- package/src/get-authorization-url.spec.ts +12 -13
- package/src/session.spec.ts +74 -63
- package/src/utils.spec.ts +14 -31
- package/src/validate-api-key.spec.ts +4 -6
- package/src/workos.spec.ts +2 -2
- package/src/workos.ts +1 -1
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { tokenStore, TokenStore } from './tokenStore.js';
|
|
2
2
|
import { getAccessTokenAction, refreshAccessTokenAction } from '../actions.js';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
getAccessTokenAction:
|
|
6
|
-
refreshAccessTokenAction:
|
|
4
|
+
vi.mock('../actions.js', () => ({
|
|
5
|
+
getAccessTokenAction: vi.fn(),
|
|
6
|
+
refreshAccessTokenAction: vi.fn(),
|
|
7
7
|
}));
|
|
8
8
|
|
|
9
|
-
const mockGetAccessTokenAction = getAccessTokenAction as
|
|
10
|
-
const mockRefreshAccessTokenAction = refreshAccessTokenAction as
|
|
9
|
+
const mockGetAccessTokenAction = getAccessTokenAction as Mock;
|
|
10
|
+
const mockRefreshAccessTokenAction = refreshAccessTokenAction as Mock;
|
|
11
11
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
12
|
const _global = global as any;
|
|
13
13
|
|
|
14
14
|
describe('tokenStore', () => {
|
|
15
15
|
beforeEach(() => {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
vi.useFakeTimers();
|
|
17
|
+
vi.resetAllMocks();
|
|
18
18
|
tokenStore.reset();
|
|
19
19
|
|
|
20
20
|
// Clean up DOM globals
|
|
@@ -23,10 +23,10 @@ describe('tokenStore', () => {
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
afterEach(() => {
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
vi.clearAllTimers();
|
|
27
|
+
vi.useRealTimers();
|
|
28
28
|
tokenStore.reset();
|
|
29
|
-
|
|
29
|
+
vi.restoreAllMocks();
|
|
30
30
|
|
|
31
31
|
// Clean up DOM globals
|
|
32
32
|
delete _global.document;
|
|
@@ -213,7 +213,7 @@ describe('tokenStore', () => {
|
|
|
213
213
|
};
|
|
214
214
|
const validToken = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.${btoa(JSON.stringify(validPayload))}.mock-signature`;
|
|
215
215
|
|
|
216
|
-
const setTimeoutSpy =
|
|
216
|
+
const setTimeoutSpy = vi.spyOn(global, 'setTimeout');
|
|
217
217
|
mockGetAccessTokenAction.mockResolvedValue(validToken);
|
|
218
218
|
|
|
219
219
|
await tokenStore.getAccessTokenSilently();
|
|
@@ -227,7 +227,7 @@ describe('tokenStore', () => {
|
|
|
227
227
|
|
|
228
228
|
describe('subscriber management', () => {
|
|
229
229
|
it('should notify subscribers when state changes', () => {
|
|
230
|
-
const listener =
|
|
230
|
+
const listener = vi.fn();
|
|
231
231
|
const unsubscribe = tokenStore.subscribe(listener);
|
|
232
232
|
|
|
233
233
|
// Trigger a state change
|
|
@@ -256,14 +256,14 @@ describe('tokenStore', () => {
|
|
|
256
256
|
mockGetAccessTokenAction.mockResolvedValue(validToken);
|
|
257
257
|
|
|
258
258
|
// Subscribe to create a listener
|
|
259
|
-
const listener =
|
|
259
|
+
const listener = vi.fn();
|
|
260
260
|
const unsubscribe = tokenStore.subscribe(listener);
|
|
261
261
|
|
|
262
262
|
// Get token to schedule a refresh
|
|
263
263
|
await tokenStore.getAccessTokenSilently();
|
|
264
264
|
|
|
265
265
|
// Spy on clearTimeout
|
|
266
|
-
const clearTimeoutSpy =
|
|
266
|
+
const clearTimeoutSpy = vi.spyOn(global, 'clearTimeout');
|
|
267
267
|
|
|
268
268
|
// Unsubscribe the last (only) subscriber - should clear timeout
|
|
269
269
|
unsubscribe();
|
|
@@ -354,7 +354,7 @@ describe('tokenStore', () => {
|
|
|
354
354
|
|
|
355
355
|
it('should consume eager auth cookie on first getAccessToken call', async () => {
|
|
356
356
|
const eagerToken = 'eager-auth-token';
|
|
357
|
-
const mockCookieSetter =
|
|
357
|
+
const mockCookieSetter = vi.fn();
|
|
358
358
|
|
|
359
359
|
// Mock document.cookie with both getter and setter
|
|
360
360
|
let cookieValue = `workos-access-token=${eagerToken};`;
|
|
@@ -404,7 +404,7 @@ describe('tokenStore', () => {
|
|
|
404
404
|
iat: now - 40,
|
|
405
405
|
};
|
|
406
406
|
const fastToken = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.${btoa(JSON.stringify(fastPayload))}.mock-signature`;
|
|
407
|
-
const mockCookieSetter =
|
|
407
|
+
const mockCookieSetter = vi.fn();
|
|
408
408
|
|
|
409
409
|
let cookieValue = `workos-access-token=${fastToken};`;
|
|
410
410
|
|
|
@@ -433,7 +433,7 @@ describe('tokenStore', () => {
|
|
|
433
433
|
configurable: true,
|
|
434
434
|
});
|
|
435
435
|
|
|
436
|
-
const setTimeoutSpy =
|
|
436
|
+
const setTimeoutSpy = vi.spyOn(global, 'setTimeout');
|
|
437
437
|
|
|
438
438
|
// Call getAccessTokenSilently to trigger fast cookie consumption and refresh scheduling
|
|
439
439
|
const token = await tokenStore.getAccessTokenSilently();
|
|
@@ -480,7 +480,7 @@ describe('tokenStore', () => {
|
|
|
480
480
|
|
|
481
481
|
it('should handle HTTP protocol for cookie deletion', async () => {
|
|
482
482
|
const eagerToken = 'http-token';
|
|
483
|
-
const mockCookieSetter =
|
|
483
|
+
const mockCookieSetter = vi.fn();
|
|
484
484
|
|
|
485
485
|
let cookieValue = `workos-access-token=${eagerToken};`;
|
|
486
486
|
|
|
@@ -624,7 +624,7 @@ describe('tokenStore', () => {
|
|
|
624
624
|
await tokenStore.getAccessTokenSilently();
|
|
625
625
|
|
|
626
626
|
// Spy on clearTimeout
|
|
627
|
-
const clearTimeoutSpy =
|
|
627
|
+
const clearTimeoutSpy = vi.spyOn(global, 'clearTimeout');
|
|
628
628
|
|
|
629
629
|
// Clear token should clear the refresh timeout
|
|
630
630
|
tokenStore.clearToken();
|
|
@@ -738,7 +738,7 @@ describe('tokenStore', () => {
|
|
|
738
738
|
mockGetAccessTokenAction.mockClear();
|
|
739
739
|
mockRefreshAccessTokenAction.mockResolvedValue(existingToken); // Same token
|
|
740
740
|
|
|
741
|
-
const listener =
|
|
741
|
+
const listener = vi.fn();
|
|
742
742
|
tokenStore.subscribe(listener);
|
|
743
743
|
|
|
744
744
|
// Force a silent refresh that returns the same token
|
|
@@ -752,7 +752,7 @@ describe('tokenStore', () => {
|
|
|
752
752
|
|
|
753
753
|
describe('TokenStore constructor', () => {
|
|
754
754
|
const setupMockEnv = (cookieValue = '', protocol = 'https:') => {
|
|
755
|
-
const mockCookieSetter =
|
|
755
|
+
const mockCookieSetter = vi.fn();
|
|
756
756
|
|
|
757
757
|
Object.defineProperty(_global, 'document', {
|
|
758
758
|
value: { cookie: cookieValue },
|
|
@@ -6,41 +6,37 @@ import { useAuth } from './authkit-provider.js';
|
|
|
6
6
|
import { useAccessToken } from './useAccessToken.js';
|
|
7
7
|
import { tokenStore } from './tokenStore.js';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
getAccessTokenAction:
|
|
11
|
-
refreshAccessTokenAction:
|
|
9
|
+
vi.mock('../actions.js', () => ({
|
|
10
|
+
getAccessTokenAction: vi.fn(),
|
|
11
|
+
refreshAccessTokenAction: vi.fn(),
|
|
12
12
|
}));
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
const originalModule =
|
|
14
|
+
vi.mock('./authkit-provider.js', async () => {
|
|
15
|
+
const originalModule = await vi.importActual<typeof import('./authkit-provider.js')>('./authkit-provider.js');
|
|
16
16
|
return {
|
|
17
17
|
...originalModule,
|
|
18
|
-
useAuth:
|
|
18
|
+
useAuth: vi.fn(),
|
|
19
19
|
};
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
describe('useAccessToken', () => {
|
|
23
23
|
beforeEach(() => {
|
|
24
24
|
tokenStore.reset();
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
vi.resetAllMocks();
|
|
26
|
+
vi.useFakeTimers({ shouldAdvanceTime: true });
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
(getAccessTokenAction as jest.Mock).mockReset();
|
|
30
|
-
(refreshAccessTokenAction as jest.Mock).mockReset();
|
|
31
|
-
|
|
32
|
-
(useAuth as jest.Mock).mockImplementation(() => ({
|
|
28
|
+
(useAuth as Mock).mockImplementation(() => ({
|
|
33
29
|
user: { id: 'user_123' },
|
|
34
30
|
sessionId: 'session_123',
|
|
35
|
-
refreshAuth:
|
|
31
|
+
refreshAuth: vi.fn().mockResolvedValue({}),
|
|
36
32
|
}));
|
|
37
33
|
});
|
|
38
34
|
|
|
39
35
|
afterEach(() => {
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
vi.clearAllTimers();
|
|
37
|
+
vi.useRealTimers();
|
|
42
38
|
tokenStore.reset();
|
|
43
|
-
|
|
39
|
+
vi.clearAllMocks();
|
|
44
40
|
});
|
|
45
41
|
|
|
46
42
|
const TestComponent = () => {
|
|
@@ -60,7 +56,7 @@ describe('useAccessToken', () => {
|
|
|
60
56
|
it('should fetch an access token on mount and show loading state initially', async () => {
|
|
61
57
|
const mockToken =
|
|
62
58
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2lkIjoic2Vzc2lvbl8xMjMiLCJleHAiOjk5OTk5OTk5OTl9.mock-signature';
|
|
63
|
-
(getAccessTokenAction as
|
|
59
|
+
(getAccessTokenAction as Mock).mockResolvedValueOnce(mockToken);
|
|
64
60
|
|
|
65
61
|
const { getByTestId } = render(<TestComponent />);
|
|
66
62
|
|
|
@@ -92,8 +88,8 @@ describe('useAccessToken', () => {
|
|
|
92
88
|
const refreshedToken =
|
|
93
89
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2lkIjoic2Vzc2lvbl8xMjMiLCJleHAiOjk5OTk5OTk5OTl9.mock-signature';
|
|
94
90
|
|
|
95
|
-
(getAccessTokenAction as
|
|
96
|
-
(refreshAccessTokenAction as
|
|
91
|
+
(getAccessTokenAction as Mock).mockResolvedValueOnce(expiringToken);
|
|
92
|
+
(refreshAccessTokenAction as Mock).mockResolvedValueOnce(refreshedToken);
|
|
97
93
|
|
|
98
94
|
const { getByTestId } = render(<TestComponent />);
|
|
99
95
|
|
|
@@ -115,8 +111,8 @@ describe('useAccessToken', () => {
|
|
|
115
111
|
const refreshedToken =
|
|
116
112
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJyZWZyZXNoZWQiLCJzaWQiOiJzZXNzaW9uXzEyMyIsImV4cCI6OTk5OTk5OTk5OX0.mock-signature-2';
|
|
117
113
|
|
|
118
|
-
(getAccessTokenAction as
|
|
119
|
-
(refreshAccessTokenAction as
|
|
114
|
+
(getAccessTokenAction as Mock).mockResolvedValueOnce(initialToken);
|
|
115
|
+
(refreshAccessTokenAction as Mock).mockResolvedValueOnce(refreshedToken);
|
|
120
116
|
|
|
121
117
|
const { getByTestId } = render(<TestComponent />);
|
|
122
118
|
|
|
@@ -141,10 +137,10 @@ describe('useAccessToken', () => {
|
|
|
141
137
|
});
|
|
142
138
|
|
|
143
139
|
it('should handle the not loggged in state', async () => {
|
|
144
|
-
(useAuth as
|
|
140
|
+
(useAuth as Mock).mockImplementation(() => ({
|
|
145
141
|
user: undefined,
|
|
146
142
|
sessionId: undefined,
|
|
147
|
-
refreshAuth:
|
|
143
|
+
refreshAuth: vi.fn().mockResolvedValue({}),
|
|
148
144
|
}));
|
|
149
145
|
|
|
150
146
|
const { getByTestId } = render(<TestComponent />);
|
|
@@ -157,7 +153,7 @@ describe('useAccessToken', () => {
|
|
|
157
153
|
|
|
158
154
|
it('should handle errors during token fetch', async () => {
|
|
159
155
|
const error = new Error('Failed to fetch token');
|
|
160
|
-
(getAccessTokenAction as
|
|
156
|
+
(getAccessTokenAction as Mock).mockRejectedValueOnce(error);
|
|
161
157
|
|
|
162
158
|
const { getByTestId } = render(<TestComponent />);
|
|
163
159
|
|
|
@@ -176,8 +172,8 @@ describe('useAccessToken', () => {
|
|
|
176
172
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2lkIjoic2Vzc2lvbl8xMjMiLCJleHAiOjk5OTk5OTk5OTl9.mock-signature';
|
|
177
173
|
const error = new Error('Failed to refresh token');
|
|
178
174
|
|
|
179
|
-
(getAccessTokenAction as
|
|
180
|
-
(refreshAccessTokenAction as
|
|
175
|
+
(getAccessTokenAction as Mock).mockResolvedValueOnce(initialToken);
|
|
176
|
+
(refreshAccessTokenAction as Mock).mockRejectedValueOnce(error);
|
|
181
177
|
|
|
182
178
|
const { getByTestId } = render(<TestComponent />);
|
|
183
179
|
|
|
@@ -200,13 +196,13 @@ describe('useAccessToken', () => {
|
|
|
200
196
|
it('should reset token state when user is undefined', async () => {
|
|
201
197
|
const mockToken =
|
|
202
198
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2lkIjoic2Vzc2lvbl8xMjMiLCJleHAiOjk5OTk5OTk5OTl9.mock-signature';
|
|
203
|
-
(getAccessTokenAction as
|
|
199
|
+
(getAccessTokenAction as Mock).mockResolvedValueOnce(mockToken);
|
|
204
200
|
|
|
205
201
|
// First render with user
|
|
206
|
-
(useAuth as
|
|
202
|
+
(useAuth as Mock).mockImplementation(() => ({
|
|
207
203
|
user: { id: 'user_123' },
|
|
208
204
|
sessionId: 'session_123',
|
|
209
|
-
refreshAuth:
|
|
205
|
+
refreshAuth: vi.fn().mockResolvedValue({}),
|
|
210
206
|
}));
|
|
211
207
|
|
|
212
208
|
const { getByTestId, rerender } = render(<TestComponent />);
|
|
@@ -215,10 +211,10 @@ describe('useAccessToken', () => {
|
|
|
215
211
|
expect(getByTestId('token')).toHaveTextContent(mockToken);
|
|
216
212
|
});
|
|
217
213
|
|
|
218
|
-
(useAuth as
|
|
214
|
+
(useAuth as Mock).mockImplementation(() => ({
|
|
219
215
|
user: undefined,
|
|
220
216
|
sessionId: undefined,
|
|
221
|
-
refreshAuth:
|
|
217
|
+
refreshAuth: vi.fn().mockResolvedValue({}),
|
|
222
218
|
}));
|
|
223
219
|
|
|
224
220
|
rerender(<TestComponent />);
|
|
@@ -230,7 +226,7 @@ describe('useAccessToken', () => {
|
|
|
230
226
|
|
|
231
227
|
it('should handle invalid tokens gracefully', async () => {
|
|
232
228
|
const invalidToken = 'invalid-token';
|
|
233
|
-
(getAccessTokenAction as
|
|
229
|
+
(getAccessTokenAction as Mock).mockResolvedValueOnce(invalidToken);
|
|
234
230
|
|
|
235
231
|
const { getByTestId } = render(<TestComponent />);
|
|
236
232
|
|
|
@@ -246,7 +242,7 @@ describe('useAccessToken', () => {
|
|
|
246
242
|
const mockToken =
|
|
247
243
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2lkIjoic2Vzc2lvbl8xMjMiLCJleHAiOjk5OTk5OTk5OTl9.mock-signature';
|
|
248
244
|
|
|
249
|
-
(getAccessTokenAction as
|
|
245
|
+
(getAccessTokenAction as Mock).mockRejectedValueOnce(error).mockResolvedValueOnce(mockToken);
|
|
250
246
|
|
|
251
247
|
const { getByTestId } = render(<TestComponent />);
|
|
252
248
|
|
|
@@ -257,7 +253,7 @@ describe('useAccessToken', () => {
|
|
|
257
253
|
});
|
|
258
254
|
|
|
259
255
|
act(() => {
|
|
260
|
-
|
|
256
|
+
vi.advanceTimersByTime(5 * 60 * 1000); // RETRY_DELAY
|
|
261
257
|
});
|
|
262
258
|
|
|
263
259
|
// Loading should remain false during retry
|
|
@@ -283,8 +279,8 @@ describe('useAccessToken', () => {
|
|
|
283
279
|
const expiringToken = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.${btoa(JSON.stringify(payload))}.mock-signature`;
|
|
284
280
|
const error = new Error('Failed to refresh token');
|
|
285
281
|
|
|
286
|
-
(getAccessTokenAction as
|
|
287
|
-
(refreshAccessTokenAction as
|
|
282
|
+
(getAccessTokenAction as Mock).mockResolvedValueOnce(expiringToken);
|
|
283
|
+
(refreshAccessTokenAction as Mock).mockRejectedValueOnce(error);
|
|
288
284
|
|
|
289
285
|
const { getByTestId } = render(<TestComponent />);
|
|
290
286
|
|
|
@@ -301,7 +297,7 @@ describe('useAccessToken', () => {
|
|
|
301
297
|
|
|
302
298
|
it('should handle token with an invalid payload format', async () => {
|
|
303
299
|
const badPayloadToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.invalidpayload.mock-signature';
|
|
304
|
-
(getAccessTokenAction as
|
|
300
|
+
(getAccessTokenAction as Mock).mockResolvedValueOnce(badPayloadToken);
|
|
305
301
|
|
|
306
302
|
const { getByTestId } = render(<TestComponent />);
|
|
307
303
|
|
|
@@ -313,7 +309,7 @@ describe('useAccessToken', () => {
|
|
|
313
309
|
});
|
|
314
310
|
|
|
315
311
|
it('should immediately try to update token when token is undefined', async () => {
|
|
316
|
-
(getAccessTokenAction as
|
|
312
|
+
(getAccessTokenAction as Mock).mockResolvedValueOnce(undefined).mockResolvedValueOnce(undefined);
|
|
317
313
|
|
|
318
314
|
const { getByTestId } = render(<TestComponent />);
|
|
319
315
|
|
|
@@ -327,17 +323,17 @@ describe('useAccessToken', () => {
|
|
|
327
323
|
|
|
328
324
|
it('should react to sessionId changes', async () => {
|
|
329
325
|
// Clear any previous mocks to ensure clean state
|
|
330
|
-
|
|
326
|
+
vi.clearAllMocks();
|
|
331
327
|
|
|
332
328
|
const token1 = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMSIsImV4cCI6OTk5OTk5OTk5OX0.mock-signature-1';
|
|
333
329
|
const token2 = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMSIsImV4cCI6OTk5OTk5OTk5OX0.mock-signature-2';
|
|
334
330
|
|
|
335
|
-
(getAccessTokenAction as
|
|
331
|
+
(getAccessTokenAction as Mock).mockResolvedValueOnce(token1).mockResolvedValueOnce(token2);
|
|
336
332
|
|
|
337
|
-
(useAuth as
|
|
333
|
+
(useAuth as Mock).mockImplementation(() => ({
|
|
338
334
|
user: { id: 'user1' },
|
|
339
335
|
sessionId: 'session1',
|
|
340
|
-
refreshAuth:
|
|
336
|
+
refreshAuth: vi.fn().mockResolvedValue({}),
|
|
341
337
|
}));
|
|
342
338
|
|
|
343
339
|
const { rerender } = render(<TestComponent />);
|
|
@@ -346,10 +342,10 @@ describe('useAccessToken', () => {
|
|
|
346
342
|
expect(getAccessTokenAction).toHaveBeenCalledTimes(1);
|
|
347
343
|
});
|
|
348
344
|
|
|
349
|
-
(useAuth as
|
|
345
|
+
(useAuth as Mock).mockImplementation(() => ({
|
|
350
346
|
user: { id: 'user1' }, // Same user ID
|
|
351
347
|
sessionId: 'session2',
|
|
352
|
-
refreshAuth:
|
|
348
|
+
refreshAuth: vi.fn().mockResolvedValue({}),
|
|
353
349
|
}));
|
|
354
350
|
|
|
355
351
|
rerender(<TestComponent />);
|
|
@@ -360,8 +356,8 @@ describe('useAccessToken', () => {
|
|
|
360
356
|
});
|
|
361
357
|
|
|
362
358
|
it('should prevent concurrent token fetches via updateToken', async () => {
|
|
363
|
-
|
|
364
|
-
(getAccessTokenAction as
|
|
359
|
+
vi.clearAllMocks();
|
|
360
|
+
(getAccessTokenAction as Mock).mockReset();
|
|
365
361
|
|
|
366
362
|
const mockToken =
|
|
367
363
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2lkIjoic2Vzc2lvbl8xMjMiLCJleHAiOjk5OTk5OTk5OTl9.mock-signature';
|
|
@@ -374,7 +370,7 @@ describe('useAccessToken', () => {
|
|
|
374
370
|
}, 0);
|
|
375
371
|
});
|
|
376
372
|
|
|
377
|
-
(getAccessTokenAction as
|
|
373
|
+
(getAccessTokenAction as Mock).mockImplementation(() => {
|
|
378
374
|
fetchCalls++;
|
|
379
375
|
return tokenPromise;
|
|
380
376
|
});
|
|
@@ -397,7 +393,7 @@ describe('useAccessToken', () => {
|
|
|
397
393
|
});
|
|
398
394
|
|
|
399
395
|
it('should prevent concurrent manual refresh operations', async () => {
|
|
400
|
-
|
|
396
|
+
vi.clearAllMocks();
|
|
401
397
|
|
|
402
398
|
let refreshCalls = 0;
|
|
403
399
|
|
|
@@ -412,12 +408,12 @@ describe('useAccessToken', () => {
|
|
|
412
408
|
setTimeout(() => resolve(refreshedToken), 10);
|
|
413
409
|
});
|
|
414
410
|
|
|
415
|
-
(refreshAccessTokenAction as
|
|
411
|
+
(refreshAccessTokenAction as Mock).mockImplementation(() => {
|
|
416
412
|
refreshCalls++;
|
|
417
413
|
return refreshPromise;
|
|
418
414
|
});
|
|
419
415
|
|
|
420
|
-
(getAccessTokenAction as
|
|
416
|
+
(getAccessTokenAction as Mock).mockImplementation(() => {
|
|
421
417
|
return Promise.resolve(mockToken);
|
|
422
418
|
});
|
|
423
419
|
|
|
@@ -446,7 +442,7 @@ describe('useAccessToken', () => {
|
|
|
446
442
|
|
|
447
443
|
it('should handle non-Error objects thrown during token fetch', async () => {
|
|
448
444
|
// Simulate a string error being thrown
|
|
449
|
-
(getAccessTokenAction as
|
|
445
|
+
(getAccessTokenAction as Mock).mockImplementation(() => {
|
|
450
446
|
throw 'String error message';
|
|
451
447
|
});
|
|
452
448
|
|
|
@@ -461,13 +457,13 @@ describe('useAccessToken', () => {
|
|
|
461
457
|
|
|
462
458
|
it('should show loading state immediately on first render when user exists but no token', () => {
|
|
463
459
|
// Mock user with no token initially
|
|
464
|
-
(useAuth as
|
|
460
|
+
(useAuth as Mock).mockImplementation(() => ({
|
|
465
461
|
user: { id: 'user_123' },
|
|
466
462
|
sessionId: 'session_123',
|
|
467
|
-
refreshAuth:
|
|
463
|
+
refreshAuth: vi.fn().mockResolvedValue({}),
|
|
468
464
|
}));
|
|
469
465
|
|
|
470
|
-
(getAccessTokenAction as
|
|
466
|
+
(getAccessTokenAction as Mock).mockImplementation(
|
|
471
467
|
() => new Promise((resolve) => setTimeout(() => resolve('token'), 100)),
|
|
472
468
|
);
|
|
473
469
|
|
|
@@ -482,12 +478,12 @@ describe('useAccessToken', () => {
|
|
|
482
478
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJleGlzdGluZyIsInNpZCI6InNlc3Npb24xMjMiLCJleHAiOjk5OTk5OTk5OTl9.existing';
|
|
483
479
|
|
|
484
480
|
await act(async () => {
|
|
485
|
-
(getAccessTokenAction as
|
|
481
|
+
(getAccessTokenAction as Mock).mockResolvedValueOnce(existingToken);
|
|
486
482
|
await tokenStore.getAccessTokenSilently();
|
|
487
483
|
});
|
|
488
484
|
|
|
489
485
|
// Reset the mock to track new calls
|
|
490
|
-
(getAccessTokenAction as
|
|
486
|
+
(getAccessTokenAction as Mock).mockClear();
|
|
491
487
|
|
|
492
488
|
const { getByTestId } = render(<TestComponent />);
|
|
493
489
|
|
|
@@ -511,8 +507,8 @@ describe('useAccessToken', () => {
|
|
|
511
507
|
resolveRefreshPromise = resolve;
|
|
512
508
|
});
|
|
513
509
|
|
|
514
|
-
(refreshAccessTokenAction as
|
|
515
|
-
(getAccessTokenAction as
|
|
510
|
+
(refreshAccessTokenAction as Mock).mockReturnValue(refreshPromise);
|
|
511
|
+
(getAccessTokenAction as Mock).mockResolvedValue(initialToken);
|
|
516
512
|
|
|
517
513
|
const { getByTestId } = render(<TestComponent />);
|
|
518
514
|
|
|
@@ -541,7 +537,7 @@ describe('useAccessToken', () => {
|
|
|
541
537
|
it('should clear refresh timeout on unmount', async () => {
|
|
542
538
|
const mockToken =
|
|
543
539
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2lkIjoic2Vzc2lvbl8xMjMiLCJleHAiOjk5OTk5OTk5OTl9.mock-signature';
|
|
544
|
-
(getAccessTokenAction as
|
|
540
|
+
(getAccessTokenAction as Mock).mockResolvedValueOnce(mockToken);
|
|
545
541
|
|
|
546
542
|
const { getByTestId, unmount } = render(<TestComponent />);
|
|
547
543
|
|
|
@@ -555,7 +551,7 @@ describe('useAccessToken', () => {
|
|
|
555
551
|
it('should handle edge cases when token data is null', async () => {
|
|
556
552
|
// Create a token that resembles a JWT but with a null payload
|
|
557
553
|
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.bnVsbA==.mock-signature'; // "null" in base64
|
|
558
|
-
(getAccessTokenAction as
|
|
554
|
+
(getAccessTokenAction as Mock).mockResolvedValueOnce(token);
|
|
559
555
|
|
|
560
556
|
const { getByTestId } = render(<TestComponent />);
|
|
561
557
|
|
|
@@ -570,7 +566,7 @@ describe('useAccessToken', () => {
|
|
|
570
566
|
it('should handle errors with string messages instead of Error objects', async () => {
|
|
571
567
|
const error = 'String error message';
|
|
572
568
|
const errorObj = new Error(error);
|
|
573
|
-
(getAccessTokenAction as
|
|
569
|
+
(getAccessTokenAction as Mock).mockRejectedValueOnce(errorObj);
|
|
574
570
|
|
|
575
571
|
const { getByTestId } = render(<TestComponent />);
|
|
576
572
|
|
|
@@ -585,9 +581,9 @@ describe('useAccessToken', () => {
|
|
|
585
581
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2lkIjoic2Vzc2lvbl8xMjMiLCJleHAiOjk5OTk5OTk5OTl9.mock-signature';
|
|
586
582
|
const stringError = 'String error directly'; // Not wrapped in Error object
|
|
587
583
|
|
|
588
|
-
(getAccessTokenAction as
|
|
584
|
+
(getAccessTokenAction as Mock).mockResolvedValueOnce(initialToken);
|
|
589
585
|
// Mock refreshAccessTokenAction to reject with a string, not an Error object
|
|
590
|
-
(refreshAccessTokenAction as
|
|
586
|
+
(refreshAccessTokenAction as Mock).mockImplementation(() => {
|
|
591
587
|
return Promise.reject(stringError); // Directly reject with string
|
|
592
588
|
});
|
|
593
589
|
|
|
@@ -613,12 +609,12 @@ describe('useAccessToken', () => {
|
|
|
613
609
|
const token =
|
|
614
610
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2lkIjoic2Vzc2lvbl8xMjMiLCJleHAiOjk5OTk5OTk5OTl9.mock-signature';
|
|
615
611
|
|
|
616
|
-
(getAccessTokenAction as
|
|
612
|
+
(getAccessTokenAction as Mock).mockResolvedValue(token);
|
|
617
613
|
|
|
618
|
-
(useAuth as
|
|
614
|
+
(useAuth as Mock).mockImplementation(() => ({
|
|
619
615
|
user: { id: 'user_123' },
|
|
620
616
|
sessionId: 'session_123',
|
|
621
|
-
refreshAuth:
|
|
617
|
+
refreshAuth: vi.fn().mockResolvedValue({}),
|
|
622
618
|
}));
|
|
623
619
|
|
|
624
620
|
const { getByTestId, rerender } = render(<TestComponent />);
|
|
@@ -628,10 +624,10 @@ describe('useAccessToken', () => {
|
|
|
628
624
|
expect(getAccessTokenAction).toHaveBeenCalledTimes(1);
|
|
629
625
|
});
|
|
630
626
|
|
|
631
|
-
(useAuth as
|
|
627
|
+
(useAuth as Mock).mockImplementation(() => ({
|
|
632
628
|
user: { id: 'user_456' }, // Different user
|
|
633
629
|
sessionId: 'session_123', // Same session
|
|
634
|
-
refreshAuth:
|
|
630
|
+
refreshAuth: vi.fn().mockResolvedValue({}),
|
|
635
631
|
}));
|
|
636
632
|
|
|
637
633
|
rerender(<TestComponent />);
|
|
@@ -642,10 +638,10 @@ describe('useAccessToken', () => {
|
|
|
642
638
|
});
|
|
643
639
|
|
|
644
640
|
it('should handle getAccessToken when user is not authenticated', async () => {
|
|
645
|
-
(useAuth as
|
|
641
|
+
(useAuth as Mock).mockImplementation(() => ({
|
|
646
642
|
user: null,
|
|
647
643
|
sessionId: undefined,
|
|
648
|
-
refreshAuth:
|
|
644
|
+
refreshAuth: vi.fn().mockResolvedValue({}),
|
|
649
645
|
}));
|
|
650
646
|
|
|
651
647
|
const TestComponentWithGetAccessToken = () => {
|
|
@@ -670,11 +666,11 @@ describe('useAccessToken', () => {
|
|
|
670
666
|
const mockToken =
|
|
671
667
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2lkIjoic2Vzc2lvbl8xMjMiLCJleHAiOjk5OTk5OTk5OTl9.mock-signature';
|
|
672
668
|
|
|
673
|
-
(getAccessTokenAction as
|
|
674
|
-
(useAuth as
|
|
669
|
+
(getAccessTokenAction as Mock).mockResolvedValue(mockToken);
|
|
670
|
+
(useAuth as Mock).mockImplementation(() => ({
|
|
675
671
|
user: { id: 'user_123' },
|
|
676
672
|
sessionId: 'session_123',
|
|
677
|
-
refreshAuth:
|
|
673
|
+
refreshAuth: vi.fn().mockResolvedValue({}),
|
|
678
674
|
}));
|
|
679
675
|
|
|
680
676
|
const TestComponentWithGetAccessToken = () => {
|
|
@@ -696,7 +692,7 @@ describe('useAccessToken', () => {
|
|
|
696
692
|
|
|
697
693
|
// Advance timers to trigger getAccessToken call
|
|
698
694
|
act(() => {
|
|
699
|
-
|
|
695
|
+
vi.advanceTimersByTime(100);
|
|
700
696
|
});
|
|
701
697
|
|
|
702
698
|
await waitFor(() => {
|
|
@@ -705,10 +701,10 @@ describe('useAccessToken', () => {
|
|
|
705
701
|
});
|
|
706
702
|
|
|
707
703
|
it('should handle manual refresh when user is not authenticated', async () => {
|
|
708
|
-
(useAuth as
|
|
704
|
+
(useAuth as Mock).mockImplementation(() => ({
|
|
709
705
|
user: null,
|
|
710
706
|
sessionId: undefined,
|
|
711
|
-
refreshAuth:
|
|
707
|
+
refreshAuth: vi.fn().mockResolvedValue({}),
|
|
712
708
|
}));
|
|
713
709
|
|
|
714
710
|
const TestComponentWithRefresh = () => {
|