mirror of
https://github.com/portainer/portainer.git
synced 2025-07-19 05:19:39 +02:00
feat(helm): helm actions [r8s-259] (#715)
Co-authored-by: James Player <james.player@portainer.io> Co-authored-by: Cara Ryan <cara.ryan@portainer.io> Co-authored-by: stevensbkang <skan070@gmail.com>
This commit is contained in:
parent
dfa32b6755
commit
4ee349bd6b
117 changed files with 4161 additions and 696 deletions
|
@ -1,4 +1,4 @@
|
|||
import { render, screen } from '@testing-library/react';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { HttpResponse } from 'msw';
|
||||
|
||||
import { withTestQueryProvider } from '@/react/test-utils/withTestQuery';
|
||||
|
@ -7,12 +7,14 @@ import { withTestRouter } from '@/react/test-utils/withRouter';
|
|||
import { UserViewModel } from '@/portainer/models/user';
|
||||
import { withUserProvider } from '@/react/test-utils/withUserProvider';
|
||||
import { mockCodeMirror } from '@/setup-tests/mock-codemirror';
|
||||
import { mockLocalizeDate } from '@/setup-tests/mock-localizeDate';
|
||||
|
||||
import { HelmApplicationView } from './HelmApplicationView';
|
||||
|
||||
// Mock the necessary hooks and dependencies
|
||||
const mockUseCurrentStateAndParams = vi.fn();
|
||||
const mockUseEnvironmentId = vi.fn();
|
||||
mockLocalizeDate();
|
||||
|
||||
vi.mock('@uirouter/react', async (importOriginal: () => Promise<object>) => ({
|
||||
...(await importOriginal()),
|
||||
|
@ -32,32 +34,109 @@ const minimalHelmRelease = {
|
|||
chart: {
|
||||
metadata: {
|
||||
name: 'test-chart',
|
||||
// appVersion: '1.0.0', // can be missing for a minimal release
|
||||
version: '2.2.2',
|
||||
},
|
||||
},
|
||||
info: {
|
||||
status: 'deployed',
|
||||
last_deployed: '2021-01-01T00:00:00Z',
|
||||
// notes: 'This is a test note', // can be missing for a minimal release
|
||||
},
|
||||
manifest: 'This is a test manifest',
|
||||
};
|
||||
|
||||
const helmReleaseWithAdditionalDetails = {
|
||||
...minimalHelmRelease,
|
||||
info: {
|
||||
...minimalHelmRelease.info,
|
||||
notes: 'This is a test note',
|
||||
},
|
||||
// Create a more complete helm release object for testing
|
||||
const completeHelmRelease = {
|
||||
name: 'test-release',
|
||||
version: '1',
|
||||
namespace: 'default',
|
||||
chart: {
|
||||
...minimalHelmRelease.chart,
|
||||
metadata: {
|
||||
...minimalHelmRelease.chart.metadata,
|
||||
name: 'test-chart',
|
||||
appVersion: '1.0.0',
|
||||
version: '2.2.2',
|
||||
},
|
||||
},
|
||||
info: {
|
||||
status: 'deployed',
|
||||
notes: 'This is a test note',
|
||||
resources: [
|
||||
{
|
||||
kind: 'Deployment',
|
||||
name: 'test-deployment',
|
||||
namespace: 'default',
|
||||
uid: 'test-deployment-uid',
|
||||
status: {
|
||||
healthSummary: {
|
||||
status: 'Healthy',
|
||||
reason: 'Running',
|
||||
message: 'All replicas are ready',
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
name: 'test-deployment',
|
||||
namespace: 'default',
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: 'Service',
|
||||
name: 'test-service',
|
||||
namespace: 'default',
|
||||
uid: 'test-service-uid',
|
||||
status: {
|
||||
healthSummary: {
|
||||
status: 'Healthy',
|
||||
reason: 'Available',
|
||||
message: 'Service is available',
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
name: 'test-service',
|
||||
namespace: 'default',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
manifest: 'This is a test manifest',
|
||||
values: {
|
||||
// Add some values to ensure the Values tab is present
|
||||
replicaCount: 1,
|
||||
image: {
|
||||
repository: 'nginx',
|
||||
tag: 'latest',
|
||||
},
|
||||
},
|
||||
resources: [
|
||||
{
|
||||
kind: 'Deployment',
|
||||
name: 'test-deployment',
|
||||
namespace: 'default',
|
||||
status: {
|
||||
healthSummary: {
|
||||
status: 'Healthy',
|
||||
reason: 'Running',
|
||||
message: 'All replicas are ready',
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
name: 'test-deployment',
|
||||
namespace: 'default',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const helmReleaseHistory = [
|
||||
{
|
||||
version: 1,
|
||||
updated: '2023-06-01T12:00:00Z',
|
||||
status: 'deployed',
|
||||
chart: 'test-chart-1.0.0',
|
||||
app_version: '1.0.0',
|
||||
description: 'Install complete',
|
||||
},
|
||||
];
|
||||
|
||||
function renderComponent() {
|
||||
const user = new UserViewModel({ Username: 'user' });
|
||||
const Wrapped = withTestQueryProvider(
|
||||
|
@ -66,92 +145,171 @@ function renderComponent() {
|
|||
return render(<Wrapped />);
|
||||
}
|
||||
|
||||
describe('HelmApplicationView', () => {
|
||||
beforeEach(() => {
|
||||
// Set up default mock values
|
||||
mockUseEnvironmentId.mockReturnValue(3);
|
||||
mockUseCurrentStateAndParams.mockReturnValue({
|
||||
params: {
|
||||
name: 'test-release',
|
||||
namespace: 'default',
|
||||
},
|
||||
describe(
|
||||
'HelmApplicationView',
|
||||
() => {
|
||||
beforeEach(() => {
|
||||
// Set up default mock values
|
||||
mockUseEnvironmentId.mockReturnValue(3);
|
||||
mockUseCurrentStateAndParams.mockReturnValue({
|
||||
params: {
|
||||
name: 'test-release',
|
||||
namespace: 'default',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should display helm release details for minimal release when data is loaded', async () => {
|
||||
vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
it('should display helm release details for minimal release when data is loaded', async () => {
|
||||
vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
server.use(
|
||||
http.get('/api/endpoints/3/kubernetes/helm/test-release', () =>
|
||||
HttpResponse.json(minimalHelmRelease)
|
||||
)
|
||||
);
|
||||
server.use(
|
||||
http.get('/api/endpoints/3/kubernetes/helm/test-release', () =>
|
||||
HttpResponse.json(minimalHelmRelease)
|
||||
),
|
||||
http.get('/api/users/undefined/helm/repositories', () =>
|
||||
HttpResponse.json({
|
||||
GlobalRepository: 'https://charts.helm.sh/stable',
|
||||
UserRepositories: [
|
||||
{ Id: '1', URL: 'https://charts.helm.sh/stable' },
|
||||
],
|
||||
})
|
||||
),
|
||||
http.get('/api/templates/helm', () =>
|
||||
HttpResponse.json({
|
||||
entries: {
|
||||
'test-chart': [{ version: '1.0.0' }],
|
||||
},
|
||||
})
|
||||
),
|
||||
http.get('/api/endpoints/3/kubernetes/helm/test-release/history', () =>
|
||||
HttpResponse.json(helmReleaseHistory)
|
||||
),
|
||||
http.get(
|
||||
'/api/endpoints/3/kubernetes/api/v1/namespaces/default/events',
|
||||
() =>
|
||||
HttpResponse.json({
|
||||
kind: 'EventList',
|
||||
apiVersion: 'v1',
|
||||
metadata: { resourceVersion: '12345' },
|
||||
items: [],
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
const { findByText, findAllByText } = renderComponent();
|
||||
const { findByText, findAllByText } = renderComponent();
|
||||
|
||||
// Check for the page header
|
||||
expect(await findByText('Helm details')).toBeInTheDocument();
|
||||
// Check for the page header
|
||||
expect(await findByText('Helm details')).toBeInTheDocument();
|
||||
|
||||
// Check for the badge content
|
||||
expect(await findByText(/Namespace/)).toBeInTheDocument();
|
||||
expect(await findByText(/Chart version:/)).toBeInTheDocument();
|
||||
expect(await findByText(/Chart:/)).toBeInTheDocument();
|
||||
expect(await findByText(/Revision/)).toBeInTheDocument();
|
||||
// Check for the badge content
|
||||
expect(await findByText(/Namespace: default/)).toBeInTheDocument();
|
||||
expect(
|
||||
await findByText(/Chart version: test-chart-2.2.2/)
|
||||
).toBeInTheDocument();
|
||||
expect(await findByText(/Chart: test-chart/)).toBeInTheDocument();
|
||||
expect(await findByText(/Revision: #1/)).toBeInTheDocument();
|
||||
expect(
|
||||
await findByText(/Last deployed: Jan 1, 2021, 12:00 AM/)
|
||||
).toBeInTheDocument();
|
||||
// Check for the actual values
|
||||
expect(await findAllByText(/test-release/)).toHaveLength(2); // title and badge
|
||||
expect(await findAllByText(/test-chart/)).toHaveLength(2); // title and badge (not checking revision list item)
|
||||
|
||||
// Check for the actual values
|
||||
expect(await findAllByText(/test-release/)).toHaveLength(2); // title and badge
|
||||
expect(await findAllByText(/test-chart/)).toHaveLength(2);
|
||||
// There shouldn't be a notes tab when there are no notes
|
||||
expect(screen.queryByText(/Notes/)).not.toBeInTheDocument();
|
||||
|
||||
// There shouldn't be a notes tab when there are no notes
|
||||
expect(screen.queryByText(/Notes/)).not.toBeInTheDocument();
|
||||
// There shouldn't be an app version badge when it's missing
|
||||
expect(screen.queryByText(/App version/)).not.toBeInTheDocument();
|
||||
|
||||
// There shouldn't be an app version badge when it's missing
|
||||
expect(screen.queryByText(/App version/)).not.toBeInTheDocument();
|
||||
// Ensure there are no console errors
|
||||
// eslint-disable-next-line no-console
|
||||
expect(console.error).not.toHaveBeenCalled();
|
||||
|
||||
// Ensure there are no console errors
|
||||
// eslint-disable-next-line no-console
|
||||
expect(console.error).not.toHaveBeenCalled();
|
||||
// Restore console.error
|
||||
vi.spyOn(console, 'error').mockRestore();
|
||||
});
|
||||
|
||||
// Restore console.error
|
||||
vi.spyOn(console, 'error').mockRestore();
|
||||
});
|
||||
it('should display error message when API request fails', async () => {
|
||||
// Mock API failure
|
||||
server.use(
|
||||
http.get('/api/endpoints/3/kubernetes/helm/test-release', () =>
|
||||
HttpResponse.error()
|
||||
),
|
||||
// Add mock for events endpoint
|
||||
http.get(
|
||||
'/api/endpoints/3/kubernetes/api/v1/namespaces/default/events',
|
||||
() =>
|
||||
HttpResponse.json({
|
||||
kind: 'EventList',
|
||||
apiVersion: 'v1',
|
||||
metadata: { resourceVersion: '12345' },
|
||||
items: [],
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
it('should display error message when API request fails', async () => {
|
||||
// Mock API failure
|
||||
server.use(
|
||||
http.get('/api/endpoints/3/kubernetes/helm/test-release', () =>
|
||||
HttpResponse.error()
|
||||
)
|
||||
);
|
||||
// Mock console.error to prevent test output pollution
|
||||
vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
||||
// Mock console.error to prevent test output pollution
|
||||
vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
renderComponent();
|
||||
|
||||
renderComponent();
|
||||
// Wait for the error message to appear
|
||||
expect(
|
||||
await screen.findByText(
|
||||
'Failed to load Helm application details',
|
||||
{},
|
||||
{ timeout: 6500 }
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
|
||||
// Wait for the error message to appear
|
||||
expect(
|
||||
await screen.findByText('Failed to load Helm application details')
|
||||
).toBeInTheDocument();
|
||||
// Restore console.error
|
||||
vi.spyOn(console, 'error').mockRestore();
|
||||
});
|
||||
|
||||
// Restore console.error
|
||||
vi.spyOn(console, 'error').mockRestore();
|
||||
});
|
||||
it('should display additional details when available in helm release', async () => {
|
||||
server.use(
|
||||
http.get('/api/endpoints/3/kubernetes/helm/test-release', () =>
|
||||
HttpResponse.json(completeHelmRelease)
|
||||
),
|
||||
http.get('/api/endpoints/3/kubernetes/helm/test-release/history', () =>
|
||||
HttpResponse.json(helmReleaseHistory)
|
||||
),
|
||||
http.get(
|
||||
'/api/endpoints/3/kubernetes/api/v1/namespaces/default/events',
|
||||
() =>
|
||||
HttpResponse.json({
|
||||
kind: 'EventList',
|
||||
apiVersion: 'v1',
|
||||
metadata: { resourceVersion: '12345' },
|
||||
items: [],
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
it('should display additional details when available in helm release', async () => {
|
||||
server.use(
|
||||
http.get('/api/endpoints/3/kubernetes/helm/test-release', () =>
|
||||
HttpResponse.json(helmReleaseWithAdditionalDetails)
|
||||
)
|
||||
);
|
||||
const { findByText } = renderComponent();
|
||||
|
||||
const { findByText } = renderComponent();
|
||||
expect(await findByText('Helm details')).toBeInTheDocument();
|
||||
|
||||
// Check for the notes tab when notes are available
|
||||
expect(await findByText(/Notes/)).toBeInTheDocument();
|
||||
// Check for the app version badge when it's available
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText(/App version/, { exact: false })
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Check for the app version badge when it's available
|
||||
expect(await findByText(/App version/)).toBeInTheDocument();
|
||||
expect(await findByText('1.0.0', { exact: false })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
await waitFor(() => {
|
||||
// Look for specific tab text
|
||||
expect(screen.getByText('Resources')).toBeInTheDocument();
|
||||
expect(screen.getByText('Values')).toBeInTheDocument();
|
||||
expect(screen.getByText('Manifest')).toBeInTheDocument();
|
||||
expect(screen.getByText('Notes')).toBeInTheDocument();
|
||||
expect(screen.getByText('Events')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(await findByText(/App version: 1.0.0/)).toBeInTheDocument();
|
||||
});
|
||||
},
|
||||
{
|
||||
timeout: 7000,
|
||||
}
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue