Reorganize file locations/directory structure (#270)

Reorganize file locations
This commit is contained in:
Michael Abashian
2019-06-19 11:41:14 -04:00
committed by GitHub
parent e3cb8d0447
commit ee56e9ccfb
229 changed files with 478 additions and 317 deletions

View File

@@ -1,5 +0,0 @@
{
"rules": {
"react/jsx-pascal-case": 0
}
}

View File

@@ -1,118 +0,0 @@
import React from 'react';
import { mountWithContexts, waitForElement } from './enzymeHelpers';
import { asyncFlush } from '../jest.setup';
import App from '../src/App';
import { ConfigAPI, MeAPI, RootAPI } from '../src/api';
jest.mock('../src/api');
describe('<App />', () => {
const ansible_version = '111';
const custom_virtualenvs = [];
const version = '222';
beforeEach(() => {
ConfigAPI.read = () => Promise.resolve({
data: {
ansible_version,
custom_virtualenvs,
version
}
});
MeAPI.read = () => Promise.resolve({ data: { results: [{}] } });
});
afterEach(() => {
jest.clearAllMocks();
});
test('expected content is rendered', () => {
const appWrapper = mountWithContexts(
<App
routeGroups={[
{
groupTitle: 'Group One',
groupId: 'group_one',
routes: [
{ title: 'Foo', path: '/foo' },
{ title: 'Bar', path: '/bar' },
],
},
{
groupTitle: 'Group Two',
groupId: 'group_two',
routes: [
{ title: 'Fiz', path: '/fiz' },
]
}
]}
render={({ routeGroups }) => (
routeGroups.map(({ groupId }) => (<div key={groupId} id={groupId} />))
)}
/>,
);
// page components
expect(appWrapper.length).toBe(1);
expect(appWrapper.find('PageHeader').length).toBe(1);
expect(appWrapper.find('PageSidebar').length).toBe(1);
// sidebar groups and route links
expect(appWrapper.find('NavExpandableGroup').length).toBe(2);
expect(appWrapper.find('a[href="/#/foo"]').length).toBe(1);
expect(appWrapper.find('a[href="/#/bar"]').length).toBe(1);
expect(appWrapper.find('a[href="/#/fiz"]').length).toBe(1);
// inline render
expect(appWrapper.find('#group_one').length).toBe(1);
expect(appWrapper.find('#group_two').length).toBe(1);
});
test('opening the about modal renders prefetched config data', async (done) => {
const wrapper = mountWithContexts(<App />);
wrapper.update();
// open about modal
const aboutDropdown = 'Dropdown QuestionCircleIcon';
const aboutButton = 'DropdownItem li button';
const aboutModalContent = 'AboutModalBoxContent';
const aboutModalClose = 'button[aria-label="Close Dialog"]';
await waitForElement(wrapper, aboutDropdown);
wrapper.find(aboutDropdown).simulate('click');
const button = await waitForElement(wrapper, aboutButton, el => !el.props().disabled);
button.simulate('click');
// check about modal content
const content = await waitForElement(wrapper, aboutModalContent);
expect(content.find('dd').text()).toContain(ansible_version);
expect(content.find('pre').text()).toContain(`< AWX ${version} >`);
// close about modal
wrapper.find(aboutModalClose).simulate('click');
expect(wrapper.find(aboutModalContent)).toHaveLength(0);
done();
});
test('handleNavToggle sets state.isNavOpen to opposite', () => {
const appWrapper = mountWithContexts(<App />).find('App');
const { handleNavToggle } = appWrapper.instance();
[true, false, true, false, true].forEach(expected => {
expect(appWrapper.state().isNavOpen).toBe(expected);
handleNavToggle();
});
});
test('onLogout makes expected call to api client', async (done) => {
const appWrapper = mountWithContexts(<App />).find('App');
appWrapper.instance().handleLogout();
await asyncFlush();
expect(RootAPI.logout).toHaveBeenCalledTimes(1);
done();
});
});

View File

@@ -1,10 +0,0 @@
import { getLanguage } from '../src/RootProvider';
describe('RootProvider.jsx', () => {
test('getLanguage returns the expected language code', () => {
expect(getLanguage({ languages: ['es-US'] })).toEqual('es');
expect(getLanguage({ languages: ['es-US'], language: 'fr-FR', userLanguage: 'en-US' })).toEqual('es');
expect(getLanguage({ language: 'fr-FR', userLanguage: 'en-US' })).toEqual('fr');
expect(getLanguage({ userLanguage: 'en-US' })).toEqual('en');
});
});

View File

@@ -1,58 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`mountWithContexts injected ConfigProvider should mount and render with custom Config value 1`] = `
<Foo>
<div>
Fizz
1.1
</div>
</Foo>
`;
exports[`mountWithContexts injected ConfigProvider should mount and render with default values 1`] = `
<Foo>
<div />
</Foo>
`;
exports[`mountWithContexts injected I18nProvider should mount and render 1`] = `
<div>
<span>
Text content
</span>
</div>
`;
exports[`mountWithContexts injected I18nProvider should mount and render deeply nested consumer 1`] = `
<Parent>
<WithI18n>
<I18n
update={true}
withHash={true}
>
<Component
i18n={"/i18n/"}
>
<div>
Text content
</div>
</Component>
</I18n>
</WithI18n>
</Parent>
`;
exports[`mountWithContexts injected Router should mount and render 1`] = `
<div>
<Link
replace={false}
to="/"
>
<a
onClick={[Function]}
>
home
</a>
</Link>
</div>
`;

View File

@@ -1,97 +0,0 @@
import Base from '../../src/api/Base';
describe('Base', () => {
const createPromise = () => Promise.resolve();
const mockBaseURL = '/api/v2/organizations/';
const mockHttp = ({
delete: jest.fn(createPromise),
get: jest.fn(createPromise),
options: jest.fn(createPromise),
patch: jest.fn(createPromise),
post: jest.fn(createPromise),
put: jest.fn(createPromise)
});
const BaseAPI = new Base(mockHttp, mockBaseURL);
afterEach(() => {
jest.clearAllMocks();
});
test('create calls http method with expected data', async (done) => {
const data = { name: 'test ' };
await BaseAPI.create(data);
expect(mockHttp.post).toHaveBeenCalledTimes(1);
expect(mockHttp.post.mock.calls[0][1]).toEqual(data);
done();
});
test('destroy calls http method with expected data', async (done) => {
const resourceId = 1;
await BaseAPI.destroy(resourceId);
expect(mockHttp.delete).toHaveBeenCalledTimes(1);
expect(mockHttp.delete.mock.calls[0][0]).toEqual(`${mockBaseURL}${resourceId}/`);
done();
});
test('read calls http method with expected data', async (done) => {
const defaultParams = {};
const testParams = { foo: 'bar' };
await BaseAPI.read(testParams);
await BaseAPI.read();
expect(mockHttp.get).toHaveBeenCalledTimes(2);
expect(mockHttp.get.mock.calls[0][1]).toEqual({ params: testParams });
expect(mockHttp.get.mock.calls[1][1]).toEqual({ params: defaultParams });
done();
});
test('readDetail calls http method with expected data', async (done) => {
const resourceId = 1;
await BaseAPI.readDetail(resourceId);
expect(mockHttp.get).toHaveBeenCalledTimes(1);
expect(mockHttp.get.mock.calls[0][0]).toEqual(`${mockBaseURL}${resourceId}/`);
done();
});
test('readOptions calls http method with expected data', async (done) => {
await BaseAPI.readOptions();
expect(mockHttp.options).toHaveBeenCalledTimes(1);
expect(mockHttp.options.mock.calls[0][0]).toEqual(`${mockBaseURL}`);
done();
});
test('replace calls http method with expected data', async (done) => {
const resourceId = 1;
const data = { name: 'test ' };
await BaseAPI.replace(resourceId, data);
expect(mockHttp.put).toHaveBeenCalledTimes(1);
expect(mockHttp.put.mock.calls[0][0]).toEqual(`${mockBaseURL}${resourceId}/`);
expect(mockHttp.put.mock.calls[0][1]).toEqual(data);
done();
});
test('update calls http method with expected data', async (done) => {
const resourceId = 1;
const data = { name: 'test ' };
await BaseAPI.update(resourceId, data);
expect(mockHttp.patch).toHaveBeenCalledTimes(1);
expect(mockHttp.patch.mock.calls[0][0]).toEqual(`${mockBaseURL}${resourceId}/`);
expect(mockHttp.patch.mock.calls[0][1]).toEqual(data);
done();
});
});

View File

@@ -1,39 +0,0 @@
import Organizations from '../../src/api/models/Organizations';
import { describeNotificationMixin } from './reusable';
describe('OrganizationsAPI', () => {
const orgId = 1;
const searchParams = { foo: 'bar' };
const createPromise = () => Promise.resolve();
const mockHttp = ({ get: jest.fn(createPromise) });
const OrganizationsAPI = new Organizations(mockHttp);
afterEach(() => {
jest.clearAllMocks();
});
test('read access list calls get with expected params', async (done) => {
await OrganizationsAPI.readAccessList(orgId);
await OrganizationsAPI.readAccessList(orgId, searchParams);
expect(mockHttp.get).toHaveBeenCalledTimes(2);
expect(mockHttp.get.mock.calls[0]).toContainEqual(`/api/v2/organizations/${orgId}/access_list/`, { params: {} });
expect(mockHttp.get.mock.calls[1]).toContainEqual(`/api/v2/organizations/${orgId}/access_list/`, { params: searchParams });
done();
});
test('read teams calls get with expected params', async (done) => {
await OrganizationsAPI.readTeams(orgId);
await OrganizationsAPI.readTeams(orgId, searchParams);
expect(mockHttp.get).toHaveBeenCalledTimes(2);
expect(mockHttp.get.mock.calls[0]).toContainEqual(`/api/v2/organizations/${orgId}/teams/`, { params: {} });
expect(mockHttp.get.mock.calls[1]).toContainEqual(`/api/v2/organizations/${orgId}/teams/`, { params: searchParams });
done();
});
});
describeNotificationMixin(Organizations, 'Organizations[NotificationsMixin]');

View File

@@ -1,34 +0,0 @@
// eslint-disable-next-line import/prefer-default-export
export function describeNotificationMixin (Model, name) {
describe(name, () => {
const mockHttp = ({ post: jest.fn(() => Promise.resolve()) });
const ModelAPI = new Model(mockHttp);
afterEach(() => {
jest.clearAllMocks();
});
const parameters = [
['success', true],
['success', false],
['error', true],
['error', false],
];
parameters.forEach(([type, state]) => {
const label = `[notificationType=${type}, associationState=${state}]`;
const testName = `updateNotificationTemplateAssociation ${label} makes expected http calls`;
test(testName, async (done) => {
await ModelAPI.updateNotificationTemplateAssociation(1, 21, type, state);
const expectedPath = `${ModelAPI.baseUrl}1/notification_templates_${type}/`;
expect(mockHttp.post).toHaveBeenCalledTimes(1);
const expectedParams = state ? { id: 21 } : { id: 21, disassociate: true };
expect(mockHttp.post.mock.calls.pop()).toEqual([expectedPath, expectedParams]);
done();
});
});
});
}

View File

@@ -1,45 +0,0 @@
import Root from '../../src/api/models/Root';
describe('RootAPI', () => {
const createPromise = () => Promise.resolve();
const mockHttp = ({ get: jest.fn(createPromise), post: jest.fn(createPromise) });
const RootAPI = new Root(mockHttp);
afterEach(() => {
jest.clearAllMocks();
});
test('login calls get and post with expected content headers', async (done) => {
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
await RootAPI.login('username', 'password');
expect(mockHttp.get).toHaveBeenCalledTimes(1);
expect(mockHttp.get.mock.calls[0]).toContainEqual({ headers });
expect(mockHttp.post).toHaveBeenCalledTimes(1);
expect(mockHttp.post.mock.calls[0]).toContainEqual({ headers });
done();
});
test('login sends expected data', async (done) => {
await RootAPI.login('foo', 'bar');
await RootAPI.login('foo', 'bar', 'baz');
expect(mockHttp.post).toHaveBeenCalledTimes(2);
expect(mockHttp.post.mock.calls[0]).toContainEqual('username=foo&password=bar&next=%2Fapi%2Fv2%2Fconfig%2F');
expect(mockHttp.post.mock.calls[1]).toContainEqual('username=foo&password=bar&next=baz');
done();
});
test('logout calls expected http method', async (done) => {
await RootAPI.logout();
expect(mockHttp.get).toHaveBeenCalledTimes(1);
done();
});
});

View File

@@ -1,35 +0,0 @@
import Teams from '../../src/api/models/Teams';
describe('TeamsAPI', () => {
const teamId = 1;
const roleId = 7;
const createPromise = () => Promise.resolve();
const mockHttp = ({ post: jest.fn(createPromise) });
const TeamsAPI = new Teams(mockHttp);
afterEach(() => {
jest.clearAllMocks();
});
test('associate role calls post with expected params', async (done) => {
await TeamsAPI.associateRole(teamId, roleId);
expect(mockHttp.post).toHaveBeenCalledTimes(1);
expect(mockHttp.post.mock.calls[0]).toContainEqual(`/api/v2/teams/${teamId}/roles/`, { id: roleId });
done();
});
test('read teams calls post with expected params', async (done) => {
await TeamsAPI.disassociateRole(teamId, roleId);
expect(mockHttp.post).toHaveBeenCalledTimes(1);
expect(mockHttp.post.mock.calls[0]).toContainEqual(`/api/v2/teams/${teamId}/roles/`, {
id: roleId,
disassociate: true
});
done();
});
});

View File

@@ -1,35 +0,0 @@
import Users from '../../src/api/models/Users';
describe('UsersAPI', () => {
const userId = 1;
const roleId = 7;
const createPromise = () => Promise.resolve();
const mockHttp = ({ post: jest.fn(createPromise) });
const UsersAPI = new Users(mockHttp);
afterEach(() => {
jest.clearAllMocks();
});
test('associate role calls post with expected params', async (done) => {
await UsersAPI.associateRole(userId, roleId);
expect(mockHttp.post).toHaveBeenCalledTimes(1);
expect(mockHttp.post.mock.calls[0]).toContainEqual(`/api/v2/users/${userId}/roles/`, { id: roleId });
done();
});
test('read users calls post with expected params', async (done) => {
await UsersAPI.disassociateRole(userId, roleId);
expect(mockHttp.post).toHaveBeenCalledTimes(1);
expect(mockHttp.post.mock.calls[0]).toContainEqual(`/api/v2/users/${userId}/roles/`, {
id: roleId,
disassociate: true
});
done();
});
});

View File

@@ -1,26 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import About from '../../src/components/About';
describe('<About />', () => {
let aboutWrapper;
let closeButton;
const onClose = jest.fn();
test('initially renders without crashing', () => {
aboutWrapper = mountWithContexts(
<About isOpen onClose={onClose} />
);
expect(aboutWrapper.length).toBe(1);
aboutWrapper.unmount();
});
test('close button calls onClose handler', () => {
aboutWrapper = mountWithContexts(
<About isOpen onClose={onClose} />
);
closeButton = aboutWrapper.find('AboutModalBoxCloseButton Button');
closeButton.simulate('click');
expect(onClose).toBeCalled();
aboutWrapper.unmount();
});
});

View File

@@ -1,223 +0,0 @@
import React from 'react';
import { shallow } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import AddResourceRole, { _AddResourceRole } from '../../src/components/AddRole/AddResourceRole';
import { TeamsAPI, UsersAPI } from '../../src/api';
jest.mock('../../src/api');
describe('<_AddResourceRole />', () => {
UsersAPI.read.mockResolvedValue({
data: {
count: 2,
results: [
{ id: 1, username: 'foo' },
{ id: 2, username: 'bar' }
]
}
});
const roles = {
admin_role: {
description: 'Can manage all aspects of the organization',
id: 1,
name: 'Admin'
},
execute_role: {
description: 'May run any executable resources in the organization',
id: 2,
name: 'Execute'
}
};
test('initially renders without crashing', () => {
shallow(
<_AddResourceRole
onClose={() => {}}
onSave={() => {}}
roles={roles}
i18n={{ _: val => val.toString() }}
/>
);
});
test('handleRoleCheckboxClick properly updates state', () => {
const wrapper = shallow(
<_AddResourceRole
onClose={() => {}}
onSave={() => {}}
roles={roles}
i18n={{ _: val => val.toString() }}
/>
);
wrapper.setState({
selectedRoleRows: [
{
description: 'Can manage all aspects of the organization',
name: 'Admin',
id: 1
}
]
});
wrapper.instance().handleRoleCheckboxClick({
description: 'Can manage all aspects of the organization',
name: 'Admin',
id: 1
});
expect(wrapper.state('selectedRoleRows')).toEqual([]);
wrapper.instance().handleRoleCheckboxClick({
description: 'Can manage all aspects of the organization',
name: 'Admin',
id: 1
});
expect(wrapper.state('selectedRoleRows')).toEqual([{
description: 'Can manage all aspects of the organization',
name: 'Admin',
id: 1
}]);
});
test('handleResourceCheckboxClick properly updates state', () => {
const wrapper = shallow(
<_AddResourceRole
onClose={() => {}}
onSave={() => {}}
roles={roles}
i18n={{ _: val => val.toString() }}
/>
);
wrapper.setState({
selectedResourceRows: [
{
id: 1,
username: 'foobar'
}
]
});
wrapper.instance().handleResourceCheckboxClick({
id: 1,
username: 'foobar'
});
expect(wrapper.state('selectedResourceRows')).toEqual([]);
wrapper.instance().handleResourceCheckboxClick({
id: 1,
username: 'foobar'
});
expect(wrapper.state('selectedResourceRows')).toEqual([{
id: 1,
username: 'foobar'
}]);
});
test('clicking user/team cards updates state', () => {
const spy = jest.spyOn(_AddResourceRole.prototype, 'handleResourceSelect');
const wrapper = mountWithContexts(
<AddResourceRole
onClose={() => {}}
onSave={() => {}}
roles={roles}
/>, { context: { network: { handleHttpError: () => {} } } }
).find('AddResourceRole');
const selectableCardWrapper = wrapper.find('SelectableCard');
expect(selectableCardWrapper.length).toBe(2);
selectableCardWrapper.first().simulate('click');
expect(spy).toHaveBeenCalledWith('users');
expect(wrapper.state('selectedResource')).toBe('users');
selectableCardWrapper.at(1).simulate('click');
expect(spy).toHaveBeenCalledWith('teams');
expect(wrapper.state('selectedResource')).toBe('teams');
});
test('handleResourceSelect clears out selected lists and sets selectedResource', () => {
const wrapper = shallow(
<_AddResourceRole
onClose={() => {}}
onSave={() => {}}
roles={roles}
i18n={{ _: val => val.toString() }}
/>
);
wrapper.setState({
selectedResource: 'teams',
selectedResourceRows: [
{
id: 1,
username: 'foobar'
}
],
selectedRoleRows: [
{
description: 'Can manage all aspects of the organization',
id: 1,
name: 'Admin'
}
]
});
wrapper.instance().handleResourceSelect('users');
expect(wrapper.state()).toEqual({
selectedResource: 'users',
selectedResourceRows: [],
selectedRoleRows: [],
currentStepId: 1,
});
wrapper.instance().handleResourceSelect('teams');
expect(wrapper.state()).toEqual({
selectedResource: 'teams',
selectedResourceRows: [],
selectedRoleRows: [],
currentStepId: 1
});
});
test('handleWizardSave makes correct api calls, calls onSave when done', async () => {
const handleSave = jest.fn();
const wrapper = mountWithContexts(
<AddResourceRole
onClose={() => {}}
onSave={handleSave}
roles={roles}
/>, { context: { network: { handleHttpError: () => {} } } }
).find('AddResourceRole');
wrapper.setState({
selectedResource: 'users',
selectedResourceRows: [
{
id: 1,
username: 'foobar'
}
],
selectedRoleRows: [
{
description: 'Can manage all aspects of the organization',
id: 1,
name: 'Admin'
},
{
description: 'May run any executable resources in the organization',
id: 2,
name: 'Execute'
}
]
});
await wrapper.instance().handleWizardSave();
expect(UsersAPI.associateRole).toHaveBeenCalledTimes(2);
expect(handleSave).toHaveBeenCalled();
wrapper.setState({
selectedResource: 'teams',
selectedResourceRows: [
{
id: 1,
name: 'foobar'
}
],
selectedRoleRows: [
{
description: 'Can manage all aspects of the organization',
id: 1,
name: 'Admin'
},
{
description: 'May run any executable resources in the organization',
id: 2,
name: 'Execute'
}
]
});
await wrapper.instance().handleWizardSave();
expect(TeamsAPI.associateRole).toHaveBeenCalledTimes(2);
expect(handleSave).toHaveBeenCalled();
});
});

View File

@@ -1,50 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import AnsibleSelect from '../../src/components/AnsibleSelect';
import { _AnsibleSelect } from '../../src/components/AnsibleSelect/AnsibleSelect';
const label = 'test select';
const mockData = ['/venv/baz/', '/venv/ansible/'];
describe('<AnsibleSelect />', () => {
test('initially renders succesfully', async () => {
mountWithContexts(
<AnsibleSelect
value="foo"
name="bar"
onChange={() => { }}
label={label}
data={mockData}
/>
);
});
test('calls "onSelectChange" on dropdown select change', () => {
const spy = jest.spyOn(_AnsibleSelect.prototype, 'onSelectChange');
const wrapper = mountWithContexts(
<AnsibleSelect
value="foo"
name="bar"
onChange={() => { }}
label={label}
data={mockData}
/>
);
expect(spy).not.toHaveBeenCalled();
wrapper.find('select').simulate('change');
expect(spy).toHaveBeenCalled();
});
test('Returns correct select options if defaultSelected props is passed', () => {
const wrapper = mountWithContexts(
<AnsibleSelect
value="foo"
name="bar"
onChange={() => { }}
label={label}
data={mockData}
defaultSelected={mockData[1]}
/>
);
expect(wrapper.find('FormSelect')).toHaveLength(1);
});
});

View File

@@ -1,13 +0,0 @@
import React from 'react';
import { mount } from 'enzyme';
import Background from '../../src/components/Background';
describe('Background', () => {
test('renders the expected content', () => {
const wrapper = mount(<Background><div id="test" /></Background>);
expect(wrapper).toHaveLength(1);
expect(wrapper.find('BackgroundImage')).toHaveLength(1);
expect(wrapper.find('#test')).toHaveLength(1);
});
});

View File

@@ -1,24 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import BrandLogo from '../../src/components/BrandLogo';
let logoWrapper;
let brandLogoElem;
let svgElem;
const findChildren = () => {
brandLogoElem = logoWrapper.find('BrandLogo');
svgElem = logoWrapper.find('svg');
};
describe('<BrandLogo />', () => {
test('initially renders without crashing', () => {
logoWrapper = mountWithContexts(
<BrandLogo />
);
findChildren();
expect(logoWrapper.length).toBe(1);
expect(brandLogoElem.length).toBe(1);
expect(svgElem.length).toBe(1);
});
});

View File

