mirror of
https://github.com/ansible/awx.git
synced 2026-05-07 09:27:36 -02:30
Merge pull request #5654 from AlexSCorey/5619-BranchFieldMissing
Fixes navigation bug in InventoryAdd Adds SCM Branch field on JTForm Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
@@ -57,7 +57,9 @@ function InventoryAdd() {
|
|||||||
);
|
);
|
||||||
await Promise.all(associatePromises);
|
await Promise.all(associatePromises);
|
||||||
}
|
}
|
||||||
const url = history.location.pathname.search('smart')
|
const url = history.location.pathname.startsWith(
|
||||||
|
'/inventories/smart_inventory'
|
||||||
|
)
|
||||||
? `/inventories/smart_inventory/${inventoryId}/details`
|
? `/inventories/smart_inventory/${inventoryId}/details`
|
||||||
: `/inventories/inventory/${inventoryId}/details`;
|
: `/inventories/inventory/${inventoryId}/details`;
|
||||||
|
|
||||||
|
|||||||
@@ -41,8 +41,7 @@ describe('<InventoryAdd />', () => {
|
|||||||
test('Initially renders successfully', () => {
|
test('Initially renders successfully', () => {
|
||||||
expect(wrapper.length).toBe(1);
|
expect(wrapper.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
test('handleSubmit should call the api and redirect to details page', async () => {
|
||||||
test('handleSubmit should call the api', async () => {
|
|
||||||
const instanceGroups = [{ name: 'Bizz', id: 1 }, { name: 'Buzz', id: 2 }];
|
const instanceGroups = [{ name: 'Bizz', id: 1 }, { name: 'Buzz', id: 2 }];
|
||||||
await waitForElement(wrapper, 'isLoading', el => el.length === 0);
|
await waitForElement(wrapper, 'isLoading', el => el.length === 0);
|
||||||
|
|
||||||
@@ -64,6 +63,7 @@ describe('<InventoryAdd />', () => {
|
|||||||
IG.id
|
IG.id
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
expect(history.location.pathname).toBe('/inventories/inventory/13/details');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handleCancel should return the user back to the inventories list', async () => {
|
test('handleCancel should return the user back to the inventories list', async () => {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ const jobTemplateData = {
|
|||||||
allow_simultaneous: false,
|
allow_simultaneous: false,
|
||||||
use_fact_cache: false,
|
use_fact_cache: false,
|
||||||
host_config_key: '',
|
host_config_key: '',
|
||||||
|
scm_branch: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('<JobTemplateAdd />', () => {
|
describe('<JobTemplateAdd />', () => {
|
||||||
|
|||||||
@@ -196,6 +196,7 @@ class JobTemplateDetail extends Component {
|
|||||||
) : (
|
) : (
|
||||||
renderMissingDataDetail(i18n._(t`Project`))
|
renderMissingDataDetail(i18n._(t`Project`))
|
||||||
)}
|
)}
|
||||||
|
<Detail label={i18n._(t`SCM Branch`)} value={template.scm_branch} />
|
||||||
<Detail label={i18n._(t`Playbook`)} value={playbook} />
|
<Detail label={i18n._(t`Playbook`)} value={playbook} />
|
||||||
<Detail label={i18n._(t`Forks`)} value={forks || '0'} />
|
<Detail label={i18n._(t`Forks`)} value={forks || '0'} />
|
||||||
<Detail label={i18n._(t`Limit`)} value={limit} />
|
<Detail label={i18n._(t`Limit`)} value={limit} />
|
||||||
|
|||||||
@@ -143,4 +143,19 @@ describe('<JobTemplateDetail />', () => {
|
|||||||
template.summary_fields.credentials[0]
|
template.summary_fields.credentials[0]
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
test('should render SCM_Branch', async () => {
|
||||||
|
const mockTemplate = { ...template };
|
||||||
|
mockTemplate.scm_branch = 'Foo branch';
|
||||||
|
|
||||||
|
const wrapper = mountWithContexts(
|
||||||
|
<JobTemplateDetail template={mockTemplate} />
|
||||||
|
);
|
||||||
|
await waitForElement(
|
||||||
|
wrapper,
|
||||||
|
'JobTemplateDetail',
|
||||||
|
el => el.state('hasContentLoading') === false
|
||||||
|
);
|
||||||
|
const SCMBranch = wrapper.find('Detail[label="SCM Branch"]');
|
||||||
|
expect(SCMBranch.prop('value')).toBe('Foo branch');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ const mockJobTemplate = {
|
|||||||
allow_simultaneous: false,
|
allow_simultaneous: false,
|
||||||
use_fact_cache: false,
|
use_fact_cache: false,
|
||||||
host_config_key: '',
|
host_config_key: '',
|
||||||
|
scm_branch: '',
|
||||||
summary_fields: {
|
summary_fields: {
|
||||||
user_capabilities: {
|
user_capabilities: {
|
||||||
edit: true,
|
edit: true,
|
||||||
@@ -161,6 +162,11 @@ describe('<JobTemplateEdit />', () => {
|
|||||||
JobTemplatesAPI.readInstanceGroups.mockReturnValue({
|
JobTemplatesAPI.readInstanceGroups.mockReturnValue({
|
||||||
data: { results: mockInstanceGroups },
|
data: { results: mockInstanceGroups },
|
||||||
});
|
});
|
||||||
|
ProjectsAPI.readDetail.mockReturnValue({
|
||||||
|
id: 1,
|
||||||
|
allow_override: true,
|
||||||
|
name: 'foo',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import {
|
|||||||
ProjectLookup,
|
ProjectLookup,
|
||||||
MultiCredentialsLookup,
|
MultiCredentialsLookup,
|
||||||
} from '@components/Lookup';
|
} from '@components/Lookup';
|
||||||
import { JobTemplatesAPI } from '@api';
|
import { JobTemplatesAPI, ProjectsAPI } from '@api';
|
||||||
import LabelSelect from './LabelSelect';
|
import LabelSelect from './LabelSelect';
|
||||||
import PlaybookSelect from './PlaybookSelect';
|
import PlaybookSelect from './PlaybookSelect';
|
||||||
|
|
||||||
@@ -81,16 +81,31 @@ class JobTemplateForm extends Component {
|
|||||||
this.loadRelatedInstanceGroups = this.loadRelatedInstanceGroups.bind(this);
|
this.loadRelatedInstanceGroups = this.loadRelatedInstanceGroups.bind(this);
|
||||||
this.handleProjectUpdate = this.handleProjectUpdate.bind(this);
|
this.handleProjectUpdate = this.handleProjectUpdate.bind(this);
|
||||||
this.setContentError = this.setContentError.bind(this);
|
this.setContentError = this.setContentError.bind(this);
|
||||||
|
this.fetchProject = this.fetchProject.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { validateField } = this.props;
|
const { validateField } = this.props;
|
||||||
this.setState({ contentError: null, hasContentLoading: true });
|
this.setState({ contentError: null, hasContentLoading: true });
|
||||||
// TODO: determine when LabelSelect has finished loading labels
|
// TODO: determine when LabelSelect has finished loading labels
|
||||||
Promise.all([this.loadRelatedInstanceGroups()]).then(() => {
|
Promise.all([this.loadRelatedInstanceGroups(), this.fetchProject()]).then(
|
||||||
this.setState({ hasContentLoading: false });
|
() => {
|
||||||
validateField('project');
|
this.setState({ hasContentLoading: false });
|
||||||
});
|
validateField('project');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchProject() {
|
||||||
|
const { project } = this.state;
|
||||||
|
if (project && project.id) {
|
||||||
|
try {
|
||||||
|
const { data: projectData } = await ProjectsAPI.readDetail(project.id);
|
||||||
|
this.setState({ project: projectData });
|
||||||
|
} catch (err) {
|
||||||
|
this.setState({ contentError: err });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadRelatedInstanceGroups() {
|
async loadRelatedInstanceGroups() {
|
||||||
@@ -124,6 +139,8 @@ class JobTemplateForm extends Component {
|
|||||||
handleProjectUpdate(project) {
|
handleProjectUpdate(project) {
|
||||||
const { setFieldValue } = this.props;
|
const { setFieldValue } = this.props;
|
||||||
setFieldValue('project', project.id);
|
setFieldValue('project', project.id);
|
||||||
|
setFieldValue('playbook', 0);
|
||||||
|
setFieldValue('scm_branch', '');
|
||||||
this.setState({ project });
|
this.setState({ project });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,6 +164,7 @@ class JobTemplateForm extends Component {
|
|||||||
i18n,
|
i18n,
|
||||||
template,
|
template,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const jobTypeOptions = [
|
const jobTypeOptions = [
|
||||||
{
|
{
|
||||||
value: '',
|
value: '',
|
||||||
@@ -269,6 +287,14 @@ class JobTemplateForm extends Component {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
|
{project && project.allow_override && (
|
||||||
|
<FormField
|
||||||
|
id="scm_branch"
|
||||||
|
name="scm_branch"
|
||||||
|
type="text"
|
||||||
|
label={i18n._(t`SCM Branch`)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Field
|
<Field
|
||||||
name="playbook"
|
name="playbook"
|
||||||
validate={required(i18n._(t`Select a value for this field`), i18n)}
|
validate={required(i18n._(t`Select a value for this field`), i18n)}
|
||||||
@@ -583,6 +609,7 @@ const FormikApp = withFormik({
|
|||||||
job_type: template.job_type || 'run',
|
job_type: template.job_type || 'run',
|
||||||
inventory: template.inventory || '',
|
inventory: template.inventory || '',
|
||||||
project: template.project || '',
|
project: template.project || '',
|
||||||
|
scm_branch: template.scm_branch || '',
|
||||||
playbook: template.playbook || '',
|
playbook: template.playbook || '',
|
||||||
labels: summary_fields.labels.results || [],
|
labels: summary_fields.labels.results || [],
|
||||||
forks: template.forks || 0,
|
forks: template.forks || 0,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ describe('<JobTemplateForm />', () => {
|
|||||||
project: 3,
|
project: 3,
|
||||||
playbook: 'Baz',
|
playbook: 'Baz',
|
||||||
type: 'job_template',
|
type: 'job_template',
|
||||||
|
scm_branch: 'Foo',
|
||||||
summary_fields: {
|
summary_fields: {
|
||||||
inventory: {
|
inventory: {
|
||||||
id: 2,
|
id: 2,
|
||||||
@@ -88,6 +89,11 @@ describe('<JobTemplateForm />', () => {
|
|||||||
ProjectsAPI.readPlaybooks.mockReturnValue({
|
ProjectsAPI.readPlaybooks.mockReturnValue({
|
||||||
data: ['debug.yml'],
|
data: ['debug.yml'],
|
||||||
});
|
});
|
||||||
|
ProjectsAPI.readDetail.mockReturnValue({
|
||||||
|
name: 'foo',
|
||||||
|
id: 1,
|
||||||
|
allow_override: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -126,7 +132,6 @@ describe('<JobTemplateForm />', () => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('input#template-name').simulate('change', {
|
wrapper.find('input#template-name').simulate('change', {
|
||||||
@@ -145,16 +150,27 @@ describe('<JobTemplateForm />', () => {
|
|||||||
wrapper.find('ProjectLookup').invoke('onChange')({
|
wrapper.find('ProjectLookup').invoke('onChange')({
|
||||||
id: 4,
|
id: 4,
|
||||||
name: 'project',
|
name: 'project',
|
||||||
|
allow_override: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('input#scm_branch').simulate('change', {
|
||||||
|
target: { value: 'devel', name: 'scm_branch' },
|
||||||
});
|
});
|
||||||
wrapper.find('AnsibleSelect[name="playbook"]').simulate('change', {
|
wrapper.find('AnsibleSelect[name="playbook"]').simulate('change', {
|
||||||
target: { value: 'new baz type', name: 'playbook' },
|
target: { value: 'new baz type', name: 'playbook' },
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
wrapper
|
wrapper
|
||||||
.find('CredentialChip')
|
.find('CredentialChip')
|
||||||
.at(0)
|
.at(0)
|
||||||
.prop('onClick')();
|
.prop('onClick')();
|
||||||
});
|
});
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
|
|
||||||
expect(wrapper.find('input#template-name').prop('value')).toEqual(
|
expect(wrapper.find('input#template-name').prop('value')).toEqual(
|
||||||
'new foo'
|
'new foo'
|
||||||
);
|
);
|
||||||
@@ -171,7 +187,9 @@ describe('<JobTemplateForm />', () => {
|
|||||||
expect(wrapper.find('ProjectLookup').prop('value')).toEqual({
|
expect(wrapper.find('ProjectLookup').prop('value')).toEqual({
|
||||||
id: 4,
|
id: 4,
|
||||||
name: 'project',
|
name: 'project',
|
||||||
|
allow_override: true,
|
||||||
});
|
});
|
||||||
|
expect(wrapper.find('input#scm_branch').prop('value')).toEqual('devel');
|
||||||
expect(
|
expect(
|
||||||
wrapper.find('AnsibleSelect[name="playbook"]').prop('value')
|
wrapper.find('AnsibleSelect[name="playbook"]').prop('value')
|
||||||
).toEqual('new baz type');
|
).toEqual('new baz type');
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ function PlaybookSelect({
|
|||||||
i18n,
|
i18n,
|
||||||
}) {
|
}) {
|
||||||
const [options, setOptions] = useState([]);
|
const [options, setOptions] = useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!projectId) {
|
if (!projectId) {
|
||||||
return;
|
return;
|
||||||
@@ -28,6 +29,7 @@ function PlaybookSelect({
|
|||||||
label: playbook,
|
label: playbook,
|
||||||
isDisabled: false,
|
isDisabled: false,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
opts.unshift({
|
opts.unshift({
|
||||||
value: '',
|
value: '',
|
||||||
key: '',
|
key: '',
|
||||||
@@ -40,7 +42,6 @@ function PlaybookSelect({
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}, [projectId, i18n, onError]);
|
}, [projectId, i18n, onError]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnsibleSelect
|
<AnsibleSelect
|
||||||
id="template-playbook"
|
id="template-playbook"
|
||||||
|
|||||||
Reference in New Issue
Block a user