[js-testing] more tests for SelectControl (#2877)

* rename spec folder

* remove special handling for viz_type control since it now uses VizTypeControl

* add test for getOptions

* linting

* add test for cwp

* linting
This commit is contained in:
Alanna Scott
2017-05-31 10:52:02 -07:00
committed by GitHub
parent 1e7773eb16
commit 90e4d6469d
21 changed files with 80 additions and 52 deletions

View File

@@ -0,0 +1,22 @@
// this test must be commented out because ChartContainer is now importing files
// from visualizations/*.js which are also importing css files which breaks in the testing env
// import React from 'react';
// import { expect } from 'chai';
// import { describe, it } from 'mocha';
// import ChartContainer from '../../../../javascripts/explore/components/ChartContainer';
// describe('ChartContainer', () => {
// const mockProps = {
// sliceName: 'Trend Line',
// vizType: 'line',
// height: '500px',
// };
// it('renders when vizType is line', () => {
// expect(
// React.isValidElement(<ChartContainer {...mockProps} />)
// ).to.equal(true);
// });
// });

View File

@@ -0,0 +1,27 @@
/* eslint-disable no-unused-expressions */
import React from 'react';
import { Checkbox } from 'react-bootstrap';
import sinon from 'sinon';
import { expect } from 'chai';
import { describe, it, beforeEach } from 'mocha';
import { mount } from 'enzyme';
import CheckboxControl from '../../../../javascripts/explore/components/controls/CheckboxControl';
const defaultProps = {
name: 'show_legend',
onChange: sinon.spy(),
value: false,
};
describe('CheckboxControl', () => {
let wrapper;
beforeEach(() => {
wrapper = mount(<CheckboxControl {...defaultProps} />);
});
it('renders a Checkbox', () => {
expect(wrapper.find(Checkbox)).to.have.lengthOf(1);
});
});

View File

@@ -0,0 +1,31 @@
import React from 'react';
import { expect } from 'chai';
import { describe, it, beforeEach } from 'mocha';
import { shallow } from 'enzyme';
import { getFormDataFromControls, defaultControls }
from '../../../../javascripts/explore/stores/store';
import {
ControlPanelsContainer,
} from '../../../../javascripts/explore/components/ControlPanelsContainer';
import ControlPanelSection from '../../../../javascripts/explore/components/ControlPanelSection';
const defaultProps = {
datasource_type: 'table',
actions: {},
controls: defaultControls,
form_data: getFormDataFromControls(defaultControls),
isDatasourceMetaLoading: false,
exploreState: {},
};
describe('ControlPanelsContainer', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<ControlPanelsContainer {...defaultProps} />);
});
it('renders ControlPanelSections', () => {
expect(wrapper.find(ControlPanelSection)).to.have.lengthOf(7);
});
});

View File

@@ -0,0 +1,18 @@
import React from 'react';
import { expect } from 'chai';
import { describe, it } from 'mocha';
import { shallow } from 'enzyme';
import ControlSetRow from '../../../../javascripts/explore/components/ControlRow';
describe('ControlSetRow', () => {
it('renders a single row with one element', () => {
const wrapper = shallow(<ControlSetRow controls={[<a />]} />);
expect(wrapper.find('.row')).to.have.lengthOf(1);
expect(wrapper.find('.row').find('a')).to.have.lengthOf(1);
});
it('renders a single row with two elements', () => {
const wrapper = shallow(<ControlSetRow controls={[<a />, <a />]} />);
expect(wrapper.find('.row')).to.have.lengthOf(1);
expect(wrapper.find('.row').find('a')).to.have.lengthOf(2);
});
});

View File

@@ -0,0 +1,30 @@
import React from 'react';
import { expect } from 'chai';
import { describe, it } from 'mocha';
import { mount } from 'enzyme';
import { Modal } from 'react-bootstrap';
import ModalTrigger from './../../../../javascripts/components/ModalTrigger';
import DisplayQueryButton from '../../../../javascripts/explore/components/DisplayQueryButton';
describe('DisplayQueryButton', () => {
const defaultProps = {
animation: false,
queryResponse: {
query: 'SELECT * FROM foo',
language: 'sql',
},
chartStatus: 'success',
queryEndpoint: 'localhost',
};
it('is valid', () => {
expect(React.isValidElement(<DisplayQueryButton {...defaultProps} />)).to.equal(true);
});
it('renders a button and a modal', () => {
const wrapper = mount(<DisplayQueryButton {...defaultProps} />);
expect(wrapper.find(ModalTrigger)).to.have.lengthOf(1);
wrapper.find('.modal-trigger').simulate('click');
expect(wrapper.find(Modal)).to.have.lengthOf(1);
});
});