@@ -1,68 +0,0 @@
import React from 'react';
import { mount } from 'enzyme';
import { MemoryRouter } from 'react-router-dom';
import Breadcrumbs from '../../src/components/Breadcrumbs/Breadcrumbs';
describe('<Breadcrumb />', () => {
let breadcrumbWrapper;
let breadcrumb;
let breadcrumbItem;
let breadcrumbHeading;
const config = {
'/foo': 'Foo',
'/foo/1': 'One',
'/foo/1/bar': 'Bar',
'/foo/1/bar/fiz': 'Fiz'
};
const findChildren = () => {
breadcrumb = breadcrumbWrapper.find('Breadcrumb');
breadcrumbItem = breadcrumbWrapper.find('BreadcrumbItem');
breadcrumbHeading = breadcrumbWrapper.find('BreadcrumbHeading');
};
test('initially renders succesfully', () => {
breadcrumbWrapper = mount(
<MemoryRouter initialEntries={['/foo/1/bar']} initialIndex={0}>
<Breadcrumbs
breadcrumbConfig={config}
/>
</MemoryRouter>
);
findChildren();
expect(breadcrumb).toHaveLength(1);
expect(breadcrumbItem).toHaveLength(2);
expect(breadcrumbHeading).toHaveLength(1);
expect(breadcrumbItem.first().text()).toBe('Foo');
expect(breadcrumbItem.last().text()).toBe('One');
expect(breadcrumbHeading.text()).toBe('Bar');
breadcrumbWrapper.unmount();
});
test('renders breadcrumb items defined in breadcrumbConfig', () => {
const routes = [
['/fo', 0],
['/foo', 0],
['/foo/1', 1],
['/foo/baz', 1],
['/foo/1/bar', 2],
['/foo/1/bar/fiz', 3],
];
routes.forEach(([location, crumbLength]) => {
breadcrumbWrapper = mount(
<MemoryRouter initialEntries={[location]}>
<Breadcrumbs
breadcrumbConfig={config}
/>
</MemoryRouter>
);
expect(breadcrumbWrapper.find('BreadcrumbItem')).toHaveLength(crumbLength);
breadcrumbWrapper.unmount();
});
});
});

View File

@@ -1,23 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import CardCloseButton from '../../src/components/CardCloseButton';
describe('<CardCloseButton>', () => {
test('should render close button', () => {
const wrapper = mountWithContexts(<CardCloseButton />);
const button = wrapper.find('Button');
expect(button).toHaveLength(1);
expect(button.prop('variant')).toBe('plain');
expect(button.prop('aria-label')).toBe('Close');
expect(wrapper.find('Link')).toHaveLength(0);
});
test('should render close link when `linkTo` prop provided', () => {
const wrapper = mountWithContexts(<CardCloseButton linkTo="/foo" />);
expect(wrapper.find('Button')).toHaveLength(0);
const link = wrapper.find('Link');
expect(link).toHaveLength(1);
expect(link.prop('to')).toEqual('/foo');
expect(link.prop('aria-label')).toEqual('Close');
});
});

View File

@@ -1,16 +0,0 @@
import React from 'react';
import { shallow } from 'enzyme';
import CheckboxCard from '../../src/components/AddRole/CheckboxCard';
describe('<CheckboxCard />', () => {
let wrapper;
test('initially renders without crashing', () => {
wrapper = shallow(
<CheckboxCard
name="Foobar"
itemId={5}
/>
);
expect(wrapper.length).toBe(1);
});
});

View File

@@ -1,69 +0,0 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
import { mountWithContexts } from '../../enzymeHelpers';
import { ChipGroup, Chip } from '../../../src/components/Chip';
describe('<ChipGroup />', () => {
test('should render all chips', () => {
const wrapper = mountWithContexts(
<ChipGroup>
<Chip>One</Chip>
<Chip>Two</Chip>
<Chip>Three</Chip>
<Chip>Four</Chip>
<Chip>Five</Chip>
<Chip>Six</Chip>
</ChipGroup>
);
expect(wrapper.find(Chip)).toHaveLength(6);
expect(wrapper.find('li')).toHaveLength(6);
});
test('should render show more toggle', () => {
const wrapper = mountWithContexts(
<ChipGroup showOverflowAfter={5}>
<Chip>One</Chip>
<Chip>Two</Chip>
<Chip>Three</Chip>
<Chip>Four</Chip>
<Chip>Five</Chip>
<Chip>Six</Chip>
<Chip>Seven</Chip>
</ChipGroup>
);
expect(wrapper.find(Chip)).toHaveLength(6);
const toggle = wrapper.find(Chip).at(5);
expect(toggle.prop('isOverflowChip')).toBe(true);
expect(toggle.text()).toEqual('2 more');
});
test('should render show less toggle', () => {
const wrapper = mountWithContexts(
<ChipGroup showOverflowAfter={5}>
<Chip>One</Chip>
<Chip>Two</Chip>
<Chip>Three</Chip>
<Chip>Four</Chip>
<Chip>Five</Chip>
<Chip>Six</Chip>
<Chip>Seven</Chip>
</ChipGroup>
);
expect(wrapper.find(Chip)).toHaveLength(6);
const toggle = wrapper.find(Chip).at(5);
expect(toggle.prop('isOverflowChip')).toBe(true);
act(() => {
toggle.prop('onClick')();
});
wrapper.update();
expect(wrapper.find(Chip)).toHaveLength(8);
expect(wrapper.find(Chip).at(7).text()).toEqual('Show Less');
act(() => {
const toggle2 = wrapper.find(Chip).at(7);
expect(toggle2.prop('isOverflowChip')).toBe(true);
toggle2.prop('onClick')();
});
wrapper.update();
expect(wrapper.find(Chip)).toHaveLength(6);
});
});

View File

@@ -1,199 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import DataListToolbar from '../../src/components/DataListToolbar';
describe('<DataListToolbar />', () => {
let toolbar;
afterEach(() => {
if (toolbar) {
toolbar.unmount();
toolbar = null;
}
});
test('it triggers the expected callbacks', () => {
const columns = [{ name: 'Name', key: 'name', isSortable: true }];
const search = 'button[aria-label="Search"]';
const searchTextInput = 'input[aria-label="Search text input"]';
const selectAll = 'input[aria-label="Select all"]';
const sort = 'button[aria-label="Sort"]';
const onSearch = jest.fn();
const onSort = jest.fn();
const onSelectAll = jest.fn();
toolbar = mountWithContexts(
<DataListToolbar
isAllSelected={false}
showExpandCollapse
sortedColumnKey="name"
sortOrder="ascending"
columns={columns}
onSearch={onSearch}
onSort={onSort}
onSelectAll={onSelectAll}
showSelectAll
/>
);
toolbar.find(sort).simulate('click');
toolbar.find(selectAll).simulate('change', { target: { checked: false } });
expect(onSelectAll).toHaveBeenCalledTimes(1);
expect(onSort).toHaveBeenCalledTimes(1);
expect(onSort).toBeCalledWith('name', 'descending');
expect(onSelectAll).toHaveBeenCalledTimes(1);
expect(onSelectAll.mock.calls[0][0]).toBe(false);
toolbar.find(searchTextInput).instance().value = 'test-321';
toolbar.find(searchTextInput).simulate('change');
toolbar.find(search).simulate('click');
expect(onSearch).toHaveBeenCalledTimes(1);
expect(onSearch).toBeCalledWith('test-321');
});
test('dropdown items sortable columns work', () => {
const sortDropdownToggleSelector = 'button[id="awx-sort"]';
const searchDropdownToggleSelector = 'button[id="awx-search"]';
const dropdownMenuItems = 'DropdownMenu > ul';
const multipleColumns = [
{ name: 'Foo', key: 'foo', isSortable: true },
{ name: 'Bar', key: 'bar', isSortable: true },
{ name: 'Bakery', key: 'bakery', isSortable: true },
{ name: 'Baz', key: 'baz' }
];
const onSort = jest.fn();
toolbar = mountWithContexts(
<DataListToolbar
sortedColumnKey="foo"
sortOrder="ascending"
columns={multipleColumns}
onSort={onSort}
/>
);
const sortDropdownToggle = toolbar.find(sortDropdownToggleSelector);
expect(sortDropdownToggle.length).toBe(1);
sortDropdownToggle.simulate('click');
toolbar.update();
const sortDropdownItems = toolbar.find(dropdownMenuItems).children();
expect(sortDropdownItems.length).toBe(2);
const mockedSortEvent = { target: { innerText: 'Bar' } };
sortDropdownItems.at(0).simulate('click', mockedSortEvent);
toolbar = mountWithContexts(
<DataListToolbar
sortedColumnKey="foo"
sortOrder="descending"
columns={multipleColumns}
onSort={onSort}
/>
);
toolbar.update();
const sortDropdownToggleDescending = toolbar.find(sortDropdownToggleSelector);
expect(sortDropdownToggleDescending.length).toBe(1);
sortDropdownToggleDescending.simulate('click');
toolbar.update();
const sortDropdownItemsDescending = toolbar.find(dropdownMenuItems).children();
expect(sortDropdownItemsDescending.length).toBe(2);
sortDropdownToggleDescending.simulate('click'); // toggle close the sort dropdown
const mockedSortEventDescending = { target: { innerText: 'Bar' } };
sortDropdownItems.at(0).simulate('click', mockedSortEventDescending);
toolbar.update();
const searchDropdownToggle = toolbar.find(searchDropdownToggleSelector);
expect(searchDropdownToggle.length).toBe(1);
searchDropdownToggle.simulate('click');
toolbar.update();
const searchDropdownItems = toolbar.find(dropdownMenuItems).children();
expect(searchDropdownItems.length).toBe(3);
const mockedSearchEvent = { target: { innerText: 'Bar' } };
searchDropdownItems.at(0).simulate('click', mockedSearchEvent);
});
test('it displays correct sort icon', () => {
const downNumericIconSelector = 'SortNumericDownIcon';
const upNumericIconSelector = 'SortNumericUpIcon';
const downAlphaIconSelector = 'SortAlphaDownIcon';
const upAlphaIconSelector = 'SortAlphaUpIcon';
const numericColumns = [{ name: 'ID', key: 'id', isSortable: true, isNumeric: true }];
const alphaColumns = [{ name: 'Name', key: 'name', isSortable: true, isNumeric: false }];
toolbar = mountWithContexts(
<DataListToolbar
sortedColumnKey="id"
sortOrder="descending"
columns={numericColumns}
/>
);
const downNumericIcon = toolbar.find(downNumericIconSelector);
expect(downNumericIcon.length).toBe(1);
toolbar = mountWithContexts(
<DataListToolbar
sortedColumnKey="id"
sortOrder="ascending"
columns={numericColumns}
/>
);
const upNumericIcon = toolbar.find(upNumericIconSelector);
expect(upNumericIcon.length).toBe(1);
toolbar = mountWithContexts(
<DataListToolbar
sortedColumnKey="name"
sortOrder="descending"
columns={alphaColumns}
/>
);
const downAlphaIcon = toolbar.find(downAlphaIconSelector);
expect(downAlphaIcon.length).toBe(1);
toolbar = mountWithContexts(
<DataListToolbar
sortedColumnKey="name"
sortOrder="ascending"
columns={alphaColumns}
/>
);
const upAlphaIcon = toolbar.find(upAlphaIconSelector);
expect(upAlphaIcon.length).toBe(1);
});
test('should render additionalControls', () => {
const columns = [{ name: 'Name', key: 'name', isSortable: true }];
const onSearch = jest.fn();
const onSort = jest.fn();
const onSelectAll = jest.fn();
toolbar = mountWithContexts(
<DataListToolbar
columns={columns}
onSearch={onSearch}
onSort={onSort}
onSelectAll={onSelectAll}
additionalControls={[<button key="1" id="test" type="button">click</button>]}
/>
);
const button = toolbar.find('#test');
expect(button).toHaveLength(1);
expect(button.text()).toEqual('click');
});
});

View File

@@ -1,20 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import ExpandCollapse from '../../src/components/ExpandCollapse';
describe('<ExpandCollapse />', () => {
const onCompact = jest.fn();
const onExpand = jest.fn();
const isCompact = false;
test('initially renders without crashing', () => {
const wrapper = mountWithContexts(
<ExpandCollapse
onCompact={onCompact}
onExpand={onExpand}
isCompact={isCompact}
/>
);
expect(wrapper.length).toBe(1);
wrapper.unmount();
});
});

View File

@@ -1,228 +0,0 @@
import React from 'react';
import { createMemoryHistory } from 'history';
import { mountWithContexts } from '../enzymeHelpers';
import Lookup from '../../src/components/Lookup';
import { _Lookup } from '../../src/components/Lookup/Lookup';
let mockData = [{ name: 'foo', id: 1, isChecked: false }];
const mockColumns = [
{ name: 'Name', key: 'name', isSortable: true }
];
describe('<Lookup />', () => {
test('initially renders succesfully', () => {
mountWithContexts(
<Lookup
lookupHeader="Foo Bar"
name="fooBar"
value={mockData}
onLookupSave={() => { }}
getItems={() => { }}
columns={mockColumns}
sortedColumnKey="name"
/>
);
});
test('API response is formatted properly', (done) => {
const wrapper = mountWithContexts(
<Lookup
lookupHeader="Foo Bar"
name="fooBar"
value={mockData}
onLookupSave={() => { }}
getItems={() => ({ data: { results: [{ name: 'test instance', id: 1 }] } })}
columns={mockColumns}
sortedColumnKey="name"
/>
).find('Lookup');
setImmediate(() => {
expect(wrapper.state().results).toEqual([{ id: 1, name: 'test instance' }]);
done();
});
});
test('Opens modal when search icon is clicked', () => {
const spy = jest.spyOn(_Lookup.prototype, 'handleModalToggle');
const mockSelected = [{ name: 'foo', id: 1 }];
const wrapper = mountWithContexts(
<Lookup
id="search"
lookupHeader="Foo Bar"
name="fooBar"
value={mockSelected}
onLookupSave={() => { }}
getItems={() => { }}
columns={mockColumns}
sortedColumnKey="name"
/>
).find('Lookup');
expect(spy).not.toHaveBeenCalled();
expect(wrapper.state('lookupSelectedItems')).toEqual(mockSelected);
const searchItem = wrapper.find('button[aria-label="Search"]');
searchItem.first().simulate('click');
expect(spy).toHaveBeenCalled();
expect(wrapper.state('lookupSelectedItems')).toEqual([{
id: 1,
name: 'foo'
}]);
expect(wrapper.state('isModalOpen')).toEqual(true);
});
test('calls "toggleSelected" when a user changes a checkbox', (done) => {
const spy = jest.spyOn(_Lookup.prototype, 'toggleSelected');
const mockSelected = [{ name: 'foo', id: 1 }];
const data = {
results: [
{ name: 'test instance', id: 1, url: '/foo' }
],
count: 1,
};
const wrapper = mountWithContexts(
<Lookup
id="search"
lookupHeader="Foo Bar"
name="fooBar"
value={mockSelected}
onLookupSave={() => { }}
getItems={() => ({ data })}
columns={mockColumns}
sortedColumnKey="name"
/>
);
setImmediate(() => {
const searchItem = wrapper.find('button[aria-label="Search"]');
searchItem.first().simulate('click');
wrapper.find('input[type="checkbox"]').simulate('change');
expect(spy).toHaveBeenCalled();
done();
});
});
test('calls "toggleSelected" when remove icon is clicked', () => {
const spy = jest.spyOn(_Lookup.prototype, 'toggleSelected');
mockData = [{ name: 'foo', id: 1 }, { name: 'bar', id: 2 }];
const data = {
results: [
{ name: 'test instance', id: 1, url: '/foo' }
],
count: 1,
};
const wrapper = mountWithContexts(
<Lookup
id="search"
lookupHeader="Foo Bar"
name="fooBar"
value={mockData}
onLookupSave={() => { }}
getItems={() => ({ data })}
columns={mockColumns}
sortedColumnKey="name"
/>
);
const removeIcon = wrapper.find('button[aria-label="close"]').first();
removeIcon.simulate('click');
expect(spy).toHaveBeenCalled();
});
test('renders chips from prop value', () => {
mockData = [{ name: 'foo', id: 0 }, { name: 'bar', id: 1 }];
const wrapper = mountWithContexts(
<Lookup
lookupHeader="Foo Bar"
onLookupSave={() => { }}
value={mockData}
selected={[]}
getItems={() => { }}
columns={mockColumns}
sortedColumnKey="name"
/>
).find('Lookup');
const chip = wrapper.find('.pf-c-chip');
expect(chip).toHaveLength(2);
});
test('toggleSelected successfully adds/removes row from lookupSelectedItems state', () => {
mockData = [];
const wrapper = mountWithContexts(
<Lookup
lookupHeader="Foo Bar"
onLookupSave={() => { }}
value={mockData}
getItems={() => { }}
columns={mockColumns}
sortedColumnKey="name"
/>
).find('Lookup');
wrapper.instance().toggleSelected({
id: 1,
name: 'foo'
});
expect(wrapper.state('lookupSelectedItems')).toEqual([{
id: 1,
name: 'foo'
}]);
wrapper.instance().toggleSelected({
id: 1,
name: 'foo'
});
expect(wrapper.state('lookupSelectedItems')).toEqual([]);
});
test('saveModal calls callback with selected items', () => {
mockData = [];
const onLookupSaveFn = jest.fn();
const wrapper = mountWithContexts(
<Lookup
lookupHeader="Foo Bar"
name="fooBar"
value={mockData}
onLookupSave={onLookupSaveFn}
getItems={() => { }}
sortedColumnKey="name"
/>
).find('Lookup');
wrapper.instance().toggleSelected({
id: 1,
name: 'foo'
});
expect(wrapper.state('lookupSelectedItems')).toEqual([{
id: 1,
name: 'foo'
}]);
wrapper.instance().saveModal();
expect(onLookupSaveFn).toHaveBeenCalledWith([{
id: 1,
name: 'foo'
}], 'fooBar');
});
test('should re-fetch data when URL params change', async () => {
const history = createMemoryHistory({
initialEntries: ['/organizations/add'],
});
const getItems = jest.fn();
const wrapper = mountWithContexts(
<_Lookup
lookupHeader="Foo Bar"
onLookupSave={() => { }}
value={mockData}
selected={[]}
columns={mockColumns}
sortedColumnKey="name"
getItems={getItems}
handleHttpError={() => {}}
location={{ history }}
i18n={{ _: val => val.toString() }}
/>
);
expect(getItems).toHaveBeenCalledTimes(1);
history.push('organizations/add?page=2');
wrapper.setProps({
location: { history },
});
wrapper.update();
expect(getItems).toHaveBeenCalledTimes(2);
});
});

View File

@@ -1,63 +0,0 @@
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { mount } from 'enzyme';
import { Nav } from '@patternfly/react-core';
import NavExpandableGroup from '../../src/components/NavExpandableGroup';
describe('NavExpandableGroup', () => {
test('initialization and render', () => {
const component = mount(
<MemoryRouter initialEntries={['/foo']}>
<Nav aria-label="Test Navigation">
<NavExpandableGroup
groupId="test"
groupTitle="Test"
routes={[
{ path: '/foo', title: 'Foo' },
{ path: '/bar', title: 'Bar' },
{ path: '/fiz', title: 'Fiz' },
]}
/>
</Nav>
</MemoryRouter>
).find('NavExpandableGroup').instance();
expect(component.navItemPaths).toEqual(['/foo', '/bar', '/fiz']);
expect(component.isActiveGroup()).toEqual(true);
});
describe('isActivePath', () => {
const params = [
['/fo', '/foo', false],
['/foo', '/foo', true],
['/foo/1/bar/fiz', '/foo', true],
['/foo/1/bar/fiz', 'foo', false],
['/foo/1/bar/fiz', 'foo/', false],
['/foo/1/bar/fiz', '/bar', false],
['/foo/1/bar/fiz', '/fiz', false],
];
params.forEach(([location, path, expected]) => {
test(`when location is ${location}, isActivePath('${path}') returns ${expected} `, () => {
const component = mount(
<MemoryRouter initialEntries={[location]}>
<Nav aria-label="Test Navigation">
<NavExpandableGroup
groupId="test"
groupTitle="Test"
routes={[
{ path: '/foo', title: 'Foo' },
{ path: '/bar', title: 'Bar' },
{ path: '/fiz', title: 'Fiz' },
]}
/>
</Nav>
</MemoryRouter>
).find('NavExpandableGroup').instance();
expect(component.isActivePath(path)).toEqual(expected);
});
});
});
});

View File

@@ -1,108 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import NotificationListItem from '../../src/components/NotificationsList/NotificationListItem';
describe('<NotificationListItem canToggleNotifications />', () => {
let wrapper;
let toggleNotification;
beforeEach(() => {
toggleNotification = jest.fn();
});
afterEach(() => {
if (wrapper) {
wrapper.unmount();
wrapper = null;
}
jest.clearAllMocks();
});
test('initially renders succesfully', () => {
wrapper = mountWithContexts(
<NotificationListItem
notification={{
id: 9000,
name: 'Foo',
notification_type: 'slack',
}}
toggleNotification={toggleNotification}
detailUrl="/foo"
canToggleNotifications
/>
);
expect(wrapper.find('NotificationListItem')).toMatchSnapshot();
});
test('handles success click when toggle is on', () => {
wrapper = mountWithContexts(
<NotificationListItem
notification={{
id: 9000,
name: 'Foo',
notification_type: 'slack',
}}
successTurnedOn
toggleNotification={toggleNotification}
detailUrl="/foo"
canToggleNotifications
/>
);
wrapper.find('Switch').first().find('input').simulate('change');
expect(toggleNotification).toHaveBeenCalledWith(9000, true, 'success');
});
test('handles success click when toggle is off', () => {
wrapper = mountWithContexts(
<NotificationListItem
notification={{
id: 9000,
name: 'Foo',
notification_type: 'slack',
}}
successTurnedOn={false}
toggleNotification={toggleNotification}
detailUrl="/foo"
canToggleNotifications
/>
);
wrapper.find('Switch').first().find('input').simulate('change');
expect(toggleNotification).toHaveBeenCalledWith(9000, false, 'success');
});
test('handles error click when toggle is on', () => {
wrapper = mountWithContexts(
<NotificationListItem
notification={{
id: 9000,
name: 'Foo',
notification_type: 'slack',
}}
errorTurnedOn
toggleNotification={toggleNotification}
detailUrl="/foo"
canToggleNotifications
/>
);
wrapper.find('Switch').at(1).find('input').simulate('change');
expect(toggleNotification).toHaveBeenCalledWith(9000, true, 'error');
});
test('handles error click when toggle is off', () => {
wrapper = mountWithContexts(
<NotificationListItem
notification={{
id: 9000,
name: 'Foo',
notification_type: 'slack',
}}
errorTurnedOn={false}
toggleNotification={toggleNotification}
detailUrl="/foo"
canToggleNotifications
/>
);
wrapper.find('Switch').at(1).find('input').simulate('change');
expect(toggleNotification).toHaveBeenCalledWith(9000, false, 'error');
});
});

View File

