import {mountWithTheme} from 'sentry-test/enzyme'; import {Column, generateFieldAsString} from 'sentry/utils/discover/fields'; import ArithmeticInput from 'sentry/views/eventsV2/table/arithmeticInput'; describe('ArithmeticInput', function () { let wrapper; let query: string; let handleQueryChange: (value: string) => void; let numericColumns: Column[]; let columns: Column[]; const operators = ['+', '-', '*', '/', '(', ')']; beforeEach(function () { query = ''; handleQueryChange = q => { query = q; }; numericColumns = [ {kind: 'field', field: 'transaction.duration'}, {kind: 'field', field: 'measurements.lcp'}, {kind: 'field', field: 'spans.http'}, {kind: 'function', function: ['p50', '', undefined, undefined]}, { kind: 'function', function: ['percentile', 'transaction.duration', '0.25', undefined], }, {kind: 'function', function: ['count', '', undefined, undefined]}, ]; columns = [ ...numericColumns, // these columns will not be rendered in the dropdown {kind: 'function', function: ['any', 'transaction.duration', undefined, undefined]}, {kind: 'field', field: 'transaction'}, {kind: 'function', function: ['failure_rate', '', undefined, undefined]}, {kind: 'equation', field: 'transaction.duration+measurements.lcp'}, ]; wrapper = mountWithTheme( ); }); afterEach(function () { wrapper?.unmount(); }); it('can toggle autocomplete dropdown on focus and blur', function () { expect(wrapper.find('TermDropdown').props().isOpen).toBeFalsy(); // focus the input wrapper.find('input').simulate('focus'); expect(wrapper.find('TermDropdown').props().isOpen).toBeTruthy(); // blur the input wrapper.find('input').simulate('blur'); expect(wrapper.find('TermDropdown').props().isOpen).toBeFalsy(); }); it('renders only numeric options in autocomplete', function () { wrapper.find('input').simulate('focus'); const options = wrapper.find('DropdownListItem'); expect(options).toHaveLength(numericColumns.length + operators.length); options.forEach((option, i) => { if (i < numericColumns.length) { expect(option.text()).toEqual(generateFieldAsString(numericColumns[i])); } else { expect(option.text()).toEqual(operators[i - numericColumns.length]); } }); }); it('can use keyboard to select an option', function () { const input = wrapper.find('input'); input.simulate('focus'); expect(wrapper.find('DropdownListItem .active').exists()).toBeFalsy(); for (const column of numericColumns) { input.simulate('keydown', {key: 'ArrowDown'}); expect(wrapper.find('DropdownListItem .active').text()).toEqual( generateFieldAsString(column) ); } for (const operator of operators) { input.simulate('keydown', {key: 'ArrowDown'}); expect(wrapper.find('DropdownListItem .active').text()).toEqual(operator); } // wrap around to the first option again input.simulate('keydown', {key: 'ArrowDown'}); for (const operator of [...operators].reverse()) { input.simulate('keydown', {key: 'ArrowUp'}); expect(wrapper.find('DropdownListItem .active').text()).toEqual(operator); } for (const column of [...numericColumns].reverse()) { input.simulate('keydown', {key: 'ArrowUp'}); expect(wrapper.find('DropdownListItem .active').text()).toEqual( generateFieldAsString(column) ); } // the update is buffered until blur happens input.simulate('keydown', {key: 'Enter'}); expect(query).toEqual(''); input.simulate('blur'); expect(query).toEqual(`${generateFieldAsString(numericColumns[0])} `); }); it('can use mouse to select an option', function () { const input = wrapper.find('input'); input.simulate('focus'); // the update is buffered until blur happens wrapper.find('DropdownListItem').first().simulate('click'); input.simulate('blur'); expect(query).toEqual(`${generateFieldAsString(numericColumns[0])} `); }); it('autocompletes the current term when it is in the front', function () { const input = wrapper.find('input'); input.simulate('focus'); const value = 'lcp + transaction.duration'; input.simulate('change', {target: {value}}); const inputElem = input.getDOMNode(); inputElem.selectionStart = 2; inputElem.selectionEnd = 2; input.simulate('change'); const option = wrapper.find('DropdownListItem'); expect(option).toHaveLength(1); expect(option.text()).toEqual( generateFieldAsString({ kind: 'field', field: 'measurements.lcp', }) ); option.simulate('click'); input.simulate('blur'); expect(query).toEqual(`measurements.lcp + transaction.duration`); }); it('autocompletes the current term when it is in the end', function () { const input = wrapper.find('input'); input.simulate('focus'); const value = 'transaction.duration + lcp'; input.simulate('change', {target: {value}}); const inputElem = input.getDOMNode(); inputElem.selectionStart = value.length - 1; inputElem.selectionEnd = value.length - 1; input.simulate('change'); const option = wrapper.find('DropdownListItem'); expect(option).toHaveLength(1); const column = numericColumns.find( c => c.kind === 'field' && c.field.includes('lcp') ); expect(option.text()).toEqual(generateFieldAsString(column!)); option.simulate('click'); input.simulate('blur'); expect(query).toEqual(`transaction.duration + measurements.lcp `); }); it('handles autocomplete on invalid term', function () { const input = wrapper.find('input'); input.simulate('focus'); const value = 'foo + bar'; input.simulate('change', {target: {value}}); input.simulate('keydown', {key: 'ArrowDown'}); const option = wrapper.find('DropdownListItem'); expect(option).toHaveLength(0); }); it('can hide Fields options', function () { wrapper = mountWithTheme( {}} options={[]} hideFieldOptions /> ); const optionGroupHeaders = wrapper.find('header'); expect(optionGroupHeaders).toHaveLength(1); expect(optionGroupHeaders.text()).toBe('Operators'); }); });