teamKeyTransactionButton.spec.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. import {act, render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
  2. import ProjectsStore from 'sentry/stores/projectsStore';
  3. import TeamStore from 'sentry/stores/teamStore';
  4. import EventView from 'sentry/utils/discover/eventView';
  5. import {MAX_TEAM_KEY_TRANSACTIONS} from 'sentry/utils/performance/constants';
  6. import TeamKeyTransactionButton from 'sentry/views/performance/transactionSummary/teamKeyTransactionButton';
  7. async function clickTeamKeyTransactionDropdown() {
  8. await waitFor(() =>
  9. expect(screen.getByRole('button', {expanded: false})).toBeEnabled()
  10. );
  11. await userEvent.click(screen.getByRole('button', {expanded: false}));
  12. }
  13. describe('TeamKeyTransactionButton', function () {
  14. const organization = TestStubs.Organization({features: ['performance-view']});
  15. const teams = [
  16. TestStubs.Team({id: '1', slug: 'team1', name: 'Team 1'}),
  17. TestStubs.Team({id: '2', slug: 'team2', name: 'Team 2'}),
  18. ];
  19. const project = TestStubs.Project({teams});
  20. const eventView = new EventView({
  21. id: '1',
  22. name: 'my query',
  23. fields: [{field: 'count()'}],
  24. sorts: [{field: 'count', kind: 'desc'}],
  25. query: '',
  26. project: [project.id],
  27. start: '2019-10-01T00:00:00',
  28. end: '2019-10-02T00:00:00',
  29. statsPeriod: '14d',
  30. environment: [],
  31. createdBy: TestStubs.User(),
  32. display: 'line',
  33. team: ['myteams'],
  34. topEvents: '5',
  35. });
  36. beforeEach(function () {
  37. MockApiClient.clearMockResponses();
  38. act(() => ProjectsStore.loadInitialData([project]));
  39. act(() => void TeamStore.loadInitialData(teams, false, null));
  40. });
  41. it('fetches key transactions with project param', function () {
  42. const getTeamKeyTransactionsMock = MockApiClient.addMockResponse({
  43. method: 'GET',
  44. url: '/organizations/org-slug/key-transactions-list/',
  45. body: teams.map(({id}) => ({
  46. team: id,
  47. count: 1,
  48. keyed: [{project_id: String(project.id), transaction: 'transaction'}],
  49. })),
  50. match: [MockApiClient.matchQuery({project: [project.id], team: ['myteams']})],
  51. });
  52. render(
  53. <TeamKeyTransactionButton
  54. eventView={eventView}
  55. organization={organization}
  56. transactionName="transaction"
  57. />
  58. );
  59. expect(getTeamKeyTransactionsMock).toHaveBeenCalledTimes(1);
  60. });
  61. it('renders with all teams checked', async function () {
  62. MockApiClient.addMockResponse({
  63. method: 'GET',
  64. url: '/organizations/org-slug/key-transactions-list/',
  65. body: teams.map(({id}) => ({
  66. team: id,
  67. count: 1,
  68. keyed: [{project_id: String(project.id), transaction: 'transaction'}],
  69. })),
  70. });
  71. render(
  72. <TeamKeyTransactionButton
  73. eventView={eventView}
  74. organization={organization}
  75. transactionName="transaction"
  76. />
  77. );
  78. await clickTeamKeyTransactionDropdown();
  79. // all teams should be checked
  80. expect(screen.getByRole('option', {name: `#${teams[0].slug}`})).toHaveAttribute(
  81. 'aria-selected',
  82. 'true'
  83. );
  84. expect(screen.getByRole('option', {name: `#${teams[1].slug}`})).toHaveAttribute(
  85. 'aria-selected',
  86. 'true'
  87. );
  88. });
  89. it('renders with some teams checked', async function () {
  90. MockApiClient.addMockResponse({
  91. method: 'GET',
  92. url: '/organizations/org-slug/key-transactions-list/',
  93. body: teams.map(({id}) => ({
  94. team: id,
  95. count: id === teams[0].id ? 1 : 0,
  96. keyed:
  97. id === teams[0].id
  98. ? [{project_id: String(project.id), transaction: 'transaction'}]
  99. : [],
  100. })),
  101. });
  102. render(
  103. <TeamKeyTransactionButton
  104. eventView={eventView}
  105. organization={organization}
  106. transactionName="transaction"
  107. />
  108. );
  109. await clickTeamKeyTransactionDropdown();
  110. // only team 1 should be checked
  111. expect(screen.getByRole('option', {name: `#${teams[0].slug}`})).toHaveAttribute(
  112. 'aria-selected',
  113. 'true'
  114. );
  115. expect(screen.getByRole('option', {name: `#${teams[1].slug}`})).toHaveAttribute(
  116. 'aria-selected',
  117. 'false'
  118. );
  119. });
  120. it('renders with no teams checked', async function () {
  121. MockApiClient.addMockResponse({
  122. method: 'GET',
  123. url: '/organizations/org-slug/key-transactions-list/',
  124. body: teams.map(({id}) => ({
  125. team: id,
  126. count: 0,
  127. keyed: [],
  128. })),
  129. });
  130. render(
  131. <TeamKeyTransactionButton
  132. eventView={eventView}
  133. organization={organization}
  134. transactionName="transaction"
  135. />
  136. );
  137. await clickTeamKeyTransactionDropdown();
  138. // all teams should be unchecked
  139. expect(screen.getByRole('option', {name: `#${teams[0].slug}`})).toHaveAttribute(
  140. 'aria-selected',
  141. 'false'
  142. );
  143. expect(screen.getByRole('option', {name: `#${teams[1].slug}`})).toHaveAttribute(
  144. 'aria-selected',
  145. 'false'
  146. );
  147. });
  148. it('should be able to check one team', async function () {
  149. MockApiClient.addMockResponse({
  150. method: 'GET',
  151. url: '/organizations/org-slug/key-transactions-list/',
  152. body: teams.map(({id}) => ({
  153. team: id,
  154. count: 0,
  155. keyed: [],
  156. })),
  157. });
  158. const postTeamKeyTransactionsMock = MockApiClient.addMockResponse({
  159. method: 'POST',
  160. url: '/organizations/org-slug/key-transactions/',
  161. body: [],
  162. match: [
  163. MockApiClient.matchQuery({project: [project.id]}),
  164. MockApiClient.matchData({team: [teams[0].id], transaction: 'transaction'}),
  165. ],
  166. });
  167. render(
  168. <TeamKeyTransactionButton
  169. eventView={eventView}
  170. organization={organization}
  171. transactionName="transaction"
  172. />
  173. );
  174. await clickTeamKeyTransactionDropdown();
  175. await userEvent.click(screen.getByRole('option', {name: `#${teams[0].slug}`}));
  176. expect(postTeamKeyTransactionsMock).toHaveBeenCalledTimes(1);
  177. });
  178. it('should be able to uncheck one team', async function () {
  179. MockApiClient.addMockResponse({
  180. method: 'GET',
  181. url: '/organizations/org-slug/key-transactions-list/',
  182. body: teams.map(({id}) => ({
  183. team: id,
  184. count: 1,
  185. keyed: [{project_id: String(project.id), transaction: 'transaction'}],
  186. })),
  187. });
  188. const deleteTeamKeyTransactionsMock = MockApiClient.addMockResponse({
  189. method: 'DELETE',
  190. url: '/organizations/org-slug/key-transactions/',
  191. body: [],
  192. match: [
  193. MockApiClient.matchQuery({project: [project.id]}),
  194. MockApiClient.matchData({team: [teams[0].id], transaction: 'transaction'}),
  195. ],
  196. });
  197. render(
  198. <TeamKeyTransactionButton
  199. eventView={eventView}
  200. organization={organization}
  201. transactionName="transaction"
  202. />
  203. );
  204. await clickTeamKeyTransactionDropdown();
  205. await userEvent.click(screen.getByRole('option', {name: `#${teams[0].slug}`}));
  206. expect(deleteTeamKeyTransactionsMock).toHaveBeenCalledTimes(1);
  207. });
  208. it('should be able to check all with my teams', async function () {
  209. MockApiClient.addMockResponse({
  210. method: 'GET',
  211. url: '/organizations/org-slug/key-transactions-list/',
  212. body: teams.map(({id}) => ({
  213. team: id,
  214. count: 0,
  215. keyed: [],
  216. })),
  217. });
  218. const postTeamKeyTransactionsMock = MockApiClient.addMockResponse({
  219. method: 'POST',
  220. url: '/organizations/org-slug/key-transactions/',
  221. body: [],
  222. match: [
  223. MockApiClient.matchQuery({project: [project.id]}),
  224. MockApiClient.matchData({
  225. team: [teams[0].id, teams[1].id],
  226. transaction: 'transaction',
  227. }),
  228. ],
  229. });
  230. render(
  231. <TeamKeyTransactionButton
  232. eventView={eventView}
  233. organization={organization}
  234. transactionName="transaction"
  235. />
  236. );
  237. await clickTeamKeyTransactionDropdown();
  238. await userEvent.click(screen.getByRole('button', {name: 'Select All in My Teams'}));
  239. // all teams should be checked now
  240. expect(screen.getByRole('option', {name: `#${teams[0].slug}`})).toHaveAttribute(
  241. 'aria-selected',
  242. 'true'
  243. );
  244. expect(screen.getByRole('option', {name: `#${teams[1].slug}`})).toHaveAttribute(
  245. 'aria-selected',
  246. 'true'
  247. );
  248. expect(postTeamKeyTransactionsMock).toHaveBeenCalledTimes(1);
  249. });
  250. it('should be able to uncheck all with my teams', async function () {
  251. MockApiClient.addMockResponse({
  252. method: 'GET',
  253. url: '/organizations/org-slug/key-transactions-list/',
  254. body: teams.map(({id}) => ({
  255. team: id,
  256. count: 1,
  257. keyed: [{project_id: String(project.id), transaction: 'transaction'}],
  258. })),
  259. });
  260. const deleteTeamKeyTransactionsMock = MockApiClient.addMockResponse({
  261. method: 'DELETE',
  262. url: '/organizations/org-slug/key-transactions/',
  263. body: [],
  264. match: [
  265. MockApiClient.matchQuery({project: [project.id]}),
  266. MockApiClient.matchData({
  267. team: [teams[0].id, teams[1].id],
  268. transaction: 'transaction',
  269. }),
  270. ],
  271. });
  272. render(
  273. <TeamKeyTransactionButton
  274. eventView={eventView}
  275. organization={organization}
  276. transactionName="transaction"
  277. />
  278. );
  279. await clickTeamKeyTransactionDropdown();
  280. await userEvent.click(screen.getByRole('button', {name: 'Unselect All in My Teams'}));
  281. // all teams should be checked now
  282. expect(screen.getByRole('option', {name: `#${teams[0].slug}`})).toHaveAttribute(
  283. 'aria-selected',
  284. 'false'
  285. );
  286. expect(screen.getByRole('option', {name: `#${teams[1].slug}`})).toHaveAttribute(
  287. 'aria-selected',
  288. 'false'
  289. );
  290. expect(deleteTeamKeyTransactionsMock).toHaveBeenCalledTimes(1);
  291. });
  292. it('renders unkeyed as disabled if count exceeds max', async function () {
  293. MockApiClient.addMockResponse({
  294. method: 'GET',
  295. url: '/organizations/org-slug/key-transactions-list/',
  296. body: teams.map(({id}) => ({
  297. team: id,
  298. count: MAX_TEAM_KEY_TRANSACTIONS,
  299. keyed: Array.from({length: MAX_TEAM_KEY_TRANSACTIONS}, (_, i) => ({
  300. project_id: String(project.id),
  301. transaction: `transaction-${i}`,
  302. })),
  303. })),
  304. });
  305. render(
  306. <TeamKeyTransactionButton
  307. eventView={eventView}
  308. organization={organization}
  309. transactionName="transaction"
  310. />
  311. );
  312. await clickTeamKeyTransactionDropdown();
  313. expect(screen.getByRole('option', {name: `#${teams[0].slug}`})).toHaveAttribute(
  314. 'aria-disabled',
  315. 'true'
  316. );
  317. expect(screen.getByRole('option', {name: `#${teams[1].slug}`})).toHaveAttribute(
  318. 'aria-disabled',
  319. 'true'
  320. );
  321. });
  322. it('renders keyed as checked even if count is maxed', async function () {
  323. MockApiClient.addMockResponse({
  324. method: 'GET',
  325. url: '/organizations/org-slug/key-transactions-list/',
  326. body: teams.map(({id}) => ({
  327. team: id,
  328. count: MAX_TEAM_KEY_TRANSACTIONS,
  329. keyed: [
  330. {project_id: String(project.id), transaction: 'transaction'},
  331. ...Array.from({length: MAX_TEAM_KEY_TRANSACTIONS - 1}, (_, i) => ({
  332. project_id: String(project.id),
  333. transaction: `transaction-${i}`,
  334. })),
  335. ],
  336. })),
  337. });
  338. render(
  339. <TeamKeyTransactionButton
  340. eventView={eventView}
  341. organization={organization}
  342. transactionName="transaction"
  343. />
  344. );
  345. await clickTeamKeyTransactionDropdown();
  346. expect(screen.getByRole('option', {name: `#${teams[0].slug}`})).toHaveAttribute(
  347. 'aria-selected',
  348. 'true'
  349. );
  350. expect(screen.getByRole('option', {name: `#${teams[1].slug}`})).toHaveAttribute(
  351. 'aria-selected',
  352. 'true'
  353. );
  354. });
  355. });