@@ -1,46 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import PageHeaderToolbar from '../../src/components/PageHeaderToolbar';
describe('PageHeaderToolbar', () => {
const pageHelpDropdownSelector = 'Dropdown QuestionCircleIcon';
const pageUserDropdownSelector = 'Dropdown UserIcon';
const onAboutClick = jest.fn();
const onLogoutClick = jest.fn();
test('expected content is rendered on initialization', () => {
const wrapper = mountWithContexts(
<PageHeaderToolbar
onAboutClick={onAboutClick}
onLogoutClick={onLogoutClick}
/>
);
expect(wrapper.find(pageHelpDropdownSelector)).toHaveLength(1);
expect(wrapper.find(pageUserDropdownSelector)).toHaveLength(1);
});
test('dropdowns have expected items and callbacks', () => {
const wrapper = mountWithContexts(
<PageHeaderToolbar
onAboutClick={onAboutClick}
onLogoutClick={onLogoutClick}
/>
);
expect(wrapper.find('DropdownItem')).toHaveLength(0);
wrapper.find(pageHelpDropdownSelector).simulate('click');
expect(wrapper.find('DropdownItem')).toHaveLength(2);
const about = wrapper.find('DropdownItem li button');
about.simulate('click');
expect(onAboutClick).toHaveBeenCalled();
expect(wrapper.find('DropdownItem')).toHaveLength(0);
wrapper.find(pageUserDropdownSelector).simulate('click');
expect(wrapper.find('DropdownItem')).toHaveLength(2);
const logout = wrapper.find('DropdownItem li button');
logout.simulate('click');
expect(onLogoutClick).toHaveBeenCalled();
});
});

View File

@@ -1,76 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../../enzymeHelpers';
import { ToolbarDeleteButton } from '../../../src/components/PaginatedDataList';
const itemA = {
id: 1,
name: 'Foo',
summary_fields: { user_capabilities: { delete: true } },
};
const itemB = {
id: 1,
name: 'Foo',
summary_fields: { user_capabilities: { delete: false } },
};
describe('<ToolbarDeleteButton />', () => {
test('should render button', () => {
const wrapper = mountWithContexts(
<ToolbarDeleteButton
onDelete={() => {}}
itemsToDelete={[]}
/>
);
expect(wrapper.find('button')).toHaveLength(1);
expect(wrapper.find('ToolbarDeleteButton')).toMatchSnapshot();
});
test('should open confirmation modal', () => {
const wrapper = mountWithContexts(
<ToolbarDeleteButton
onDelete={() => {}}
itemsToDelete={[itemA]}
/>
);
wrapper.find('button').simulate('click');
expect(wrapper.find('ToolbarDeleteButton').state('isModalOpen'))
.toBe(true);
wrapper.update();
expect(wrapper.find('Modal')).toHaveLength(1);
});
test('should invoke onDelete prop', () => {
const onDelete = jest.fn();
const wrapper = mountWithContexts(
<ToolbarDeleteButton
onDelete={onDelete}
itemsToDelete={[itemA]}
/>
);
wrapper.find('ToolbarDeleteButton').setState({ isModalOpen: true });
wrapper.find('button.pf-m-danger').simulate('click');
expect(onDelete).toHaveBeenCalled();
expect(wrapper.find('ToolbarDeleteButton').state('isModalOpen')).toBe(false);
});
test('should disable button when no delete permissions', () => {
const wrapper = mountWithContexts(
<ToolbarDeleteButton
onDelete={() => {}}
itemsToDelete={[itemB]}
/>
);
expect(wrapper.find('button[disabled]')).toHaveLength(1);
});
test('should render tooltip', () => {
const wrapper = mountWithContexts(
<ToolbarDeleteButton
onDelete={() => {}}
itemsToDelete={[itemA]}
/>
);
expect(wrapper.find('Tooltip')).toHaveLength(1);
expect(wrapper.find('Tooltip').prop('content')).toEqual('Delete');
});
});

View File

@@ -1,123 +0,0 @@
import React from 'react';
import { createMemoryHistory } from 'history';
import { mountWithContexts } from '../../enzymeHelpers';
import { sleep } from '../../testUtils';
import PaginatedDataList from '../../../src/components/PaginatedDataList';
const mockData = [
{ id: 1, name: 'one', url: '/org/team/1' },
{ id: 2, name: 'two', url: '/org/team/2' },
{ id: 3, name: 'three', url: '/org/team/3' },
{ id: 4, name: 'four', url: '/org/team/4' },
{ id: 5, name: 'five', url: '/org/team/5' },
];
const qsConfig = {
namespace: 'item',
defaultParams: { page: 1, page_size: 5 },
integerFields: [],
};
describe('<PaginatedDataList />', () => {
afterEach(() => {
jest.restoreAllMocks();
});
test('initially renders succesfully', () => {
mountWithContexts(
<PaginatedDataList
items={mockData}
itemCount={7}
queryParams={{
page: 1,
page_size: 5,
order_by: 'name',
}}
qsConfig={qsConfig}
/>
);
});
test('should navigate when DataListToolbar calls onSort prop', async () => {
const history = createMemoryHistory({
initialEntries: ['/organizations/1/teams'],
});
const wrapper = mountWithContexts(
<PaginatedDataList
items={mockData}
itemCount={7}
queryParams={{
page: 1,
page_size: 5,
order_by: 'name',
}}
qsConfig={qsConfig}
/>, { context: { router: { history } } }
);
const toolbar = wrapper.find('DataListToolbar');
expect(toolbar.prop('sortedColumnKey')).toEqual('name');
expect(toolbar.prop('sortOrder')).toEqual('ascending');
toolbar.prop('onSort')('name', 'descending');
expect(history.location.search).toEqual('?item.order_by=-name');
await sleep(0);
wrapper.update();
expect(toolbar.prop('sortedColumnKey')).toEqual('name');
// TODO: this assertion required updating queryParams prop. Consider
// fixing after #147 is done:
// expect(toolbar.prop('sortOrder')).toEqual('descending');
toolbar.prop('onSort')('name', 'ascending');
expect(history.location.search).toEqual('?item.order_by=name');
});
test('should navigate to page when Pagination calls onSetPage prop', () => {
const history = createMemoryHistory({
initialEntries: ['/organizations/1/teams'],
});
const wrapper = mountWithContexts(
<PaginatedDataList
items={mockData}
itemCount={7}
queryParams={{
page: 1,
page_size: 5,
order_by: 'name',
}}
qsConfig={qsConfig}
/>, { context: { router: { history } } }
);
const pagination = wrapper.find('Pagination');
pagination.prop('onSetPage')(null, 2);
expect(history.location.search).toEqual('?item.page=2');
wrapper.update();
pagination.prop('onSetPage')(null, 1);
expect(history.location.search).toEqual('?item.page=1');
});
test('should navigate to page when Pagination calls onPerPageSelect prop', () => {
const history = createMemoryHistory({
initialEntries: ['/organizations/1/teams'],
});
const wrapper = mountWithContexts(
<PaginatedDataList
items={mockData}
itemCount={7}
queryParams={{
page: 1,
page_size: 5,
order_by: 'name',
}}
qsConfig={qsConfig}
/>, { context: { router: { history } } }
);
const pagination = wrapper.find('Pagination');
pagination.prop('onPerPageSelect')(null, 5);
expect(history.location.search).toEqual('?item.page_size=5');
wrapper.update();
pagination.prop('onPerPageSelect')(null, 25);
expect(history.location.search).toEqual('?item.page_size=25');
});
});

View File

@@ -1,25 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../../enzymeHelpers';
import { ToolbarAddButton } from '../../../src/components/PaginatedDataList';
describe('<ToolbarAddButton />', () => {
test('should render button', () => {
const onClick = jest.fn();
const wrapper = mountWithContexts(
<ToolbarAddButton onClick={onClick} />
);
const button = wrapper.find('button');
expect(button).toHaveLength(1);
button.simulate('click');
expect(onClick).toHaveBeenCalled();
});
test('should render link', () => {
const wrapper = mountWithContexts(
<ToolbarAddButton linkTo="/foo" />
);
const link = wrapper.find('Link');
expect(link).toHaveLength(1);
expect(link.prop('to')).toBe('/foo');
});
});

View File

@@ -1,202 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<ToolbarDeleteButton /> should render button 1`] = `
<ToolbarDeleteButton
i18n={"/i18n/"}
itemName="item"
itemsToDelete={Array []}
onDelete={[Function]}
>
<Tooltip
appendTo={[Function]}
className={null}
content="Select a row to delete"
enableFlip={true}
entryDelay={500}
exitDelay={500}
maxWidth="18.75rem"
position="top"
trigger="mouseenter focus"
zIndex={9999}
>
<Tippy
animateFill={false}
appendTo={[Function]}
content={
<div
className="pf-c-tooltip"
role="tooltip"
>
<TooltipArrow
className={null}
/>
<TooltipContent
className={null}
>
Select a row to delete
</TooltipContent>
</div>
}
delay={
Array [
500,
500,
]
}
distance={15}
flip={true}
lazy={true}
maxWidth="18.75rem"
onCreate={[Function]}
performance={true}
placement="top"
popperOptions={
Object {
"modifiers": Object {
"hide": Object {
"enabled": true,
},
"preventOverflow": Object {
"enabled": true,
},
},
}
}
theme="pf-tippy"
trigger="mouseenter focus"
zIndex={9999}
>
<div>
<ToolbarDeleteButton__DeleteButton
aria-label="Delete"
isDisabled={true}
onClick={[Function]}
variant="plain"
>
<StyledComponent
aria-label="Delete"
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "ToolbarDeleteButton__DeleteButton-sc-1e3r0eg-0",
"isStatic": true,
"lastClassName": "bQjfFG",
"rules": Array [
"padding:5px 8px;&:hover{background-color:#d9534f;color:white;}&[disabled]{color:var(--pf-c-button--m-plain--Color);pointer-events:initial;cursor:not-allowed;}",
],
},
"displayName": "ToolbarDeleteButton__DeleteButton",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "ToolbarDeleteButton__DeleteButton-sc-1e3r0eg-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
isDisabled={true}
onClick={[Function]}
variant="plain"
>
<Button
aria-label="Delete"
className="ToolbarDeleteButton__DeleteButton-sc-1e3r0eg-0 bQjfFG"
component="button"
isActive={false}
isBlock={false}
isDisabled={true}
isFocus={false}
isHover={false}
isInline={false}
onClick={[Function]}
type="button"
variant="plain"
>
<button
aria-disabled={null}
aria-label="Delete"
className="pf-c-button pf-m-plain pf-m-disabled ToolbarDeleteButton__DeleteButton-sc-1e3r0eg-0 bQjfFG"
disabled={true}
onClick={[Function]}
tabIndex={null}
type="button"
>
<TrashAltIcon
color="currentColor"
size="sm"
title={null}
>
<svg
aria-hidden={true}
aria-labelledby={null}
fill="currentColor"
height="1em"
role="img"
style={
Object {
"verticalAlign": "-0.125em",
}
}
viewBox="0 0 448 512"
width="1em"
>
<path
d="M32 464a48 48 0 0 0 48 48h288a48 48 0 0 0 48-48V128H32zm272-256a16 16 0 0 1 32 0v224a16 16 0 0 1-32 0zm-96 0a16 16 0 0 1 32 0v224a16 16 0 0 1-32 0zm-96 0a16 16 0 0 1 32 0v224a16 16 0 0 1-32 0zM432 32H312l-9.4-18.7A24 24 0 0 0 281.1 0H166.8a23.72 23.72 0 0 0-21.4 13.3L136 32H16A16 16 0 0 0 0 48v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16z"
transform=""
/>
</svg>
</TrashAltIcon>
</button>
</Button>
</StyledComponent>
</ToolbarDeleteButton__DeleteButton>
</div>
<Portal
containerInfo={
<div>
<div
class="pf-c-tooltip"
role="tooltip"
>
<div
class="pf-c-tooltip__arrow"
/>
<div
class="pf-c-tooltip__content"
>
Select a row to delete
</div>
</div>
</div>
}
>
<div
className="pf-c-tooltip"
role="tooltip"
>
<TooltipArrow
className={null}
>
<div
className="pf-c-tooltip__arrow"
/>
</TooltipArrow>
<TooltipContent
className={null}
>
<div
className="pf-c-tooltip__content"
>
Select a row to delete
</div>
</TooltipContent>
</div>
</Portal>
</Tippy>
</Tooltip>
</ToolbarDeleteButton>
`;

View File

@@ -1,63 +0,0 @@
import React from 'react';
import { mount, shallow } from 'enzyme';
import { Router } from 'react-router-dom';
import { createMemoryHistory } from 'history';
import { Tab } from '@patternfly/react-core';
import RoutedTabs, { _RoutedTabs } from '../../src/components/Tabs/RoutedTabs';
let wrapper;
let history;
const tabs = [
{ name: 'Details', link: '/organizations/19/details', id: 1 },
{ name: 'Access', link: '/organizations/19/access', id: 2 },
{ name: 'Teams', link: '/organizations/19/teams', id: 3 },
{ name: 'Notification', link: '/organizations/19/notification', id: 4 }
];
describe('<RoutedTabs />', () => {
beforeEach(() => {
history = createMemoryHistory({
initialEntries: ['/organizations/19/teams'],
});
});
test('RoutedTabs renders successfully', () => {
wrapper = shallow(
<_RoutedTabs
tabsArray={tabs}
history={history}
/>
);
expect(wrapper.find(Tab)).toHaveLength(4);
});
test('Given a URL the correct tab is active', async () => {
wrapper = mount(
<Router history={history}>
<RoutedTabs
tabsArray={tabs}
/>
</Router>
);
expect(history.location.pathname).toEqual('/organizations/19/teams');
expect(wrapper.find('Tabs').prop('activeKey')).toBe(3);
});
test('should update history when new tab selected', async () => {
wrapper = mount(
<Router history={history}>
<RoutedTabs
tabsArray={tabs}
/>
</Router>
);
wrapper.find('Tabs').prop('onSelect')({}, 2);
wrapper.update();
expect(history.location.pathname).toEqual('/organizations/19/access');
expect(wrapper.find('Tabs').prop('activeKey')).toBe(2);
});
});

View File

@@ -1,70 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import Search from '../../src/components/Search';
describe('<Search />', () => {
let search;
afterEach(() => {
if (search) {
search = null;
}
});
test('it triggers the expected callbacks', () => {
const columns = [{ name: 'Name', key: 'name', isSortable: true }];
const searchBtn = 'button[aria-label="Search"]';
const searchTextInput = 'input[aria-label="Search text input"]';
const onSearch = jest.fn();
search = mountWithContexts(
<Search
sortedColumnKey="name"
columns={columns}
onSearch={onSearch}
/>
);
search.find(searchTextInput).instance().value = 'test-321';
search.find(searchTextInput).simulate('change');
search.find(searchBtn).simulate('click');
expect(onSearch).toHaveBeenCalledTimes(1);
expect(onSearch).toBeCalledWith('test-321');
});
test('handleDropdownToggle properly updates state', async () => {
const columns = [{ name: 'Name', key: 'name', isSortable: true }];
const onSearch = jest.fn();
const wrapper = mountWithContexts(
<Search
sortedColumnKey="name"
columns={columns}
onSearch={onSearch}
/>
).find('Search');
expect(wrapper.state('isSearchDropdownOpen')).toEqual(false);
wrapper.instance().handleDropdownToggle(true);
expect(wrapper.state('isSearchDropdownOpen')).toEqual(true);
});
test('handleDropdownSelect properly updates state', async () => {
const columns = [
{ name: 'Name', key: 'name', isSortable: true },
{ name: 'Description', key: 'description', isSortable: true }
];
const onSearch = jest.fn();
const wrapper = mountWithContexts(
<Search
sortedColumnKey="name"
columns={columns}
onSearch={onSearch}
/>
).find('Search');
expect(wrapper.state('searchKey')).toEqual('name');
wrapper.instance().handleDropdownSelect({ target: { innerText: 'Description' } });
expect(wrapper.state('searchKey')).toEqual('description');
});
});

View File

@@ -1,118 +0,0 @@
import React from 'react';
import { createMemoryHistory } from 'history';
import { shallow } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import { sleep } from '../testUtils';
import SelectResourceStep from '../../src/components/AddRole/SelectResourceStep';
describe('<SelectResourceStep />', () => {
const columns = [
{ name: 'Username', key: 'username', isSortable: true }
];
afterEach(() => {
jest.restoreAllMocks();
});
test('initially renders without crashing', () => {
shallow(
<SelectResourceStep
columns={columns}
displayKey="username"
onRowClick={() => {}}
onSearch={() => {}}
sortedColumnKey="username"
/>
);
});
test('fetches resources on mount', async () => {
const handleSearch = jest.fn().mockResolvedValue({
data: {
count: 2,
results: [
{ id: 1, username: 'foo', url: 'item/1' },
{ id: 2, username: 'bar', url: 'item/2' }
]
}
});
mountWithContexts(
<SelectResourceStep
columns={columns}
displayKey="username"
onRowClick={() => {}}
onSearch={handleSearch}
sortedColumnKey="username"
/>
);
expect(handleSearch).toHaveBeenCalledWith({
order_by: 'username',
page: 1,
page_size: 5
});
});
test('readResourceList properly adds rows to state', async () => {
const selectedResourceRows = [
{ id: 1, username: 'foo', url: 'item/1' }
];
const handleSearch = jest.fn().mockResolvedValue({
data: {
count: 2,
results: [
{ id: 1, username: 'foo', url: 'item/1' },
{ id: 2, username: 'bar', url: 'item/2' }
]
}
});
const history = createMemoryHistory({
initialEntries: ['/organizations/1/access?resource.page=1&resource.order_by=-username'],
});
const wrapper = await mountWithContexts(
<SelectResourceStep
columns={columns}
displayKey="username"
onRowClick={() => {}}
onSearch={handleSearch}
selectedResourceRows={selectedResourceRows}
sortedColumnKey="username"
/>, { context: { router: { history, route: { location: history.location } } } }
).find('SelectResourceStep');
await wrapper.instance().readResourceList();
expect(handleSearch).toHaveBeenCalledWith({
order_by: '-username',
page: 1,
page_size: 5,
});
expect(wrapper.state('resources')).toEqual([
{ id: 1, username: 'foo', url: 'item/1' },
{ id: 2, username: 'bar', url: 'item/2' }
]);
});
test('clicking on row fires callback with correct params', async () => {
const handleRowClick = jest.fn();
const data = {
count: 2,
results: [
{ id: 1, username: 'foo', url: 'item/1' },
{ id: 2, username: 'bar', url: 'item/2' }
]
};
const wrapper = mountWithContexts(
<SelectResourceStep
columns={columns}
displayKey="username"
onRowClick={handleRowClick}
onSearch={() => ({ data })}
selectedResourceRows={[]}
sortedColumnKey="username"
/>
);
await sleep(0);
wrapper.update();
const checkboxListItemWrapper = wrapper.find('CheckboxListItem');
expect(checkboxListItemWrapper.length).toBe(2);
checkboxListItemWrapper.first().find('input[type="checkbox"]')
.simulate('change', { target: { checked: true } });
expect(handleRowClick).toHaveBeenCalledWith(data.results[0]);
});
});

View File

@@ -1,64 +0,0 @@
import React from 'react';
import { shallow } from 'enzyme';
import { mountWithContexts } from '../enzymeHelpers';
import SelectRoleStep from '../../src/components/AddRole/SelectRoleStep';
describe('<SelectRoleStep />', () => {
let wrapper;
const roles = {
project_admin_role: {
id: 1,
name: 'Project Admin',
description: 'Can manage all projects of the organization'
},
execute_role: {
id: 2,
name: 'Execute',
description: 'May run any executable resources in the organization'
}
};
const selectedRoles = [
{
id: 1,
name: 'Project Admin',
description: 'Can manage all projects of the organization'
}
];
const selectedResourceRows = [
{
id: 1,
name: 'foo'
}
];
test('initially renders without crashing', () => {
wrapper = shallow(
<SelectRoleStep
roles={roles}
selectedResourceRows={selectedResourceRows}
selectedRoleRows={selectedRoles}
/>
);
expect(wrapper.length).toBe(1);
wrapper.unmount();
});
test('clicking role fires onRolesClick callback', () => {
const onRolesClick = jest.fn();
wrapper = mountWithContexts(
<SelectRoleStep
onRolesClick={onRolesClick}
roles={roles}
selectedResourceRows={selectedResourceRows}
selectedRoleRows={selectedRoles}
/>
);
const CheckboxCards = wrapper.find('CheckboxCard');
expect(CheckboxCards.length).toBe(2);
CheckboxCards.first().prop('onSelect')();
expect(onRolesClick).toBeCalledWith({
id: 1,
name: 'Project Admin',
description: 'Can manage all projects of the organization'
});
wrapper.unmount();
});
});

View File

@@ -1,27 +0,0 @@
import React from 'react';
import { shallow } from 'enzyme';
import SelectableCard from '../../src/components/AddRole/SelectableCard';
describe('<SelectableCard />', () => {
let wrapper;
const onClick = jest.fn();
test('initially renders without crashing when not selected', () => {
wrapper = shallow(
<SelectableCard
onClick={onClick}
/>
);
expect(wrapper.length).toBe(1);
wrapper.unmount();
});
test('initially renders without crashing when selected', () => {
wrapper = shallow(
<SelectableCard
isSelected
onClick={onClick}
/>
);
expect(wrapper.length).toBe(1);
wrapper.unmount();
});
});

View File

@@ -1,63 +0,0 @@
import React from 'react';
import { mount } from 'enzyme';
import SelectedList from '../../src/components/SelectedList';
import { ChipGroup } from '../../src/components/Chip';
describe('<SelectedList />', () => {
test('initially renders succesfully', () => {
const mockSelected = [
{
id: 1,
name: 'foo'
}, {
id: 2,
name: 'bar'
}
];
mount(
<SelectedList
label="Selected"
selected={mockSelected}
showOverflowAfter={5}
onRemove={() => {}}
/>
);
});
test('showOverflow should set showOverflow on ChipGroup', () => {
const wrapper = mount(
<SelectedList
label="Selected"
selected={[]}
showOverflowAfter={5}
onRemove={() => {}}
/>
);
const chipGroup = wrapper.find(ChipGroup);
expect(chipGroup).toHaveLength(1);
expect(chipGroup.prop('showOverflowAfter')).toEqual(5);
});
test('Clicking remove on chip calls onRemove callback prop with correct params', () => {
const onRemove = jest.fn();
const mockSelected = [
{
id: 1,
name: 'foo'
}
];
const wrapper = mount(
<SelectedList
label="Selected"
selected={mockSelected}
showOverflowAfter={3}
onRemove={onRemove}
/>
);
wrapper.find('.pf-c-chip button').first().simulate('click');
expect(onRemove).toBeCalledWith({
id: 1,
name: 'foo'
});
});
});

View File

@@ -1,188 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import Sort from '../../src/components/Sort';
describe('<Sort />', () => {
let sort;
afterEach(() => {
if (sort) {
sort = null;
}
});
test('it triggers the expected callbacks', () => {
const columns = [{ name: 'Name', key: 'name', isSortable: true }];
const sortBtn = 'button[aria-label="Sort"]';
const onSort = jest.fn();
const wrapper = mountWithContexts(
<Sort
sortedColumnKey="name"
sortOrder="ascending"
columns={columns}
onSort={onSort}
/>
).find('Sort');
wrapper.find(sortBtn).simulate('click');
expect(onSort).toHaveBeenCalledTimes(1);
expect(onSort).toBeCalledWith('name', 'descending');
});
test('onSort properly passes back descending when ascending was passed as prop', () => {
const multipleColumns = [
{ name: 'Foo', key: 'foo', isSortable: true },
{ name: 'Bar', key: 'bar', isSortable: true },
{ name: 'Bakery', key: 'bakery', isSortable: true },
{ name: 'Baz', key: 'baz' }
];
const onSort = jest.fn();
const wrapper = mountWithContexts(
<Sort
sortedColumnKey="foo"
sortOrder="ascending"
columns={multipleColumns}
onSort={onSort}
/>
).find('Sort');
const sortDropdownToggle = wrapper.find('Button');
expect(sortDropdownToggle.length).toBe(1);
sortDropdownToggle.simulate('click');
expect(onSort).toHaveBeenCalledWith('foo', 'descending');
});
test('onSort properly passes back ascending when descending was passed as prop', () => {
const multipleColumns = [
{ name: 'Foo', key: 'foo', isSortable: true },
{ name: 'Bar', key: 'bar', isSortable: true },
{ name: 'Bakery', key: 'bakery', isSortable: true },
{ name: 'Baz', key: 'baz' }
];
const onSort = jest.fn();
const wrapper = mountWithContexts(
<Sort
sortedColumnKey="foo"
sortOrder="descending"
columns={multipleColumns}
onSort={onSort}
/>
).find('Sort');
const sortDropdownToggle = wrapper.find('Button');
expect(sortDropdownToggle.length).toBe(1);
sortDropdownToggle.simulate('click');
expect(onSort).toHaveBeenCalledWith('foo', 'ascending');
});
test('Changing dropdown correctly passes back new sort key', () => {
const multipleColumns = [
{ name: 'Foo', key: 'foo', isSortable: true },
{ name: 'Bar', key: 'bar', isSortable: true },
{ name: 'Bakery', key: 'bakery', isSortable: true },
{ name: 'Baz', key: 'baz' }
];
const onSort = jest.fn();
const wrapper = mountWithContexts(
<Sort
sortedColumnKey="foo"
sortOrder="ascending"
columns={multipleColumns}
onSort={onSort}
/>
).find('Sort');
wrapper.instance().handleDropdownSelect({ target: { innerText: 'Bar' } });
expect(onSort).toBeCalledWith('bar', 'ascending');
});
test('Opening dropdown correctly updates state', () => {
const multipleColumns = [
{ name: 'Foo', key: 'foo', isSortable: true },
{ name: 'Bar', key: 'bar', isSortable: true },
{ name: 'Bakery', key: 'bakery', isSortable: true },
{ name: 'Baz', key: 'baz' }
];
const onSort = jest.fn();
const wrapper = mountWithContexts(
<Sort
sortedColumnKey="foo"
sortOrder="ascending"
columns={multipleColumns}
onSort={onSort}
/>
).find('Sort');
expect(wrapper.state('isSortDropdownOpen')).toEqual(false);
wrapper.instance().handleDropdownToggle(true);
expect(wrapper.state('isSortDropdownOpen')).toEqual(true);
});
test('It displays correct sort icon', () => {
const downNumericIconSelector = 'SortNumericDownIcon';
const upNumericIconSelector = 'SortNumericUpIcon';
const downAlphaIconSelector = 'SortAlphaDownIcon';
const upAlphaIconSelector = 'SortAlphaUpIcon';
const numericColumns = [{ name: 'ID', key: 'id', isSortable: true, isNumeric: true }];
const alphaColumns = [{ name: 'Name', key: 'name', isSortable: true, isNumeric: false }];
const onSort = jest.fn();
sort = mountWithContexts(
<Sort
sortedColumnKey="id"
sortOrder="descending"
columns={numericColumns}
onSort={onSort}
/>
);
const downNumericIcon = sort.find(downNumericIconSelector);
expect(downNumericIcon.length).toBe(1);
sort = mountWithContexts(
<Sort
sortedColumnKey="id"
sortOrder="ascending"
columns={numericColumns}
onSort={onSort}
/>
);
const upNumericIcon = sort.find(upNumericIconSelector);
expect(upNumericIcon.length).toBe(1);
sort = mountWithContexts(
<Sort
sortedColumnKey="name"
sortOrder="descending"
columns={alphaColumns}
onSort={onSort}
/>
);
const downAlphaIcon = sort.find(downAlphaIconSelector);
expect(downAlphaIcon.length).toBe(1);
sort = mountWithContexts(
<Sort
sortedColumnKey="name"
sortOrder="ascending"
columns={alphaColumns}
onSort={onSort}
/>
);
const upAlphaIcon = sort.find(upAlphaIconSelector);
expect(upAlphaIcon.length).toBe(1);
});
});

View File

@@ -1,11 +0,0 @@
import React from 'react';
import { mount } from 'enzyme';
import VerticalSeparator from '../../src/components/VerticalSeparator';
describe('VerticalSeparator', () => {
test('renders the expected content', () => {
const wrapper = mount(<VerticalSeparator />);
expect(wrapper).toHaveLength(1);
});
});

View File

@@ -1,456 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<NotificationListItem canToggleNotifications /> initially renders succesfully 1`] = `
<NotificationListItem
canToggleNotifications={true}
detailUrl="/foo"
errorTurnedOn={false}
i18n={"/i18n/"}
notification={
Object {
"id": 9000,
"name": "Foo",
"notification_type": "slack",
}
}
successTurnedOn={false}
toggleNotification={[MockFunction]}
>
<DataListItem
aria-labelledby="items-list-item-9000"
className=""
isExpanded={false}
key="9000"
>
<li
aria-labelledby="items-list-item-9000"
className="pf-c-data-list__item"
>
<DataListItemRow
className=""
key=".0"
rowid="items-list-item-9000"
>
<div
className="pf-c-data-list__item-row"
>
<DataListItemCells
className=""
dataListCells={
Array [
<ForwardRef>
<ForwardRef
to={
Object {
"pathname": "/foo",
}
}
>
<b
id="items-list-item-9000"
>
Foo
</b>
</ForwardRef>
<ForwardRef
isRead={true}
>
slack
</ForwardRef>
</ForwardRef>,
<ForwardRef
righthalf="true"
>
<ForwardRef
aria-label="Toggle notification success"
id="notification-9000-success-toggle"
isChecked={false}
isDisabled={false}
label="Successful"
onChange={[Function]}
/>
<ForwardRef
aria-label="Toggle notification failure"
id="notification-9000-error-toggle"
isChecked={false}
isDisabled={false}
label="Failure"
onChange={[Function]}
/>
</ForwardRef>,
]
}
key=".0"
rowid="items-list-item-9000"
>
<div
className="pf-c-data-list__item-content"
>
<NotificationListItem__DataListCell
key="name"
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__DataListCell-j7c411-0",
"isStatic": false,
"lastClassName": "hoXOpW",
"rules": Array [
"display:flex;justify-content:",
[Function],
";padding-bottom:",
[Function],
";@media screen and (min-width:768px){justify-content:",
[Function],
";padding-bottom:0;}",
],
},
"displayName": "NotificationListItem__DataListCell",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__DataListCell-j7c411-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
>
<DataListCell
alignRight={false}
className="NotificationListItem__DataListCell-j7c411-0 kIdLtz"
isFilled={true}
isIcon={false}
width={1}
>
<div
className="pf-c-data-list__cell NotificationListItem__DataListCell-j7c411-0 kIdLtz"
>
<Styled(Link)
to={
Object {
"pathname": "/foo",
}
}
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "sc-bdVaJa",
"isStatic": true,
"lastClassName": "eBseNd",
"rules": Array [
"margin-right: 1.5em;",
],
},
"displayName": "Styled(Link)",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "sc-bdVaJa",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
to={
Object {
"pathname": "/foo",
}
}
>
<Link
className="sc-bdVaJa eBseNd"
replace={false}
to={
Object {
"pathname": "/foo",
}
}
>
<a
className="sc-bdVaJa eBseNd"
onClick={[Function]}
>
<b
id="items-list-item-9000"
>
Foo
</b>
</a>
</Link>
</StyledComponent>
</Styled(Link)>
<Styled(Badge)
isRead={true}
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "sc-bwzfXH",
"isStatic": true,
"lastClassName": "chTbOZ",
"rules": Array [
"text-transform: capitalize;",
],
},
"displayName": "Styled(Badge)",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "sc-bwzfXH",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
isRead={true}
>
<Badge
className="sc-bwzfXH chTbOZ"
isRead={true}
>
<span
className="pf-c-badge pf-m-read sc-bwzfXH chTbOZ"
>
slack
</span>
</Badge>
</StyledComponent>
</Styled(Badge)>
</div>
</DataListCell>
</StyledComponent>
</NotificationListItem__DataListCell>
<NotificationListItem__DataListCell
key="toggles"
righthalf="true"
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__DataListCell-j7c411-0",
"isStatic": false,
"lastClassName": "hoXOpW",
"rules": Array [
"display:flex;justify-content:",
[Function],
";padding-bottom:",
[Function],
";@media screen and (min-width:768px){justify-content:",
[Function],
";padding-bottom:0;}",
],
},
"displayName": "NotificationListItem__DataListCell",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__DataListCell-j7c411-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
righthalf="true"
>
<DataListCell
alignRight={false}
className="NotificationListItem__DataListCell-j7c411-0 hoXOpW"
isFilled={true}
isIcon={false}
righthalf="true"
width={1}
>
<div
className="pf-c-data-list__cell NotificationListItem__DataListCell-j7c411-0 hoXOpW"
righthalf="true"
>
<NotificationListItem__Switch
aria-label="Toggle notification success"
id="notification-9000-success-toggle"
isChecked={false}
isDisabled={false}
label="Successful"
onChange={[Function]}
>
<StyledComponent
aria-label="Toggle notification success"
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__Switch-j7c411-1",
"isStatic": true,
"lastClassName": "ceuHGn",
"rules": Array [
"display:flex;flex-wrap:no-wrap;",
],
},
"displayName": "NotificationListItem__Switch",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__Switch-j7c411-1",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
id="notification-9000-success-toggle"
isChecked={false}
isDisabled={false}
label="Successful"
onChange={[Function]}
>
<Switch
aria-label="Toggle notification success"
className="NotificationListItem__Switch-j7c411-1 ceuHGn"
id="notification-9000-success-toggle"
isChecked={false}
isDisabled={false}
label="Successful"
onChange={[Function]}
>
<label
className="pf-c-switch NotificationListItem__Switch-j7c411-1 ceuHGn"
htmlFor="notification-9000-success-toggle"
>
<input
aria-label="Toggle notification success"
checked={false}
className="pf-c-switch__input"
disabled={false}
id="notification-9000-success-toggle"
onChange={[Function]}
type="checkbox"
/>
<span
className="pf-c-switch__toggle"
/>
<span
aria-hidden="true"
className="pf-c-switch__label pf-m-on"
>
Successful
</span>
<span
aria-hidden="true"
className="pf-c-switch__label pf-m-off"
>
Successful
</span>
</label>
</Switch>
</StyledComponent>
</NotificationListItem__Switch>
<NotificationListItem__Switch
aria-label="Toggle notification failure"
id="notification-9000-error-toggle"
isChecked={false}
isDisabled={false}
label="Failure"
onChange={[Function]}
>
<StyledComponent
aria-label="Toggle notification failure"
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "NotificationListItem__Switch-j7c411-1",
"isStatic": true,
"lastClassName": "ceuHGn",
"rules": Array [
"display:flex;flex-wrap:no-wrap;",
],
},
"displayName": "NotificationListItem__Switch",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "NotificationListItem__Switch-j7c411-1",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
id="notification-9000-error-toggle"
isChecked={false}
isDisabled={false}
label="Failure"
onChange={[Function]}
>
<Switch
aria-label="Toggle notification failure"
className="NotificationListItem__Switch-j7c411-1 ceuHGn"
id="notification-9000-error-toggle"
isChecked={false}
isDisabled={false}
label="Failure"
onChange={[Function]}
>
<label
className="pf-c-switch NotificationListItem__Switch-j7c411-1 ceuHGn"
htmlFor="notification-9000-error-toggle"
>
<input
aria-label="Toggle notification failure"
checked={false}
className="pf-c-switch__input"
disabled={false}
id="notification-9000-error-toggle"
onChange={[Function]}
type="checkbox"
/>
<span
className="pf-c-switch__toggle"
/>
<span
aria-hidden="true"
className="pf-c-switch__label pf-m-on"
>
Failure
</span>
<span
aria-hidden="true"
className="pf-c-switch__label pf-m-off"
>
Failure
</span>
</label>
</Switch>
</StyledComponent>
</NotificationListItem__Switch>
</div>
</DataListCell>
</StyledComponent>
</NotificationListItem__DataListCell>
</div>
</DataListItemCells>
</div>
</DataListItemRow>
</li>
</DataListItem>
</NotificationListItem>
`;

