smartSearchBar.spec.jsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. import React from 'react';
  2. import {shallow, mount} from 'enzyme';
  3. import {SmartSearchBar, addSpace, removeSpace} from 'app/components/smartSearchBar';
  4. import TagStore from 'app/stores/tagStore';
  5. describe('addSpace()', function() {
  6. it('should add a space when there is no trailing space', function() {
  7. expect(addSpace('one')).toEqual('one ');
  8. });
  9. it('should not add another space when there is already one', function() {
  10. expect(addSpace('one ')).toEqual('one ');
  11. });
  12. it('should leave the empty string alone', function() {
  13. expect(addSpace('')).toEqual('');
  14. });
  15. });
  16. describe('removeSpace()', function() {
  17. it('should remove a trailing space', function() {
  18. expect(removeSpace('one ')).toEqual('one');
  19. });
  20. it('should not remove the last character if it is not a space', function() {
  21. expect(removeSpace('one')).toEqual('one');
  22. });
  23. it('should leave the empty string alone', function() {
  24. expect(removeSpace('')).toEqual('');
  25. });
  26. });
  27. describe('SmartSearchBar', function() {
  28. let sandbox;
  29. let options;
  30. let environmentTagValuesMock;
  31. let supportedTags;
  32. let tagValuesMock = jest.fn(() => Promise.resolve([]));
  33. beforeEach(function() {
  34. TagStore.reset();
  35. TagStore.onLoadTagsSuccess(TestStubs.Tags());
  36. tagValuesMock.mockClear();
  37. supportedTags = {};
  38. sandbox = sinon.sandbox.create();
  39. options = {
  40. context: {organization: {id: '123'}},
  41. };
  42. environmentTagValuesMock = MockApiClient.addMockResponse({
  43. url: '/projects/123/456/tags/environment/values/',
  44. body: [],
  45. });
  46. });
  47. afterEach(function() {
  48. MockApiClient.clearMockResponses();
  49. sandbox.restore();
  50. });
  51. describe('componentWillReceiveProps()', function() {
  52. it('should add a space when setting state.query', function() {
  53. let searchBar = shallow(
  54. <SmartSearchBar supportedTags={supportedTags} query="one" />,
  55. options
  56. );
  57. expect(searchBar.state().query).toEqual('one ');
  58. });
  59. it('should update state.query if props.query is updated from outside', function() {
  60. let searchBar = shallow(
  61. <SmartSearchBar supportedTags={supportedTags} query="one" />,
  62. options
  63. );
  64. searchBar.setProps({query: 'two'});
  65. expect(searchBar.state().query).toEqual('two ');
  66. });
  67. it('should not reset user input if a noop props change happens', function() {
  68. let searchBar = shallow(
  69. <SmartSearchBar supportedTags={supportedTags} query="one" />,
  70. options
  71. );
  72. searchBar.setState({query: 'two'});
  73. searchBar.setProps({query: 'one'});
  74. expect(searchBar.state().query).toEqual('two');
  75. });
  76. it('should reset user input if a meaningful props change happens', function() {
  77. let searchBar = shallow(
  78. <SmartSearchBar supportedTags={supportedTags} query="one" />,
  79. options
  80. );
  81. searchBar.setState({query: 'two'});
  82. searchBar.setProps({query: 'three'});
  83. expect(searchBar.state().query).toEqual('three ');
  84. });
  85. });
  86. describe('getQueryTerms()', function() {
  87. it('should extract query terms from a query string', function() {
  88. let query = 'tagname: ';
  89. expect(SmartSearchBar.getQueryTerms(query, query.length)).toEqual(['tagname:']);
  90. query = 'tagname:derp browser:';
  91. expect(SmartSearchBar.getQueryTerms(query, query.length)).toEqual([
  92. 'tagname:derp',
  93. 'browser:',
  94. ]);
  95. query = ' browser:"Chrome 33.0" ';
  96. expect(SmartSearchBar.getQueryTerms(query, query.length)).toEqual([
  97. 'browser:"Chrome 33.0"',
  98. ]);
  99. });
  100. });
  101. describe('getLastTermIndex()', function() {
  102. it('should provide the index of the last query term, given cursor index', function() {
  103. let query = 'tagname:';
  104. expect(SmartSearchBar.getLastTermIndex(query, 0)).toEqual(8);
  105. query = 'tagname:foo'; // 'f' (index 9)
  106. expect(SmartSearchBar.getLastTermIndex(query, 9)).toEqual(11);
  107. query = 'tagname:foo anothertag:bar'; // 'f' (index 9)
  108. expect(SmartSearchBar.getLastTermIndex(query, 9)).toEqual(11);
  109. });
  110. });
  111. describe('clearSearch()', function() {
  112. it('clears the query', function() {
  113. let props = {
  114. orgId: '123',
  115. projectId: '456',
  116. query: 'is:unresolved ruby',
  117. defaultQuery: 'is:unresolved',
  118. supportedTags,
  119. };
  120. let searchBar = shallow(<SmartSearchBar {...props} />, options).instance();
  121. searchBar.clearSearch();
  122. expect(searchBar.state.query).toEqual('');
  123. });
  124. it('calls onSearch()', async function() {
  125. let props = {
  126. orgId: '123',
  127. projectId: '456',
  128. query: 'is:unresolved ruby',
  129. defaultQuery: 'is:unresolved',
  130. supportedTags,
  131. onSearch: sandbox.spy(),
  132. };
  133. let searchBar = shallow(<SmartSearchBar {...props} />, options).instance();
  134. await searchBar.clearSearch();
  135. expect(props.onSearch.calledWith('')).toBe(true);
  136. });
  137. });
  138. describe('onQueryFocus()', function() {
  139. it('displays the drop down', function() {
  140. let searchBar = shallow(
  141. <SmartSearchBar
  142. orgId="123"
  143. projectId="456"
  144. supportedTags={supportedTags}
  145. onGetTagValues={tagValuesMock}
  146. />,
  147. options
  148. ).instance();
  149. expect(searchBar.state.dropdownVisible).toBe(false);
  150. searchBar.onQueryFocus();
  151. expect(searchBar.state.dropdownVisible).toBe(true);
  152. });
  153. });
  154. describe('onQueryBlur()', function() {
  155. it('hides the drop down', function() {
  156. let searchBar = shallow(
  157. <SmartSearchBar orgId="123" projectId="456" supportedTags={supportedTags} />,
  158. options
  159. ).instance();
  160. searchBar.state.dropdownVisible = true;
  161. let clock = sandbox.useFakeTimers();
  162. searchBar.onQueryBlur();
  163. clock.tick(201); // doesn't close until 200ms
  164. expect(searchBar.state.dropdownVisible).toBe(false);
  165. });
  166. });
  167. describe('onKeyUp()', function() {
  168. describe('escape', function() {
  169. it('blurs the input', function() {
  170. let wrapper = shallow(
  171. <SmartSearchBar orgId="123" projectId="456" supportedTags={supportedTags} />,
  172. options
  173. );
  174. wrapper.setState({dropdownVisible: true});
  175. let instance = wrapper.instance();
  176. sandbox.stub(instance, 'blur');
  177. wrapper.find('input').simulate('keyup', {key: 'Escape', keyCode: '27'});
  178. expect(instance.blur.calledOnce).toBeTruthy();
  179. });
  180. });
  181. });
  182. describe('render()', function() {
  183. it('invokes onSearch() when submitting the form', function() {
  184. let stubbedOnSearch = sandbox.spy();
  185. let wrapper = mount(
  186. <SmartSearchBar
  187. onSearch={stubbedOnSearch}
  188. orgId="123"
  189. projectId="456"
  190. query="is:unresolved"
  191. supportedTags={supportedTags}
  192. />,
  193. options
  194. );
  195. wrapper.find('form').simulate('submit', {
  196. preventDefault() {},
  197. });
  198. expect(stubbedOnSearch.calledWith('is:unresolved')).toBe(true);
  199. });
  200. it('invokes onSearch() when search is cleared', function(done) {
  201. let props = {
  202. orgId: '123',
  203. projectId: '456',
  204. query: 'is:unresolved',
  205. supportedTags,
  206. onSearch: sandbox.spy(),
  207. };
  208. let wrapper = mount(<SmartSearchBar {...props} />, options);
  209. wrapper.find('.search-clear-form').simulate('click');
  210. setTimeout(function() {
  211. expect(props.onSearch.calledWith('')).toBe(true);
  212. done();
  213. });
  214. });
  215. });
  216. it('handles an empty query', function() {
  217. let props = {
  218. orgId: '123',
  219. projectId: '456',
  220. query: '',
  221. defaultQuery: 'is:unresolved',
  222. supportedTags,
  223. };
  224. let wrapper = mount(<SmartSearchBar {...props} />, options);
  225. expect(wrapper.state('query')).toEqual('');
  226. });
  227. describe('updateAutoCompleteItems()', function() {
  228. let clock;
  229. beforeEach(function() {
  230. clock = sandbox.useFakeTimers();
  231. });
  232. afterEach(function() {
  233. clock.restore();
  234. });
  235. it('sets state when empty', function() {
  236. let props = {
  237. orgId: '123',
  238. projectId: '456',
  239. query: '',
  240. supportedTags,
  241. };
  242. let searchBar = mount(<SmartSearchBar {...props} />, options).instance();
  243. searchBar.updateAutoCompleteItems();
  244. expect(searchBar.state.searchTerm).toEqual('');
  245. expect(searchBar.state.searchItems).toEqual(searchBar.props.defaultSearchItems);
  246. expect(searchBar.state.activeSearchItem).toEqual(0);
  247. });
  248. it('sets state when incomplete tag', function() {
  249. let props = {
  250. orgId: '123',
  251. projectId: '456',
  252. query: 'fu',
  253. supportedTags,
  254. };
  255. let searchBar = mount(<SmartSearchBar {...props} />, options).instance();
  256. searchBar.updateAutoCompleteItems();
  257. expect(searchBar.state.searchTerm).toEqual('fu');
  258. expect(searchBar.state.searchItems).toEqual([]);
  259. expect(searchBar.state.activeSearchItem).toEqual(0);
  260. });
  261. it('sets state when incomplete tag as second input', function() {
  262. let props = {
  263. orgId: '123',
  264. projectId: '456',
  265. query: 'is:unresolved fu',
  266. supportedTags,
  267. };
  268. let searchBar = mount(<SmartSearchBar {...props} />, options).instance();
  269. searchBar.getCursorPosition = jest.fn();
  270. searchBar.getCursorPosition.mockReturnValue(15); // end of line
  271. searchBar.updateAutoCompleteItems();
  272. expect(searchBar.state.searchTerm).toEqual('fu');
  273. expect(searchBar.state.searchItems).toHaveLength(0);
  274. expect(searchBar.state.activeSearchItem).toEqual(0);
  275. });
  276. it('does not request values when tag is environments', function() {
  277. let props = {
  278. orgId: '123',
  279. projectId: '456',
  280. query: 'environment:production',
  281. excludeEnvironment: true,
  282. supportedTags,
  283. };
  284. let searchBar = mount(<SmartSearchBar {...props} />, options).instance();
  285. searchBar.updateAutoCompleteItems();
  286. clock.tick(301);
  287. expect(environmentTagValuesMock).not.toHaveBeenCalled();
  288. });
  289. it('does not request values when tag is `timesSeen`', function() {
  290. // This should never get called
  291. let mock = MockApiClient.addMockResponse({
  292. url: '/projects/123/456/tags/timesSeen/values/',
  293. body: [],
  294. });
  295. let props = {
  296. orgId: '123',
  297. projectId: '456',
  298. query: 'timesSeen:',
  299. supportedTags,
  300. };
  301. let searchBar = mount(<SmartSearchBar {...props} />, options).instance();
  302. searchBar.updateAutoCompleteItems();
  303. clock.tick(301);
  304. expect(mock).not.toHaveBeenCalled();
  305. });
  306. });
  307. });