feat: [AH-609]: Add unit tests for create virtual registry flow (#2944)

* feat: [AH-609]: pass not supported type instead of GENERIC
* feat: [AH-609]: Add unit tests for create virtual registry flow
pull/3586/head
Shivanand Sonnad 2024-11-12 12:34:34 +00:00 committed by Harness
parent 042df435bd
commit 92bea1dfbf
5 changed files with 452 additions and 4 deletions

View File

@ -59,3 +59,27 @@ Object.defineProperty(window, 'matchMedia', {
})
jest.mock('react-timeago', () => () => 'dummy date')
class MockIntersectionObserver {
constructor() {
this.root = null
this.rootMargin = ''
this.thresholds = []
this.disconnect = () => null
this.observe = () => null
this.takeRecords = () => []
this.unobserve = () => null
}
}
Object.defineProperty(window, 'IntersectionObserver', {
writable: true,
configurable: true,
value: MockIntersectionObserver
})
Object.defineProperty(global, 'IntersectionObserver', {
writable: true,
configurable: true,
value: MockIntersectionObserver
})

View File

@ -61,7 +61,7 @@ function FormContent(props: FormContentProps): JSX.Element {
const { getString } = useStrings()
const { values } = formikProps
const { packageType, config } = values
const { type } = config
const { type } = config || {}
useEffect(() => {
const newDefaultValues = getDefaultValuesByRepositoryType(packageType as RepositoryPackageType, values)
@ -113,7 +113,7 @@ function RepositoryCreateForm(props: RepositoryCreateFormProps, formikRef: Formi
const { isLoading: createLoading, mutateAsync: createRepository } = useCreateRegistryMutation()
useEffect(() => {
setShowOverlay?.(createLoading)
setShowOverlay(createLoading)
}, [createLoading])
const getDefaultValuesByRepositoryType = useCallback(
@ -136,7 +136,7 @@ function RepositoryCreateForm(props: RepositoryCreateFormProps, formikRef: Formi
const handleSubmit = async (values: VirtualRegistryRequest): Promise<void> => {
try {
const packageType = values?.packageType
const packageType = values.packageType
const repositoryType = factory.getRepositoryType(packageType)
if (repositoryType) {
const formattedValues = repositoryType.processRepositoryFormData(values) as VirtualRegistryRequest

View File

@ -0,0 +1,271 @@
/*
* 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 type { FormikProps } from 'formik'
import { useToaster } from '@harnessio/uicore'
import userEvent from '@testing-library/user-event'
import { fireEvent, render, waitFor } from '@testing-library/react'
import { useCreateRegistryMutation } from '@harnessio/react-har-service-client'
import { RepositoryPackageType } from '@ar/common/types'
import ArTestWrapper from '@ar/utils/testUtils/ArTestWrapper'
import repositoryFactory from '@ar/frameworks/RepositoryStep/RepositoryFactory'
import { HelmRepositoryType } from '@ar/pages/repository-details/HelmRepository/HelmRepositoryType'
import { DockerRepositoryType } from '@ar/pages/repository-details/DockerRepository/DockerRepositoryType'
import { queryByNameAttribute } from 'utils/test/testUtils'
import RepositoryCreateForm from '../RepositoryCreateForm'
const mutateCreateRegistrySuccess = jest.fn().mockImplementation(
() =>
new Promise(onSuccess => {
onSuccess({
content: {
status: 'SUCCESS',
data: { identifier: '1234' }
}
})
})
)
const mutateCreateRegistryFailure = jest.fn().mockImplementation(
() =>
new Promise((_, onFailed) => {
onFailed({
message: 'Custom message for failed scenario'
})
})
)
jest.mock('@harnessio/react-har-service-client', () => ({
useCreateRegistryMutation: jest.fn()
}))
jest.mock('@harnessio/uicore', () => ({
...jest.requireActual('@harnessio/uicore'),
useToaster: jest.fn().mockImplementation(() => ({
showSuccess: jest.fn,
showError: jest.fn(),
clear: jest.fn()
}))
}))
describe('Verify RepositoryCreateForm', () => {
beforeAll(() => {
repositoryFactory.registerStep(new DockerRepositoryType())
repositoryFactory.registerStep(new HelmRepositoryType())
})
beforeEach(() => {
jest.clearAllMocks()
})
test('Should render form without error and should submit form without error', async () => {
;(useCreateRegistryMutation as jest.Mock).mockImplementation(() => ({
isLoading: false,
mutateAsync: mutateCreateRegistrySuccess
}))
const setShowOverlay = jest.fn()
const onSuccess = jest.fn()
const formikRef = React.createRef<FormikProps<unknown>>()
const { container } = render(
<ArTestWrapper>
<RepositoryCreateForm setShowOverlay={setShowOverlay} onSuccess={onSuccess} ref={formikRef} />
</ArTestWrapper>
)
const selectPackageTypeLabel = 'repositoryDetails.repositoryForm.selectRepoType'
expect(container).toHaveTextContent(selectPackageTypeLabel)
const dockerType = container.querySelector('input[type="checkbox"][name="packageType"][value="DOCKER"]')
expect(dockerType).toBeChecked()
const helmType = container.querySelector('input[type="checkbox"][name="packageType"][value="HELM"]')
expect(helmType).not.toBeChecked()
await userEvent.click(container.querySelector('span[data-icon="service-helm"]')!)
await waitFor(() => {
expect(helmType).toBeChecked()
})
const nameField = queryByNameAttribute('identifier', container)
expect(nameField).toBeInTheDocument()
fireEvent.change(nameField!, { target: { value: 'helm-repo-1' } })
formikRef.current?.submitForm()
await waitFor(() => {
expect(mutateCreateRegistrySuccess).toHaveBeenCalledWith({
body: {
cleanupPolicy: [],
config: { type: 'VIRTUAL', upstreamProxies: [] },
identifier: 'helm-repo-1',
packageType: 'HELM',
parentRef: 'undefined',
scanners: []
},
queryParams: { space_ref: 'undefined' }
})
})
await waitFor(() => {
expect(onSuccess).toHaveBeenCalledWith({ identifier: '1234' })
})
})
test('Should work form validations correctly', async () => {
;(useCreateRegistryMutation as jest.Mock).mockImplementation(() => ({
isLoading: false,
mutateAsync: mutateCreateRegistrySuccess
}))
const setShowOverlay = jest.fn()
const onSuccess = jest.fn()
const formikRef = React.createRef<FormikProps<unknown>>()
const { container } = render(
<ArTestWrapper>
<RepositoryCreateForm setShowOverlay={setShowOverlay} onSuccess={onSuccess} ref={formikRef} />
</ArTestWrapper>
)
formikRef.current?.submitForm()
await waitFor(() => {
expect(mutateCreateRegistrySuccess).not.toHaveBeenCalled()
})
await waitFor(() => {
expect(onSuccess).not.toHaveBeenCalled()
})
const nameField = queryByNameAttribute('identifier', container)
expect(nameField).toBeInTheDocument()
fireEvent.change(nameField!, { target: { value: 'repo-1' } })
formikRef.current?.submitForm()
await waitFor(() => {
expect(mutateCreateRegistrySuccess).toHaveBeenCalled()
})
await waitFor(() => {
expect(onSuccess).toHaveBeenCalled()
})
})
test('Should select default type based on defaultType prop', () => {
const formikRef = React.createRef<FormikProps<unknown>>()
const { container } = render(
<ArTestWrapper>
<RepositoryCreateForm
defaultType={RepositoryPackageType.HELM}
setShowOverlay={jest.fn}
onSuccess={jest.fn}
ref={formikRef}
/>
</ArTestWrapper>
)
const dockerType = container.querySelector('input[type="checkbox"][name="packageType"][value="DOCKER"]')
expect(dockerType).not.toBeChecked()
const helmType = container.querySelector('input[type="checkbox"][name="packageType"][value="HELM"]')
expect(helmType).toBeChecked()
})
test('Should disable types based on allowedTypes prop', () => {
const formikRef = React.createRef<FormikProps<unknown>>()
const { container } = render(
<ArTestWrapper>
<RepositoryCreateForm
allowedPackageTypes={[RepositoryPackageType.HELM]}
setShowOverlay={jest.fn}
onSuccess={jest.fn}
ref={formikRef}
/>
</ArTestWrapper>
)
const dockerType = container.querySelector('input[type="checkbox"][name="packageType"][value="DOCKER"]')
expect(dockerType).toBeDisabled()
const helmType = container.querySelector('input[type="checkbox"][name="packageType"][value="HELM"]')
expect(helmType).not.toBeDisabled()
expect(helmType).toBeChecked()
})
test('Should show error if failed to create registry', async () => {
;(useCreateRegistryMutation as jest.Mock).mockImplementation(() => ({
isLoading: false,
mutateAsync: mutateCreateRegistryFailure
}))
const showError = jest.fn()
;(useToaster as jest.Mock).mockImplementation(() => ({
showSuccess: jest.fn,
showError,
clear: jest.fn()
}))
const setShowOverlay = jest.fn()
const onSuccess = jest.fn()
const formikRef = React.createRef<FormikProps<unknown>>()
const { container } = render(
<ArTestWrapper>
<RepositoryCreateForm setShowOverlay={setShowOverlay} onSuccess={onSuccess} ref={formikRef} />
</ArTestWrapper>
)
const nameField = queryByNameAttribute('identifier', container)
expect(nameField).toBeInTheDocument()
fireEvent.change(nameField!, { target: { value: 'docker-repo-1' } })
formikRef.current?.submitForm()
await waitFor(() => {
expect(mutateCreateRegistryFailure).toHaveBeenCalledWith({
body: {
cleanupPolicy: [],
config: { type: 'VIRTUAL', upstreamProxies: [] },
identifier: 'docker-repo-1',
packageType: 'DOCKER',
parentRef: 'undefined',
scanners: []
},
queryParams: { space_ref: 'undefined' }
})
})
await waitFor(() => {
expect(showError).toHaveBeenCalledWith('Custom message for failed scenario')
})
})
test('Should render default text if passed incorrect type or disabled type to defaultType', () => {
const formikRef = React.createRef<FormikProps<unknown>>()
const { container } = render(
<ArTestWrapper>
<RepositoryCreateForm
defaultType={'RANDOM' as RepositoryPackageType}
setShowOverlay={jest.fn}
onSuccess={jest.fn}
ref={formikRef}
/>
</ArTestWrapper>
)
const nameField = queryByNameAttribute('identifier', container)
expect(nameField).not.toBeInTheDocument()
})
})

View File

@ -0,0 +1,153 @@
/*
* 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 { Button } from '@harnessio/uicore'
import userEvent from '@testing-library/user-event'
import { fireEvent, render, waitFor } from '@testing-library/react'
import { RepositoryPackageType } from '@ar/common/types'
import ArTestWrapper from '@ar/utils/testUtils/ArTestWrapper'
import repositoryFactory from '@ar/frameworks/RepositoryStep/RepositoryFactory'
import { CreateRepository } from '@ar/pages/repository-list/components/CreateRepository/CreateRepository'
import { queryByNameAttribute } from 'utils/test/testUtils'
import { HelmRepositoryType } from '../../HelmRepository/HelmRepositoryType'
import { DockerRepositoryType } from '../../DockerRepository/DockerRepositoryType'
import {
useCreateRepositoryModal,
useCreateRepositoryModalProps
} from '../useCreateRepositoryModal/useCreateRepositoryModal'
const mutateCreateRegistry = jest.fn().mockImplementation(
() =>
new Promise(onSuccess => {
onSuccess({
content: {
status: 'SUCCESS',
data: { identifier: '1234' }
}
})
})
)
jest.mock('@harnessio/react-har-service-client', () => ({
useCreateRegistryMutation: jest.fn().mockImplementation(() => ({
isLoading: false,
mutateAsync: mutateCreateRegistry
}))
}))
const openModalDialog = async (container: HTMLElement): Promise<HTMLElement> => {
const mainBtn = container.querySelector('button[aria-label="repositoryList.newRepository"]')
expect(mainBtn!).toBeInTheDocument()
await userEvent.click(mainBtn!)
const modalDialog = document.querySelector('div.bp3-dialog') as HTMLElement
await waitFor(() => {
expect(modalDialog).toBeInTheDocument()
})
return modalDialog
}
const CustomCreateRepositoryModal = (props: useCreateRepositoryModalProps) => {
const [open] = useCreateRepositoryModal(props)
return <Button text="repositoryList.newRepository" onClick={open} />
}
describe('Verify CreateRepositoryModal', () => {
beforeAll(() => {
repositoryFactory.registerStep(new DockerRepositoryType())
repositoryFactory.registerStep(new HelmRepositoryType())
})
beforeEach(() => {
jest.clearAllMocks()
})
test('should able to open create repository form and able to submit without any error', async () => {
const { container } = render(
<ArTestWrapper>
<CreateRepository />
</ArTestWrapper>
)
const modalDialog = await openModalDialog(container)
const modalHeader = modalDialog.querySelector('[data-testid="modaldialog-header"]')
expect(modalHeader).toBeInTheDocument()
expect(modalHeader).toHaveTextContent('repositoryDetails.repositoryForm.modalTitle')
expect(modalHeader).toHaveTextContent('repositoryDetails.repositoryForm.modalSubTitle')
const nameField = queryByNameAttribute('identifier', modalDialog)
expect(nameField).toBeInTheDocument()
fireEvent.change(nameField!, { target: { value: 'repo1' } })
const submitBtn = modalDialog.querySelector('button[aria-label="repositoryDetails.repositoryForm.create"]')
expect(submitBtn).toBeInTheDocument()
await userEvent.click(submitBtn!)
await waitFor(() => {
expect(mutateCreateRegistry).toHaveBeenCalledWith({
body: {
cleanupPolicy: [],
config: { type: 'VIRTUAL', upstreamProxies: [] },
identifier: 'repo1',
packageType: 'DOCKER',
parentRef: 'undefined',
scanners: []
},
queryParams: { space_ref: 'undefined' }
})
})
})
test('should able to close the modal on click on Close button', async () => {
const { container } = render(
<ArTestWrapper>
<CreateRepository />
</ArTestWrapper>
)
const modalDialog = await openModalDialog(container)
const cancelBtn = modalDialog.querySelector('button[aria-label="cancel"]')
expect(cancelBtn).toBeInTheDocument()
await userEvent.click(cancelBtn!)
await waitFor(() => {
expect(modalDialog).not.toBeInTheDocument()
})
const modalDialogAgain = await openModalDialog(container)
const closeIcon = modalDialogAgain.querySelector('span[data-icon="Stroke"]')
expect(closeIcon).toBeInTheDocument()
await userEvent.click(closeIcon!)
await waitFor(() => {
expect(modalDialog).not.toBeInTheDocument()
})
})
test('should render modal with allowed types correctly', async () => {
const { container } = render(
<ArTestWrapper>
<CustomCreateRepositoryModal onSuccess={jest.fn()} allowedPackageTypes={[RepositoryPackageType.HELM]} />
</ArTestWrapper>
)
const dialog = await openModalDialog(container)
const dockerType = dialog.querySelector('input[type="checkbox"][name="packageType"][value="DOCKER"]')
expect(dockerType).toBeDisabled()
const helmType = dialog.querySelector('input[type="checkbox"][name="packageType"][value="HELM"]')
expect(helmType).not.toBeDisabled()
})
})

View File

@ -25,7 +25,7 @@ import type { RepositoryPackageType } from '@ar/common/types'
import RepositoryCreateForm from '@ar/pages/repository-details/components/Forms/RepositoryCreateForm'
import type { Repository } from '@ar/pages/repository-details/types'
interface useCreateRepositoryModalProps {
export interface useCreateRepositoryModalProps {
onSuccess: (data: Repository) => void
allowedPackageTypes?: RepositoryPackageType[]
}