From c28d39172a39c3a653e5f8979535ad8cd38cfd32 Mon Sep 17 00:00:00 2001 From: Abhi Date: Wed, 30 Aug 2023 21:47:47 +0530 Subject: [PATCH] feat(integrationtest): integrationTest params support --- .../hooks/__data__/workflow-data.ts | 4 +- .../IntegrationTest/FormikParamsField.tsx | 258 +++++++++++++ .../IntegrationTestSection.tsx | 6 +- .../IntegrationTestView.tsx | 16 + .../__tests__/IntegrationTestSection.spec.tsx | 10 + .../IntegrationTestForm/types.ts | 2 + .../utils/__tests__/create-utils.spec.ts | 75 +++- .../IntegrationTestForm/utils/create-utils.ts | 23 +- .../__tests__/FormikParamsField.spec.tsx | 364 ++++++++++++++++++ src/pages/EditIntegrationTestPage.tsx | 2 +- src/types/coreBuildService.ts | 3 +- 11 files changed, 752 insertions(+), 11 deletions(-) create mode 100644 src/components/IntegrationTest/FormikParamsField.tsx create mode 100644 src/components/IntegrationTest/__tests__/FormikParamsField.spec.tsx diff --git a/src/components/ApplicationDetails/tabs/overview/visualization/hooks/__data__/workflow-data.ts b/src/components/ApplicationDetails/tabs/overview/visualization/hooks/__data__/workflow-data.ts index 2413b9d5b..1ee4392c1 100644 --- a/src/components/ApplicationDetails/tabs/overview/visualization/hooks/__data__/workflow-data.ts +++ b/src/components/ApplicationDetails/tabs/overview/visualization/hooks/__data__/workflow-data.ts @@ -757,7 +757,7 @@ export const sampleIntegrationTestScenarios: IntegrationTestScenarioKind[] = [ params: [ { name: 'test-param', - value: ['test'], + value: 'test', }, ], resolverRef: null, @@ -786,7 +786,7 @@ export const sampleIntegrationTestScenarios: IntegrationTestScenarioKind[] = [ params: [ { name: 'test-param', - value: ['test'], + value: 'test', }, ], resolverRef: null, diff --git a/src/components/IntegrationTest/FormikParamsField.tsx b/src/components/IntegrationTest/FormikParamsField.tsx new file mode 100644 index 000000000..4127a7b86 --- /dev/null +++ b/src/components/IntegrationTest/FormikParamsField.tsx @@ -0,0 +1,258 @@ +import * as React from 'react'; +import { + Flex, + FlexItem, + TextContent, + DataList, + DataListItem, + DataListToggle, + DataListContent, + Button, + Alert, + AlertVariant, + InputGroup, + FormGroup, + DataListItemRow, + DataListItemCells, + DataListCell, + CardHeader, + Card, + CardExpandableContent, + CardBody, +} from '@patternfly/react-core'; +import { MinusCircleIcon } from '@patternfly/react-icons/dist/js/icons/minus-circle-icon'; +import { PlusCircleIcon } from '@patternfly/react-icons/dist/js/icons/plus-circle-icon'; +import { FieldArray, useField } from 'formik'; +import { InputField } from '../../shared'; +import { Param } from '../../types/coreBuildService'; + +interface IntegrationTestParamsProps { + heading?: string; + fieldName: string; + initExpanded?: boolean; +} + +const IntegrationTestParams: React.FC = ({ + heading, + fieldName, + initExpanded = false, +}) => { + const [, { value: parameters, error }] = useField(fieldName); + + const initExpandedState = React.useMemo(() => { + const state = []; + for (let i = 0; i < parameters?.length; i++) { + state.push(false); + } + return state; + }, [parameters]); + + const [expanded, setExpanded] = React.useState(initExpandedState); + const [paramExpanded, setParamExpanded] = React.useState(initExpanded); + + const toggleExpandedState = (i) => { + const state = [...expanded]; + state[i] = !state[i]; + setExpanded(state); + }; + + const addParam = () => { + const state = [...expanded, true]; + setExpanded(state); + }; + + const removeParam = (i) => { + const state = [...expanded]; + state.splice(i, 1); + setExpanded(state); + }; + + return ( +
+ + setParamExpanded((v) => !v)} + toggleButtonProps={{ + id: `toggle-${name}`, + 'aria-label': name, + 'aria-labelledby': `review-${name} toggle-${name}`, + 'aria-expanded': paramExpanded, + 'data-test': `${name}-toggle-button`, + className: 'pf-v5-u-pr-xs pf-v5-u-pl-sm', + }} + className="pf-v5-u-pl-xs pf-v5-u-pr-xs" + > + {heading ?? 'Parameters'} + {error && ( + + )} + + + + ( + <> + + {Array.isArray(parameters) && + parameters.length > 0 && + parameters.map((p, i) => { + return ( + + + toggleExpandedState(i)} + isExpanded={expanded[i]} + data-test={`expand-param-${i + 1}`} + className="pf-v5-u-mr-0" + /> + + {`Parameter${i + 1}`} + , + + + + , + ]} + /> + + + + + + + + , + + + ( + <> + {p.values && + p.values.length > 0 && + p.values.map((val, j) => ( + + + + + )} + /> + + , + ]} + /> + + + + ); + })} + + + + + + + + + + + + + )} + /> + + + +
+ ); +}; + +export default IntegrationTestParams; diff --git a/src/components/IntegrationTest/IntegrationTestForm/IntegrationTestSection.tsx b/src/components/IntegrationTest/IntegrationTestForm/IntegrationTestSection.tsx index 7e88992af..ceeaa2505 100644 --- a/src/components/IntegrationTest/IntegrationTestForm/IntegrationTestSection.tsx +++ b/src/components/IntegrationTest/IntegrationTestForm/IntegrationTestSection.tsx @@ -15,6 +15,7 @@ import { EnvironmentType, getEnvironmentType } from '../../Environment/environme import { AccessHelpText } from '../../ImportForm/SourceSection/SourceSection'; import { useAccessCheck } from '../../ImportForm/utils/auth-utils'; import { gitUrlRegex, RESOURCE_NAME_REGEX_MSG } from '../../ImportForm/utils/validation-utils'; +import FormikParamsField from '../FormikParamsField'; import { ENVIRONMENTS, IntegrationTestFormValues } from './types'; import './IntegrationTestSection.scss'; @@ -31,9 +32,6 @@ const IntegrationTestSection: React.FC = ({ isInPage, edit }) => { type: 'dropdown', }); const [environments, environmentsLoaded] = useAllEnvironments(); - // const [currentEnvironment, setCurrentEnvironment] = React.useState( - // ITSEnvName ?? ENVIRONMENTS.DEFAULT, - // ); const dropdownItems = React.useMemo(() => { const items = [{ key: 'none', value: 'No environment' }]; @@ -195,6 +193,8 @@ const IntegrationTestSection: React.FC = ({ isInPage, edit }) => { isDisabled={edit} className="integration-test-section__dropdown" /> + + = ( (param) => param.name === ResolverRefParams.PATH, ); + const getFormParamValues = (params) => { + if (!params || !Array.isArray(params) || params?.length === 0) { + return []; + } + const formParams = []; + params.forEach((param) => { + if (param.value) { + formParams.push({ name: param.name, values: [param.value] }); + } else { + formParams.push(param); + } + }); + return formParams; + }; + const initialValues = { integrationTest: { name: integrationTest?.metadata.name ?? '', @@ -46,6 +61,7 @@ const IntegrationTestView: React.FunctionComponent = ( path: path?.value ?? '', environmentName: integrationTest?.spec?.environment?.name ?? '', environmentType: integrationTest?.spec?.environment?.type ?? '', + params: getFormParamValues(integrationTest?.spec?.params), optional: integrationTest?.metadata.labels?.[IntegrationTestLabels.OPTIONAL] === 'true' ?? false, }, diff --git a/src/components/IntegrationTest/IntegrationTestForm/__tests__/IntegrationTestSection.spec.tsx b/src/components/IntegrationTest/IntegrationTestForm/__tests__/IntegrationTestSection.spec.tsx index bed7a1dd4..568f1cd51 100644 --- a/src/components/IntegrationTest/IntegrationTestForm/__tests__/IntegrationTestSection.spec.tsx +++ b/src/components/IntegrationTest/IntegrationTestForm/__tests__/IntegrationTestSection.spec.tsx @@ -50,6 +50,7 @@ describe('IntegrationTestSection', () => { expect(wrapper.getByTestId('integration-test-section-header')).toBeTruthy(); }); }); + it('should hide the page header when isInPage is set', async () => { const wrapper = formikRenderer(, { source: 'test-source', @@ -66,6 +67,15 @@ describe('IntegrationTestSection', () => { await waitFor(() => expect(found).toEqual(false)); }); + it('should render parameter section', async () => { + formikRenderer(, { + source: 'test-source', + secret: null, + }); + + screen.queryByTestId('its-param-field'); + }); + it('should return show error message if the repo is not accessible', async () => { useK8sWatchResourceMock.mockReturnValue([[], true]); useAccessCheckMock.mockReturnValue([ diff --git a/src/components/IntegrationTest/IntegrationTestForm/types.ts b/src/components/IntegrationTest/IntegrationTestForm/types.ts index 85dc05ac4..368837023 100644 --- a/src/components/IntegrationTest/IntegrationTestForm/types.ts +++ b/src/components/IntegrationTest/IntegrationTestForm/types.ts @@ -1,5 +1,6 @@ import { K8sResourceCommon } from '@openshift/dynamic-plugin-sdk-utils'; import { ImportFormValues } from '../../../components/ImportForm/utils/types'; +import { Param } from '../../../types/coreBuildService'; export type IntegrationTestFormValues = { name: string; @@ -10,6 +11,7 @@ export type IntegrationTestFormValues = { secret?: string; environmentName?: string; environmentType?: string; + params?: Param[]; }; export enum IntegrationTestAnnotations { diff --git a/src/components/IntegrationTest/IntegrationTestForm/utils/__tests__/create-utils.spec.ts b/src/components/IntegrationTest/IntegrationTestForm/utils/__tests__/create-utils.spec.ts index c093461af..21b2d16f1 100644 --- a/src/components/IntegrationTest/IntegrationTestForm/utils/__tests__/create-utils.spec.ts +++ b/src/components/IntegrationTest/IntegrationTestForm/utils/__tests__/create-utils.spec.ts @@ -7,6 +7,7 @@ import { createIntegrationTest, getLabelForParam, getURLForParam, + formatParams, } from '../create-utils'; jest.mock('@openshift/dynamic-plugin-sdk-utils', () => ({ @@ -24,6 +25,7 @@ const integrationTestData = { }, spec: { application: 'Test Application', + params: null, environment: { name: 'test1', type: 'POC', @@ -111,7 +113,7 @@ describe('Create Utils', () => { expect(resource.metadata.labels).not.toBeDefined(); }); - it('Should not contain the optional label if the test is mandatory', async () => { + it('Should contain parameters with correct value', async () => { createResourceMock.mockImplementation(({ resource }) => resource); const resource = await createIntegrationTest( { @@ -122,11 +124,38 @@ describe('Create Utils', () => { optional: false, environmentName: 'test1', environmentType: 'POC', + params: [{ name: 'param1', values: ['value'] }], }, 'Test Application', 'test-ns', ); - expect(resource.metadata.labels).not.toBeDefined(); + expect(resource.spec.params).toBeDefined(); + expect(resource.spec.params[0].name).toBe('param1'); + expect(resource.spec.params[0].value).toBe('value'); + }); + + it('Should contain parameters with multiple values', async () => { + createResourceMock.mockImplementation(({ resource }) => resource); + const resource = await createIntegrationTest( + { + name: 'app-test', + revision: 'test-revision', + url: 'test-url', + path: 'test-path', + optional: false, + environmentName: 'test1', + environmentType: 'POC', + params: [{ name: 'param1', values: ['value1', 'value2', 'value3'] }], + }, + 'Test Application', + 'test-ns', + ); + expect(resource.spec.params).toBeDefined(); + expect(resource.spec.params[0].name).toBe('param1'); + expect(resource.spec.params[0].values).toBeDefined(); + expect(resource.spec.params[0].values.length).toBe(3); + expect(resource.spec.params[0].values[0]).toBe('value1'); + expect(resource.spec.params[0].values[2]).toBe('value3'); }); it('Should return correct labels for params', () => { @@ -162,3 +191,45 @@ describe('Create Utils', () => { ); }); }); + +describe('Create Utils formatParams', () => { + it('Should render null if no params or empty array []', () => { + const formattedParams = formatParams([]); + expect(formattedParams).toBeNull(); + }); + + it('Should render 3 params ', () => { + const formattedParams = formatParams([ + { name: 'apple', values: ['val1', 'val2'] }, + { name: 'mango', values: ['val1', 'val2'] }, + { name: 'orange', values: ['val1', 'val2'] }, + ]); + expect(formattedParams.length).toBe(3); + }); + + it('Should remove empty values ', () => { + const formattedParams = formatParams([ + { name: 'apple', values: ['val1', '', ''] }, + { name: 'mango', values: ['val1', '', 'val2', ''] }, + { name: 'orange', values: ['', '', 'val1', '', 'val2'] }, + ]); + expect(formattedParams.length).toBe(3); + expect(formattedParams[0].value).toBe('val1'); + expect(formattedParams[1].values.length).toBe(2); + expect(formattedParams[1].values[0]).toBe('val1'); + expect(formattedParams[1].values[1]).toBe('val2'); + expect(formattedParams[2].values.length).toBe(2); + expect(formattedParams[2].values[0]).toBe('val1'); + expect(formattedParams[2].values[1]).toBe('val2'); + }); + + it('Should convert Values to value if only 1 entry', () => { + const formattedParams = formatParams([ + { name: 'apple', values: ['val1'] }, + { name: 'mango', values: ['val1', 'val2'] }, + { name: 'orange', values: ['val1', 'val2'] }, + ]); + expect(formattedParams.length).toBe(3); + expect(formattedParams[0].value).toBe('val1'); + }); +}); diff --git a/src/components/IntegrationTest/IntegrationTestForm/utils/create-utils.ts b/src/components/IntegrationTest/IntegrationTestForm/utils/create-utils.ts index 8ad8ed7a1..486b8d8c9 100644 --- a/src/components/IntegrationTest/IntegrationTestForm/utils/create-utils.ts +++ b/src/components/IntegrationTest/IntegrationTestForm/utils/create-utils.ts @@ -6,6 +6,7 @@ import { } from '../../../../models'; import { IntegrationTestScenarioKind, + Param, ResolverParam, ResolverType, } from '../../../../types/coreBuildService'; @@ -22,12 +23,28 @@ export enum ResolverRefParams { REVISION = 'revision', } +export const formatParams = (params): Param[] => { + if (!params || !Array.isArray(params) || params.length === 0) return null; + const newParams = []; + params.forEach((param) => { + const formattedValues = param.values?.filter((v) => !!v); + if (formattedValues?.length === 1) { + newParams.push({ name: param.name, value: formattedValues[0] }); + } else { + newParams.push({ name: param.name, values: formattedValues }); + } + }); + + return newParams.length > 0 ? newParams : null; +}; + export const editIntegrationTest = ( integrationTest: IntegrationTestScenarioKind, integrationTestValues: IntegrationTestFormValues, dryRun?: boolean, ): Promise => { - const { url, revision, path, optional, environmentName, environmentType } = integrationTestValues; + const { url, revision, path, optional, environmentName, environmentType, params } = + integrationTestValues; const integrationTestResource: IntegrationTestScenarioKind = { ...integrationTest, metadata: { @@ -54,6 +71,7 @@ export const editIntegrationTest = ( { name: ResolverRefParams.PATH, value: path }, ], }, + params: formatParams(params), contexts: [ { description: 'Application testing', @@ -90,7 +108,7 @@ export const createIntegrationTest = ( namespace: string, dryRun?: boolean, ): Promise => { - const { name, url, revision, path, optional, environmentName, environmentType } = + const { name, url, revision, path, optional, environmentName, environmentType, params } = integrationTestValues; const integrationTestResource: IntegrationTestScenarioKind = { apiVersion: `${IntegrationTestScenarioGroupVersionKind.group}/${IntegrationTestScenarioGroupVersionKind.version}`, @@ -120,6 +138,7 @@ export const createIntegrationTest = ( { name: ResolverRefParams.PATH, value: path }, ], }, + params: formatParams(params), contexts: [ { description: 'Application testing', diff --git a/src/components/IntegrationTest/__tests__/FormikParamsField.spec.tsx b/src/components/IntegrationTest/__tests__/FormikParamsField.spec.tsx new file mode 100644 index 000000000..5676f8c5c --- /dev/null +++ b/src/components/IntegrationTest/__tests__/FormikParamsField.spec.tsx @@ -0,0 +1,364 @@ +import * as React from 'react'; +import '@testing-library/jest-dom'; +import { configure, screen, render, act, fireEvent, waitFor } from '@testing-library/react'; +import { useField, FieldArray } from 'formik'; +import FormikParamsField from '../FormikParamsField'; + +jest.mock('formik', () => ({ + useField: jest.fn(), + FieldArray: jest.fn(), +})); + +const useFieldMock = useField as jest.Mock; +const FieldArrayMock = FieldArray as jest.Mock; + +configure({ testIdAttribute: 'data-test' }); + +describe('FormikParamsField', () => { + beforeEach(() => { + FieldArrayMock.mockImplementation((props) => { + const renderFunction = props.render; + return
{renderFunction()}
; + }); + }); + + it('should render custom heading if provided', () => { + useFieldMock.mockReturnValue([{}, { value: '' }]); + render(, {}); + expect(screen.getByText('My heading')).toBeInTheDocument(); + }); + + it('should render default heading and add parameter button when no params', () => { + useFieldMock.mockReturnValue([{}, { value: '' }]); + render(); + expect(screen.getByText('Parameters')).toBeInTheDocument(); + ('its-param-error-alert'); + expect(screen.getByTestId('add-param-button')).toBeInTheDocument(); + }); + + it('should error when error occurs', () => { + useFieldMock.mockReturnValue([{}, { value: '', touched: false, error: 'some error occurred' }]); + render(); + const errorr = screen.getByTestId('its-param-error-alert'); + expect(errorr).toBeInTheDocument(); + expect(errorr.classList.contains('pf-m-danger')).toBe(true); + expect(screen.getByText('some error occurred')).toBeInTheDocument(); + }); + + it('should three params', async () => { + useFieldMock.mockReturnValue([ + {}, + { + value: [ + { name: 'param1', value: 'value1' }, + { name: 'param2', value: 'value2' }, + { name: 'param3', value: 'value3' }, + ], + touched: false, + }, + ]); + render(); + screen.getByTestId('its-param-list'); + screen.getByTestId('its-param-1'); + screen.getByTestId('its-param-2'); + screen.getByTestId('its-param-3'); + + const expandParam = screen.getByTestId('expand-param-1').childNodes[0].childNodes[0]; + + act(() => { + fireEvent.click(expandParam); + }); + + await waitFor(() => { + screen.queryByText('param1'); + screen.queryByText('value1'); + }); + }); + + it('should render param with multiple values', async () => { + const setValue = jest.fn(); + const setTouched = jest.fn(); + useFieldMock.mockReturnValue([ + {}, + { + value: [{ name: 'param1', values: ['value1', 'value2', 'value3'] }], + touched: false, + }, + { setValue, setTouched }, + ]); + render(); + + const expandParam = screen.getByTestId('expand-param-1').childNodes[0].childNodes[0]; + + act(() => { + fireEvent.click(expandParam); + }); + + await waitFor(() => { + screen.queryByText('param1'); + screen.queryByText('value1'); + screen.queryByText('value2'); + screen.queryByText('value3'); + }); + }); + + it('should remove param when remove param is clicked', async () => { + const push = jest.fn(); + const remove = jest.fn(); + + FieldArrayMock.mockImplementation((props) => { + const renderFunction = props.render; + return
{renderFunction({ push, remove })}
; + }); + + useFieldMock.mockReturnValue([ + {}, + { + value: [{ name: 'param1', values: ['value1', 'value2', 'value3'] }], + touched: false, + }, + ]); + + render(); + + const expandParam = screen.getByTestId('expand-param-1').childNodes[0].childNodes[0]; + + act(() => { + fireEvent.click(expandParam); + }); + + await waitFor(() => { + screen.queryByText('param1'); + screen.queryByText('value1'); + screen.queryByText('value2'); + screen.queryByText('value3'); + }); + + const removeParam = screen.getByTestId('remove-param-1').childNodes[0].childNodes[0]; + + act(() => { + fireEvent.click(removeParam); + }); + + expect(remove).toHaveBeenCalled(); + expect(remove).toHaveBeenLastCalledWith(0); + }); + + it('should add value to param when add value is clicked', async () => { + const push = jest.fn(); + const remove = jest.fn(); + + FieldArrayMock.mockImplementation((props) => { + const renderFunction = props.render; + return
{renderFunction({ push, remove })}
; + }); + + useFieldMock.mockReturnValue([ + {}, + { + value: [{ name: 'param1', value: 'value' }], + touched: false, + }, + ]); + render(); + + const expandParam = screen.getByTestId('expand-param-1').childNodes[0].childNodes[0]; + + act(() => { + fireEvent.click(expandParam); + }); + + await waitFor(() => { + screen.queryByText('param1'); + screen.queryByText('value'); + }); + + const addValue = screen.getByTestId('add-value-1').childNodes[0].childNodes[0]; + + act(() => { + fireEvent.click(addValue); + }); + + expect(push).toHaveBeenCalled(); + expect(push).toHaveBeenLastCalledWith(''); + }); + + it('should render param with multiple values', async () => { + useFieldMock.mockReturnValue([ + {}, + { + value: [{ name: 'param1', values: ['value1', 'value2', 'value3'] }], + touched: false, + }, + ]); + render(); + + const expandParam = screen.getByTestId('expand-param-1').childNodes[0].childNodes[0]; + + act(() => { + fireEvent.click(expandParam); + }); + + await waitFor(() => { + screen.queryByText('param1'); + screen.queryByText('value1'); + screen.queryByText('value2'); + screen.queryByText('value3'); + }); + }); + + it('should add param when add param is clicked', async () => { + const push = jest.fn(); + const remove = jest.fn(); + + FieldArrayMock.mockImplementation((props) => { + const renderFunction = props.render; + return
{renderFunction({ push, remove })}
; + }); + + useFieldMock.mockReturnValue([ + {}, + { + value: [ + { name: 'param1', values: ['value1', 'value2', 'value3'] }, + { name: 'param2', values: ['value1'] }, + ], + touched: false, + }, + ]); + + render(); + + const expandParam = screen.getByTestId('expand-param-1').childNodes[0].childNodes[0]; + + act(() => { + fireEvent.click(expandParam); + }); + + await waitFor(() => { + screen.queryByText('param1'); + screen.queryByText('value1'); + screen.queryByText('value2'); + screen.queryByText('value3'); + }); + + const removeParam = screen.getByTestId('add-param-button').childNodes[0].childNodes[0]; + + act(() => { + fireEvent.click(removeParam); + }); + + expect(push).toHaveBeenCalled(); + expect(push).toHaveBeenLastCalledWith({ name: 'param3', values: [''] }); + }); + + it('should remove param when remove param is clicked', async () => { + const push = jest.fn(); + const remove = jest.fn(); + + FieldArrayMock.mockImplementation((props) => { + const renderFunction = props.render; + return
{renderFunction({ push, remove })}
; + }); + + useFieldMock.mockReturnValue([ + {}, + { + value: [{ name: 'param1', values: ['value1', 'value2', 'value3'] }], + touched: false, + }, + ]); + + render(); + + const expandParam = screen.getByTestId('expand-param-1').childNodes[0].childNodes[0]; + + act(() => { + fireEvent.click(expandParam); + }); + + await waitFor(() => { + screen.queryByText('param1'); + screen.queryByText('value1'); + screen.queryByText('value2'); + screen.queryByText('value3'); + }); + + const removeParam = screen.getByTestId('remove-param-1').childNodes[0].childNodes[0]; + + act(() => { + fireEvent.click(removeParam); + }); + + expect(remove).toHaveBeenCalled(); + expect(remove).toHaveBeenLastCalledWith(0); + }); + + it('should have enabled remove value button when multiple values and should remove values', async () => { + const push = jest.fn(); + const remove = jest.fn(); + + FieldArrayMock.mockImplementation((props) => { + const renderFunction = props.render; + return
{renderFunction({ push, remove })}
; + }); + + useFieldMock.mockReturnValue([ + {}, + { + value: [{ name: 'param1', values: ['value1', 'value2', 'value3'] }], + touched: false, + }, + ]); + render(); + + const expandParam = screen.getByTestId('expand-param-1').childNodes[0].childNodes[0]; + + act(() => { + fireEvent.click(expandParam); + }); + + await waitFor(() => { + screen.queryByText('param1'); + screen.queryByText('value'); + }); + + const removeValue = screen.getByTestId('remove-value-1-3') as HTMLElement; + expect(removeValue.getAttribute('aria-disabled')).toEqual('false'); + + act(() => { + fireEvent.click(removeValue); + }); + + expect(remove).toHaveBeenCalled(); + expect(remove).toHaveBeenLastCalledWith(2); + }); + + it('should have disabled remove value button when only single value', async () => { + const setValue = jest.fn(); + const setTouched = jest.fn(); + useFieldMock.mockReturnValue([ + {}, + { + value: [{ name: 'param1', values: ['value'] }], + touched: false, + }, + { setValue, setTouched }, + ]); + render(); + + const expandParam = screen.getByTestId('expand-param-1').childNodes[0].childNodes[0]; + + act(() => { + fireEvent.click(expandParam); + }); + + await waitFor(() => { + screen.queryByText('param1'); + screen.queryByText('value'); + }); + + const removeValue = screen.getByTestId('remove-value-1-1') as HTMLElement; + expect(removeValue.getAttribute('aria-disabled')).toEqual('true'); + }); +}); diff --git a/src/pages/EditIntegrationTestPage.tsx b/src/pages/EditIntegrationTestPage.tsx index b16ba9773..36eacca03 100644 --- a/src/pages/EditIntegrationTestPage.tsx +++ b/src/pages/EditIntegrationTestPage.tsx @@ -58,7 +58,7 @@ const EditIntegrationTestPage: React.FunctionComponent = () => { diff --git a/src/types/coreBuildService.ts b/src/types/coreBuildService.ts index 9f360a9c2..2cd282003 100644 --- a/src/types/coreBuildService.ts +++ b/src/types/coreBuildService.ts @@ -43,7 +43,8 @@ export type Environment = { export type Param = { name: string; - value: string[]; + value?: string; + values?: string[]; }; export type EnvironmentKind = K8sResourceCommon & {