Fixes naviation bug by create ProjectTemplateList and ProjectTemplateListItem

Adds tests for those new files and removes uncessary test from TemplateListItem
This commit is contained in:
Alex Corey 2020-02-23 11:16:44 -05:00
parent df77147d65
commit ce8897d3e8
5 changed files with 330 additions and 81 deletions

View File

@ -18,7 +18,7 @@ import PaginatedDataList, {
import { getQSConfig, parseQueryString } from '@util/qs';
import AddDropDownButton from '@components/AddDropDownButton';
import ProjectTemplatesListItem from '../../Template/TemplateList/TemplateListItem';
import ProjectTemplatesListItem from './ProjectJobTemplatesListItem';
// The type value in const QS_CONFIG below does not have a space between job_template and
// workflow_job_template so the params sent to the API match what the api expects.
@ -238,7 +238,7 @@ function ProjectJobTemplatesList({ i18n }) {
itemsToDelete={selected}
pluralizedItemName="Templates"
/>,
(canAddJT || canAddWFJT) && addButton,
...(canAddJT || canAddWFJT ? [addButton] : []),
]}
/>
)}

View File

@ -0,0 +1,126 @@
import React from 'react';
import { Link } from 'react-router-dom';
import {
Button,
DataListAction as _DataListAction,
DataListCell,
DataListCheck,
DataListItem,
DataListItemRow,
DataListItemCells,
Tooltip,
} from '@patternfly/react-core';
import { t } from '@lingui/macro';
import { withI18n } from '@lingui/react';
import {
ExclamationTriangleIcon,
PencilAltIcon,
RocketIcon,
} from '@patternfly/react-icons';
import LaunchButton from '@components/LaunchButton';
import Sparkline from '@components/Sparkline';
import { toTitleCase } from '@util/strings';
import styled from 'styled-components';
const DataListAction = styled(_DataListAction)`
align-items: center;
display: grid;
grid-gap: 16px;
grid-template-columns: repeat(2, 40px);
`;
function ProjectJobTemplateListItem({
i18n,
template,
isSelected,
onSelect,
detailUrl,
}) {
const labelId = `check-action-${template.id}`;
const canLaunch = template.summary_fields.user_capabilities.start;
const missingResourceIcon =
template.type === 'job_template' &&
(!template.summary_fields.project ||
(!template.summary_fields.inventory &&
!template.ask_inventory_on_launch));
return (
<DataListItem aria-labelledby={labelId} id={`${template.id}`}>
<DataListItemRow>
<DataListCheck
id={`select-jobTemplate-${template.id}`}
checked={isSelected}
onChange={onSelect}
aria-labelledby={labelId}
/>
<DataListItemCells
dataListCells={[
<DataListCell key="divider">
<span>
<Link to={`${detailUrl}`}>
<b>{template.name}</b>
</Link>
</span>
{missingResourceIcon && (
<span>
<Tooltip
content={i18n._(
t`Resources are missing from this template.`
)}
position="right"
>
<ExclamationTriangleIcon css="color: #c9190b; margin-left: 20px;" />
</Tooltip>
</span>
)}
</DataListCell>,
<DataListCell key="type">
{toTitleCase(template.type)}
</DataListCell>,
<DataListCell key="sparkline">
<Sparkline jobs={template.summary_fields.recent_jobs} />
</DataListCell>,
]}
/>
<DataListAction
aria-label="actions"
aria-labelledby={labelId}
id={labelId}
>
{canLaunch && template.type === 'job_template' && (
<Tooltip content={i18n._(t`Launch Template`)} position="top">
<LaunchButton resource={template}>
{({ handleLaunch }) => (
<Button
css="grid-column: 1"
variant="plain"
onClick={handleLaunch}
>
<RocketIcon />
</Button>
)}
</LaunchButton>
</Tooltip>
)}
{template.summary_fields.user_capabilities.edit && (
<Tooltip content={i18n._(t`Edit Template`)} position="top">
<Button
css="grid-column: 2"
variant="plain"
component={Link}
to={`/templates/${template.type}/${template.id}/edit`}
>
<PencilAltIcon />
</Button>
</Tooltip>
)}
</DataListAction>
</DataListItemRow>
</DataListItem>
);
}
export { ProjectJobTemplateListItem as _ProjectJobTemplateListItem };
export default withI18n()(ProjectJobTemplateListItem);

View File

