diff --git a/web/ui/react-app/src/ExpressionInput.test.tsx b/web/ui/react-app/src/ExpressionInput.test.tsx index 12b142289..66b16f24d 100644 --- a/web/ui/react-app/src/ExpressionInput.test.tsx +++ b/web/ui/react-app/src/ExpressionInput.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { mount } from 'enzyme'; +import { mount, ReactWrapper } from 'enzyme'; import ExpressionInput from './ExpressionInput'; import Downshift from 'downshift'; import { Button, InputGroup, InputGroupAddon, Input } from 'reactstrap'; @@ -25,11 +25,15 @@ describe('ExpressionInput', () => { executeQuery: (): void => {}, loading: false, }; - const expressionInput = mount(); + + let expressionInput: ReactWrapper; + beforeEach(() => { + expressionInput = mount(); + }); it('renders a downshift component', () => { const downshift = expressionInput.find(Downshift); - expect(downshift.prop('inputValue')).toEqual('node_cpu'); + expect(downshift).toHaveLength(1); }); it('renders an InputGroup', () => { @@ -52,7 +56,6 @@ describe('ExpressionInput', () => { }); it('renders an Input', () => { - const expressionInput = mount(); const input = expressionInput.find(Input); expect(input.prop('style')).toEqual({ height: 0 }); expect(input.prop('autoFocus')).toEqual(true); @@ -84,6 +87,83 @@ describe('ExpressionInput', () => { }); }); + describe('handleInput', () => { + it('should call setState', () => { + const instance: any = expressionInput.instance(); + const stateSpy = jest.spyOn(instance, 'setState'); + instance.handleInput(); + expect(stateSpy).toHaveBeenCalled(); + }); + }); + + describe('handleDropdownSelection', () => { + it('should call setState with selected value', () => { + const instance: any = expressionInput.instance(); + const stateSpy = jest.spyOn(instance, 'setState'); + instance.handleDropdownSelection('foo'); + expect(stateSpy).toHaveBeenCalledWith({ value: 'foo', height: 'auto' }, expect.anything()); + }); + }); + + describe('handleKeyPress', () => { + it('should call executeQuery on Enter key pressed', () => { + const spyExecuteQuery = jest.fn(); + const input = mount(); + const instance: any = input.instance(); + instance.handleKeyPress({ preventDefault: jest.fn, key: 'Enter' }); + expect(spyExecuteQuery).toHaveBeenCalled(); + }); + it('should NOT call executeQuery on Enter + Shift', () => { + const spyExecuteQuery = jest.fn(); + const input = mount(); + const instance: any = input.instance(); + instance.handleKeyPress({ preventDefault: jest.fn, key: 'Enter', shiftKey: true }); + expect(spyExecuteQuery).not.toHaveBeenCalled(); + }); + }); + + describe('getSearchMatches', () => { + it('should return matched value', () => { + const instance: any = expressionInput.instance(); + expect(instance.getSearchMatches('foo', ['barfoobaz', 'bazasdbaz'])).toHaveLength(1); + }); + it('should return empty array if no match found', () => { + const instance: any = expressionInput.instance(); + expect(instance.getSearchMatches('foo', ['barbaz', 'bazasdbaz'])).toHaveLength(0); + }); + }); + + describe('createAutocompleteSection', () => { + it('should close menu if no matches found', () => { + const input = mount(); + const instance: any = input.instance(); + const spyCloseMenu = jest.fn(); + instance.createAutocompleteSection({ inputValue: 'qqqqqq', closeMenu: spyCloseMenu }); + setTimeout(() => { + expect(spyCloseMenu).toHaveBeenCalled(); + }); + }); + it('should not render lsit if inputValue not exist', () => { + const input = mount(); + const instance: any = input.instance(); + const spyCloseMenu = jest.fn(); + instance.createAutocompleteSection({ closeMenu: spyCloseMenu }); + setTimeout(() => expect(spyCloseMenu).toHaveBeenCalled()); + }); + it('should render autosuggest-dropdown', () => { + const input = mount(); + const instance: any = input.instance(); + const spyGetMenuProps = jest.fn(); + const sections = instance.createAutocompleteSection({ + inputValue: 'foo', + highlightedIndex: 0, + getMenuProps: spyGetMenuProps, + getItemProps: jest.fn, + }); + expect(sections.props.className).toEqual('autosuggest-dropdown'); + }); + }); + describe('when downshift is open', () => { it('closes the menu on "Enter"', () => { const downshift = expressionInput.find(Downshift); @@ -94,6 +174,18 @@ describe('ExpressionInput', () => { expect(downshift.state('isOpen')).toBe(false); }); + it('should blur input on escape', () => { + const downshift = expressionInput.find(Downshift); + const instance: any = expressionInput.instance(); + const spyBlur = jest.fn(); + instance.exprInputRef.current = { blur: spyBlur }; + const input = downshift.find(Input); + downshift.setState({ isOpen: false }); + const event = getKeyEvent('Escape'); + input.simulate('keydown', event); + expect(spyBlur).toHaveBeenCalled(); + }); + it('noops on ArrowUp or ArrowDown', () => { const downshift = expressionInput.find(Downshift); const input = downshift.find(Input); @@ -107,16 +199,6 @@ describe('ExpressionInput', () => { }); it('does not render an autosuggest if there are no matches', () => { - const expressionInputProps = { - value: 'foo', - autocompleteSections: { - 'Query History': [], - 'Metric Names': [], - }, - executeQuery: (): void => {}, - loading: false, - }; - const expressionInput = mount(); const downshift = expressionInput.find(Downshift); downshift.setState({ isOpen: true }); const ul = downshift.find('ul'); @@ -126,12 +208,14 @@ describe('ExpressionInput', () => { it('renders an autosuggest if there are matches', () => { const downshift = expressionInput.find(Downshift); downshift.setState({ isOpen: true }); - const ul = downshift.find('ul'); - expect(ul.prop('className')).toEqual('autosuggest-dropdown-list'); - const items = ul.find(SanitizeHTML); - expect(items.map(item => item.text()).join(', ')).toEqual( - 'node_cpu_guest_seconds_total, node_cpu_seconds_total, instance:node_cpu_utilisation:rate1m' - ); + setTimeout(() => { + const ul = downshift.find('ul'); + expect(ul.prop('className')).toEqual('card list-group'); + const items = ul.find(SanitizeHTML); + expect(items.map(item => item.text()).join(', ')).toEqual( + 'node_cpu_guest_seconds_total, node_cpu_seconds_total, instance:node_cpu_utilisation:rate1m' + ); + }); }); }); diff --git a/web/ui/react-app/src/ExpressionInput.tsx b/web/ui/react-app/src/ExpressionInput.tsx index e3b0cf5a9..d36020b63 100644 --- a/web/ui/react-app/src/ExpressionInput.tsx +++ b/web/ui/react-app/src/ExpressionInput.tsx @@ -57,7 +57,7 @@ class ExpressionInput extends Component) => { if (event.key === 'Enter' && !event.shiftKey) { - this.props.executeQuery(this.exprInputRef.current!.value); + this.executeQuery(); event.preventDefault(); } }; @@ -126,7 +126,7 @@ class ExpressionInput extends Component + {downshift => ( @@ -171,6 +171,7 @@ class ExpressionInput extends Component