mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 17:05:18 +00:00
Select targets tests (#1307)
* Adds tests for the TargetDetails component * Adds tests for Select Targets Menu * Rename target_stub to target_mock * Adds tests for the SelectTargetsDropdown & minor refactor
This commit is contained in:
parent
71a384f34a
commit
7a7fb9eac9
@ -29,20 +29,19 @@ class SelectTargetsDropdown extends Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
this.mounted = true;
|
||||
|
||||
this.state = {
|
||||
isEmpty: false,
|
||||
isLoadingTargets: false,
|
||||
moreInfoTarget: null,
|
||||
query: '',
|
||||
targets: [],
|
||||
wrapperHeight: 0,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
componentWillMount () {
|
||||
this.fetchTargets();
|
||||
this.mounted = true;
|
||||
this.wrapperHeight = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -106,19 +105,6 @@ class SelectTargetsDropdown extends Component {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { target_type: targetType } = moreInfoTarget;
|
||||
|
||||
if (targetType.toLowerCase() === 'labels') {
|
||||
return Kolide.getLabelHosts(moreInfoTarget.id)
|
||||
.then((hosts) => {
|
||||
this.setState({
|
||||
moreInfoTarget: { ...moreInfoTarget, hosts },
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({ moreInfoTarget });
|
||||
|
||||
return false;
|
||||
@ -126,12 +112,10 @@ class SelectTargetsDropdown extends Component {
|
||||
}
|
||||
|
||||
onBackToResults = () => {
|
||||
this.setState({
|
||||
moreInfoTarget: null,
|
||||
});
|
||||
this.setState({ moreInfoTarget: null });
|
||||
}
|
||||
|
||||
fetchTargets = (query, selectedTargets = this.props.selectedTargets) => {
|
||||
fetchTargets = (query = '', selectedTargets = this.props.selectedTargets) => {
|
||||
const { onFetchTargets } = this.props;
|
||||
|
||||
if (!this.mounted) {
|
||||
@ -142,26 +126,25 @@ class SelectTargetsDropdown extends Component {
|
||||
|
||||
return Kolide.targets.loadAll(query, formatSelectedTargetsForApi(selectedTargets))
|
||||
.then((response) => {
|
||||
const {
|
||||
targets,
|
||||
} = response;
|
||||
const { targets } = response;
|
||||
const isEmpty = targets.length === 0;
|
||||
|
||||
if (!this.mounted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (targets.length === 0) {
|
||||
if (isEmpty) {
|
||||
// We don't want the lib's default "No Results" so we fake it
|
||||
targets.push({});
|
||||
|
||||
this.setState({ isEmpty: true });
|
||||
} else {
|
||||
this.setState({ isEmpty: false });
|
||||
}
|
||||
|
||||
onFetchTargets(query, response);
|
||||
|
||||
this.setState({ isLoadingTargets: false, targets });
|
||||
this.setState({
|
||||
isEmpty,
|
||||
isLoadingTargets: false,
|
||||
targets,
|
||||
});
|
||||
|
||||
return query;
|
||||
})
|
||||
|
@ -0,0 +1,169 @@
|
||||
import React from 'react';
|
||||
import expect, { createSpy, restoreSpies } from 'expect';
|
||||
import { mount } from 'enzyme';
|
||||
import nock from 'nock';
|
||||
import { noop } from 'lodash';
|
||||
|
||||
import SelectTargetsDropdown from 'components/forms/fields/SelectTargetsDropdown';
|
||||
import Test from 'test';
|
||||
|
||||
describe('SelectTargetsDropdown - component', () => {
|
||||
const defaultProps = {
|
||||
disabled: false,
|
||||
label: 'Select Targets',
|
||||
onFetchTargets: noop,
|
||||
onSelect: noop,
|
||||
selectedTargets: [],
|
||||
targetsCount: 0,
|
||||
};
|
||||
const DefaultComponent = mount(<SelectTargetsDropdown {...defaultProps} />);
|
||||
|
||||
afterEach(() => nock.cleanAll());
|
||||
|
||||
it('sets default state', () => {
|
||||
Test.Mocks.targetMock();
|
||||
|
||||
expect(DefaultComponent.state()).toEqual({
|
||||
isEmpty: false,
|
||||
isLoadingTargets: false,
|
||||
moreInfoTarget: null,
|
||||
query: '',
|
||||
targets: [],
|
||||
});
|
||||
});
|
||||
|
||||
describe('rendering', () => {
|
||||
beforeEach(() => Test.Mocks.targetMock());
|
||||
|
||||
it('renders', () => {
|
||||
expect(DefaultComponent.length).toEqual(1, 'Expected component to render');
|
||||
});
|
||||
|
||||
it('renders the SelectTargetsInput', () => {
|
||||
const SelectTargetsInput = DefaultComponent.find('SelectTargetsInput');
|
||||
|
||||
expect(SelectTargetsInput.length).toEqual(1, 'Expected SelectTargetsInput to render');
|
||||
});
|
||||
|
||||
it('renders a label when passed as a prop', () => {
|
||||
const noLabelProps = { ...defaultProps, label: undefined };
|
||||
const ComponentWithoutLabel = mount(<SelectTargetsDropdown {...noLabelProps} />);
|
||||
const Label = DefaultComponent.find('.target-select__label');
|
||||
const NoLabel = ComponentWithoutLabel.find('.target-select__label');
|
||||
|
||||
expect(Label.length).toEqual(1, 'Expected label to render');
|
||||
expect(NoLabel.length).toEqual(0, 'Expected label to not render');
|
||||
});
|
||||
|
||||
it('renders the error when passed as a prop', () => {
|
||||
const errorProps = { ...defaultProps, error: "You can't do this!" };
|
||||
const ErrorComponent = mount(<SelectTargetsDropdown {...errorProps} />);
|
||||
const Error = ErrorComponent.find('.target-select__label--error');
|
||||
const NoError = DefaultComponent.find('.target-select__label--error');
|
||||
|
||||
expect(Error.length).toEqual(1, 'Expected error to render');
|
||||
expect(NoError.length).toEqual(0, 'Expected error to not render');
|
||||
});
|
||||
|
||||
it('renders the target count', () => {
|
||||
const targetCountProps = { ...defaultProps, targetsCount: 10 };
|
||||
const TargetCountComponent = mount(<SelectTargetsDropdown {...targetCountProps} />);
|
||||
|
||||
expect(DefaultComponent.text()).toInclude('0 unique hosts');
|
||||
expect(TargetCountComponent.text()).toInclude('10 unique hosts');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#fetchTargets', () => {
|
||||
const apiResponseWithTargets = {
|
||||
targets: {
|
||||
hosts: [],
|
||||
labels: [Test.Stubs.labelStub],
|
||||
},
|
||||
};
|
||||
const apiResponseWithoutTargets = {
|
||||
targets: {
|
||||
hosts: [],
|
||||
labels: [],
|
||||
},
|
||||
};
|
||||
const defaultSelectedTargets = { hosts: [], labels: [] };
|
||||
const defaultParams = {
|
||||
query: '',
|
||||
selected: defaultSelectedTargets,
|
||||
};
|
||||
const expectedApiClientResponseWithTargets = {
|
||||
targets: [{ ...Test.Stubs.labelStub, target_type: 'labels' }],
|
||||
};
|
||||
|
||||
afterEach(() => restoreSpies());
|
||||
|
||||
it('calls the api', () => {
|
||||
nock.cleanAll();
|
||||
|
||||
const request = Test.Mocks.targetMock(defaultParams, apiResponseWithTargets);
|
||||
const Component = mount(<SelectTargetsDropdown {...defaultProps} />);
|
||||
const node = Component.node;
|
||||
|
||||
return node.fetchTargets()
|
||||
.then(() => {
|
||||
expect(request.isDone()).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls the onFetchTargets prop', () => {
|
||||
nock.cleanAll();
|
||||
|
||||
const onFetchTargets = createSpy();
|
||||
const props = { ...defaultProps, onFetchTargets };
|
||||
const Component = mount(<SelectTargetsDropdown {...props} />);
|
||||
const node = Component.node;
|
||||
|
||||
Test.Mocks.targetMock(defaultParams, apiResponseWithTargets);
|
||||
|
||||
return node.fetchTargets()
|
||||
.then(() => {
|
||||
expect(onFetchTargets).toHaveBeenCalledWith('', expectedApiClientResponseWithTargets);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not call the onFetchTargets prop when the component is not mounted', () => {
|
||||
const onFetchTargets = createSpy();
|
||||
const props = { ...defaultProps, onFetchTargets };
|
||||
const Component = mount(<SelectTargetsDropdown {...props} />);
|
||||
const node = Component.node;
|
||||
|
||||
node.mounted = false;
|
||||
|
||||
expect(node.fetchTargets()).toEqual(false);
|
||||
expect(onFetchTargets).toNotHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('sets state correctly when no targets are returned', () => {
|
||||
const Component = mount(<SelectTargetsDropdown {...defaultProps} />);
|
||||
const node = Component.node;
|
||||
|
||||
Test.Mocks.targetMock(defaultParams, apiResponseWithoutTargets);
|
||||
|
||||
return node.fetchTargets()
|
||||
.then(() => {
|
||||
expect(Component.state('isEmpty')).toEqual(true);
|
||||
expect(Component.state('targets')).toEqual([{}]);
|
||||
expect(Component.state('isLoadingTargets')).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the query', () => {
|
||||
const query = 'select * from users';
|
||||
const Component = mount(<SelectTargetsDropdown {...defaultProps} />);
|
||||
const node = Component.node;
|
||||
|
||||
Test.Mocks.targetMock(defaultParams);
|
||||
|
||||
return node.fetchTargets(query)
|
||||
.then((q) => {
|
||||
expect(q).toEqual(query);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,93 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import expect, { createSpy, restoreSpies } from 'expect';
|
||||
import { mount } from 'enzyme';
|
||||
import { noop } from 'lodash';
|
||||
|
||||
import SelectTargetsMenuWrapper from 'components/forms/fields/SelectTargetsDropdown/SelectTargetsMenu';
|
||||
import Test from 'test';
|
||||
|
||||
const DummyOption = (props) => {
|
||||
return (
|
||||
<div>{props.children}</div>
|
||||
);
|
||||
};
|
||||
|
||||
DummyOption.propTypes = { children: PropTypes.node };
|
||||
|
||||
describe('SelectTargetsMenu - component', () => {
|
||||
afterEach(() => restoreSpies());
|
||||
|
||||
let onMoreInfoClick = noop;
|
||||
const moreInfoTarget = undefined;
|
||||
const handleBackToResults = noop;
|
||||
const defaultProps = {
|
||||
focusedOption: undefined,
|
||||
instancePrefix: '',
|
||||
onFocus: noop,
|
||||
onOptionRef: noop,
|
||||
onSelect: noop,
|
||||
optionComponent: DummyOption,
|
||||
};
|
||||
|
||||
describe('rendering', () => {
|
||||
it('renders', () => {
|
||||
const SelectTargetsMenu = SelectTargetsMenuWrapper(onMoreInfoClick, moreInfoTarget, handleBackToResults);
|
||||
const Component = mount(<SelectTargetsMenu {...defaultProps} />);
|
||||
|
||||
expect(Component.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('renders no target text', () => {
|
||||
const SelectTargetsMenu = SelectTargetsMenuWrapper(onMoreInfoClick, moreInfoTarget, handleBackToResults);
|
||||
const Component = mount(<SelectTargetsMenu {...defaultProps} />);
|
||||
const componentText = Component.text();
|
||||
|
||||
expect(componentText).toInclude('Unable to find any matching labels.');
|
||||
expect(componentText).toInclude('Unable to find any matching hosts.');
|
||||
});
|
||||
|
||||
it('renders a target option component for each target', () => {
|
||||
const SelectTargetsMenu = SelectTargetsMenuWrapper(onMoreInfoClick, moreInfoTarget, handleBackToResults);
|
||||
const options = [Test.Stubs.labelStub, Test.Stubs.hostStub];
|
||||
const props = { ...defaultProps, options };
|
||||
const Component = mount(<SelectTargetsMenu {...props} />);
|
||||
const TargetOption = Component.find('TargetOption');
|
||||
|
||||
expect(TargetOption.length).toEqual(options.length);
|
||||
|
||||
expect(options).toInclude(TargetOption.first().prop('target'));
|
||||
expect(options).toInclude(TargetOption.last().prop('target'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('clicking a target', () => {
|
||||
it('calls the onMoreInfoClick function', () => {
|
||||
const spy = createSpy();
|
||||
onMoreInfoClick = (t) => {
|
||||
return () => spy(t);
|
||||
};
|
||||
const SelectTargetsMenu = SelectTargetsMenuWrapper(onMoreInfoClick, moreInfoTarget, handleBackToResults);
|
||||
const options = [Test.Stubs.labelStub];
|
||||
const props = { ...defaultProps, options };
|
||||
const Component = mount(<SelectTargetsMenu {...props} />);
|
||||
const TargetOption = Component.find('TargetOption');
|
||||
|
||||
TargetOption.find('.target-option__target-content').simulate('click');
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(Test.Stubs.labelStub);
|
||||
});
|
||||
|
||||
it('calls the onSelect prop when the add button is clicked', () => {
|
||||
const spy = createSpy();
|
||||
const SelectTargetsMenu = SelectTargetsMenuWrapper(onMoreInfoClick, moreInfoTarget, handleBackToResults);
|
||||
const options = [Test.Stubs.labelStub];
|
||||
const props = { ...defaultProps, onSelect: spy, options };
|
||||
const Component = mount(<SelectTargetsMenu {...props} />);
|
||||
const TargetOption = Component.find('TargetOption');
|
||||
|
||||
TargetOption.find('.target-option__add-btn').simulate('click');
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,121 @@
|
||||
import React from 'react';
|
||||
import expect, { createSpy, restoreSpies } from 'expect';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import TargetDetails from 'components/forms/fields/SelectTargetsDropdown/TargetDetails';
|
||||
import Test from 'test';
|
||||
|
||||
describe('TargetDetails - component', () => {
|
||||
afterEach(() => restoreSpies());
|
||||
|
||||
const defaultProps = { target: Test.Stubs.labelStub };
|
||||
|
||||
describe('rendering', () => {
|
||||
it('does not render without a target', () => {
|
||||
const Component = mount(<TargetDetails />);
|
||||
|
||||
expect(Component.html()).toNotExist();
|
||||
});
|
||||
|
||||
it('renders when there is a target', () => {
|
||||
const Component = mount(<TargetDetails {...defaultProps} />);
|
||||
|
||||
expect(Component.length).toEqual(1);
|
||||
});
|
||||
|
||||
describe('when the target is a host', () => {
|
||||
it('renders target information', () => {
|
||||
const target = {
|
||||
display_text: 'display_text',
|
||||
host_mac: 'host_mac',
|
||||
host_ip_address: 'host_ip_address',
|
||||
memory: 1074000000, // 1 GB memory
|
||||
osquery_version: 'osquery_version',
|
||||
os_version: 'os_version',
|
||||
platform: 'platform',
|
||||
status: 'status',
|
||||
};
|
||||
const Component = mount(<TargetDetails target={target} />);
|
||||
const componentText = Component.text();
|
||||
|
||||
expect(componentText).toInclude(target.display_text);
|
||||
expect(componentText).toInclude(target.host_mac);
|
||||
expect(componentText).toInclude(target.host_ip_address);
|
||||
expect(componentText).toInclude('1.00 GB');
|
||||
expect(componentText).toInclude(target.osquery_version);
|
||||
expect(componentText).toInclude(target.os_version);
|
||||
expect(componentText).toInclude(target.platform);
|
||||
expect(componentText).toInclude(target.status);
|
||||
});
|
||||
|
||||
it('renders a success check icon when the target is online', () => {
|
||||
const target = { ...Test.Stubs.hostStub, status: 'online' };
|
||||
const Component = mount(<TargetDetails target={target} />);
|
||||
const Icon = Component.find('Icon');
|
||||
const onlineIcon = Icon.find('.host-target__icon--online');
|
||||
const offlineIcon = Icon.find('.host-target__icon--offline');
|
||||
|
||||
expect(onlineIcon.length).toEqual(1, 'Expected the online icon to render');
|
||||
expect(offlineIcon.length).toEqual(0, 'Expected the offline icon to not render');
|
||||
});
|
||||
|
||||
it('renders a offline icon when the target is offline', () => {
|
||||
const target = { ...Test.Stubs.hostStub, status: 'offline' };
|
||||
const Component = mount(<TargetDetails target={target} />);
|
||||
const Icon = Component.find('Icon');
|
||||
const onlineIcon = Icon.find('.host-target__icon--online');
|
||||
const offlineIcon = Icon.find('.host-target__icon--offline');
|
||||
|
||||
expect(onlineIcon.length).toEqual(0, 'Expected the online icon to not render');
|
||||
expect(offlineIcon.length).toEqual(1, 'Expected the offline icon to render');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the target is a label', () => {
|
||||
const target = {
|
||||
...Test.Stubs.labelStub,
|
||||
count: 10,
|
||||
description: 'target description',
|
||||
display_text: 'display_text',
|
||||
label_type: 0,
|
||||
online: 10,
|
||||
query: 'query',
|
||||
};
|
||||
const Component = mount(<TargetDetails target={target} />);
|
||||
|
||||
it('renders the label data', () => {
|
||||
const componentText = Component.text();
|
||||
|
||||
expect(componentText).toInclude('100% ONLINE');
|
||||
expect(componentText).toInclude(target.display_text);
|
||||
expect(componentText).toInclude(target.description);
|
||||
});
|
||||
|
||||
it('renders a read-only AceEditor', () => {
|
||||
const AceEditor = Component.find('ReactAce');
|
||||
|
||||
expect(AceEditor.prop('readOnly')).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('calls the handleBackToResults prop when the back button is clicked', () => {
|
||||
const labelSpy = createSpy();
|
||||
const labelProps = { ...defaultProps, handleBackToResults: labelSpy };
|
||||
const LabelComponent = mount(<TargetDetails {...labelProps} />);
|
||||
const LabelBackButton = LabelComponent.find('.label-target__back');
|
||||
|
||||
const hostSpy = createSpy();
|
||||
const hostProps = { target: Test.Stubs.hostStub, handleBackToResults: hostSpy };
|
||||
const HostComponent = mount(<TargetDetails {...hostProps} />);
|
||||
const HostBackButton = HostComponent.find('.host-target__back');
|
||||
|
||||
LabelBackButton.simulate('click');
|
||||
|
||||
expect(labelSpy).toHaveBeenCalled();
|
||||
|
||||
HostBackButton.simulate('click');
|
||||
|
||||
expect(hostSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -4,11 +4,11 @@ import { mount } from 'enzyme';
|
||||
import { noop } from 'lodash';
|
||||
|
||||
import { fillInFormInput } from 'test/helpers';
|
||||
import targetStub from 'test/target_stub';
|
||||
import targetMock from 'test/target_mock';
|
||||
import PackForm from './index';
|
||||
|
||||
describe('PackForm - component', () => {
|
||||
beforeEach(targetStub);
|
||||
beforeEach(targetMock);
|
||||
afterEach(restoreSpies);
|
||||
|
||||
it('renders the base error', () => {
|
||||
|
@ -4,7 +4,7 @@ import { mount } from 'enzyme';
|
||||
import { noop } from 'lodash';
|
||||
|
||||
import { fillInFormInput } from 'test/helpers';
|
||||
import targetStub from 'test/target_stub';
|
||||
import targetMock from 'test/target_mock';
|
||||
import QueryForm from './index';
|
||||
|
||||
const query = {
|
||||
@ -16,7 +16,7 @@ const query = {
|
||||
const queryText = 'SELECT * FROM users';
|
||||
|
||||
describe('QueryForm - component', () => {
|
||||
beforeEach(targetStub);
|
||||
beforeEach(targetMock);
|
||||
afterEach(restoreSpies);
|
||||
|
||||
it('renders the base error', () => {
|
||||
|
10
frontend/test/index.js
Normal file
10
frontend/test/index.js
Normal file
@ -0,0 +1,10 @@
|
||||
import helpers from 'test/helpers';
|
||||
import stubs from 'test/stubs';
|
||||
import mocks from 'test/mocks';
|
||||
import targetMock from 'test/target_mock';
|
||||
|
||||
export default {
|
||||
Helpers: helpers,
|
||||
Mocks: { ...mocks, targetMock },
|
||||
Stubs: stubs,
|
||||
};
|
21
frontend/test/target_mock.js
Normal file
21
frontend/test/target_mock.js
Normal file
@ -0,0 +1,21 @@
|
||||
import nock from 'nock';
|
||||
|
||||
const defaultParams = {
|
||||
selected: {
|
||||
hosts: [],
|
||||
labels: [],
|
||||
},
|
||||
};
|
||||
const defaultResponse = {
|
||||
targets: {
|
||||
hosts: [],
|
||||
labels: [],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
export default (params = defaultParams, response = defaultResponse, responseStatus = 200) => {
|
||||
return nock('http://localhost:8080')
|
||||
.post('/api/v1/kolide/targets', JSON.stringify(params))
|
||||
.reply(responseStatus, response);
|
||||
};
|
@ -1,16 +0,0 @@
|
||||
import nock from 'nock';
|
||||
|
||||
const defaultParams = {
|
||||
selected: {
|
||||
hosts: [],
|
||||
labels: [],
|
||||
},
|
||||
};
|
||||
|
||||
export default (params = defaultParams) => {
|
||||
nock('http://localhost:8080')
|
||||
.post('/api/v1/kolide/targets', JSON.stringify(params))
|
||||
.reply(200, {
|
||||
targets: [],
|
||||
});
|
||||
};
|
Loading…
Reference in New Issue
Block a user