diff --git a/awx/ui/package-lock.json b/awx/ui/package-lock.json index ff35c6549bb3..03c26547252f 100644 --- a/awx/ui/package-lock.json +++ b/awx/ui/package-lock.json @@ -47,6 +47,7 @@ "@nteract/mockument": "^1.0.4", "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.5", + "@testing-library/user-event": "14.4.3", "@wojtekmaj/enzyme-adapter-react-17": "0.6.5", "babel-plugin-macros": "3.1.0", "enzyme": "^3.10.0", @@ -4514,6 +4515,19 @@ "react-dom": "<18.0.0" } }, + "node_modules/@testing-library/user-event": { + "version": "14.4.3", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.4.3.tgz", + "integrity": "sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==", + "dev": true, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -25653,6 +25667,13 @@ "@types/react-dom": "<18.0.0" } }, + "@testing-library/user-event": { + "version": "14.4.3", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.4.3.tgz", + "integrity": "sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==", + "dev": true, + "requires": {} + }, "@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", diff --git a/awx/ui/package.json b/awx/ui/package.json index cae5912ad5b9..4b244eebb4b7 100644 --- a/awx/ui/package.json +++ b/awx/ui/package.json @@ -47,6 +47,7 @@ "@nteract/mockument": "^1.0.4", "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.5", + "@testing-library/user-event": "14.4.3", "@wojtekmaj/enzyme-adapter-react-17": "0.6.5", "babel-plugin-macros": "3.1.0", "enzyme": "^3.10.0", diff --git a/awx/ui/src/api/models/Instances.js b/awx/ui/src/api/models/Instances.js index 5552e85e691f..388bb2eb4e17 100644 --- a/awx/ui/src/api/models/Instances.js +++ b/awx/ui/src/api/models/Instances.js @@ -8,6 +8,7 @@ class Instances extends Base { this.readHealthCheckDetail = this.readHealthCheckDetail.bind(this); this.healthCheck = this.healthCheck.bind(this); this.readInstanceGroup = this.readInstanceGroup.bind(this); + this.deprovisionInstance = this.deprovisionInstance.bind(this); } healthCheck(instanceId) { diff --git a/awx/ui/src/screens/Instances/Shared/RemoveInstanceButton.test.js b/awx/ui/src/screens/Instances/Shared/RemoveInstanceButton.test.js new file mode 100644 index 000000000000..3d2d467956fd --- /dev/null +++ b/awx/ui/src/screens/Instances/Shared/RemoveInstanceButton.test.js @@ -0,0 +1,133 @@ +import React from 'react'; +import { within, render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { InstanceGroupsAPI } from 'api'; +import RemoveInstanceButton from './RemoveInstanceButton'; +import { I18nProvider } from '@lingui/react'; +import { i18n } from '@lingui/core'; +import { en } from 'make-plural/plurals'; +import english from '../../../../src/locales/en/messages'; + +jest.mock('api'); + +const instances = [ + { + id: 1, + type: 'instance', + url: '/api/v2/instances/1/', + capacity_adjustment: '0.40', + version: '13.0.0', + capacity: 10, + consumed_capacity: 0, + percent_capacity_remaining: 60.0, + jobs_running: 0, + jobs_total: 68, + cpu: 6, + node_type: 'execution', + node_state: 'ready', + memory: 2087469056, + cpu_capacity: 24, + mem_capacity: 1, + enabled: true, + managed_by_policy: true, + }, + { + id: 2, + type: 'instance', + url: '/api/v2/instances/2/', + capacity_adjustment: '0.40', + version: '13.0.0', + capacity: 10, + consumed_capacity: 0, + percent_capacity_remaining: 60.0, + jobs_running: 0, + jobs_total: 68, + cpu: 6, + node_type: 'control', + node_state: 'ready', + memory: 2087469056, + cpu_capacity: 24, + mem_capacity: 1, + enabled: true, + managed_by_policy: false, + }, +]; +describe('', () => { + test('Should open modal and deprovision node', async () => { + i18n.loadLocaleData({ en: { plurals: en } }); + i18n.load({ en: english }); + i18n.activate('en'); + InstanceGroupsAPI.read.mockResolvedValue({ + data: { results: [{ id: 1 }], count: 1 }, + }); + const user = userEvent.setup(); + const onRemove = jest.fn(); + render( + + + + ); + + const button = screen.getByRole('button'); + await user.click(button); + await waitFor(() => screen.getByRole('dialog')); + const modal = screen.getByRole('dialog'); + const removeButton = within(modal).getByRole('button', { + name: 'Confirm remove', + }); + + await user.click(removeButton); + + await waitFor(() => expect(onRemove).toBeCalled()); + }); + + test('Should be disabled', async () => { + const user = userEvent.setup(); + render( + + ); + + const button = screen.getByRole('button'); + await user.hover(button); + await waitFor(() => + screen.getByText('You do not have permission to remove instances:') + ); + }); + + test('Should handle error when fetching warning message details.', async () => { + InstanceGroupsAPI.read.mockRejectedValue( + new Error({ + response: { + config: { + method: 'get', + url: '/api/v2/instance_groups', + }, + data: 'An error occurred', + status: 403, + }, + }) + ); + const user = userEvent.setup(); + const onRemove = jest.fn(); + render( + + ); + + const button = screen.getByRole('button'); + await user.click(button); + await waitFor(() => screen.getByRole('dialog')); + screen.getByText('Error!'); + }); +});