@@ -2,10 +2,11 @@ import type { CustomPage } from '@clerk/shared/types';
22import { describe , expect , it } from 'vitest' ;
33
44import { bindCreateFixtures } from '@/test/create-fixtures' ;
5- import { cleanup , render , screen , waitFor } from '@/test/utils' ;
5+ import { render , screen , waitFor } from '@/test/utils' ;
6+ import { VirtualRouter } from '@/ui/router' ;
67
7- import { OrganizationProfile } from '../ ' ;
8- import { OrganizationSecurityPage } from '../OrganizationSecurityPage ' ;
8+ import { OrganizationProfile } from '..' ;
9+ import { OrganizationProfileRoutes } from '../OrganizationProfileRoutes ' ;
910
1011const { createFixtures } = bindCreateFixtures ( 'OrganizationProfile' ) ;
1112
@@ -538,8 +539,8 @@ describe('OrganizationProfile', () => {
538539 await waitFor ( ( ) => expect ( queryByText ( 'Security' ) ) . toBeNull ( ) ) ;
539540 } ) ;
540541
541- it ( 'includes SSO even when the user does not have the manage enterprise connections permission, but the page surfaces a warning ' , async ( ) => {
542- const { wrapper, fixtures } = await createFixtures ( f => {
542+ it ( 'does not include SSO when the user lacks the manage enterprise connections permission' , async ( ) => {
543+ const { wrapper } = await createFixtures ( f => {
543544 f . withEnterpriseSso ( { selfServeSSO : true } ) ;
544545 f . withOrganizations ( ) ;
545546 f . withUser ( {
@@ -554,17 +555,56 @@ describe('OrganizationProfile', () => {
554555 } ) ;
555556 } ) ;
556557
557- fixtures . clerk . user ?. getEnterpriseConnections . mockResolvedValue ( [ ] ) ;
558+ const { queryByText } = render ( < OrganizationProfile /> , { wrapper } ) ;
559+ expect ( queryByText ( 'Security' ) ) . not . toBeInTheDocument ( ) ;
560+ } ) ;
561+ } ) ;
558562
559- render ( < OrganizationProfile /> , { wrapper } ) ;
560- expect ( await screen . findByText ( 'Security' ) ) . toBeDefined ( ) ;
563+ describe ( 'SSO route guard' , ( ) => {
564+ const SECURITY_DESCRIPTION =
565+ 'Require members with a matching email domain to sign in through your identity provider.' ;
566+
567+ // The navbar tests above only prove the link is hidden. This block exercises the
568+ // route guard wrapping the security route, so direct navigation is blocked too.
569+ // The shared fixture wrapper installs a mocked RouteContext that never matches a
570+ // path, so the guarded sub-route only mounts under a real router seeded to its path.
571+ const renderSecurityRoute = ( wrapper : React . ComponentType < { children ?: React . ReactNode } > ) =>
572+ render (
573+ < VirtualRouter startPath = '/organization-security' >
574+ < OrganizationProfileRoutes contentRef = { { current : null } } />
575+ </ VirtualRouter > ,
576+ { wrapper } ,
577+ ) ;
578+
579+ const withSelfServeSSO = ( permissions : string [ ] ) => ( f : Parameters < Parameters < typeof createFixtures > [ 0 ] > [ 0 ] ) => {
580+ f . withEnterpriseSso ( { selfServeSSO : true } ) ;
581+ f . withEmailAddress ( ) ;
582+ f . withOrganizations ( ) ;
583+ f . withOrganizationDomains ( undefined , 'org:member' ) ;
584+ f . withUser ( {
585+ email_addresses : [ 'test@clerk.com' ] ,
586+ organization_memberships : [ { name : 'Org1' , self_serve_sso_enabled : true , permissions } ] ,
587+ } ) ;
588+ } ;
589+
590+ it ( 'renders the security page on the guarded route when the user can manage enterprise connections' , async ( ) => {
591+ const { wrapper, fixtures } = await createFixtures ( withSelfServeSSO ( [ 'org:sys_entconns:manage' ] ) ) ;
592+ fixtures . clerk . organization ?. getEnterpriseConnections . mockResolvedValue ( [ ] ) ;
593+
594+ renderSecurityRoute ( wrapper ) ;
595+
596+ expect ( await screen . findByText ( SECURITY_DESCRIPTION ) ) . toBeInTheDocument ( ) ;
597+ expect ( screen . getByRole ( 'button' , { name : 'Start configuration' } ) ) . toBeInTheDocument ( ) ;
598+ } ) ;
599+
600+ it ( 'blocks the security page on the guarded route when the user lacks the manage enterprise connections permission' , async ( ) => {
601+ const { wrapper, fixtures } = await createFixtures ( withSelfServeSSO ( [ ] ) ) ;
602+ fixtures . clerk . organization ?. getEnterpriseConnections . mockResolvedValue ( [ ] ) ;
603+
604+ renderSecurityRoute ( wrapper ) ;
561605
562- cleanup ( ) ;
563- render ( < OrganizationSecurityPage /> , { wrapper } ) ;
564- expect ( await screen . findByText ( / y o u d o n o t h a v e p e r m i s s i o n t o m a n a g e s i n g l e s i g n - o n / i) ) . toBeDefined ( ) ;
565- expect (
566- screen . queryByText ( / c o n t a c t y o u r o r g a n i z a t i o n .* a d m i n i s t r a t o r t o u p g r a d e y o u r p e r m i s s i o n s / i) ,
567- ) . toBeInTheDocument ( ) ;
606+ await waitFor ( ( ) => expect ( screen . queryByText ( SECURITY_DESCRIPTION ) ) . not . toBeInTheDocument ( ) ) ;
607+ expect ( screen . queryByRole ( 'button' , { name : 'Start configuration' } ) ) . not . toBeInTheDocument ( ) ;
568608 } ) ;
569609 } ) ;
570610
0 commit comments