draggableTabBar.spec.tsx 14 KB

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