mirror of
https://github.com/ansible/awx.git
synced 2026-02-16 02:30:01 -03:30
Reorganize file locations/directory structure (#270)
Reorganize file locations
This commit is contained in:
118
src/App.test.jsx
Normal file
118
src/App.test.jsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import React from 'react';
|
||||
|
||||
import { mountWithContexts, waitForElement } from '../testUtils/enzymeHelpers';
|
||||
import { asyncFlush } from '../jest.setup';
|
||||
|
||||
import App from './App';
|
||||
import { ConfigAPI, MeAPI, RootAPI } from './api';
|
||||
|
||||
jest.mock('./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();
|
||||
});
|
||||
});
|
||||
10
src/RootProvider.test.jsx
Normal file
10
src/RootProvider.test.jsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { getLanguage } from './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');
|
||||
});
|
||||
});
|
||||
97
src/api/Base.test.jsx
Normal file
97
src/api/Base.test.jsx
Normal file
@@ -0,0 +1,97 @@
|
||||
import Base from './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();
|
||||
});
|
||||
});
|
||||
39
src/api/models/Organizations.test.jsx
Normal file
39
src/api/models/Organizations.test.jsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import Organizations from './Organizations';
|
||||
import { describeNotificationMixin } from '../../../testUtils/apiReusable';
|
||||
|
||||
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]');
|
||||
45
src/api/models/Root.test.jsx
Normal file
45
src/api/models/Root.test.jsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import Root from './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();
|
||||
});
|
||||
});
|
||||
35
src/api/models/Teams.test.jsx
Normal file
35
src/api/models/Teams.test.jsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import Teams from './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();
|
||||
});
|
||||
});
|
||||
35
src/api/models/Users.test.jsx
Normal file
35
src/api/models/Users.test.jsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import Users from './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();
|
||||
});
|
||||
});
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
TextListItem
|
||||
} from '@patternfly/react-core';
|
||||
|
||||
import { BrandName } from '../variables';
|
||||
import brandLogoImg from '../../images/brand-logo.svg';
|
||||
import { BrandName } from '../../variables';
|
||||
import brandLogoImg from '../../../images/brand-logo.svg';
|
||||
|
||||
class About extends React.Component {
|
||||
static createSpeechBubble (version) {
|
||||
26
src/components/About/About.test.jsx
Normal file
26
src/components/About/About.test.jsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import About from './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();
|
||||
});
|
||||
});
|
||||
1
src/components/About/index.js
Normal file
1
src/components/About/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './About';
|
||||
224
src/components/AddRole/AddResourceRole.test.jsx
Normal file
224
src/components/AddRole/AddResourceRole.test.jsx
Normal file
@@ -0,0 +1,224 @@
|
||||
/* eslint-disable react/jsx-pascal-case */
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import AddResourceRole, { _AddResourceRole } from './AddResourceRole';
|
||||
import { TeamsAPI, UsersAPI } from '../../api';
|
||||
|
||||
jest.mock('../../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();
|
||||
});
|
||||
});
|
||||
16
src/components/AddRole/CheckboxCard.test.jsx
Normal file
16
src/components/AddRole/CheckboxCard.test.jsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import CheckboxCard from './CheckboxCard';
|
||||
|
||||
describe('<CheckboxCard />', () => {
|
||||
let wrapper;
|
||||
test('initially renders without crashing', () => {
|
||||
wrapper = shallow(
|
||||
<CheckboxCard
|
||||
name="Foobar"
|
||||
itemId={5}
|
||||
/>
|
||||
);
|
||||
expect(wrapper.length).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -5,7 +5,7 @@ import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import PaginatedDataList from '../PaginatedDataList';
|
||||
import DataListToolbar from '../DataListToolbar';
|
||||
import CheckboxListItem from '../ListItem';
|
||||
import CheckboxListItem from '../CheckboxListItem';
|
||||
import SelectedList from '../SelectedList';
|
||||
import { getQSConfig, parseNamespacedQueryString } from '../../util/qs';
|
||||
|
||||
|
||||
118
src/components/AddRole/SelectResourceStep.test.jsx
Normal file
118
src/components/AddRole/SelectResourceStep.test.jsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import React from 'react';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import { shallow } from 'enzyme';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import { sleep } from '../../../testUtils/testUtils';
|
||||
import SelectResourceStep from './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]);
|
||||
});
|
||||
});
|
||||
64
src/components/AddRole/SelectRoleStep.test.jsx
Normal file
64
src/components/AddRole/SelectRoleStep.test.jsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import SelectRoleStep from './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();
|
||||
});
|
||||
});
|
||||
27
src/components/AddRole/SelectableCard.test.jsx
Normal file
27
src/components/AddRole/SelectableCard.test.jsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import SelectableCard from './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();
|
||||
});
|
||||
});
|
||||
5
src/components/AddRole/index.js
Normal file
5
src/components/AddRole/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export { default as AddResourceRole } from './AddResourceRole';
|
||||
export { default as CheckboxCard } from './CheckboxCard';
|
||||
export { default as SelectableCard } from './SelectableCard';
|
||||
export { default as SelectResourceStep } from './SelectResourceStep';
|
||||
export { default as SelectRoleStep } from './SelectRoleStep';
|
||||
13
src/components/AlertModal/AlertModal.test.jsx
Normal file
13
src/components/AlertModal/AlertModal.test.jsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import AlertModal from './AlertModal';
|
||||
|
||||
describe('AlertModal', () => {
|
||||
test('renders the expected content', () => {
|
||||
const wrapper = mount(
|
||||
<AlertModal title="Danger!" />
|
||||
);
|
||||
expect(wrapper).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
1
src/components/AlertModal/index.js
Normal file
1
src/components/AlertModal/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './AlertModal';
|
||||
49
src/components/AnsibleSelect/AnsibleSelect.test.jsx
Normal file
49
src/components/AnsibleSelect/AnsibleSelect.test.jsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import AnsibleSelect, { _AnsibleSelect } from './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);
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1 @@
|
||||
import AnsibleSelect from './AnsibleSelect';
|
||||
|
||||
export default AnsibleSelect;
|
||||
export { default } from './AnsibleSelect';
|
||||
|
||||
13
src/components/Background/Background.test.jsx
Normal file
13
src/components/Background/Background.test.jsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import Background from './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);
|
||||
});
|
||||
});
|
||||
1
src/components/Background/index.js
Normal file
1
src/components/Background/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './Background';
|
||||
24
src/components/BrandLogo/BrandLogo.test.jsx
Normal file
24
src/components/BrandLogo/BrandLogo.test.jsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import BrandLogo from './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);
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1 @@
|
||||
import BrandLogo from './BrandLogo';
|
||||
|
||||
export default BrandLogo;
|
||||
export { default } from './BrandLogo';
|
||||
|
||||
68
src/components/Breadcrumbs/Breadcrumbs.test.jsx
Normal file
68
src/components/Breadcrumbs/Breadcrumbs.test.jsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import Breadcrumbs from './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();
|
||||
});
|
||||
});
|
||||
});
|
||||
1
src/components/Breadcrumbs/index.js
Normal file
1
src/components/Breadcrumbs/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './Breadcrumbs';
|
||||
23
src/components/CardCloseButton/CardCloseButton.test.jsx
Normal file
23
src/components/CardCloseButton/CardCloseButton.test.jsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import CardCloseButton from './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');
|
||||
});
|
||||
});
|
||||
1
src/components/CardCloseButton/index.js
Normal file
1
src/components/CardCloseButton/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './CardCloseButton';
|
||||
18
src/components/CheckboxListItem/CheckboxListItem.test.jsx
Normal file
18
src/components/CheckboxListItem/CheckboxListItem.test.jsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import CheckboxListItem from './CheckboxListItem';
|
||||
|
||||
describe('CheckboxListItem', () => {
|
||||
test('renders the expected content', () => {
|
||||
const wrapper = mount(
|
||||
<CheckboxListItem
|
||||
itemId={1}
|
||||
name="Buzz"
|
||||
isSelected={false}
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
);
|
||||
expect(wrapper).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
1
src/components/CheckboxListItem/index.js
Normal file
1
src/components/CheckboxListItem/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './CheckboxListItem';
|
||||
11
src/components/Chip/Chip.test.jsx
Normal file
11
src/components/Chip/Chip.test.jsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import Chip from './Chip';
|
||||
|
||||
describe('Chip', () => {
|
||||
test('renders the expected content', () => {
|
||||
const wrapper = mount(<Chip />);
|
||||
expect(wrapper).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
69
src/components/Chip/ChipGroup.test.jsx
Normal file
69
src/components/Chip/ChipGroup.test.jsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import { ChipGroup, Chip } from '.';
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { Formik } from 'formik';
|
||||
import { sleep } from '../../../__tests__/testUtils';
|
||||
import { sleep } from '../../../testUtils/testUtils';
|
||||
import VariablesField from './VariablesField';
|
||||
|
||||
describe('VariablesField', () => {
|
||||
|
||||
11
src/components/ContentEmpty/ContentEmpty.test.jsx
Normal file
11
src/components/ContentEmpty/ContentEmpty.test.jsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
|
||||
import ContentEmpty from './ContentEmpty';
|
||||
|
||||
describe('ContentEmpty', () => {
|
||||
test('renders the expected content', () => {
|
||||
const wrapper = mountWithContexts(<ContentEmpty />);
|
||||
expect(wrapper).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
1
src/components/ContentEmpty/index.js
Normal file
1
src/components/ContentEmpty/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './ContentEmpty';
|
||||
11
src/components/ContentError/ContentError.test.jsx
Normal file
11
src/components/ContentError/ContentError.test.jsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
|
||||
import ContentError from './ContentError';
|
||||
|
||||
describe('ContentError', () => {
|
||||
test('renders the expected content', () => {
|
||||
const wrapper = mountWithContexts(<ContentError />);
|
||||
expect(wrapper).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
1
src/components/ContentError/index.js
Normal file
1
src/components/ContentError/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './ContentError';
|
||||
11
src/components/ContentLoading/ContentLoading.test.jsx
Normal file
11
src/components/ContentLoading/ContentLoading.test.jsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
|
||||
import ContentLoading from './ContentLoading';
|
||||
|
||||
describe('ContentLoading', () => {
|
||||
test('renders the expected content', () => {
|
||||
const wrapper = mountWithContexts(<ContentLoading />);
|
||||
expect(wrapper).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
1
src/components/ContentLoading/index.js
Normal file
1
src/components/ContentLoading/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './ContentLoading';
|
||||
199
src/components/DataListToolbar/DataListToolbar.test.jsx
Normal file
199
src/components/DataListToolbar/DataListToolbar.test.jsx
Normal file
@@ -0,0 +1,199 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import DataListToolbar from './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');
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1 @@
|
||||
import DataListToolbar from './DataListToolbar';
|
||||
|
||||
export default DataListToolbar;
|
||||
export { default } from './DataListToolbar';
|
||||
|
||||
13
src/components/DetailList/Detail.test.jsx
Normal file
13
src/components/DetailList/Detail.test.jsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import Detail from './Detail';
|
||||
|
||||
describe('Detail', () => {
|
||||
test('renders the expected content', () => {
|
||||
const wrapper = mount(
|
||||
<Detail label="foo" />
|
||||
);
|
||||
expect(wrapper).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
11
src/components/DetailList/DetailList.test.jsx
Normal file
11
src/components/DetailList/DetailList.test.jsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import DetailList from './DetailList';
|
||||
|
||||
describe('DetailList', () => {
|
||||
test('renders the expected content', () => {
|
||||
const wrapper = mount(<DetailList />);
|
||||
expect(wrapper).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
20
src/components/ExpandCollapse/ExpandCollapse.test.jsx
Normal file
20
src/components/ExpandCollapse/ExpandCollapse.test.jsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import ExpandCollapse from './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();
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1 @@
|
||||
import ExpandCollapse from './ExpandCollapse';
|
||||
|
||||
export default ExpandCollapse;
|
||||
export { default } from './ExpandCollapse';
|
||||
|
||||
16
src/components/FormActionGroup/FormActionGroup.test.jsx
Normal file
16
src/components/FormActionGroup/FormActionGroup.test.jsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
|
||||
import FormActionGroup from './FormActionGroup';
|
||||
|
||||
describe('FormActionGroup', () => {
|
||||
test('renders the expected content', () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<FormActionGroup
|
||||
onSubmit={() => {}}
|
||||
onCancel={() => {}}
|
||||
/>
|
||||
);
|
||||
expect(wrapper).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
1
src/components/FormActionGroup/index.js
Normal file
1
src/components/FormActionGroup/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './FormActionGroup';
|
||||
@@ -11,7 +11,7 @@ function FormField (props) {
|
||||
name={name}
|
||||
validate={validate}
|
||||
render={({ field, form }) => {
|
||||
const isValid = !form.touched[field.name] || !form.errors[field.name];
|
||||
const isValid = form && (!form.touched[field.name] || !form.errors[field.name]);
|
||||
|
||||
return (
|
||||
<FormGroup
|
||||
1
src/components/FormField/index.js
Normal file
1
src/components/FormField/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './FormField';
|
||||
11
src/components/FormRow/FormRow.test.jsx
Normal file
11
src/components/FormRow/FormRow.test.jsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import FormRow from './FormRow';
|
||||
|
||||
describe('FormRow', () => {
|
||||
test('renders the expected content', () => {
|
||||
const wrapper = mount(<FormRow />);
|
||||
expect(wrapper).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
1
src/components/FormRow/index.js
Normal file
1
src/components/FormRow/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './FormRow';
|
||||
@@ -1,3 +0,0 @@
|
||||
import CheckboxListItem from './CheckboxListItem';
|
||||
|
||||
export default CheckboxListItem;
|
||||
@@ -13,7 +13,7 @@ import { t } from '@lingui/macro';
|
||||
|
||||
import PaginatedDataList from '../PaginatedDataList';
|
||||
import DataListToolbar from '../DataListToolbar';
|
||||
import CheckboxListItem from '../ListItem';
|
||||
import CheckboxListItem from '../CheckboxListItem';
|
||||
import SelectedList from '../SelectedList';
|
||||
import { ChipGroup, Chip } from '../Chip';
|
||||
import { getQSConfig, parseNamespacedQueryString } from '../../util/qs';
|
||||
|
||||
228
src/components/Lookup/Lookup.test.jsx
Normal file
228
src/components/Lookup/Lookup.test.jsx
Normal file
@@ -0,0 +1,228 @@
|
||||
/* eslint-disable react/jsx-pascal-case */
|
||||
import React from 'react';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import Lookup, { _Lookup } from './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);
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1 @@
|
||||
import Lookup from './Lookup';
|
||||
|
||||
export default Lookup;
|
||||
export { default } from './Lookup';
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import { Nav } from '@patternfly/react-core';
|
||||
import NavExpandableGroup from './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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
1
src/components/NavExpandableGroup/index.js
Normal file
1
src/components/NavExpandableGroup/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './NavExpandableGroup';
|
||||
108
src/components/NotificationsList/NotificationListItem.test.jsx
Normal file
108
src/components/NotificationsList/NotificationListItem.test.jsx
Normal file
@@ -0,0 +1,108 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import NotificationListItem from './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');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,456 @@
|
||||
// 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>
|
||||
`;
|
||||
1
src/components/NotificationsList/index.js
Normal file
1
src/components/NotificationsList/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './NotificationListItem';
|
||||
46
src/components/PageHeaderToolbar/PageHeaderToolbar.test.jsx
Normal file
46
src/components/PageHeaderToolbar/PageHeaderToolbar.test.jsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import PageHeaderToolbar from './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();
|
||||
});
|
||||
});
|
||||
1
src/components/PageHeaderToolbar/index.js
Normal file
1
src/components/PageHeaderToolbar/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './PageHeaderToolbar';
|
||||
123
src/components/PaginatedDataList/PaginatedDataList.test.jsx
Normal file
123
src/components/PaginatedDataList/PaginatedDataList.test.jsx
Normal file
@@ -0,0 +1,123 @@
|
||||
import React from 'react';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import { sleep } from '../../../testUtils/testUtils';
|
||||
import PaginatedDataList from './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');
|
||||
});
|
||||
});
|
||||
25
src/components/PaginatedDataList/ToolbarAddButton.test.jsx
Normal file
25
src/components/PaginatedDataList/ToolbarAddButton.test.jsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import ToolbarAddButton from './ToolbarAddButton';
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,76 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import ToolbarDeleteButton from './ToolbarDeleteButton';
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,202 @@
|
||||
// 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>
|
||||
`;
|
||||
@@ -1,6 +1,4 @@
|
||||
import PaginatedDataList from './PaginatedDataList';
|
||||
|
||||
export default PaginatedDataList;
|
||||
export { default } from './PaginatedDataList';
|
||||
export { default as PaginatedDataListItem } from './PaginatedDataListItem';
|
||||
export { default as ToolbarDeleteButton } from './ToolbarDeleteButton';
|
||||
export { default as ToolbarAddButton } from './ToolbarAddButton';
|
||||
|
||||
16
src/components/Pagination/Pagination.test.jsx
Normal file
16
src/components/Pagination/Pagination.test.jsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
|
||||
import Pagination from './Pagination';
|
||||
|
||||
describe('Pagination', () => {
|
||||
test('renders the expected content', () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<Pagination
|
||||
itemCount={0}
|
||||
max={9000}
|
||||
/>
|
||||
);
|
||||
expect(wrapper).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1 @@
|
||||
import Pagination from './Pagination';
|
||||
|
||||
export default Pagination;
|
||||
export { default } from './Pagination';
|
||||
|
||||
64
src/components/RoutedTabs/RoutedTabs.test.jsx
Normal file
64
src/components/RoutedTabs/RoutedTabs.test.jsx
Normal file
@@ -0,0 +1,64 @@
|
||||
/* eslint-disable react/jsx-pascal-case */
|
||||
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 './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);
|
||||
});
|
||||
});
|
||||
1
src/components/RoutedTabs/index.js
Normal file
1
src/components/RoutedTabs/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './RoutedTabs';
|
||||
70
src/components/Search/Search.test.jsx
Normal file
70
src/components/Search/Search.test.jsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import Search from './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');
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1 @@
|
||||
import Search from './Search';
|
||||
|
||||
export default Search;
|
||||
export { default } from './Search';
|
||||
|
||||
63
src/components/SelectedList/SelectedList.test.jsx
Normal file
63
src/components/SelectedList/SelectedList.test.jsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { ChipGroup } from '../Chip';
|
||||
import SelectedList from './SelectedList';
|
||||
|
||||
describe('<SelectedList />', () => {
|
||||
test('initially renders succesfully', () => {
|
||||
const mockSelected = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'foo'
|
||||
}, {
|
||||
id: 2,
|
||||
name: 'bar'
|
||||
}
|
||||
];
|
||||
mount(
|
||||
<SelectedList
|
||||
label="Selectedeeee"
|
||||
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'
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1 @@
|
||||
import SelectedList from './SelectedList';
|
||||
|
||||
export default SelectedList;
|
||||
export { default } from './SelectedList';
|
||||
|
||||
188
src/components/Sort/Sort.test.jsx
Normal file
188
src/components/Sort/Sort.test.jsx
Normal file
@@ -0,0 +1,188 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import Sort from './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);
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1 @@
|
||||
import Sort from './Sort';
|
||||
|
||||
export default Sort;
|
||||
export { default } from './Sort';
|
||||
|
||||
11
src/components/VerticalSeparator/VerticalSeparator.test.jsx
Normal file
11
src/components/VerticalSeparator/VerticalSeparator.test.jsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import VerticalSeparator from './VerticalSeparator';
|
||||
|
||||
describe('VerticalSeparator', () => {
|
||||
test('renders the expected content', () => {
|
||||
const wrapper = mount(<VerticalSeparator />);
|
||||
expect(wrapper).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1 @@
|
||||
import VerticalSeparator from './VerticalSeparator';
|
||||
|
||||
export default VerticalSeparator;
|
||||
export { default } from './VerticalSeparator';
|
||||
|
||||
@@ -20,29 +20,29 @@ import App from './App';
|
||||
import { BrandName } from './variables';
|
||||
import { isAuthenticated } from './util/auth';
|
||||
|
||||
import Applications from './pages/Applications';
|
||||
import Credentials from './pages/Credentials';
|
||||
import CredentialTypes from './pages/CredentialTypes';
|
||||
import Dashboard from './pages/Dashboard';
|
||||
import InstanceGroups from './pages/InstanceGroups';
|
||||
import Inventories from './pages/Inventories';
|
||||
import InventoryScripts from './pages/InventoryScripts';
|
||||
import Jobs from './pages/Jobs';
|
||||
import Login from './pages/Login';
|
||||
import ManagementJobs from './pages/ManagementJobs';
|
||||
import NotificationTemplates from './pages/NotificationTemplates';
|
||||
import Organizations from './pages/Organizations/Organizations';
|
||||
import Portal from './pages/Portal';
|
||||
import Projects from './pages/Projects';
|
||||
import Schedules from './pages/Schedules';
|
||||
import AuthSettings from './pages/AuthSettings';
|
||||
import JobsSettings from './pages/JobsSettings';
|
||||
import SystemSettings from './pages/SystemSettings';
|
||||
import UISettings from './pages/UISettings';
|
||||
import License from './pages/License';
|
||||
import Teams from './pages/Teams';
|
||||
import Templates from './pages/Templates/Templates';
|
||||
import Users from './pages/Users';
|
||||
import Applications from './screens/Application';
|
||||
import Credentials from './screens/Credential';
|
||||
import CredentialTypes from './screens/CredentialType';
|
||||
import Dashboard from './screens/Dashboard';
|
||||
import InstanceGroups from './screens/InstanceGroup';
|
||||
import Inventories from './screens/Inventory';
|
||||
import InventoryScripts from './screens/InventoryScript';
|
||||
import { Jobs } from './screens/Job';
|
||||
import Login from './screens/Login';
|
||||
import ManagementJobs from './screens/ManagementJob';
|
||||
import NotificationTemplates from './screens/NotificationTemplate';
|
||||
import Organizations from './screens/Organization';
|
||||
import Portal from './screens/Portal';
|
||||
import Projects from './screens/Project';
|
||||
import Schedules from './screens/Schedule';
|
||||
import AuthSettings from './screens/AuthSetting';
|
||||
import JobsSettings from './screens/JobsSetting';
|
||||
import SystemSettings from './screens/SystemSetting';
|
||||
import UISettings from './screens/UISetting';
|
||||
import License from './screens/License';
|
||||
import Teams from './screens/Team';
|
||||
import Templates from './screens/Template';
|
||||
import Users from './screens/User';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export function main (render) {
|
||||
|
||||
17
src/index.test.jsx
Normal file
17
src/index.test.jsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { main } from './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);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +0,0 @@
|
||||
import JobDetail from './JobDetail';
|
||||
|
||||
export default JobDetail;
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
import JobOutput from './JobOutput';
|
||||
|
||||
export default JobOutput;
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
export { default as Job } from './Job';
|
||||
export { default } from './Jobs';
|
||||
29
src/screens/Application/Applications.test.jsx
Normal file
29
src/screens/Application/Applications.test.jsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
import Applications from './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();
|
||||
});
|
||||
});
|
||||
});
|
||||
1
src/screens/Application/index.js
Normal file
1
src/screens/Application/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './Applications';
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user