Skip to content

Commit

Permalink
fix(clerk-js): Hide Add domain button when user is missing `org:sys_d…
Browse files Browse the repository at this point in the history
…omains:manage`
  • Loading branch information
panteliselef committed Nov 30, 2023
1 parent 0551488 commit de597b0
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 55 deletions.
5 changes: 5 additions & 0 deletions .changeset/strong-cows-sit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/clerk-js': patch
---

Hide "Add domain" button inside `<OrganizationProfile/>` when user is missing the `org:sys_domains:manage` permission.
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,13 @@ const OrganizationDomainsSection = () => {
>
<DomainList redirectSubPath={'domain'} />

<AddBlockButton
textLocalizationKey={localizationKeys('organizationProfile.profilePage.domainSection.primaryButton')}
id='addOrganizationDomain'
onClick={() => navigate('domain')}
/>
<Gate permission='org:sys_domains:manage'>
<AddBlockButton
textLocalizationKey={localizationKeys('organizationProfile.profilePage.domainSection.primaryButton')}
id='addOrganizationDomain'
onClick={() => navigate('domain')}
/>
</Gate>
</ProfileSection>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { OrganizationDomainResource, OrganizationMembershipResource } from '@clerk/types';
import type { ClerkPaginatedResponse, OrganizationDomainResource, OrganizationMembershipResource } from '@clerk/types';
import { describe, it } from '@jest/globals';
import userEvent from '@testing-library/user-event';
import React from 'react';

import { act, render, waitFor } from '../../../../testUtils';
import { bindCreateFixtures } from '../../../utils/test/createFixtures';
Expand Down Expand Up @@ -30,12 +31,14 @@ describe('OrganizationSettings', () => {
);

const { getByText } = render(<OrganizationSettings />, { wrapper });

await waitFor(() => {
expect(fixtures.clerk.organization?.getMemberships).toHaveBeenCalled();
expect(getByText('Settings')).toBeDefined();
expect(getByText('Org1', { exact: false }).closest('button')).not.toBeNull();
expect(getByText(/leave organization/i, { exact: false }).closest('button')).toHaveAttribute('disabled');
});

expect(getByText('Settings')).toBeDefined();
expect(getByText('Org1', { exact: false }).closest('button')).not.toBeNull();
expect(getByText(/leave organization/i, { exact: false }).closest('button')).toHaveAttribute('disabled');
});