View File

@@ -0,0 +1,47 @@
import React from 'react';
import { expect } from 'chai';
import { describe, it } from 'mocha';
import { shallow, mount } from 'enzyme';
import { OverlayTrigger } from 'react-bootstrap';
import EmbedCodeButton from '../../../../javascripts/explore/components/EmbedCodeButton';
describe('EmbedCodeButton', () => {
const defaultProps = {
slice: {
data: {
standalone_endpoint: 'endpoint_url',
},
},
};
it('renders', () => {
expect(React.isValidElement(<EmbedCodeButton {...defaultProps} />)).to.equal(true);
});
it('renders overlay trigger', () => {
const wrapper = shallow(<EmbedCodeButton {...defaultProps} />);
expect(wrapper.find(OverlayTrigger)).to.have.length(1);
});
it('returns correct embed code', () => {
const wrapper = mount(<EmbedCodeButton {...defaultProps} />);
wrapper.setState({
height: '1000',
width: '2000',
srcLink: 'http://localhost/endpoint_url',
});
const embedHTML = (
'<iframe\n' +
' width="2000"\n' +
' height="1000"\n' +
' seamless\n' +
' frameBorder="0"\n' +
' scrolling="no"\n' +
' src="nullendpoint_url&height=1000"\n' +
'>\n' +
'</iframe>'
);
expect(wrapper.instance().generateEmbedHTML()).to.equal(embedHTML);
});
});

View File

@@ -0,0 +1,30 @@
import React from 'react';
import { expect } from 'chai';
import { describe, it } from 'mocha';
import { shallow } from 'enzyme';
import ExploreActionButtons from
'../../../../javascripts/explore/components/ExploreActionButtons';
describe('ExploreActionButtons', () => {
const defaultProps = {
canDownload: 'True',
slice: {
data: {
csv_endpoint: '',
json_endpoint: '',
},
},
queryEndpoint: 'localhost',
};
it('renders', () => {
expect(
React.isValidElement(<ExploreActionButtons {...defaultProps} />),
).to.equal(true);
});
it('should render 5 children/buttons', () => {
const wrapper = shallow(<ExploreActionButtons {...defaultProps} />);
expect(wrapper.children()).to.have.length(5);
});
});

View File

@@ -0,0 +1,39 @@
// this test must be commented out because ChartContainer is now importing files
// from visualizations/*.js which are also importing css files which breaks in the testing env.
// import React from 'react';
// import { expect } from 'chai';
// import { describe, it } from 'mocha';
// import { shallow } from 'enzyme';
// import ExploreViewContainer
// from '../../../../javascripts/explore/components/ExploreViewContainer';
// import QueryAndSaveBtns
// from '../../../../javascripts/explore/components/QueryAndSaveBtns';
// import ControlPanelsContainer
// from '../../../../javascripts/explore/components/ControlPanelsContainer';
// import ChartContainer
// from '../../../../javascripts/explore/components/ChartContainer';
// describe('ExploreViewContainer', () => {
// it('renders', () => {
// expect(
// React.isValidElement(<ExploreViewContainer />)
// ).to.equal(true);
// });
// it('renders QueryAndSaveButtons', () => {
// const wrapper = shallow(<ExploreViewContainer />);
// expect(wrapper.find(QueryAndSaveBtns)).to.have.length(1);
// });
// it('renders ControlPanelsContainer', () => {
// const wrapper = shallow(<ExploreViewContainer />);
// expect(wrapper.find(ControlPanelsContainer)).to.have.length(1);
// });
// it('renders ChartContainer', () => {
// const wrapper = shallow(<ExploreViewContainer />);
// expect(wrapper.find(ChartContainer)).to.have.length(1);
// });
// });

View File