View File

@@ -1,156 +0,0 @@
/*
* Enzyme helpers for injecting top-level contexts
* derived from https://lingui.js.org/guides/testing.html
*/
import React from 'react';
import { shape, object, string, arrayOf } from 'prop-types';
import { mount, shallow } from 'enzyme';
import { I18nProvider } from '@lingui/react';
import { ConfigProvider } from '../src/contexts/Config';
const language = 'en-US';
const intlProvider = new I18nProvider(
{
language,
catalogs: {
[language]: {}
}
},
{}
);
const {
linguiPublisher: { i18n: originalI18n }
} = intlProvider.getChildContext();
const defaultContexts = {
linguiPublisher: {
i18n: {
...originalI18n,
_: key => key.id, // provide _ macro, for just passing down the key
toJSON: () => '/i18n/',
},
},
config: {
ansible_version: null,
custom_virtualenvs: [],
version: null,
toJSON: () => '/config/'
},
router: {
history: {
push: () => {},
replace: () => {},
createHref: () => {},
location: {
hash: '',
pathname: '',
search: '',
state: '',
},
toJSON: () => '/history/',
},
route: {
location: {
hash: '',
pathname: '',
search: '',
state: '',
},
match: {
params: {},
isExact: false,
path: '',
url: '',
}
},
toJSON: () => '/router/',
},
};
function wrapContexts (node, context) {
const { config } = context;
class Wrap extends React.Component {
render () {
// eslint-disable-next-line react/no-this-in-sfc
const { children, ...props } = this.props;
const component = React.cloneElement(children, props);
return (
<ConfigProvider value={config}>
{component}
</ConfigProvider>
);
}
}
return (
<Wrap>{node}</Wrap>
);
}
function applyDefaultContexts (context) {
if (!context) {
return defaultContexts;
}
const newContext = {};
Object.keys(defaultContexts).forEach(key => {
newContext[key] = {
...defaultContexts[key],
...context[key],
};
});
return newContext;
}
export function shallowWithContexts (node, options = {}) {
const context = applyDefaultContexts(options.context);
return shallow(wrapContexts(node, context));
}
export function mountWithContexts (node, options = {}) {
const context = applyDefaultContexts(options.context);
const childContextTypes = {
linguiPublisher: shape({
i18n: object.isRequired
}).isRequired,
config: shape({
ansible_version: string,
custom_virtualenvs: arrayOf(string),
version: string,
}),
router: shape({
route: shape({
location: shape({}),
match: shape({}),
}).isRequired,
history: shape({}).isRequired,
}),
...options.childContextTypes
};
return mount(wrapContexts(node, context), { context, childContextTypes });
}
/**
* Wait for element(s) to achieve a desired state.
*
* @param[wrapper] - A ReactWrapper instance
* @param[selector] - The selector of the element(s) to wait for.
* @param[callback] - Callback to poll - by default this checks for a node count of 1.
*/
export function waitForElement (wrapper, selector, callback = el => el.length === 1) {
const interval = 100;
return new Promise((resolve, reject) => {
let attempts = 30;
(function pollElement () {
wrapper.update();
const el = wrapper.find(selector);
if (callback(el)) {
return resolve(el);
}
if (--attempts <= 0) {
const message = `Expected condition for <${selector}> not met: ${callback.toString()}`;
return reject(new Error(message));
}
return setTimeout(pollElement, interval);
}());
});
}

View File

@@ -1,161 +0,0 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { mountWithContexts, waitForElement } from './enzymeHelpers';
import { Config } from '../src/contexts/Config';
describe('mountWithContexts', () => {
describe('injected I18nProvider', () => {
test('should mount and render', () => {
const Child = withI18n()(({ i18n }) => (
<div>
<span>{i18n._(t`Text content`)}</span>
</div>
));
const wrapper = mountWithContexts(<Child />);
expect(wrapper.find('div')).toMatchSnapshot();
});
test('should mount and render deeply nested consumer', () => {
const Child = withI18n()(({ i18n }) => (
<div>{i18n._(t`Text content`)}</div>
));
const Parent = () => (<Child />);
const wrapper = mountWithContexts(<Parent />);
expect(wrapper.find('Parent')).toMatchSnapshot();
});
});
describe('injected Router', () => {
it('should mount and render', () => {
const wrapper = mountWithContexts(
<div>
<Link to="/">home</Link>
</div>
);
expect(wrapper.find('div')).toMatchSnapshot();
});
it('should mount and render with stubbed context', () => {
const context = {
router: {
history: {
push: jest.fn(),
replace: jest.fn(),
createHref: jest.fn(),
},
route: {
location: {},
match: {}
}
}
};
const wrapper = mountWithContexts(
(
<div>
<Link to="/">home</Link>
</div>
),
{ context }
);
const link = wrapper.find('Link');
expect(link).toHaveLength(1);
link.simulate('click', { button: 0 });
wrapper.update();
expect(context.router.history.push).toHaveBeenCalledWith('/');
});
});
describe('injected ConfigProvider', () => {
it('should mount and render with default values', () => {
const Foo = () => (
<Config>
{value => (
<div>
{value.custom_virtualenvs[0]}
{value.version}
</div>
)}
</Config>
);
const wrapper = mountWithContexts(<Foo />);
expect(wrapper.find('Foo')).toMatchSnapshot();
});
it('should mount and render with custom Config value', () => {
const config = {
custom_virtualenvs: ['Fizz', 'Buzz'],
version: '1.1',
};
const Foo = () => (
<Config>
{value => (
<div>
{value.custom_virtualenvs[0]}
{value.version}
</div>
)}
</Config>
);
const wrapper = mountWithContexts(
<Foo />,
{ context: { config } }
);
expect(wrapper.find('Foo')).toMatchSnapshot();
});
});
});
/**
* This is a fixture for testing async components. It renders a div
* after a short amount of time.
*/
class TestAsyncComponent extends Component {
constructor (props) {
super(props);
this.state = { displayElement: false };
}
componentDidMount () {
setTimeout(() => this.setState({ displayElement: true }), 500);
}
render () {
const { displayElement } = this.state;
if (displayElement) {
return (<div id="test-async-component" />);
}
return null;
}
}
describe('waitForElement', () => {
it('waits for the element and returns it', async (done) => {
const selector = '#test-async-component';
const wrapper = mountWithContexts(<TestAsyncComponent />);
expect(wrapper.exists(selector)).toEqual(false);
const elem = await waitForElement(wrapper, selector);
expect(elem.props().id).toEqual('test-async-component');
expect(wrapper.exists(selector)).toEqual(true);
done();
});
it('eventually throws an error for elements that don\'t exist', async (done) => {
const wrapper = mountWithContexts(<div />);
let error;
try {
await waitForElement(wrapper, '#does-not-exist');
} catch (err) {
error = err;
} finally {
expect(error).toEqual(new Error('Expected condition for <#does-not-exist> not met: el => el.length === 1'));
done();
}
});
});

View File

