@@ -4,7 +4,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
44import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth' ;
55import {
66 useLibrary , usePermissionsByRole , useTeamMembers , useAssignTeamMembersRole , useRevokeUserRoles ,
7- useValidateUsers ,
7+ useValidateUsers , useScopes , useOrganizations ,
88} from './hooks' ;
99
1010jest . mock ( '@edx/frontend-platform/auth' , ( ) => ( {
@@ -290,6 +290,128 @@ describe('useValidateUsers', () => {
290290 } ) ;
291291} ) ;
292292
293+ describe ( 'useScopes' , ( ) => {
294+ beforeEach ( ( ) => {
295+ jest . clearAllMocks ( ) ;
296+ } ) ;
297+
298+ const makeScopesResponse = ( next : string | null = null ) => ( {
299+ results : [ {
300+ externalKey : 'lib:testorg:testlib' ,
301+ displayName : 'Test Library' ,
302+ org : { id : 1 , name : 'Test Org' , slug : 'testorg' } ,
303+ } ] ,
304+ count : 1 ,
305+ next,
306+ previous : null ,
307+ } ) ;
308+
309+ it ( 'returns pages data on success' , async ( ) => {
310+ getAuthenticatedHttpClient . mockReturnValue ( {
311+ get : jest . fn ( ) . mockResolvedValue ( { data : makeScopesResponse ( ) } ) ,
312+ } ) ;
313+
314+ const { result } = renderHook ( ( ) => useScopes ( { } ) , { wrapper : createWrapper ( ) } ) ;
315+
316+ await waitFor ( ( ) => expect ( result . current . isSuccess ) . toBe ( true ) ) ;
317+
318+ expect ( result . current . data ?. pages ) . toHaveLength ( 1 ) ;
319+ expect ( result . current . data ?. pages [ 0 ] . results ) . toHaveLength ( 1 ) ;
320+ } ) ;
321+
322+ it ( 'hasNextPage is false when next is null' , async ( ) => {
323+ getAuthenticatedHttpClient . mockReturnValue ( {
324+ get : jest . fn ( ) . mockResolvedValue ( { data : makeScopesResponse ( null ) } ) ,
325+ } ) ;
326+
327+ const { result } = renderHook ( ( ) => useScopes ( { } ) , { wrapper : createWrapper ( ) } ) ;
328+
329+ await waitFor ( ( ) => expect ( result . current . isSuccess ) . toBe ( true ) ) ;
330+ expect ( result . current . hasNextPage ) . toBe ( false ) ;
331+ } ) ;
332+
333+ it ( 'hasNextPage is true when next URL has page param' , async ( ) => {
334+ getAuthenticatedHttpClient . mockReturnValue ( {
335+ get : jest . fn ( ) . mockResolvedValue ( {
336+ data : makeScopesResponse ( 'http://localhost:8000/api/authz/v1/scopes/?page=2' ) ,
337+ } ) ,
338+ } ) ;
339+
340+ const { result } = renderHook ( ( ) => useScopes ( { } ) , { wrapper : createWrapper ( ) } ) ;
341+
342+ await waitFor ( ( ) => expect ( result . current . isSuccess ) . toBe ( true ) ) ;
343+ expect ( result . current . hasNextPage ) . toBe ( true ) ;
344+ } ) ;
345+
346+ it ( 'hasNextPage is false when next URL has no page param' , async ( ) => {
347+ getAuthenticatedHttpClient . mockReturnValue ( {
348+ get : jest . fn ( ) . mockResolvedValue ( {
349+ data : makeScopesResponse ( 'http://localhost:8000/api/authz/v1/scopes/' ) ,
350+ } ) ,
351+ } ) ;
352+
353+ const { result } = renderHook ( ( ) => useScopes ( { } ) , { wrapper : createWrapper ( ) } ) ;
354+
355+ await waitFor ( ( ) => expect ( result . current . isSuccess ) . toBe ( true ) ) ;
356+ expect ( result . current . hasNextPage ) . toBe ( false ) ;
357+ } ) ;
358+
359+ it ( 'hasNextPage is false when next is an invalid URL' , async ( ) => {
360+ getAuthenticatedHttpClient . mockReturnValue ( {
361+ get : jest . fn ( ) . mockResolvedValue ( {
362+ data : makeScopesResponse ( 'not-a-valid-url' ) ,
363+ } ) ,
364+ } ) ;
365+
366+ const { result } = renderHook ( ( ) => useScopes ( { } ) , { wrapper : createWrapper ( ) } ) ;
367+
368+ await waitFor ( ( ) => expect ( result . current . isSuccess ) . toBe ( true ) ) ;
369+ expect ( result . current . hasNextPage ) . toBe ( false ) ;
370+ } ) ;
371+
372+ it ( 'handles error when API call fails' , async ( ) => {
373+ getAuthenticatedHttpClient . mockReturnValue ( {
374+ get : jest . fn ( ) . mockRejectedValue ( new Error ( 'Network error' ) ) ,
375+ } ) ;
376+
377+ const { result } = renderHook ( ( ) => useScopes ( { } ) , { wrapper : createWrapper ( ) } ) ;
378+
379+ await waitFor ( ( ) => expect ( result . current . isError ) . toBe ( true ) ) ;
380+ expect ( result . current . error ) . toBeDefined ( ) ;
381+ } ) ;
382+ } ) ;
383+
384+ describe ( 'useOrganizations' , ( ) => {
385+ beforeEach ( ( ) => {
386+ jest . clearAllMocks ( ) ;
387+ } ) ;
388+
389+ const mockOrgs = [ {
390+ id : 1 , name : 'Org One' , shortName : 'org1' , description : '' , logo : null , active : true ,
391+ } ] ;
392+
393+ it ( 'returns organizations on success' , async ( ) => {
394+ getAuthenticatedHttpClient . mockReturnValue ( {
395+ get : jest . fn ( ) . mockResolvedValue ( { data : { results : mockOrgs } } ) ,
396+ } ) ;
397+
398+ const { result } = renderHook ( ( ) => useOrganizations ( ) , { wrapper : createWrapper ( ) } ) ;
399+
400+ await waitFor ( ( ) => expect ( result . current . isSuccess ) . toBe ( true ) ) ;
401+ expect ( result . current . data ) . toEqual ( mockOrgs ) ;
402+ } ) ;
403+
404+ it ( 'handles error when API fails' , async ( ) => {
405+ getAuthenticatedHttpClient . mockReturnValue ( {
406+ get : jest . fn ( ) . mockRejectedValue ( new Error ( 'Failed' ) ) ,
407+ } ) ;
408+
409+ const { result } = renderHook ( ( ) => useOrganizations ( ) , { wrapper : createWrapper ( ) } ) ;
410+
411+ await waitFor ( ( ) => expect ( result . current . isError ) . toBe ( true ) ) ;
412+ } ) ;
413+ } ) ;
414+
293415describe ( 'useRevokeUserRoles' , ( ) => {
294416 beforeEach ( ( ) => {
295417 jest . clearAllMocks ( ) ;
0 commit comments