@@ -0,0 +1,48 @@
/* eslint-disable no-unused-expressions */
import React from 'react';
import { Button } from 'react-bootstrap';
import sinon from 'sinon';
import { expect } from 'chai';
import { describe, it, beforeEach } from 'mocha';
import { shallow } from 'enzyme';
import FilterControl from '../../../../javascripts/explore/components/controls/FilterControl';
import Filter from '../../../../javascripts/explore/components/controls/Filter';
const defaultProps = {
choices: ['country_name'],
prefix: 'flt',
onChange: sinon.spy(),
value: [],
datasource: {
id: 1,
type: 'table',
filter_select: false,
filterable_cols: ['country_name'],
},
};
describe('FilterControl', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<FilterControl {...defaultProps} />);
});
it('renders Filters', () => {
expect(
React.isValidElement(<FilterControl {...defaultProps} />),
).to.equal(true);
});
it('renders one button', () => {
expect(wrapper.find(Filter)).to.have.lengthOf(0);
expect(wrapper.find(Button)).to.have.lengthOf(1);
});
it('add a filter when Add Filter button is clicked', () => {
const addButton = wrapper.find('#add-button');
expect(addButton).to.have.lengthOf(1);
addButton.simulate('click');
expect(defaultProps.onChange).to.have.property('callCount', 1);
});
});

View File

@@ -0,0 +1,91 @@
/* eslint-disable no-unused-expressions */
import React from 'react';
import Select from 'react-select';
import { Button } from 'react-bootstrap';
import sinon from 'sinon';
import { expect } from 'chai';
import { describe, it, beforeEach } from 'mocha';
import { shallow } from 'enzyme';
import Filter from '../../../../javascripts/explore/components/controls/Filter';
import SelectControl from '../../../../javascripts/explore/components/controls/SelectControl';
const defaultProps = {
changeFilter: sinon.spy(),
removeFilter: () => {},
filter: {
col: null,
op: 'in',
value: ['val'],
},
datasource: {
id: 1,
type: 'qtable',
filter_select: false,
filterable_cols: ['col1', 'col2'],
metrics_combo: [
['m1', 'v1'],
['m2', 'v2'],
],
},
};
describe('Filter', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<Filter {...defaultProps} />);
});
it('renders Filters', () => {
expect(
React.isValidElement(<Filter {...defaultProps} />),
).to.equal(true);
});
it('renders two selects, one button and one input', () => {
expect(wrapper.find(Select)).to.have.lengthOf(2);
expect(wrapper.find(Button)).to.have.lengthOf(1);
expect(wrapper.find(SelectControl)).to.have.lengthOf(1);
expect(wrapper.find('#select-op').prop('options')).to.have.lengthOf(8);
});
it('renders five op choices for table datasource', () => {
const props = Object.assign({}, defaultProps);
props.datasource = {
id: 1,
type: 'druid',
filter_select: false,
filterable_cols: ['country_name'],
};
const druidWrapper = shallow(<Filter {...props} />);
expect(druidWrapper.find('#select-op').prop('options')).to.have.lengthOf(9);
});
it('renders six op choices for having filter', () => {
const props = Object.assign({}, defaultProps);
props.having = true;
const havingWrapper = shallow(<Filter {...props} />);
expect(havingWrapper.find('#select-op').prop('options')).to.have.lengthOf(6);
});
it('calls changeFilter when select is changed', () => {
const selectCol = wrapper.find('#select-col');
selectCol.simulate('change', { value: 'col' });
const selectOp = wrapper.find('#select-op');
selectOp.simulate('change', { value: 'in' });
const selectVal = wrapper.find(SelectControl);
selectVal.simulate('change', { value: 'x' });
expect(defaultProps.changeFilter).to.have.property('callCount', 3);
});
it('renders input for regex filters', () => {
const props = Object.assign({}, defaultProps);
props.filter = {
col: null,
op: 'regex',
value: 'val',
};
const regexWrapper = shallow(<Filter {...props} />);
expect(regexWrapper.find('input')).to.have.lengthOf(1);
});
});

View File

@@ -0,0 +1,44 @@
import React from 'react';
import { beforeEach, describe, it } from 'mocha';
import { expect } from 'chai';
import { shallow } from 'enzyme';
import sinon from 'sinon';
import QueryAndSaveButtons from '../../../../javascripts/explore/components/QueryAndSaveBtns';
import Button from '../../../../javascripts/components/Button';
describe('QueryAndSaveButtons', () => {
const defaultProps = {
canAdd: 'True',
onQuery: sinon.spy(),
};
// It must render
it('renders', () => {
expect(React.isValidElement(<QueryAndSaveButtons {...defaultProps} />)).to.equal(true);
});
// Test the output
describe('output', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<QueryAndSaveButtons {...defaultProps} />);
});
it('renders 2 buttons', () => {
expect(wrapper.find(Button)).to.have.lengthOf(2);
});
it('renders buttons with correct text', () => {
expect(wrapper.find(Button).contains(' Query')).to.eql(true);
expect(wrapper.find(Button).contains(' Save as')).to.eql(true);
});
it('calls onQuery when query button is clicked', () => {
const queryButton = wrapper.find('.query');
queryButton.simulate('click');
expect(defaultProps.onQuery.called).to.eql(true);
});
});
});

