Merge pull request #9544 from AlexSCorey/9485-9319-7516-fix

Fixes Several Bugs

SUMMARY
This address #9485 (Job template project field validate), #9319 (Job Details view only would show job type run, even if it was a job type check, #7516 (changes the Completed Jobs tab for a JT or WFJT to show Jobs since it show completed and pending/running jobs).
ISSUE TYPE

Bugfix Pull Request

COMPONENT NAME

UI

Reviewed-by: Jake McDermott <yo@jakemcdermott.me>
Reviewed-by: John Mitchell <None>
Reviewed-by: Tiago Góes <tiago.goes2009@gmail.com>
This commit is contained in:
softwarefactory-project-zuul[bot] 2021-03-22 14:00:08 +00:00 committed by GitHub
commit 7d190da1c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 100 additions and 40 deletions

View File

@ -94,4 +94,56 @@ describe('<ProjectLookup />', () => {
expect(wrapper.find('ProjectLookup')).toHaveLength(1);
expect(wrapper.find('Lookup').prop('isDisabled')).toBe(true);
});
test('should not show helper text', async () => {
let wrapper;
ProjectsAPI.readOptions.mockReturnValue({
data: {
actions: {
GET: {},
},
related_search_fields: [],
},
});
await act(async () => {
wrapper = mountWithContexts(
<ProjectLookup
isValid
helperTextInvalid="select value"
onChange={() => {}}
/>
);
});
wrapper.update();
expect(wrapper.find('div#project-helper').length).toBe(0);
});
test('should not show helper text', async () => {
let wrapper;
ProjectsAPI.readOptions.mockReturnValue({
data: {
actions: {
GET: {},
},
related_search_fields: [],
},
});
await act(async () => {
wrapper = mountWithContexts(
<ProjectLookup
isValid={false}
helperTextInvalid="select value"
onChange={() => {}}
/>
);
});
wrapper.update();
expect(wrapper.find('div#project-helper').text('helperTextInvalid')).toBe(
'select value'
);
});
});

View File

@ -64,8 +64,8 @@ function Host({ i18n, setBreadcrumb }) {
id: 2,
},
{
name: i18n._(t`Completed Jobs`),
link: `${match.url}/completed_jobs`,
name: i18n._(t`Jobs`),
link: `${match.url}/jobs`,
id: 3,
},
];
@ -122,7 +122,7 @@ function Host({ i18n, setBreadcrumb }) {
<Route path="/hosts/:id/groups" key="groups">
<HostGroups host={host} />
</Route>,
<Route path="/hosts/:id/completed_jobs" key="completed-jobs">
<Route path="/hosts/:id/jobs" key="jobs">
<JobList defaultParams={{ job__hosts: host.id }} />
</Route>,
]}

View File

@ -29,7 +29,7 @@ function Hosts({ i18n }) {
[`/hosts/${host.id}/details`]: i18n._(t`Details`),
[`/hosts/${host.id}/facts`]: i18n._(t`Facts`),
[`/hosts/${host.id}/groups`]: i18n._(t`Groups`),
[`/hosts/${host.id}/completed_jobs`]: i18n._(t`Completed Jobs`),
[`/hosts/${host.id}/jobs`]: i18n._(t`Jobs`),
});
},
[i18n]

View File

