diff --git a/web/src/ar/pages/artifact-details/__tests__/ArtifactDetailsPage.test.tsx b/web/src/ar/pages/artifact-details/__tests__/ArtifactDetailsPage.test.tsx new file mode 100644 index 000000000..08a0609e7 --- /dev/null +++ b/web/src/ar/pages/artifact-details/__tests__/ArtifactDetailsPage.test.tsx @@ -0,0 +1,135 @@ +/* + * Copyright 2024 Harness, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react' +import userEvent from '@testing-library/user-event' +import { useGetArtifactSummaryQuery } from '@harnessio/react-har-service-client' +import { getByTestId, queryByTestId, render, waitFor } from '@testing-library/react' + +import '@ar/pages/version-details/VersionFactory' +import '@ar/pages/repository-details/RepositoryFactory' +import { DEFAULT_DATE_TIME_FORMAT } from '@ar/constants' +import { getReadableDateTime } from '@ar/common/dateUtils' +import ArTestWrapper from '@ar/utils/testUtils/ArTestWrapper' + +import ArtifactDetailsPage from '../ArtifactDetailsPage' +import { MOCK_GENERIC_ARTIFACT_SUMMARY } from './__mockData__' + +jest.mock('@harnessio/react-har-service-client', () => ({ + useGetArtifactSummaryQuery: jest.fn().mockImplementation(() => { + return { + data: MOCK_GENERIC_ARTIFACT_SUMMARY, + loading: false, + error: null, + refetch: jest.fn() + } + }), + useGetAllArtifactVersionsQuery: jest.fn().mockImplementation(() => { + return { + data: { content: { data: [] } }, + error: null, + loading: false, + refetch: jest.fn() + } + }) +})) + +describe('Verify Artifact Details Page', () => { + test('Should render Header without any error', () => { + const { container } = render( + + + + ) + + const pageHeader = getByTestId(container, 'page-header') + expect(pageHeader).toBeInTheDocument() + + const data = MOCK_GENERIC_ARTIFACT_SUMMARY.content.data + expect(pageHeader).toHaveTextContent(data.imageName) + expect(pageHeader?.querySelector('span[data-icon=generic-repository-type]')).toBeInTheDocument() + expect(pageHeader).toHaveTextContent('artifactDetails.totalDownloads' + data.downloadsCount) + expect(pageHeader).toHaveTextContent(getReadableDateTime(Number(data.createdAt), DEFAULT_DATE_TIME_FORMAT)) + expect(pageHeader).toHaveTextContent(getReadableDateTime(Number(data.modifiedAt), DEFAULT_DATE_TIME_FORMAT)) + }) + + test('Should show error message with retry button if failed to load summary api', async () => { + const retryFn = jest.fn() + ;(useGetArtifactSummaryQuery as jest.Mock).mockImplementation(() => { + return { + data: null, + loading: false, + error: { message: 'Failed to load with custom error message' }, + refetch: retryFn + } + }) + const { container } = render( + + + + ) + + expect(container.querySelector('span[icon="error"]')).toBeInTheDocument() + expect(container).toHaveTextContent('Failed to load with custom error message') + const retryBtn = container.querySelector('button[aria-label="Retry"]') + expect(retryBtn).toBeInTheDocument() + + await userEvent.click(retryBtn!) + await waitFor(() => { + expect(retryFn).toHaveBeenCalled() + }) + }) + + test('Should not render header if no data or empty data', async () => { + const retryFn = jest.fn() + ;(useGetArtifactSummaryQuery as jest.Mock).mockImplementation(() => { + return { + data: {}, + loading: false, + error: null, + refetch: retryFn + } + }) + const { container } = render( + + + + ) + + const pageHeader = queryByTestId(container, 'page-header') + expect(pageHeader).not.toBeInTheDocument() + }) + + test('Should show loading for api loading', async () => { + const retryFn = jest.fn() + ;(useGetArtifactSummaryQuery as jest.Mock).mockImplementation(() => { + return { + data: null, + isFetching: true, + error: null, + refetch: retryFn + } + }) + const { container } = render( + + + + ) + + const pageSpinner = queryByTestId(container, 'page-spinner') + expect(pageSpinner).toBeInTheDocument() + }) +}) diff --git a/web/src/ar/pages/artifact-details/context/mock.ts b/web/src/ar/pages/artifact-details/__tests__/__mockData__.ts similarity index 66% rename from web/src/ar/pages/artifact-details/context/mock.ts rename to web/src/ar/pages/artifact-details/__tests__/__mockData__.ts index a776d94d3..a3facefd9 100644 --- a/web/src/ar/pages/artifact-details/context/mock.ts +++ b/web/src/ar/pages/artifact-details/__tests__/__mockData__.ts @@ -14,13 +14,16 @@ * limitations under the License. */ -import type { ArtifactSummary } from '@harnessio/react-har-service-client' - -export const ArtifactDetailsSummary: ArtifactSummary = { - imageName: 'Artifact 1', - labels: ['label1', 'label2'], - packageType: 'DOCKER', - downloadsCount: 1000, - createdAt: '1713247878550', - modifiedAt: '1713247878550' +export const MOCK_GENERIC_ARTIFACT_SUMMARY = { + content: { + data: { + createdAt: '1738048875014', + downloadsCount: 0, + imageName: 'artifact name', + labels: null, + modifiedAt: '1738048875014', + packageType: 'GENERIC' + }, + status: 'SUCCESS' + } } diff --git a/web/src/ar/pages/digest-list/components/DigestListTable/DigestListTable.module.scss b/web/src/ar/pages/digest-list/components/DigestListTable/DigestListTable.module.scss index c4172c25b..ec664d90f 100644 --- a/web/src/ar/pages/digest-list/components/DigestListTable/DigestListTable.module.scss +++ b/web/src/ar/pages/digest-list/components/DigestListTable/DigestListTable.module.scss @@ -34,7 +34,7 @@ div[class*='TableV2--cells'], div[class*='TableV2--header'] { display: grid !important; - grid-template-columns: minmax(var(--har-table-name-column-min-width), 1fr) 10rem 10rem 10rem 20rem 10rem 50px !important; + grid-template-columns: minmax(var(--har-table-name-column-min-width), 1fr) 1fr 1fr 1fr 1fr 1fr !important; } div[class*='TableV2--header'] { diff --git a/web/src/ar/pages/version-details/DockerVersion/__tests__/DockerVersionListPage.test.tsx b/web/src/ar/pages/version-details/DockerVersion/__tests__/DockerVersionListPage.test.tsx new file mode 100644 index 000000000..47518048b --- /dev/null +++ b/web/src/ar/pages/version-details/DockerVersion/__tests__/DockerVersionListPage.test.tsx @@ -0,0 +1,348 @@ +/* + * Copyright 2024 Harness, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react' +import copy from 'clipboard-copy' +import userEvent from '@testing-library/user-event' +import { fireEvent, getByText, render, waitFor } from '@testing-library/react' +import { + useGetAllArtifactVersionsQuery as _useGetAllArtifactVersionsQuery, + useGetDockerArtifactManifestsQuery as _useGetDockerArtifactManifestsQuery, + ArtifactVersionMetadata, + DockerManifestDetails +} from '@harnessio/react-har-service-client' + +import '@ar/pages/version-details/VersionFactory' +import '@ar/pages/repository-details/RepositoryFactory' + +import { getShortDigest } from '@ar/pages/digest-list/utils' +import ArTestWrapper from '@ar/utils/testUtils/ArTestWrapper' +import VersionListPage from '@ar/pages/version-list/VersionListPage' +import { getTableColumn, getTableRow } from '@ar/utils/testUtils/utils' + +import { mockDockerLatestVersionListTableData, mockDockerManifestListTableData } from './__mockData__' + +const useGetAllArtifactVersionsQuery = _useGetAllArtifactVersionsQuery as jest.Mock +const useGetDockerArtifactManifestsQuery = _useGetDockerArtifactManifestsQuery as jest.Mock + +jest.mock('@harnessio/react-har-service-client', () => ({ + useGetAllArtifactVersionsQuery: jest.fn(), + useGetDockerArtifactManifestsQuery: jest.fn() +})) + +jest.mock('clipboard-copy', () => ({ + __esModule: true, + default: jest.fn() +})) + +describe('Verify DockerVersion List Page', () => { + test('Should render empty list text if response is empty', () => { + useGetAllArtifactVersionsQuery.mockImplementation(() => { + return { + data: { content: { data: [] } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + + const { getByText: getByTextLocal } = render( + + + + ) + + const noItemsText = getByTextLocal('versionList.table.noVersionsTitle') + expect(noItemsText).toBeInTheDocument() + }) + + test('Should render docker version list', async () => { + useGetAllArtifactVersionsQuery.mockImplementation(() => { + return { + data: { content: { data: mockDockerLatestVersionListTableData } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + const { container, getByTestId } = render( + + + + ) + + const artifact = mockDockerLatestVersionListTableData.artifactVersions?.[0] as ArtifactVersionMetadata + + const table = container.querySelector('[class*="TableV2--table"]') + expect(table).toBeInTheDocument() + + const rows = container.querySelectorAll('[class*="TableV2--row"]') + expect(rows).toHaveLength(1) + + const getFirstRowColumn = (col: number) => getTableColumn(1, col) as HTMLElement + + const name = getByText(getFirstRowColumn(2), artifact.name as string) + expect(name).toBeInTheDocument() + + const nonProdDep = getByTestId('nonProdDeployments') + const prodDep = getByTestId('prodDeployments') + + expect(getByText(nonProdDep, artifact.deploymentMetadata?.nonProdEnvCount as number)).toBeInTheDocument() + expect(getByText(nonProdDep, 'nonProd')).toBeInTheDocument() + + expect(getByText(prodDep, artifact.deploymentMetadata?.prodEnvCount as number)).toBeInTheDocument() + expect(getByText(prodDep, 'prod')).toBeInTheDocument() + + const digestValue = getByText(getFirstRowColumn(4), artifact.digestCount as number) + expect(digestValue).toBeInTheDocument() + + const curlColumn = getFirstRowColumn(6) + expect(curlColumn).toHaveTextContent('copy') + const copyCurlBtn = curlColumn.querySelector('[data-icon="code-copy"]') as HTMLElement + expect(copyCurlBtn).toBeInTheDocument() + await userEvent.click(copyCurlBtn) + expect(copy).toHaveBeenCalled() + + const expandIcon = getFirstRowColumn(1).querySelector('[data-icon="chevron-down"') as HTMLElement + expect(expandIcon).toBeInTheDocument() + }) + + test('Should render docker manifest list', async () => { + useGetAllArtifactVersionsQuery.mockImplementation(() => { + return { + data: { content: { data: mockDockerLatestVersionListTableData } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + useGetDockerArtifactManifestsQuery.mockImplementation(() => { + return { + data: { content: mockDockerManifestListTableData }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + + const { container } = render( + + + + ) + + const getFirstRowColumn = (col: number) => getTableColumn(1, col, container) as HTMLElement + + const expandIcon = getFirstRowColumn(1).querySelector('[data-icon="chevron-down"') as HTMLElement + expect(expandIcon).toBeInTheDocument() + await userEvent.click(expandIcon) + + const digestTableData = mockDockerManifestListTableData.data.manifests?.[0] as DockerManifestDetails + const digestListTable = getTableRow(1, container) as HTMLElement + const getFirstDigestRowColumn = (col: number) => getTableColumn(1, col, digestListTable) as HTMLElement + + const digestName = getFirstDigestRowColumn(1) + expect(digestName).toHaveTextContent(getShortDigest(digestTableData.digest)) + + const osArch = getFirstDigestRowColumn(2) + expect(osArch).toHaveTextContent(digestTableData.osArch) + + const size = getFirstDigestRowColumn(3) + expect(size).toHaveTextContent(digestTableData.size as string) + + const downloads = getFirstDigestRowColumn(5) + expect(downloads).toHaveTextContent(digestTableData.downloadsCount?.toString() as string) + }) + + test('Should show error message docker manifest list ', async () => { + useGetAllArtifactVersionsQuery.mockImplementation(() => { + return { + data: { content: { data: mockDockerLatestVersionListTableData } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + const mockRefetchFn = jest.fn().mockImplementation(() => undefined) + + useGetDockerArtifactManifestsQuery.mockImplementation(() => { + return { + data: null, + loading: false, + error: { message: 'failed to load data' }, + refetch: mockRefetchFn + } + }) + + const { container, getByText: getByTextLocal } = render( + + + + ) + + const getFirstRowColumn = (col: number) => getTableColumn(1, col, container) as HTMLElement + + const expandIcon = getFirstRowColumn(1).querySelector('[data-icon="chevron-down"') as HTMLElement + expect(expandIcon).toBeInTheDocument() + await userEvent.click(expandIcon) + + const errorText = getByTextLocal('failed to load data') + expect(errorText).toBeInTheDocument() + + const retryBtn = getByTextLocal('Retry') + expect(retryBtn).toBeInTheDocument() + + await userEvent.click(retryBtn) + expect(mockRefetchFn).toHaveBeenCalled() + }) + + test('Pagination should work', async () => { + useGetAllArtifactVersionsQuery.mockImplementation(() => { + return { + data: { content: { data: mockDockerLatestVersionListTableData } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + const { getByText: getByTextLocal, getByTestId } = render( + + + + ) + + const nextPageBtn = getByTextLocal('Next') + await userEvent.click(nextPageBtn) + + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 1, search_term: '', size: 50, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + + const pageSizeSelect = getByTestId('dropdown-button') + await userEvent.click(pageSizeSelect) + const pageSize20option = getByTextLocal('20') + await userEvent.click(pageSize20option) + + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '', size: 20, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + }) + + test('Filters should work', async () => { + useGetAllArtifactVersionsQuery.mockImplementation(() => { + return { + data: { content: { data: mockDockerLatestVersionListTableData } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + const { getByText: getByTextLocal, getByPlaceholderText } = render( + + + + ) + + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '', size: 50, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + + useGetAllArtifactVersionsQuery.mockImplementationOnce(() => { + return { + data: { content: { data: { artifactVersions: [] } } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + + const searchInput = getByPlaceholderText('search') + expect(searchInput).toBeInTheDocument() + fireEvent.change(searchInput, { target: { value: '1234' } }) + + await waitFor(async () => { + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '1234', size: 50, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + }) + + const clearAllFiltersBtn = getByTextLocal('clearFilters') + await userEvent.click(clearAllFiltersBtn) + + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '', size: 50, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + }) + + test('Sorting should work', async () => { + const { getByText: getByTextLocal } = render( + + + + ) + + const artifactNameSortIcon = getByTextLocal('versionList.table.columns.version').nextSibling + ?.firstChild as HTMLElement + await userEvent.click(artifactNameSortIcon) + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '', size: 50, sort_field: 'name', sort_order: 'ASC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + }) + + test('Should show error message with which listing api fails', async () => { + const mockRefetchFn = jest.fn().mockImplementation(() => undefined) + useGetAllArtifactVersionsQuery.mockImplementationOnce(() => { + return { + data: null, + loading: false, + error: { message: 'error message' }, + refetch: mockRefetchFn + } + }) + + const { getByText: getByTextLocal } = render( + + + + ) + const errorText = getByTextLocal('error message') + expect(errorText).toBeInTheDocument() + + const retryBtn = getByTextLocal('Retry') + expect(retryBtn).toBeInTheDocument() + + await userEvent.click(retryBtn) + expect(mockRefetchFn).toHaveBeenCalled() + }) +}) diff --git a/web/src/ar/pages/version-details/DockerVersion/__tests__/__mockData__.ts b/web/src/ar/pages/version-details/DockerVersion/__tests__/__mockData__.ts new file mode 100644 index 000000000..db8cb4bdc --- /dev/null +++ b/web/src/ar/pages/version-details/DockerVersion/__tests__/__mockData__.ts @@ -0,0 +1,67 @@ +/* + * Copyright 2024 Harness, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { DockerManifestsResponseResponse, ListArtifactVersion } from '@harnessio/react-har-service-client' + +export const mockDockerLatestVersionListTableData: ListArtifactVersion = { + artifactVersions: [ + { + deploymentMetadata: { + nonProdEnvCount: 0, + prodEnvCount: 0 + }, + digestCount: 1, + islatestVersion: true, + lastModified: '1730978736333', + name: '1.0.0', + packageType: 'DOCKER', + pullCommand: 'docker pull pkg.qa.harness.io/iwnhltqp7q/docker-repo/podinfo-artifact:1.0.0', + registryIdentifier: '', + registryPath: '', + size: '69.56MB' + } + ], + itemCount: 55, + pageCount: 2, + pageIndex: 0, + pageSize: 50 +} + +export const mockDockerManifestListTableData: DockerManifestsResponseResponse = { + data: { + imageName: 'maven-app', + manifests: [ + { + createdAt: '1738923119376', + digest: 'sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d', + downloadsCount: 11, + osArch: 'linux/arm64', + size: '246.43MB', + stoExecutionId: 'Tbi7s6nETjmOMKU3Qrnm7A', + stoPipelineId: 'HARNESS_ARTIFACT_SCAN_PIPELINE' + }, + { + createdAt: '1738923119376', + digest: 'sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d', + downloadsCount: 11, + osArch: 'linux/arm64', + size: '246.43MB' + } + ], + version: '1.0.0' + }, + status: 'SUCCESS' +} diff --git a/web/src/ar/pages/version-details/GenericVersion/GenericVersionType.tsx b/web/src/ar/pages/version-details/GenericVersion/GenericVersionType.tsx index 6d7702cbb..48720eadd 100644 --- a/web/src/ar/pages/version-details/GenericVersion/GenericVersionType.tsx +++ b/web/src/ar/pages/version-details/GenericVersion/GenericVersionType.tsx @@ -43,10 +43,10 @@ export class GenericVersionType extends VersionStep { ] versionListTableColumnConfig: CommonVersionListTableProps['columnConfigs'] = { - [VersionListColumnEnum.Name]: { width: '30%' }, - [VersionListColumnEnum.Size]: { width: '30%' }, - [VersionListColumnEnum.FileCount]: { width: '20%' }, - [VersionListColumnEnum.LastModified]: { width: '20%' } + [VersionListColumnEnum.Name]: { width: '100%' }, + [VersionListColumnEnum.Size]: { width: '100%' }, + [VersionListColumnEnum.FileCount]: { width: '100%' }, + [VersionListColumnEnum.LastModified]: { width: '100%' } } renderVersionListTable(props: VersionListTableProps): JSX.Element { diff --git a/web/src/ar/pages/version-details/GenericVersion/__tests__/GenericVersionListPage.test.tsx b/web/src/ar/pages/version-details/GenericVersion/__tests__/GenericVersionListPage.test.tsx new file mode 100644 index 000000000..08d811831 --- /dev/null +++ b/web/src/ar/pages/version-details/GenericVersion/__tests__/GenericVersionListPage.test.tsx @@ -0,0 +1,237 @@ +/* + * Copyright 2024 Harness, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react' +import userEvent from '@testing-library/user-event' +import { fireEvent, getByText, render, waitFor } from '@testing-library/react' +import { + useGetAllArtifactVersionsQuery as _useGetAllArtifactVersionsQuery, + ArtifactVersionMetadata +} from '@harnessio/react-har-service-client' + +import '@ar/pages/version-details/VersionFactory' +import '@ar/pages/repository-details/RepositoryFactory' + +import { getTableColumn } from '@ar/utils/testUtils/utils' +import ArTestWrapper from '@ar/utils/testUtils/ArTestWrapper' +import VersionListPage from '@ar/pages/version-list/VersionListPage' + +import { mockGenericLatestVersionListTableData } from './__mockData__' + +const useGetAllArtifactVersionsQuery = _useGetAllArtifactVersionsQuery as jest.Mock + +jest.mock('@harnessio/react-har-service-client', () => ({ + useGetAllArtifactVersionsQuery: jest.fn() +})) + +jest.mock('clipboard-copy', () => ({ + __esModule: true, + default: jest.fn() +})) + +describe('Verify GenericVersion List Page', () => { + test('Should render empty list text if response is empty', () => { + useGetAllArtifactVersionsQuery.mockImplementation(() => { + return { + data: { content: { data: [] } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + + const { getByText: getByTextLocal } = render( + + + + ) + + const noItemsText = getByTextLocal('versionList.table.noVersionsTitle') + expect(noItemsText).toBeInTheDocument() + }) + + test('Should render generic version list', async () => { + useGetAllArtifactVersionsQuery.mockImplementation(() => { + return { + data: { content: { data: mockGenericLatestVersionListTableData } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + const { container } = render( + + + + ) + + const artifact = mockGenericLatestVersionListTableData.artifactVersions?.[0] as ArtifactVersionMetadata + + const table = container.querySelector('[class*="TableV2--table"]') + expect(table).toBeInTheDocument() + + const rows = container.querySelectorAll('[class*="TableV2--row"]') + expect(rows).toHaveLength(1) + + const getFirstRowColumn = (col: number) => getTableColumn(1, col) as HTMLElement + + const name = getByText(getFirstRowColumn(1), artifact.name as string) + expect(name).toBeInTheDocument() + + const size = getByText(getFirstRowColumn(2), artifact.size as string) + expect(size).toBeInTheDocument() + + const fileCount = getByText(getFirstRowColumn(3), artifact.fileCount?.toString() as string) + expect(fileCount).toBeInTheDocument() + }) + + test('Pagination should work', async () => { + useGetAllArtifactVersionsQuery.mockImplementation(() => { + return { + data: { content: { data: mockGenericLatestVersionListTableData } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + const { getByText: getByTextLocal, getByTestId } = render( + + + + ) + + const nextPageBtn = getByTextLocal('Next') + await userEvent.click(nextPageBtn) + + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 1, search_term: '', size: 50, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + + const pageSizeSelect = getByTestId('dropdown-button') + await userEvent.click(pageSizeSelect) + const pageSize20option = getByTextLocal('20') + await userEvent.click(pageSize20option) + + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '', size: 20, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + }) + + test('Filters should work', async () => { + useGetAllArtifactVersionsQuery.mockImplementation(() => { + return { + data: { content: { data: mockGenericLatestVersionListTableData } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + const { getByText: getByTextLocal, getByPlaceholderText } = render( + + + + ) + + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '', size: 50, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + + useGetAllArtifactVersionsQuery.mockImplementationOnce(() => { + return { + data: { content: { data: { artifactVersions: [] } } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + + const searchInput = getByPlaceholderText('search') + expect(searchInput).toBeInTheDocument() + fireEvent.change(searchInput, { target: { value: '1234' } }) + + await waitFor(async () => { + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '1234', size: 50, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + }) + + const clearAllFiltersBtn = getByTextLocal('clearFilters') + await userEvent.click(clearAllFiltersBtn) + + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '', size: 50, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + }) + + test('Sorting should work', async () => { + const { getByText: getByTextLocal } = render( + + + + ) + + const artifactNameSortIcon = getByTextLocal('versionList.table.columns.version').nextSibling + ?.firstChild as HTMLElement + await userEvent.click(artifactNameSortIcon) + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '', size: 50, sort_field: 'name', sort_order: 'ASC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + }) + + test('Should show error message with which listing api fails', async () => { + const mockRefetchFn = jest.fn().mockImplementation(() => undefined) + useGetAllArtifactVersionsQuery.mockImplementationOnce(() => { + return { + data: null, + loading: false, + error: { message: 'error message' }, + refetch: mockRefetchFn + } + }) + + const { getByText: getByTextLocal } = render( + + + + ) + const errorText = getByTextLocal('error message') + expect(errorText).toBeInTheDocument() + + const retryBtn = getByTextLocal('Retry') + expect(retryBtn).toBeInTheDocument() + + await userEvent.click(retryBtn) + expect(mockRefetchFn).toHaveBeenCalled() + }) +}) diff --git a/web/src/ar/pages/version-details/GenericVersion/__tests__/__mockData__.ts b/web/src/ar/pages/version-details/GenericVersion/__tests__/__mockData__.ts new file mode 100644 index 000000000..7859afd82 --- /dev/null +++ b/web/src/ar/pages/version-details/GenericVersion/__tests__/__mockData__.ts @@ -0,0 +1,35 @@ +/* + * Copyright 2024 Harness, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { ListArtifactVersion } from '@harnessio/react-har-service-client' + +export const mockGenericLatestVersionListTableData: ListArtifactVersion = { + artifactVersions: [ + { + fileCount: 10, + lastModified: '1730978736333', + name: '1.0.0', + packageType: 'GENERIC', + registryIdentifier: '', + registryPath: '', + size: '69.56MB' + } + ], + itemCount: 55, + pageCount: 2, + pageIndex: 0, + pageSize: 50 +} diff --git a/web/src/ar/pages/version-details/HelmVersion/HelmVersionType.tsx b/web/src/ar/pages/version-details/HelmVersion/HelmVersionType.tsx index a29e980c4..76fbb3037 100644 --- a/web/src/ar/pages/version-details/HelmVersion/HelmVersionType.tsx +++ b/web/src/ar/pages/version-details/HelmVersion/HelmVersionType.tsx @@ -43,11 +43,11 @@ export class HelmVersionType extends VersionStep { VersionDetailsTab.CODE ] versionListTableColumnConfig: CommonVersionListTableProps['columnConfigs'] = { - [VersionListColumnEnum.Name]: { width: '30%' }, - [VersionListColumnEnum.Size]: { width: '8%' }, - [VersionListColumnEnum.DownloadCount]: { width: '10%' }, - [VersionListColumnEnum.LastModified]: { width: '12%' }, - [VersionListColumnEnum.PullCommand]: { width: '40%' } + [VersionListColumnEnum.Name]: { width: '100%' }, + [VersionListColumnEnum.Size]: { width: '100%' }, + [VersionListColumnEnum.DownloadCount]: { width: '100%' }, + [VersionListColumnEnum.LastModified]: { width: '100%' }, + [VersionListColumnEnum.PullCommand]: { width: '100%' } } renderVersionListTable(props: VersionListTableProps): JSX.Element { diff --git a/web/src/ar/pages/version-details/HelmVersion/__tests__/HelmVersionListPage.test.tsx b/web/src/ar/pages/version-details/HelmVersion/__tests__/HelmVersionListPage.test.tsx new file mode 100644 index 000000000..3755e4ff6 --- /dev/null +++ b/web/src/ar/pages/version-details/HelmVersion/__tests__/HelmVersionListPage.test.tsx @@ -0,0 +1,245 @@ +/* + * Copyright 2024 Harness, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react' +import copy from 'clipboard-copy' +import userEvent from '@testing-library/user-event' +import { fireEvent, getByText, render, waitFor } from '@testing-library/react' +import { + useGetAllArtifactVersionsQuery as _useGetAllArtifactVersionsQuery, + ArtifactVersionMetadata +} from '@harnessio/react-har-service-client' + +import '@ar/pages/version-details/VersionFactory' +import '@ar/pages/repository-details/RepositoryFactory' + +import { getTableColumn } from '@ar/utils/testUtils/utils' +import ArTestWrapper from '@ar/utils/testUtils/ArTestWrapper' +import VersionListPage from '@ar/pages/version-list/VersionListPage' + +import { mockHelmLatestVersionListTableData } from './__mockData__' + +const useGetAllArtifactVersionsQuery = _useGetAllArtifactVersionsQuery as jest.Mock + +jest.mock('@harnessio/react-har-service-client', () => ({ + useGetAllArtifactVersionsQuery: jest.fn() +})) + +jest.mock('clipboard-copy', () => ({ + __esModule: true, + default: jest.fn() +})) + +describe('Verify HelmVersion List Page', () => { + test('Should render empty list text if response is empty', () => { + useGetAllArtifactVersionsQuery.mockImplementation(() => { + return { + data: { content: { data: [] } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + + const { getByText: getByTextLocal } = render( + + + + ) + + const noItemsText = getByTextLocal('versionList.table.noVersionsTitle') + expect(noItemsText).toBeInTheDocument() + }) + + test('Should render helm version list', async () => { + useGetAllArtifactVersionsQuery.mockImplementation(() => { + return { + data: { content: { data: mockHelmLatestVersionListTableData } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + const { container } = render( + + + + ) + + const artifact = mockHelmLatestVersionListTableData.artifactVersions?.[0] as ArtifactVersionMetadata + + const table = container.querySelector('[class*="TableV2--table"]') + expect(table).toBeInTheDocument() + + const rows = container.querySelectorAll('[class*="TableV2--row"]') + expect(rows).toHaveLength(1) + + const getFirstRowColumn = (col: number) => getTableColumn(1, col) as HTMLElement + + const name = getByText(getFirstRowColumn(1), artifact.name as string) + expect(name).toBeInTheDocument() + + const size = getByText(getFirstRowColumn(2), artifact.size as string) + expect(size).toBeInTheDocument() + + const downloads = getByText(getFirstRowColumn(3), artifact.downloadsCount?.toString() as string) + expect(downloads).toBeInTheDocument() + + const curlColumn = getFirstRowColumn(5) + expect(curlColumn).toHaveTextContent('copy') + const copyCurlBtn = curlColumn.querySelector('[data-icon="code-copy"]') as HTMLElement + expect(copyCurlBtn).toBeInTheDocument() + await userEvent.click(copyCurlBtn) + expect(copy).toHaveBeenCalled() + }) + + test('Pagination should work', async () => { + useGetAllArtifactVersionsQuery.mockImplementation(() => { + return { + data: { content: { data: mockHelmLatestVersionListTableData } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + const { getByText: getByTextLocal, getByTestId } = render( + + + + ) + + const nextPageBtn = getByTextLocal('Next') + await userEvent.click(nextPageBtn) + + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 1, search_term: '', size: 50, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + + const pageSizeSelect = getByTestId('dropdown-button') + await userEvent.click(pageSizeSelect) + const pageSize20option = getByTextLocal('20') + await userEvent.click(pageSize20option) + + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '', size: 20, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + }) + + test('Filters should work', async () => { + useGetAllArtifactVersionsQuery.mockImplementation(() => { + return { + data: { content: { data: mockHelmLatestVersionListTableData } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + const { getByText: getByTextLocal, getByPlaceholderText } = render( + + + + ) + + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '', size: 50, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + + useGetAllArtifactVersionsQuery.mockImplementationOnce(() => { + return { + data: { content: { data: { artifactVersions: [] } } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + + const searchInput = getByPlaceholderText('search') + expect(searchInput).toBeInTheDocument() + fireEvent.change(searchInput, { target: { value: '1234' } }) + + await waitFor(async () => { + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '1234', size: 50, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + }) + + const clearAllFiltersBtn = getByTextLocal('clearFilters') + await userEvent.click(clearAllFiltersBtn) + + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '', size: 50, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + }) + + test('Sorting should work', async () => { + const { getByText: getByTextLocal } = render( + + + + ) + + const artifactNameSortIcon = getByTextLocal('versionList.table.columns.version').nextSibling + ?.firstChild as HTMLElement + await userEvent.click(artifactNameSortIcon) + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '', size: 50, sort_field: 'name', sort_order: 'ASC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + }) + + test('Should show error message with which listing api fails', async () => { + const mockRefetchFn = jest.fn().mockImplementation(() => undefined) + useGetAllArtifactVersionsQuery.mockImplementationOnce(() => { + return { + data: null, + loading: false, + error: { message: 'error message' }, + refetch: mockRefetchFn + } + }) + + const { getByText: getByTextLocal } = render( + + + + ) + const errorText = getByTextLocal('error message') + expect(errorText).toBeInTheDocument() + + const retryBtn = getByTextLocal('Retry') + expect(retryBtn).toBeInTheDocument() + + await userEvent.click(retryBtn) + expect(mockRefetchFn).toHaveBeenCalled() + }) +}) diff --git a/web/src/ar/pages/version-details/HelmVersion/__tests__/__mockData__.ts b/web/src/ar/pages/version-details/HelmVersion/__tests__/__mockData__.ts new file mode 100644 index 000000000..253136434 --- /dev/null +++ b/web/src/ar/pages/version-details/HelmVersion/__tests__/__mockData__.ts @@ -0,0 +1,38 @@ +/* + * Copyright 2024 Harness, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { ListArtifactVersion } from '@harnessio/react-har-service-client' + +export const mockHelmLatestVersionListTableData: ListArtifactVersion = { + artifactVersions: [ + { + digestCount: 1, + downloadsCount: 100, + islatestVersion: true, + lastModified: '1730978736333', + name: '1.0.0', + packageType: 'HELM', + pullCommand: 'docker pull pkg.qa.harness.io/iwnhltqp7q/docker-repo/podinfo-artifact:1.0.0', + registryIdentifier: '', + registryPath: '', + size: '69.56MB' + } + ], + itemCount: 55, + pageCount: 2, + pageIndex: 0, + pageSize: 50 +} diff --git a/web/src/ar/pages/version-details/MavenVersion/MavenVersion.tsx b/web/src/ar/pages/version-details/MavenVersion/MavenVersion.tsx index 1d0d0a184..369544c17 100644 --- a/web/src/ar/pages/version-details/MavenVersion/MavenVersion.tsx +++ b/web/src/ar/pages/version-details/MavenVersion/MavenVersion.tsx @@ -45,10 +45,10 @@ export class MavenVersionType extends VersionStep { ] versionListTableColumnConfig: CommonVersionListTableProps['columnConfigs'] = { - [VersionListColumnEnum.Name]: { width: '30%' }, - [VersionListColumnEnum.Size]: { width: '30%' }, - [VersionListColumnEnum.FileCount]: { width: '20%' }, - [VersionListColumnEnum.LastModified]: { width: '20%' } + [VersionListColumnEnum.Name]: { width: '100%' }, + [VersionListColumnEnum.Size]: { width: '100%' }, + [VersionListColumnEnum.FileCount]: { width: '100%' }, + [VersionListColumnEnum.LastModified]: { width: '100%' } } renderVersionListTable(props: VersionListTableProps): JSX.Element { diff --git a/web/src/ar/pages/version-details/MavenVersion/__tests__/MavenVersionListPage.test.tsx b/web/src/ar/pages/version-details/MavenVersion/__tests__/MavenVersionListPage.test.tsx new file mode 100644 index 000000000..ef3027624 --- /dev/null +++ b/web/src/ar/pages/version-details/MavenVersion/__tests__/MavenVersionListPage.test.tsx @@ -0,0 +1,237 @@ +/* + * Copyright 2024 Harness, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react' +import userEvent from '@testing-library/user-event' +import { fireEvent, getByText, render, waitFor } from '@testing-library/react' +import { + useGetAllArtifactVersionsQuery as _useGetAllArtifactVersionsQuery, + ArtifactVersionMetadata +} from '@harnessio/react-har-service-client' + +import '@ar/pages/version-details/VersionFactory' +import '@ar/pages/repository-details/RepositoryFactory' + +import { getTableColumn } from '@ar/utils/testUtils/utils' +import ArTestWrapper from '@ar/utils/testUtils/ArTestWrapper' +import VersionListPage from '@ar/pages/version-list/VersionListPage' + +import { mockMavenLatestVersionListTableData } from './__mockData__' + +const useGetAllArtifactVersionsQuery = _useGetAllArtifactVersionsQuery as jest.Mock + +jest.mock('@harnessio/react-har-service-client', () => ({ + useGetAllArtifactVersionsQuery: jest.fn() +})) + +jest.mock('clipboard-copy', () => ({ + __esModule: true, + default: jest.fn() +})) + +describe('Verify MavenVersion List Page', () => { + test('Should render empty list text if response is empty', () => { + useGetAllArtifactVersionsQuery.mockImplementation(() => { + return { + data: { content: { data: [] } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + + const { getByText: getByTextLocal } = render( + + + + ) + + const noItemsText = getByTextLocal('versionList.table.noVersionsTitle') + expect(noItemsText).toBeInTheDocument() + }) + + test('Should render maven version list', async () => { + useGetAllArtifactVersionsQuery.mockImplementation(() => { + return { + data: { content: { data: mockMavenLatestVersionListTableData } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + const { container } = render( + + + + ) + + const artifact = mockMavenLatestVersionListTableData.artifactVersions?.[0] as ArtifactVersionMetadata + + const table = container.querySelector('[class*="TableV2--table"]') + expect(table).toBeInTheDocument() + + const rows = container.querySelectorAll('[class*="TableV2--row"]') + expect(rows).toHaveLength(1) + + const getFirstRowColumn = (col: number) => getTableColumn(1, col) as HTMLElement + + const name = getByText(getFirstRowColumn(1), artifact.name as string) + expect(name).toBeInTheDocument() + + const size = getByText(getFirstRowColumn(2), artifact.size as string) + expect(size).toBeInTheDocument() + + const fileCount = getByText(getFirstRowColumn(3), artifact.fileCount?.toString() as string) + expect(fileCount).toBeInTheDocument() + }) + + test('Pagination should work', async () => { + useGetAllArtifactVersionsQuery.mockImplementation(() => { + return { + data: { content: { data: mockMavenLatestVersionListTableData } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + const { getByText: getByTextLocal, getByTestId } = render( + + + + ) + + const nextPageBtn = getByTextLocal('Next') + await userEvent.click(nextPageBtn) + + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 1, search_term: '', size: 50, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + + const pageSizeSelect = getByTestId('dropdown-button') + await userEvent.click(pageSizeSelect) + const pageSize20option = getByTextLocal('20') + await userEvent.click(pageSize20option) + + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '', size: 20, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + }) + + test('Filters should work', async () => { + useGetAllArtifactVersionsQuery.mockImplementation(() => { + return { + data: { content: { data: mockMavenLatestVersionListTableData } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + const { getByText: getByTextLocal, getByPlaceholderText } = render( + + + + ) + + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '', size: 50, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + + useGetAllArtifactVersionsQuery.mockImplementationOnce(() => { + return { + data: { content: { data: { artifactVersions: [] } } }, + loading: false, + error: null, + refetch: jest.fn() + } + }) + + const searchInput = getByPlaceholderText('search') + expect(searchInput).toBeInTheDocument() + fireEvent.change(searchInput, { target: { value: '1234' } }) + + await waitFor(async () => { + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '1234', size: 50, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + }) + + const clearAllFiltersBtn = getByTextLocal('clearFilters') + await userEvent.click(clearAllFiltersBtn) + + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '', size: 50, sort_field: 'updatedAt', sort_order: 'DESC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + }) + + test('Sorting should work', async () => { + const { getByText: getByTextLocal } = render( + + + + ) + + const artifactNameSortIcon = getByTextLocal('versionList.table.columns.version').nextSibling + ?.firstChild as HTMLElement + await userEvent.click(artifactNameSortIcon) + expect(useGetAllArtifactVersionsQuery).toHaveBeenLastCalledWith({ + artifact: 'undefined/+', + queryParams: { page: 0, search_term: '', size: 50, sort_field: 'name', sort_order: 'ASC' }, + registry_ref: 'undefined/+', + stringifyQueryParamsOptions: { arrayFormat: 'repeat' } + }) + }) + + test('Should show error message with which listing api fails', async () => { + const mockRefetchFn = jest.fn().mockImplementation(() => undefined) + useGetAllArtifactVersionsQuery.mockImplementationOnce(() => { + return { + data: null, + loading: false, + error: { message: 'error message' }, + refetch: mockRefetchFn + } + }) + + const { getByText: getByTextLocal } = render( + + + + ) + const errorText = getByTextLocal('error message') + expect(errorText).toBeInTheDocument() + + const retryBtn = getByTextLocal('Retry') + expect(retryBtn).toBeInTheDocument() + + await userEvent.click(retryBtn) + expect(mockRefetchFn).toHaveBeenCalled() + }) +}) diff --git a/web/src/ar/pages/version-details/MavenVersion/__tests__/__mockData__.ts b/web/src/ar/pages/version-details/MavenVersion/__tests__/__mockData__.ts new file mode 100644 index 000000000..f2e692529 --- /dev/null +++ b/web/src/ar/pages/version-details/MavenVersion/__tests__/__mockData__.ts @@ -0,0 +1,35 @@ +/* + * Copyright 2024 Harness, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { ListArtifactVersion } from '@harnessio/react-har-service-client' + +export const mockMavenLatestVersionListTableData: ListArtifactVersion = { + artifactVersions: [ + { + fileCount: 10, + lastModified: '1730978736333', + name: '1.0.0', + packageType: 'MAVEN', + registryIdentifier: '', + registryPath: '', + size: '69.56MB' + } + ], + itemCount: 55, + pageCount: 2, + pageIndex: 0, + pageSize: 50 +} diff --git a/web/src/ar/pages/version-list/DockerVersion/VersionListTable/DockerVersionListTable.module.scss b/web/src/ar/pages/version-list/DockerVersion/VersionListTable/DockerVersionListTable.module.scss index 18405042d..1c6223483 100644 --- a/web/src/ar/pages/version-list/DockerVersion/VersionListTable/DockerVersionListTable.module.scss +++ b/web/src/ar/pages/version-list/DockerVersion/VersionListTable/DockerVersionListTable.module.scss @@ -44,7 +44,7 @@ div[class*='TableV2--cells'], div[class*='TableV2--header'] { display: grid !important; - grid-template-columns: 40px minmax(var(--har-table-name-column-min-width), 1fr) 10rem 10rem 38rem; + grid-template-columns: 40px minmax(var(--har-table-name-column-min-width), 1fr) 1fr 1fr 1fr; } } @@ -52,7 +52,7 @@ div[class*='TableV2--cells'], div[class*='TableV2--header'] { display: grid !important; - grid-template-columns: 40px minmax(var(--har-table-name-column-min-width), 1fr) 15rem 10rem 10rem 30rem; + grid-template-columns: 40px minmax(var(--har-table-name-column-min-width), 1fr) 1fr 1fr 1fr 1fr; } } diff --git a/web/src/ar/utils/testUtils/utils.ts b/web/src/ar/utils/testUtils/utils.ts index d4a276535..8d05b22ca 100644 --- a/web/src/ar/utils/testUtils/utils.ts +++ b/web/src/ar/utils/testUtils/utils.ts @@ -98,14 +98,20 @@ export const getTableHeaderColumn = (column: number) => { return document.querySelector(`div[class*="TableV2--header--"] [class*="TableV2--cell"]:nth-child(${column})`) } -export const getTableRow = (row: number) => { - return document.querySelector(`div[class*="TableV2--body--"] [class*="TableV2--row"]:nth-child(${row})`) +export const getTableRow = (row: number, container: Document | Element = document): HTMLDivElement | null => { + return container.querySelector(`div[class*="TableV2--body--"] [class*="TableV2--row"]:nth-child(${row})`) } -export const getTableColumn = (row: number, column: number): HTMLDivElement | null => { - return document.querySelector( - `div[class*="TableV2--body--"] [class*="TableV2--row"]:nth-child(${row}) [class*="TableV2--cell--"]:nth-child(${column})` - ) +export const getTableColumn = ( + row: number, + column: number, + container: Document | Element = document +): HTMLDivElement | null => { + const rowElement = getTableRow(row, container) + if (rowElement) { + return rowElement.querySelector(`[class*="TableV2--cell--"]:nth-child(${column})`) + } + return null } export const testSelectChange = async (