From 5fa44c61d2a38080f6555e3a1169e63a8a2dd67b Mon Sep 17 00:00:00 2001 From: nixocio Date: Mon, 15 Mar 2021 17:35:07 -0400 Subject: [PATCH] Add copy functionality to EE Add copy functionality to EE. See: https://github.com/ansible/awx/issues/9307 --- .../ExecutionEnvironmentList.jsx | 1 + .../ExecutionEnvironmentListItem.jsx | 40 +++++++++- .../ExecutionEnvironmentListItem.test.jsx | 75 ++++++++++++++++++- 3 files changed, 114 insertions(+), 2 deletions(-) diff --git a/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentList/ExecutionEnvironmentList.jsx b/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentList/ExecutionEnvironmentList.jsx index 278566e632..914b884f1f 100644 --- a/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentList/ExecutionEnvironmentList.jsx +++ b/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentList/ExecutionEnvironmentList.jsx @@ -195,6 +195,7 @@ function ExecutionEnvironmentList({ i18n }) { isSelected={selected.some( row => row.id === executionEnvironment.id )} + fetchExecutionEnvironments={fetchExecutionEnvironments} /> )} emptyStateControls={ diff --git a/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentList/ExecutionEnvironmentListItem.jsx b/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentList/ExecutionEnvironmentListItem.jsx index 1f56b53727..07816ff5fc 100644 --- a/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentList/ExecutionEnvironmentListItem.jsx +++ b/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentList/ExecutionEnvironmentListItem.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState, useCallback } from 'react'; import { string, bool, func } from 'prop-types'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; @@ -8,7 +8,10 @@ import { Tr, Td } from '@patternfly/react-table'; import { PencilAltIcon } from '@patternfly/react-icons'; import { ActionsTd, ActionItem } from '../../../components/PaginatedTable'; +import CopyButton from '../../../components/CopyButton'; import { ExecutionEnvironment } from '../../../types'; +import { ExecutionEnvironmentsAPI } from '../../../api'; +import { timeOfDay } from '../../../util/dates'; function ExecutionEnvironmentListItem({ executionEnvironment, @@ -17,7 +20,29 @@ function ExecutionEnvironmentListItem({ onSelect, i18n, rowIndex, + fetchExecutionEnvironments, }) { + const [isDisabled, setIsDisabled] = useState(false); + + const copyExecutionEnvironment = useCallback(async () => { + await ExecutionEnvironmentsAPI.copy(executionEnvironment.id, { + name: `${executionEnvironment.name} @ ${timeOfDay()}`, + }); + await fetchExecutionEnvironments(); + }, [ + executionEnvironment.id, + executionEnvironment.name, + fetchExecutionEnvironments, + ]); + + const handleCopyStart = useCallback(() => { + setIsDisabled(true); + }, []); + + const handleCopyFinish = useCallback(() => { + setIsDisabled(false); + }, []); + const labelId = `check-action-${executionEnvironment.id}`; return ( @@ -65,6 +90,19 @@ function ExecutionEnvironmentListItem({ + + + ); diff --git a/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentList/ExecutionEnvironmentListItem.test.jsx b/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentList/ExecutionEnvironmentListItem.test.jsx index 0e7c037aed..c32cb7ab4f 100644 --- a/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentList/ExecutionEnvironmentListItem.test.jsx +++ b/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentList/ExecutionEnvironmentListItem.test.jsx @@ -4,8 +4,15 @@ import { act } from 'react-dom/test-utils'; import { mountWithContexts } from '../../../../testUtils/enzymeHelpers'; import ExecutionEnvironmentListItem from './ExecutionEnvironmentListItem'; +import { ExecutionEnvironmentsAPI } from '../../../api'; + +jest.mock('../../../api'); describe('', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + let wrapper; const executionEnvironment = { name: 'Foo', @@ -13,7 +20,9 @@ describe('', () => { image: 'https://registry.com/r/image/manifest', organization: null, credential: null, - summary_fields: { user_capabilities: { edit: true } }, + summary_fields: { + user_capabilities: { edit: true, copy: true, delete: true }, + }, }; test('should mount successfully', async () => { @@ -71,4 +80,68 @@ describe('', () => { expect(wrapper.find('PencilAltIcon').exists()).toBeTruthy(); }); + + test('should call api to copy execution environment', async () => { + ExecutionEnvironmentsAPI.copy.mockResolvedValue(); + + wrapper = mountWithContexts( + + + {}} + /> + +
+ ); + + await act(async () => + wrapper.find('Button[aria-label="Copy"]').prop('onClick')() + ); + expect(ExecutionEnvironmentsAPI.copy).toHaveBeenCalled(); + }); + + test('should render proper alert modal on copy error', async () => { + ExecutionEnvironmentsAPI.copy.mockRejectedValue(new Error()); + + wrapper = mountWithContexts( + + + {}} + /> + +
+ ); + + await act(async () => + wrapper.find('Button[aria-label="Copy"]').prop('onClick')() + ); + wrapper.update(); + expect(wrapper.find('Modal').prop('isOpen')).toBe(true); + }); + + test('should not render copy button', async () => { + wrapper = mountWithContexts( + + + {}} + /> + +
+ ); + expect(wrapper.find('CopyButton').length).toBe(0); + }); });