@ -0,0 +1,189 @@
import React from 'react';
import { mountWithContexts } from '@testUtils/enzymeHelpers';
import { createMemoryHistory } from 'history';
import ProjectJobTemplatesListItem from './ProjectJobTemplatesListItem';
describe('<ProjectJobTemplatesListItem />', () => {
test('launch button shown to users with start capabilities', () => {
const wrapper = mountWithContexts(
<ProjectJobTemplatesListItem
isSelected={false}
template={{
id: 1,
name: 'Template 1',
url: '/templates/job_template/1',
type: 'job_template',
summary_fields: {
user_capabilities: {
start: true,
},
},
}}
/>
);
expect(wrapper.find('LaunchButton').exists()).toBeTruthy();
});
test('launch button hidden from users without start capabilities', () => {
const wrapper = mountWithContexts(
<ProjectJobTemplatesListItem
isSelected={false}
template={{
id: 1,
name: 'Template 1',
url: '/templates/job_template/1',
type: 'job_template',
summary_fields: {
user_capabilities: {
start: false,
},
},
}}
/>
);
expect(wrapper.find('LaunchButton').exists()).toBeFalsy();
});
test('edit button shown to users with edit capabilities', () => {
const wrapper = mountWithContexts(
<ProjectJobTemplatesListItem
isSelected={false}
template={{
id: 1,
name: 'Template 1',
url: '/templates/job_template/1',
type: 'job_template',
summary_fields: {
user_capabilities: {
edit: true,
},
},
}}
/>
);
expect(wrapper.find('PencilAltIcon').exists()).toBeTruthy();
});
test('edit button hidden from users without edit capabilities', () => {
const wrapper = mountWithContexts(
<ProjectJobTemplatesListItem
isSelected={false}
template={{
id: 1,
name: 'Template 1',
url: '/templates/job_template/1',
type: 'job_template',
summary_fields: {
user_capabilities: {
edit: false,
},
},
}}
/>
);
expect(wrapper.find('PencilAltIcon').exists()).toBeFalsy();
});
test('missing resource icon is shown.', () => {
const wrapper = mountWithContexts(
<ProjectJobTemplatesListItem
isSelected={false}
template={{
id: 1,
name: 'Template 1',
url: '/templates/job_template/1',
type: 'job_template',
summary_fields: {
user_capabilities: {
edit: false,
},
},
}}
/>
);
expect(wrapper.find('ExclamationTriangleIcon').exists()).toBe(true);
});
test('missing resource icon is not shown when there is a project and an inventory.', () => {
const wrapper = mountWithContexts(
<ProjectJobTemplatesListItem
isSelected={false}
template={{
id: 1,
name: 'Template 1',
url: '/templates/job_template/1',
type: 'job_template',
summary_fields: {
user_capabilities: {
edit: false,
},
project: { name: 'Foo', id: 2 },
inventory: { name: 'Bar', id: 2 },
},
}}
/>
);
expect(wrapper.find('ExclamationTriangleIcon').exists()).toBe(false);
});
test('missing resource icon is not shown when inventory is prompt_on_launch, and a project', () => {
const wrapper = mountWithContexts(
<ProjectJobTemplatesListItem
isSelected={false}
template={{
id: 1,
name: 'Template 1',
url: '/templates/job_template/1',
type: 'job_template',
ask_inventory_on_launch: true,
summary_fields: {
user_capabilities: {
edit: false,
},
project: { name: 'Foo', id: 2 },
},
}}
/>
);
expect(wrapper.find('ExclamationTriangleIcon').exists()).toBe(false);
});
test('missing resource icon is not shown type is workflow_job_template', () => {
const wrapper = mountWithContexts(
<ProjectJobTemplatesListItem
isSelected={false}
template={{
id: 1,
name: 'Template 1',
url: '/templates/job_template/1',
type: 'workflow_job_template',
summary_fields: {
user_capabilities: {
edit: false,
},
},
}}
/>
);
expect(wrapper.find('ExclamationTriangleIcon').exists()).toBe(false);
});
test('clicking on template from project templates list navigates properly', () => {
const history = createMemoryHistory({
initialEntries: ['/projects/1/job_templates'],
});
const wrapper = mountWithContexts(
<ProjectJobTemplatesListItem
isSelected={false}
detailUrl="/templates/job_template/2/details"
template={{
id: 2,
name: 'Template 2',
summary_fields: {
user_capabilities: {
edit: false,
},
},
}}
/>,
{ context: { router: { history } } }
);
wrapper.find('Link').simulate('click', { button: 0 });
expect(history.location.pathname).toEqual(
'/templates/job_template/2/details'
);
});
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { Link, useLocation } from 'react-router-dom';
import { Link } from 'react-router-dom';
import {
Button,
DataListAction as _DataListAction,
@ -40,12 +40,6 @@ function TemplateListItem({ i18n, template, isSelected, onSelect, detailUrl }) {
(!template.summary_fields.inventory &&
!template.ask_inventory_on_launch));
// const location = useLocation();
// if (location.pathname.startsWith('/projects')) {
// detailUrl = `/templates/job_template/${template.id}/details`;
// }
return (
<DataListItem aria-labelledby={labelId} id={`${template.id}`}>
<DataListItemRow>

View File

@ -1,5 +1,4 @@
import React from 'react';
import { Route } from 'react-router-dom';
import { mountWithContexts } from '@testUtils/enzymeHelpers';
import { createMemoryHistory } from 'history';
@ -167,83 +166,24 @@ describe('<TemplateListItem />', () => {
initialEntries: ['/templates'],
});
const wrapper = mountWithContexts(
<Route
path="/templates"
component={() => (
<TemplateListItem
isSelected={false}
detailUrl="/templates/job_template/1/details"
template={{
id: 1,
name: 'Template 1',
summary_fields: {
user_capabilities: {
edit: false,
},
},
}}
/>
)}
/>,
{
context: {
router: {
history,
route: {
location: history.location,
match: {
params: { id: 1 },
},
<TemplateListItem
isSelected={false}
detailUrl="/templates/job_template/1/details"
template={{
id: 1,
name: 'Template 1',
summary_fields: {
user_capabilities: {
edit: false,
},
},
},
}
}}
/>,
{ context: { router: { history } } }
);
wrapper.find('Link').simulate('click', { button: 0 });
expect(history.location.pathname).toEqual(
'/templates/job_template/1/details'
);
});
test('clicking on template from project templates list navigates properly', () => {
const history = createMemoryHistory({
initialEntries: ['/projects/1/job_templates'],
});
const wrapper = mountWithContexts(
<Route
path="/projects/1/job_templates"
component={() => (
<TemplateListItem
isSelected={false}
detailUrl=""
template={{
id: 2,
name: 'Template 2',
summary_fields: {
user_capabilities: {
edit: false,
},
},
}}
/>
)}
/>,
{
context: {
router: {
history,
route: {
location: history.location,
match: {
params: { id: 1 },
},
},
},
},
}
);
wrapper.find('Link').simulate('click', { button: 0 });
expect(history.location.pathname).toEqual(
'/templates/job_template/2/details'
);
});
});