mirror of
https://github.com/ansible/awx.git
synced 2026-01-19 13:41:28 -03:30
refactoring and updating tests
This commit is contained in:
parent
2daf202e52
commit
76a7a76e81
79
__tests__/components/RoutedTabs.test.jsx
Normal file
79
__tests__/components/RoutedTabs.test.jsx
Normal file
@ -0,0 +1,79 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import { Router } from 'react-router-dom';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import { I18nProvider } from '@lingui/react';
|
||||
import RoutedTabs from '../../src/components/Tabs/RoutedTabs';
|
||||
import { DonateIcon } from '@patternfly/react-icons';
|
||||
|
||||
let wrapper;
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
const tabs = [
|
||||
{ name: 'Details', link: 'organizations/19/details', id: 0 },
|
||||
{ name: 'Access', link: 'organizations/19/access', id: 1 },
|
||||
{ name: 'Teams', link: 'organizations/19/teams', id: 2 }
|
||||
];
|
||||
|
||||
const history = createMemoryHistory({
|
||||
history: {
|
||||
location: {
|
||||
pathname: '/organizations/19/details'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
describe('<RoutedTabs />', () => {
|
||||
test('RoutedTabs renders successfully', () => {
|
||||
wrapper = mount(
|
||||
<I18nProvider>
|
||||
<Router history={history}>
|
||||
<RoutedTabs
|
||||
tabsArray={tabs}
|
||||
/>
|
||||
</Router>
|
||||
</I18nProvider>
|
||||
).find('RoutedTabs');
|
||||
});
|
||||
|
||||
test('the correct tab is rendered', async () => {
|
||||
const currentTab = 'organizations/1/details';
|
||||
wrapper = mount(
|
||||
<I18nProvider>
|
||||
<Router history={history}>
|
||||
<RoutedTabs
|
||||
tabsArray={tabs}
|
||||
location={currentTab}
|
||||
/>
|
||||
</Router>
|
||||
</I18nProvider>
|
||||
).find('RoutedTabs');
|
||||
wrapper.find('button').at(2).simulate('click');
|
||||
wrapper.update();
|
||||
expect(history.location.pathname).toEqual('/organizations/19/access');
|
||||
});
|
||||
|
||||
test('Given a URL the correct tab is displayed', async (done) => {
|
||||
const currentTab = createMemoryHistory({
|
||||
initialEntries: ['/organizations/19/teams'],
|
||||
});
|
||||
wrapper = mount(
|
||||
<I18nProvider>
|
||||
<Router history={currentTab}>
|
||||
<RoutedTabs
|
||||
tabsArray={tabs}
|
||||
/>
|
||||
</Router>
|
||||
</I18nProvider>
|
||||
).find('RoutedTabs');
|
||||
setImmediate(() => {
|
||||
wrapper.find('Tabs').prop('onSelect')({}, 2);
|
||||
const selectedTab = wrapper.find('li').get(2).props.className;
|
||||
expect(selectedTab).toBe('pf-c-tabs__item pf-m-current');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -3,56 +3,19 @@ import { mount } from 'enzyme';
|
||||
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { I18nProvider } from '@lingui/react';
|
||||
import Organization, { _Organization } from '../../../../../src/pages/Organizations/screens/Organization/Organization';
|
||||
import Organization from '../../../../../src/pages/Organizations/screens/Organization/Organization';
|
||||
|
||||
describe('<OrganizationView />', () => {
|
||||
test('initially renders succesfully', () => {
|
||||
const spy = jest.spyOn(_Organization.prototype, 'checkLocation');
|
||||
mount(
|
||||
<I18nProvider>
|
||||
<MemoryRouter initialEntries={['/organizations/1']} initialIndex={0}>
|
||||
<_Organization
|
||||
<Organization
|
||||
match={{ path: '/organizations/:id', url: '/organizations/1' }}
|
||||
location={{ search: '', pathname: '/organizations/1' }}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</I18nProvider>
|
||||
);
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('handleTabSelect renders the correct tab', async () => {
|
||||
const currentTab = 'organizations/19/access';
|
||||
const spy = jest.spyOn(_Organization.prototype, 'handleTabSelect');
|
||||
const wrapper = mount(
|
||||
<I18nProvider>
|
||||
<MemoryRouter initialEntries={['/organizations/1']} initialIndex={0}>
|
||||
<Organization location={currentTab} />
|
||||
</MemoryRouter>
|
||||
</I18nProvider>
|
||||
).find('Organization');
|
||||
wrapper.find('button').at(2).simulate('click');
|
||||
setImmediate(async () => {
|
||||
wrapper.setState({ activeTabKey: 1 });
|
||||
});
|
||||
wrapper.update();
|
||||
expect(spy).toBeCalled();
|
||||
expect(wrapper.state('activeTabKey')).toBe(1);
|
||||
});
|
||||
|
||||
test('checkLocation renders proper state when new tab is selected', async () => {
|
||||
const currentTab = 'organizations/19/access';
|
||||
const wrapper = mount(
|
||||
<I18nProvider>
|
||||
<MemoryRouter initialEntries={['/organizations/1']} initialIndex={0}>
|
||||
<Organization location={currentTab} />
|
||||
</MemoryRouter>
|
||||
</I18nProvider>
|
||||
).find('Organization');
|
||||
setImmediate(async () => {
|
||||
wrapper.setState({ activeTabKey: 1 });
|
||||
});
|
||||
wrapper.find('button').at(3).simulate('click');
|
||||
expect(wrapper.state('activeTabKey')).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
55
src/components/Tabs/RoutedTabs.jsx
Normal file
55
src/components/Tabs/RoutedTabs.jsx
Normal file
@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
withRouter
|
||||
} from 'react-router-dom';
|
||||
|
||||
import {
|
||||
Tab,
|
||||
Tabs
|
||||
} from '@patternfly/react-core';
|
||||
|
||||
class RoutedTabs extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
this.handleTabSelect = this.handleTabSelect.bind(this);
|
||||
}
|
||||
|
||||
getActiveTabId () {
|
||||
const { history, tabsArray } = this.props;
|
||||
const matchTab = tabsArray.find(selectedTab => selectedTab.link === history.location.pathname);
|
||||
return matchTab ? matchTab.id : 0;
|
||||
}
|
||||
|
||||
handleTabSelect (event, eventKey) {
|
||||
const { history, tabsArray } = this.props;
|
||||
|
||||
const tab = tabsArray.find(tabElement => tabElement.id === eventKey);
|
||||
history.push(tab.link);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { tabsArray } = this.props;
|
||||
return (
|
||||
<Tabs
|
||||
activeKey={this.getActiveTabId()}
|
||||
onSelect={this.handleTabSelect}
|
||||
>
|
||||
{tabsArray.map(tabElement => (
|
||||
<Tab
|
||||
className={`${tabElement.name}`}
|
||||
aria-label={`${tabElement.name}`}
|
||||
eventKey={tabElement.id}
|
||||
key={tabElement.id}
|
||||
link={tabElement.link}
|
||||
title={tabElement.name}
|
||||
/>
|
||||
))}
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { RoutedTabs as _RoutedTabs };
|
||||
export default withRouter(RoutedTabs);
|
||||
@ -1,33 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import './tabs.scss';
|
||||
|
||||
const Tab = ({ children, link, replace }) => (
|
||||
<li className="pf-c-tabs__item">
|
||||
<NavLink
|
||||
to={link}
|
||||
replace={replace}
|
||||
className="pf-c-tabs__button"
|
||||
activeClassName="pf-m-current"
|
||||
>
|
||||
{children}
|
||||
</NavLink>
|
||||
</li>
|
||||
);
|
||||
|
||||
Tab.propTypes = {
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node
|
||||
]).isRequired,
|
||||
link: PropTypes.string,
|
||||
replace: PropTypes.bool,
|
||||
};
|
||||
|
||||
Tab.defaultProps = {
|
||||
link: null,
|
||||
replace: false,
|
||||
};
|
||||
|
||||
export default Tab;
|
||||
@ -1,53 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Button, Tooltip } from '@patternfly/react-core';
|
||||
import { TimesIcon } from '@patternfly/react-icons';
|
||||
import './tabs.scss';
|
||||
|
||||
const Tabs = ({ children, labelText, closeButton }) => (
|
||||
<div
|
||||
aria-label={labelText}
|
||||
className="pf-c-tabs"
|
||||
>
|
||||
<ul className="pf-c-tabs__list">
|
||||
{children}
|
||||
</ul>
|
||||
{closeButton
|
||||
&& (
|
||||
<Tooltip
|
||||
content={closeButton.text}
|
||||
position="top"
|
||||
>
|
||||
<Link to={closeButton.link}>
|
||||
<Button
|
||||
variant="plain"
|
||||
aria-label={closeButton.text}
|
||||
>
|
||||
<TimesIcon />
|
||||
</Button>
|
||||
</Link>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
|
||||
Tabs.propTypes = {
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node
|
||||
]).isRequired,
|
||||
labelText: PropTypes.string,
|
||||
closeButton: PropTypes.shape({
|
||||
text: PropTypes.string,
|
||||
link: PropTypes.string,
|
||||
}),
|
||||
};
|
||||
|
||||
Tabs.defaultProps = {
|
||||
labelText: null,
|
||||
closeButton: null,
|
||||
};
|
||||
|
||||
export default Tabs;
|
||||
@ -1,71 +0,0 @@
|
||||
.pf-c-card__header {
|
||||
--pf-c-card__header--PaddingBottom: 0;
|
||||
--pf-c-card__header--PaddingX: 0;
|
||||
--pf-c-card__header--PaddingRight: 0;
|
||||
--pf-c-card__header--PaddingLeft: 0;
|
||||
--pf-c-card__header--PaddingTop: 0;
|
||||
}
|
||||
|
||||
.pf-c-tabs {
|
||||
--pf-global--link--Color: #484848;
|
||||
--pf-global--link--Color--hover: #484848;
|
||||
--pf-global--link--TextDecoration--hover: none;
|
||||
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
&:before {
|
||||
border-bottom: 1px solid var(--pf-c-tabs__item--BorderColor);
|
||||
border-top: 1px solid var(--pf-c-tabs__item--BorderColor);
|
||||
bottom: 0;
|
||||
content: " ";
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.pf-c-tabs__button {
|
||||
--pf-c-tabs__button--PaddingLeft: 20px;
|
||||
--pf-c-tabs__button--PaddingRight: 20px;
|
||||
display: block;
|
||||
font-weight: 700;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-tabs__item:first-child .pf-c-tabs__button:before {
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
.pf-c-tabs__item:not(.pf-m-current):hover
|
||||
.pf-c-tabs__button::after {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.pf-c-tabs__item:hover
|
||||
.pf-c-tabs__button:not(.pf-m-current)::after {
|
||||
border-bottom: 3px solid var(--pf-global--Color--dark-200);
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.pf-c-tabs__button.pf-m-current {
|
||||
color: var(--pf-c-tabs__item--m-current--Color);
|
||||
}
|
||||
|
||||
.pf-c-tabs__button.pf-m-current::after {
|
||||
content: '';
|
||||
border-bottom: 3px solid var(--pf-c-tabs__item--m-current--Color);
|
||||
border-top: none;
|
||||
margin-left: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,8 +12,6 @@ import {
|
||||
Card,
|
||||
CardHeader,
|
||||
PageSection,
|
||||
Tab,
|
||||
Tabs
|
||||
} from '@patternfly/react-core';
|
||||
import {
|
||||
TimesIcon
|
||||
@ -25,6 +23,7 @@ import OrganizationDetail from './OrganizationDetail';
|
||||
import OrganizationEdit from './OrganizationEdit';
|
||||
import OrganizationNotifications from './OrganizationNotifications';
|
||||
import OrganizationTeams from './OrganizationTeams';
|
||||
import RoutedTabs from '../../../../components/Tabs/RoutedTabs';
|
||||
|
||||
class Organization extends Component {
|
||||
constructor (props) {
|
||||
@ -34,17 +33,9 @@ class Organization extends Component {
|
||||
organization: null,
|
||||
error: false,
|
||||
loading: true,
|
||||
tabElements: [
|
||||
{ name: i18nMark('Details'), link: `${props.match.url}/details`, id: 0 },
|
||||
{ name: i18nMark('Access'), link: `${props.match.url}/access`, id: 1 },
|
||||
{ name: i18nMark('Teams'), link: `${props.match.url}/teams`, id: 2 },
|
||||
{ name: i18nMark('Notifications'), link: `${props.match.url}/notifications`, id: 3 },
|
||||
],
|
||||
};
|
||||
|
||||
this.fetchOrganization = this.fetchOrganization.bind(this);
|
||||
this.handleTabSelect = this.handleTabSelect.bind(this);
|
||||
this.checkLocation = this.checkLocation.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
@ -75,28 +66,9 @@ class Organization extends Component {
|
||||
this.setState({ error: true });
|
||||
} finally {
|
||||
this.setState({ loading: false });
|
||||
this.checkLocation();
|
||||
}
|
||||
}
|
||||
|
||||
checkLocation () {
|
||||
const { location } = this.props;
|
||||
const { tabElements } = this.state;
|
||||
const activeTab = tabElements.filter(tabElement => tabElement.link === location.pathname);
|
||||
this.setState({ activeTabKey: activeTab[0].id });
|
||||
}
|
||||
|
||||
handleTabSelect (event, eventKey) {
|
||||
const { history } = this.props;
|
||||
const { tabElements } = this.state;
|
||||
|
||||
const tab = tabElements.find(tabElement => tabElement.id === eventKey);
|
||||
history.push(tab.link);
|
||||
const activeTab = tabElements.filter(selectedTab => selectedTab.link === tab.link)
|
||||
.map(selectedTab => selectedTab.id);
|
||||
this.setState({ activeTabKey: activeTab[0] });
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
location,
|
||||
@ -104,36 +76,25 @@ class Organization extends Component {
|
||||
history
|
||||
} = this.props;
|
||||
const {
|
||||
activeTabKey,
|
||||
organization,
|
||||
error,
|
||||
loading,
|
||||
tabElements
|
||||
} = this.state;
|
||||
|
||||
let cardHeader = (
|
||||
<CardHeader>
|
||||
<I18n>
|
||||
{({ i18n }) => (
|
||||
<>
|
||||
<Tabs
|
||||
<React.Fragment>
|
||||
<RoutedTabs
|
||||
labeltext={i18n._(t`Organization detail tabs`)}
|
||||
activeKey={activeTabKey}
|
||||
onSelect={(event, eventKey) => {
|
||||
this.handleTabSelect(event, eventKey);
|
||||
}}
|
||||
>
|
||||
{tabElements.map(tabElement => (
|
||||
<Tab
|
||||
className={`${tabElement.name}`}
|
||||
aria-label={`${tabElement.name}`}
|
||||
eventKey={tabElement.id}
|
||||
key={tabElement.id}
|
||||
link={tabElement.link}
|
||||
title={tabElement.name}
|
||||
/>
|
||||
))}
|
||||
</Tabs>
|
||||
tabsArray={[
|
||||
{ name: i18nMark('Details'), link: `${match.url}/details`, id: 0 },
|
||||
{ name: i18nMark('Access'), link: `${match.url}/access`, id: 1 },
|
||||
{ name: i18nMark('Teams'), link: `${match.url}/teams`, id: 2 },
|
||||
{ name: i18nMark('Notifications'), link: `${match.url}/notifications`, id: 3 },
|
||||
]}
|
||||
/>
|
||||
<Link
|
||||
aria-label="Close"
|
||||
title="Close"
|
||||
@ -141,7 +102,7 @@ class Organization extends Component {
|
||||
>
|
||||
<TimesIcon className="OrgsTab-closeButton" />
|
||||
</Link>
|
||||
</>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</I18n>
|
||||
</CardHeader>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user