draggableTabBar.spec.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. import {RouterFixture} from 'sentry-fixture/routerFixture';
  2. import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
  3. import {
  4. DraggableTabBar,
  5. type Tab,
  6. } from 'sentry/views/issueList/groupSearchViewTabs/draggableTabBar';
  7. import {IssueSortOptions} from 'sentry/views/issueList/utils';
  8. // biome-ignore lint/suspicious/noSkippedTests: <explanation>
  9. describe.skip('DraggableTabBar', () => {
  10. const mockOnTabRenamed = jest.fn();
  11. const mockOnAddView = jest.fn();
  12. const mockOnDelete = jest.fn();
  13. const mockOnDiscard = jest.fn();
  14. const mockOnDuplicate = jest.fn();
  15. const mockOnSave = jest.fn();
  16. const mockOnDiscardTempView = jest.fn();
  17. const mockOnSaveTempView = jest.fn();
  18. const router = RouterFixture({
  19. location: {
  20. pathname: 'test',
  21. },
  22. });
  23. const tabs: Tab[] = [
  24. {
  25. id: '1',
  26. key: '1',
  27. label: 'Prioritized',
  28. queryCount: 20,
  29. query: 'priority:high',
  30. querySort: IssueSortOptions.DATE,
  31. unsavedChanges: ['priority:low', IssueSortOptions.DATE],
  32. },
  33. {
  34. id: '2',
  35. key: '2',
  36. label: 'For Review',
  37. queryCount: 1001,
  38. query: 'is:unassigned',
  39. querySort: IssueSortOptions.DATE,
  40. },
  41. {
  42. id: '3',
  43. key: '3',
  44. label: 'Regressed',
  45. query: 'is:regressed',
  46. querySort: IssueSortOptions.DATE,
  47. },
  48. ];
  49. describe('Tabs render as expected', () => {
  50. it('should render tabs from props', () => {
  51. render(
  52. <DraggableTabBar
  53. initialTabKey="1"
  54. setTempTab={jest.fn()}
  55. tabs={tabs}
  56. setTabs={jest.fn()}
  57. onTabRenamed={mockOnTabRenamed}
  58. orgSlug={'test-org'}
  59. router={router}
  60. />
  61. );
  62. expect(screen.getAllByRole('tab').length).toBe(tabs.length);
  63. // The query count is included in the tab name here
  64. expect(screen.getByRole('tab', {name: 'Prioritized 20'})).toBeInTheDocument();
  65. expect(screen.getByRole('tab', {name: 'For Review 1000+'})).toBeInTheDocument();
  66. expect(screen.getByRole('tab', {name: 'Regressed'})).toBeInTheDocument();
  67. });
  68. });
  69. // Skipping this and next tests due to excessive unexplainable flakiness
  70. // biome-ignore lint/suspicious/noSkippedTests: <explanation>
  71. describe.skip('Tab menu options', () => {
  72. it('should render the correct set of actions for changed tabs', async () => {
  73. render(
  74. <DraggableTabBar
  75. initialTabKey="1"
  76. setTempTab={jest.fn()}
  77. tabs={tabs}
  78. setTabs={jest.fn()}
  79. onTabRenamed={mockOnTabRenamed}
  80. orgSlug={'test-org'}
  81. router={router}
  82. />
  83. );
  84. await userEvent.click(
  85. screen.getByRole('button', {name: 'Prioritized Tab Options'})
  86. );
  87. expect(
  88. await screen.findByRole('menuitemradio', {name: 'Save Changes'})
  89. ).toBeInTheDocument();
  90. expect(
  91. await screen.findByRole('menuitemradio', {name: 'Discard Changes'})
  92. ).toBeInTheDocument();
  93. expect(
  94. await screen.findByRole('menuitemradio', {name: 'Rename'})
  95. ).toBeInTheDocument();
  96. expect(
  97. await screen.findByRole('menuitemradio', {name: 'Duplicate'})
  98. ).toBeInTheDocument();
  99. expect(
  100. await screen.findByRole('menuitemradio', {name: 'Delete'})
  101. ).toBeInTheDocument();
  102. });
  103. it('should render the correct set of actions for unchanged tabs', async () => {
  104. render(
  105. <DraggableTabBar
  106. initialTabKey="1"
  107. setTempTab={jest.fn()}
  108. tabs={tabs}
  109. setTabs={jest.fn()}
  110. onTabRenamed={mockOnTabRenamed}
  111. orgSlug={'test-org'}
  112. router={router}
  113. />
  114. );
  115. // We need to explicitly click on the For Review tab since it is not the default (first) tab in props
  116. await userEvent.click(screen.getByRole('tab', {name: 'For Review 1000+'}));
  117. await userEvent.click(
  118. await screen.findByRole('button', {name: 'For Review Tab Options'})
  119. );
  120. expect(
  121. await screen.findByRole('menuitemradio', {name: 'Rename'})
  122. ).toBeInTheDocument();
  123. expect(
  124. await screen.findByRole('menuitemradio', {name: 'Duplicate'})
  125. ).toBeInTheDocument();
  126. expect(
  127. await screen.findByRole('menuitemradio', {name: 'Delete'})
  128. ).toBeInTheDocument();
  129. expect(
  130. screen.queryByRole('menuitemradio', {name: 'Save Changes'})
  131. ).not.toBeInTheDocument();
  132. expect(
  133. screen.queryByRole('menuitemradio', {name: 'Discard Changes'})
  134. ).not.toBeInTheDocument();
  135. });
  136. it('should render the correct set of actions for a single tab', async () => {
  137. render(
  138. <DraggableTabBar
  139. initialTabKey="1"
  140. setTempTab={jest.fn()}
  141. tabs={[tabs[1]]}
  142. setTabs={jest.fn()}
  143. onTabRenamed={mockOnTabRenamed}
  144. orgSlug={'test-org'}
  145. router={router}
  146. />
  147. );
  148. await userEvent.click(screen.getByRole('button', {name: 'For Review Tab Options'}));
  149. expect(
  150. await screen.findByRole('menuitemradio', {name: 'Rename'})
  151. ).toBeInTheDocument();
  152. expect(
  153. await screen.findByRole('menuitemradio', {name: 'Duplicate'})
  154. ).toBeInTheDocument();
  155. // Delete should not be present since there is only one tab
  156. expect(
  157. screen.queryByRole('menuitemradio', {name: 'Delete'})
  158. ).not.toBeInTheDocument();
  159. });
  160. it('should render the correct set of actions for temporary tabs', async () => {
  161. render(
  162. <DraggableTabBar
  163. initialTabKey="1"
  164. setTempTab={jest.fn()}
  165. tabs={tabs}
  166. setTabs={jest.fn()}
  167. onTabRenamed={mockOnTabRenamed}
  168. orgSlug={'test-org'}
  169. router={router}
  170. />
  171. );
  172. // We need to explicitly click on the For Review tab since it is not the default (first) tab in props
  173. await userEvent.click(screen.getByRole('tab', {name: 'Unsaved'}));
  174. await userEvent.click(
  175. await screen.findByRole('button', {name: 'Unsaved Tab Options'})
  176. );
  177. expect(
  178. await screen.findByRole('menuitemradio', {name: 'Save View'})
  179. ).toBeInTheDocument();
  180. expect(
  181. await screen.findByRole('menuitemradio', {name: 'Discard'})
  182. ).toBeInTheDocument();
  183. });
  184. });
  185. // biome-ignore lint/suspicious/noSkippedTests: <explanation>
  186. describe.skip('Tab actions', () => {
  187. it('should allow renaming a tab', async () => {
  188. render(
  189. <DraggableTabBar
  190. initialTabKey="1"
  191. setTempTab={jest.fn()}
  192. tabs={tabs}
  193. setTabs={jest.fn()}
  194. onTabRenamed={mockOnTabRenamed}
  195. orgSlug={'test-org'}
  196. router={router}
  197. />
  198. );
  199. await userEvent.click(screen.getByRole('tab', {name: 'For Review 1000+'}));
  200. await userEvent.click(
  201. await screen.findByRole('button', {name: 'For Review Tab Options'})
  202. );
  203. await userEvent.click(await screen.findByRole('menuitemradio', {name: 'Rename'}));
  204. // Ctrl+A to select all text, then backspace to delete it
  205. // (We purposely do not highlight the text when hitting rename)
  206. await userEvent.keyboard('{Control>}A{/Control}{Backspace}');
  207. await userEvent.paste('New Name');
  208. await userEvent.keyboard('{enter}');
  209. expect(mockOnTabRenamed).toHaveBeenCalledWith('2', 'New Name');
  210. });
  211. it('should not allow renaming a tab to empty string', async () => {
  212. render(
  213. <DraggableTabBar
  214. initialTabKey="1"
  215. setTempTab={jest.fn()}
  216. tabs={tabs}
  217. setTabs={jest.fn()}
  218. onTabRenamed={mockOnTabRenamed}
  219. orgSlug={'test-org'}
  220. router={router}
  221. />
  222. );
  223. await userEvent.click(screen.getByRole('tab', {name: 'For Review 1000+'}));
  224. await userEvent.click(
  225. await screen.findByRole('button', {name: 'For Review Tab Options'})
  226. );
  227. await userEvent.click(await screen.findByRole('menuitemradio', {name: 'Rename'}));
  228. await userEvent.keyboard('{Control>}A{/Control}{Backspace}');
  229. await userEvent.keyboard('{enter}');
  230. // Tab name should not have changed
  231. expect(screen.getByRole('tab', {name: 'For Review 1000+'})).toBeInTheDocument();
  232. expect(mockOnTabRenamed).not.toHaveBeenCalled();
  233. });
  234. it('should discard changes if esc is pressed while renaming', async () => {
  235. render(
  236. <DraggableTabBar
  237. initialTabKey="1"
  238. setTempTab={jest.fn()}
  239. tabs={tabs}
  240. setTabs={jest.fn()}
  241. onTabRenamed={mockOnTabRenamed}
  242. orgSlug={'test-org'}
  243. router={router}
  244. />
  245. );
  246. await userEvent.click(screen.getByRole('tab', {name: 'For Review 1000+'}));
  247. await userEvent.click(
  248. await screen.findByRole('button', {name: 'For Review Tab Options'})
  249. );
  250. await userEvent.click(await screen.findByRole('menuitemradio', {name: 'Rename'}));
  251. await userEvent.keyboard('{Control>}A{/Control}{Backspace}');
  252. await userEvent.paste('New Name');
  253. await userEvent.keyboard('{esc}');
  254. expect(mockOnTabRenamed).not.toHaveBeenCalled();
  255. });
  256. it('should fire the onSave callback when save changes is pressed', async () => {
  257. render(
  258. <DraggableTabBar
  259. initialTabKey="1"
  260. setTempTab={jest.fn()}
  261. tabs={tabs}
  262. setTabs={jest.fn()}
  263. onSave={mockOnSave}
  264. orgSlug={'test-org'}
  265. router={router}
  266. />
  267. );
  268. await userEvent.click(screen.getByRole('tab', {name: 'Prioritized 20'}));
  269. await userEvent.click(
  270. await screen.findByRole('button', {name: 'Prioritized Tab Options'})
  271. );
  272. await userEvent.click(
  273. await screen.findByRole('menuitemradio', {name: 'Save Changes'})
  274. );
  275. expect(mockOnSave).toHaveBeenCalledWith('1');
  276. });
  277. it('should fire the onDiscard callback when discard is pressed', async () => {
  278. render(
  279. <DraggableTabBar
  280. initialTabKey="1"
  281. setTempTab={jest.fn()}
  282. tabs={tabs}
  283. setTabs={jest.fn()}
  284. onDiscard={mockOnDiscard}
  285. orgSlug={'test-org'}
  286. router={router}
  287. />
  288. );
  289. await userEvent.click(screen.getByRole('tab', {name: 'Prioritized 20'}));
  290. await userEvent.click(
  291. await screen.findByRole('button', {name: 'Prioritized Tab Options'})
  292. );
  293. await userEvent.click(
  294. await screen.findByRole('menuitemradio', {name: 'Discard Changes'})
  295. );
  296. expect(mockOnDiscard).toHaveBeenCalledWith('1');
  297. });
  298. it('should fire the onDelete callback when delete is pressed', async () => {
  299. render(
  300. <DraggableTabBar
  301. initialTabKey="1"
  302. setTempTab={jest.fn()}
  303. tabs={tabs}
  304. setTabs={jest.fn()}
  305. onDelete={mockOnDelete}
  306. orgSlug={'test-org'}
  307. router={router}
  308. />
  309. );
  310. await userEvent.click(screen.getByRole('tab', {name: 'For Review 1000+'}));
  311. await userEvent.click(
  312. await screen.findByRole('button', {name: 'For Review Tab Options'})
  313. );
  314. await userEvent.click(await screen.findByRole('menuitemradio', {name: 'Delete'}));
  315. expect(mockOnDelete).toHaveBeenCalledWith('2');
  316. });
  317. it('should fire the onDuplicate callback when duplicate is pressed', async () => {
  318. render(
  319. <DraggableTabBar
  320. initialTabKey="1"
  321. setTempTab={jest.fn()}
  322. tabs={tabs}
  323. setTabs={jest.fn()}
  324. onDuplicate={mockOnDuplicate}
  325. orgSlug={'test-org'}
  326. router={router}
  327. />
  328. );
  329. await userEvent.click(screen.getByRole('tab', {name: 'For Review 1000+'}));
  330. await userEvent.click(
  331. await screen.findByRole('button', {name: 'For Review Tab Options'})
  332. );
  333. await userEvent.click(
  334. await screen.findByRole('menuitemradio', {name: 'Duplicate'})
  335. );
  336. expect(mockOnDuplicate).toHaveBeenCalledWith('2');
  337. });
  338. it('should fire the onDiscardTempView callback when the discard button is pressed for a temp view', async () => {
  339. render(
  340. <DraggableTabBar
  341. initialTabKey="1"
  342. setTempTab={jest.fn()}
  343. tabs={tabs}
  344. setTabs={jest.fn()}
  345. onDiscardTempView={mockOnDiscardTempView}
  346. orgSlug={'test-org'}
  347. router={router}
  348. />
  349. );
  350. await userEvent.click(screen.getByRole('tab', {name: 'Unsaved'}));
  351. await userEvent.click(
  352. await screen.findByRole('button', {name: 'Unsaved Tab Options'})
  353. );
  354. await userEvent.click(await screen.findByRole('menuitemradio', {name: 'Discard'}));
  355. expect(mockOnDiscardTempView).toHaveBeenCalled();
  356. });
  357. it('should fire the onSaveTempView callback when the discard button is pressed for a temp view', async () => {
  358. render(
  359. <DraggableTabBar
  360. initialTabKey="1"
  361. setTempTab={jest.fn()}
  362. tabs={tabs}
  363. setTabs={jest.fn()}
  364. onSaveTempView={mockOnSaveTempView}
  365. orgSlug={'test-org'}
  366. router={router}
  367. />
  368. );
  369. await userEvent.click(screen.getByRole('tab', {name: 'Unsaved'}));
  370. await userEvent.click(
  371. await screen.findByRole('button', {name: 'Unsaved Tab Options'})
  372. );
  373. await userEvent.click(
  374. await screen.findByRole('menuitemradio', {name: 'Save View'})
  375. );
  376. expect(mockOnSaveTempView).toHaveBeenCalled();
  377. });
  378. it('should fire the onAddView callback when the add view button is pressed', async () => {
  379. render(
  380. <DraggableTabBar
  381. initialTabKey="1"
  382. setTempTab={jest.fn()}
  383. tabs={tabs}
  384. setTabs={jest.fn()}
  385. onAddView={mockOnAddView}
  386. orgSlug={'test-org'}
  387. router={router}
  388. />
  389. );
  390. await userEvent.click(screen.getByRole('button', {name: 'Add View'}));
  391. expect(mockOnAddView).toHaveBeenCalled();
  392. });
  393. });
  394. });