mirror of
https://github.com/portainer/portainer.git
synced 2025-07-18 21:09:40 +02:00
fix(ui): Fixed TagsDatatable name column link (#847)
This commit is contained in:
parent
3bf84e8b0c
commit
ef10ea2a7d
2 changed files with 177 additions and 8 deletions
|
@ -0,0 +1,169 @@
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { vi } from 'vitest';
|
||||||
|
|
||||||
|
import { withTestQueryProvider } from '@/react/test-utils/withTestQuery';
|
||||||
|
import { withTestRouter } from '@/react/test-utils/withRouter';
|
||||||
|
|
||||||
|
import { TagsDatatable } from './TagsDatatable';
|
||||||
|
import { Tag } from './types';
|
||||||
|
import { RepositoryTagViewModel } from './view-model';
|
||||||
|
|
||||||
|
// Mock the necessary hooks
|
||||||
|
const mockUseCurrentStateAndParams = vi.fn();
|
||||||
|
|
||||||
|
vi.mock('@uirouter/react', async (importOriginal: () => Promise<object>) => ({
|
||||||
|
...(await importOriginal()),
|
||||||
|
useCurrentStateAndParams: () => mockUseCurrentStateAndParams(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Mock the Link component to capture route parameters and generate proper hrefs
|
||||||
|
vi.mock('@@/Link', () => ({
|
||||||
|
Link: ({
|
||||||
|
children,
|
||||||
|
params,
|
||||||
|
'data-cy': dataCy,
|
||||||
|
title,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
params?: Record<string, string>;
|
||||||
|
'data-cy'?: string;
|
||||||
|
title?: string;
|
||||||
|
}) => {
|
||||||
|
// Simulate href generation based on route and params
|
||||||
|
// For 'portainer.registries.registry.repository.tag' route
|
||||||
|
const baseParams = {
|
||||||
|
endpointId: '1',
|
||||||
|
id: '1',
|
||||||
|
repository: 'test-repo',
|
||||||
|
...params,
|
||||||
|
};
|
||||||
|
|
||||||
|
const tag = (baseParams as Record<string, string>).tag || '';
|
||||||
|
const href = `/endpoints/${baseParams.endpointId}/registries/${baseParams.id}/repositories/${baseParams.repository}/tags/${tag}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a href={href} data-cy={dataCy} title={title}>
|
||||||
|
{children}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('../../queries/useTagDetails', () => ({
|
||||||
|
useTagDetails: vi.fn(
|
||||||
|
(
|
||||||
|
params,
|
||||||
|
{ select }: { select?: (data: RepositoryTagViewModel) => string } = {}
|
||||||
|
) => {
|
||||||
|
const data: RepositoryTagViewModel = {
|
||||||
|
Name: params.tag,
|
||||||
|
Os: 'linux',
|
||||||
|
Architecture: 'amd64',
|
||||||
|
ImageId: `sha256:${params.tag}123`,
|
||||||
|
Size: 1024,
|
||||||
|
ImageDigest: '',
|
||||||
|
ManifestV2: {
|
||||||
|
digest: `sha256:${params.tag}123`,
|
||||||
|
schemaVersion: 2,
|
||||||
|
mediaType: 'application/vnd.docker.distribution.manifest.v2+json',
|
||||||
|
config: {
|
||||||
|
digest: `sha256:${params.tag}123`,
|
||||||
|
mediaType: 'application/vnd.docker.container.image.v1+json',
|
||||||
|
size: 1024,
|
||||||
|
},
|
||||||
|
layers: [],
|
||||||
|
},
|
||||||
|
History: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: select?.(data) || data,
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Create mock data
|
||||||
|
const mockTags: Tag[] = [
|
||||||
|
{ Name: 'latest' },
|
||||||
|
{ Name: 'v1.0.0' },
|
||||||
|
{ Name: 'dev-branch' },
|
||||||
|
{ Name: 'feature/new-ui' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
dataset: mockTags,
|
||||||
|
advancedFeaturesAvailable: true,
|
||||||
|
onRemove: vi.fn(),
|
||||||
|
onRetag: vi.fn().mockResolvedValue(undefined),
|
||||||
|
};
|
||||||
|
|
||||||
|
function renderComponent() {
|
||||||
|
const Wrapped = withTestQueryProvider(
|
||||||
|
withTestRouter(() => <TagsDatatable {...defaultProps} />)
|
||||||
|
);
|
||||||
|
return render(<Wrapped />);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('TagsDatatable', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
// Set up default mock values
|
||||||
|
mockUseCurrentStateAndParams.mockReturnValue({
|
||||||
|
params: {
|
||||||
|
endpointId: '1',
|
||||||
|
id: '1',
|
||||||
|
repository: 'test-repo',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders basic table structure', () => {
|
||||||
|
renderComponent();
|
||||||
|
expect(screen.getByText('Tags')).toBeInTheDocument();
|
||||||
|
expect(screen.getByPlaceholderText('Search...')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders tag data in table', () => {
|
||||||
|
renderComponent();
|
||||||
|
|
||||||
|
// Check that our mock tags are rendered somewhere in the table
|
||||||
|
expect(screen.getByText('latest')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('v1.0.0')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('dev-branch')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('feature/new-ui')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates correct hrefs for tag name links', () => {
|
||||||
|
renderComponent();
|
||||||
|
|
||||||
|
// Get the links by their data-cy attributes
|
||||||
|
const latestLink = screen.getByTestId(
|
||||||
|
'registry-tag-name_latest'
|
||||||
|
) as HTMLAnchorElement;
|
||||||
|
const v100Link = screen.getByTestId(
|
||||||
|
'registry-tag-name_v1.0.0'
|
||||||
|
) as HTMLAnchorElement;
|
||||||
|
const devBranchLink = screen.getByTestId(
|
||||||
|
'registry-tag-name_dev-branch'
|
||||||
|
) as HTMLAnchorElement;
|
||||||
|
const featureLink = screen.getByTestId(
|
||||||
|
'registry-tag-name_feature/new-ui'
|
||||||
|
) as HTMLAnchorElement;
|
||||||
|
|
||||||
|
// Verify the exact path portion of the href
|
||||||
|
expect(new URL(latestLink.href).pathname).toBe(
|
||||||
|
'/endpoints/1/registries/1/repositories/test-repo/tags/latest'
|
||||||
|
);
|
||||||
|
expect(new URL(v100Link.href).pathname).toBe(
|
||||||
|
'/endpoints/1/registries/1/repositories/test-repo/tags/v1.0.0'
|
||||||
|
);
|
||||||
|
expect(new URL(devBranchLink.href).pathname).toBe(
|
||||||
|
'/endpoints/1/registries/1/repositories/test-repo/tags/dev-branch'
|
||||||
|
);
|
||||||
|
expect(new URL(featureLink.href).pathname).toBe(
|
||||||
|
'/endpoints/1/registries/1/repositories/test-repo/tags/feature/new-ui'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
|
@ -4,7 +4,7 @@ import _ from 'lodash';
|
||||||
import { humanize } from '@/portainer/filters/filters';
|
import { humanize } from '@/portainer/filters/filters';
|
||||||
import { trimSHA } from '@/docker/filters/utils';
|
import { trimSHA } from '@/docker/filters/utils';
|
||||||
|
|
||||||
import { buildNameColumn } from '@@/datatables/buildNameColumn';
|
import { buildNameColumnFromObject } from '@@/datatables/buildNameColumn';
|
||||||
|
|
||||||
import { Tag } from '../types';
|
import { Tag } from '../types';
|
||||||
|
|
||||||
|
@ -13,13 +13,13 @@ import { buildCell } from './buildCell';
|
||||||
import { actions } from './actions';
|
import { actions } from './actions';
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
buildNameColumn<Tag>(
|
buildNameColumnFromObject<Tag>({
|
||||||
'Name',
|
nameKey: 'Name',
|
||||||
'portainer.registries.registry.repository.tag',
|
path: 'portainer.registries.registry.repository.tag',
|
||||||
'tag',
|
dataCy: 'registry-tag-name',
|
||||||
'registry-tag-name',
|
idParam: 'tag',
|
||||||
(item) => item.Name
|
idGetter: (item) => item.Name,
|
||||||
),
|
}),
|
||||||
helper.display({
|
helper.display({
|
||||||
header: 'OS/Architecture',
|
header: 'OS/Architecture',
|
||||||
cell: buildCell((model) => `${model.Os}/${model.Architecture}`),
|
cell: buildCell((model) => `${model.Os}/${model.Architecture}`),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue