teamKeyTransactionField.spec.jsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. import {mountWithTheme} from 'sentry-test/enzyme';
  2. import {act} from 'sentry-test/reactTestingLibrary';
  3. import * as TeamKeyTransactionManager from 'sentry/components/performance/teamKeyTransactionsManager';
  4. import ProjectsStore from 'sentry/stores/projectsStore';
  5. import TeamStore from 'sentry/stores/teamStore';
  6. import TeamKeyTransactionField from 'sentry/utils/discover/teamKeyTransactionField';
  7. async function clickTeamKeyTransactionDropdown(wrapper) {
  8. wrapper.find('IconStar').simulate('click');
  9. await tick();
  10. wrapper.update();
  11. }
  12. describe('TeamKeyTransactionField', function () {
  13. const organization = TestStubs.Organization();
  14. const teams = [
  15. TestStubs.Team({id: '1', slug: 'team1', name: 'Team 1'}),
  16. TestStubs.Team({id: '2', slug: 'team2', name: 'Team 2'}),
  17. ];
  18. const project = TestStubs.Project({teams});
  19. beforeEach(function () {
  20. MockApiClient.clearMockResponses();
  21. act(() => ProjectsStore.loadInitialData([project]));
  22. act(() => TeamStore.loadInitialData(teams));
  23. });
  24. it('renders with all teams checked', async function () {
  25. const getTeamKeyTransactionsMock = MockApiClient.addMockResponse({
  26. method: 'GET',
  27. url: `/organizations/${organization.slug}/key-transactions-list/`,
  28. body: teams.map(({id}) => ({
  29. team: id,
  30. count: 1,
  31. keyed: [{project_id: String(project.id), transaction: 'transaction'}],
  32. })),
  33. });
  34. const wrapper = mountWithTheme(
  35. <TeamKeyTransactionManager.Provider
  36. organization={organization}
  37. teams={teams}
  38. selectedTeams={['myteams']}
  39. >
  40. <TeamKeyTransactionField
  41. isKeyTransaction
  42. organization={organization}
  43. projectSlug={project.slug}
  44. transactionName="transaction"
  45. />
  46. </TeamKeyTransactionManager.Provider>
  47. );
  48. await tick();
  49. wrapper.update();
  50. expect(getTeamKeyTransactionsMock).toHaveBeenCalledTimes(1);
  51. expect(wrapper.find('IconStar').exists()).toBeTruthy();
  52. expect(wrapper.find('IconStar').props().isSolid).toBeTruthy();
  53. clickTeamKeyTransactionDropdown(wrapper);
  54. // header should show the checked state
  55. const header = wrapper.find('DropdownMenuHeader');
  56. expect(header.exists()).toBeTruthy();
  57. expect(header.find('CheckboxFancy').props().isChecked).toBeTruthy();
  58. expect(header.find('CheckboxFancy').props().isIndeterminate).toBeFalsy();
  59. // all teams should be checked
  60. const entries = wrapper.find('DropdownMenuItem');
  61. expect(entries.length).toBe(2);
  62. entries.forEach((entry, i) => {
  63. expect(entry.text()).toEqual(teams[i].slug);
  64. expect(entry.find('CheckboxFancy').props().isChecked).toBeTruthy();
  65. });
  66. });
  67. it('renders with some teams checked', async function () {
  68. const getTeamKeyTransactionsMock = MockApiClient.addMockResponse({
  69. method: 'GET',
  70. url: `/organizations/${organization.slug}/key-transactions-list/`,
  71. body: teams.map(({id}) => ({
  72. team: id,
  73. count: id === teams[0].id ? 1 : 0,
  74. keyed:
  75. id === teams[0].id
  76. ? [{project_id: String(project.id), transaction: 'transaction'}]
  77. : [],
  78. })),
  79. });
  80. const wrapper = mountWithTheme(
  81. <TeamKeyTransactionManager.Provider
  82. organization={organization}
  83. teams={teams}
  84. selectedTeams={['myteams']}
  85. >
  86. <TeamKeyTransactionField
  87. isKeyTransaction
  88. organization={organization}
  89. projectSlug={project.slug}
  90. transactionName="transaction"
  91. />
  92. </TeamKeyTransactionManager.Provider>
  93. );
  94. await tick();
  95. wrapper.update();
  96. expect(getTeamKeyTransactionsMock).toHaveBeenCalledTimes(1);
  97. expect(wrapper.find('IconStar').exists()).toBeTruthy();
  98. expect(wrapper.find('IconStar').props().isSolid).toBeTruthy();
  99. clickTeamKeyTransactionDropdown(wrapper);
  100. // header should show the indeterminate state
  101. const header = wrapper.find('DropdownMenuHeader');
  102. expect(header.exists()).toBeTruthy();
  103. expect(header.find('CheckboxFancy').props().isChecked).toBeFalsy();
  104. expect(header.find('CheckboxFancy').props().isIndeterminate).toBeTruthy();
  105. // all teams should be checked
  106. const entries = wrapper.find('DropdownMenuItem');
  107. expect(entries.length).toBe(2);
  108. entries.forEach((entry, i) => {
  109. expect(entry.text()).toEqual(teams[i].slug);
  110. });
  111. expect(entries.at(0).find('CheckboxFancy').props().isChecked).toBeTruthy();
  112. expect(entries.at(1).find('CheckboxFancy').props().isChecked).toBeFalsy();
  113. });
  114. it('renders with no teams checked', async function () {
  115. const getTeamKeyTransactionsMock = MockApiClient.addMockResponse({
  116. method: 'GET',
  117. url: `/organizations/${organization.slug}/key-transactions-list/`,
  118. body: teams.map(({id}) => ({
  119. team: id,
  120. count: 0,
  121. keyed: [],
  122. })),
  123. });
  124. const wrapper = mountWithTheme(
  125. <TeamKeyTransactionManager.Provider
  126. organization={organization}
  127. teams={teams}
  128. selectedTeams={['myteams']}
  129. >
  130. <TeamKeyTransactionField
  131. isKeyTransaction
  132. organization={organization}
  133. projectSlug={project.slug}
  134. transactionName="transaction"
  135. />
  136. </TeamKeyTransactionManager.Provider>
  137. );
  138. await tick();
  139. wrapper.update();
  140. expect(getTeamKeyTransactionsMock).toHaveBeenCalledTimes(1);
  141. expect(wrapper.find('IconStar').exists()).toBeTruthy();
  142. expect(wrapper.find('IconStar').props().isSolid).toBeFalsy();
  143. clickTeamKeyTransactionDropdown(wrapper);
  144. // header should show the unchecked state
  145. const header = wrapper.find('DropdownMenuHeader');
  146. expect(header.exists()).toBeTruthy();
  147. expect(header.find('CheckboxFancy').props().isChecked).toBeFalsy();
  148. expect(header.find('CheckboxFancy').props().isIndeterminate).toBeFalsy();
  149. // all teams should be unchecked
  150. const entries = wrapper.find('DropdownMenuItem');
  151. expect(entries.length).toBe(2);
  152. entries.forEach((entry, i) => {
  153. expect(entry.text()).toEqual(teams[i].slug);
  154. expect(entry.find('CheckboxFancy').props().isChecked).toBeFalsy();
  155. });
  156. });
  157. it('should be able to check one team', async function () {
  158. MockApiClient.addMockResponse({
  159. method: 'GET',
  160. url: `/organizations/${organization.slug}/key-transactions-list/`,
  161. body: teams.map(({id}) => ({
  162. team: id,
  163. count: 0,
  164. keyed: [],
  165. })),
  166. });
  167. const postTeamKeyTransactionsMock = MockApiClient.addMockResponse({
  168. method: 'POST',
  169. url: '/organizations/org-slug/key-transactions/',
  170. body: [],
  171. match: [
  172. MockApiClient.matchQuery({project: [project.id]}),
  173. MockApiClient.matchData({team: [teams[0].id], transaction: 'transaction'}),
  174. ],
  175. });
  176. const wrapper = mountWithTheme(
  177. <TeamKeyTransactionManager.Provider
  178. organization={organization}
  179. teams={teams}
  180. selectedTeams={['myteams']}
  181. >
  182. <TeamKeyTransactionField
  183. isKeyTransaction
  184. organization={organization}
  185. projectSlug={project.slug}
  186. transactionName="transaction"
  187. />
  188. </TeamKeyTransactionManager.Provider>
  189. );
  190. await tick();
  191. wrapper.update();
  192. clickTeamKeyTransactionDropdown(wrapper);
  193. expect(
  194. wrapper.find('DropdownMenuItem CheckboxFancy').first().props().isChecked
  195. ).toBeFalsy();
  196. wrapper.find('DropdownMenuItem CheckboxFancy').first().simulate('click');
  197. await tick();
  198. wrapper.update();
  199. expect(
  200. wrapper.find('DropdownMenuItem CheckboxFancy').first().props().isChecked
  201. ).toBeTruthy();
  202. expect(postTeamKeyTransactionsMock).toHaveBeenCalledTimes(1);
  203. });
  204. it('should be able to uncheck one team', async function () {
  205. MockApiClient.addMockResponse({
  206. method: 'GET',
  207. url: `/organizations/${organization.slug}/key-transactions-list/`,
  208. body: teams.map(({id}) => ({
  209. team: id,
  210. count: 1,
  211. keyed: [{project_id: String(project.id), transaction: 'transaction'}],
  212. })),
  213. });
  214. const deleteTeamKeyTransactionsMock = MockApiClient.addMockResponse({
  215. method: 'DELETE',
  216. url: '/organizations/org-slug/key-transactions/',
  217. body: [],
  218. match: [
  219. MockApiClient.matchQuery({project: [project.id]}),
  220. MockApiClient.matchData({team: [teams[0].id], transaction: 'transaction'}),
  221. ],
  222. });
  223. const wrapper = mountWithTheme(
  224. <TeamKeyTransactionManager.Provider
  225. organization={organization}
  226. teams={teams}
  227. selectedTeams={['myteams']}
  228. >
  229. <TeamKeyTransactionField
  230. isKeyTransaction
  231. organization={organization}
  232. projectSlug={project.slug}
  233. transactionName="transaction"
  234. />
  235. </TeamKeyTransactionManager.Provider>
  236. );
  237. await tick();
  238. wrapper.update();
  239. clickTeamKeyTransactionDropdown(wrapper);
  240. expect(
  241. wrapper.find('DropdownMenuItem CheckboxFancy').first().props().isChecked
  242. ).toBeTruthy();
  243. wrapper.find('DropdownMenuItem CheckboxFancy').first().simulate('click');
  244. await tick();
  245. wrapper.update();
  246. expect(
  247. wrapper.find('DropdownMenuItem CheckboxFancy').first().props().isChecked
  248. ).toBeFalsy();
  249. expect(deleteTeamKeyTransactionsMock).toHaveBeenCalledTimes(1);
  250. });
  251. it('should be able to check all with my teams', async function () {
  252. MockApiClient.addMockResponse({
  253. method: 'GET',
  254. url: `/organizations/${organization.slug}/key-transactions-list/`,
  255. body: teams.map(({id}) => ({
  256. team: id,
  257. count: 0,
  258. keyed: [],
  259. })),
  260. });
  261. const postTeamKeyTransactionsMock = MockApiClient.addMockResponse({
  262. method: 'POST',
  263. url: '/organizations/org-slug/key-transactions/',
  264. body: [],
  265. match: [
  266. MockApiClient.matchQuery({project: [project.id]}),
  267. MockApiClient.matchData({
  268. team: [teams[0].id, teams[1].id],
  269. transaction: 'transaction',
  270. }),
  271. ],
  272. });
  273. const wrapper = mountWithTheme(
  274. <TeamKeyTransactionManager.Provider
  275. organization={organization}
  276. teams={teams}
  277. selectedTeams={['myteams']}
  278. >
  279. <TeamKeyTransactionField
  280. isKeyTransaction
  281. organization={organization}
  282. projectSlug={project.slug}
  283. transactionName="transaction"
  284. />
  285. </TeamKeyTransactionManager.Provider>
  286. );
  287. await tick();
  288. wrapper.update();
  289. clickTeamKeyTransactionDropdown(wrapper);
  290. wrapper.find('DropdownMenuHeader CheckboxFancy').simulate('click');
  291. await tick();
  292. wrapper.update();
  293. const headerCheckbox = wrapper.find('DropdownMenuHeader CheckboxFancy');
  294. expect(headerCheckbox.props().isChecked).toBeTruthy();
  295. expect(headerCheckbox.props().isIndeterminate).toBeFalsy();
  296. expect(postTeamKeyTransactionsMock).toHaveBeenCalledTimes(1);
  297. });
  298. it('should be able to uncheck all with my teams', async function () {
  299. MockApiClient.addMockResponse({
  300. method: 'GET',
  301. url: `/organizations/${organization.slug}/key-transactions-list/`,
  302. body: teams.map(({id}) => ({
  303. team: id,
  304. count: 1,
  305. keyed: [{project_id: String(project.id), transaction: 'transaction'}],
  306. })),
  307. });
  308. const deleteTeamKeyTransactionsMock = MockApiClient.addMockResponse({
  309. method: 'DELETE',
  310. url: '/organizations/org-slug/key-transactions/',
  311. body: [],
  312. match: [
  313. MockApiClient.matchQuery({project: [project.id]}),
  314. MockApiClient.matchData({
  315. team: [teams[0].id, teams[1].id],
  316. transaction: 'transaction',
  317. }),
  318. ],
  319. });
  320. const wrapper = mountWithTheme(
  321. <TeamKeyTransactionManager.Provider
  322. organization={organization}
  323. teams={teams}
  324. selectedTeams={['myteams']}
  325. >
  326. <TeamKeyTransactionField
  327. isKeyTransaction
  328. organization={organization}
  329. projectSlug={project.slug}
  330. transactionName="transaction"
  331. />
  332. </TeamKeyTransactionManager.Provider>
  333. );
  334. await tick();
  335. wrapper.update();
  336. clickTeamKeyTransactionDropdown(wrapper);
  337. wrapper.find('DropdownMenuHeader CheckboxFancy').simulate('click');
  338. await tick();
  339. wrapper.update();
  340. const headerCheckbox = wrapper.find('DropdownMenuHeader CheckboxFancy');
  341. expect(headerCheckbox.props().isChecked).toBeFalsy();
  342. expect(headerCheckbox.props().isIndeterminate).toBeFalsy();
  343. expect(deleteTeamKeyTransactionsMock).toHaveBeenCalledTimes(1);
  344. });
  345. it('should render teams without access separately', async function () {
  346. const myTeams = [...teams, TestStubs.Team({id: '3', slug: 'team3', name: 'Team 3'})];
  347. act(() => {
  348. TeamStore.loadInitialData(myTeams);
  349. });
  350. MockApiClient.addMockResponse({
  351. method: 'GET',
  352. url: `/organizations/${organization.slug}/key-transactions-list/`,
  353. body: myTeams.map(({id}) => ({
  354. team: id,
  355. count: 0,
  356. keyed: [],
  357. })),
  358. });
  359. const wrapper = mountWithTheme(
  360. <TeamKeyTransactionManager.Provider
  361. organization={organization}
  362. teams={myTeams}
  363. selectedTeams={['myteams']}
  364. >
  365. <TeamKeyTransactionField
  366. isKeyTransaction
  367. organization={organization}
  368. projectSlug={project.slug}
  369. transactionName="transaction"
  370. />
  371. </TeamKeyTransactionManager.Provider>
  372. );
  373. await tick();
  374. wrapper.update();
  375. clickTeamKeyTransactionDropdown(wrapper);
  376. const headers = wrapper.find('DropdownMenuHeader');
  377. expect(headers.length).toEqual(2);
  378. expect(headers.at(0).text()).toEqual('My Teams with Access');
  379. expect(headers.at(1).text()).toEqual('My Teams without Access');
  380. const entries = wrapper.find('DropdownMenuItem');
  381. expect(entries.length).toEqual(3);
  382. entries.forEach((entry, i) => {
  383. expect(entry.text()).toEqual(myTeams[i].slug);
  384. });
  385. expect(entries.at(0).find('CheckboxFancy').exists()).toBeTruthy();
  386. expect(entries.at(1).find('CheckboxFancy').exists()).toBeTruthy();
  387. // this team does not have access so there is no checkbox
  388. expect(entries.at(2).find('CheckboxFancy').exists()).toBeFalsy();
  389. });
  390. });