@workos-inc/authkit-nextjs 2.13.0 → 2.14.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/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 +27 -29
- 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
|
@@ -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 = () => {
|
|
@@ -3,25 +3,25 @@ import { render, waitFor } from '@testing-library/react';
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { useAuth } from './authkit-provider.js';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
getAccessTokenAction:
|
|
8
|
-
refreshAccessTokenAction:
|
|
6
|
+
vi.mock('../actions.js', () => ({
|
|
7
|
+
getAccessTokenAction: vi.fn(),
|
|
8
|
+
refreshAccessTokenAction: vi.fn(),
|
|
9
9
|
}));
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
const originalModule =
|
|
11
|
+
vi.mock('./authkit-provider.js', async () => {
|
|
12
|
+
const originalModule = await vi.importActual<typeof import('./authkit-provider.js')>('./authkit-provider.js');
|
|
13
13
|
return {
|
|
14
14
|
...originalModule,
|
|
15
|
-
useAuth:
|
|
15
|
+
useAuth: vi.fn(),
|
|
16
16
|
};
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
useAccessToken:
|
|
19
|
+
vi.mock('./useAccessToken.js', () => ({
|
|
20
|
+
useAccessToken: vi.fn(() => ({ accessToken: undefined })),
|
|
21
21
|
}));
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
decodeJwt:
|
|
23
|
+
vi.mock('jose', () => ({
|
|
24
|
+
decodeJwt: vi.fn((token: string) => {
|
|
25
25
|
if (token === 'malformed-token' || token === 'throw-error-token') {
|
|
26
26
|
throw new Error('Invalid JWT');
|
|
27
27
|
}
|
|
@@ -42,21 +42,21 @@ import { useTokenClaims } from './useTokenClaims.js';
|
|
|
42
42
|
|
|
43
43
|
describe('useTokenClaims', () => {
|
|
44
44
|
beforeEach(() => {
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
vi.clearAllMocks();
|
|
46
|
+
vi.useFakeTimers({ shouldAdvanceTime: true });
|
|
47
47
|
|
|
48
|
-
(useAuth as
|
|
48
|
+
(useAuth as Mock).mockImplementation(() => ({
|
|
49
49
|
user: { id: 'user_123' },
|
|
50
50
|
sessionId: 'session_123',
|
|
51
|
-
refreshAuth:
|
|
51
|
+
refreshAuth: vi.fn().mockResolvedValue({}),
|
|
52
52
|
}));
|
|
53
53
|
|
|
54
54
|
// Reset useAccessToken mock to default
|
|
55
|
-
(useAccessToken as
|
|
55
|
+
(useAccessToken as Mock).mockReturnValue({ accessToken: undefined });
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
afterEach(() => {
|
|
59
|
-
|
|
59
|
+
vi.useRealTimers();
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
const TokenClaimsTestComponent = () => {
|
|
@@ -69,7 +69,7 @@ describe('useTokenClaims', () => {
|
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
it('should return empty object when no access token is available', async () => {
|
|
72
|
-
(useAccessToken as
|
|
72
|
+
(useAccessToken as Mock).mockReturnValue({ accessToken: undefined });
|
|
73
73
|
|
|
74
74
|
const { getByTestId } = render(<TokenClaimsTestComponent />);
|
|
75
75
|
|
|
@@ -101,7 +101,7 @@ describe('useTokenClaims', () => {
|
|
|
101
101
|
};
|
|
102
102
|
const token = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.${btoa(JSON.stringify(payload))}.mock-signature`;
|
|
103
103
|
|
|
104
|
-
(useAccessToken as
|
|
104
|
+
(useAccessToken as Mock).mockReturnValue({ accessToken: token });
|
|
105
105
|
|
|
106
106
|
const { getByTestId } = render(<TokenClaimsTestComponent />);
|
|
107
107
|
|
|
@@ -129,7 +129,7 @@ describe('useTokenClaims', () => {
|
|
|
129
129
|
};
|
|
130
130
|
const token = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.${btoa(JSON.stringify(payload))}.mock-signature`;
|
|
131
131
|
|
|
132
|
-
(useAccessToken as
|
|
132
|
+
(useAccessToken as Mock).mockReturnValue({ accessToken: token });
|
|
133
133
|
|
|
134
134
|
const { getByTestId } = render(<TokenClaimsTestComponent />);
|
|
135
135
|
|
|
@@ -147,7 +147,7 @@ describe('useTokenClaims', () => {
|
|
|
147
147
|
};
|
|
148
148
|
const token = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.${btoa(JSON.stringify(payload))}.mock-signature`;
|
|
149
149
|
|
|
150
|
-
(useAccessToken as
|
|
150
|
+
(useAccessToken as Mock).mockReturnValue({ accessToken: token });
|
|
151
151
|
|
|
152
152
|
const { getByTestId } = render(<TokenClaimsTestComponent />);
|
|
153
153
|
|
|
@@ -175,7 +175,7 @@ describe('useTokenClaims', () => {
|
|
|
175
175
|
};
|
|
176
176
|
const token = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.${btoa(JSON.stringify(payload))}.mock-signature`;
|
|
177
177
|
|
|
178
|
-
(useAccessToken as
|
|
178
|
+
(useAccessToken as Mock).mockReturnValue({ accessToken: token });
|
|
179
179
|
|
|
180
180
|
const { getByTestId } = render(<TokenClaimsTestComponent />);
|
|
181
181
|
|
|
@@ -185,7 +185,7 @@ describe('useTokenClaims', () => {
|
|
|
185
185
|
});
|
|
186
186
|
|
|
187
187
|
it('should return empty object when decodeJwt throws an error', async () => {
|
|
188
|
-
(useAccessToken as
|
|
188
|
+
(useAccessToken as Mock).mockReturnValue({ accessToken: 'malformed-token' });
|
|
189
189
|
|
|
190
190
|
const { getByTestId } = render(<TokenClaimsTestComponent />);
|
|
191
191
|
|
package/src/cookie.spec.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import { describe, it, expect } from '@jest/globals';
|
|
2
|
-
|
|
3
|
-
// Mock at the top of the file
|
|
4
|
-
jest.mock('./env-variables');
|
|
5
|
-
|
|
6
1
|
describe('cookie.ts', () => {
|
|
7
2
|
beforeEach(() => {
|
|
8
3
|
// Clear all mocks before each test
|
|
9
|
-
|
|
10
|
-
// Reset modules
|
|
11
|
-
|
|
4
|
+
vi.clearAllMocks();
|
|
5
|
+
// Reset modules to ensure fresh imports
|
|
6
|
+
vi.resetModules();
|
|
7
|
+
// Re-mock env-variables with a fresh copy each time
|
|
8
|
+
vi.doMock('./env-variables', async (importOriginal) => {
|
|
9
|
+
return { ...(await importOriginal<typeof import('./env-variables')>()) };
|
|
10
|
+
});
|
|
12
11
|
});
|
|
13
12
|
|
|
14
13
|
describe('getCookieOptions', () => {
|
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, jest } from '@jest/globals';
|
|
2
1
|
import { getAuthorizationUrl } from './get-authorization-url.js';
|
|
3
2
|
import { headers } from 'next/headers';
|
|
4
3
|
import { getWorkOS } from './workos.js';
|
|
5
4
|
|
|
6
|
-
jest.mock('next/headers');
|
|
7
|
-
|
|
8
5
|
// Mock dependencies
|
|
9
|
-
const fakeWorkosInstance = {
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
const { fakeWorkosInstance } = vi.hoisted(() => ({
|
|
7
|
+
fakeWorkosInstance: {
|
|
8
|
+
userManagement: {
|
|
9
|
+
getAuthorizationUrl: vi.fn(),
|
|
10
|
+
},
|
|
12
11
|
},
|
|
13
|
-
};
|
|
12
|
+
}));
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
getWorkOS:
|
|
14
|
+
vi.mock('./workos', () => ({
|
|
15
|
+
getWorkOS: vi.fn(() => fakeWorkosInstance),
|
|
17
16
|
}));
|
|
18
17
|
|
|
19
18
|
describe('getAuthorizationUrl', () => {
|
|
20
19
|
const workos = getWorkOS();
|
|
21
20
|
beforeEach(() => {
|
|
22
|
-
|
|
21
|
+
vi.clearAllMocks();
|
|
23
22
|
});
|
|
24
23
|
|
|
25
24
|
it('uses x-redirect-uri header when redirectUri option is not provided', async () => {
|
|
@@ -27,7 +26,7 @@ describe('getAuthorizationUrl', () => {
|
|
|
27
26
|
nextHeaders.set('x-redirect-uri', 'http://test-redirect.com');
|
|
28
27
|
|
|
29
28
|
// Mock workos.userManagement.getAuthorizationUrl
|
|
30
|
-
|
|
29
|
+
vi.mocked(workos.userManagement.getAuthorizationUrl).mockReturnValue('mock-url');
|
|
31
30
|
|
|
32
31
|
await getAuthorizationUrl({});
|
|
33
32
|
|
|
@@ -39,7 +38,7 @@ describe('getAuthorizationUrl', () => {
|
|
|
39
38
|
});
|
|
40
39
|
|
|
41
40
|
it('works when called with no arguments', async () => {
|
|
42
|
-
|
|
41
|
+
vi.mocked(workos.userManagement.getAuthorizationUrl).mockReturnValue('mock-url');
|
|
43
42
|
|
|
44
43
|
await getAuthorizationUrl(); // Call with no arguments
|
|
45
44
|
|
|
@@ -47,7 +46,7 @@ describe('getAuthorizationUrl', () => {
|
|
|
47
46
|
});
|
|
48
47
|
|
|
49
48
|
it('works when prompt is provided', async () => {
|
|
50
|
-
|
|
49
|
+
vi.mocked(workos.userManagement.getAuthorizationUrl).mockReturnValue('mock-url');
|
|
51
50
|
|
|
52
51
|
await getAuthorizationUrl({ prompt: 'consent' });
|
|
53
52
|
|