portainer/app/react/sidebar/SettingsSidebar.test.tsx

393 lines
12 KiB
TypeScript

import { render, screen } from '@testing-library/react';
import { vi } from 'vitest';
import { UserViewModel } from '@/portainer/models/user';
import { withUserProvider } from '@/react/test-utils/withUserProvider';
import { withTestRouter } from '@/react/test-utils/withRouter';
import * as featureFlags from '@/react/portainer/feature-flags/feature-flags.service';
import { usePublicSettings } from '@/react/portainer/settings/queries';
import { PublicSettingsResponse } from '@/react/portainer/settings/types';
import { TestSidebarProvider } from './useSidebarState';
import { SettingsSidebar } from './SettingsSidebar';
vi.mock('@/react/portainer/settings/queries', () => ({
usePublicSettings: vi.fn(),
}));
describe('SettingsSidebar', () => {
beforeEach(() => {
vi.spyOn(featureFlags, 'isBE', 'get').mockReturnValue(false);
// Default mock for usePublicSettings - returns data based on selector
vi.mocked(usePublicSettings).mockImplementation(((options?: {
select?: (settings: PublicSettingsResponse) => PublicSettingsResponse;
}) => {
const settings: PublicSettingsResponse = {
TeamSync: false,
EnableEdgeComputeFeatures: false,
} as unknown as PublicSettingsResponse;
return {
data: options?.select ? options.select(settings) : settings,
isLoading: false,
isError: false,
};
}) as typeof usePublicSettings);
window.ddExtension = false;
});
afterEach(() => {
vi.restoreAllMocks();
});
describe('Pure Admin User', () => {
it('should render all admin sections for pure admin', () => {
renderComponent({ isPureAdmin: true, isAdmin: true });
expect(screen.getByText('Administration')).toBeInTheDocument();
expect(
screen.getByTestId('portainerSidebar-userRelated')
).toBeInTheDocument();
expect(
screen.getByTestId('portainerSidebar-environments-area')
).toBeInTheDocument();
expect(
screen.getByTestId('portainerSidebar-registries')
).toBeInTheDocument();
expect(screen.getByTestId('k8sSidebar-logs')).toBeInTheDocument();
expect(
screen.getByTestId('portainerSidebar-notifications')
).toBeInTheDocument();
expect(
screen.getByTestId('portainerSidebar-settings')
).toBeInTheDocument();
});
it('should render user-related submenu items', () => {
renderComponent({ isPureAdmin: true, isAdmin: true });
expect(screen.getByTestId('portainerSidebar-users')).toBeInTheDocument();
expect(screen.getByTestId('portainerSidebar-teams')).toBeInTheDocument();
expect(screen.getByTestId('portainerSidebar-roles')).toBeInTheDocument();
});
it('should render environment-related submenu items', () => {
renderComponent({ isPureAdmin: true, isAdmin: true });
expect(
screen.getByTestId('portainerSidebar-environments')
).toBeInTheDocument();
expect(
screen.getByTestId('portainerSidebar-environmentGroups')
).toBeInTheDocument();
expect(
screen.getByTestId('portainerSidebar-environmentTags')
).toBeInTheDocument();
});
it('should render logs submenu items', () => {
renderComponent({ isPureAdmin: true, isAdmin: true });
expect(
screen.getByTestId('portainerSidebar-authLogs')
).toBeInTheDocument();
expect(
screen.getByTestId('portainerSidebar-activityLogs')
).toBeInTheDocument();
});
it('should render settings submenu items', () => {
renderComponent({ isPureAdmin: true, isAdmin: true });
expect(
screen.getByTestId('portainerSidebar-generalSettings')
).toBeInTheDocument();
expect(
screen.getByTestId('portainerSidebar-authentication')
).toBeInTheDocument();
expect(
screen.getByTestId('portainerSidebar-edgeCompute')
).toBeInTheDocument();
});
it('should render Get Help link with correct CE URL', () => {
renderComponent({ isPureAdmin: true, isAdmin: true });
const helpLink = screen.getByRole('link', { name: /Get Help/i });
expect(helpLink).toBeInTheDocument();
expect(helpLink).toHaveAttribute(
'href',
'https://www.portainer.io/community_help'
);
expect(helpLink).toHaveAttribute('target', '_blank');
expect(helpLink).toHaveAttribute('rel', 'noreferrer');
});
it('should not render Licenses sidebar item in CE', () => {
renderComponent({ isPureAdmin: true, isAdmin: true });
expect(
screen.queryByTestId('portainerSidebar-licenses')
).not.toBeInTheDocument();
});
it('should not render Shared Credentials in CE', () => {
renderComponent({ isPureAdmin: true, isAdmin: true });
expect(
screen.queryByTestId('portainerSidebar-cloud')
).not.toBeInTheDocument();
});
});
describe('Team Leader User', () => {
it('should render user-related section for team leader when TeamSync is disabled', () => {
vi.mocked(usePublicSettings).mockReturnValue({
data: false,
isLoading: false,
isError: false,
} as ReturnType<typeof usePublicSettings>);
renderComponent({
isPureAdmin: false,
isAdmin: false,
isTeamLeader: true,
});
expect(
screen.getByTestId('portainerSidebar-userRelated')
).toBeInTheDocument();
expect(screen.getByTestId('portainerSidebar-users')).toBeInTheDocument();
expect(screen.getByTestId('portainerSidebar-teams')).toBeInTheDocument();
});
it('should not render Roles for team leader', () => {
vi.mocked(usePublicSettings).mockReturnValue({
data: false,
isLoading: false,
isError: false,
} as ReturnType<typeof usePublicSettings>);
renderComponent({
isPureAdmin: false,
isAdmin: false,
isTeamLeader: true,
});
expect(
screen.queryByTestId('portainerSidebar-roles')
).not.toBeInTheDocument();
});
it('should not render user-related section when TeamSync is enabled', () => {
vi.mocked(usePublicSettings).mockReturnValue({
data: true,
isLoading: false,
isError: false,
} as ReturnType<typeof usePublicSettings>);
renderComponent({
isPureAdmin: false,
isAdmin: false,
isTeamLeader: true,
});
expect(
screen.queryByTestId('portainerSidebar-userRelated')
).not.toBeInTheDocument();
});
it('should not render admin-only sections for team leader', () => {
renderComponent({
isPureAdmin: false,
isAdmin: false,
isTeamLeader: true,
});
expect(
screen.queryByTestId('portainerSidebar-environments-area')
).not.toBeInTheDocument();
expect(
screen.queryByTestId('portainerSidebar-registries')
).not.toBeInTheDocument();
expect(screen.queryByTestId('k8sSidebar-logs')).not.toBeInTheDocument();
expect(
screen.queryByTestId('portainerSidebar-settings')
).not.toBeInTheDocument();
});
it('should render notifications for team leader', () => {
renderComponent({
isPureAdmin: false,
isAdmin: false,
isTeamLeader: true,
});
expect(
screen.getByTestId('portainerSidebar-notifications')
).toBeInTheDocument();
});
});
describe('Regular Admin User (not Pure Admin)', () => {
it('should not render admin-only sections', () => {
renderComponent({ isPureAdmin: false, isAdmin: true });
expect(
screen.queryByTestId('portainerSidebar-userRelated')
).not.toBeInTheDocument();
expect(
screen.queryByTestId('portainerSidebar-environments-area')
).not.toBeInTheDocument();
expect(
screen.queryByTestId('portainerSidebar-registries')
).not.toBeInTheDocument();
expect(screen.queryByTestId('k8sSidebar-logs')).not.toBeInTheDocument();
expect(
screen.queryByTestId('portainerSidebar-settings')
).not.toBeInTheDocument();
});
it('should render notifications for regular admin', () => {
renderComponent({ isPureAdmin: false, isAdmin: true });
expect(
screen.getByTestId('portainerSidebar-notifications')
).toBeInTheDocument();
});
});
describe('DD Extension Environment', () => {
it('should not render user-related section when ddExtension is enabled', () => {
window.ddExtension = true;
renderComponent({ isPureAdmin: true, isAdmin: true });
expect(
screen.queryByTestId('portainerSidebar-userRelated')
).not.toBeInTheDocument();
});
it('should not render authentication settings when ddExtension is enabled', () => {
window.ddExtension = true;
renderComponent({ isPureAdmin: true, isAdmin: true });
expect(
screen.queryByTestId('portainerSidebar-authentication')
).not.toBeInTheDocument();
});
it('should still render other admin sections when ddExtension is enabled', () => {
window.ddExtension = true;
renderComponent({ isPureAdmin: true, isAdmin: true });
expect(
screen.getByTestId('portainerSidebar-environments-area')
).toBeInTheDocument();
expect(
screen.getByTestId('portainerSidebar-registries')
).toBeInTheDocument();
expect(
screen.getByTestId('portainerSidebar-settings')
).toBeInTheDocument();
});
});
describe('Edge Compute Features', () => {
it('should not render Update & Rollback when edge compute is disabled in CE', () => {
vi.mocked(usePublicSettings).mockImplementation(((options?: {
select?: (settings: PublicSettingsResponse) => PublicSettingsResponse;
}) => {
const settings: PublicSettingsResponse = {
TeamSync: false,
EnableEdgeComputeFeatures: false,
} as unknown as PublicSettingsResponse;
return {
data: options?.select ? options.select(settings) : settings,
isLoading: false,
isError: false,
};
}) as typeof usePublicSettings);
renderComponent({ isPureAdmin: true, isAdmin: true });
expect(
screen.queryByTestId('portainerSidebar-updateSchedules')
).not.toBeInTheDocument();
});
it('should not render Update & Rollback in CE even when edge compute is enabled', () => {
vi.mocked(usePublicSettings).mockImplementation(((options?: {
select?: (settings: PublicSettingsResponse) => PublicSettingsResponse;
}) => {
const settings: PublicSettingsResponse = {
TeamSync: false,
EnableEdgeComputeFeatures: true,
} as unknown as PublicSettingsResponse;
return {
data: options?.select ? options.select(settings) : settings,
isLoading: false,
isError: false,
};
}) as typeof usePublicSettings);
renderComponent({ isPureAdmin: true, isAdmin: true });
expect(
screen.queryByTestId('portainerSidebar-updateSchedules')
).not.toBeInTheDocument();
});
});
describe('Loading States', () => {
it('should handle loading state for public settings', () => {
vi.mocked(usePublicSettings).mockReturnValue({
data: undefined,
isLoading: true,
isError: false,
} as ReturnType<typeof usePublicSettings>);
renderComponent({ isPureAdmin: true, isAdmin: true });
expect(screen.getByText('Administration')).toBeInTheDocument();
});
it('should handle error state for public settings', () => {
vi.mocked(usePublicSettings).mockReturnValue({
data: undefined,
isLoading: false,
isError: true,
} as ReturnType<typeof usePublicSettings>);
renderComponent({ isPureAdmin: true, isAdmin: true });
expect(screen.getByText('Administration')).toBeInTheDocument();
});
});
});
function renderComponent({
isPureAdmin,
isAdmin,
isTeamLeader = false,
}: {
isPureAdmin: boolean;
isAdmin: boolean;
isTeamLeader?: boolean;
}) {
const user = new UserViewModel({ Username: 'user' });
const Wrapped = withUserProvider(withTestRouter(SettingsSidebar), user);
return render(
<TestSidebarProvider>
<Wrapped
isPureAdmin={isPureAdmin}
isAdmin={isAdmin}
isTeamLeader={isTeamLeader}
/>
</TestSidebarProvider>
);
}