Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 120 additions & 19 deletions src/components/impersonation.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,6 @@ describe('Impersonation', () => {
impersonator: null,
user: { id: '123', email: '[email protected]' },
organizationId: null,
loading: false,
});

const { container } = render(<Impersonation />);
expect(container).toBeEmptyDOMElement();
});

it('should return null if loading', () => {
(useAuth as jest.Mock).mockReturnValue({
impersonator: { email: '[email protected]' },
user: { id: '123', email: '[email protected]' },
organizationId: null,
loading: true,
});

const { container } = render(<Impersonation />);
Expand All @@ -51,7 +38,6 @@ describe('Impersonation', () => {
impersonator: { email: '[email protected]' },
user: { id: '123', email: '[email protected]' },
organizationId: null,
loading: false,
});

const { container } = render(<Impersonation />);
Expand All @@ -63,7 +49,6 @@ describe('Impersonation', () => {
impersonator: { email: '[email protected]' },
user: { id: '123', email: '[email protected]' },
organizationId: 'org_123',
loading: false,
});

(getOrganizationAction as jest.Mock).mockResolvedValue({
Expand All @@ -83,7 +68,6 @@ describe('Impersonation', () => {
impersonator: { email: '[email protected]' },
user: { id: '123', email: '[email protected]' },
organizationId: null,
loading: false,
});

const { container } = render(<Impersonation />);
Expand All @@ -96,7 +80,6 @@ describe('Impersonation', () => {
impersonator: { email: '[email protected]' },
user: { id: '123', email: '[email protected]' },
organizationId: null,
loading: false,
});

const { container } = render(<Impersonation side="top" />);
Expand All @@ -109,7 +92,6 @@ describe('Impersonation', () => {
impersonator: { email: '[email protected]' },
user: { id: '123', email: '[email protected]' },
organizationId: null,
loading: false,
});

const customStyle = { backgroundColor: 'red' };
Expand All @@ -123,7 +105,6 @@ describe('Impersonation', () => {
impersonator: { email: '[email protected]' },
user: { id: '123', email: '[email protected]' },
organizationId: null,
loading: false,
});

render(<Impersonation />);
Expand All @@ -146,4 +127,124 @@ describe('Impersonation', () => {
stopButton.click();
expect(handleSignOutAction).toHaveBeenCalledWith({ returnTo });
});

it('should not call getOrganizationAction when organizationId is not provided', () => {
(useAuth as jest.Mock).mockReturnValue({
impersonator: { email: '[email protected]' },
user: { id: '123', email: '[email protected]' },
organizationId: null,
});

render(<Impersonation />);
expect(getOrganizationAction).not.toHaveBeenCalled();
});

it('should not call getOrganizationAction when impersonator is not present', () => {
(useAuth as jest.Mock).mockReturnValue({
impersonator: null,
user: { id: '123', email: '[email protected]' },
organizationId: 'org_123',
});

render(<Impersonation />);
expect(getOrganizationAction).not.toHaveBeenCalled();
});

it('should not call getOrganizationAction when user is not present', () => {
(useAuth as jest.Mock).mockReturnValue({
impersonator: { email: '[email protected]' },
user: null,
organizationId: 'org_123',
});

render(<Impersonation />);
expect(getOrganizationAction).not.toHaveBeenCalled();
});

it('should not call getOrganizationAction again when organization is already loaded with same ID', async () => {
const mockOrg = {
id: 'org_123',
name: 'Test Org',
};

(getOrganizationAction as jest.Mock).mockResolvedValue(mockOrg);

const { rerender } = await act(async () => {
(useAuth as jest.Mock).mockReturnValue({
impersonator: { email: '[email protected]' },
user: { id: '123', email: '[email protected]' },
organizationId: 'org_123',
});

return render(<Impersonation />);
});

// Wait for the initial call to complete
await act(async () => {
await new Promise((resolve) => setTimeout(resolve, 0));
});

expect(getOrganizationAction).toHaveBeenCalledTimes(1);

// Rerender with the same organizationId
await act(async () => {
(useAuth as jest.Mock).mockReturnValue({
impersonator: { email: '[email protected]' },
user: { id: '123', email: '[email protected]' },
organizationId: 'org_123',
});

rerender(<Impersonation />);
});

// Should still be called only once
expect(getOrganizationAction).toHaveBeenCalledTimes(1);
});

it('should call getOrganizationAction again when organizationId changes', async () => {
const mockOrg1 = {
id: 'org_123',
name: 'Test Org 1',
};

const mockOrg2 = {
id: 'org_456',
name: 'Test Org 2',
};

(getOrganizationAction as jest.Mock).mockResolvedValueOnce(mockOrg1).mockResolvedValueOnce(mockOrg2);

const { rerender } = await act(async () => {
(useAuth as jest.Mock).mockReturnValue({
impersonator: { email: '[email protected]' },
user: { id: '123', email: '[email protected]' },
organizationId: 'org_123',
});

return render(<Impersonation />);
});

// Wait for the initial call to complete
await act(async () => {
await new Promise((resolve) => setTimeout(resolve, 0));
});

expect(getOrganizationAction).toHaveBeenCalledTimes(1);
expect(getOrganizationAction).toHaveBeenCalledWith('org_123');

// Rerender with a different organizationId
await act(async () => {
(useAuth as jest.Mock).mockReturnValue({
impersonator: { email: '[email protected]' },
user: { id: '123', email: '[email protected]' },
organizationId: 'org_456',
});

rerender(<Impersonation />);
});

// Should be called again with the new ID
expect(getOrganizationAction).toHaveBeenCalledTimes(2);
expect(getOrganizationAction).toHaveBeenCalledWith('org_456');
});
});
9 changes: 5 additions & 4 deletions src/components/impersonation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@ interface ImpersonationProps extends React.ComponentPropsWithoutRef<'div'> {
}

export function Impersonation({ side = 'bottom', returnTo, ...props }: ImpersonationProps) {
const { user, impersonator, organizationId, loading } = useAuth();
const { user, impersonator, organizationId } = useAuth();

const [organization, setOrganization] = React.useState<Organization | null>(null);

React.useEffect(() => {
if (!organizationId) return;
if (!organizationId || !impersonator || !user) return;
if (organization && organization.id === organizationId) return;
getOrganizationAction(organizationId).then(setOrganization);
}, [organizationId]);
}, [organizationId, impersonator, user]);

if (loading || !impersonator || !user) return null;
if (!impersonator || !user) return null;

return (
<div
Expand Down