View File

@@ -0,0 +1,34 @@
import React from 'react';
import { expect } from 'chai';
import { describe, it, beforeEach } from 'mocha';
import { shallow } from 'enzyme';
import RunQueryActionButton
from '../../../../javascripts/SqlLab/components/RunQueryActionButton';
import Button from '../../../../javascripts/components/Button';
describe('RunQueryActionButton', () => {
let wrapper;
const defaultProps = {
allowAsync: false,
dbId: 1,
queryState: 'pending',
runQuery: () => {}, // eslint-disable-line
selectedText: null,
stopQuery: () => {}, // eslint-disable-line
};
beforeEach(() => {
wrapper = shallow(<RunQueryActionButton {...defaultProps} />);
});
it('is a valid react element', () => {
expect(
React.isValidElement(<RunQueryActionButton {...defaultProps} />),
).to.equal(true);
});
it('renders a single Button', () => {
expect(wrapper.find(Button)).to.have.lengthOf(1);
});
});

View File

@@ -0,0 +1,76 @@
import React from 'react';
import { expect } from 'chai';
import { describe, it, beforeEach } from 'mocha';
import { shallow } from 'enzyme';
import { Modal, Button, Radio } from 'react-bootstrap';
import sinon from 'sinon';
import { defaultFormData } from '../../../../javascripts/explore/stores/store';
import { SaveModal } from '../../../../javascripts/explore/components/SaveModal';
const defaultProps = {
can_edit: true,
onHide: () => ({}),
actions: {
saveSlice: sinon.spy(),
},
form_data: defaultFormData,
user_id: '1',
dashboards: [],
slice: {},
};
describe('SaveModal', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<SaveModal {...defaultProps} />);
});
it('renders a Modal with 7 inputs and 2 buttons', () => {
expect(wrapper.find(Modal)).to.have.lengthOf(1);
expect(wrapper.find('input')).to.have.lengthOf(2);
expect(wrapper.find(Button)).to.have.lengthOf(2);
expect(wrapper.find(Radio)).to.have.lengthOf(5);
});
it('does not show overwrite option for new slice', () => {
defaultProps.slice = null;
const wrapperNewSlice = shallow(<SaveModal {...defaultProps} />);
expect(wrapperNewSlice.find('#overwrite-radio')).to.have.lengthOf(0);
expect(wrapperNewSlice.find('#saveas-radio')).to.have.lengthOf(1);
});
it('disable overwrite option for non-owner', () => {
defaultProps.slice = {};
defaultProps.can_overwrite = false;
const wrapperForNonOwner = shallow(<SaveModal {...defaultProps} />);
const overwriteRadio = wrapperForNonOwner.find('#overwrite-radio');
expect(overwriteRadio).to.have.lengthOf(1);
expect(overwriteRadio.prop('disabled')).to.equal(true);
});
it('saves a new slice', () => {
defaultProps.slice = {
slice_id: 1,
slice_name: 'title',
};
defaultProps.can_overwrite = false;
const wrapperForNewSlice = shallow(<SaveModal {...defaultProps} />);
const saveasRadio = wrapperForNewSlice.find('#saveas-radio');
saveasRadio.simulate('click');
expect(wrapperForNewSlice.state().action).to.equal('saveas');
});
it('overwrite a slice', () => {
defaultProps.slice = {
slice_id: 1,
slice_name: 'title',
};
defaultProps.can_overwrite = true;
const wrapperForOverwrite = shallow(<SaveModal {...defaultProps} />);
const overwriteRadio = wrapperForOverwrite.find('#overwrite-radio');
overwriteRadio.simulate('click');
expect(wrapperForOverwrite.state().action).to.equal('overwrite');
});
});

View File

