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!');
+ });
+});