Convert MeasurementList to TypeScript and add tests

pull/10616/head
Andrew Watkins 2018-03-03 21:56:50 -08:00
parent e2ceba3560
commit f4eca0a228
3 changed files with 212 additions and 55 deletions

View File

@ -23,10 +23,6 @@ interface DatabaseListState {
namespaces: Namespace[]
}
export interface DatabaseListContext {
source: Source
}
const {shape, string} = PropTypes
class DatabaseList extends PureComponent<DatabaseListProps, DatabaseListState> {

View File

@ -1,52 +1,56 @@
import React, {PropTypes} from 'react'
import React, {PureComponent} from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import _ from 'lodash'
import {showMeasurements} from 'shared/apis/metaQuery'
import showMeasurementsParser from 'shared/parsing/showMeasurements'
import {showMeasurements} from 'src/shared/apis/metaQuery'
import showMeasurementsParser from 'src/shared/parsing/showMeasurements'
import {Query, Source} from 'src/types'
import TagList from 'src/data_explorer/components/TagList'
import FancyScrollbar from 'shared/components/FancyScrollbar'
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
const {func, shape, string} = PropTypes
interface Props {
query: Query
querySource: Source
onChooseTag: () => void
onGroupByTag: () => void
onToggleTagAcceptance: () => void
onChooseMeasurement: (measurement: string) => void
}
const MeasurementList = React.createClass({
propTypes: {
query: shape({
database: string,
measurement: string,
}).isRequired,
onChooseMeasurement: func.isRequired,
onChooseTag: func.isRequired,
onToggleTagAcceptance: func.isRequired,
onGroupByTag: func.isRequired,
querySource: shape({
links: shape({
proxy: string.isRequired,
}).isRequired,
}),
},
interface State {
measurements: string[]
filterText: string
}
contextTypes: {
const {shape, string} = PropTypes
class MeasurementList extends PureComponent<Props, State> {
constructor(props) {
super(props)
this.state = {
measurements: [],
filterText: '',
}
this.handleEscape = this.handleEscape.bind(this)
this.handleFilterText = this.handleFilterText.bind(this)
this.handleAcceptReject = this.handleAcceptReject.bind(this)
}
public static defaultProps: Partial<Props> = {
querySource: null,
}
public static contextTypes = {
source: shape({
links: shape({
proxy: string.isRequired,
}).isRequired,
}).isRequired,
},
getInitialState() {
return {
measurements: [],
filterText: '',
}
},
getDefaultProps() {
return {
querySource: null,
}
},
}
componentDidMount() {
if (!this.props.query.database) {
@ -54,7 +58,7 @@ const MeasurementList = React.createClass({
}
this._getMeasurements()
},
}
componentDidUpdate(prevProps) {
const {query, querySource} = this.props
@ -71,14 +75,14 @@ const MeasurementList = React.createClass({
}
this._getMeasurements()
},
}
handleFilterText(e) {
e.stopPropagation()
this.setState({
filterText: this.refs.filterText.value,
filterText: e.target.value,
})
},
}
handleEscape(e) {
if (e.key !== 'Escape') {
@ -89,12 +93,11 @@ const MeasurementList = React.createClass({
this.setState({
filterText: '',
})
},
}
handleAcceptReject(e) {
e.stopPropagation()
handleAcceptReject() {
this.props.onToggleTagAcceptance()
},
}
render() {
return (
@ -105,14 +108,13 @@ const MeasurementList = React.createClass({
? <div className="query-builder--filter">
<input
className="form-control input-sm"
ref="filterText"
placeholder="Filter"
type="text"
value={this.state.filterText}
onChange={this.handleFilterText}
onKeyUp={this.handleEscape}
spellCheck={false}
autoComplete={false}
autoComplete="false"
/>
<span className="icon search" />
</div>
@ -121,7 +123,7 @@ const MeasurementList = React.createClass({
{this.renderList()}
</div>
)
},
}
renderList() {
if (!this.props.query.database) {
@ -193,7 +195,7 @@ const MeasurementList = React.createClass({
</FancyScrollbar>
</div>
)
},
}
_getMeasurements() {
const {source} = this.context
@ -206,14 +208,14 @@ const MeasurementList = React.createClass({
const {errors, measurementSets} = showMeasurementsParser(resp.data)
if (errors.length) {
// TODO: display errors in the UI.
return console.error('InfluxDB returned error(s): ', errors) // eslint-disable-line no-console
return console.error('InfluxDB returned error(s): ', errors)
}
this.setState({
measurements: measurementSets[0].measurements,
})
})
},
})
}
}
export default MeasurementList

View File

@ -0,0 +1,159 @@
import React from 'react'
import MeasurementList from 'src/shared/components/MeasurementList'
import {shallow} from 'enzyme'
import {query, source} from 'test/resources'
const setup = (override = {}) => {
const props = {
query,
querySource: source,
onChooseTag: () => {},
onGroupByTag: () => {},
onToggleTagAcceptance: () => {},
onChooseMeasurement: () => {},
...override,
}
MeasurementList.prototype._getMeasurements = jest.fn(() => Promise.resolve())
const wrapper = shallow(<MeasurementList {...props} />, {
context: {source},
})
const instance = wrapper.instance() as MeasurementList
return {
props,
wrapper,
instance,
}
}
describe('Shared.Components.MeasurementList', () => {
describe('rendering', () => {
it('renders to the page', () => {
const {wrapper} = setup()
expect(wrapper.exists()).toBe(true)
})
})
describe('lifecycle methods', () => {
describe('componentDidMount', () => {
it('does not fire getMeasurements if there is no database', () => {
const _getMeasurements = jest.fn()
const {instance} = setup({query: {...query, database: ''}})
instance._getMeasurements = _getMeasurements
instance.componentDidMount()
expect(_getMeasurements).not.toHaveBeenCalled()
})
it('does fire _getMeasurements if there is a database', () => {
const _getMeasurements = jest.fn()
const {instance} = setup()
instance._getMeasurements = _getMeasurements
instance.componentDidMount()
expect(_getMeasurements).toHaveBeenCalled()
})
})
describe('componentDidUpdate', () => {
it('does not fire getMeasurements if there is no database', () => {
const _getMeasurements = jest.fn()
const {instance, props} = setup({query: {...query, database: ''}})
instance._getMeasurements = _getMeasurements
instance.componentDidUpdate(props)
expect(_getMeasurements).not.toHaveBeenCalled()
})
it('does not fire _getMeasurements if the database does not change and the sources are equal', () => {
const _getMeasurements = jest.fn()
const {instance, props} = setup()
instance._getMeasurements = _getMeasurements
instance.componentDidUpdate(props)
expect(_getMeasurements).not.toHaveBeenCalled()
})
it('it fires _getMeasurements if there is a change in database', () => {
const _getMeasurements = jest.fn()
const {instance, props} = setup()
instance._getMeasurements = _getMeasurements
instance.componentDidUpdate({
...props,
query: {...query, database: 'diffDb'},
})
expect(_getMeasurements).toHaveBeenCalled()
})
it('it calls _getMeasurements if there is a change in source', () => {
const _getMeasurements = jest.fn()
const {instance, props} = setup()
instance._getMeasurements = _getMeasurements
instance.componentDidUpdate({
...props,
querySource: {...source, id: 'newSource'},
})
expect(_getMeasurements).toHaveBeenCalled()
})
})
})
describe('instance methods', () => {
describe('handleFilterText', () => {
it('sets the filterText state to the event targets value', () => {
const {instance} = setup()
const value = 'spectacs'
const stopPropagation = () => {}
const event = {target: {value}, stopPropagation}
instance.handleFilterText(event)
expect(instance.state.filterText).toBe(value)
})
})
describe('handleEscape', () => {
it('resets fiterText when escape is pressed', () => {
const {instance} = setup()
const key = 'Escape'
const stopPropagation = () => {}
const event = {key, stopPropagation}
instance.setState({filterText: 'foo'})
expect(instance.state.filterText).toBe('foo')
instance.handleEscape(event)
expect(instance.state.filterText).toBe('')
})
it('does not reset fiterText when escape is pressed', () => {
const {instance} = setup()
const key = 'Enter'
const stopPropagation = () => {}
const event = {key, stopPropagation}
instance.setState({filterText: 'foo'})
instance.handleEscape(event)
expect(instance.state.filterText).toBe('foo')
})
})
describe('handleAcceptReject', () => {
it('it calls onToggleTagAcceptance', () => {
const onToggleTagAcceptance = jest.fn()
const {instance} = setup({onToggleTagAcceptance})
instance.handleAcceptReject()
expect(onToggleTagAcceptance).toHaveBeenCalledTimes(1)
})
})
})
})