Skip to content

Commit 80d6d8e

Browse files
committed
Add and reorder tests for the AuthProvider
1 parent b0ebd39 commit 80d6d8e

File tree

1 file changed

+184
-18
lines changed

1 file changed

+184
-18
lines changed

packages/cpt-ui/__tests__/AuthProvider.test.tsx

Lines changed: 184 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,14 @@ describe('AuthProvider', () => {
117117
});
118118
});
119119

120+
// Initialization and Configuration
120121
it('should configure Amplify on mount', async () => {
121122
// Verify Amplify.configure is called when the provider mounts
122123
await renderWithProvider();
123124
expect(Amplify.configure).toHaveBeenCalled();
124125
});
125126

127+
// Session Handling
126128
it('should set isSignedIn to false if no valid tokens are returned', async () => {
127129
// Render without valid tokens
128130
await renderWithProvider();
@@ -149,6 +151,172 @@ describe('AuthProvider', () => {
149151
});
150152
});
151153

154+
it('should handle missing tokens during session fetch', async () => {
155+
// Simulate a session fetch with missing tokens
156+
const incompleteSession = { tokens: {} };
157+
158+
await renderWithProvider({ sessionMock: incompleteSession });
159+
160+
await waitFor(() => {
161+
// Assert that the user is not signed in due to missing tokens
162+
expect(screen.getByTestId('isSignedIn').textContent).toBe('false');
163+
expect(screen.getByTestId('user').textContent).toBe('');
164+
});
165+
});
166+
167+
// Error Handling
168+
it('should handle fetchAuthSession failure', async () => {
169+
// Mock fetchAuthSession to throw an error
170+
(fetchAuthSession as jest.Mock).mockRejectedValue(new Error('Session fetch failed'));
171+
172+
await renderWithProvider();
173+
174+
await waitFor(() => {
175+
// Assert that the user is not signed in due to session fetch failure
176+
expect(screen.getByTestId('isSignedIn').textContent).toBe('false');
177+
expect(screen.getByTestId('user').textContent).toBe('');
178+
});
179+
});
180+
181+
it('should handle getCurrentUser failure gracefully', async () => {
182+
// Mock getCurrentUser to throw an error
183+
(getCurrentUser as jest.Mock).mockRejectedValue(new Error('User fetch failed'));
184+
185+
await renderWithProvider({
186+
sessionMock: createTokenMocks(),
187+
});
188+
189+
await waitFor(() => {
190+
// Assert that valid tokens do not automatically result in user data due to user fetch failure
191+
expect(screen.getByTestId('isSignedIn').textContent).toBe('true');
192+
expect(screen.getByTestId('user').textContent).toBe('');
193+
});
194+
});
195+
196+
it('should log an error and reset state when fetching user session fails', async () => {
197+
// Mock console.error to track calls
198+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
199+
200+
// Mock fetchAuthSession to throw an error
201+
const sessionError = new Error('Session fetch failed');
202+
(fetchAuthSession as jest.Mock).mockRejectedValueOnce(sessionError);
203+
204+
// Render the provider
205+
await renderWithProvider();
206+
207+
// Wait for the state to be reset
208+
await waitFor(() => {
209+
// Verify that the state is reset correctly
210+
expect(screen.getByTestId('isSignedIn').textContent).toBe('false');
211+
expect(screen.getByTestId('user').textContent).toBe('');
212+
});
213+
214+
// Verify that the error was logged
215+
expect(consoleErrorSpy).toHaveBeenCalledWith(
216+
'Error fetching user session:',
217+
sessionError
218+
);
219+
220+
// Restore the original console.error implementation
221+
consoleErrorSpy.mockRestore();
222+
});
223+
224+
it('should log an error if signOut fails', async () => {
225+
// Mock console.error to track calls
226+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
227+
228+
// Mock signOut to throw an error
229+
const signOutError = new Error('Sign out failed');
230+
(signOut as jest.Mock).mockRejectedValue(signOutError);
231+
232+
let contextValue: any;
233+
234+
const TestComponent = () => {
235+
contextValue = useContext(AuthContext);
236+
return null;
237+
};
238+
239+
// Render the provider
240+
await act(async () => {
241+
render(
242+
<AuthProvider>
243+
<TestComponent />
244+
</AuthProvider>
245+
);
246+
});
247+
248+
// Attempt to sign out and verify the logged error
249+
await act(async () => {
250+
await contextValue.cognitoSignOut();
251+
});
252+
253+
expect(consoleErrorSpy).toHaveBeenCalledWith('Failed to sign out:', signOutError); // Error logged
254+
255+
// Restore the original console.error implementation
256+
consoleErrorSpy.mockRestore();
257+
});
258+
259+
// Token Handling
260+
it('should log a warning and reset state when the ID token is expired', async () => {
261+
// Mock console.warn to track calls
262+
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
263+
264+
// Create mock tokens with an expired ID token
265+
const expiredIdToken = {
266+
tokens: {
267+
idToken: {
268+
toString: () => `header.${btoa(JSON.stringify({ exp: Math.floor(Date.now() / 1000) - 3600 }))}.signature`,
269+
payload: { exp: Math.floor(Date.now() / 1000) - 3600 },
270+
},
271+
accessToken: {
272+
toString: () => `header.${btoa(JSON.stringify({ exp: Math.floor(Date.now() / 1000) + 3600 }))}.signature`,
273+
payload: { exp: Math.floor(Date.now() / 1000) + 3600 },
274+
},
275+
},
276+
};
277+
278+
// Render the provider with the expired ID token
279+
await renderWithProvider({ sessionMock: expiredIdToken });
280+
281+
// Wait for the state to be reset and verify the warning
282+
await waitFor(() => {
283+
expect(screen.getByTestId('isSignedIn').textContent).toBe('false'); // State reset
284+
expect(screen.getByTestId('user').textContent).toBe('');
285+
expect(consoleWarnSpy).toHaveBeenCalledWith(
286+
'ID token is expired. Consider refreshing the token.'
287+
); // Warning logged
288+
});
289+
290+
// Restore the original console.warn implementation
291+
consoleWarnSpy.mockRestore();
292+
});
293+
294+
it('should handle expired tokens', async () => {
295+
// Create mock expired tokens
296+
const expiredTokens = {
297+
tokens: {
298+
idToken: {
299+
toString: () => `header.${btoa(JSON.stringify({ exp: Math.floor(Date.now() / 1000) - 3600 }))}.signature`,
300+
payload: { exp: Math.floor(Date.now() / 1000) - 3600 },
301+
},
302+
accessToken: {
303+
toString: () => `header.${btoa(JSON.stringify({ exp: Math.floor(Date.now() / 1000) - 3600 }))}.signature`,
304+
payload: { exp: Math.floor(Date.now() / 1000) - 3600 },
305+
},
306+
},
307+
};
308+
309+
// Render with expired tokens
310+
await renderWithProvider({ sessionMock: expiredTokens });
311+
312+
await waitFor(() => {
313+
// Verify signed-in state is false and user is cleared
314+
expect(screen.getByTestId('isSignedIn').textContent).toBe('false');
315+
expect(screen.getByTestId('user').textContent).toBe('');
316+
});
317+
});
318+
319+
// Hub Events
152320
it('should handle Hub event signInWithRedirect', async () => {
153321
// Mock session and user for a successful signInWithRedirect Hub event
154322
const mockSession = createTokenMocks(); // Create valid mock tokens
@@ -174,16 +342,18 @@ describe('AuthProvider', () => {
174342

175343
// Simulate the Hub event "signInWithRedirect"
176344
act(() => {
345+
// Simulate a successful Hub event for signInWithRedirect
177346
hubCallback!({ payload: { event: 'signInWithRedirect' } });
178347
});
179348

180349
// Wait for the context state to update and verify changes
181350
await waitFor(() => {
351+
// Assert that the user is signed in after the Hub event
182352
expect(screen.getByTestId('isSignedIn').textContent).toBe('true'); // User is signed in
183353
expect(screen.getByTestId('user').textContent).toBe('UserPresent'); // User object is present
184354
});
185355
});
186-
356+
187357
it('should handle Hub event signInWithRedirect_failure', async () => {
188358
// Render the AuthProvider with a TestConsumer to observe context changes
189359
await renderWithProvider();
@@ -200,37 +370,33 @@ describe('AuthProvider', () => {
200370

201371
// Wait for the context state to update and verify changes
202372
await waitFor(() => {
373+
// Assert that an error is set after the Hub event failure
203374
expect(screen.getByTestId('error').textContent).toBe(
204375
'An error has occurred during the OAuth flow.' // Error state is updated
205376
);
206377
});
207378
});
208379

209-
it('should handle expired tokens', async () => {
210-
// Create mock expired tokens
211-
const expiredTokens = {
212-
tokens: {
213-
idToken: {
214-
toString: () => `header.${btoa(JSON.stringify({ exp: Math.floor(Date.now() / 1000) - 3600 }))}.signature`,
215-
payload: { exp: Math.floor(Date.now() / 1000) - 3600 },
216-
},
217-
accessToken: {
218-
toString: () => `header.${btoa(JSON.stringify({ exp: Math.floor(Date.now() / 1000) - 3600 }))}.signature`,
219-
payload: { exp: Math.floor(Date.now() / 1000) - 3600 },
220-
},
221-
},
222-
};
380+
it('should handle Hub event signedOut', async () => {
381+
// Simulate a Hub event for user sign-out
382+
await renderWithProvider();
223383

224-
// Render with expired tokens
225-
await renderWithProvider({ sessionMock: expiredTokens });
384+
if (!hubCallback) {
385+
throw new Error('hubCallback is not initialized');
386+
}
387+
388+
act(() => {
389+
hubCallback!({ payload: { event: 'signedOut' } });
390+
});
226391

227392
await waitFor(() => {
228-
// Verify signed-in state is false and user is cleared
393+
// Assert that the user is signed out and state is cleared
229394
expect(screen.getByTestId('isSignedIn').textContent).toBe('false');
230395
expect(screen.getByTestId('user').textContent).toBe('');
231396
});
232397
});
233398

399+
// Auth Functions
234400
it('should provide cognitoSignIn and cognitoSignOut functions', async () => {
235401
let contextValue: any;
236402

0 commit comments

Comments
 (0)