diff --git a/awx/ui_next/src/api/index.js b/awx/ui_next/src/api/index.js index 2fd939ea69..f9acea4211 100644 --- a/awx/ui_next/src/api/index.js +++ b/awx/ui_next/src/api/index.js @@ -24,7 +24,6 @@ import UnifiedJobs from './models/UnifiedJobs'; import Users from './models/Users'; import WorkflowJobs from './models/WorkflowJobs'; import WorkflowJobTemplates from './models/WorkflowJobTemplates'; -import useRequest from './useRequest'; const AdHocCommandsAPI = new AdHocCommands(); const ConfigAPI = new Config(); @@ -80,5 +79,4 @@ export { UsersAPI, WorkflowJobsAPI, WorkflowJobTemplatesAPI, - useRequest, }; diff --git a/awx/ui_next/src/screens/Inventory/InventoryDetail/InventoryDetail.jsx b/awx/ui_next/src/screens/Inventory/InventoryDetail/InventoryDetail.jsx index c48057f648..8d3f2eb8fb 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryDetail/InventoryDetail.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryDetail/InventoryDetail.jsx @@ -10,7 +10,8 @@ import { VariablesDetail } from '@components/CodeMirrorInput'; import DeleteButton from '@components/DeleteButton'; import ContentError from '@components/ContentError'; import ContentLoading from '@components/ContentLoading'; -import { InventoriesAPI, useRequest } from '@api'; +import { InventoriesAPI } from '@api'; +import useRequest from '@util/useRequest'; import { Inventory } from '../../../types'; function InventoryDetail({ inventory, i18n }) { diff --git a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx index 3f571d9a55..f2b37daada 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx @@ -4,7 +4,8 @@ import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; import { Card, PageSection } from '@patternfly/react-core'; -import { OrganizationsAPI, useRequest } from '@api'; +import { OrganizationsAPI } from '@api'; +import useRequest from '@util/useRequest'; import AlertModal from '@components/AlertModal'; import DataListToolbar from '@components/DataListToolbar'; import ErrorDetail from '@components/ErrorDetail'; @@ -58,7 +59,7 @@ function OrganizationsList({ i18n }) { const { isLoading: isDeleteLoading, - // error: deletionError, + error: dError, request: deleteOrganizations, } = useRequest( useCallback(async () => { @@ -68,6 +69,12 @@ function OrganizationsList({ i18n }) { }, [selected]) ); + useEffect(() => { + if (dError) { + setDeletionError(dError); + } + }, [dError]); + useEffect(() => { fetchOrganizations(); }, [fetchOrganizations]); diff --git a/awx/ui_next/src/api/useRequest.js b/awx/ui_next/src/util/useRequest.js similarity index 100% rename from awx/ui_next/src/api/useRequest.js rename to awx/ui_next/src/util/useRequest.js diff --git a/awx/ui_next/src/util/useRequest.test.jsx b/awx/ui_next/src/util/useRequest.test.jsx new file mode 100644 index 0000000000..1cf7ed955b --- /dev/null +++ b/awx/ui_next/src/util/useRequest.test.jsx @@ -0,0 +1,109 @@ +import React from 'react'; +import { act } from 'react-dom/test-utils'; +import { mount } from 'enzyme'; +import useRequest from './useRequest'; + +function TestInner() { + return
; +} +function Test({ makeRequest, initialValue = {} }) { + const request = useRequest(makeRequest, initialValue); + return ; +} + +describe('useRequest', () => { + test('should return initial value as result', async () => { + const makeRequest = jest.fn(); + makeRequest.mockResolvedValue({ data: 'foo' }); + const wrapper = mount( + + ); + + expect(wrapper.find('TestInner').prop('result')).toEqual({ initial: true }); + }); + + test('should return result', async () => { + const makeRequest = jest.fn(); + makeRequest.mockResolvedValue({ data: 'foo' }); + const wrapper = mount(); + + await act(async () => { + wrapper.find('TestInner').invoke('request')(); + }); + wrapper.update(); + expect(wrapper.find('TestInner').prop('result')).toEqual({ data: 'foo' }); + }); + + test('should is isLoading flag', async () => { + const makeRequest = jest.fn(); + let resolve; + const promise = new Promise(r => { + resolve = r; + }); + makeRequest.mockReturnValue(promise); + const wrapper = mount(); + + await act(async () => { + wrapper.find('TestInner').invoke('request')(); + }); + wrapper.update(); + expect(wrapper.find('TestInner').prop('isLoading')).toEqual(true); + await act(async () => { + resolve({ data: 'foo' }); + }); + wrapper.update(); + expect(wrapper.find('TestInner').prop('isLoading')).toEqual(false); + expect(wrapper.find('TestInner').prop('result')).toEqual({ data: 'foo' }); + }); + + test('should invoke request function', async () => { + const makeRequest = jest.fn(); + makeRequest.mockResolvedValue({ data: 'foo' }); + const wrapper = mount(); + + expect(makeRequest).not.toHaveBeenCalled(); + await act(async () => { + wrapper.find('TestInner').invoke('request')(); + }); + wrapper.update(); + expect(makeRequest).toHaveBeenCalledTimes(1); + }); + + test('should return error thrown from request function', async () => { + const error = new Error('error'); + const makeRequest = () => { + throw error; + }; + const wrapper = mount(); + + await act(async () => { + wrapper.find('TestInner').invoke('request')(); + }); + wrapper.update(); + expect(wrapper.find('TestInner').prop('error')).toEqual(error); + }); + + test('should not update state after unmount', async () => { + const makeRequest = jest.fn(); + let resolve; + const promise = new Promise(r => { + resolve = r; + }); + makeRequest.mockReturnValue(promise); + const wrapper = mount(); + + expect(makeRequest).not.toHaveBeenCalled(); + await act(async () => { + wrapper.find('TestInner').invoke('request')(); + }); + wrapper.unmount(); + await act(async () => { + resolve({ data: 'foo' }); + }); + }); +});