@@ -1,17 +0,0 @@
import React from 'react';
import { mount } from 'enzyme';
import { MemoryRouter } from 'react-router-dom';
import { main } from '../src/index';
const render = template => mount(
<MemoryRouter>
{template}
</MemoryRouter>
);
describe('index.jsx', () => {
test('index.jsx loads without issue', () => {
const wrapper = main(render);
expect(wrapper.find('RootProvider')).toHaveLength(1);
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import Applications from '../../src/pages/Applications';
describe('<Applications />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<Applications />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import AuthSettings from '../../src/pages/AuthSettings';
describe('<AuthSettings />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<AuthSettings />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import CredentialTypes from '../../src/pages/CredentialTypes';
describe('<CredentialTypes />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<CredentialTypes />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import Credentials from '../../src/pages/Credentials';
describe('<Credentials />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<Credentials />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import Dashboard from '../../src/pages/Dashboard';
describe('<Dashboard />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<Dashboard />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import InstanceGroups from '../../src/pages/InstanceGroups';
describe('<InstanceGroups />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<InstanceGroups />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import Inventories from '../../src/pages/Inventories';
describe('<Inventories />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<Inventories />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import InventoryScripts from '../../src/pages/InventoryScripts';
describe('<InventoryScripts />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<InventoryScripts />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,36 +0,0 @@
import React from 'react';
import { createMemoryHistory } from 'history';
import { mountWithContexts } from '../../enzymeHelpers';
import Jobs from '../../../src/pages/Jobs';
describe('<Jobs />', () => {
test('initially renders succesfully', () => {
mountWithContexts(
<Jobs />
);
});
test('should display a breadcrumb heading', () => {
const history = createMemoryHistory({
initialEntries: ['/jobs'],
});
const match = { path: '/jobs', url: '/jobs', isExact: true };
const wrapper = mountWithContexts(
<Jobs />,
{
context: {
router: {
history,
route: {
location: history.location,
match
}
}
}
}
);
expect(wrapper.find('BreadcrumbHeading').length).toBe(1);
wrapper.unmount();
});
});

View File

@@ -1,9 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../../../../enzymeHelpers';
import { Job } from '../../../../../src/pages/Jobs';
describe('<Job />', () => {
test('initially renders succesfully', () => {
mountWithContexts(<Job />);
});
});

View File

@@ -1,24 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../../../../enzymeHelpers';
import JobDetail from '../../../../../src/pages/Jobs/JobDetail';
describe('<JobDetail />', () => {
const mockDetails = {
name: 'Foo'
};
test('initially renders succesfully', () => {
mountWithContexts(
<JobDetail job={mockDetails} />
);
});
test('should display a Close button', () => {
const wrapper = mountWithContexts(
<JobDetail job={mockDetails} />
);
expect(wrapper.find('Button[aria-label="close"]').length).toBe(1);
wrapper.unmount();
});
});

View File

@@ -1,15 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../../../../enzymeHelpers';
import JobOutput from '../../../../../src/pages/Jobs/JobOutput';
describe('<JobOutput />', () => {
const mockDetails = {
name: 'Foo'
};
test('initially renders succesfully', () => {
mountWithContexts(
<JobOutput job={mockDetails} />
);
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import JobsSettings from '../../src/pages/JobsSettings';
describe('<JobsSettings />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<JobsSettings />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import License from '../../src/pages/License';
describe('<License />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<License />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,215 +0,0 @@
import React from 'react';
import { mountWithContexts, waitForElement } from '../enzymeHelpers';
import AWXLogin from '../../src/pages/Login';
import { RootAPI } from '../../src/api';
jest.mock('../../src/api');
describe('<Login />', () => {
async function findChildren (wrapper) {
const [
awxLogin,
loginPage,
loginForm,
usernameInput,
passwordInput,
submitButton,
loginHeaderLogo,
] = await Promise.all([
waitForElement(wrapper, 'AWXLogin', (el) => el.length === 1),
waitForElement(wrapper, 'LoginPage', (el) => el.length === 1),
waitForElement(wrapper, 'LoginForm', (el) => el.length === 1),
waitForElement(wrapper, 'input#pf-login-username-id', (el) => el.length === 1),
waitForElement(wrapper, 'input#pf-login-password-id', (el) => el.length === 1),
waitForElement(wrapper, 'Button[type="submit"]', (el) => el.length === 1),
waitForElement(wrapper, 'img', (el) => el.length === 1),
]);
return {
awxLogin,
loginPage,
loginForm,
usernameInput,
passwordInput,
submitButton,
loginHeaderLogo,
};
}
beforeEach(() => {
RootAPI.read.mockResolvedValue({
data: {
custom_login_info: '',
custom_logo: 'images/foo.jpg'
}
});
});
afterEach(() => {
jest.clearAllMocks();
});
test('initially renders without crashing', async (done) => {
const loginWrapper = mountWithContexts(
<AWXLogin isAuthenticated={() => false} />
);
const {
awxLogin,
usernameInput,
passwordInput,
submitButton,
} = await findChildren(loginWrapper);
expect(usernameInput.props().value).toBe('');
expect(passwordInput.props().value).toBe('');
expect(awxLogin.state('validationError')).toBe(false);
expect(submitButton.props().isDisabled).toBe(false);
done();
});
test('custom logo renders Brand component with correct src and alt', async (done) => {
const loginWrapper = mountWithContexts(
<AWXLogin alt="Foo Application" isAuthenticated={() => false} />
);
const { loginHeaderLogo } = await findChildren(loginWrapper);
const { alt, src } = loginHeaderLogo.props();
expect([alt, src]).toEqual(['Foo Application', 'data:image/jpeg;images/foo.jpg']);
done();
});
test('default logo renders Brand component with correct src and alt', async (done) => {
RootAPI.read.mockResolvedValue({ data: {} });
const loginWrapper = mountWithContexts(
<AWXLogin isAuthenticated={() => false} />
);
const { loginHeaderLogo } = await findChildren(loginWrapper);
const { alt, src } = loginHeaderLogo.props();
expect([alt, src]).toEqual(['AWX', 'brand-logo.svg']);
done();
});
test('default logo renders on data initialization error', async (done) => {
RootAPI.read.mockRejectedValueOnce({ response: { status: 500 } });
const loginWrapper = mountWithContexts(
<AWXLogin isAuthenticated={() => false} />
);
const { loginHeaderLogo } = await findChildren(loginWrapper);
const { alt, src } = loginHeaderLogo.props();
expect([alt, src]).toEqual(['AWX', 'brand-logo.svg']);
done();
});
test('state maps to un/pw input value props', async (done) => {
const loginWrapper = mountWithContexts(
<AWXLogin isAuthenticated={() => false} />
);
const { usernameInput, passwordInput } = await findChildren(loginWrapper);
usernameInput.props().onChange({ currentTarget: { value: 'un' } });
passwordInput.props().onChange({ currentTarget: { value: 'pw' } });
await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('username') === 'un');
await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('password') === 'pw');
done();
});
test('handles input validation errors and clears on input value change', async (done) => {
const formError = '.pf-c-form__helper-text.pf-m-error';
const loginWrapper = mountWithContexts(
<AWXLogin isAuthenticated={() => false} />
);
const {
usernameInput,
passwordInput,
submitButton
} = await findChildren(loginWrapper);
RootAPI.login.mockRejectedValueOnce({ response: { status: 401 } });
usernameInput.props().onChange({ currentTarget: { value: 'invalid' } });
passwordInput.props().onChange({ currentTarget: { value: 'invalid' } });
submitButton.simulate('click');
await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('username') === 'invalid');
await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('password') === 'invalid');
await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('validationError') === true);
await waitForElement(loginWrapper, formError, (el) => el.length === 1);
usernameInput.props().onChange({ currentTarget: { value: 'dsarif' } });
passwordInput.props().onChange({ currentTarget: { value: 'freneticpny' } });
await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('username') === 'dsarif');
await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('password') === 'freneticpny');
await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('validationError') === false);
await waitForElement(loginWrapper, formError, (el) => el.length === 0);
done();
});
test('handles other errors and clears on resubmit', async (done) => {
const loginWrapper = mountWithContexts(
<AWXLogin isAuthenticated={() => false} />
);
const {
usernameInput,
passwordInput,
submitButton
} = await findChildren(loginWrapper);
RootAPI.login.mockRejectedValueOnce({ response: { status: 500 } });
submitButton.simulate('click');
await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('authenticationError') === true);
usernameInput.props().onChange({ currentTarget: { value: 'sgrimes' } });
passwordInput.props().onChange({ currentTarget: { value: 'ovid' } });
await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('username') === 'sgrimes');
await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('password') === 'ovid');
await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('authenticationError') === true);
submitButton.simulate('click');
await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('authenticationError') === false);
done();
});
test('no login requests are made when already authenticating', async (done) => {
const loginWrapper = mountWithContexts(
<AWXLogin isAuthenticated={() => false} />
);
const { awxLogin, submitButton } = await findChildren(loginWrapper);
awxLogin.setState({ isAuthenticating: true });
submitButton.simulate('click');
submitButton.simulate('click');
expect(RootAPI.login).toHaveBeenCalledTimes(0);
awxLogin.setState({ isAuthenticating: false });
submitButton.simulate('click');
submitButton.simulate('click');
expect(RootAPI.login).toHaveBeenCalledTimes(1);
done();
});
test('submit calls api.login successfully', async (done) => {
const loginWrapper = mountWithContexts(
<AWXLogin isAuthenticated={() => false} />
);
const {
usernameInput,
passwordInput,
submitButton,
} = await findChildren(loginWrapper);
usernameInput.props().onChange({ currentTarget: { value: 'gthorpe' } });
passwordInput.props().onChange({ currentTarget: { value: 'hydro' } });
submitButton.simulate('click');
await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('isAuthenticating') === true);
await waitForElement(loginWrapper, 'AWXLogin', (el) => el.state('isAuthenticating') === false);
expect(RootAPI.login).toHaveBeenCalledTimes(1);
expect(RootAPI.login).toHaveBeenCalledWith('gthorpe', 'hydro');
done();
});
test('render Redirect to / when already authenticated', async (done) => {
const loginWrapper = mountWithContexts(
<AWXLogin isAuthenticated={() => true} />
);
await waitForElement(loginWrapper, 'Redirect', (el) => el.length === 1);
await waitForElement(loginWrapper, 'Redirect', (el) => el.props().to === '/');
done();
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import ManagementJobs from '../../src/pages/ManagementJobs';
describe('<ManagementJobs />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<ManagementJobs />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import NotificationTemplates from '../../src/pages/NotificationTemplates';
describe('<NotificationTemplates />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<NotificationTemplates />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,16 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../../enzymeHelpers';
import Organizations from '../../../src/pages/Organizations/Organizations';
jest.mock('../../../src/api');
describe('<Organizations />', () => {
test('initially renders succesfully', () => {
mountWithContexts(
<Organizations
match={{ path: '/organizations', url: '/organizations' }}
location={{ search: '', pathname: '/organizations' }}
/>
);
});
});

View File

@@ -1,27 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../../../enzymeHelpers';
import DeleteRoleConfirmationModal from '../../../../src/pages/Organizations/components/DeleteRoleConfirmationModal';
const role = {
id: 3,
name: 'Member',
resource_name: 'Org',
resource_type: 'organization',
team_id: 5,
team_name: 'The Team',
};
describe('<DeleteRoleConfirmationModal />', () => {
test('should render initially', () => {
const wrapper = mountWithContexts(
<DeleteRoleConfirmationModal
role={role}
username="jane"
onCancel={() => {}}
onConfirm={() => {}}
/>
);
wrapper.update();
expect(wrapper.find('DeleteRoleConfirmationModal')).toMatchSnapshot();
});
});

View File

@@ -1,37 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../../../enzymeHelpers';
import OrganizationAccessItem from '../../../../src/pages/Organizations/components/OrganizationAccessItem';
const accessRecord = {
id: 2,
username: 'jane',
url: '/bar',
first_name: 'jane',
last_name: 'brown',
summary_fields: {
direct_access: [{
role: {
id: 3,
name: 'Member',
resource_name: 'Org',
resource_type: 'organization',
team_id: 5,
team_name: 'The Team',
user_capabilities: { unattach: true },
}
}],
indirect_access: [],
}
};
describe('<OrganizationAccessItem />', () => {
test('initially renders succesfully', () => {
const wrapper = mountWithContexts(
<OrganizationAccessItem
accessRecord={accessRecord}
onRoleDelete={() => {}}
/>
);
expect(wrapper.find('OrganizationAccessItem')).toMatchSnapshot();
});
});

View File

@@ -1,307 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../../../enzymeHelpers';
import { sleep } from '../../../testUtils';
import OrganizationForm from '../../../../src/pages/Organizations/components/OrganizationForm';
import { OrganizationsAPI } from '../../../../src/api';
jest.mock('../../../../src/api');
describe('<OrganizationForm />', () => {
const network = {};
const meConfig = {
me: {
is_superuser: false
}
};
const mockData = {
id: 1,
name: 'Foo',
description: 'Bar',
max_hosts: 1,
custom_virtualenv: 'Fizz',
related: {
instance_groups: '/api/v2/organizations/1/instance_groups'
}
};
afterEach(() => {
jest.clearAllMocks();
});
test('should request related instance groups from api', () => {
mountWithContexts(
(
<OrganizationForm
organization={mockData}
handleSubmit={jest.fn()}
handleCancel={jest.fn()}
me={meConfig.me}
/>
), {
context: { network },
}
);
expect(OrganizationsAPI.readInstanceGroups).toHaveBeenCalledTimes(1);
});
test('componentDidMount should set instanceGroups to state', async () => {
const mockInstanceGroups = [
{ name: 'One', id: 1 },
{ name: 'Two', id: 2 }
];
OrganizationsAPI.readInstanceGroups.mockReturnValue({
data: {
results: mockInstanceGroups
}
});
const wrapper = mountWithContexts(
(
<OrganizationForm
organization={mockData}
handleSubmit={jest.fn()}
handleCancel={jest.fn()}
me={meConfig.me}
/>
), {
context: { network },
}
);
await sleep(0);
expect(OrganizationsAPI.readInstanceGroups).toHaveBeenCalled();
expect(wrapper.find('OrganizationForm').state().instanceGroups).toEqual(mockInstanceGroups);
});
test('changing instance group successfully sets instanceGroups state', () => {
const wrapper = mountWithContexts(
<OrganizationForm
organization={mockData}
handleSubmit={jest.fn()}
handleCancel={jest.fn()}
me={meConfig.me}
/>
);
const lookup = wrapper.find('InstanceGroupsLookup');
expect(lookup.length).toBe(1);
lookup.prop('onChange')([
{
id: 1,
name: 'foo'
}
], 'instanceGroups');
expect(wrapper.find('OrganizationForm').state().instanceGroups).toEqual([
{
id: 1,
name: 'foo'
}
]);
});
test('changing inputs should update form values', () => {
const wrapper = mountWithContexts(
<OrganizationForm
organization={mockData}
handleSubmit={jest.fn()}
handleCancel={jest.fn()}
me={meConfig.me}
/>
);
const form = wrapper.find('Formik');
wrapper.find('input#org-name').simulate('change', {
target: { value: 'new foo', name: 'name' }
});
expect(form.state('values').name).toEqual('new foo');
wrapper.find('input#org-description').simulate('change', {
target: { value: 'new bar', name: 'description' }
});
expect(form.state('values').description).toEqual('new bar');
wrapper.find('input#org-max_hosts').simulate('change', {
target: { value: '134', name: 'max_hosts' }
});
expect(form.state('values').max_hosts).toEqual('134');
});
test('AnsibleSelect component renders if there are virtual environments', () => {
const config = {
custom_virtualenvs: ['foo', 'bar'],
};
const wrapper = mountWithContexts(
(
<OrganizationForm
organization={mockData}
handleSubmit={jest.fn()}
handleCancel={jest.fn()}
me={meConfig.me}
/>
), {
context: { config },
}
);
expect(wrapper.find('FormSelect')).toHaveLength(1);
expect(wrapper.find('FormSelectOption')).toHaveLength(2);
});
test('calls handleSubmit when form submitted', async () => {
const handleSubmit = jest.fn();
const wrapper = mountWithContexts(
<OrganizationForm
organization={mockData}
handleSubmit={handleSubmit}
handleCancel={jest.fn()}
me={meConfig.me}
/>
);
expect(handleSubmit).not.toHaveBeenCalled();
wrapper.find('button[aria-label="Save"]').simulate('click');
await sleep(1);
expect(handleSubmit).toHaveBeenCalledWith({
name: 'Foo',
description: 'Bar',
max_hosts: 1,
custom_virtualenv: 'Fizz',
}, [], []);
});
test('handleSubmit associates and disassociates instance groups', async () => {
const mockInstanceGroups = [
{ name: 'One', id: 1 },
{ name: 'Two', id: 2 }
];
OrganizationsAPI.readInstanceGroups.mockReturnValue({
data: {
results: mockInstanceGroups
}
});
const mockDataForm = {
name: 'Foo',
description: 'Bar',
max_hosts: 1,
custom_virtualenv: 'Fizz',
};
const handleSubmit = jest.fn();
OrganizationsAPI.update.mockResolvedValue(1, mockDataForm);
OrganizationsAPI.associateInstanceGroup.mockResolvedValue('done');
OrganizationsAPI.disassociateInstanceGroup.mockResolvedValue('done');
const wrapper = mountWithContexts(
(
<OrganizationForm
organization={mockData}
handleSubmit={handleSubmit}
handleCancel={jest.fn()}
me={meConfig.me}
/>
), {
context: { network }
}
);
await sleep(0);
wrapper.find('InstanceGroupsLookup').prop('onChange')([
{ name: 'One', id: 1 },
{ name: 'Three', id: 3 }
], 'instanceGroups');
wrapper.find('button[aria-label="Save"]').simulate('click');
await sleep(0);
expect(handleSubmit).toHaveBeenCalledWith(mockDataForm, [3], [2]);
});
test('handleSubmit is called with max_hosts value if it is in range', async () => {
const handleSubmit = jest.fn();
// normal mount
const wrapper = mountWithContexts(
<OrganizationForm
organization={mockData}
handleSubmit={handleSubmit}
handleCancel={jest.fn()}
me={meConfig.me}
/>
);
wrapper.find('button[aria-label="Save"]').simulate('click');
await sleep(0);
expect(handleSubmit).toHaveBeenCalledWith({
name: 'Foo',
description: 'Bar',
max_hosts: 1,
custom_virtualenv: 'Fizz',
}, [], []);
});
test('handleSubmit does not get called if max_hosts value is out of range', async () => {
const handleSubmit = jest.fn();
// not mount with Negative value
const mockDataNegative = JSON.parse(JSON.stringify(mockData));
mockDataNegative.max_hosts = -5;
const wrapper1 = mountWithContexts(
<OrganizationForm
organization={mockDataNegative}
handleSubmit={handleSubmit}
handleCancel={jest.fn()}
me={meConfig.me}
/>
);
wrapper1.find('button[aria-label="Save"]').simulate('click');
await sleep(0);
expect(handleSubmit).not.toHaveBeenCalled();
// not mount with Out of Range value
const mockDataOoR = JSON.parse(JSON.stringify(mockData));
mockDataOoR.max_hosts = 999999999999;
const wrapper2 = mountWithContexts(
<OrganizationForm
organization={mockDataOoR}
handleSubmit={handleSubmit}
handleCancel={jest.fn()}
me={meConfig.me}
/>
);
wrapper2.find('button[aria-label="Save"]').simulate('click');
await sleep(0);
expect(handleSubmit).not.toHaveBeenCalled();
});
test('handleSubmit is called and max_hosts value defaults to 0 if input is not a number', async () => {
const handleSubmit = jest.fn();
// mount with String value (default to zero)
const mockDataString = JSON.parse(JSON.stringify(mockData));
mockDataString.max_hosts = 'Bee';
const wrapper = mountWithContexts(
<OrganizationForm
organization={mockDataString}
handleSubmit={handleSubmit}
handleCancel={jest.fn()}
me={meConfig.me}
/>
);
wrapper.find('button[aria-label="Save"]').simulate('click');
await sleep(0);
expect(handleSubmit).toHaveBeenCalledWith({
name: 'Foo',
description: 'Bar',
max_hosts: 0,
custom_virtualenv: 'Fizz',
}, [], []);
});
test('calls "handleCancel" when Cancel button is clicked', () => {
const handleCancel = jest.fn();
const wrapper = mountWithContexts(
<OrganizationForm
organization={mockData}
handleSubmit={jest.fn()}
handleCancel={handleCancel}
me={meConfig.me}
/>
);
expect(handleCancel).not.toHaveBeenCalled();
wrapper.find('button[aria-label="Cancel"]').prop('onClick')();
expect(handleCancel).toBeCalled();
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { I18nProvider } from '@lingui/react';
import { mountWithContexts } from '../../../enzymeHelpers';
import OrganizationListItem from '../../../../src/pages/Organizations/components/OrganizationListItem';
describe('<OrganizationListItem />', () => {
test('initially renders succesfully', () => {
mountWithContexts(
<I18nProvider>
<MemoryRouter initialEntries={['/organizations']} initialIndex={0}>
<OrganizationListItem
organization={{
id: 1,
name: 'Org',
summary_fields: { related_field_counts: {
users: 1,
teams: 1,
} }
}}
detailUrl="/organization/1"
isSelected
onSelect={() => {}}
/>
</MemoryRouter>
</I18nProvider>
);
});
});

View File

@@ -1,483 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
<DeleteRoleConfirmationModal
i18n={"/i18n/"}
onCancel={[Function]}
onConfirm={[Function]}
role={
Object {
"id": 3,
"name": "Member",
"resource_name": "Org",
"resource_type": "organization",
"team_id": 5,
"team_name": "The Team",
}
}
username="jane"
>
<_default
actions={
Array [
<Button
aria-label="Confirm delete"
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
isInline={false}
onClick={[Function]}
type="button"
variant="danger"
>
Delete
</Button>,
<Button
aria-label={null}
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
isInline={false}
onClick={[Function]}
type="button"
variant="secondary"
>
Cancel
</Button>,
]
}
isOpen={true}
onClose={[Function]}
title="Remove {0} Access"
variant="danger"
>
<Modal
actions={
Array [
<Button
aria-label="Confirm delete"
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
isInline={false}
onClick={[Function]}
type="button"
variant="danger"
>
Delete
</Button>,
<Button
aria-label={null}
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
isInline={false}
onClick={[Function]}
type="button"
variant="secondary"
>
Cancel
</Button>,
]
}
ariaDescribedById=""
className="awx-c-modal at-c-alertModal at-c-alertModal--danger"
hideTitle={false}
isLarge={false}
isOpen={true}
isSmall={false}
onClose={[Function]}
title="Remove {0} Access"
width={null}
>
<Portal
containerInfo={
<div>
<div
class="pf-c-backdrop"
>
<div
class="pf-l-bullseye"
>
<div
aria-describedby="pf-modal-0"
aria-label="Remove {0} Access"
aria-modal="true"
class="pf-c-modal-box awx-c-modal at-c-alertModal at-c-alertModal--danger"
role="dialog"
>
<button
aria-label="Close"
class="pf-c-button pf-m-plain"
type="button"
>
<svg
aria-hidden="true"
fill="currentColor"
height="1em"
role="img"
style="vertical-align: -0.125em;"
viewBox="0 0 352 512"
width="1em"
>
<path
d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"
transform=""
/>
</svg>
</button>
<h3
class="pf-c-title pf-m-2xl"
>
Remove {0} Access
</h3>
<div
class="pf-c-modal-box__body"
id="pf-modal-0"
>
Are you sure you want to remove {0} access from {1}? Doing so affects all members of the team.
<br />
<br />
If you {0} want to remove access for this particular user, please remove them from the team.
<svg
aria-hidden="true"
class="at-c-alertModal__icon"
fill="currentColor"
height="1em"
role="img"
style="vertical-align: -0.125em;"
viewBox="0 0 512 512"
width="1em"
>
<path
d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
transform=""
/>
</svg>
</div>
<div
class="pf-c-modal-box__footer"
>
<button
aria-label="Confirm delete"
class="pf-c-button pf-m-danger"
type="button"
>
Delete
</button>
<button
class="pf-c-button pf-m-secondary"
type="button"
>
Cancel
</button>
</div>
</div>
</div>
</div>
</div>
}
>
<ModalContent
actions={
Array [
<Button
aria-label="Confirm delete"
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
isInline={false}
onClick={[Function]}
type="button"
variant="danger"
>
Delete
</Button>,
<Button
aria-label={null}
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
isInline={false}
onClick={[Function]}
type="button"
variant="secondary"
>
Cancel
</Button>,
]
}
ariaDescribedById=""
className="awx-c-modal at-c-alertModal at-c-alertModal--danger"
hideTitle={false}
id="pf-modal-0"
isLarge={false}
isOpen={true}
isSmall={false}
onClose={[Function]}
title="Remove {0} Access"
width={null}
>
<Backdrop
className=""
>
<div
className="pf-c-backdrop"
>
<FocusTrap
_createFocusTrap={[Function]}
active={true}
className="pf-l-bullseye"
focusTrapOptions={
Object {
"clickOutsideDeactivates": true,
}
}
paused={false}
tag="div"
>
<div
className="pf-l-bullseye"
>
<ModalBox
className="awx-c-modal at-c-alertModal at-c-alertModal--danger"
id="pf-modal-0"
isLarge={false}
isSmall={false}
style={
Object {
"width": null,
}
}
title="Remove {0} Access"
>
<div
aria-describedby="pf-modal-0"
aria-label="Remove {0} Access"
aria-modal="true"
className="pf-c-modal-box awx-c-modal at-c-alertModal at-c-alertModal--danger"
role="dialog"
style={
Object {
"width": null,
}
}
>
<ModalBoxCloseButton
className=""
onClose={[Function]}
>
<Button
aria-label="Close"
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
isInline={false}
onClick={[Function]}
type="button"
variant="plain"
>
<button
aria-disabled={null}
aria-label="Close"
className="pf-c-button pf-m-plain"
disabled={false}
onClick={[Function]}
tabIndex={null}
type="button"
>
<TimesIcon
color="currentColor"
size="sm"
title={null}
>
<svg
aria-hidden={true}
aria-labelledby={null}
fill="currentColor"
height="1em"
role="img"
style={
Object {
"verticalAlign": "-0.125em",
}
}
viewBox="0 0 352 512"
width="1em"
>
<path
d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"
transform=""
/>
</svg>
</TimesIcon>
</button>
</Button>
</ModalBoxCloseButton>
<ModalBoxHeader
className=""
hideTitle={false}
>
<Title
className=""
headingLevel="h3"
size="2xl"
>
<h3
className="pf-c-title pf-m-2xl"
>
Remove {0} Access
</h3>
</Title>
</ModalBoxHeader>
<ModalBoxBody
className=""
id="pf-modal-0"
>
<div
className="pf-c-modal-box__body"
id="pf-modal-0"
>
Are you sure you want to remove {0} access from {1}? Doing so affects all members of the team.
<br />
<br />
If you {0} want to remove access for this particular user, please remove them from the team.
<ExclamationCircleIcon
className="at-c-alertModal__icon"
color="currentColor"
size="sm"
title={null}
>
<svg
aria-hidden={true}
aria-labelledby={null}
className="at-c-alertModal__icon"
fill="currentColor"
height="1em"
role="img"
style={
Object {
"verticalAlign": "-0.125em",
}
}
viewBox="0 0 512 512"
width="1em"
>
<path
d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
transform=""
/>
</svg>
</ExclamationCircleIcon>
</div>
</ModalBoxBody>
<ModalBoxFooter
className=""
>
<div
className="pf-c-modal-box__footer"
>
<Button
aria-label="Confirm delete"
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
isInline={false}
key="delete"
onClick={[Function]}
type="button"
variant="danger"
>
<button
aria-disabled={null}
aria-label="Confirm delete"
className="pf-c-button pf-m-danger"
disabled={false}
onClick={[Function]}
tabIndex={null}
type="button"
>
Delete
</button>
</Button>
<Button
aria-label={null}
className=""
component="button"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
isInline={false}
key="cancel"
onClick={[Function]}
type="button"
variant="secondary"
>
<button
aria-disabled={null}
aria-label={null}
className="pf-c-button pf-m-secondary"
disabled={false}
onClick={[Function]}
tabIndex={null}
type="button"
>
Cancel
</button>
</Button>
</div>
</ModalBoxFooter>
</div>
</ModalBox>
</div>
</FocusTrap>
</div>
</Backdrop>
</ModalContent>
</Portal>
</Modal>
</_default>
</DeleteRoleConfirmationModal>
`;

View File

@@ -1,872 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<OrganizationAccessItem /> initially renders succesfully 1`] = `
<OrganizationAccessItem
accessRecord={
Object {
"first_name": "jane",
"id": 2,
"last_name": "brown",
"summary_fields": Object {
"direct_access": Array [
Object {
"role": Object {
"id": 3,
"name": "Member",
"resource_name": "Org",
"resource_type": "organization",
"team_id": 5,
"team_name": "The Team",
"user_capabilities": Object {
"unattach": true,
},
},
},
],
"indirect_access": Array [],
},
"url": "/bar",
"username": "jane",
}
}
i18n={"/i18n/"}
onRoleDelete={[Function]}
>
<DataListItem
aria-labelledby="access-list-item"
className=""
isExpanded={false}
key="2"
>
<li
aria-labelledby="access-list-item"
className="pf-c-data-list__item"
>
<DataListItemRow
className=""
key=".0"
rowid="access-list-item"
>
<div
className="pf-c-data-list__item-row"
>
<OrganizationAccessItem__DataListItemCells
dataListCells={
Array [
<DataListCell
alignRight={false}
className=""
isFilled={true}
isIcon={false}
width={1}
>
<TextContent
className=""
>
<Text
className=""
component="h6"
>
<ForwardRef
to={
Object {
"pathname": "/bar",
}
}
>
jane
</ForwardRef>
</Text>
</TextContent>
<ForwardRef
stacked={true}
>
<Detail
fullWidth={false}
label="Name"
value="jane brown"
/>
</ForwardRef>
</DataListCell>,
<DataListCell
alignRight={false}
className=""
isFilled={true}
isIcon={false}
width={1}
>
<ForwardRef
stacked={true}
>
<Detail
fullWidth={false}
label="Team Roles"
value={
<ForwardRef>
<ForwardRef
isReadOnly={false}
onClick={[Function]}
>
Member
</ForwardRef>
</ForwardRef>
}
/>
</ForwardRef>
</DataListCell>,
]
}
key=".0"
rowid="access-list-item"
>
<StyledComponent
dataListCells={
Array [
<DataListCell
alignRight={false}
className=""
isFilled={true}
isIcon={false}
width={1}
>
<TextContent
className=""
>
<Text
className=""
component="h6"
>
<ForwardRef
to={
Object {
"pathname": "/bar",
}
}
>
jane
</ForwardRef>
</Text>
</TextContent>
<ForwardRef
stacked={true}
>
<Detail
fullWidth={false}
label="Name"
value="jane brown"
/>
</ForwardRef>
</DataListCell>,
<DataListCell
alignRight={false}
className=""
isFilled={true}
isIcon={false}
width={1}
>
<ForwardRef
stacked={true}
>
<Detail
fullWidth={false}
label="Team Roles"
value={
<ForwardRef>
<ForwardRef
isReadOnly={false}
onClick={[Function]}
>
Member
</ForwardRef>
</ForwardRef>
}
/>
</ForwardRef>
</DataListCell>,
]
}
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "OrganizationAccessItem__DataListItemCells-sc-1yema4k-0",
"isStatic": true,
"lastClassName": "OJmEc",
"rules": Array [
"align-items:start;",
],
},
"displayName": "OrganizationAccessItem__DataListItemCells",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "OrganizationAccessItem__DataListItemCells-sc-1yema4k-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
rowid="access-list-item"
>
<DataListItemCells
className="OrganizationAccessItem__DataListItemCells-sc-1yema4k-0 OJmEc"
dataListCells={
Array [
<DataListCell
alignRight={false}
className=""
isFilled={true}
isIcon={false}
width={1}
>
<TextContent
className=""
>
<Text
className=""
component="h6"
>
<ForwardRef
to={
Object {
"pathname": "/bar",
}
}
>
jane
</ForwardRef>
</Text>
</TextContent>
<ForwardRef
stacked={true}
>
<Detail
fullWidth={false}
label="Name"
value="jane brown"
/>
</ForwardRef>
</DataListCell>,
<DataListCell
alignRight={false}
className=""
isFilled={true}
isIcon={false}
width={1}
>
<ForwardRef
stacked={true}
>
<Detail
fullWidth={false}
label="Team Roles"
value={
<ForwardRef>
<ForwardRef
isReadOnly={false}
onClick={[Function]}
>
Member
</ForwardRef>
</ForwardRef>
}
/>
</ForwardRef>
</DataListCell>,
]
}
rowid="access-list-item"
>
<div
className="pf-c-data-list__item-content OrganizationAccessItem__DataListItemCells-sc-1yema4k-0 OJmEc"
>
<DataListCell
alignRight={false}
className=""
isFilled={true}
isIcon={false}
key="name"
width={1}
>
<div
className="pf-c-data-list__cell"
>
<TextContent
className=""
>
<div
className="pf-c-content"
>
<Text
className=""
component="h6"
>
<h6
className=""
data-pf-content={true}
>
<Styled(Link)
to={
Object {
"pathname": "/bar",
}
}
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "sc-bdVaJa",
"isStatic": true,
"lastClassName": "fqQVUT",
"rules": Array [
"font-weight: bold",
],
},
"displayName": "Styled(Link)",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "sc-bdVaJa",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
to={
Object {
"pathname": "/bar",
}
}
>
<Link
className="sc-bdVaJa fqQVUT"
replace={false}
to={
Object {
"pathname": "/bar",
}
}
>
<a
className="sc-bdVaJa fqQVUT"
onClick={[Function]}
>
jane
</a>
</Link>
</StyledComponent>
</Styled(Link)>
</h6>
</Text>
</div>
</TextContent>
<DetailList
stacked={true}
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "DetailList-sc-12g7m4-0",
"isStatic": false,
"lastClassName": "hndBXy",
"rules": Array [
"display:grid;grid-gap:20px;",
[Function],
],
},
"displayName": "DetailList",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "DetailList-sc-12g7m4-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
stacked={true}
>
<DetailList
className="DetailList-sc-12g7m4-0 hndBXy"
stacked={true}
>
<TextList
className="DetailList-sc-12g7m4-0 hndBXy"
component="dl"
>
<dl
className="DetailList-sc-12g7m4-0 hndBXy"
data-pf-content={true}
>
<Detail
fullWidth={false}
label="Name"
value="jane brown"
>
<Detail__DetailName
component="dt"
fullWidth={false}
>
<StyledComponent
component="dt"
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "Detail__DetailName-sc-16ypsyv-0",
"isStatic": false,
"lastClassName": "gRioSK",
"rules": Array [
"font-weight:var(--pf-global--FontWeight--bold);text-align:right;",
[Function],
],
},
"displayName": "Detail__DetailName",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "Detail__DetailName-sc-16ypsyv-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
fullWidth={false}
>
<Component
className="Detail__DetailName-sc-16ypsyv-0 gRioSK"
component="dt"
fullWidth={false}
>
<TextListItem
className="Detail__DetailName-sc-16ypsyv-0 gRioSK"
component="dt"
>
<dt
className="Detail__DetailName-sc-16ypsyv-0 gRioSK"
data-pf-content={true}
>
Name
</dt>
</TextListItem>
</Component>
</StyledComponent>
</Detail__DetailName>
<Detail__DetailValue
component="dd"
fullWidth={false}
>
<StyledComponent
component="dd"
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "Detail__DetailValue-sc-16ypsyv-1",
"isStatic": false,
"lastClassName": "yHlYM",
"rules": Array [
"word-break:break-all;",
[Function],
],
},
"displayName": "Detail__DetailValue",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "Detail__DetailValue-sc-16ypsyv-1",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
fullWidth={false}
>
<Component
className="Detail__DetailValue-sc-16ypsyv-1 yHlYM"
component="dd"
fullWidth={false}
>
<TextListItem
className="Detail__DetailValue-sc-16ypsyv-1 yHlYM"
component="dd"
>
<dd
className="Detail__DetailValue-sc-16ypsyv-1 yHlYM"
data-pf-content={true}
>
jane brown
</dd>
</TextListItem>
</Component>
</StyledComponent>
</Detail__DetailValue>
</Detail>
</dl>
</TextList>
</DetailList>
</StyledComponent>
</DetailList>
</div>
</DataListCell>
<DataListCell
alignRight={false}
className=""
isFilled={true}
isIcon={false}
key="roles"
width={1}
>
<div
className="pf-c-data-list__cell"
>
<DetailList
stacked={true}
>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "DetailList-sc-12g7m4-0",
"isStatic": false,
"lastClassName": "hndBXy",
"rules": Array [
"display:grid;grid-gap:20px;",
[Function],
],
},
"displayName": "DetailList",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "DetailList-sc-12g7m4-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
stacked={true}
>
<DetailList
className="DetailList-sc-12g7m4-0 hndBXy"
stacked={true}
>
<TextList
className="DetailList-sc-12g7m4-0 hndBXy"
component="dl"
>
<dl
className="DetailList-sc-12g7m4-0 hndBXy"
data-pf-content={true}
>
<Detail
fullWidth={false}
label="Team Roles"
value={
<ForwardRef>
<ForwardRef
isReadOnly={false}
onClick={[Function]}
>
Member
</ForwardRef>
</ForwardRef>
}
>
<Detail__DetailName
component="dt"
fullWidth={false}
>
<StyledComponent
component="dt"
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "Detail__DetailName-sc-16ypsyv-0",
"isStatic": false,
"lastClassName": "gRioSK",
"rules": Array [
"font-weight:var(--pf-global--FontWeight--bold);text-align:right;",
[Function],
],
},
"displayName": "Detail__DetailName",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "Detail__DetailName-sc-16ypsyv-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
fullWidth={false}
>
<Component
className="Detail__DetailName-sc-16ypsyv-0 gRioSK"
component="dt"
fullWidth={false}
>
<TextListItem
className="Detail__DetailName-sc-16ypsyv-0 gRioSK"
component="dt"
>
<dt
className="Detail__DetailName-sc-16ypsyv-0 gRioSK"
data-pf-content={true}
>
Team Roles
</dt>
</TextListItem>
</Component>
</StyledComponent>
</Detail__DetailName>
<Detail__DetailValue
component="dd"
fullWidth={false}
>
<StyledComponent
component="dd"
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "Detail__DetailValue-sc-16ypsyv-1",
"isStatic": false,
"lastClassName": "yHlYM",
"rules": Array [
"word-break:break-all;",
[Function],
],
},
"displayName": "Detail__DetailValue",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "Detail__DetailValue-sc-16ypsyv-1",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
fullWidth={false}
>
<Component
className="Detail__DetailValue-sc-16ypsyv-1 yHlYM"
component="dd"
fullWidth={false}
>
<TextListItem
className="Detail__DetailValue-sc-16ypsyv-1 yHlYM"
component="dd"
>
<dd
className="Detail__DetailValue-sc-16ypsyv-1 yHlYM"
data-pf-content={true}
>
<ChipGroup>
<StyledComponent
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "ChipGroup-sc-10zu8t0-0",
"isStatic": true,
"lastClassName": "vIGxT",
"rules": Array [
"--pf-c-chip-group--c-chip--MarginRight:10px;--pf-c-chip-group--c-chip--MarginBottom:10px;",
],
},
"displayName": "ChipGroup",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "ChipGroup-sc-10zu8t0-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
>
<ChipGroup
className="ChipGroup-sc-10zu8t0-0 vIGxT"
showOverflowAfter={null}
>
<ul
className="pf-c-chip-group ChipGroup-sc-10zu8t0-0 vIGxT"
>
<Chip
component="li"
isReadOnly={false}
key=".$3"
onClick={[Function]}
>
<StyledComponent
component="li"
forwardedComponent={
Object {
"$$typeof": Symbol(react.forward_ref),
"attrs": Array [],
"componentStyle": ComponentStyle {
"componentId": "Chip-sc-1rzr8oo-0",
"isStatic": false,
"lastClassName": "dgUGLg",
"rules": Array [
"--pf-c-chip--m-read-only--PaddingTop:3px;--pf-c-chip--m-read-only--PaddingRight:8px;--pf-c-chip--m-read-only--PaddingBottom:3px;--pf-c-chip--m-read-only--PaddingLeft:8px;& > .pf-c-button{padding:3px 8px;}",
[Function],
],
},
"displayName": "Chip",
"foldedComponentIds": Array [],
"render": [Function],
"styledComponentId": "Chip-sc-1rzr8oo-0",
"target": [Function],
"toString": [Function],
"warnTooManyClasses": [Function],
"withComponent": [Function],
}
}
forwardedRef={null}
isReadOnly={false}
onClick={[Function]}
>
<Chip
className="Chip-sc-1rzr8oo-0 dgUGLg"
closeBtnAriaLabel="close"
component="li"
isOverflowChip={false}
isReadOnly={false}
onClick={[Function]}
tooltipPosition="top"
>
<GenerateId
prefix="pf-random-id-"
>
<li
className="pf-c-chip Chip-sc-1rzr8oo-0 dgUGLg"
>
<span
className="pf-c-chip__text"
id="pf-random-id-0"
>
Member
</span>
<ChipButton
aria-labelledby="remove_pf-random-id-0 pf-random-id-0"
ariaLabel="close"
className=""
id="remove_pf-random-id-0"
onClick={[Function]}
>
<Button
aria-label="close"
aria-labelledby="remove_pf-random-id-0 pf-random-id-0"
className=""
component="button"
id="remove_pf-random-id-0"
isActive={false}
isBlock={false}
isDisabled={false}
isFocus={false}
isHover={false}
isInline={false}
onClick={[Function]}
type="button"
variant="plain"
>
<button
aria-disabled={null}
aria-label="close"
aria-labelledby="remove_pf-random-id-0 pf-random-id-0"
className="pf-c-button pf-m-plain"
disabled={false}
id="remove_pf-random-id-0"
onClick={[Function]}
tabIndex={null}
type="button"
>
<TimesCircleIcon
aria-hidden="true"
color="currentColor"
size="sm"
title={null}
>
<svg
aria-hidden="true"
aria-labelledby={null}
fill="currentColor"
height="1em"
role="img"
style={
Object {
"verticalAlign": "-0.125em",
}
}
viewBox="0 0 512 512"
width="1em"
>
<path
d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"
transform=""
/>
</svg>
</TimesCircleIcon>
</button>
</Button>
</ChipButton>
</li>
</GenerateId>
</Chip>
</StyledComponent>
</Chip>
</ul>
</ChipGroup>
</StyledComponent>
</ChipGroup>
</dd>
</TextListItem>
</Component>
</StyledComponent>
</Detail__DetailValue>
</Detail>
</dl>
</TextList>
</DetailList>
</StyledComponent>
</DetailList>
</div>
</DataListCell>
</div>
</DataListItemCells>
</StyledComponent>
</OrganizationAccessItem__DataListItemCells>
</div>
</DataListItemRow>
</li>
</DataListItem>
</OrganizationAccessItem>
`;

View File

@@ -1,232 +0,0 @@
import React from 'react';
import { mountWithContexts, waitForElement } from '../../../../enzymeHelpers';
import Organization from '../../../../../src/pages/Organizations/screens/Organization/Organization';
import { OrganizationsAPI } from '../../../../../src/api';
jest.mock('../../../../../src/api');
const mockMe = {
is_super_user: true,
is_system_auditor: false
};
const mockNoResults = {
count: 0,
next: null,
previous: null,
data: { results: [] }
};
const mockDetails = {
data: {
id: 1,
type: 'organization',
url: '/api/v2/organizations/1/',
related: {
notification_templates: '/api/v2/organizations/1/notification_templates/',
notification_templates_any: '/api/v2/organizations/1/notification_templates_any/',
notification_templates_success: '/api/v2/organizations/1/notification_templates_success/',
notification_templates_error: '/api/v2/organizations/1/notification_templates_error/',
object_roles: '/api/v2/organizations/1/object_roles/',
access_list: '/api/v2/organizations/1/access_list/',
instance_groups: '/api/v2/organizations/1/instance_groups/'
},
summary_fields: {
created_by: {
id: 1,
username: 'admin',
first_name: 'Super',
last_name: 'User'
},
modified_by: {
id: 1,
username: 'admin',
first_name: 'Super',
last_name: 'User'
},
object_roles: {
admin_role: {
description: 'Can manage all aspects of the organization',
name: 'Admin',
id: 42
},
notification_admin_role: {
description: 'Can manage all notifications of the organization',
name: 'Notification Admin',
id: 1683
},
auditor_role: {
description: 'Can view all aspects of the organization',
name: 'Auditor',
id: 41
},
},
user_capabilities: {
edit: true,
delete: true
},
related_field_counts: {
users: 51,
admins: 19,
inventories: 23,
teams: 12,
projects: 33,
job_templates: 30
}
},
created: '2015-07-07T17:21:26.429745Z',
modified: '2017-09-05T19:23:15.418808Z',
name: 'Sarif Industries',
description: '',
max_hosts: 0,
custom_virtualenv: null
}
};
const adminOrganization = {
id: 1,
type: 'organization',
url: '/api/v2/organizations/1/',
related: {
instance_groups: '/api/v2/organizations/1/instance_groups/',
object_roles: '/api/v2/organizations/1/object_roles/',
access_list: '/api/v2/organizations/1/access_list/',
},
summary_fields: {
created_by: {
id: 1,
username: 'admin',
first_name: 'Super',
last_name: 'User'
},
modified_by: {
id: 1,
username: 'admin',
first_name: 'Super',
last_name: 'User'
},
},
created: '2015-07-07T17:21:26.429745Z',
modified: '2017-09-05T19:23:15.418808Z',
name: 'Sarif Industries',
description: '',
max_hosts: 0,
custom_virtualenv: null
};
const auditorOrganization = {
id: 2,
type: 'organization',
url: '/api/v2/organizations/2/',
related: {
instance_groups: '/api/v2/organizations/2/instance_groups/',
object_roles: '/api/v2/organizations/2/object_roles/',
access_list: '/api/v2/organizations/2/access_list/',
},
summary_fields: {
created_by: {
id: 2,
username: 'admin',
first_name: 'Super',
last_name: 'User'
},
modified_by: {
id: 2,
username: 'admin',
first_name: 'Super',
last_name: 'User'
},
},
created: '2015-07-07T17:21:26.429745Z',
modified: '2017-09-05T19:23:15.418808Z',
name: 'Autobots',
description: '',
max_hosts: 0,
custom_virtualenv: null
};
const notificationAdminOrganization = {
id: 3,
type: 'organization',
url: '/api/v2/organizations/3/',
related: {
instance_groups: '/api/v2/organizations/3/instance_groups/',
object_roles: '/api/v2/organizations/3/object_roles/',
access_list: '/api/v2/organizations/3/access_list/',
},
summary_fields: {
created_by: {
id: 1,
username: 'admin',
first_name: 'Super',
last_name: 'User'
},
modified_by: {
id: 1,
username: 'admin',
first_name: 'Super',
last_name: 'User'
},
},
created: '2015-07-07T17:21:26.429745Z',
modified: '2017-09-05T19:23:15.418808Z',
name: 'Decepticons',
description: '',
max_hosts: 0,
custom_virtualenv: null
};
const allOrganizations = [
adminOrganization,
auditorOrganization,
notificationAdminOrganization
];
async function getOrganizations (params) {
let results = allOrganizations;
if (params && params.role_level) {
if (params.role_level === 'admin_role') {
results = [adminOrganization];
}
if (params.role_level === 'auditor_role') {
results = [auditorOrganization];
}
if (params.role_level === 'notification_admin_role') {
results = [notificationAdminOrganization];
}
}
return {
count: results.length,
next: null,
previous: null,
data: { results }
};
}
describe.only('<Organization />', () => {
test('initially renders succesfully', () => {
OrganizationsAPI.readDetail.mockResolvedValue(mockDetails);
OrganizationsAPI.read.mockImplementation(getOrganizations);
mountWithContexts(<Organization setBreadcrumb={() => {}} me={mockMe} />);
});
test('notifications tab shown for admins', async (done) => {
OrganizationsAPI.readDetail.mockResolvedValue(mockDetails);
OrganizationsAPI.read.mockImplementation(getOrganizations);
const wrapper = mountWithContexts(<Organization setBreadcrumb={() => {}} me={mockMe} />);
const tabs = await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 4);
expect(tabs.last().text()).toEqual('Notifications');
done();
});
test('notifications tab hidden with reduced permissions', async (done) => {
OrganizationsAPI.readDetail.mockResolvedValue(mockDetails);
OrganizationsAPI.read.mockResolvedValue(mockNoResults);
const wrapper = mountWithContexts(<Organization setBreadcrumb={() => {}} me={mockMe} />);
const tabs = await waitForElement(wrapper, '.pf-c-tabs__item', el => el.length === 3);
tabs.forEach(tab => expect(tab.text()).not.toEqual('Notifications'));
done();
});
});

View File

@@ -1,162 +0,0 @@
import React from 'react';
import { mountWithContexts, waitForElement } from '../../../../enzymeHelpers';
import OrganizationAccess from '../../../../../src/pages/Organizations/screens/Organization/OrganizationAccess';
import { sleep } from '../../../../testUtils';
import { OrganizationsAPI, TeamsAPI, UsersAPI } from '../../../../../src/api';
jest.mock('../../../../../src/api');
describe('<OrganizationAccess />', () => {
const organization = {
id: 1,
name: 'Default',
summary_fields: {
object_roles: {},
user_capabilities: {
edit: true
}
}
};
const data = {
count: 2,
results: [{
id: 1,
username: 'joe',
url: '/foo',
first_name: 'joe',
last_name: 'smith',
summary_fields: {
direct_access: [{
role: {
id: 1,
name: 'Member',
resource_name: 'Org',
resource_type: 'organization',
user_capabilities: { unattach: true },
}
}],
indirect_access: [],
}
}, {
id: 2,
username: 'jane',
url: '/bar',
first_name: 'jane',
last_name: 'brown',
summary_fields: {
direct_access: [{
role: {
id: 3,
name: 'Member',
resource_name: 'Org',
resource_type: 'organization',
team_id: 5,
team_name: 'The Team',
user_capabilities: { unattach: true },
}
}],
indirect_access: [],
}
}]
};
beforeEach(() => {
OrganizationsAPI.readAccessList.mockResolvedValue({ data });
TeamsAPI.disassociateRole.mockResolvedValue({});
UsersAPI.disassociateRole.mockResolvedValue({});
});
afterEach(() => {
jest.clearAllMocks();
});
test('initially renders succesfully', () => {
const wrapper = mountWithContexts(<OrganizationAccess organization={organization} />);
expect(wrapper.find('OrganizationAccess')).toMatchSnapshot();
});
test('should fetch and display access records on mount', async (done) => {
const wrapper = mountWithContexts(<OrganizationAccess organization={organization} />);
await waitForElement(wrapper, 'OrganizationAccessItem', el => el.length === 2);
expect(wrapper.find('PaginatedDataList').prop('items')).toEqual(data.results);
expect(wrapper.find('OrganizationAccess').state('contentLoading')).toBe(false);
expect(wrapper.find('OrganizationAccess').state('contentError')).toBe(false);
done();
});
test('should open confirmation dialog when deleting role', async (done) => {
const wrapper = mountWithContexts(<OrganizationAccess organization={organization} />);
await sleep(0);
wrapper.update();
const button = wrapper.find('ChipButton').at(0);
button.prop('onClick')();
wrapper.update();
const component = wrapper.find('OrganizationAccess');
expect(component.state('deletionRole'))
.toEqual(data.results[0].summary_fields.direct_access[0].role);
expect(component.state('deletionRecord'))
.toEqual(data.results[0]);
expect(component.find('DeleteRoleConfirmationModal')).toHaveLength(1);
done();
});
it('should close dialog when cancel button clicked', async (done) => {
const wrapper = mountWithContexts(<OrganizationAccess organization={organization} />);
await sleep(0);
wrapper.update();
const button = wrapper.find('ChipButton').at(0);
button.prop('onClick')();
wrapper.update();
wrapper.find('DeleteRoleConfirmationModal').prop('onCancel')();
const component = wrapper.find('OrganizationAccess');
expect(component.state('deletionRole')).toBeNull();
expect(component.state('deletionRecord')).toBeNull();
expect(TeamsAPI.disassociateRole).not.toHaveBeenCalled();
expect(UsersAPI.disassociateRole).not.toHaveBeenCalled();
done();
});
it('should delete user role', async (done) => {
const wrapper = mountWithContexts(<OrganizationAccess organization={organization} />);
const button = await waitForElement(wrapper, 'ChipButton', el => el.length === 2);
button.at(0).prop('onClick')();
const confirmation = await waitForElement(wrapper, 'DeleteRoleConfirmationModal');
confirmation.prop('onConfirm')();
await waitForElement(wrapper, 'DeleteRoleConfirmationModal', el => el.length === 0);
await sleep(0);
wrapper.update();
const component = wrapper.find('OrganizationAccess');
expect(component.state('deletionRole')).toBeNull();
expect(component.state('deletionRecord')).toBeNull();
expect(TeamsAPI.disassociateRole).not.toHaveBeenCalled();
expect(UsersAPI.disassociateRole).toHaveBeenCalledWith(1, 1);
expect(OrganizationsAPI.readAccessList).toHaveBeenCalledTimes(2);
done();
});
it('should delete team role', async (done) => {
const wrapper = mountWithContexts(<OrganizationAccess organization={organization} />);
const button = await waitForElement(wrapper, 'ChipButton', el => el.length === 2);
button.at(1).prop('onClick')();
const confirmation = await waitForElement(wrapper, 'DeleteRoleConfirmationModal');
confirmation.prop('onConfirm')();
await waitForElement(wrapper, 'DeleteRoleConfirmationModal', el => el.length === 0);
await sleep(0);
wrapper.update();
const component = wrapper.find('OrganizationAccess');
expect(component.state('deletionRole')).toBeNull();
expect(component.state('deletionRecord')).toBeNull();
expect(TeamsAPI.disassociateRole).toHaveBeenCalledWith(5, 3);
expect(UsersAPI.disassociateRole).not.toHaveBeenCalled();
expect(OrganizationsAPI.readAccessList).toHaveBeenCalledTimes(2);
done();
});
});

View File

@@ -1,93 +0,0 @@
import React from 'react';
import { mountWithContexts, waitForElement } from '../../../../enzymeHelpers';
import OrganizationDetail from '../../../../../src/pages/Organizations/screens/Organization/OrganizationDetail';
import { OrganizationsAPI } from '../../../../../src/api';
jest.mock('../../../../../src/api');
describe('<OrganizationDetail />', () => {
const mockOrganization = {
name: 'Foo',
description: 'Bar',
custom_virtualenv: 'Fizz',
max_hosts: '0',
created: 'Bat',
modified: 'Boo',
summary_fields: {
user_capabilities: {
edit: true
}
}
};
const mockInstanceGroups = {
data: {
results: [
{ name: 'One', id: 1 },
{ name: 'Two', id: 2 }
]
}
};
beforeEach(() => {
OrganizationsAPI.readInstanceGroups.mockResolvedValue(mockInstanceGroups);
});
afterEach(() => {
jest.clearAllMocks();
});
test('initially renders succesfully', () => {
mountWithContexts(<OrganizationDetail organization={mockOrganization} />);
});
test('should request instance groups from api', () => {
mountWithContexts(<OrganizationDetail organization={mockOrganization} />);
expect(OrganizationsAPI.readInstanceGroups).toHaveBeenCalledTimes(1);
});
test('should handle setting instance groups to state', async (done) => {
const wrapper = mountWithContexts(
<OrganizationDetail organization={mockOrganization} />
);
const component = await waitForElement(wrapper, 'OrganizationDetail');
expect(component.state().instanceGroups).toEqual(mockInstanceGroups.data.results);
done();
});
test('should render Details', async (done) => {
const wrapper = mountWithContexts(<OrganizationDetail organization={mockOrganization} />);
const testParams = [
{ label: 'Name', value: 'Foo' },
{ label: 'Description', value: 'Bar' },
{ label: 'Ansible Environment', value: 'Fizz' },
{ label: 'Created', value: 'Bat' },
{ label: 'Last Modified', value: 'Boo' },
{ label: 'Max Hosts', value: '0' },
];
// eslint-disable-next-line no-restricted-syntax
for (const { label, value } of testParams) {
// eslint-disable-next-line no-await-in-loop
const detail = await waitForElement(wrapper, `Detail[label="${label}"]`);
expect(detail.find('dt').text()).toBe(label);
expect(detail.find('dd').text()).toBe(value);
}
done();
});
test('should show edit button for users with edit permission', async (done) => {
const wrapper = mountWithContexts(<OrganizationDetail organization={mockOrganization} />);
const editButton = await waitForElement(wrapper, 'OrganizationDetail Button');
expect(editButton.text()).toEqual('Edit');
expect(editButton.prop('to')).toBe('/organizations/undefined/edit');
done();
});
test('should hide edit button for users without edit permission', async (done) => {
const readOnlyOrg = { ...mockOrganization };
readOnlyOrg.summary_fields.user_capabilities.edit = false;
const wrapper = mountWithContexts(<OrganizationDetail organization={readOnlyOrg} />);
await waitForElement(wrapper, 'OrganizationDetail');
expect(wrapper.find('OrganizationDetail Button').length).toBe(0);
done();
});
});

View File

@@ -1,95 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../../../../enzymeHelpers';
import OrganizationEdit from '../../../../../src/pages/Organizations/screens/Organization/OrganizationEdit';
import { OrganizationsAPI } from '../../../../../src/api';
jest.mock('../../../../../src/api');
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
describe('<OrganizationEdit />', () => {
let api;
const mockData = {
name: 'Foo',
description: 'Bar',
custom_virtualenv: 'Fizz',
id: 1,
related: {
instance_groups: '/api/v2/organizations/1/instance_groups'
}
};
beforeEach(() => {
api = {
getInstanceGroups: jest.fn(),
updateOrganizationDetails: jest.fn(),
associateInstanceGroup: jest.fn(),
disassociate: jest.fn(),
};
});
test('handleSubmit should call api update', () => {
const wrapper = mountWithContexts(
<OrganizationEdit
organization={mockData}
/>, { context: { network: {
api,
} } }
);
const updatedOrgData = {
name: 'new name',
description: 'new description',
custom_virtualenv: 'Buzz',
};
wrapper.find('OrganizationForm').prop('handleSubmit')(updatedOrgData, [], []);
expect(OrganizationsAPI.update).toHaveBeenCalledWith(1, updatedOrgData);
});
test('handleSubmit associates and disassociates instance groups', async () => {
const wrapper = mountWithContexts(
<OrganizationEdit
organization={mockData}
/>, { context: { network: {
api,
} } }
);
const updatedOrgData = {
name: 'new name',
description: 'new description',
custom_virtualenv: 'Buzz',
};
wrapper.find('OrganizationForm').prop('handleSubmit')(updatedOrgData, [3, 4], [2]);
await sleep(1);
expect(OrganizationsAPI.associateInstanceGroup).toHaveBeenCalledWith(1, 3);
expect(OrganizationsAPI.associateInstanceGroup).toHaveBeenCalledWith(1, 4);
expect(OrganizationsAPI.disassociateInstanceGroup).toHaveBeenCalledWith(1, 2);
});
test('should navigate to organization detail when cancel is clicked', () => {
const history = {
push: jest.fn(),
};
const wrapper = mountWithContexts(
<OrganizationEdit
organization={mockData}
/>, { context: {
network: {
api: { api },
},
router: { history }
} }
);
expect(history.push).not.toHaveBeenCalled();
wrapper.find('button[aria-label="Cancel"]').prop('onClick')();
expect(history.push).toHaveBeenCalledWith('/organizations/1');
});
});

View File

@@ -1,146 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../../../../enzymeHelpers';
import OrganizationNotifications from '../../../../../src/pages/Organizations/screens/Organization/OrganizationNotifications';
import { sleep } from '../../../../testUtils';
import { OrganizationsAPI } from '../../../../../src/api';
jest.mock('../../../../../src/api');
describe('<OrganizationNotifications />', () => {
let data;
beforeEach(() => {
data = {
count: 2,
results: [{
id: 1,
name: 'Notification one',
url: '/api/v2/notification_templates/1/',
notification_type: 'email',
}, {
id: 2,
name: 'Notification two',
url: '/api/v2/notification_templates/2/',
notification_type: 'email',
}]
};
OrganizationsAPI.readNotificationTemplates.mockReturnValue({ data });
OrganizationsAPI.readNotificationTemplatesSuccess.mockReturnValue({
data: { results: [{ id: 1 }] },
});
OrganizationsAPI.readNotificationTemplatesError.mockReturnValue({
data: { results: [{ id: 2 }] },
});
});
afterEach(() => {
jest.clearAllMocks();
});
test('initially renders succesfully', async () => {
const wrapper = mountWithContexts(
<OrganizationNotifications id={1} canToggleNotifications />
);
await sleep(0);
wrapper.update();
expect(wrapper).toMatchSnapshot();
});
test('should render list fetched of items', async () => {
const wrapper = mountWithContexts(
<OrganizationNotifications id={1} canToggleNotifications />
);
await sleep(0);
wrapper.update();
expect(OrganizationsAPI.readNotificationTemplates).toHaveBeenCalled();
expect(wrapper.find('OrganizationNotifications').state('notifications'))
.toEqual(data.results);
const items = wrapper.find('NotificationListItem');
expect(items).toHaveLength(2);
expect(items.at(0).prop('successTurnedOn')).toEqual(true);
expect(items.at(0).prop('errorTurnedOn')).toEqual(false);
expect(items.at(1).prop('successTurnedOn')).toEqual(false);
expect(items.at(1).prop('errorTurnedOn')).toEqual(true);
});
test('should enable success notification', async () => {
const wrapper = mountWithContexts(
<OrganizationNotifications id={1} canToggleNotifications />
);
await sleep(0);
wrapper.update();
expect(
wrapper.find('OrganizationNotifications').state('successTemplateIds')
).toEqual([1]);
const items = wrapper.find('NotificationListItem');
items.at(1).find('Switch').at(0).prop('onChange')();
expect(OrganizationsAPI.updateNotificationTemplateAssociation).toHaveBeenCalledWith(1, 2, 'success', true);
await sleep(0);
wrapper.update();
expect(
wrapper.find('OrganizationNotifications').state('successTemplateIds')
).toEqual([1, 2]);
});
test('should enable error notification', async () => {
const wrapper = mountWithContexts(
<OrganizationNotifications id={1} canToggleNotifications />
);
await sleep(0);
wrapper.update();
expect(
wrapper.find('OrganizationNotifications').state('errorTemplateIds')
).toEqual([2]);
const items = wrapper.find('NotificationListItem');
items.at(0).find('Switch').at(1).prop('onChange')();
expect(OrganizationsAPI.updateNotificationTemplateAssociation).toHaveBeenCalledWith(1, 1, 'error', true);
await sleep(0);
wrapper.update();
expect(
wrapper.find('OrganizationNotifications').state('errorTemplateIds')
).toEqual([2, 1]);
});
test('should disable success notification', async () => {
const wrapper = mountWithContexts(
<OrganizationNotifications id={1} canToggleNotifications />
);
await sleep(0);
wrapper.update();
expect(
wrapper.find('OrganizationNotifications').state('successTemplateIds')
).toEqual([1]);
const items = wrapper.find('NotificationListItem');
items.at(0).find('Switch').at(0).prop('onChange')();
expect(OrganizationsAPI.updateNotificationTemplateAssociation).toHaveBeenCalledWith(1, 1, 'success', false);
await sleep(0);
wrapper.update();
expect(
wrapper.find('OrganizationNotifications').state('successTemplateIds')
).toEqual([]);
});
test('should disable error notification', async () => {
const wrapper = mountWithContexts(
<OrganizationNotifications id={1} canToggleNotifications />
);
await sleep(0);
wrapper.update();
expect(
wrapper.find('OrganizationNotifications').state('errorTemplateIds')
).toEqual([2]);
const items = wrapper.find('NotificationListItem');
items.at(1).find('Switch').at(1).prop('onChange')();
expect(OrganizationsAPI.updateNotificationTemplateAssociation).toHaveBeenCalledWith(1, 2, 'error', false);
await sleep(0);
wrapper.update();
expect(
wrapper.find('OrganizationNotifications').state('errorTemplateIds')
).toEqual([]);
});
});

View File

@@ -1,80 +0,0 @@
import React from 'react';
import { shallow } from 'enzyme';
import { mountWithContexts } from '../../../../enzymeHelpers';
import { sleep } from '../../../../testUtils';
import OrganizationTeams, { _OrganizationTeams } from '../../../../../src/pages/Organizations/screens/Organization/OrganizationTeams';
import { OrganizationsAPI } from '../../../../../src/api';
jest.mock('../../../../../src/api');
const listData = {
data: {
count: 7,
results: [
{ id: 1, name: 'one', url: '/org/team/1' },
{ id: 2, name: 'two', url: '/org/team/2' },
{ id: 3, name: 'three', url: '/org/team/3' },
{ id: 4, name: 'four', url: '/org/team/4' },
{ id: 5, name: 'five', url: '/org/team/5' },
]
}
};
describe('<OrganizationTeams />', () => {
beforeEach(() => {
OrganizationsAPI.readTeams.mockResolvedValue(listData);
});
afterEach(() => {
jest.clearAllMocks();
});
test('renders succesfully', () => {
shallow(
<_OrganizationTeams
id={1}
searchString=""
location={{ search: '', pathname: '/organizations/1/teams' }}
/>
);
});
test('should load teams on mount', () => {
mountWithContexts(
<OrganizationTeams
id={1}
searchString=""
/>
).find('OrganizationTeams');
expect(OrganizationsAPI.readTeams).toHaveBeenCalledWith(1, {
page: 1,
page_size: 5,
order_by: 'name',
});
});
test('should pass fetched teams to PaginatedDatalist', async () => {
const wrapper = mountWithContexts(
<OrganizationTeams
id={1}
searchString=""
/>
);
await sleep(0);
wrapper.update();
const list = wrapper.find('PaginatedDataList');
expect(list.prop('items')).toEqual(listData.data.results);
expect(list.prop('itemCount')).toEqual(listData.data.count);
expect(list.prop('qsConfig')).toEqual({
namespace: 'team',
defaultParams: {
page: 1,
page_size: 5,
order_by: 'name',
},
integerFields: ['page', 'page_size'],
});
});
});

View File

@@ -1,260 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<OrganizationAccess /> initially renders succesfully 1`] = `
<OrganizationAccess
history={"/history/"}
i18n={"/i18n/"}
location={
Object {
"hash": "",
"pathname": "",
"search": "",
"state": "",
}
}
match={
Object {
"isExact": false,
"params": Object {},
"path": "",
"url": "",
}
}
organization={
Object {
"id": 1,
"name": "Default",
"summary_fields": Object {
"object_roles": Object {},
"user_capabilities": Object {
"edit": true,
},
},
}
}
>
<WithI18n
contentError={false}
contentLoading={true}
itemCount={0}
itemName="role"
items={Array []}
qsConfig={
Object {
"defaultParams": Object {
"order_by": "first_name",
"page": 1,
"page_size": 5,
},
"integerFields": Array [
"page",
"page_size",
],
"namespace": "access",
}
}
renderItem={[Function]}
renderToolbar={[Function]}
toolbarColumns={
Array [
Object {
"isSortable": true,
"key": "first_name",
"name": "Name",
},
Object {
"isSortable": true,
"key": "username",
"name": "Username",
},
Object {
"isSortable": true,
"key": "last_name",
"name": "Last Name",
},
]
}
>
<I18n
update={true}
withHash={true}
>
<withRouter(PaginatedDataList)
contentError={false}
contentLoading={true}
i18n={"/i18n/"}
itemCount={0}
itemName="role"
items={Array []}
qsConfig={
Object {
"defaultParams": Object {
"order_by": "first_name",
"page": 1,
"page_size": 5,
},
"integerFields": Array [
"page",
"page_size",
],
"namespace": "access",
}
}
renderItem={[Function]}
renderToolbar={[Function]}
toolbarColumns={
Array [
Object {
"isSortable": true,
"key": "first_name",
"name": "Name",
},
Object {
"isSortable": true,
"key": "username",
"name": "Username",
},
Object {
"isSortable": true,
"key": "last_name",
"name": "Last Name",
},
]
}
>
<Route>
<PaginatedDataList
contentError={false}
contentLoading={true}
history={"/history/"}
i18n={"/i18n/"}
itemCount={0}
itemName="role"
itemNamePlural=""
items={Array []}
location={
Object {
"hash": "",
"pathname": "",
"search": "",
"state": "",
}
}
match={
Object {
"isExact": false,
"params": Object {},
"path": "",
"url": "",
}
}
qsConfig={
Object {
"defaultParams": Object {
"order_by": "first_name",
"page": 1,
"page_size": 5,
},
"integerFields": Array [
"page",
"page_size",
],
"namespace": "access",
}
}
renderItem={[Function]}
renderToolbar={[Function]}
showPageSizeOptions={true}
toolbarColumns={
Array [
Object {
"isSortable": true,
"key": "first_name",
"name": "Name",
},
Object {
"isSortable": true,
"key": "username",
"name": "Username",
},
Object {
"isSortable": true,
"key": "last_name",
"name": "Last Name",
},
]
}
>
<WithI18n>
<I18n
update={true}
withHash={true}
>
<ContentLoading
i18n={"/i18n/"}
>
<EmptyState
className=""
variant="large"
>
<div
className="pf-c-empty-state pf-m-lg"
>
<EmptyStateBody
className=""
>
<p
className="pf-c-empty-state__body"
>
Loading...
</p>
</EmptyStateBody>
</div>
</EmptyState>
</ContentLoading>
</I18n>
</WithI18n>
</PaginatedDataList>
</Route>
</withRouter(PaginatedDataList)>
</I18n>
</WithI18n>
<_default
isOpen={false}
onClose={[Function]}
title="Error!"
variant="danger"
>
<Modal
actions={Array []}
ariaDescribedById=""
className="awx-c-modal at-c-alertModal at-c-alertModal--danger"
hideTitle={false}
isLarge={false}
isOpen={false}
isSmall={false}
onClose={[Function]}
title="Error!"
width={null}
>
<Portal
containerInfo={<div />}
>
<ModalContent
actions={Array []}
ariaDescribedById=""
className="awx-c-modal at-c-alertModal at-c-alertModal--danger"
hideTitle={false}
id="pf-modal-0"
isLarge={false}
isOpen={false}
isSmall={false}
onClose={[Function]}
title="Error!"
width={null}
/>
</Portal>
</Modal>
</_default>
</OrganizationAccess>
`;

View File

@@ -1,121 +0,0 @@
import React from 'react';
import { mountWithContexts, waitForElement } from '../../../enzymeHelpers';
import OrganizationAdd from '../../../../src/pages/Organizations/screens/OrganizationAdd';
import { OrganizationsAPI } from '../../../../src/api';
jest.mock('../../../../src/api');
describe('<OrganizationAdd />', () => {
test('handleSubmit should post to api', () => {
const wrapper = mountWithContexts(<OrganizationAdd />);
const updatedOrgData = {
name: 'new name',
description: 'new description',
custom_virtualenv: 'Buzz',
};
wrapper.find('OrganizationForm').prop('handleSubmit')(updatedOrgData, [], []);
expect(OrganizationsAPI.create).toHaveBeenCalledWith(updatedOrgData);
});
test('should navigate to organizations list when cancel is clicked', () => {
const history = {
push: jest.fn(),
};
const wrapper = mountWithContexts(
<OrganizationAdd />,
{ context: { router: { history } } }
);
expect(history.push).not.toHaveBeenCalled();
wrapper.find('button[aria-label="Cancel"]').prop('onClick')();
expect(history.push).toHaveBeenCalledWith('/organizations');
});
test('should navigate to organizations list when close (x) is clicked', () => {
const history = {
push: jest.fn(),
};
const wrapper = mountWithContexts(
<OrganizationAdd />,
{ context: { router: { history } } }
);
expect(history.push).not.toHaveBeenCalled();
wrapper.find('button[aria-label="Close"]').prop('onClick')();
expect(history.push).toHaveBeenCalledWith('/organizations');
});
test('successful form submission should trigger redirect', async (done) => {
const history = {
push: jest.fn(),
};
const orgData = {
name: 'new name',
description: 'new description',
custom_virtualenv: 'Buzz',
};
OrganizationsAPI.create.mockResolvedValueOnce({
data: {
id: 5,
related: {
instance_groups: '/bar',
},
...orgData,
}
});
const wrapper = mountWithContexts(
<OrganizationAdd />,
{ context: { router: { history } } }
);
await waitForElement(wrapper, 'button[aria-label="Save"]');
await wrapper.find('OrganizationForm').prop('handleSubmit')(orgData, [3], []);
expect(history.push).toHaveBeenCalledWith('/organizations/5');
done();
});
test('handleSubmit should post instance groups', async (done) => {
const orgData = {
name: 'new name',
description: 'new description',
custom_virtualenv: 'Buzz',
};
OrganizationsAPI.create.mockResolvedValueOnce({
data: {
id: 5,
related: {
instance_groups: '/api/v2/organizations/5/instance_groups',
},
...orgData,
}
});
const wrapper = mountWithContexts(<OrganizationAdd />);
await waitForElement(wrapper, 'button[aria-label="Save"]');
await wrapper.find('OrganizationForm').prop('handleSubmit')(orgData, [3], []);
expect(OrganizationsAPI.associateInstanceGroup)
.toHaveBeenCalledWith(5, 3);
done();
});
test('AnsibleSelect component renders if there are virtual environments', () => {
const config = {
custom_virtualenvs: ['foo', 'bar'],
};
const wrapper = mountWithContexts(
<OrganizationAdd />,
{ context: { config } }
).find('AnsibleSelect');
expect(wrapper.find('FormSelect')).toHaveLength(1);
expect(wrapper.find('FormSelectOption')).toHaveLength(2);
});
test('AnsibleSelect component does not render if there are 0 virtual environments', () => {
const config = {
custom_virtualenvs: [],
};
const wrapper = mountWithContexts(
<OrganizationAdd />,
{ context: { config } }
).find('AnsibleSelect');
expect(wrapper.find('FormSelect')).toHaveLength(0);
});
});

View File

@@ -1,136 +0,0 @@
import React from 'react';
import { mountWithContexts, waitForElement } from '../../../enzymeHelpers';
import OrganizationsList, { _OrganizationsList } from '../../../../src/pages/Organizations/screens/OrganizationsList';
import { OrganizationsAPI } from '../../../../src/api';
jest.mock('../../../../src/api');
const mockAPIOrgsList = {
data: {
count: 3,
results: [{
name: 'Organization 0',
id: 1,
url: '/organizations/1',
summary_fields: {
related_field_counts: {
teams: 3,
users: 4
},
user_capabilities: {
delete: true
}
},
},
{
name: 'Organization 1',
id: 2,
url: '/organizations/2',
summary_fields: {
related_field_counts: {
teams: 2,
users: 5
},
user_capabilities: {
delete: true
}
},
},
{
name: 'Organization 2',
id: 3,
url: '/organizations/3',
summary_fields: {
related_field_counts: {
teams: 5,
users: 6
},
user_capabilities: {
delete: true
}
},
}]
},
isModalOpen: false,
warningTitle: 'title',
warningMsg: 'message'
};
describe('<OrganizationsList />', () => {
let wrapper;
test('initially renders succesfully', () => {
mountWithContexts(<OrganizationsList />);
});
test('Puts 1 selected Org in state when handleSelect is called.', () => {
wrapper = mountWithContexts(<OrganizationsList />).find('OrganizationsList');
wrapper.setState({
organizations: mockAPIOrgsList.data.results,
itemCount: 3,
isInitialized: true
});
wrapper.update();
expect(wrapper.state('selected').length).toBe(0);
wrapper.instance().handleSelect(mockAPIOrgsList.data.results.slice(0, 1));
expect(wrapper.state('selected').length).toBe(1);
});
test('Puts all Orgs in state when handleSelectAll is called.', () => {
wrapper = mountWithContexts(<OrganizationsList />);
const list = wrapper.find('OrganizationsList');
list.setState({
organizations: mockAPIOrgsList.data.results,
itemCount: 3,
isInitialized: true
});
expect(list.state('selected').length).toBe(0);
list.instance().handleSelectAll(true);
wrapper.update();
expect(list.state('selected').length)
.toEqual(list.state('organizations').length);
});
test('api is called to delete Orgs for each org in selected.', () => {
wrapper = mountWithContexts(<OrganizationsList />);
const component = wrapper.find('OrganizationsList');
wrapper.find('OrganizationsList').setState({
organizations: mockAPIOrgsList.data.results,
itemCount: 3,
isInitialized: true,
isModalOpen: mockAPIOrgsList.isModalOpen,
selected: mockAPIOrgsList.data.results
});
wrapper.find('ToolbarDeleteButton').prop('onDelete')();
expect(OrganizationsAPI.destroy).toHaveBeenCalledTimes(component.state('selected').length);
});
test('call loadOrganizations after org(s) have been deleted', () => {
const fetchOrgs = jest.spyOn(_OrganizationsList.prototype, 'loadOrganizations');
const event = { preventDefault: () => { } };
wrapper = mountWithContexts(<OrganizationsList />);
wrapper.find('OrganizationsList').setState({
organizations: mockAPIOrgsList.data.results,
itemCount: 3,
isInitialized: true,
selected: mockAPIOrgsList.data.results.slice(0, 1)
});
const component = wrapper.find('OrganizationsList');
component.instance().handleOrgDelete(event);
expect(fetchOrgs).toBeCalled();
});
test('error is shown when org not successfully deleted from api', async () => {
OrganizationsAPI.destroy = () => Promise.reject();
wrapper = mountWithContexts(<OrganizationsList />);
wrapper.find('OrganizationsList').setState({
organizations: mockAPIOrgsList.data.results,
itemCount: 3,
isInitialized: true,
selected: mockAPIOrgsList.data.results.slice(0, 1)
});
wrapper.find('ToolbarDeleteButton').prop('onDelete')();
await waitForElement(wrapper, 'Modal', (el) => el.props().isOpen === true && el.props().title === 'Error!');
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import Portal from '../../src/pages/Portal';
describe('<Portal />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<Portal />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import Projects from '../../src/pages/Projects';
describe('<Projects />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<Projects />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import Schedules from '../../src/pages/Schedules';
describe('<Schedules />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<Schedules />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import SystemSettings from '../../src/pages/SystemSettings';
describe('<SystemSettings />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<SystemSettings />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import Teams from '../../src/pages/Teams';
describe('<Teams />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<Teams />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,19 +0,0 @@
import React from 'react';
import Templates from '../../src/pages/Templates/Templates';
import { mountWithContexts } from '../enzymeHelpers';
describe('<Templates />', () => {
let pageWrapper;
beforeEach(() => {
pageWrapper = mountWithContexts(<Templates />);
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
});
});

View File

@@ -1,11 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../../enzymeHelpers';
import Templates from '../../../src/pages/Templates/Templates';
describe('<Templates />', () => {
test('initially renders succesfully', () => {
mountWithContexts(
<Templates />
);
});
});

View File

@@ -1,161 +0,0 @@
import React from 'react';
import { mountWithContexts, waitForElement } from '../../enzymeHelpers';
import TemplatesList, { _TemplatesList } from '../../../src/pages/Templates/TemplatesList';
import { JobTemplatesAPI, UnifiedJobTemplatesAPI, WorkflowJobTemplatesAPI } from '../../../src/api';
jest.mock('../../../src/api');
const mockTemplates = [{
id: 1,
name: 'Job Template 1',
url: '/templates/job_template/1',
type: 'job_template',
summary_fields: {
user_capabilities: {
delete: true
}
}
},
{
id: 2,
name: 'Job Template 2',
url: '/templates/job_template/2',
type: 'job_template',
summary_fields: {
user_capabilities: {
delete: true
}
}
},
{
id: 3,
name: 'Job Template 3',
url: '/templates/job_template/3',
type: 'job_template',
summary_fields: {
user_capabilities: {
delete: true
}
}
},
{
id: 4,
name: 'Workflow Job Template 1',
url: '/templates/workflow_job_template/4',
type: 'workflow_job_template',
summary_fields: {
user_capabilities: {
delete: true
}
}
},
{
id: 5,
name: 'Workflow Job Template 2',
url: '/templates/workflow_job_template/5',
type: 'workflow_job_template',
summary_fields: {
user_capabilities: {
delete: false
}
}
}];
describe('<TemplatesList />', () => {
beforeEach(() => {
UnifiedJobTemplatesAPI.read.mockResolvedValue({
data: {
count: mockTemplates.length,
results: mockTemplates
}
});
});
afterEach(() => {
jest.clearAllMocks();
});
test('initially renders succesfully', () => {
mountWithContexts(
<TemplatesList
match={{ path: '/templates', url: '/templates' }}
location={{ search: '', pathname: '/templates' }}
/>
);
});
test('Templates are retrieved from the api and the components finishes loading', async (done) => {
const loadTemplates = jest.spyOn(_TemplatesList.prototype, 'loadTemplates');
const wrapper = mountWithContexts(<TemplatesList />);
await waitForElement(wrapper, 'TemplatesList', (el) => el.state('contentLoading') === true);
expect(loadTemplates).toHaveBeenCalled();
await waitForElement(wrapper, 'TemplatesList', (el) => el.state('contentLoading') === false);
done();
});
test('handleSelect is called when a template list item is selected', async (done) => {
const handleSelect = jest.spyOn(_TemplatesList.prototype, 'handleSelect');
const wrapper = mountWithContexts(<TemplatesList />);
await waitForElement(wrapper, 'TemplatesList', (el) => el.state('contentLoading') === false);
wrapper.find('DataListCheck#select-jobTemplate-1').props().onChange();
expect(handleSelect).toBeCalled();
await waitForElement(wrapper, 'TemplatesList', (el) => el.state('selected').length === 1);
done();
});
test('handleSelectAll is called when a template list item is selected', async (done) => {
const handleSelectAll = jest.spyOn(_TemplatesList.prototype, 'handleSelectAll');
const wrapper = mountWithContexts(<TemplatesList />);
await waitForElement(wrapper, 'TemplatesList', (el) => el.state('contentLoading') === false);
wrapper.find('Checkbox#select-all').props().onChange(true);
expect(handleSelectAll).toBeCalled();
await waitForElement(wrapper, 'TemplatesList', (el) => el.state('selected').length === 5);
done();
});
test('delete button is disabled if user does not have delete capabilities on a selected template', async (done) => {
const wrapper = mountWithContexts(<TemplatesList />);
wrapper.find('TemplatesList').setState({
templates: mockTemplates,
itemCount: 5,
isInitialized: true,
selected: mockTemplates.slice(0, 4)
});
await waitForElement(wrapper, 'ToolbarDeleteButton * button', (el) => el.getDOMNode().disabled === false);
wrapper.find('TemplatesList').setState({
selected: mockTemplates
});
await waitForElement(wrapper, 'ToolbarDeleteButton * button', (el) => el.getDOMNode().disabled === true);
done();
});
test('api is called to delete templates for each selected template.', () => {
JobTemplatesAPI.destroy = jest.fn();
WorkflowJobTemplatesAPI.destroy = jest.fn();
const wrapper = mountWithContexts(<TemplatesList />);
wrapper.find('TemplatesList').setState({
templates: mockTemplates,
itemCount: 5,
isInitialized: true,
isModalOpen: true,
selected: mockTemplates.slice(0, 4)
});
wrapper.find('ToolbarDeleteButton').prop('onDelete')();
expect(JobTemplatesAPI.destroy).toHaveBeenCalledTimes(3);
expect(WorkflowJobTemplatesAPI.destroy).toHaveBeenCalledTimes(1);
});
test('error is shown when template not successfully deleted from api', async () => {
JobTemplatesAPI.destroy = () => Promise.reject();
const wrapper = mountWithContexts(<TemplatesList />);
wrapper.find('TemplatesList').setState({
templates: mockTemplates,
itemCount: 1,
isInitialized: true,
isModalOpen: true,
selected: mockTemplates.slice(0, 1)
});
wrapper.find('ToolbarDeleteButton').prop('onDelete')();
await waitForElement(wrapper, 'Modal', (el) => el.props().isOpen === true && el.props().title === 'Error!');
});
});

View File

@@ -1,16 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../../../enzymeHelpers';
import TemplatesListItem from '../../../../src/pages/Templates/components/TemplateListItem';
describe('<TemplatesListItem />', () => {
test('initially render successfully', () => {
mountWithContexts(<TemplatesListItem
template={{
id: 1,
name: 'Template 1',
url: '/templates/job_template/1',
type: 'job_template'
}}
/>);
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import UISettings from '../../src/pages/UISettings';
describe('<UISettings />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<UISettings />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,29 +0,0 @@
import React from 'react';
import { mountWithContexts } from '../enzymeHelpers';
import Users from '../../src/pages/Users';
describe('<Users />', () => {
let pageWrapper;
let pageSections;
let title;
beforeEach(() => {
pageWrapper = mountWithContexts(<Users />);
pageSections = pageWrapper.find('PageSection');
title = pageWrapper.find('Title');
});
afterEach(() => {
pageWrapper.unmount();
});
test('initially renders without crashing', () => {
expect(pageWrapper.length).toBe(1);
expect(pageSections.length).toBe(2);
expect(title.length).toBe(1);
expect(title.props().size).toBe('2xl');
pageSections.forEach(section => {
expect(section.props().variant).toBeDefined();
});
});
});

View File

@@ -1,4 +0,0 @@
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
/* eslint-disable-next-line import/prefer-default-export */
export { sleep };

View File

@@ -1,19 +0,0 @@
import { isAuthenticated } from '../../src/util/auth';
const invalidCookie = 'invalid';
const validLoggedOutCookie = 'current_user=%7B%22id%22%3A1%2C%22type%22%3A%22user%22%2C%22url%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2F%22%2C%22related%22%3A%7B%22admin_of_organizations%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fadmin_of_organizations%2F%22%2C%22authorized_tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fauthorized_tokens%2F%22%2C%22roles%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Froles%2F%22%2C%22organizations%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Forganizations%2F%22%2C%22access_list%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Faccess_list%2F%22%2C%22teams%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fteams%2F%22%2C%22tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Ftokens%2F%22%2C%22personal_tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fpersonal_tokens%2F%22%2C%22credentials%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fcredentials%2F%22%2C%22activity_stream%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Factivity_stream%2F%22%2C%22projects%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fprojects%2F%22%7D%2C%22summary_fields%22%3A%7B%7D%2C%22created%22%3A%222018-10-19T16%3A30%3A59.141963Z%22%2C%22username%22%3A%22admin%22%2C%22first_name%22%3A%22%22%2C%22last_name%22%3A%22%22%2C%22email%22%3A%22%22%2C%22is_superuser%22%3Atrue%2C%22is_system_auditor%22%3Afalse%2C%22ldap_dn%22%3A%22%22%2C%22external_account%22%3Anull%2C%22auth%22%3A%5B%5D%7D; userLoggedIn=false; csrftoken=lhOHpLQUFHlIVqx8CCZmEpdEZAz79GIRBIT3asBzTbPE7HS7wizt7WBsgJClz8Ge';
const validLoggedInCookie = 'current_user=%7B%22id%22%3A1%2C%22type%22%3A%22user%22%2C%22url%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2F%22%2C%22related%22%3A%7B%22admin_of_organizations%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fadmin_of_organizations%2F%22%2C%22authorized_tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fauthorized_tokens%2F%22%2C%22roles%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Froles%2F%22%2C%22organizations%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Forganizations%2F%22%2C%22access_list%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Faccess_list%2F%22%2C%22teams%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fteams%2F%22%2C%22tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Ftokens%2F%22%2C%22personal_tokens%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fpersonal_tokens%2F%22%2C%22credentials%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fcredentials%2F%22%2C%22activity_stream%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Factivity_stream%2F%22%2C%22projects%22%3A%22%2Fapi%2Fv2%2Fusers%2F1%2Fprojects%2F%22%7D%2C%22summary_fields%22%3A%7B%7D%2C%22created%22%3A%222018-10-19T16%3A30%3A59.141963Z%22%2C%22username%22%3A%22admin%22%2C%22first_name%22%3A%22%22%2C%22last_name%22%3A%22%22%2C%22email%22%3A%22%22%2C%22is_superuser%22%3Atrue%2C%22is_system_auditor%22%3Afalse%2C%22ldap_dn%22%3A%22%22%2C%22external_account%22%3Anull%2C%22auth%22%3A%5B%5D%7D; userLoggedIn=true; csrftoken=lhOHpLQUFHlIVqx8CCZmEpdEZAz79GIRBIT3asBzTbPE7HS7wizt7WBsgJClz8Ge';
describe('isAuthenticated', () => {
test('returns false for invalid cookie', () => {
expect(isAuthenticated(invalidCookie)).toEqual(false);
});
test('returns false for expired cookie', () => {
expect(isAuthenticated(validLoggedOutCookie)).toEqual(false);
});
test('returns true for valid authenticated cookie', () => {
expect(isAuthenticated(validLoggedInCookie)).toEqual(true);
});
});

View File

@@ -1,213 +0,0 @@
import {
encodeQueryString,
parseQueryString,
getQSConfig,
parseNamespacedQueryString,
encodeNamespacedQueryString,
updateNamespacedQueryString,
} from '../../src/util/qs';
describe('qs (qs.js)', () => {
test('encodeQueryString returns the expected queryString', () => {
[
[null, ''],
[{}, ''],
[{ order_by: 'name', page: 1, page_size: 5 }, 'order_by=name&page=1&page_size=5'],
[{ '-order_by': 'name', page: '1', page_size: 5 }, '-order_by=name&page=1&page_size=5'],
]
.forEach(([params, expectedQueryString]) => {
const actualQueryString = encodeQueryString(params);
expect(actualQueryString).toEqual(expectedQueryString);
});
});
test('encodeQueryString omits null values', () => {
const vals = {
order_by: 'name',
page: null,
};
expect(encodeQueryString(vals)).toEqual('order_by=name');
});
describe('parseQueryString', () => {
test('parseQueryString returns the expected queryParams', () => {
[
['order_by=name&page=1&page_size=5', ['page', 'page_size'], { order_by: 'name', page: 1, page_size: 5 }],
['order_by=name&page=1&page_size=5', ['page_size'], { order_by: 'name', page: '1', page_size: 5 }],
]
.forEach(([queryString, integerFields, expectedQueryParams]) => {
const actualQueryParams = parseQueryString(queryString, integerFields);
expect(actualQueryParams).toEqual(expectedQueryParams);
});
});
test('parseQueryString should strip leading "?"', () => {
expect(parseQueryString('?foo=bar&order_by=win')).toEqual({
foo: 'bar',
order_by: 'win',
});
expect(parseQueryString('foo=bar&order_by=?win')).toEqual({
foo: 'bar',
order_by: '?win',
});
});
test('should return empty object if no values', () => {
expect(parseQueryString('')).toEqual({});
});
});
test('should get default QS config object', () => {
expect(getQSConfig('organization')).toEqual({
namespace: 'organization',
defaultParams: { page: 1, page_size: 5, order_by: 'name' },
integerFields: ['page', 'page_size'],
});
});
test('should throw if no namespace given', () => {
expect(() => getQSConfig()).toThrow();
});
test('should build configured QS config object', () => {
const defaults = {
page: 1,
page_size: 15,
};
expect(getQSConfig('inventory', defaults)).toEqual({
namespace: 'inventory',
defaultParams: { page: 1, page_size: 15 },
integerFields: ['page', 'page_size'],
});
});
describe('parseNamespacedQueryString', () => {
test('should get query params', () => {
const config = {
namespace: null,
defaultParams: { page: 1, page_size: 15 },
integerFields: ['page', 'page_size'],
};
const query = '?foo=bar&page=3';
expect(parseNamespacedQueryString(config, query)).toEqual({
foo: 'bar',
page: 3,
page_size: 15,
});
});
test('should get query params with correct integer fields', () => {
const config = {
namespace: null,
defaultParams: {},
integerFields: ['page', 'foo'],
};
const query = '?foo=4&bar=5';
expect(parseNamespacedQueryString(config, query)).toEqual({
foo: 4,
bar: '5',
});
});
test('should get namespaced query params', () => {
const config = {
namespace: 'inventory',
defaultParams: { page: 1, page_size: 5 },
integerFields: ['page', 'page_size'],
};
const query = '?inventory.page=2&inventory.order_by=name&other=15';
expect(parseNamespacedQueryString(config, query)).toEqual({
page: 2,
order_by: 'name',
page_size: 5,
});
});
test('should exclude other namespaced query params', () => {
const config = {
namespace: 'inventory',
defaultParams: { page: 1, page_size: 5 },
integerFields: ['page', 'page_size'],
};
const query = '?inventory.page=2&inventory.order_by=name&foo.other=15';
expect(parseNamespacedQueryString(config, query)).toEqual({
page: 2,
order_by: 'name',
page_size: 5,
});
});
test('should exclude defaults if includeDefaults is false', () => {
const config = {
namespace: null,
defaultParams: { page: 1, page_size: 15 },
integerFields: ['page', 'page_size'],
};
const query = '?foo=bar&page=3';
expect(parseNamespacedQueryString(config, query, false)).toEqual({
foo: 'bar',
page: 3,
});
});
});
describe('encodeNamespacedQueryString', () => {
test('should encode params without namespace', () => {
const config = {
namespace: null,
defaultParams: { page: 1, page_size: 5 },
integerFields: ['page', 'page_size'],
};
const params = {
page: 1,
order_by: 'name',
};
const qs = 'order_by=name&page=1';
expect(encodeNamespacedQueryString(config, params)).toEqual(qs);
});
test('should encode params with namespace', () => {
const config = {
namespace: 'inventory',
defaultParams: { page: 1, page_size: 5 },
integerFields: ['page', 'page_size'],
};
const params = {
page: 1,
order_by: 'name',
};
const qs = 'inventory.order_by=name&inventory.page=1';
expect(encodeNamespacedQueryString(config, params)).toEqual(qs);
});
});
describe('updateNamespacedQueryString', () => {
test('should return current values', () => {
const qs = '?foo=bar&inventory.page=1';
const updated = updateNamespacedQueryString({}, qs, {});
expect(updated).toEqual('foo=bar&inventory.page=1');
});
test('should update new values', () => {
const qs = '?foo=bar&inventory.page=1';
const updated = updateNamespacedQueryString({}, qs, { foo: 'baz' });
expect(updated).toEqual('foo=baz&inventory.page=1');
});
test('should add new values', () => {
const qs = '?foo=bar&inventory.page=1';
const updated = updateNamespacedQueryString({}, qs, { page: 5 });
expect(updated).toEqual('foo=bar&inventory.page=1&page=5');
});
test('should update namespaced values', () => {
const qs = '?foo=bar&inventory.page=1';
const config = { namespace: 'inventory' };
const updated = updateNamespacedQueryString(config, qs, { page: 2 });
expect(updated).toEqual('foo=bar&inventory.page=2');
});
});
});

View File

@@ -1,34 +0,0 @@
import { pluralize, getArticle, ucFirst } from '../../src/util/strings';
describe('string utils', () => {
describe('pluralize', () => {
test('should add an "s"', () => {
expect(pluralize('team')).toEqual('teams');
});
test('should add an "es"', () => {
expect(pluralize('class')).toEqual('classes');
});
});
describe('getArticle', () => {
test('should return "a"', () => {
expect(getArticle('team')).toEqual('a');
expect(getArticle('notification')).toEqual('a');
});
test('should return "an"', () => {
expect(getArticle('aardvark')).toEqual('an');
expect(getArticle('ear')).toEqual('an');
expect(getArticle('interest')).toEqual('an');
expect(getArticle('ogre')).toEqual('an');
expect(getArticle('umbrella')).toEqual('an');
});
});
describe('ucFirst', () => {
test('should capitalize first character', () => {
expect(ucFirst('team')).toEqual('Team');
});
});
});

View File

@@ -1,36 +0,0 @@
import { required, maxLength } from '../../src/util/validators';
const i18n = { _: val => val };
describe('validators', () => {
test('required returns undefined if value given', () => {
expect(required(null, i18n)('some value')).toBeUndefined();
expect(required('oops', i18n)('some value')).toBeUndefined();
});
test('required returns default message if value missing', () => {
expect(required(null, i18n)('')).toEqual({ id: 'This field must not be blank' });
});
test('required returns custom message if value missing', () => {
expect(required('oops', i18n)('')).toEqual('oops');
});
test('required interprets white space as empty value', () => {
expect(required(null, i18n)(' ')).toEqual({ id: 'This field must not be blank' });
expect(required(null, i18n)('\t')).toEqual({ id: 'This field must not be blank' });
});
test('maxLength accepts value below max', () => {
expect(maxLength(10, i18n)('snazzy')).toBeUndefined();
});
test('maxLength accepts value equal to max', () => {
expect(maxLength(10, i18n)('abracadbra')).toBeUndefined();
});
test('maxLength rejects value above max', () => {
expect(maxLength(8, i18n)('abracadbra'))
.toEqual({ id: 'This field must not exceed {max} characters', values: { max: 8 } });
});
});