@ -56,7 +56,7 @@ function Inventories({ i18n }) {
...initScreenHeader.current,
[inventoryPath]: `${inventory.name}`,
[`${inventoryPath}/access`]: i18n._(t`Access`),
[`${inventoryPath}/completed_jobs`]: i18n._(t`Completed jobs`),
[`${inventoryPath}/jobs`]: i18n._(t`Jobs`),
[`${inventoryPath}/details`]: i18n._(t`Details`),
[`${inventoryPath}/edit`]: i18n._(t`Edit details`),
@ -69,9 +69,7 @@ function Inventories({ i18n }) {
[`${inventoryHostsPath}/${nestedObject?.id}/details`]: i18n._(
t`Host details`
),
[`${inventoryHostsPath}/${nestedObject?.id}/completed_jobs`]: i18n._(
t`Completed jobs`
),
[`${inventoryHostsPath}/${nestedObject?.id}/jobs`]: i18n._(t`Jobs`),
[`${inventoryHostsPath}/${nestedObject?.id}/facts`]: i18n._(t`Facts`),
[`${inventoryHostsPath}/${nestedObject?.id}/groups`]: i18n._(t`Groups`),

View File

@ -65,8 +65,8 @@ function Inventory({ i18n, setBreadcrumb }) {
{ name: i18n._(t`Hosts`), link: `${match.url}/hosts`, id: 3 },
{ name: i18n._(t`Sources`), link: `${match.url}/sources`, id: 4 },
{
name: i18n._(t`Completed Jobs`),
link: `${match.url}/completed_jobs`,
name: i18n._(t`Jobs`),
link: `${match.url}/jobs`,
id: 5,
},
];
@ -160,10 +160,7 @@ function Inventory({ i18n, setBreadcrumb }) {
setBreadcrumb={setBreadcrumb}
/>
</Route>,
<Route
path="/inventories/inventory/:id/completed_jobs"
key="completed_jobs"
>
<Route path="/inventories/inventory/:id/jobs" key="jobs">
<JobList
defaultParams={{
or__job__inventory: inventory.id,

View File

@ -85,8 +85,8 @@ function InventoryHost({ i18n, setBreadcrumb, inventory }) {
id: 3,
},
{
name: i18n._(t`Completed Jobs`),
link: `${match.url}/completed_jobs`,
name: i18n._(t`Jobs`),
link: `${match.url}/jobs`,
id: 4,
},
];
@ -151,8 +151,8 @@ function InventoryHost({ i18n, setBreadcrumb, inventory }) {
<InventoryHostGroups />
</Route>
<Route
key="completed-jobs"
path="/inventories/inventory/:id/hosts/:hostId/completed_jobs"
key="jobs"
path="/inventories/inventory/:id/hosts/:hostId/jobs"
>
<JobList defaultParams={{ job__hosts: host.id }} />
</Route>

View File

@ -49,7 +49,7 @@ describe('<InventoryHost />', () => {
'Details',
'Facts',
'Groups',
'Completed Jobs',
'Jobs',
];
wrapper.find('RoutedTabs li').forEach((tab, index) => {
expect(tab.text()).toEqual(expectedTabs[index]);

View File

@ -70,8 +70,8 @@ function SmartInventory({ i18n, setBreadcrumb }) {
{ name: i18n._(t`Access`), link: `${match.url}/access`, id: 1 },
{ name: i18n._(t`Hosts`), link: `${match.url}/hosts`, id: 2 },
{
name: i18n._(t`Completed jobs`),
link: `${match.url}/completed_jobs`,
name: i18n._(t`Jobs`),
link: `${match.url}/jobs`,
id: 3,
},
];
@ -150,10 +150,7 @@ function SmartInventory({ i18n, setBreadcrumb }) {
setBreadcrumb={setBreadcrumb}
/>
</Route>,
<Route
key="completed_jobs"
path="/inventories/smart_inventory/:id/completed_jobs"
>
<Route key="jobs" path="/inventories/smart_inventory/:id/jobs">
<JobList
defaultParams={{
or__job__inventory: inventory.id,

View File

@ -80,7 +80,10 @@ function JobDetail({ job, i18n }) {
const jobTypes = {
project_update: i18n._(t`Source Control Update`),
inventory_update: i18n._(t`Inventory Sync`),
job: i18n._(t`Playbook Run`),
job:
job.job_type === 'check'
? i18n._(t`Playbook Check`)
: i18n._(t`Playbook Run`),
ad_hoc_command: i18n._(t`Command`),
management_job: i18n._(t`Management Job`),
workflow_job: i18n._(t`Workflow Job`),

View File

@ -10,16 +10,15 @@ jest.mock('../../../api');
describe('<JobDetail />', () => {
let wrapper;
function assertDetail(label, value) {
expect(wrapper.find(`Detail[label="${label}"] dt`).text()).toBe(label);
expect(wrapper.find(`Detail[label="${label}"] dd`).text()).toBe(value);
}
afterEach(() => {
wrapper.unmount();
});
test('should display details', () => {
function assertDetail(label, value) {
expect(wrapper.find(`Detail[label="${label}"] dt`).text()).toBe(label);
expect(wrapper.find(`Detail[label="${label}"] dd`).text()).toBe(value);
}
wrapper = mountWithContexts(
<JobDetail
job={{
@ -170,4 +169,15 @@ describe('<JobDetail />', () => {
assertMissingDetail('Project');
assertMissingDetail('Inventory');
});
test('should display Playbook Check detail', () => {
wrapper = mountWithContexts(
<JobDetail
job={{
...mockJobData,
job_type: 'check',
}}
/>
);
assertDetail('Job Type', 'Playbook Check');
});
});

View File

@ -161,8 +161,8 @@ function Template({ i18n, setBreadcrumb }) {
tabsArray.push(
{
name: i18n._(t`Completed Jobs`),
link: `${match.url}/completed_jobs`,
name: i18n._(t`Jobs`),
link: `${match.url}/jobs`,
},
{
name: canAddAndEditSurvey ? i18n._(t`Survey`) : i18n._(t`View Survey`),
@ -246,7 +246,7 @@ function Template({ i18n, setBreadcrumb }) {
/>
</Route>
)}
<Route path="/templates/:templateType/:id/completed_jobs">
<Route path="/templates/:templateType/:id/jobs">
<JobList defaultParams={{ job__job_template: template.id }} />
</Route>
<Route path="/templates/:templateType/:id/survey">

View File

@ -45,7 +45,7 @@ function Templates({ i18n }) {
[`${templatePath}/edit`]: i18n._(t`Edit Details`),
[`${templatePath}/access`]: i18n._(t`Access`),
[`${templatePath}/notifications`]: i18n._(t`Notifications`),
[`${templatePath}/completed_jobs`]: i18n._(t`Completed Jobs`),
[`${templatePath}/jobs`]: i18n._(t`Jobs`),
[surveyPath]: i18n._(t`Survey`),
[`${surveyPath}/add`]: i18n._(t`Add Question`),
[`${surveyPath}/edit`]: i18n._(t`Edit Question`),

View File

@ -142,8 +142,8 @@ function WorkflowJobTemplate({ i18n, setBreadcrumb }) {
link: `${match.url}/visualizer`,
},
{
name: i18n._(t`Completed Jobs`),
link: `${match.url}/completed_jobs`,
name: i18n._(t`Jobs`),
link: `${match.url}/jobs`,
},
{
name: canAddAndEditSurvey ? i18n._(t`Survey`) : i18n._(t`View Survey`),
@ -253,7 +253,7 @@ function WorkflowJobTemplate({ i18n, setBreadcrumb }) {
</Route>
)}
{template?.id && (
<Route path="/templates/:templateType/:id/completed_jobs">
<Route path="/templates/:templateType/:id/jobs">
<JobList
defaultParams={{
workflow_job__workflow_job_template: template.id,

View File

@ -154,7 +154,7 @@ function JobTemplateForm({
const handleProjectUpdate = useCallback(
value => {
setFieldValue('playbook', 0);
setFieldValue('playbook', '');
setFieldValue('scm_branch', '');
setFieldValue('project', value);
},
@ -271,7 +271,9 @@ function JobTemplateForm({
onBlur={() => projectHelpers.setTouched()}
tooltip={i18n._(t`Select the project containing the playbook
you want this job to execute.`)}
isValid={!projectMeta.touched || !projectMeta.error}
isValid={
!projectMeta.touched || !projectMeta.error || projectField.value
}
helperTextInvalid={projectMeta.error}
onChange={handleProjectUpdate}
required

View File

@ -58,6 +58,7 @@ function PlaybookSelect({
placeholderText={i18n._(t`Select a playbook`)}
isCreateable={false}
onSelect={(event, value) => {
setIsOpen(false);
onChange(value);
}}
id="template-playbook"