@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
|
@@ -10,17 +10,17 @@ import {
|
|
|
10
10
|
switchToOrganizationAction,
|
|
11
11
|
} from '../actions.js';
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
checkSessionAction:
|
|
15
|
-
getAuthAction:
|
|
16
|
-
refreshAuthAction:
|
|
17
|
-
handleSignOutAction:
|
|
18
|
-
switchToOrganizationAction:
|
|
13
|
+
vi.mock('../actions', () => ({
|
|
14
|
+
checkSessionAction: vi.fn(),
|
|
15
|
+
getAuthAction: vi.fn(),
|
|
16
|
+
refreshAuthAction: vi.fn(),
|
|
17
|
+
handleSignOutAction: vi.fn(),
|
|
18
|
+
switchToOrganizationAction: vi.fn(),
|
|
19
19
|
}));
|
|
20
20
|
|
|
21
21
|
describe('AuthKitProvider', () => {
|
|
22
22
|
beforeEach(() => {
|
|
23
|
-
|
|
23
|
+
vi.clearAllMocks();
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
it('should render children', async () => {
|
|
@@ -134,7 +134,7 @@ describe('AuthKitProvider', () => {
|
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
it('should call getAuthAction when initialAuth is not provided', async () => {
|
|
137
|
-
(getAuthAction as
|
|
137
|
+
(getAuthAction as Mock).mockResolvedValueOnce({
|
|
138
138
|
user: { email: 'test@example.com' },
|
|
139
139
|
sessionId: 'test-session',
|
|
140
140
|
});
|
|
@@ -151,7 +151,7 @@ describe('AuthKitProvider', () => {
|
|
|
151
151
|
});
|
|
152
152
|
|
|
153
153
|
it('should do nothing if onSessionExpired is false', async () => {
|
|
154
|
-
|
|
154
|
+
vi.spyOn(window, 'addEventListener');
|
|
155
155
|
|
|
156
156
|
await act(async () => {
|
|
157
157
|
render(
|
|
@@ -166,8 +166,8 @@ describe('AuthKitProvider', () => {
|
|
|
166
166
|
});
|
|
167
167
|
|
|
168
168
|
it('should call onSessionExpired when session is expired', async () => {
|
|
169
|
-
(checkSessionAction as
|
|
170
|
-
const onSessionExpired =
|
|
169
|
+
(checkSessionAction as Mock).mockRejectedValueOnce(new Error('Failed to fetch'));
|
|
170
|
+
const onSessionExpired = vi.fn();
|
|
171
171
|
|
|
172
172
|
render(
|
|
173
173
|
<AuthKitProvider onSessionExpired={onSessionExpired}>
|
|
@@ -186,8 +186,8 @@ describe('AuthKitProvider', () => {
|
|
|
186
186
|
});
|
|
187
187
|
|
|
188
188
|
it('should only call onSessionExpired once if multiple visibility changes occur', async () => {
|
|
189
|
-
(checkSessionAction as
|
|
190
|
-
const onSessionExpired =
|
|
189
|
+
(checkSessionAction as Mock).mockRejectedValueOnce(new Error('Failed to fetch'));
|
|
190
|
+
const onSessionExpired = vi.fn();
|
|
191
191
|
|
|
192
192
|
render(
|
|
193
193
|
<AuthKitProvider onSessionExpired={onSessionExpired}>
|
|
@@ -207,9 +207,9 @@ describe('AuthKitProvider', () => {
|
|
|
207
207
|
});
|
|
208
208
|
|
|
209
209
|
it('should pass through if checkSessionAction does not throw "Failed to fetch"', async () => {
|
|
210
|
-
(checkSessionAction as
|
|
210
|
+
(checkSessionAction as Mock).mockResolvedValueOnce(false);
|
|
211
211
|
|
|
212
|
-
const onSessionExpired =
|
|
212
|
+
const onSessionExpired = vi.fn();
|
|
213
213
|
|
|
214
214
|
render(
|
|
215
215
|
<AuthKitProvider onSessionExpired={onSessionExpired}>
|
|
@@ -234,7 +234,7 @@ describe('AuthKitProvider', () => {
|
|
|
234
234
|
originalLocation = window.location;
|
|
235
235
|
// @ts-expect-error - deleting window.location to mock it
|
|
236
236
|
delete window.location;
|
|
237
|
-
window.location = { reload:
|
|
237
|
+
window.location = { reload: vi.fn() } as unknown as Location;
|
|
238
238
|
});
|
|
239
239
|
|
|
240
240
|
afterEach(() => {
|
|
@@ -242,7 +242,7 @@ describe('AuthKitProvider', () => {
|
|
|
242
242
|
});
|
|
243
243
|
|
|
244
244
|
it('should reload the page when session is expired and no onSessionExpired handler is provided', async () => {
|
|
245
|
-
(checkSessionAction as
|
|
245
|
+
(checkSessionAction as Mock).mockRejectedValueOnce(new Error('Failed to fetch'));
|
|
246
246
|
|
|
247
247
|
render(
|
|
248
248
|
<AuthKitProvider>
|
|
@@ -261,8 +261,8 @@ describe('AuthKitProvider', () => {
|
|
|
261
261
|
});
|
|
262
262
|
|
|
263
263
|
it('should not call onSessionExpired or reload the page if session is valid', async () => {
|
|
264
|
-
(checkSessionAction as
|
|
265
|
-
const onSessionExpired =
|
|
264
|
+
(checkSessionAction as Mock).mockResolvedValueOnce(true);
|
|
265
|
+
const onSessionExpired = vi.fn();
|
|
266
266
|
|
|
267
267
|
render(
|
|
268
268
|
<AuthKitProvider onSessionExpired={onSessionExpired}>
|
|
@@ -285,12 +285,12 @@ describe('AuthKitProvider', () => {
|
|
|
285
285
|
|
|
286
286
|
describe('useAuth', () => {
|
|
287
287
|
beforeEach(() => {
|
|
288
|
-
|
|
288
|
+
vi.clearAllMocks();
|
|
289
289
|
});
|
|
290
290
|
|
|
291
291
|
it('should call getAuth when a user is not returned when ensureSignedIn is true', async () => {
|
|
292
292
|
// First and second calls return no user, second call returns a user
|
|
293
|
-
(getAuthAction as
|
|
293
|
+
(getAuthAction as Mock)
|
|
294
294
|
.mockResolvedValueOnce({ user: null, loading: true })
|
|
295
295
|
.mockResolvedValueOnce({ user: { email: 'test@example.com' }, loading: false });
|
|
296
296
|
|
|
@@ -319,7 +319,7 @@ describe('useAuth', () => {
|
|
|
319
319
|
};
|
|
320
320
|
|
|
321
321
|
// Suppress console.error for this test since we expect an error
|
|
322
|
-
const consoleSpy =
|
|
322
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
323
323
|
|
|
324
324
|
expect(() => {
|
|
325
325
|
render(<TestComponent />);
|
|
@@ -329,7 +329,7 @@ describe('useAuth', () => {
|
|
|
329
329
|
});
|
|
330
330
|
|
|
331
331
|
it('should provide auth context values when used within AuthKitProvider', async () => {
|
|
332
|
-
(getAuthAction as
|
|
332
|
+
(getAuthAction as Mock).mockResolvedValueOnce({
|
|
333
333
|
user: { email: 'test@example.com' },
|
|
334
334
|
sessionId: 'test-session',
|
|
335
335
|
organizationId: 'test-org',
|
|
@@ -377,8 +377,8 @@ describe('useAuth', () => {
|
|
|
377
377
|
sessionId: 'test-session',
|
|
378
378
|
};
|
|
379
379
|
|
|
380
|
-
(getAuthAction as
|
|
381
|
-
(refreshAuthAction as
|
|
380
|
+
(getAuthAction as Mock).mockResolvedValueOnce(mockAuth);
|
|
381
|
+
(refreshAuthAction as Mock).mockResolvedValueOnce({
|
|
382
382
|
...mockAuth,
|
|
383
383
|
sessionId: 'new-session',
|
|
384
384
|
});
|
|
@@ -420,10 +420,10 @@ describe('useAuth', () => {
|
|
|
420
420
|
organizationId: 'new-org',
|
|
421
421
|
};
|
|
422
422
|
|
|
423
|
-
(getAuthAction as
|
|
423
|
+
(getAuthAction as Mock)
|
|
424
424
|
.mockResolvedValue(mockAuth)
|
|
425
425
|
.mockResolvedValueOnce({ ...mockAuth, organizationId: 'old-org' });
|
|
426
|
-
(switchToOrganizationAction as
|
|
426
|
+
(switchToOrganizationAction as Mock).mockResolvedValueOnce(mockAuth);
|
|
427
427
|
|
|
428
428
|
const TestComponent = () => {
|
|
429
429
|
const auth = useAuth();
|
|
@@ -456,7 +456,7 @@ describe('useAuth', () => {
|
|
|
456
456
|
});
|
|
457
457
|
|
|
458
458
|
it('should receive an error when refreshAuth fails with an error', async () => {
|
|
459
|
-
(refreshAuthAction as
|
|
459
|
+
(refreshAuthAction as Mock).mockRejectedValueOnce(new Error('Refresh failed'));
|
|
460
460
|
|
|
461
461
|
let error: string | undefined;
|
|
462
462
|
|
|
@@ -493,7 +493,7 @@ describe('useAuth', () => {
|
|
|
493
493
|
});
|
|
494
494
|
|
|
495
495
|
it('should receive an error when refreshAuth fails with a string error', async () => {
|
|
496
|
-
(refreshAuthAction as
|
|
496
|
+
(refreshAuthAction as Mock).mockRejectedValueOnce('Refresh failed');
|
|
497
497
|
|
|
498
498
|
let error: string | undefined;
|
|
499
499
|
|
|
@@ -530,7 +530,7 @@ describe('useAuth', () => {
|
|
|
530
530
|
});
|
|
531
531
|
|
|
532
532
|
it('should call handleSignOutAction when signOut is called', async () => {
|
|
533
|
-
(handleSignOutAction as
|
|
533
|
+
(handleSignOutAction as Mock).mockResolvedValueOnce({});
|
|
534
534
|
|
|
535
535
|
const TestComponent = () => {
|
|
536
536
|
const auth = useAuth();
|
|
@@ -556,7 +556,7 @@ describe('useAuth', () => {
|
|
|
556
556
|
});
|
|
557
557
|
|
|
558
558
|
it('should pass returnTo parameter to handleSignOutAction', async () => {
|
|
559
|
-
(handleSignOutAction as
|
|
559
|
+
(handleSignOutAction as Mock).mockResolvedValueOnce({});
|
|
560
560
|
|
|
561
561
|
const TestComponent = () => {
|
|
562
562
|
const auth = useAuth();
|
|
@@ -24,12 +24,10 @@ describe('Button', () => {
|
|
|
24
24
|
const { getByRole } = render(<Button style={{ backgroundColor: 'red' }}>Click me</Button>);
|
|
25
25
|
const button = getByRole('button');
|
|
26
26
|
|
|
27
|
-
expect(button).
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
justifyContent: 'center',
|
|
32
|
-
});
|
|
27
|
+
expect(button.style.backgroundColor).toBe('red');
|
|
28
|
+
expect(button.style.display).toBe('inline-flex');
|
|
29
|
+
expect(button.style.alignItems).toBe('center');
|
|
30
|
+
expect(button.style.justifyContent).toBe('center');
|
|
33
31
|
});
|
|
34
32
|
|
|
35
33
|
it('should pass through additional props', () => {
|
|
@@ -7,23 +7,23 @@ import * as React from 'react';
|
|
|
7
7
|
import { handleSignOutAction } from '../actions.js';
|
|
8
8
|
|
|
9
9
|
// Mock the useAuth hook
|
|
10
|
-
|
|
11
|
-
useAuth:
|
|
10
|
+
vi.mock('./authkit-provider', () => ({
|
|
11
|
+
useAuth: vi.fn(),
|
|
12
12
|
}));
|
|
13
13
|
|
|
14
14
|
// Mock the getOrganizationAction
|
|
15
|
-
|
|
16
|
-
getOrganizationAction:
|
|
17
|
-
handleSignOutAction:
|
|
15
|
+
vi.mock('../actions', () => ({
|
|
16
|
+
getOrganizationAction: vi.fn(),
|
|
17
|
+
handleSignOutAction: vi.fn(),
|
|
18
18
|
}));
|
|
19
19
|
|
|
20
20
|
describe('Impersonation', () => {
|
|
21
21
|
beforeEach(() => {
|
|
22
|
-
|
|
22
|
+
vi.clearAllMocks();
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
it('should return null if not impersonating', () => {
|
|
26
|
-
(useAuth as
|
|
26
|
+
(useAuth as Mock).mockReturnValue({
|
|
27
27
|
impersonator: null,
|
|
28
28
|
user: { id: '123', email: 'user@example.com' },
|
|
29
29
|
organizationId: null,
|
|
@@ -34,7 +34,7 @@ describe('Impersonation', () => {
|
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
it('should render impersonation banner when impersonating', () => {
|
|
37
|
-
(useAuth as
|
|
37
|
+
(useAuth as Mock).mockReturnValue({
|
|
38
38
|
impersonator: { email: 'admin@example.com' },
|
|
39
39
|
user: { id: '123', email: 'user@example.com' },
|
|
40
40
|
organizationId: null,
|
|
@@ -45,13 +45,13 @@ describe('Impersonation', () => {
|
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
it('should render with organization info when organizationId is provided', async () => {
|
|
48
|
-
(useAuth as
|
|
48
|
+
(useAuth as Mock).mockReturnValue({
|
|
49
49
|
impersonator: { email: 'admin@example.com' },
|
|
50
50
|
user: { id: '123', email: 'user@example.com' },
|
|
51
51
|
organizationId: 'org_123',
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
-
(getOrganizationAction as
|
|
54
|
+
(getOrganizationAction as Mock).mockResolvedValue({
|
|
55
55
|
id: 'org_123',
|
|
56
56
|
name: 'Test Org',
|
|
57
57
|
});
|
|
@@ -64,7 +64,7 @@ describe('Impersonation', () => {
|
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
it('should render at the bottom by default', () => {
|
|
67
|
-
(useAuth as
|
|
67
|
+
(useAuth as Mock).mockReturnValue({
|
|
68
68
|
impersonator: { email: 'admin@example.com' },
|
|
69
69
|
user: { id: '123', email: 'user@example.com' },
|
|
70
70
|
organizationId: null,
|
|
@@ -76,7 +76,7 @@ describe('Impersonation', () => {
|
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
it('should render at the top when side prop is "top"', () => {
|
|
79
|
-
(useAuth as
|
|
79
|
+
(useAuth as Mock).mockReturnValue({
|
|
80
80
|
impersonator: { email: 'admin@example.com' },
|
|
81
81
|
user: { id: '123', email: 'user@example.com' },
|
|
82
82
|
organizationId: null,
|
|
@@ -88,7 +88,7 @@ describe('Impersonation', () => {
|
|
|
88
88
|
});
|
|
89
89
|
|
|
90
90
|
it('should merge custom styles with default styles', () => {
|
|
91
|
-
(useAuth as
|
|
91
|
+
(useAuth as Mock).mockReturnValue({
|
|
92
92
|
impersonator: { email: 'admin@example.com' },
|
|
93
93
|
user: { id: '123', email: 'user@example.com' },
|
|
94
94
|
organizationId: null,
|
|
@@ -97,11 +97,11 @@ describe('Impersonation', () => {
|
|
|
97
97
|
const customStyle = { backgroundColor: 'red' };
|
|
98
98
|
const { container } = render(<Impersonation style={customStyle} />);
|
|
99
99
|
const root = container.querySelector('[data-workos-impersonation-root]');
|
|
100
|
-
expect(root).
|
|
100
|
+
expect((root as HTMLElement).style.backgroundColor).toBe('red');
|
|
101
101
|
});
|
|
102
102
|
|
|
103
103
|
it('should should sign out when the Stop button is called', async () => {
|
|
104
|
-
(useAuth as
|
|
104
|
+
(useAuth as Mock).mockReturnValue({
|
|
105
105
|
impersonator: { email: 'admin@example.com' },
|
|
106
106
|
user: { id: '123', email: 'user@example.com' },
|
|
107
107
|
organizationId: null,
|
|
@@ -114,7 +114,7 @@ describe('Impersonation', () => {
|
|
|
114
114
|
});
|
|
115
115
|
|
|
116
116
|
it('should pass returnTo prop to handleSignOutAction when provided', async () => {
|
|
117
|
-
(useAuth as
|
|
117
|
+
(useAuth as Mock).mockReturnValue({
|
|
118
118
|
impersonator: { email: 'admin@example.com' },
|
|
119
119
|
user: { id: '123', email: 'user@example.com' },
|
|
120
120
|
organizationId: null,
|
|
@@ -129,7 +129,7 @@ describe('Impersonation', () => {
|
|
|
129
129
|
});
|
|
130
130
|
|
|
131
131
|
it('should not call getOrganizationAction when organizationId is not provided', () => {
|
|
132
|
-
(useAuth as
|
|
132
|
+
(useAuth as Mock).mockReturnValue({
|
|
133
133
|
impersonator: { email: 'admin@example.com' },
|
|
134
134
|
user: { id: '123', email: 'user@example.com' },
|
|
135
135
|
organizationId: null,
|
|
@@ -140,7 +140,7 @@ describe('Impersonation', () => {
|
|
|
140
140
|
});
|
|
141
141
|
|
|
142
142
|
it('should not call getOrganizationAction when impersonator is not present', () => {
|
|
143
|
-
(useAuth as
|
|
143
|
+
(useAuth as Mock).mockReturnValue({
|
|
144
144
|
impersonator: null,
|
|
145
145
|
user: { id: '123', email: 'user@example.com' },
|
|
146
146
|
organizationId: 'org_123',
|
|
@@ -151,7 +151,7 @@ describe('Impersonation', () => {
|
|
|
151
151
|
});
|
|
152
152
|
|
|
153
153
|
it('should not call getOrganizationAction when user is not present', () => {
|
|
154
|
-
(useAuth as
|
|
154
|
+
(useAuth as Mock).mockReturnValue({
|
|
155
155
|
impersonator: { email: 'admin@example.com' },
|
|
156
156
|
user: null,
|
|
157
157
|
organizationId: 'org_123',
|
|
@@ -167,10 +167,10 @@ describe('Impersonation', () => {
|
|
|
167
167
|
name: 'Test Org',
|
|
168
168
|
};
|
|
169
169
|
|
|
170
|
-
(getOrganizationAction as
|
|
170
|
+
(getOrganizationAction as Mock).mockResolvedValue(mockOrg);
|
|
171
171
|
|
|
172
172
|
const { rerender } = await act(async () => {
|
|
173
|
-
(useAuth as
|
|
173
|
+
(useAuth as Mock).mockReturnValue({
|
|
174
174
|
impersonator: { email: 'admin@example.com' },
|
|
175
175
|
user: { id: '123', email: 'user@example.com' },
|
|
176
176
|
organizationId: 'org_123',
|
|
@@ -188,7 +188,7 @@ describe('Impersonation', () => {
|
|
|
188
188
|
|
|
189
189
|
// Rerender with the same organizationId
|
|
190
190
|
await act(async () => {
|
|
191
|
-
(useAuth as
|
|
191
|
+
(useAuth as Mock).mockReturnValue({
|
|
192
192
|
impersonator: { email: 'admin@example.com' },
|
|
193
193
|
user: { id: '123', email: 'user@example.com' },
|
|
194
194
|
organizationId: 'org_123',
|
|
@@ -212,10 +212,10 @@ describe('Impersonation', () => {
|
|
|
212
212
|
name: 'Test Org 2',
|
|
213
213
|
};
|
|
214
214
|
|
|
215
|
-
(getOrganizationAction as
|
|
215
|
+
(getOrganizationAction as Mock).mockResolvedValueOnce(mockOrg1).mockResolvedValueOnce(mockOrg2);
|
|
216
216
|
|
|
217
217
|
const { rerender } = await act(async () => {
|
|
218
|
-
(useAuth as
|
|
218
|
+
(useAuth as Mock).mockReturnValue({
|
|
219
219
|
impersonator: { email: 'admin@example.com' },
|
|
220
220
|
user: { id: '123', email: 'user@example.com' },
|
|
221
221
|
organizationId: 'org_123',
|
|
@@ -234,7 +234,7 @@ describe('Impersonation', () => {
|
|
|
234
234
|
|
|
235
235
|
// Rerender with a different organizationId
|
|
236
236
|
await act(async () => {
|
|
237
|
-
(useAuth as
|
|
237
|
+
(useAuth as Mock).mockReturnValue({
|
|
238
238
|
impersonator: { email: 'admin@example.com' },
|
|
239
239
|
user: { id: '123', email: 'user@example.com' },
|
|
240
240
|
organizationId: 'org_456',
|
|
@@ -14,7 +14,7 @@ describe('MinMaxButton', () => {
|
|
|
14
14
|
afterEach(() => {
|
|
15
15
|
// Clean up after each test
|
|
16
16
|
document.body.innerHTML = '';
|
|
17
|
-
|
|
17
|
+
vi.restoreAllMocks();
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
it('sets minimized value when clicked', () => {
|
|
@@ -34,7 +34,7 @@ describe('MinMaxButton', () => {
|
|
|
34
34
|
const root = document.querySelector('[data-workos-impersonation-root]');
|
|
35
35
|
|
|
36
36
|
// Mock querySelector to return null for this test
|
|
37
|
-
|
|
37
|
+
vi.spyOn(document, 'querySelector').mockReturnValue(null);
|
|
38
38
|
|
|
39
39
|
act(() => {
|
|
40
40
|
getByRole('button').click();
|
|
@@ -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 },
|