@@ -2,7 +2,9 @@ import { screen } from '@testing-library/react';
22import userEvent from '@testing-library/user-event' ;
33import { useNavigate } from 'react-router-dom' ;
44import { initializeMockApp } from '@edx/frontend-platform/testing' ;
5- import { renderWrapper } from '@src/setupTest' ;
5+ import { renderWithAllProviders } from '@src/setupTest' ;
6+ import { useValidateUserPermissionsNonSuspense } from '@src/data/hooks' ;
7+ import { CONTENT_COURSE_PERMISSIONS , CONTENT_LIBRARY_PERMISSIONS } from '../roles-permissions' ;
68import AddRoleButton from './AddRoleButton' ;
79
810// Mock react-router-dom navigation
@@ -11,6 +13,20 @@ jest.mock('react-router-dom', () => ({
1113 useNavigate : jest . fn ( ) ,
1214} ) ) ;
1315
16+ jest . mock ( '@src/data/hooks' , ( ) => ( {
17+ ...jest . requireActual ( '@src/data/hooks' ) ,
18+ useValidateUserPermissionsNonSuspense : jest . fn ( ) ,
19+ } ) ) ;
20+
21+ const mockUseValidatePermissions = useValidateUserPermissionsNonSuspense as jest . Mock ;
22+ const allowAllPermissions = {
23+ data : [
24+ { action : CONTENT_LIBRARY_PERMISSIONS . MANAGE_LIBRARY_TEAM , allowed : true } ,
25+ { action : CONTENT_COURSE_PERMISSIONS . MANAGE_COURSE_TEAM , allowed : true } ,
26+ ] ,
27+ isLoading : false ,
28+ } ;
29+
1430describe ( 'AddRoleButton' , ( ) => {
1531 const mockNavigate = jest . fn ( ) ;
1632
@@ -26,6 +42,7 @@ describe('AddRoleButton', () => {
2642
2743 beforeEach ( ( ) => {
2844 ( useNavigate as jest . Mock ) . mockReturnValue ( mockNavigate ) ;
45+ mockUseValidatePermissions . mockReturnValue ( allowAllPermissions ) ;
2946 } ) ;
3047
3148 afterEach ( ( ) => {
@@ -34,21 +51,21 @@ describe('AddRoleButton', () => {
3451
3552 describe ( 'rendering' , ( ) => {
3653 it ( 'renders the assign role button with correct text' , ( ) => {
37- renderWrapper ( < AddRoleButton /> ) ;
54+ renderWithAllProviders ( < AddRoleButton /> ) ;
3855
3956 const button = screen . getByRole ( 'button' , { name : / a s s i g n r o l e / i } ) ;
4057 expect ( button ) . toBeInTheDocument ( ) ;
4158 } ) ;
4259
4360 it ( 'displays the plus icon' , ( ) => {
44- renderWrapper ( < AddRoleButton /> ) ;
61+ renderWithAllProviders ( < AddRoleButton /> ) ;
4562
4663 const button = screen . getByRole ( 'button' , { name : / a s s i g n r o l e / i } ) ;
4764 expect ( button . querySelector ( 'svg' ) ) . toBeInTheDocument ( ) ;
4865 } ) ;
4966
5067 it ( 'renders correctly when presetUsername is provided' , ( ) => {
51- renderWrapper ( < AddRoleButton presetUsername = "testuser123" /> ) ;
68+ renderWithAllProviders ( < AddRoleButton presetUsername = "testuser123" /> ) ;
5269
5370 const button = screen . getByRole ( 'button' , { name : / a s s i g n r o l e / i } ) ;
5471 expect ( button ) . toBeInTheDocument ( ) ;
@@ -58,7 +75,7 @@ describe('AddRoleButton', () => {
5875 describe ( 'navigation behavior' , ( ) => {
5976 it ( 'navigates to assign role page without username when clicked' , async ( ) => {
6077 const user = userEvent . setup ( ) ;
61- renderWrapper ( < AddRoleButton /> ) ;
78+ renderWithAllProviders ( < AddRoleButton /> ) ;
6279
6380 const button = screen . getByRole ( 'button' , { name : / a s s i g n r o l e / i } ) ;
6481 await user . click ( button ) ;
@@ -70,7 +87,7 @@ describe('AddRoleButton', () => {
7087 it ( 'navigates to assign role page with username query parameter when presetUsername is provided' , async ( ) => {
7188 const user = userEvent . setup ( ) ;
7289 const presetUsername = 'john.doe' ;
73- renderWrapper ( < AddRoleButton presetUsername = { presetUsername } /> ) ;
90+ renderWithAllProviders ( < AddRoleButton presetUsername = { presetUsername } /> ) ;
7491
7592 const button = screen . getByRole ( 'button' , { name : / a s s i g n r o l e / i } ) ;
7693 await user . click ( button ) ;
@@ -82,7 +99,7 @@ describe('AddRoleButton', () => {
8299 it ( 'handles special characters in presetUsername correctly' , async ( ) => {
83100 const user = userEvent . setup ( ) ;
84101 const presetUsername = 'user@example.com' ;
85- renderWrapper ( < AddRoleButton presetUsername = { presetUsername } /> ) ;
102+ renderWithAllProviders ( < AddRoleButton presetUsername = { presetUsername } /> ) ;
86103
87104 const button = screen . getByRole ( 'button' , { name : / a s s i g n r o l e / i } ) ;
88105 await user . click ( button ) ;
@@ -95,7 +112,7 @@ describe('AddRoleButton', () => {
95112 describe ( 'user interactions' , ( ) => {
96113 it ( 'responds to keyboard navigation' , async ( ) => {
97114 const user = userEvent . setup ( ) ;
98- renderWrapper ( < AddRoleButton /> ) ;
115+ renderWithAllProviders ( < AddRoleButton /> ) ;
99116
100117 const button = screen . getByRole ( 'button' , { name : / a s s i g n r o l e / i } ) ;
101118
@@ -108,7 +125,7 @@ describe('AddRoleButton', () => {
108125
109126 it ( 'responds to spacebar activation' , async ( ) => {
110127 const user = userEvent . setup ( ) ;
111- renderWrapper ( < AddRoleButton /> ) ;
128+ renderWithAllProviders ( < AddRoleButton /> ) ;
112129
113130 const button = screen . getByRole ( 'button' , { name : / a s s i g n r o l e / i } ) ;
114131 button . focus ( ) ;
@@ -119,7 +136,7 @@ describe('AddRoleButton', () => {
119136
120137 it ( 'handles multiple clicks gracefully' , async ( ) => {
121138 const user = userEvent . setup ( ) ;
122- renderWrapper ( < AddRoleButton presetUsername = "testuser" /> ) ;
139+ renderWithAllProviders ( < AddRoleButton presetUsername = "testuser" /> ) ;
123140
124141 const button = screen . getByRole ( 'button' , { name : / a s s i g n r o l e / i } ) ;
125142
@@ -131,4 +148,39 @@ describe('AddRoleButton', () => {
131148 expect ( mockNavigate ) . toHaveBeenCalledWith ( '/authz/assign-role?users=testuser' ) ;
132149 } ) ;
133150 } ) ;
151+
152+ describe ( 'permission gating' , ( ) => {
153+ it ( 'does not render the button when the user cannot manage any team' , ( ) => {
154+ mockUseValidatePermissions . mockReturnValue ( {
155+ data : [
156+ { action : CONTENT_LIBRARY_PERMISSIONS . MANAGE_LIBRARY_TEAM , allowed : false } ,
157+ { action : CONTENT_COURSE_PERMISSIONS . MANAGE_COURSE_TEAM , allowed : false } ,
158+ ] ,
159+ isLoading : false ,
160+ } ) ;
161+ renderWithAllProviders ( < AddRoleButton /> ) ;
162+
163+ expect ( screen . queryByRole ( 'button' , { name : / a s s i g n r o l e / i } ) ) . not . toBeInTheDocument ( ) ;
164+ } ) ;
165+
166+ it ( 'renders the button when at least one team-management permission is allowed' , ( ) => {
167+ mockUseValidatePermissions . mockReturnValue ( {
168+ data : [
169+ { action : CONTENT_LIBRARY_PERMISSIONS . MANAGE_LIBRARY_TEAM , allowed : false } ,
170+ { action : CONTENT_COURSE_PERMISSIONS . MANAGE_COURSE_TEAM , allowed : true } ,
171+ ] ,
172+ isLoading : false ,
173+ } ) ;
174+ renderWithAllProviders ( < AddRoleButton /> ) ;
175+
176+ expect ( screen . getByRole ( 'button' , { name : / a s s i g n r o l e / i } ) ) . toBeInTheDocument ( ) ;
177+ } ) ;
178+
179+ it ( 'does not render the button while permissions are loading' , ( ) => {
180+ mockUseValidatePermissions . mockReturnValue ( { data : undefined , isLoading : true } ) ;
181+ renderWithAllProviders ( < AddRoleButton /> ) ;
182+
183+ expect ( screen . queryByRole ( 'button' , { name : / a s s i g n r o l e / i } ) ) . not . toBeInTheDocument ( ) ;
184+ } ) ;
185+ } ) ;
134186} ) ;
0 commit comments