diff --git a/awx/ui_next/src/screens/Project/ProjectAdd/ProjectAdd.test.jsx b/awx/ui_next/src/screens/Project/ProjectAdd/ProjectAdd.test.jsx
index 2b9857bc89..3bd0469dea 100644
--- a/awx/ui_next/src/screens/Project/ProjectAdd/ProjectAdd.test.jsx
+++ b/awx/ui_next/src/screens/Project/ProjectAdd/ProjectAdd.test.jsx
@@ -108,7 +108,11 @@ describe('', () => {
);
});
await changeState;
- wrapper.find('form').simulate('submit');
+ await act(async () => {
+ wrapper.find('form').simulate('submit');
+ });
+ wrapper.update();
+ expect(ProjectsAPI.create).toHaveBeenCalledTimes(1);
});
test('handleSubmit should throw an error', async () => {
diff --git a/awx/ui_next/src/screens/Project/ProjectEdit/ProjectEdit.test.jsx b/awx/ui_next/src/screens/Project/ProjectEdit/ProjectEdit.test.jsx
new file mode 100644
index 0000000000..99975a807b
--- /dev/null
+++ b/awx/ui_next/src/screens/Project/ProjectEdit/ProjectEdit.test.jsx
@@ -0,0 +1,153 @@
+import React from 'react';
+import { act } from 'react-dom/test-utils';
+import { createMemoryHistory } from 'history';
+import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
+import ProjectEdit from './ProjectEdit';
+import { ProjectsAPI, CredentialTypesAPI } from '@api';
+
+jest.mock('@api');
+
+describe('', () => {
+ let wrapper;
+ const projectData = {
+ id: 123,
+ name: 'foo',
+ description: 'bar',
+ scm_type: 'git',
+ scm_url: 'https://foo.bar',
+ scm_clean: true,
+ credential: 100,
+ organization: 2,
+ scm_update_on_launch: true,
+ scm_update_cache_timeout: 3,
+ allow_override: false,
+ custom_virtualenv: '/venv/custom-env',
+ summary_fields: {
+ credential: {
+ id: 100,
+ credential_type_id: 5,
+ kind: 'insights',
+ },
+ },
+ };
+
+ const projectOptionsResolve = {
+ data: {
+ actions: {
+ GET: {
+ scm_type: {
+ choices: [
+ ['', 'Manual'],
+ ['git', 'Git'],
+ ['hg', 'Mercurial'],
+ ['svn', 'Subversion'],
+ ['insights', 'Red Hat Insights'],
+ ],
+ },
+ },
+ },
+ },
+ };
+
+ const scmCredentialResolve = {
+ data: {
+ results: [
+ {
+ id: 4,
+ name: 'Source Control',
+ kind: 'scm',
+ },
+ ],
+ },
+ };
+
+ const insightsCredentialResolve = {
+ data: {
+ results: [
+ {
+ id: 5,
+ name: 'Insights',
+ kind: 'insights',
+ },
+ ],
+ },
+ };
+
+ beforeEach(async () => {
+ await ProjectsAPI.readOptions.mockImplementation(
+ () => projectOptionsResolve
+ );
+ await CredentialTypesAPI.read.mockImplementationOnce(
+ () => scmCredentialResolve
+ );
+ await CredentialTypesAPI.read.mockImplementationOnce(
+ () => insightsCredentialResolve
+ );
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ test('initially renders successfully', async () => {
+ await act(async () => {
+ wrapper = mountWithContexts();
+ });
+ expect(wrapper.length).toBe(1);
+ });
+
+ test('handleSubmit should post to the api', async () => {
+ const history = createMemoryHistory();
+ ProjectsAPI.update.mockResolvedValueOnce({
+ data: { ...projectData },
+ });
+ await act(async () => {
+ wrapper = mountWithContexts(, {
+ context: { router: { history } },
+ });
+ });
+ await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
+ await act(async () => {
+ wrapper.find('form').simulate('submit');
+ });
+ wrapper.update();
+ expect(ProjectsAPI.update).toHaveBeenCalledTimes(1);
+ });
+
+ test('handleSubmit should throw an error', async () => {
+ ProjectsAPI.update.mockImplementation(() => Promise.reject(new Error()));
+ await act(async () => {
+ wrapper = mountWithContexts();
+ });
+ await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
+ await act(async () => {
+ wrapper.find('form').simulate('submit');
+ });
+ wrapper.update();
+ expect(ProjectsAPI.update).toHaveBeenCalledTimes(1);
+ expect(wrapper.find('ProjectEdit .formSubmitError').length).toBe(1);
+ });
+
+ test('CardHeader close button should navigate to project details', async () => {
+ const history = createMemoryHistory();
+ await act(async () => {
+ wrapper = mountWithContexts(, {
+ context: { router: { history } },
+ });
+ });
+ wrapper.find('CardCloseButton').simulate('click');
+ expect(history.location.pathname).toEqual('/projects/123/details');
+ });
+
+ test('CardBody cancel button should navigate to project details', async () => {
+ const history = createMemoryHistory();
+ await act(async () => {
+ wrapper = mountWithContexts(, {
+ context: { router: { history } },
+ });
+ });
+ await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
+ wrapper.find('ProjectEdit button[aria-label="Cancel"]').simulate('click');
+ expect(history.location.pathname).toEqual('/projects/123/details');
+ });
+});
diff --git a/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx b/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx
index 94126eb1c7..046e90fa27 100644
--- a/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx
+++ b/awx/ui_next/src/screens/Project/shared/ProjectForm.jsx
@@ -122,16 +122,23 @@ function ProjectForm({ project, ...props }) {
scm_update_cache_timeout: 0,
};
+ /* Save current scm subform field values to state */
const saveSubFormState = form => {
- const updatedScmFormFields = { ...scmFormFields };
+ const currentScmFormFields = { ...scmFormFields };
- Object.keys(updatedScmFormFields).forEach(label => {
- updatedScmFormFields[label] = form.values[label];
+ Object.keys(currentScmFormFields).forEach(label => {
+ currentScmFormFields[label] = form.values[label];
});
- setScmSubFormState(updatedScmFormFields);
+ setScmSubFormState(currentScmFormFields);
};
+ /**
+ * If scm type is !== the initial scm type value,
+ * reset scm subform field values to defaults.
+ * If scm type is === the initial scm type value,
+ * reset scm subform field values to scmSubFormState.
+ */
const resetScmTypeFields = (value, form) => {
if (form.values.scm_type === form.initialValues.scm_type) {
saveSubFormState(form);
diff --git a/awx/ui_next/src/screens/Project/shared/ProjectForm.test.jsx b/awx/ui_next/src/screens/Project/shared/ProjectForm.test.jsx
index 18b4c5f680..550f60f7c4 100644
--- a/awx/ui_next/src/screens/Project/shared/ProjectForm.test.jsx
+++ b/awx/ui_next/src/screens/Project/shared/ProjectForm.test.jsx
@@ -21,6 +21,13 @@ describe('', () => {
scm_update_cache_timeout: 3,
allow_override: false,
custom_virtualenv: '/venv/custom-env',
+ summary_fields: {
+ credential: {
+ id: 100,
+ credential_type_id: 4,
+ kind: 'scm',
+ },
+ },
};
const projectOptionsResolve = {