mirror of
https://github.com/harness/drone.git
synced 2025-05-04 00:10:07 +08:00
feat: [AH-667]: add unit test coverage for version details page > artifact details tab (#3433)
* feat: [AH-667]: add unit test coverage for version details page > artifact details tab
This commit is contained in:
parent
186751ec6c
commit
ff82bd4b0d
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* 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 { getByTestId, getByText, render, waitFor } from '@testing-library/react'
|
||||
import { useGetDockerArtifactLayersQuery, useGetDockerArtifactManifestQuery } from '@harnessio/react-har-service-client'
|
||||
|
||||
import { getTableColumn } from '@ar/utils/testUtils/utils'
|
||||
import ArTestWrapper from '@ar/utils/testUtils/ArTestWrapper'
|
||||
|
||||
import VersionDetailsPage from '../../VersionDetailsPage'
|
||||
import {
|
||||
mockDockerArtifactLayers,
|
||||
mockDockerArtifactManifest,
|
||||
mockDockerManifestList,
|
||||
mockDockerVersionList,
|
||||
mockDockerVersionSummary
|
||||
} from './__mockData__'
|
||||
|
||||
const mockHistoryPush = jest.fn()
|
||||
// eslint-disable-next-line jest-no-mock
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useHistory: () => ({
|
||||
push: mockHistoryPush
|
||||
})
|
||||
}))
|
||||
|
||||
jest.mock('clipboard-copy', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn()
|
||||
}))
|
||||
|
||||
jest.mock('@harnessio/react-har-service-client', () => ({
|
||||
useGetArtifactVersionSummaryQuery: jest.fn().mockImplementation(() => ({
|
||||
data: { content: mockDockerVersionSummary },
|
||||
error: null,
|
||||
isLoading: false,
|
||||
refetch: jest.fn()
|
||||
})),
|
||||
useGetDockerArtifactManifestsQuery: jest.fn().mockImplementation(() => ({
|
||||
data: { content: mockDockerManifestList },
|
||||
error: null,
|
||||
isLoading: false,
|
||||
refetch: jest.fn()
|
||||
})),
|
||||
getAllArtifactVersions: jest.fn().mockImplementation(
|
||||
() =>
|
||||
new Promise(success => {
|
||||
success({ content: mockDockerVersionList })
|
||||
})
|
||||
),
|
||||
useGetDockerArtifactLayersQuery: jest.fn().mockImplementation(() => ({
|
||||
data: { content: mockDockerArtifactLayers },
|
||||
error: null,
|
||||
isLoading: false,
|
||||
refetch: jest.fn()
|
||||
})),
|
||||
useGetDockerArtifactManifestQuery: jest.fn().mockImplementation(() => ({
|
||||
data: { content: mockDockerArtifactManifest },
|
||||
error: null,
|
||||
isLoading: false,
|
||||
refetch: jest.fn()
|
||||
}))
|
||||
}))
|
||||
|
||||
describe('Verify Docker Version Artifact Details Tab', () => {
|
||||
beforeAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
test('Verify Sub Tabs', async () => {
|
||||
const { container } = render(
|
||||
<ArTestWrapper
|
||||
path="/registries/:repositoryIdentifier/artifacts/:artifactIdentifier/versions/:versionIdentifier/:versionTab"
|
||||
pathParams={{
|
||||
repositoryIdentifier: '1',
|
||||
artifactIdentifier: '1',
|
||||
versionIdentifier: '1',
|
||||
versionTab: 'artifact_details'
|
||||
}}
|
||||
queryParams={{
|
||||
digest: 'sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d',
|
||||
detailsTab: 'layers'
|
||||
}}>
|
||||
<VersionDetailsPage />
|
||||
</ArTestWrapper>
|
||||
)
|
||||
|
||||
expect(getByTestId(container, 'layers')).toBeInTheDocument()
|
||||
expect(getByTestId(container, 'manifest')).toBeInTheDocument()
|
||||
|
||||
const manifestTab = getByTestId(container, 'manifest')
|
||||
await userEvent.click(manifestTab)
|
||||
expect(mockHistoryPush).toHaveBeenCalledWith(
|
||||
'/registries/1/artifacts/1/versions/1/artifact_details?digest=sha256%3A144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d&detailsTab=manifest'
|
||||
)
|
||||
|
||||
const layersTab = getByTestId(container, 'layers')
|
||||
await userEvent.click(layersTab)
|
||||
expect(mockHistoryPush).toHaveBeenCalledWith(
|
||||
'/registries/1/artifacts/1/versions/1/artifact_details?digest=sha256%3A144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d&detailsTab=layers'
|
||||
)
|
||||
})
|
||||
|
||||
test('Verify Layers Tab', async () => {
|
||||
const { container } = render(
|
||||
<ArTestWrapper
|
||||
path="/registries/:repositoryIdentifier/artifacts/:artifactIdentifier/versions/:versionIdentifier/:versionTab"
|
||||
pathParams={{
|
||||
repositoryIdentifier: '1',
|
||||
artifactIdentifier: '1',
|
||||
versionIdentifier: '1',
|
||||
versionTab: 'artifact_details'
|
||||
}}
|
||||
queryParams={{
|
||||
digest: 'sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d',
|
||||
detailsTab: 'layers'
|
||||
}}>
|
||||
<VersionDetailsPage />
|
||||
</ArTestWrapper>
|
||||
)
|
||||
|
||||
expect(container).toHaveTextContent('versionDetails.artifactDetails.layers.imageLayers')
|
||||
|
||||
const getTableRowColumn = (row: number, col: number) => getTableColumn(row, col) as HTMLElement
|
||||
|
||||
const data = mockDockerArtifactLayers.data.layers?.[0]
|
||||
const row = 1
|
||||
expect(getTableRowColumn(row, 1)).toHaveTextContent(row.toString())
|
||||
expect(getTableRowColumn(row, 2)).toHaveTextContent(data?.command as string)
|
||||
expect(getTableRowColumn(row, 3)).toHaveTextContent(data?.size as string)
|
||||
const copyColumn = getTableRowColumn(row, 4)
|
||||
const copyBtn = copyColumn.querySelector('[data-icon="code-copy"]') as HTMLElement
|
||||
expect(copyBtn).toBeInTheDocument()
|
||||
await userEvent.click(copyBtn)
|
||||
expect(copy).toHaveBeenCalledWith(data?.command)
|
||||
})
|
||||
|
||||
test('should show error message if failed to load layers data', async () => {
|
||||
const refetchFn = jest.fn()
|
||||
;(useGetDockerArtifactLayersQuery as jest.Mock).mockImplementation(() => ({
|
||||
data: null,
|
||||
error: { message: 'Failed to fetch artifact details' },
|
||||
isLoading: false,
|
||||
refetch: refetchFn
|
||||
}))
|
||||
const { container } = render(
|
||||
<ArTestWrapper
|
||||
path="/registries/:repositoryIdentifier/artifacts/:artifactIdentifier/versions/:versionIdentifier/:versionTab"
|
||||
pathParams={{
|
||||
repositoryIdentifier: '1',
|
||||
artifactIdentifier: '1',
|
||||
versionIdentifier: '1',
|
||||
versionTab: 'artifact_details'
|
||||
}}
|
||||
queryParams={{
|
||||
digest: 'sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d',
|
||||
detailsTab: 'layers'
|
||||
}}>
|
||||
<VersionDetailsPage />
|
||||
</ArTestWrapper>
|
||||
)
|
||||
expect(getByText(container, 'Failed to fetch artifact details')).toBeInTheDocument()
|
||||
const retryBtn = container.querySelector('button[aria-label=Retry]')
|
||||
expect(retryBtn).toBeInTheDocument()
|
||||
await userEvent.click(retryBtn!)
|
||||
await waitFor(() => {
|
||||
expect(refetchFn).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
test('Verify Manifest Tab', async () => {
|
||||
const { container } = render(
|
||||
<ArTestWrapper
|
||||
path="/registries/:repositoryIdentifier/artifacts/:artifactIdentifier/versions/:versionIdentifier/:versionTab"
|
||||
pathParams={{
|
||||
repositoryIdentifier: '1',
|
||||
artifactIdentifier: '1',
|
||||
versionIdentifier: '1',
|
||||
versionTab: 'artifact_details'
|
||||
}}
|
||||
queryParams={{
|
||||
digest: 'sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d',
|
||||
detailsTab: 'manifest'
|
||||
}}>
|
||||
<VersionDetailsPage />
|
||||
</ArTestWrapper>
|
||||
)
|
||||
|
||||
expect(container).toHaveTextContent('versionDetails.artifactDetails.tabs.manifest')
|
||||
|
||||
const copyBtn = container.querySelector('[data-icon="code-copy"]') as HTMLElement
|
||||
expect(copyBtn).toBeInTheDocument()
|
||||
await userEvent.click(copyBtn)
|
||||
expect(copy).toHaveBeenCalledWith(mockDockerArtifactManifest.data.manifest)
|
||||
})
|
||||
|
||||
test('should show error message if failed to load manifest data', async () => {
|
||||
const refetchFn = jest.fn()
|
||||
;(useGetDockerArtifactManifestQuery as jest.Mock).mockImplementation(() => ({
|
||||
data: null,
|
||||
error: { message: 'Failed to fetch artifact details' },
|
||||
isLoading: false,
|
||||
refetch: refetchFn
|
||||
}))
|
||||
const { container } = render(
|
||||
<ArTestWrapper
|
||||
path="/registries/:repositoryIdentifier/artifacts/:artifactIdentifier/versions/:versionIdentifier/:versionTab"
|
||||
pathParams={{
|
||||
repositoryIdentifier: '1',
|
||||
artifactIdentifier: '1',
|
||||
versionIdentifier: '1',
|
||||
versionTab: 'artifact_details'
|
||||
}}
|
||||
queryParams={{
|
||||
digest: 'sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d',
|
||||
detailsTab: 'manifest'
|
||||
}}>
|
||||
<VersionDetailsPage />
|
||||
</ArTestWrapper>
|
||||
)
|
||||
expect(getByText(container, 'Failed to fetch artifact details')).toBeInTheDocument()
|
||||
const retryBtn = container.querySelector('button[aria-label=Retry]')
|
||||
expect(retryBtn).toBeInTheDocument()
|
||||
await userEvent.click(retryBtn!)
|
||||
await waitFor(() => {
|
||||
expect(refetchFn).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
@ -20,6 +20,7 @@ import { getByTestId, render, waitFor } from '@testing-library/react'
|
||||
import {
|
||||
type DockerManifestDetails,
|
||||
getAllArtifactVersions,
|
||||
useGetArtifactVersionSummaryQuery,
|
||||
useGetDockerArtifactManifestsQuery
|
||||
} from '@harnessio/react-har-service-client'
|
||||
|
||||
@ -29,7 +30,12 @@ import { testSelectChange } from '@ar/utils/testUtils/utils'
|
||||
import ArTestWrapper from '@ar/utils/testUtils/ArTestWrapper'
|
||||
|
||||
import VersionDetailsPage from '../../VersionDetailsPage'
|
||||
import { mockDockerManifestList, mockDockerVersionList, mockDockerVersionSummary } from './__mockData__'
|
||||
import {
|
||||
mockDockerManifestList,
|
||||
mockDockerVersionList,
|
||||
mockDockerVersionSummary,
|
||||
mockDockerVersionSummaryWithoutSscaAndStoData
|
||||
} from './__mockData__'
|
||||
|
||||
const mockHistoryPush = jest.fn()
|
||||
// eslint-disable-next-line jest-no-mock
|
||||
@ -124,7 +130,7 @@ describe('Verify DockerVersionHeader component render', () => {
|
||||
await testSelectChange(versionSelector, '1.0.1', data.version)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockHistoryPush).toHaveBeenLastCalledWith('/registries/artifacts/versions/1.0.1')
|
||||
expect(mockHistoryPush).toHaveBeenCalledWith('/registries/artifacts/versions/1.0.1')
|
||||
})
|
||||
})
|
||||
|
||||
@ -262,4 +268,80 @@ describe('Verify DockerVersionHeader component render', () => {
|
||||
const selectPopover = dialogs[0] as HTMLElement
|
||||
expect(selectPopover).toHaveTextContent('No items found')
|
||||
})
|
||||
|
||||
test('verify tab navigation with ssca and sto data', async () => {
|
||||
const { container } = render(
|
||||
<ArTestWrapper
|
||||
queryParams={{
|
||||
digest: 'sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d'
|
||||
}}>
|
||||
<VersionDetailsPage />
|
||||
</ArTestWrapper>
|
||||
)
|
||||
|
||||
const overviewTab = container.querySelector('div[data-tab-id=overview]')
|
||||
await userEvent.click(overviewTab!)
|
||||
expect(mockHistoryPush).toHaveBeenLastCalledWith(
|
||||
'/registries/undefined/artifacts/undefined/versions/undefined/overview?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d'
|
||||
)
|
||||
|
||||
const artifactDetailsTab = container.querySelector('div[data-tab-id=artifact_details]')
|
||||
await userEvent.click(artifactDetailsTab!)
|
||||
expect(mockHistoryPush).toHaveBeenLastCalledWith(
|
||||
'/registries/undefined/artifacts/undefined/versions/undefined/artifact_details?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d'
|
||||
)
|
||||
|
||||
const sscaTab = container.querySelector('div[data-tab-id=supply_chain]')
|
||||
await userEvent.click(sscaTab!)
|
||||
expect(mockHistoryPush).toHaveBeenLastCalledWith(
|
||||
'/registries/undefined/artifacts/undefined/versions/undefined/artifact-sources/67a5dccf6d75916b0c3ea1b5/artifacts/67a5dccf6d75916b0c3ea1b6/supply_chain?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d'
|
||||
)
|
||||
|
||||
const stoTab = container.querySelector('div[data-tab-id=security_tests]')
|
||||
await userEvent.click(stoTab!)
|
||||
expect(mockHistoryPush).toHaveBeenLastCalledWith(
|
||||
'/registries/undefined/artifacts/undefined/versions/undefined/pipelines/HARNESS_ARTIFACT_SCAN_PIPELINE/executions/Tbi7s6nETjmOMKU3Qrnm7A/security_tests?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d'
|
||||
)
|
||||
})
|
||||
|
||||
test('verify tab navigation with ssca and sto data', async () => {
|
||||
;(useGetArtifactVersionSummaryQuery as jest.Mock).mockImplementation(() => ({
|
||||
data: { content: mockDockerVersionSummaryWithoutSscaAndStoData },
|
||||
error: null,
|
||||
isLoading: false,
|
||||
refetch: jest.fn()
|
||||
}))
|
||||
const { container } = render(
|
||||
<ArTestWrapper
|
||||
queryParams={{
|
||||
digest: 'sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d'
|
||||
}}>
|
||||
<VersionDetailsPage />
|
||||
</ArTestWrapper>
|
||||
)
|
||||
|
||||
const overviewTab = container.querySelector('div[data-tab-id=overview]')
|
||||
await userEvent.click(overviewTab!)
|
||||
expect(mockHistoryPush).toHaveBeenLastCalledWith(
|
||||
'/registries/undefined/artifacts/undefined/versions/undefined/overview?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d'
|
||||
)
|
||||
|
||||
const artifactDetailsTab = container.querySelector('div[data-tab-id=artifact_details]')
|
||||
await userEvent.click(artifactDetailsTab!)
|
||||
expect(mockHistoryPush).toHaveBeenLastCalledWith(
|
||||
'/registries/undefined/artifacts/undefined/versions/undefined/artifact_details?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d'
|
||||
)
|
||||
|
||||
const sscaTab = container.querySelector('div[data-tab-id=supply_chain]')
|
||||
await userEvent.click(sscaTab!)
|
||||
expect(mockHistoryPush).toHaveBeenLastCalledWith(
|
||||
'/registries/undefined/artifacts/undefined/versions/undefined/supply_chain?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d'
|
||||
)
|
||||
|
||||
const stoTab = container.querySelector('div[data-tab-id=security_tests]')
|
||||
await userEvent.click(stoTab!)
|
||||
expect(mockHistoryPush).toHaveBeenLastCalledWith(
|
||||
'/registries/undefined/artifacts/undefined/versions/undefined/security_tests?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
useGetDockerArtifactDetailsQuery,
|
||||
useGetDockerArtifactIntegrationDetailsQuery
|
||||
} from '@harnessio/react-har-service-client'
|
||||
import { downloadSbom } from '@harnessio/react-ssca-manager-client'
|
||||
|
||||
import ArTestWrapper from '@ar/utils/testUtils/ArTestWrapper'
|
||||
|
||||
@ -34,10 +35,31 @@ import {
|
||||
mockDockerArtifactDetails,
|
||||
mockDockerArtifactIntegrationDetails,
|
||||
mockDockerManifestList,
|
||||
mockDockerSbomData,
|
||||
mockDockerVersionList,
|
||||
mockDockerVersionSummary
|
||||
} from './__mockData__'
|
||||
|
||||
const mockHistoryPush = jest.fn()
|
||||
// eslint-disable-next-line jest-no-mock
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useHistory: () => ({
|
||||
push: mockHistoryPush
|
||||
})
|
||||
}))
|
||||
|
||||
const showErrorToast = jest.fn()
|
||||
|
||||
jest.mock('@harnessio/uicore', () => ({
|
||||
...jest.requireActual('@harnessio/uicore'),
|
||||
useToaster: jest.fn().mockImplementation(() => ({
|
||||
showSuccess: jest.fn(),
|
||||
showError: showErrorToast,
|
||||
clear: jest.fn()
|
||||
}))
|
||||
}))
|
||||
|
||||
jest.mock('@harnessio/react-har-service-client', () => ({
|
||||
useGetArtifactVersionSummaryQuery: jest.fn().mockImplementation(() => ({
|
||||
data: { content: mockDockerVersionSummary },
|
||||
@ -71,6 +93,15 @@ jest.mock('@harnessio/react-har-service-client', () => ({
|
||||
}))
|
||||
}))
|
||||
|
||||
jest.mock('@harnessio/react-ssca-manager-client', () => ({
|
||||
downloadSbom: jest.fn().mockImplementation(
|
||||
() =>
|
||||
new Promise(success => {
|
||||
success({ content: mockDockerSbomData })
|
||||
})
|
||||
)
|
||||
}))
|
||||
|
||||
describe('Verify docker version overview page', () => {
|
||||
beforeAll(() => {
|
||||
jest.clearAllMocks()
|
||||
@ -99,12 +130,24 @@ describe('Verify docker version overview page', () => {
|
||||
expect(deploymentCard).toHaveTextContent(data.deploymentsDetails?.totalDeployment?.toString() as string)
|
||||
expect(deploymentCard).toHaveTextContent(data.buildDetails?.pipelineDisplayName as string)
|
||||
expect(deploymentCard).toHaveTextContent(data.buildDetails?.pipelineExecutionId as string)
|
||||
await userEvent.click(deploymentCard)
|
||||
await waitFor(() => {
|
||||
expect(mockHistoryPush).toHaveBeenCalledWith(
|
||||
'/registries/1/artifacts/1/versions/1/deployments?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d'
|
||||
)
|
||||
})
|
||||
|
||||
const sscaCard = getByTestId(container, 'integration-supply-chain-card')
|
||||
expect(sscaCard).toBeInTheDocument()
|
||||
expect(sscaCard).toHaveTextContent(data.sbomDetails?.componentsCount?.toString() as string)
|
||||
expect(sscaCard).toHaveTextContent(Number(data.sbomDetails?.avgScore).toFixed(2) as string)
|
||||
expect(getByText(sscaCard, 'versionDetails.cards.supplyChain.downloadSbom')).toBeInTheDocument()
|
||||
await userEvent.click(sscaCard)
|
||||
await waitFor(() => {
|
||||
expect(mockHistoryPush).toHaveBeenCalledWith(
|
||||
'/registries/1/artifacts/1/versions/1/artifact-sources/67a5dccf6d75916b0c3ea1b5/artifacts/67a5dccf6d75916b0c3ea1b6/supply_chain?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d'
|
||||
)
|
||||
})
|
||||
|
||||
const securityTestsCard = getByTestId(container, 'integration-security-tests-card')
|
||||
expect(securityTestsCard).toBeInTheDocument()
|
||||
@ -113,6 +156,62 @@ describe('Verify docker version overview page', () => {
|
||||
expect(securityTestsCard).toHaveTextContent(data.stoDetails?.high?.toString() as string)
|
||||
expect(securityTestsCard).toHaveTextContent(data.stoDetails?.medium?.toString() as string)
|
||||
expect(securityTestsCard).toHaveTextContent(data.stoDetails?.low?.toString() as string)
|
||||
await userEvent.click(securityTestsCard)
|
||||
await waitFor(() => {
|
||||
expect(mockHistoryPush).toHaveBeenCalledWith(
|
||||
'/registries/1/artifacts/1/versions/1/pipelines/HARNESS_ARTIFACT_SCAN_PIPELINE/executions/Tbi7s6nETjmOMKU3Qrnm7A/security_tests?digest=sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
test('Verify action button on ssca card', async () => {
|
||||
const { container } = render(
|
||||
<ArTestWrapper
|
||||
path="/registries/:repositoryIdentifier/artifacts/:artifactIdentifier/versions/:versionIdentifier/:versionTab"
|
||||
pathParams={{
|
||||
repositoryIdentifier: '1',
|
||||
artifactIdentifier: '1',
|
||||
versionIdentifier: '1',
|
||||
versionTab: 'overview'
|
||||
}}
|
||||
queryParams={{ digest: 'sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d' }}>
|
||||
<VersionDetailsPage />
|
||||
</ArTestWrapper>
|
||||
)
|
||||
|
||||
expect(getByTestId(container, 'integration-cards')).toBeInTheDocument()
|
||||
|
||||
const sscaCard = getByTestId(container, 'integration-supply-chain-card')
|
||||
const downloadSbomBtn = getByText(sscaCard, 'versionDetails.cards.supplyChain.downloadSbom')
|
||||
expect(downloadSbomBtn).toBeInTheDocument()
|
||||
await userEvent.click(downloadSbomBtn)
|
||||
await waitFor(() => {
|
||||
expect(downloadSbom).toHaveBeenCalledWith({ 'orchestration-id': 'yw0D70fiTqetxx0HIyvEUQ', org: '', project: '' })
|
||||
})
|
||||
|
||||
// empty response scenario
|
||||
;(downloadSbom as jest.Mock).mockImplementationOnce(
|
||||
() =>
|
||||
new Promise(success => {
|
||||
success({ content: { sbom: null } })
|
||||
})
|
||||
)
|
||||
await userEvent.click(downloadSbomBtn)
|
||||
await waitFor(() => {
|
||||
expect(showErrorToast).toHaveBeenCalledWith('versionDetails.cards.supplyChain.SBOMDataNotAvailable')
|
||||
})
|
||||
|
||||
// error scenario
|
||||
;(downloadSbom as jest.Mock).mockImplementationOnce(
|
||||
() =>
|
||||
new Promise((_, reject) => {
|
||||
reject({ message: 'error message' })
|
||||
})
|
||||
)
|
||||
await userEvent.click(downloadSbomBtn)
|
||||
await waitFor(() => {
|
||||
expect(showErrorToast).toHaveBeenCalledWith('error message')
|
||||
})
|
||||
})
|
||||
|
||||
test('should show error message if failed to load integration data', async () => {
|
||||
|
@ -18,6 +18,8 @@ import type {
|
||||
ArtifactVersionSummaryResponseResponse,
|
||||
DockerArtifactDetailIntegrationResponseResponse,
|
||||
DockerArtifactDetailResponseResponse,
|
||||
DockerArtifactManifestResponseResponse,
|
||||
DockerLayersResponseResponse,
|
||||
DockerManifestsResponseResponse,
|
||||
ListArtifactVersion,
|
||||
ListArtifactVersionResponseResponse
|
||||
@ -86,6 +88,15 @@ export const mockDockerVersionSummary: ArtifactVersionSummaryResponseResponse =
|
||||
status: 'SUCCESS'
|
||||
}
|
||||
|
||||
export const mockDockerVersionSummaryWithoutSscaAndStoData: ArtifactVersionSummaryResponseResponse = {
|
||||
data: {
|
||||
imageName: 'maven-app',
|
||||
packageType: 'DOCKER',
|
||||
version: '1.0.0'
|
||||
},
|
||||
status: 'SUCCESS'
|
||||
}
|
||||
|
||||
export const mockDockerVersionList: ListArtifactVersionResponseResponse = {
|
||||
data: {
|
||||
artifactVersions: [
|
||||
@ -238,3 +249,80 @@ export const mockDockerArtifactIntegrationDetails: DockerArtifactDetailIntegrati
|
||||
},
|
||||
status: 'SUCCESS'
|
||||
}
|
||||
|
||||
export const mockDockerArtifactLayers: DockerLayersResponseResponse = {
|
||||
data: {
|
||||
digest: 'sha256:144cdab68a435424250fe06e9a4f8a5f6b6b8a8a55d257bc6ee77476a6ec520d',
|
||||
layers: [
|
||||
{
|
||||
command: '/bin/sh -c #(nop) ADD file:6fef7a4ab2de57c438dad76949e7eb87cfb1ea6f45b0f2423f71188efdaa0d8e in /',
|
||||
size: '40.07 MB'
|
||||
},
|
||||
{
|
||||
command: '/bin/sh -c #(nop) CMD ["/bin/bash"]',
|
||||
size: '0 B'
|
||||
},
|
||||
{
|
||||
command:
|
||||
'/bin/sh -c set -eux; \tmicrodnf install \t\tgzip \t\ttar \t\t\t\tbinutils \t\tfreetype fontconfig \t; \tmicrodnf clean all',
|
||||
size: '13.63 MB'
|
||||
},
|
||||
{
|
||||
command: '/bin/sh -c #(nop) ENV JAVA_HOME=/usr/java/openjdk-17',
|
||||
size: '0 B'
|
||||
},
|
||||
{
|
||||
command:
|
||||
'/bin/sh -c #(nop) ENV PATH=/usr/java/openjdk-17/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
|
||||
size: '0 B'
|
||||
},
|
||||
{
|
||||
command: '/bin/sh -c #(nop) ENV LANG=C.UTF-8',
|
||||
size: '0 B'
|
||||
},
|
||||
{
|
||||
command: '/bin/sh -c #(nop) ENV JAVA_VERSION=17.0.2',
|
||||
size: '0 B'
|
||||
},
|
||||
{
|
||||
command:
|
||||
'/bin/sh -c set -eux; \t\tarch="$(objdump="$(command -v objdump)" \u0026\u0026 objdump --file-headers "$objdump" | awk -F \'[:,]+[[:space:]]+\' \'$1 == "architecture" { print $2 }\')"; \tcase "$arch" in \t\t\'i386:x86-64\') \t\t\tdownloadUrl=\'https://download.java.net/java/GA/jdk17.0.2/dfd4a8d0985749f896bed50d7138ee7f/8/GPL/openjdk-17.0.2_linux-x64_bin.tar.gz\'; \t\t\tdownloadSha256=\'0022753d0cceecacdd3a795dd4cea2bd7ffdf9dc06e22ffd1be98411742fbb44\'; \t\t\t;; \t\t\'aarch64\') \t\t\tdownloadUrl=\'https://download.java.net/java/GA/jdk17.0.2/dfd4a8d0985749f896bed50d7138ee7f/8/GPL/openjdk-17.0.2_linux-aarch64_bin.tar.gz\'; \t\t\tdownloadSha256=\'13bfd976acf8803f862e82c7113fb0e9311ca5458b1decaef8a09ffd91119fa4\'; \t\t\t;; \t\t*) echo \u003e\u00262 "error: unsupported architecture: \'$arch\'"; exit 1 ;; \tesac; \t\tcurl -fL -o openjdk.tgz "$downloadUrl"; \techo "$downloadSha256 *openjdk.tgz" | sha256sum --strict --check -; \t\tmkdir -p "$JAVA_HOME"; \ttar --extract \t\t--file openjdk.tgz \t\t--directory "$JAVA_HOME" \t\t--strip-components 1 \t\t--no-same-owner \t; \trm openjdk.tgz*; \t\trm -rf "$JAVA_HOME/lib/security/cacerts"; \tln -sT /etc/pki/ca-trust/extracted/java/cacerts "$JAVA_HOME/lib/security/cacerts"; \t\tln -sfT "$JAVA_HOME" /usr/java/default; \tln -sfT "$JAVA_HOME" /usr/java/latest; \tfor bin in "$JAVA_HOME/bin/"*; do \t\tbase="$(basename "$bin")"; \t\t[ ! -e "/usr/bin/$base" ]; \t\talternatives --install "/usr/bin/$base" "$base" "$bin" 20000; \tdone; \t\tjava -Xshare:dump; \t\tfileEncoding="$(echo \'System.out.println(System.getProperty("file.encoding"))\' | jshell -s -)"; [ "$fileEncoding" = \'UTF-8\' ]; rm -rf ~/.java; \tjavac --version; \tjava --version',
|
||||
size: '177.73 MB'
|
||||
},
|
||||
{
|
||||
command: '/bin/sh -c #(nop) CMD ["jshell"]',
|
||||
size: '0 B'
|
||||
},
|
||||
{
|
||||
command: 'WORKDIR /app',
|
||||
size: '93 B'
|
||||
},
|
||||
{
|
||||
command: 'COPY target/mavensampleapp-1.0.0.jar app.jar # buildkit',
|
||||
size: '14.98 MB'
|
||||
},
|
||||
{
|
||||
command: 'EXPOSE map[8080/tcp:{}]',
|
||||
size: '0 B'
|
||||
},
|
||||
{
|
||||
command: 'ENTRYPOINT ["java" "-jar" "app.jar"]',
|
||||
size: '0 B'
|
||||
}
|
||||
],
|
||||
osArch: 'linux/arm64'
|
||||
},
|
||||
status: 'SUCCESS'
|
||||
}
|
||||
|
||||
export const mockDockerArtifactManifest: DockerArtifactManifestResponseResponse = {
|
||||
data: {
|
||||
manifest:
|
||||
'{\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "digest": "sha256:f152582e19dbda7e3fb677e83af07fb60b05a56757b15edd8915aef2191aad62",\n "size": 4682\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "digest": "sha256:416105dc84fc8cf66df5d2c9f81570a2cc36a6cae58aedd4d58792f041f7a2f5",\n "size": 42018977\n },\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "digest": "sha256:fe66142579ff5bb0bb5cf989222e2bc77a97dcbd0283887dec04d5b9dfd48cfa",\n "size": 14294224\n },\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "digest": "sha256:1250d2aa493e8744c8f6cb528c8a882c14b6d7ff0af6862bbbfe676f60ea979e",\n "size": 186363988\n },\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "digest": "sha256:6a92a853917fafa754249a4f309e5c34caae0ee5df4369dc1c9383d7cd1b395e",\n "size": 93\n },\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "digest": "sha256:b2e80a78034b601c2513a1b00947bda6c6cda46a95e217b954ed84f5b1b5c0fe",\n "size": 15712414\n }\n ]\n}'
|
||||
},
|
||||
status: 'SUCCESS'
|
||||
}
|
||||
|
||||
export const mockDockerSbomData = {
|
||||
sbom: 'Test Data'
|
||||
}
|
||||
|
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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 { useGetArtifactFilesQuery } from '@harnessio/react-har-service-client'
|
||||
import { getByText, queryByText, render, waitFor } from '@testing-library/react'
|
||||
|
||||
import { getTableColumn } from '@ar/utils/testUtils/utils'
|
||||
import ArTestWrapper from '@ar/utils/testUtils/ArTestWrapper'
|
||||
|
||||
import VersionDetailsPage from '../../VersionDetailsPage'
|
||||
import { mockGenericArtifactFiles, mockGenericVersionList, mockGenericVersionSummary } from './__mockData__'
|
||||
|
||||
const mockHistoryPush = jest.fn()
|
||||
// eslint-disable-next-line jest-no-mock
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useHistory: () => ({
|
||||
push: mockHistoryPush
|
||||
})
|
||||
}))
|
||||
|
||||
jest.mock('clipboard-copy', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn()
|
||||
}))
|
||||
|
||||
jest.mock('@harnessio/react-har-service-client', () => ({
|
||||
useGetArtifactVersionSummaryQuery: jest.fn().mockImplementation(() => ({
|
||||
data: { content: mockGenericVersionSummary },
|
||||
error: null,
|
||||
isLoading: false,
|
||||
refetch: jest.fn()
|
||||
})),
|
||||
getAllArtifactVersions: jest.fn().mockImplementation(
|
||||
() =>
|
||||
new Promise(success => {
|
||||
success({ content: mockGenericVersionList })
|
||||
})
|
||||
),
|
||||
useGetArtifactFilesQuery: jest.fn().mockImplementation(() => ({
|
||||
data: { content: mockGenericArtifactFiles },
|
||||
error: null,
|
||||
isLoading: false,
|
||||
refetch: jest.fn()
|
||||
}))
|
||||
}))
|
||||
|
||||
describe('Verify Generic Artifact Version Artifact Details Tab', () => {
|
||||
test('verify file list table', async () => {
|
||||
const { container } = render(
|
||||
<ArTestWrapper
|
||||
path="/registries/:repositoryIdentifier/artifacts/:artifactIdentifier/versions/:versionIdentifier/:versionTab"
|
||||
pathParams={{
|
||||
repositoryIdentifier: '1',
|
||||
artifactIdentifier: '1',
|
||||
versionIdentifier: '1',
|
||||
versionTab: 'artifact_details'
|
||||
}}>
|
||||
<VersionDetailsPage />
|
||||
</ArTestWrapper>
|
||||
)
|
||||
|
||||
expect(container).toBeInTheDocument()
|
||||
|
||||
const getTableRowColumn = (row: number, col: number) => getTableColumn(row, col) as HTMLElement
|
||||
|
||||
const data = mockGenericArtifactFiles.files[0]
|
||||
const row = 1
|
||||
expect(getTableRowColumn(row, 1)).toHaveTextContent(data.name)
|
||||
expect(getTableRowColumn(row, 2)).toHaveTextContent(data?.size as string)
|
||||
|
||||
const column3Content = getTableRowColumn(row, 3)
|
||||
data.checksums.forEach(async each => {
|
||||
const [checksum, value] = each.split(': ')
|
||||
const btn = getByText(column3Content, `copy ${checksum}`)
|
||||
await userEvent.click(btn)
|
||||
expect(copy).toHaveBeenCalledWith(value)
|
||||
})
|
||||
|
||||
const copyColumn = getTableRowColumn(row, 4)
|
||||
expect(copyColumn).toHaveTextContent('copy')
|
||||
const copyBtn = copyColumn.querySelector('[data-icon="code-copy"]') as HTMLElement
|
||||
expect(copyBtn).toBeInTheDocument()
|
||||
await userEvent.click(copyBtn)
|
||||
expect(copy).toHaveBeenCalledWith(data?.downloadCommand)
|
||||
})
|
||||
|
||||
test('verify pagination and sorting actions', async () => {
|
||||
const { container } = render(
|
||||
<ArTestWrapper
|
||||
path="/registries/:repositoryIdentifier/artifacts/:artifactIdentifier/versions/:versionIdentifier/:versionTab"
|
||||
pathParams={{
|
||||
repositoryIdentifier: '1',
|
||||
artifactIdentifier: '1',
|
||||
versionIdentifier: '1',
|
||||
versionTab: 'artifact_details'
|
||||
}}>
|
||||
<VersionDetailsPage />
|
||||
</ArTestWrapper>
|
||||
)
|
||||
const nextPageBtn = getByText(container, 'Next')
|
||||
await userEvent.click(nextPageBtn)
|
||||
|
||||
expect(useGetArtifactFilesQuery).toHaveBeenLastCalledWith({
|
||||
artifact: '1/+',
|
||||
queryParams: { page: 0, size: 50, sort_field: 'updatedAt', sort_order: 'DESC' },
|
||||
registry_ref: 'undefined/1/+',
|
||||
version: '1'
|
||||
})
|
||||
|
||||
const fileNameSortIcon = getByText(container, 'versionDetails.artifactFiles.table.columns.name').nextSibling
|
||||
?.firstChild as HTMLElement
|
||||
await userEvent.click(fileNameSortIcon)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(useGetArtifactFilesQuery).toHaveBeenLastCalledWith({
|
||||
artifact: '1/+',
|
||||
queryParams: { page: 0, size: 50, sort_field: 'updatedAt', sort_order: 'DESC' },
|
||||
registry_ref: 'undefined/1/+',
|
||||
version: '1'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('verify error message', async () => {
|
||||
const mockRefetchFn = jest.fn().mockImplementation(() => undefined)
|
||||
;(useGetArtifactFilesQuery as jest.Mock).mockImplementationOnce(() => {
|
||||
return {
|
||||
data: null,
|
||||
loading: false,
|
||||
error: { message: 'error message' },
|
||||
refetch: mockRefetchFn
|
||||
}
|
||||
})
|
||||
|
||||
const { container } = render(
|
||||
<ArTestWrapper
|
||||
path="/registries/:repositoryIdentifier/artifacts/:artifactIdentifier/versions/:versionIdentifier/:versionTab"
|
||||
pathParams={{
|
||||
repositoryIdentifier: '1',
|
||||
artifactIdentifier: '1',
|
||||
versionIdentifier: '1',
|
||||
versionTab: 'artifact_details'
|
||||
}}>
|
||||
<VersionDetailsPage />
|
||||
</ArTestWrapper>
|
||||
)
|
||||
|
||||
const errorText = getByText(container, 'error message')
|
||||
expect(errorText).toBeInTheDocument()
|
||||
|
||||
const retryBtn = getByText(container, 'Retry')
|
||||
expect(retryBtn).toBeInTheDocument()
|
||||
|
||||
await userEvent.click(retryBtn)
|
||||
expect(mockRefetchFn).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('verify with empty pagination response', async () => {
|
||||
const mockRefetchFn = jest.fn().mockImplementation(() => undefined)
|
||||
;(useGetArtifactFilesQuery as jest.Mock).mockImplementationOnce(() => {
|
||||
return {
|
||||
data: { content: { files: mockGenericArtifactFiles.files } },
|
||||
loading: false,
|
||||
error: null,
|
||||
refetch: mockRefetchFn
|
||||
}
|
||||
})
|
||||
|
||||
const { container } = render(
|
||||
<ArTestWrapper
|
||||
path="/registries/:repositoryIdentifier/artifacts/:artifactIdentifier/versions/:versionIdentifier/:versionTab"
|
||||
pathParams={{
|
||||
repositoryIdentifier: '1',
|
||||
artifactIdentifier: '1',
|
||||
versionIdentifier: '1',
|
||||
versionTab: 'artifact_details'
|
||||
}}>
|
||||
<VersionDetailsPage />
|
||||
</ArTestWrapper>
|
||||
)
|
||||
|
||||
const nextPageBtn = queryByText(container, 'Next')
|
||||
expect(nextPageBtn).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
@ -17,6 +17,8 @@
|
||||
import type {
|
||||
ArtifactDetailResponseResponse,
|
||||
ArtifactVersionSummaryResponseResponse,
|
||||
FileDetail,
|
||||
FileDetailResponseResponse,
|
||||
ListArtifactVersion,
|
||||
ListArtifactVersionResponseResponse
|
||||
} from '@harnessio/react-har-service-client'
|
||||
@ -119,3 +121,31 @@ export const mockGenericArtifactDetails: ArtifactDetailResponseResponse = {
|
||||
},
|
||||
status: 'SUCCESS'
|
||||
}
|
||||
|
||||
export const mockGenericArtifactFiles: FileDetailResponseResponse = {
|
||||
files: [
|
||||
{
|
||||
checksums: [
|
||||
'SHA-512: ebfd0613c1c298d97fd7743ecd731b96a9c0a7f7cdbfdd9f19ec4682f9b4ceb400420a6191c9671bfb3e1cc5a9fef873ea1ad78f1b30794989a0fdb591f847cd',
|
||||
'SHA-256: cc5ac7831841b65901c5578a47d6b125259f9a4364d1d73b0b5d8891ad3b7d34',
|
||||
'SHA-1: b0e3200eb5eaca07d773916e306cd1ed9866d3a4',
|
||||
'MD5: cc576cbab9119ad7589cae7b57146af6'
|
||||
],
|
||||
createdAt: '1738258177381',
|
||||
downloadCommand:
|
||||
"curl --location 'https://pkg.qa.harness.io/generic/iWnhltqOT7GFt7R-F_zP7Q/generic-registry/artifact:v1:image.png' --header 'x-api-key: \u003cAPI_KEY\u003e' -J -O",
|
||||
name: 'image.png',
|
||||
size: '170.18KB'
|
||||
},
|
||||
{
|
||||
createdAt: '1738085520008',
|
||||
name: 'hello.yaml',
|
||||
size: '2.79MB'
|
||||
} as FileDetail
|
||||
],
|
||||
itemCount: 2,
|
||||
pageCount: 4,
|
||||
pageIndex: 0,
|
||||
pageSize: 50,
|
||||
status: 'SUCCESS'
|
||||
}
|
||||
|
@ -1,22 +0,0 @@
|
||||
/*
|
||||
* 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 DeploymentsContent from '@ar/pages/version-details/components/DeploymentsContent/DeploymentsContent'
|
||||
|
||||
export default function GenericArtifactDeploymentsPage() {
|
||||
return <DeploymentsContent />
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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 { getByText, render, waitFor } from '@testing-library/react'
|
||||
import { useGetHelmArtifactManifestQuery } from '@harnessio/react-har-service-client'
|
||||
|
||||
import ArTestWrapper from '@ar/utils/testUtils/ArTestWrapper'
|
||||
|
||||
import { prettifyManifestJSON } from '../../utils'
|
||||
import VersionDetailsPage from '../../VersionDetailsPage'
|
||||
import { mockHelmArtifactManifest, mockHelmVersionList, mockHelmVersionSummary } from './__mockData__'
|
||||
|
||||
const mockHistoryPush = jest.fn()
|
||||
// eslint-disable-next-line jest-no-mock
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useHistory: () => ({
|
||||
push: mockHistoryPush
|
||||
})
|
||||
}))
|
||||
|
||||
jest.mock('clipboard-copy', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn()
|
||||
}))
|
||||
|
||||
jest.mock('@harnessio/react-har-service-client', () => ({
|
||||
useGetArtifactVersionSummaryQuery: jest.fn().mockImplementation(() => ({
|
||||
data: { content: mockHelmVersionSummary },
|
||||
error: null,
|
||||
isLoading: false,
|
||||
refetch: jest.fn()
|
||||
})),
|
||||
getAllArtifactVersions: jest.fn().mockImplementation(
|
||||
() =>
|
||||
new Promise(success => {
|
||||
success({ content: mockHelmVersionList })
|
||||
})
|
||||
),
|
||||
useGetHelmArtifactManifestQuery: jest.fn().mockImplementation(() => ({
|
||||
data: { content: mockHelmArtifactManifest },
|
||||
error: null,
|
||||
isLoading: false,
|
||||
refetch: jest.fn()
|
||||
}))
|
||||
}))
|
||||
|
||||
describe('Verify Helm Version Artifact Details Tab', () => {
|
||||
beforeAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
test('Verify Manifest', async () => {
|
||||
const { container } = render(
|
||||
<ArTestWrapper
|
||||
path="/registries/:repositoryIdentifier/artifacts/:artifactIdentifier/versions/:versionIdentifier/:versionTab"
|
||||
pathParams={{
|
||||
repositoryIdentifier: '1',
|
||||
artifactIdentifier: '1',
|
||||
versionIdentifier: '1',
|
||||
versionTab: 'artifact_details'
|
||||
}}>
|
||||
<VersionDetailsPage />
|
||||
</ArTestWrapper>
|
||||
)
|
||||
|
||||
expect(container).toHaveTextContent('versionDetails.artifactDetails.tabs.manifest')
|
||||
|
||||
const copyBtn = container.querySelector('[data-icon="code-copy"]') as HTMLElement
|
||||
expect(copyBtn).toBeInTheDocument()
|
||||
await userEvent.click(copyBtn)
|
||||
expect(copy).toHaveBeenCalledWith(prettifyManifestJSON(mockHelmArtifactManifest.data.manifest))
|
||||
})
|
||||
|
||||
test('should show error message if failed to load manifest data', async () => {
|
||||
const refetchFn = jest.fn()
|
||||
;(useGetHelmArtifactManifestQuery as jest.Mock).mockImplementation(() => ({
|
||||
data: null,
|
||||
error: { message: 'Failed to fetch artifact details' },
|
||||
isLoading: false,
|
||||
refetch: refetchFn
|
||||
}))
|
||||
const { container } = render(
|
||||
<ArTestWrapper
|
||||
path="/registries/:repositoryIdentifier/artifacts/:artifactIdentifier/versions/:versionIdentifier/:versionTab"
|
||||
pathParams={{
|
||||
repositoryIdentifier: '1',
|
||||
artifactIdentifier: '1',
|
||||
versionIdentifier: '1',
|
||||
versionTab: 'artifact_details'
|
||||
}}>
|
||||
<VersionDetailsPage />
|
||||
</ArTestWrapper>
|
||||
)
|
||||
expect(getByText(container, 'Failed to fetch artifact details')).toBeInTheDocument()
|
||||
const retryBtn = container.querySelector('button[aria-label=Retry]')
|
||||
expect(retryBtn).toBeInTheDocument()
|
||||
await userEvent.click(retryBtn!)
|
||||
await waitFor(() => {
|
||||
expect(refetchFn).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
@ -17,6 +17,7 @@
|
||||
import type {
|
||||
ArtifactVersionSummaryResponseResponse,
|
||||
HelmArtifactDetailResponseResponse,
|
||||
HelmArtifactManifestResponseResponse,
|
||||
ListArtifactVersion,
|
||||
ListArtifactVersionResponseResponse
|
||||
} from '@harnessio/react-har-service-client'
|
||||
@ -128,3 +129,11 @@ export const mockHelmArtifactDetails: HelmArtifactDetailResponseResponse = {
|
||||
},
|
||||
status: 'SUCCESS'
|
||||
}
|
||||
|
||||
export const mockHelmArtifactManifest: HelmArtifactManifestResponseResponse = {
|
||||
data: {
|
||||
manifest:
|
||||
'{"schemaVersion":2,"config":{"mediaType":"application/vnd.cncf.helm.config.v1+json","digest":"sha256:3d3ca122368140982b0a494f4f357fff2fa9894f9adcc8809fa8e74e2a327d94","size":161},"layers":[{"mediaType":"application/vnd.cncf.helm.chart.content.v1.tar+gzip","digest":"sha256:a8b12f90950f22927e8c2e4f3e9b32655ae5287e95ae801662cef7cf66bd9be3","size":8062}],"annotations":{"org.opencontainers.image.created":"2024-10-25T18:43:34+05:30","org.opencontainers.image.description":"A Helm chart for deploying harness-delegate","org.opencontainers.image.title":"production","org.opencontainers.image.version":"1.0.15"}}'
|
||||
},
|
||||
status: 'SUCCESS'
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* 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 { Layout } from '@harnessio/uicore'
|
||||
|
||||
import DeploymentsCard from '@ar/pages/version-details/components/DeploymentsCard/DeploymentsCard'
|
||||
|
||||
export default function HelmVersionOverviewCards() {
|
||||
return (
|
||||
<Layout.Horizontal width="100%" spacing="medium">
|
||||
<DeploymentsCard prodCount={10} nonProdCount={12} pipelineName="TestPipeline" executionId="1234566" />
|
||||
</Layout.Horizontal>
|
||||
)
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* 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 { getByText, render } from '@testing-library/react'
|
||||
import { useGetArtifactFilesQuery } from '@harnessio/react-har-service-client'
|
||||
|
||||
import { getTableColumn } from '@ar/utils/testUtils/utils'
|
||||
import ArTestWrapper from '@ar/utils/testUtils/ArTestWrapper'
|
||||
|
||||
import VersionDetailsPage from '../../VersionDetailsPage'
|
||||
import { mockMavenArtifactFiles, mockMavenVersionList, mockMavenVersionSummary } from './__mockData__'
|
||||
|
||||
const mockHistoryPush = jest.fn()
|
||||
// eslint-disable-next-line jest-no-mock
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useHistory: () => ({
|
||||
push: mockHistoryPush
|
||||
})
|
||||
}))
|
||||
|
||||
jest.mock('clipboard-copy', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn()
|
||||
}))
|
||||
|
||||
jest.mock('@harnessio/react-har-service-client', () => ({
|
||||
useGetArtifactVersionSummaryQuery: jest.fn().mockImplementation(() => ({
|
||||
data: { content: mockMavenVersionSummary },
|
||||
error: null,
|
||||
isLoading: false,
|
||||
refetch: jest.fn()
|
||||
})),
|
||||
getAllArtifactVersions: jest.fn().mockImplementation(
|
||||
() =>
|
||||
new Promise(success => {
|
||||
success({ content: mockMavenVersionList })
|
||||
})
|
||||
),
|
||||
useGetArtifactFilesQuery: jest.fn().mockImplementation(() => ({
|
||||
data: { content: mockMavenArtifactFiles },
|
||||
error: null,
|
||||
isLoading: false,
|
||||
refetch: jest.fn()
|
||||
}))
|
||||
}))
|
||||
|
||||
describe('Verify Maven Artifact Version Artifact Details Tab', () => {
|
||||
test('verify file list table', async () => {
|
||||
const { container } = render(
|
||||
<ArTestWrapper
|
||||
path="/registries/:repositoryIdentifier/artifacts/:artifactIdentifier/versions/:versionIdentifier/:versionTab"
|
||||
pathParams={{
|
||||
repositoryIdentifier: '1',
|
||||
artifactIdentifier: '1',
|
||||
versionIdentifier: '1',
|
||||
versionTab: 'artifact_details'
|
||||
}}>
|
||||
<VersionDetailsPage />
|
||||
</ArTestWrapper>
|
||||
)
|
||||
|
||||
expect(container).toBeInTheDocument()
|
||||
|
||||
const getTableRowColumn = (row: number, col: number) => getTableColumn(row, col) as HTMLElement
|
||||
|
||||
const data = mockMavenArtifactFiles.files[0]
|
||||
const row = 1
|
||||
expect(getTableRowColumn(row, 1)).toHaveTextContent(data.name)
|
||||
expect(getTableRowColumn(row, 2)).toHaveTextContent(data?.size as string)
|
||||
|
||||
const column3Content = getTableRowColumn(row, 3)
|
||||
data.checksums.forEach(async each => {
|
||||
const [checksum, value] = each.split(': ')
|
||||
const btn = getByText(column3Content, `copy ${checksum}`)
|
||||
await userEvent.click(btn)
|
||||
expect(copy).toHaveBeenCalledWith(value)
|
||||
})
|
||||
|
||||
const copyColumn = getTableRowColumn(row, 4)
|
||||
expect(copyColumn).toHaveTextContent('copy')
|
||||
const copyBtn = copyColumn.querySelector('[data-icon="code-copy"]') as HTMLElement
|
||||
expect(copyBtn).toBeInTheDocument()
|
||||
await userEvent.click(copyBtn)
|
||||
expect(copy).toHaveBeenCalledWith(data?.downloadCommand)
|
||||
})
|
||||
|
||||
test('verify pagination and sorting actions', async () => {
|
||||
const { container } = render(
|
||||
<ArTestWrapper
|
||||
path="/registries/:repositoryIdentifier/artifacts/:artifactIdentifier/versions/:versionIdentifier/:versionTab"
|
||||
pathParams={{
|
||||
repositoryIdentifier: '1',
|
||||
artifactIdentifier: '1',
|
||||
versionIdentifier: '1',
|
||||
versionTab: 'artifact_details'
|
||||
}}>
|
||||
<VersionDetailsPage />
|
||||
</ArTestWrapper>
|
||||
)
|
||||
const nextPageBtn = getByText(container, 'Next')
|
||||
await userEvent.click(nextPageBtn)
|
||||
|
||||
expect(useGetArtifactFilesQuery).toHaveBeenLastCalledWith({
|
||||
artifact: '1/+',
|
||||
queryParams: { page: 0, size: 50, sort_field: 'updatedAt', sort_order: 'DESC' },
|
||||
registry_ref: 'undefined/1/+',
|
||||
version: '1'
|
||||
})
|
||||
|
||||
const fileNameSortIcon = getByText(container, 'versionDetails.artifactFiles.table.columns.name').nextSibling
|
||||
?.firstChild as HTMLElement
|
||||
await userEvent.click(fileNameSortIcon)
|
||||
|
||||
expect(useGetArtifactFilesQuery).toHaveBeenLastCalledWith({
|
||||
artifact: '1/+',
|
||||
queryParams: { page: 0, size: 50, sort_field: 'updatedAt', sort_order: 'DESC' },
|
||||
registry_ref: 'undefined/1/+',
|
||||
version: '1'
|
||||
})
|
||||
})
|
||||
|
||||
test('verify error message', async () => {
|
||||
const mockRefetchFn = jest.fn().mockImplementation(() => undefined)
|
||||
;(useGetArtifactFilesQuery as jest.Mock).mockImplementationOnce(() => {
|
||||
return {
|
||||
data: null,
|
||||
loading: false,
|
||||
error: { message: 'error message' },
|
||||
refetch: mockRefetchFn
|
||||
}
|
||||
})
|
||||
|
||||
const { container } = render(
|
||||
<ArTestWrapper
|
||||
path="/registries/:repositoryIdentifier/artifacts/:artifactIdentifier/versions/:versionIdentifier/:versionTab"
|
||||
pathParams={{
|
||||
repositoryIdentifier: '1',
|
||||
artifactIdentifier: '1',
|
||||
versionIdentifier: '1',
|
||||
versionTab: 'artifact_details'
|
||||
}}>
|
||||
<VersionDetailsPage />
|
||||
</ArTestWrapper>
|
||||
)
|
||||
|
||||
const errorText = getByText(container, 'error message')
|
||||
expect(errorText).toBeInTheDocument()
|
||||
|
||||
const retryBtn = getByText(container, 'Retry')
|
||||
expect(retryBtn).toBeInTheDocument()
|
||||
|
||||
await userEvent.click(retryBtn)
|
||||
expect(mockRefetchFn).toHaveBeenCalled()
|
||||
})
|
||||
})
|
@ -17,6 +17,7 @@
|
||||
import type {
|
||||
ArtifactDetailResponseResponse,
|
||||
ArtifactVersionSummaryResponseResponse,
|
||||
FileDetailResponseResponse,
|
||||
ListArtifactVersion,
|
||||
ListArtifactVersionResponseResponse
|
||||
} from '@harnessio/react-har-service-client'
|
||||
@ -121,3 +122,65 @@ export const mockMavenArtifactDetails: ArtifactDetailResponseResponse = {
|
||||
},
|
||||
status: 'SUCCESS'
|
||||
}
|
||||
|
||||
export const mockMavenArtifactFiles: FileDetailResponseResponse = {
|
||||
files: [
|
||||
{
|
||||
checksums: [
|
||||
'SHA-512: 43b206d0651f2748d685c9ed63942091fe529a0c838effeb15b3d21139a5c25f1086bad9e2df57a3032bf1cf26837b7cfe504f7b033d872a6d2d41d311aba882',
|
||||
'SHA-256: 04cf2f4ad947a81667690d64059ee29d3edc0d74649f82df489565c6cc1edcc0',
|
||||
'SHA-1: 475741bfe798caedd27a1c2580ea211aeba32521',
|
||||
'MD5: 5eb4f955706b83204f7d4dbbdecdb0e6'
|
||||
],
|
||||
createdAt: '1738316037624',
|
||||
downloadCommand:
|
||||
"curl --location 'https://pkg.qa.harness.io/maven/iWnhltqOT7GFt7R-F_zP7Q/maven-up-1/junit/junit/3.8.1/junit-3.8.1.jar.sha1' --header 'x-api-key: \u003cIDENTITY_TOKEN\u003e' -O",
|
||||
name: 'junit-3.8.1.jar.sha1',
|
||||
size: '40.00B'
|
||||
},
|
||||
{
|
||||
checksums: [
|
||||
'SHA-512: cfc89ca8303af5c04c75a73db181b61a34371b9e0dcc59e4d746190ac2e7636f0b257303ebef4db9a2cd980d192ab8879c91d84682d472b03fd3b9a732f184b6',
|
||||
'SHA-256: ab9b2ba5775492d85d45240f6f12e5880eb0ce26385fd80a1083e3b4ded402c2',
|
||||
'SHA-1: 11c996e14e70c07f6758f325838ea07e3bdf0742',
|
||||
'MD5: 4f215459aacbaaac97d02c29c41b2a57'
|
||||
],
|
||||
createdAt: '1738316032125',
|
||||
downloadCommand:
|
||||
"curl --location 'https://pkg.qa.harness.io/maven/iWnhltqOT7GFt7R-F_zP7Q/maven-up-1/junit/junit/3.8.1/junit-3.8.1.pom.sha1' --header 'x-api-key: \u003cIDENTITY_TOKEN\u003e' -O",
|
||||
name: 'junit-3.8.1.pom.sha1',
|
||||
size: '58.00B'
|
||||
},
|
||||
{
|
||||
checksums: [
|
||||
'SHA-512: 8e6f9fa5eb3ba93a8b1b5a39e01a81c142b33078264dbd0a2030d60dd26735407249a12e66f5cdcab8056e93a5687124fe66e741c233b4c7a06cc8e49f82e98b',
|
||||
'SHA-256: b58e459509e190bed737f3592bc1950485322846cf10e78ded1d065153012d70',
|
||||
'SHA-1: 99129f16442844f6a4a11ae22fbbee40b14d774f',
|
||||
'MD5: 1f40fb782a4f2cf78f161d32670f7a3a'
|
||||
],
|
||||
createdAt: '1738152520460',
|
||||
downloadCommand:
|
||||
"curl --location 'https://pkg.qa.harness.io/maven/iWnhltqOT7GFt7R-F_zP7Q/maven-up-1/junit/junit/3.8.1/junit-3.8.1.jar' --header 'x-api-key: \u003cIDENTITY_TOKEN\u003e' -O",
|
||||
name: 'junit-3.8.1.jar',
|
||||
size: '118.23KB'
|
||||
},
|
||||
{
|
||||
checksums: [
|
||||
'SHA-512: d43bddd7228b108eab508871d64725a730f6f159b0cee0e25a62df61f5362dc4c3e7c3413b5562b22e20934b40b5d994c1b1f66fec0e1a340613913e05203396',
|
||||
'SHA-256: e68f33343d832398f3c8aa78afcd808d56b7c1020de4d3ad8ce47909095ee904',
|
||||
'SHA-1: 16d74791c801c89b0071b1680ea0bc85c93417bb',
|
||||
'MD5: 50b40cb7342f52b702e6337d5debf1ae'
|
||||
],
|
||||
createdAt: '1738152517836',
|
||||
downloadCommand:
|
||||
"curl --location 'https://pkg.qa.harness.io/maven/iWnhltqOT7GFt7R-F_zP7Q/maven-up-1/junit/junit/3.8.1/junit-3.8.1.pom' --header 'x-api-key: \u003cIDENTITY_TOKEN\u003e' -O",
|
||||
name: 'junit-3.8.1.pom',
|
||||
size: '998.00B'
|
||||
}
|
||||
],
|
||||
itemCount: 4,
|
||||
pageCount: 1,
|
||||
pageIndex: 0,
|
||||
pageSize: 50,
|
||||
status: 'SUCCESS'
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ import type { DockerVersionDetailsQueryParams } from '../../DockerVersion/types'
|
||||
import css from './VersionDetailsTab.module.scss'
|
||||
|
||||
export default function VersionDetailsTabs(): JSX.Element {
|
||||
const [tab, setTab] = useState(VersionDetailsTab.OVERVIEW)
|
||||
const [tab, setTab] = useState('')
|
||||
|
||||
const routes = useRoutes()
|
||||
const history = useHistory()
|
||||
@ -109,7 +109,11 @@ export default function VersionDetailsTabs(): JSX.Element {
|
||||
routeDefinitions.toARVersionDetailsTab({ ...versionDetailsTabWithSSCADetailsPathParams }),
|
||||
routeDefinitions.toARVersionDetailsTab({ ...versionDetailsTabWithPipelineDetailsPathParams })
|
||||
]}>
|
||||
<VersionDetailsTabWidget onInit={setTab} packageType={data.packageType as RepositoryPackageType} tab={tab} />
|
||||
<VersionDetailsTabWidget
|
||||
onInit={setTab}
|
||||
packageType={data.packageType as RepositoryPackageType}
|
||||
tab={tab as VersionDetailsTab}
|
||||
/>
|
||||
</RouteProvider>
|
||||
</Switch>
|
||||
</>
|
||||
|
Loading…
Reference in New Issue
Block a user