it('enables organization profile button and enables leave when user is admin and there is more', async () => {
Expand Down Expand Up @@ -63,7 +66,10 @@ describe('OrganizationSettings', () => {
});

it.skip('disables organization profile button and enables leave when user is not admin', async () => {
const adminsList: OrganizationMembershipResource[] = [createFakeMember({ id: '1', orgId: '1', role: 'admin' })];
const adminsList: ClerkPaginatedResponse<OrganizationMembershipResource> = {
data: [createFakeMember({ id: '1', orgId: '1', role: 'admin' })],
total_count: 1,
};

const { wrapper, fixtures } = await createFixtures(f => {
f.withOrganizations();
Expand Down Expand Up @@ -98,7 +104,7 @@ describe('OrganizationSettings', () => {
expect(fixtures.clerk.organization?.getDomains).not.toBeCalled();
});

it('shows domains when `read` permission exists', async () => {
it('shows domains when `read` permission exists but hides the Add domain button', async () => {
const { wrapper, fixtures } = await createFixtures(f => {
f.withOrganizations();
f.withOrganizationDomains();
Expand All @@ -117,6 +123,30 @@ describe('OrganizationSettings', () => {

await new Promise(r => setTimeout(r, 100));
expect(queryByText('Verified domains')).toBeInTheDocument();
expect(queryByText('Add domain')).not.toBeInTheDocument();
expect(fixtures.clerk.organization?.getDomains).toBeCalled();
});

it('shows domains and shows the Add domain button when `org:sys_domains:manage` exists', async () => {
const { wrapper, fixtures } = await createFixtures(f => {
f.withOrganizations();
f.withOrganizationDomains();
f.withUser({
email_addresses: ['[email protected]'],
organization_memberships: [{ name: 'Org1', permissions: ['org:sys_domains:read', 'org:sys_domains:manage'] }],
});
});
fixtures.clerk.organization?.getDomains.mockReturnValue(
Promise.resolve({
data: [],
total_count: 0,
}),
);
const { queryByText } = await act(() => render(<OrganizationSettings />, { wrapper }));

await new Promise(r => setTimeout(r, 100));
expect(queryByText('Verified domains')).toBeInTheDocument();
expect(queryByText('Add domain')).toBeInTheDocument();
expect(fixtures.clerk.organization?.getDomains).toBeCalled();
});

Expand Down Expand Up @@ -156,18 +186,21 @@ describe('OrganizationSettings', () => {
});

it.skip('disabled leave organization button with delete organization button', async () => {
const adminsList: OrganizationMembershipResource[] = [
createFakeMember({
id: '1',
orgId: '1',
role: 'admin',
}),
createFakeMember({
id: '2',
orgId: '1',
role: 'admin',
}),
];
const adminsList: ClerkPaginatedResponse<OrganizationMembershipResource> = {
data: [
createFakeMember({
id: '1',
orgId: '1',
role: 'admin',
}),
createFakeMember({
id: '2',
orgId: '1',
role: 'admin',
}),
],
total_count: 2,
};

const { wrapper, fixtures } = await createFixtures(f => {
f.withOrganizations();
Expand Down Expand Up @@ -212,21 +245,18 @@ describe('OrganizationSettings', () => {
expect(fixtures.router.navigate).toHaveBeenCalledWith('profile');
});

it('navigates to Leave Organization page when clicking on the respective button and user is not admin', async () => {
const adminsList: OrganizationMembershipResource[] = [createFakeMember({ id: '1', orgId: '1', role: 'admin' })];

// TODO(@panteliselef): Update this test to allow user to leave an org, only if there will be at least one person left with the minimum set of permissions
it('navigates to Leave Organization page when clicking on the respective button', async () => {
const { wrapper, fixtures } = await createFixtures(f => {
f.withOrganizations();
f.withUser({
email_addresses: ['[email protected]'],
organization_memberships: [{ name: 'Org1', role: 'basic_member' }],
organization_memberships: [{ name: 'Org1', permissions: [] }],
});
});

fixtures.clerk.organization?.getMemberships.mockReturnValue(Promise.resolve(adminsList));
const { findByText } = render(<OrganizationSettings />, { wrapper });
await waitFor(async () => {
// expect(fixtures.clerk.organization?.getMemberships).toHaveBeenCalled();
await userEvent.click(await findByText(/leave organization/i, { exact: false }));
});
expect(fixtures.router.navigate).toHaveBeenCalledWith('leave');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,13 @@ const OrganizationDomainsSection = () => {
>
<DomainList redirectSubPath={'domain'} />

<AddBlockButton
textLocalizationKey={localizationKeys('organizationProfile.profilePage.domainSection.primaryButton')}
id='addOrganizationDomain'
onClick={() => navigate('domain')}
/>
<Gate permission='org:sys_domains:manage'>
<AddBlockButton
textLocalizationKey={localizationKeys('organizationProfile.profilePage.domainSection.primaryButton')}
id='addOrganizationDomain'
onClick={() => navigate('domain')}
/>
</Gate>
</ProfileSection>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { OrganizationDomainResource, OrganizationMembershipResource } from '@clerk/types';
import type { ClerkPaginatedResponse, OrganizationDomainResource, OrganizationMembershipResource } from '@clerk/types';
import { describe, it } from '@jest/globals';
import userEvent from '@testing-library/user-event';
import React from 'react';
Expand Down Expand Up @@ -66,7 +66,10 @@ describe('OrganizationSettings', () => {
});

it.skip('disables organization profile button and enables leave when user is not admin', async () => {
const adminsList: OrganizationMembershipResource[] = [createFakeMember({ id: '1', orgId: '1', role: 'admin' })];
const adminsList: ClerkPaginatedResponse<OrganizationMembershipResource> = {
data: [createFakeMember({ id: '1', orgId: '1', role: 'admin' })],
total_count: 1,
};

const { wrapper, fixtures } = await createFixtures(f => {
f.withOrganizations();
Expand Down Expand Up @@ -101,7 +104,7 @@ describe('OrganizationSettings', () => {
expect(fixtures.clerk.organization?.getDomains).not.toBeCalled();
});

it('shows domains when `read` permission exists', async () => {
it('shows domains when `read` permission exists but hides the Add domain button', async () => {
const { wrapper, fixtures } = await createFixtures(f => {
f.withOrganizations();
f.withOrganizationDomains();
Expand All @@ -120,6 +123,30 @@ describe('OrganizationSettings', () => {

await new Promise(r => setTimeout(r, 100));
expect(queryByText('Verified domains')).toBeInTheDocument();
expect(queryByText('Add domain')).not.toBeInTheDocument();
expect(fixtures.clerk.organization?.getDomains).toBeCalled();
});

it('shows domains and shows the Add domain button when `org:sys_domains:manage` exists', async () => {
const { wrapper, fixtures } = await createFixtures(f => {
f.withOrganizations();
f.withOrganizationDomains();
f.withUser({
email_addresses: ['[email protected]'],
organization_memberships: [{ name: 'Org1', permissions: ['org:sys_domains:read', 'org:sys_domains:manage'] }],
});
});
fixtures.clerk.organization?.getDomains.mockReturnValue(
Promise.resolve({
data: [],
total_count: 0,
}),
);
const { queryByText } = await act(() => render(<OrganizationSettings />, { wrapper }));

await new Promise(r => setTimeout(r, 100));
expect(queryByText('Verified domains')).toBeInTheDocument();
expect(queryByText('Add domain')).toBeInTheDocument();
expect(fixtures.clerk.organization?.getDomains).toBeCalled();
});

Expand Down Expand Up @@ -159,18 +186,21 @@ describe('OrganizationSettings', () => {
});

it.skip('disabled leave organization button with delete organization button', async () => {
const adminsList: OrganizationMembershipResource[] = [
createFakeMember({
id: '1',
orgId: '1',
role: 'admin',
}),
createFakeMember({
id: '2',
orgId: '1',
role: 'admin',
}),
];
const adminsList: ClerkPaginatedResponse<OrganizationMembershipResource> = {
data: [
createFakeMember({
id: '1',
orgId: '1',
role: 'admin',
}),
createFakeMember({
id: '2',
orgId: '1',
role: 'admin',
}),
],
total_count: 2,
};

const { wrapper, fixtures } = await createFixtures(f => {
f.withOrganizations();
Expand Down Expand Up @@ -215,21 +245,18 @@ describe('OrganizationSettings', () => {
expect(fixtures.router.navigate).toHaveBeenCalledWith('profile');
});

it('navigates to Leave Organization page when clicking on the respective button and user is not admin', async () => {
const adminsList: OrganizationMembershipResource[] = [createFakeMember({ id: '1', orgId: '1', role: 'admin' })];

// TODO(@panteliselef): Update this test to allow user to leave an org, only if there will be at least one person left with the minimum set of permissions
it('navigates to Leave Organization page when clicking on the respective button', async () => {
const { wrapper, fixtures } = await createFixtures(f => {
f.withOrganizations();
f.withUser({
email_addresses: ['[email protected]'],
organization_memberships: [{ name: 'Org1', role: 'basic_member' }],
organization_memberships: [{ name: 'Org1', permissions: [] }],
});
});

fixtures.clerk.organization?.getMemberships.mockReturnValue(Promise.resolve(adminsList));
const { findByText } = render(<OrganizationSettings />, { wrapper });
await waitFor(async () => {
// expect(fixtures.clerk.organization?.getMemberships).toHaveBeenCalled();
await userEvent.click(await findByText(/leave organization/i, { exact: false }));
});
expect(fixtures.router.navigate).toHaveBeenCalledWith('leave');
Expand Down

0 comments on commit de597b0

Please sign in to comment.