@@ -0,0 +1,79 @@
/* eslint-disable no-unused-expressions */
import React from 'react';
import Select, { Creatable } from 'react-select';
import sinon from 'sinon';
import { expect } from 'chai';
import { describe, it, beforeEach } from 'mocha';
import { shallow } from 'enzyme';
import SelectControl from '../../../../javascripts/explore/components/controls/SelectControl';
const defaultProps = {
choices: [['1 year ago', '1 year ago'], ['today', 'today']],
name: 'row_limit',
label: 'Row Limit',
onChange: sinon.spy(),
};
const options = [
{ value: '1 year ago', label: '1 year ago' },
{ value: 'today', label: 'today' },
];
describe('SelectControl', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<SelectControl {...defaultProps} />);
});
it('renders a Select', () => {
expect(wrapper.find(Select)).to.have.lengthOf(1);
});
it('calls onChange when toggled', () => {
const select = wrapper.find(Select);
select.simulate('change', { value: 50 });
expect(defaultProps.onChange.calledWith(50)).to.be.true;
});
it('renders a Creatable for freeform', () => {
wrapper = shallow(<SelectControl {...defaultProps} freeForm />);
expect(wrapper.find(Creatable)).to.have.lengthOf(1);
});
describe('getOptions', () => {
it('returns the correct options', () => {
expect(wrapper.instance().getOptions(defaultProps)).to.deep.equal(options);
});
it('returns the correct options when freeform is set to true', () => {
const freeFormProps = Object.assign(defaultProps, {
choices: [],
freeForm: true,
value: ['one', 'two'],
});
const newOptions = [
{ value: 'one', label: 'one' },
{ value: 'two', label: 'two' },
];
expect(wrapper.instance().getOptions(freeFormProps)).to.deep.equal(newOptions);
});
});
describe('componentWillReceiveProps', () => {
it('sets state.options if props.choices has changed', () => {
const updatedOptions = [
{ value: 'three', label: 'three' },
{ value: 'four', label: 'four' },
];
const newProps = {
choices: [['three', 'three'], ['four', 'four']],
name: 'name',
freeForm: false,
value: null,
};
wrapper.setProps(newProps);
expect(wrapper.state().options).to.deep.equal(updatedOptions);
});
});
});

View File

@@ -0,0 +1,32 @@
/* eslint-disable no-unused-expressions */
import React from 'react';
import { FormControl } from 'react-bootstrap';
import sinon from 'sinon';
import { expect } from 'chai';
import { describe, it, beforeEach } from 'mocha';
import { shallow } from 'enzyme';
import TextAreaControl from '../../../../javascripts/explore/components/controls/TextAreaControl';
const defaultProps = {
name: 'x_axis_label',
label: 'X Axis Label',
onChange: sinon.spy(),
};
describe('SelectControl', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<TextAreaControl {...defaultProps} />);
});
it('renders a FormControl', () => {
expect(wrapper.find(FormControl)).to.have.lengthOf(1);
});
it('calls onChange when toggled', () => {
const select = wrapper.find(FormControl);
select.simulate('change', { target: { value: 'x' } });
expect(defaultProps.onChange.calledWith('x')).to.be.true;
});
});

View File

@@ -0,0 +1,17 @@
import React from 'react';
import { expect } from 'chai';
import { describe, it } from 'mocha';
import URLShortLinkButton from '../../../../javascripts/explore/components/URLShortLinkButton';
describe('URLShortLinkButton', () => {
const defaultProps = {
slice: {
querystring: () => 'query string',
},
};
it('renders', () => {
expect(React.isValidElement(<URLShortLinkButton {...defaultProps} />)).to.equal(true);
});
});

View File

@@ -0,0 +1,37 @@
import React from 'react';
import sinon from 'sinon';
import { expect } from 'chai';
import { describe, it, beforeEach } from 'mocha';
import { shallow } from 'enzyme';
import { Modal } from 'react-bootstrap';
import VizTypeControl from '../../../../javascripts/explore/components/controls/VizTypeControl';
const defaultProps = {
name: 'viz_type',
label: 'Visualization Type',
value: 'table',
onChange: sinon.spy(),
};
describe('VizTypeControl', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<VizTypeControl {...defaultProps} />);
});
it('renders a Modal', () => {
expect(wrapper.find(Modal)).to.have.lengthOf(1);
});
it('calls onChange when toggled', () => {
const select = wrapper.find('.viztype-selector-container').first();
select.simulate('click');
expect(defaultProps.onChange.called).to.equal(true);
});
it('filters images based on text input', () => {
expect(wrapper.find('img').length).to.be.above(20);
wrapper.setState({ filter: 'time' });
expect(wrapper.find('img').length).to.be.below(10);
});
});