import React from 'react';
import {shallow, mount} from 'enzyme';
import {SmartSearchBar, addSpace, removeSpace} from 'app/components/smartSearchBar';
import TagStore from 'app/stores/tagStore';
describe('addSpace()', function() {
it('should add a space when there is no trailing space', function() {
expect(addSpace('one')).toEqual('one ');
});
it('should not add another space when there is already one', function() {
expect(addSpace('one ')).toEqual('one ');
});
it('should leave the empty string alone', function() {
expect(addSpace('')).toEqual('');
});
});
describe('removeSpace()', function() {
it('should remove a trailing space', function() {
expect(removeSpace('one ')).toEqual('one');
});
it('should not remove the last character if it is not a space', function() {
expect(removeSpace('one')).toEqual('one');
});
it('should leave the empty string alone', function() {
expect(removeSpace('')).toEqual('');
});
});
describe('SmartSearchBar', function() {
let sandbox;
let options;
let environmentTagValuesMock;
let supportedTags;
let tagValuesMock = jest.fn(() => Promise.resolve([]));
beforeEach(function() {
TagStore.reset();
TagStore.onLoadTagsSuccess(TestStubs.Tags());
tagValuesMock.mockClear();
supportedTags = {};
sandbox = sinon.sandbox.create();
options = {
context: {organization: {id: '123'}},
};
environmentTagValuesMock = MockApiClient.addMockResponse({
url: '/projects/123/456/tags/environment/values/',
body: [],
});
});
afterEach(function() {
MockApiClient.clearMockResponses();
sandbox.restore();
});
describe('componentWillReceiveProps()', function() {
it('should add a space when setting state.query', function() {
let searchBar = shallow(
,
options
);
expect(searchBar.state().query).toEqual('one ');
});
it('should update state.query if props.query is updated from outside', function() {
let searchBar = shallow(
,
options
);
searchBar.setProps({query: 'two'});
expect(searchBar.state().query).toEqual('two ');
});
it('should not reset user input if a noop props change happens', function() {
let searchBar = shallow(
,
options
);
searchBar.setState({query: 'two'});
searchBar.setProps({query: 'one'});
expect(searchBar.state().query).toEqual('two');
});
it('should reset user input if a meaningful props change happens', function() {
let searchBar = shallow(
,
options
);
searchBar.setState({query: 'two'});
searchBar.setProps({query: 'three'});
expect(searchBar.state().query).toEqual('three ');
});
});
describe('getQueryTerms()', function() {
it('should extract query terms from a query string', function() {
let query = 'tagname: ';
expect(SmartSearchBar.getQueryTerms(query, query.length)).toEqual(['tagname:']);
query = 'tagname:derp browser:';
expect(SmartSearchBar.getQueryTerms(query, query.length)).toEqual([
'tagname:derp',
'browser:',
]);
query = ' browser:"Chrome 33.0" ';
expect(SmartSearchBar.getQueryTerms(query, query.length)).toEqual([
'browser:"Chrome 33.0"',
]);
});
});
describe('getLastTermIndex()', function() {
it('should provide the index of the last query term, given cursor index', function() {
let query = 'tagname:';
expect(SmartSearchBar.getLastTermIndex(query, 0)).toEqual(8);
query = 'tagname:foo'; // 'f' (index 9)
expect(SmartSearchBar.getLastTermIndex(query, 9)).toEqual(11);
query = 'tagname:foo anothertag:bar'; // 'f' (index 9)
expect(SmartSearchBar.getLastTermIndex(query, 9)).toEqual(11);
});
});
describe('clearSearch()', function() {
it('clears the query', function() {
let props = {
orgId: '123',
projectId: '456',
query: 'is:unresolved ruby',
defaultQuery: 'is:unresolved',
supportedTags,
};
let searchBar = shallow(, options).instance();
searchBar.clearSearch();
expect(searchBar.state.query).toEqual('');
});
it('calls onSearch()', async function() {
let props = {
orgId: '123',
projectId: '456',
query: 'is:unresolved ruby',
defaultQuery: 'is:unresolved',
supportedTags,
onSearch: sandbox.spy(),
};
let searchBar = shallow(, options).instance();
await searchBar.clearSearch();
expect(props.onSearch.calledWith('')).toBe(true);
});
});
describe('onQueryFocus()', function() {
it('displays the drop down', function() {
let searchBar = shallow(
,
options
).instance();
expect(searchBar.state.dropdownVisible).toBe(false);
searchBar.onQueryFocus();
expect(searchBar.state.dropdownVisible).toBe(true);
});
});
describe('onQueryBlur()', function() {
it('hides the drop down', function() {
let searchBar = shallow(
,
options
).instance();
searchBar.state.dropdownVisible = true;
let clock = sandbox.useFakeTimers();
searchBar.onQueryBlur();
clock.tick(201); // doesn't close until 200ms
expect(searchBar.state.dropdownVisible).toBe(false);
});
});
describe('onKeyUp()', function() {
describe('escape', function() {
it('blurs the input', function() {
let wrapper = shallow(
,
options
);
wrapper.setState({dropdownVisible: true});
let instance = wrapper.instance();
sandbox.stub(instance, 'blur');
wrapper.find('input').simulate('keyup', {key: 'Escape', keyCode: '27'});
expect(instance.blur.calledOnce).toBeTruthy();
});
});
});
describe('render()', function() {
it('invokes onSearch() when submitting the form', function() {
let stubbedOnSearch = sandbox.spy();
let wrapper = mount(
,
options
);
wrapper.find('form').simulate('submit', {
preventDefault() {},
});
expect(stubbedOnSearch.calledWith('is:unresolved')).toBe(true);
});
it('invokes onSearch() when search is cleared', function(done) {
let props = {
orgId: '123',
projectId: '456',
query: 'is:unresolved',
supportedTags,
onSearch: sandbox.spy(),
};
let wrapper = mount(, options);
wrapper.find('.search-clear-form').simulate('click');
setTimeout(function() {
expect(props.onSearch.calledWith('')).toBe(true);
done();
});
});
});
it('handles an empty query', function() {
let props = {
orgId: '123',
projectId: '456',
query: '',
defaultQuery: 'is:unresolved',
supportedTags,
};
let wrapper = mount(, options);
expect(wrapper.state('query')).toEqual('');
});
describe('updateAutoCompleteItems()', function() {
let clock;
beforeEach(function() {
clock = sandbox.useFakeTimers();
});
afterEach(function() {
clock.restore();
});
it('sets state when empty', function() {
let props = {
orgId: '123',
projectId: '456',
query: '',
supportedTags,
};
let searchBar = mount(, options).instance();
searchBar.updateAutoCompleteItems();
expect(searchBar.state.searchTerm).toEqual('');
expect(searchBar.state.searchItems).toEqual(searchBar.props.defaultSearchItems);
expect(searchBar.state.activeSearchItem).toEqual(0);
});
it('sets state when incomplete tag', function() {
let props = {
orgId: '123',
projectId: '456',
query: 'fu',
supportedTags,
};
let searchBar = mount(, options).instance();
searchBar.updateAutoCompleteItems();
expect(searchBar.state.searchTerm).toEqual('fu');
expect(searchBar.state.searchItems).toEqual([]);
expect(searchBar.state.activeSearchItem).toEqual(0);
});
it('sets state when incomplete tag as second input', function() {
let props = {
orgId: '123',
projectId: '456',
query: 'is:unresolved fu',
supportedTags,
};
let searchBar = mount(, options).instance();
searchBar.getCursorPosition = jest.fn();
searchBar.getCursorPosition.mockReturnValue(15); // end of line
searchBar.updateAutoCompleteItems();
expect(searchBar.state.searchTerm).toEqual('fu');
expect(searchBar.state.searchItems).toHaveLength(0);
expect(searchBar.state.activeSearchItem).toEqual(0);
});
it('does not request values when tag is environments', function() {
let props = {
orgId: '123',
projectId: '456',
query: 'environment:production',
excludeEnvironment: true,
supportedTags,
};
let searchBar = mount(, options).instance();
searchBar.updateAutoCompleteItems();
clock.tick(301);
expect(environmentTagValuesMock).not.toHaveBeenCalled();
});
it('does not request values when tag is `timesSeen`', function() {
// This should never get called
let mock = MockApiClient.addMockResponse({
url: '/projects/123/456/tags/timesSeen/values/',
body: [],
});
let props = {
orgId: '123',
projectId: '456',
query: 'timesSeen:',
supportedTags,
};
let searchBar = mount(, options).instance();
searchBar.updateAutoCompleteItems();
clock.tick(301);
expect(mock).not.toHaveBeenCalled();
});
});
});