mirror of
https://github.com/portainer/portainer.git
synced 2025-08-01 03:45:22 +02:00
feat(app/kubernetes): Popout kubectl shell into new window [r8s-307] (#922)
This commit is contained in:
parent
e7d97d7a2b
commit
7bcb37c761
10 changed files with 677 additions and 158 deletions
127
app/react/sidebar/KubernetesSidebar/KubectlShellButton.test.tsx
Normal file
127
app/react/sidebar/KubernetesSidebar/KubectlShellButton.test.tsx
Normal file
|
@ -0,0 +1,127 @@
|
|||
import { render, fireEvent, screen } from '@testing-library/react';
|
||||
import { vi } from 'vitest';
|
||||
import { PropsWithChildren, useMemo } from 'react';
|
||||
|
||||
import { useAnalytics } from '@/react/hooks/useAnalytics';
|
||||
|
||||
import { Context } from '../useSidebarState';
|
||||
|
||||
import { KubectlShellButton } from './KubectlShellButton';
|
||||
|
||||
vi.mock('@/react/hooks/useAnalytics', () => ({
|
||||
useAnalytics: vi.fn().mockReturnValue({
|
||||
trackEvent: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('@/portainer/helpers/pathHelper', () => ({
|
||||
baseHref: vi.fn().mockReturnValue('/portainer'),
|
||||
}));
|
||||
|
||||
const mockWindowOpen = vi.fn();
|
||||
const originalWindowOpen = window.open;
|
||||
|
||||
beforeEach(() => {
|
||||
window.open = mockWindowOpen;
|
||||
mockWindowOpen.mockClear();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
window.open = originalWindowOpen;
|
||||
});
|
||||
|
||||
function MockSidebarProvider({
|
||||
children,
|
||||
isOpen = true,
|
||||
}: PropsWithChildren<{ isOpen?: boolean }>) {
|
||||
const state = useMemo(() => ({ isOpen, toggle: vi.fn() }), [isOpen]);
|
||||
|
||||
return <Context.Provider value={state}>{children}</Context.Provider>;
|
||||
}
|
||||
|
||||
function renderComponent(environmentId = 1, isSidebarOpen = true) {
|
||||
return render(
|
||||
<MockSidebarProvider isOpen={isSidebarOpen}>
|
||||
<KubectlShellButton environmentId={environmentId} />
|
||||
</MockSidebarProvider>
|
||||
);
|
||||
}
|
||||
|
||||
describe('KubectlShellButton', () => {
|
||||
test('should render button with text when sidebar is open', () => {
|
||||
renderComponent();
|
||||
|
||||
const button = screen.getByTestId('k8sSidebar-shellButton');
|
||||
expect(button).toBeVisible();
|
||||
expect(button).toHaveTextContent('kubectl shell');
|
||||
});
|
||||
|
||||
test('should render button without text when sidebar is closed', () => {
|
||||
renderComponent(1, false);
|
||||
|
||||
const button = screen.getByTestId('k8sSidebar-shellButton');
|
||||
expect(button).toBeVisible();
|
||||
expect(button).not.toHaveTextContent('kubectl shell');
|
||||
expect(button).toHaveClass('!p-1');
|
||||
});
|
||||
|
||||
test('should wrap button in tooltip when sidebar is closed', () => {
|
||||
renderComponent(1, false);
|
||||
|
||||
// When sidebar is closed, the button is wrapped in a span with flex classes
|
||||
const button = screen.getByTestId('k8sSidebar-shellButton');
|
||||
const wrapperSpan = button.parentElement;
|
||||
expect(wrapperSpan).toHaveClass('flex', 'w-full', 'justify-center');
|
||||
|
||||
// The button should have the !p-1 class when sidebar is closed
|
||||
expect(button).toHaveClass('!p-1');
|
||||
});
|
||||
|
||||
test('should open new window with correct URL when button is clicked', () => {
|
||||
const environmentId = 5;
|
||||
renderComponent(environmentId);
|
||||
|
||||
const button = screen.getByTestId('k8sSidebar-shellButton');
|
||||
fireEvent.click(button);
|
||||
|
||||
expect(mockWindowOpen).toHaveBeenCalledTimes(1);
|
||||
const [url, windowName, windowFeatures] = mockWindowOpen.mock.calls[0];
|
||||
|
||||
expect(url).toBe(
|
||||
`${window.location.origin}/portainer#!/${environmentId}/kubernetes/kubectl-shell`
|
||||
);
|
||||
expect(windowName).toMatch(/^kubectl-shell-5-[a-f0-9-]+$/);
|
||||
expect(windowFeatures).toBe('width=800,height=600');
|
||||
});
|
||||
|
||||
test('should track analytics event when button is clicked', () => {
|
||||
const mockTrackEvent = vi.fn();
|
||||
vi.mocked(useAnalytics).mockReturnValue({ trackEvent: mockTrackEvent });
|
||||
|
||||
renderComponent();
|
||||
|
||||
const button = screen.getByRole('button');
|
||||
fireEvent.click(button);
|
||||
|
||||
expect(mockTrackEvent).toHaveBeenCalledWith('kubernetes-kubectl-shell', {
|
||||
category: 'kubernetes',
|
||||
});
|
||||
});
|
||||
|
||||
test('should generate unique window names for multiple clicks', () => {
|
||||
renderComponent();
|
||||
|
||||
const button = screen.getByRole('button');
|
||||
|
||||
// Click multiple times
|
||||
fireEvent.click(button);
|
||||
fireEvent.click(button);
|
||||
|
||||
expect(mockWindowOpen).toHaveBeenCalledTimes(2);
|
||||
|
||||
const windowName1 = mockWindowOpen.mock.calls[0][1];
|
||||
const windowName2 = mockWindowOpen.mock.calls[1][1];
|
||||
|
||||
expect(windowName1).not.toBe(windowName2);
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue