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 (