@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
|
@@ -3,19 +3,21 @@ import { handleAuth } from './authkit-callback-route.js';
|
|
|
3
3
|
import { getSessionFromCookie, saveSession } from './session.js';
|
|
4
4
|
import { NextRequest, NextResponse } from 'next/server';
|
|
5
5
|
|
|
6
|
-
// Mocked in
|
|
6
|
+
// Mocked in vitest.setup.ts
|
|
7
7
|
import { cookies, headers } from 'next/headers';
|
|
8
8
|
|
|
9
9
|
// Mock dependencies
|
|
10
|
-
const fakeWorkosInstance = {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
const { fakeWorkosInstance } = vi.hoisted(() => ({
|
|
11
|
+
fakeWorkosInstance: {
|
|
12
|
+
userManagement: {
|
|
13
|
+
authenticateWithCode: vi.fn(),
|
|
14
|
+
getJwksUrl: vi.fn(() => 'https://api.workos.com/sso/jwks/client_1234567890'),
|
|
15
|
+
},
|
|
14
16
|
},
|
|
15
|
-
};
|
|
17
|
+
}));
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
getWorkOS:
|
|
19
|
+
vi.mock('../src/workos', () => ({
|
|
20
|
+
getWorkOS: vi.fn(() => fakeWorkosInstance),
|
|
19
21
|
}));
|
|
20
22
|
|
|
21
23
|
describe('authkit-callback-route', () => {
|
|
@@ -51,12 +53,12 @@ describe('authkit-callback-route', () => {
|
|
|
51
53
|
|
|
52
54
|
beforeAll(() => {
|
|
53
55
|
// Silence console.error during tests
|
|
54
|
-
|
|
56
|
+
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
55
57
|
});
|
|
56
58
|
|
|
57
59
|
beforeEach(async () => {
|
|
58
60
|
// Reset all mocks
|
|
59
|
-
|
|
61
|
+
vi.clearAllMocks();
|
|
60
62
|
|
|
61
63
|
// Create a new request with searchParams
|
|
62
64
|
request = new NextRequest(new URL('http://example.com/callback'));
|
|
@@ -72,7 +74,7 @@ describe('authkit-callback-route', () => {
|
|
|
72
74
|
});
|
|
73
75
|
|
|
74
76
|
it('should handle successful authentication', async () => {
|
|
75
|
-
|
|
77
|
+
vi.mocked(workos.userManagement.authenticateWithCode).mockResolvedValue(mockAuthResponse);
|
|
76
78
|
|
|
77
79
|
// Set up request with code
|
|
78
80
|
request.nextUrl.searchParams.set('code', 'test-code');
|
|
@@ -89,7 +91,7 @@ describe('authkit-callback-route', () => {
|
|
|
89
91
|
|
|
90
92
|
it('should handle authentication failure', async () => {
|
|
91
93
|
// Mock authentication failure
|
|
92
|
-
(workos.userManagement.authenticateWithCode as
|
|
94
|
+
(workos.userManagement.authenticateWithCode as Mock).mockRejectedValue(new Error('Auth failed'));
|
|
93
95
|
|
|
94
96
|
request.nextUrl.searchParams.set('code', 'invalid-code');
|
|
95
97
|
|
|
@@ -103,7 +105,7 @@ describe('authkit-callback-route', () => {
|
|
|
103
105
|
|
|
104
106
|
it('should handle authentication failure if a non-Error object is thrown', async () => {
|
|
105
107
|
// Mock authentication failure
|
|
106
|
-
|
|
108
|
+
vi.mocked(workos.userManagement.authenticateWithCode).mockRejectedValue('Auth failed');
|
|
107
109
|
|
|
108
110
|
request.nextUrl.searchParams.set('code', 'invalid-code');
|
|
109
111
|
|
|
@@ -117,7 +119,7 @@ describe('authkit-callback-route', () => {
|
|
|
117
119
|
|
|
118
120
|
it('should handle authentication failure with custom onError handler', async () => {
|
|
119
121
|
// Mock authentication failure
|
|
120
|
-
|
|
122
|
+
vi.mocked(workos.userManagement.authenticateWithCode).mockRejectedValue('Auth failed');
|
|
121
123
|
request.nextUrl.searchParams.set('code', 'invalid-code');
|
|
122
124
|
|
|
123
125
|
const handler = handleAuth({
|
|
@@ -145,7 +147,7 @@ describe('authkit-callback-route', () => {
|
|
|
145
147
|
});
|
|
146
148
|
|
|
147
149
|
it('should respect custom returnPathname', async () => {
|
|
148
|
-
|
|
150
|
+
vi.mocked(workos.userManagement.authenticateWithCode).mockResolvedValue(mockAuthResponse);
|
|
149
151
|
|
|
150
152
|
request.nextUrl.searchParams.set('code', 'test-code');
|
|
151
153
|
|
|
@@ -156,7 +158,7 @@ describe('authkit-callback-route', () => {
|
|
|
156
158
|
});
|
|
157
159
|
|
|
158
160
|
it('should handle state parameter with returnPathname', async () => {
|
|
159
|
-
|
|
161
|
+
vi.mocked(workos.userManagement.authenticateWithCode).mockResolvedValue(mockAuthResponse);
|
|
160
162
|
|
|
161
163
|
const state = btoa(JSON.stringify({ returnPathname: '/custom-path' }));
|
|
162
164
|
request.nextUrl.searchParams.set('code', 'test-code');
|
|
@@ -169,7 +171,7 @@ describe('authkit-callback-route', () => {
|
|
|
169
171
|
});
|
|
170
172
|
|
|
171
173
|
it('should extract custom search params from returnPathname', async () => {
|
|
172
|
-
|
|
174
|
+
vi.mocked(workos.userManagement.authenticateWithCode).mockResolvedValue(mockAuthResponse);
|
|
173
175
|
|
|
174
176
|
const state = btoa(JSON.stringify({ returnPathname: '/custom-path?foo=bar&baz=qux' }));
|
|
175
177
|
request.nextUrl.searchParams.set('code', 'test-code');
|
|
@@ -182,7 +184,7 @@ describe('authkit-callback-route', () => {
|
|
|
182
184
|
});
|
|
183
185
|
|
|
184
186
|
it('should handle full URL in returnPathname by extracting only the pathname', async () => {
|
|
185
|
-
|
|
187
|
+
vi.mocked(workos.userManagement.authenticateWithCode).mockResolvedValue(mockAuthResponse);
|
|
186
188
|
|
|
187
189
|
const state = btoa(JSON.stringify({ returnPathname: 'https://example.com/invite/k0123456789' }));
|
|
188
190
|
request.nextUrl.searchParams.set('code', 'test-code');
|
|
@@ -200,7 +202,7 @@ describe('authkit-callback-route', () => {
|
|
|
200
202
|
const originalRedirect = NextResponse.redirect;
|
|
201
203
|
(NextResponse as Partial<typeof NextResponse>).redirect = undefined;
|
|
202
204
|
|
|
203
|
-
|
|
205
|
+
vi.mocked(workos.userManagement.authenticateWithCode).mockResolvedValue(mockAuthResponse);
|
|
204
206
|
|
|
205
207
|
// Set up request with code
|
|
206
208
|
request.nextUrl.searchParams.set('code', 'test-code');
|
|
@@ -232,7 +234,7 @@ describe('authkit-callback-route', () => {
|
|
|
232
234
|
});
|
|
233
235
|
|
|
234
236
|
it('should use baseURL if provided', async () => {
|
|
235
|
-
|
|
237
|
+
vi.mocked(workos.userManagement.authenticateWithCode).mockResolvedValue(mockAuthResponse);
|
|
236
238
|
|
|
237
239
|
// Set up request with code
|
|
238
240
|
request.nextUrl.searchParams.set('code', 'test-code');
|
|
@@ -248,7 +250,7 @@ describe('authkit-callback-route', () => {
|
|
|
248
250
|
user: { id: 'user_123' },
|
|
249
251
|
};
|
|
250
252
|
|
|
251
|
-
(workos.userManagement.authenticateWithCode as
|
|
253
|
+
(workos.userManagement.authenticateWithCode as Mock).mockResolvedValue(mockAuthResponse);
|
|
252
254
|
|
|
253
255
|
// Set up request with code
|
|
254
256
|
request.nextUrl.searchParams.set('code', 'test-code');
|
|
@@ -260,12 +262,12 @@ describe('authkit-callback-route', () => {
|
|
|
260
262
|
});
|
|
261
263
|
|
|
262
264
|
it('should call onSuccess if provided', async () => {
|
|
263
|
-
|
|
265
|
+
vi.mocked(workos.userManagement.authenticateWithCode).mockResolvedValue(mockAuthResponse);
|
|
264
266
|
|
|
265
267
|
// Set up request with code
|
|
266
268
|
request.nextUrl.searchParams.set('code', 'test-code');
|
|
267
269
|
|
|
268
|
-
const onSuccess =
|
|
270
|
+
const onSuccess = vi.fn();
|
|
269
271
|
const handler = handleAuth({ onSuccess: onSuccess });
|
|
270
272
|
await handler(request);
|
|
271
273
|
|
|
@@ -276,7 +278,7 @@ describe('authkit-callback-route', () => {
|
|
|
276
278
|
|
|
277
279
|
it('should allow onSuccess to update session', async () => {
|
|
278
280
|
const newAccessToken = 'new-access-token';
|
|
279
|
-
|
|
281
|
+
vi.mocked(workos.userManagement.authenticateWithCode).mockResolvedValue(mockAuthResponse);
|
|
280
282
|
|
|
281
283
|
// Set up request with code
|
|
282
284
|
request.nextUrl.searchParams.set('code', 'test-code');
|
|
@@ -293,7 +295,7 @@ describe('authkit-callback-route', () => {
|
|
|
293
295
|
});
|
|
294
296
|
|
|
295
297
|
it('should pass custom state data to onSuccess callback', async () => {
|
|
296
|
-
|
|
298
|
+
vi.mocked(workos.userManagement.authenticateWithCode).mockResolvedValue(mockAuthResponse);
|
|
297
299
|
|
|
298
300
|
// Create state with new format: internal.user
|
|
299
301
|
const internalState = btoa(JSON.stringify({ returnPathname: '/dashboard' }))
|
|
@@ -305,7 +307,7 @@ describe('authkit-callback-route', () => {
|
|
|
305
307
|
request.nextUrl.searchParams.set('code', 'test-code');
|
|
306
308
|
request.nextUrl.searchParams.set('state', state);
|
|
307
309
|
|
|
308
|
-
const onSuccess =
|
|
310
|
+
const onSuccess = vi.fn();
|
|
309
311
|
const handler = handleAuth({ onSuccess });
|
|
310
312
|
await handler(request);
|
|
311
313
|
|
|
@@ -323,7 +325,7 @@ describe('authkit-callback-route', () => {
|
|
|
323
325
|
});
|
|
324
326
|
|
|
325
327
|
it('should handle state without custom data', async () => {
|
|
326
|
-
|
|
328
|
+
vi.mocked(workos.userManagement.authenticateWithCode).mockResolvedValue(mockAuthResponse);
|
|
327
329
|
|
|
328
330
|
// State with only returnPathname
|
|
329
331
|
const state = btoa(JSON.stringify({ returnPathname: '/profile' }));
|
|
@@ -331,7 +333,7 @@ describe('authkit-callback-route', () => {
|
|
|
331
333
|
request.nextUrl.searchParams.set('code', 'test-code');
|
|
332
334
|
request.nextUrl.searchParams.set('state', state);
|
|
333
335
|
|
|
334
|
-
const onSuccess =
|
|
336
|
+
const onSuccess = vi.fn();
|
|
335
337
|
const handler = handleAuth({ onSuccess });
|
|
336
338
|
await handler(request);
|
|
337
339
|
|
|
@@ -345,7 +347,7 @@ describe('authkit-callback-route', () => {
|
|
|
345
347
|
});
|
|
346
348
|
|
|
347
349
|
it('should handle backward compatibility with old state format', async () => {
|
|
348
|
-
|
|
350
|
+
vi.mocked(workos.userManagement.authenticateWithCode).mockResolvedValue(mockAuthResponse);
|
|
349
351
|
|
|
350
352
|
// Old format: just returnPathname
|
|
351
353
|
const state = btoa(JSON.stringify({ returnPathname: '/old-path' }));
|
|
@@ -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();
|