1- import { render , screen , waitFor } from '@testing-library/react' ;
1+ import {
2+ render , screen , waitFor , act ,
3+ } from '@testing-library/react' ;
24import { AppContext } from '@edx/frontend-platform/react' ;
35import userEvent from '@testing-library/user-event' ;
46import { MemoryRouter , Route , Routes } from 'react-router-dom' ;
@@ -17,6 +19,21 @@ jest.mock('@edx/frontend-platform/logging', () => ({
1719 logError : jest . fn ( ) ,
1820} ) ) ;
1921
22+ // Mock StudioHeader to avoid prop validation errors in tests
23+ jest . mock ( '@edx/frontend-component-header' , ( ) => ( {
24+ StudioHeader : ( { children, ...props } : any ) => < div data-testid = "mocked-studio-header" { ...props } > { children } </ div > ,
25+ } ) ) ;
26+
27+ // Mock the useRevokeUserRoles hook
28+ const mockRevokeUserRoles = jest . fn ( ) ;
29+ jest . mock ( '@src/authz-module/data/hooks' , ( ) => ( {
30+ ...jest . requireActual ( '@src/authz-module/data/hooks' ) ,
31+ useRevokeUserRoles : ( ) => ( {
32+ mutate : mockRevokeUserRoles ,
33+ isPending : false ,
34+ } ) ,
35+ } ) ) ;
36+
2037const mockUser = {
2138 username : 'johndoe' ,
2239@@ -50,9 +67,16 @@ const renderWithRouter = (route = '/audit/johndoe') => {
5067 authenticatedUser : {
5168 username : 'testuser' ,
526970+ userId : 1 ,
5371 } ,
5472 config : {
55- // @ts -ignore
73+ LMS_BASE_URL : 'http://localhost:18000' ,
74+ STUDIO_BASE_URL : 'http://localhost:18010' ,
75+ AUTHZ_MICROFRONTEND_URL : 'http://localhost:18012' ,
76+ ACCESS_TOKEN_COOKIE_NAME : 'edx-jwt-cookie-header-payload' ,
77+ BASE_URL : 'http://localhost:18012' ,
78+ ENVIRONMENT : 'test' ,
79+ LANGUAGE_PREFERENCE_COOKIE_NAME : 'openedx-language-preference' ,
5680 ...process . env ,
5781 } ,
5882 } ;
@@ -78,6 +102,11 @@ const renderWithRouter = (route = '/audit/johndoe') => {
78102describe ( 'AuditUserPage' , ( ) => {
79103 beforeEach ( ( ) => {
80104 jest . clearAllMocks ( ) ;
105+ // Set up default mock behavior for useRevokeUserRoles
106+ mockRevokeUserRoles . mockImplementation ( ( variables , { onSuccess } ) => {
107+ // Simulate successful deletion by default
108+ onSuccess ( { errors : [ ] , completed : [ 'role1' ] } ) ;
109+ } ) ;
81110 } ) ;
82111
83112 beforeAll ( ( ) => {
@@ -250,7 +279,6 @@ describe('AuditUserPage', () => {
250279 . fn ( )
251280 . mockResolvedValueOnce ( { data : mockUser } )
252281 . mockResolvedValueOnce ( { data : mockAssignments } ) ,
253- delete : jest . fn ( ) . mockResolvedValue ( { data : { errors : [ ] } } ) ,
254282 } ) ;
255283
256284 renderWithRouter ( ) ;
@@ -269,26 +297,35 @@ describe('AuditUserPage', () => {
269297 } ) ;
270298
271299 const removeButton = screen . getByRole ( 'button' , { name : / r e m o v e / i } ) ;
272- await user . click ( removeButton ) ;
300+
301+ await act ( async ( ) => {
302+ await user . click ( removeButton ) ;
303+ } ) ;
273304
274305 await waitFor ( ( ) => {
275306 expect ( screen . queryByRole ( 'dialog' ) ) . not . toBeInTheDocument ( ) ;
307+ } ) ;
308+
309+ await waitFor ( ( ) => {
276310 expect ( screen . getByText ( / r o l e h a s b e e n s u c c e s s f u l l y r e m o v e d / i) ) . toBeInTheDocument ( ) ;
277311 } ) ;
278312 } ) ;
279313
280314 it ( 'shows error toast when role revocation succeeds but returns errors' , async ( ) => {
315+ // Override mock for this specific test case
316+ mockRevokeUserRoles . mockImplementation ( ( _ , { onSuccess } ) => {
317+ // Call onSuccess immediately with errors
318+ onSuccess ( {
319+ errors : [ 'Failed to revoke user role' ] ,
320+ completed : [ ] ,
321+ } ) ;
322+ } ) ;
323+
281324 ( getAuthenticatedHttpClient as jest . Mock ) . mockReturnValue ( {
282325 get : jest
283326 . fn ( )
284327 . mockResolvedValueOnce ( { data : mockUser } )
285328 . mockResolvedValueOnce ( { data : mockAssignments } ) ,
286- delete : jest . fn ( ) . mockResolvedValue ( {
287- data : {
288- errors : [ 'Failed to revoke user role' ] ,
289- completed : [ ] ,
290- } ,
291- } ) ,
292329 } ) ;
293330
294331 renderWithRouter ( ) ;
@@ -307,20 +344,28 @@ describe('AuditUserPage', () => {
307344 } ) ;
308345
309346 const removeButton = screen . getByRole ( 'button' , { name : / r e m o v e / i } ) ;
310- await user . click ( removeButton ) ;
347+
348+ await act ( async ( ) => {
349+ await user . click ( removeButton ) ;
350+ } ) ;
311351
312352 await waitFor ( ( ) => {
313353 expect ( screen . getByText ( / s o m e t h i n g w e n t w r o n g / i) ) . toBeInTheDocument ( ) ;
314354 } ) ;
315355 } ) ;
316356
317357 it ( 'shows error toast with retry when role revocation fails' , async ( ) => {
358+ // Override mock for this specific test case
359+ mockRevokeUserRoles . mockImplementation ( ( variables , { onError } ) => {
360+ // Call onError immediately to simulate failure
361+ onError ( new Error ( 'Network error' ) ) ;
362+ } ) ;
363+
318364 ( getAuthenticatedHttpClient as jest . Mock ) . mockReturnValue ( {
319365 get : jest
320366 . fn ( )
321367 . mockResolvedValueOnce ( { data : mockUser } )
322368 . mockResolvedValueOnce ( { data : mockAssignments } ) ,
323- delete : jest . fn ( ) . mockRejectedValue ( new Error ( 'Network error' ) ) ,
324369 } ) ;
325370
326371 renderWithRouter ( ) ;
@@ -339,7 +384,10 @@ describe('AuditUserPage', () => {
339384 } ) ;
340385
341386 const removeButton = screen . getByRole ( 'button' , { name : / r e m o v e / i } ) ;
342- await user . click ( removeButton ) ;
387+
388+ await act ( async ( ) => {
389+ await user . click ( removeButton ) ;
390+ } ) ;
343391
344392 await waitFor ( ( ) => {
345393 expect ( screen . getByText ( / s o m e t h i n g w e n t w r o n g o n o u r e n d / i) ) . toBeInTheDocument ( ) ;
0 commit comments