You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

QualityGate-it.tsx 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2023 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. import { act, screen, waitFor, within } from '@testing-library/react';
  21. import userEvent from '@testing-library/user-event';
  22. import selectEvent from 'react-select-event';
  23. import { QualityGatesServiceMock } from '../../../../api/mocks/QualityGatesServiceMock';
  24. import { searchProjects, searchUsers } from '../../../../api/quality-gates';
  25. import { renderAppRoutes, RenderContext } from '../../../../helpers/testReactTestingUtils';
  26. import { Feature } from '../../../../types/features';
  27. import routes from '../../routes';
  28. jest.mock('../../../../api/quality-gates');
  29. let handler: QualityGatesServiceMock;
  30. beforeAll(() => {
  31. handler = new QualityGatesServiceMock();
  32. });
  33. afterEach(() => handler.reset());
  34. it('should open the default quality gates', async () => {
  35. renderQualityGateApp();
  36. const defaultQualityGate = handler.getDefaultQualityGate();
  37. expect(
  38. await screen.findByRole('link', { current: 'page', name: `${defaultQualityGate.name} default` })
  39. ).toBeInTheDocument();
  40. });
  41. it('should list all quality gates', async () => {
  42. renderQualityGateApp();
  43. expect(
  44. await screen.findByRole('link', {
  45. name: `${handler.getDefaultQualityGate().name} default`,
  46. })
  47. ).toBeInTheDocument();
  48. expect(
  49. screen.getByRole('link', {
  50. name: `${handler.getBuiltInQualityGate().name} quality_gates.built_in`,
  51. })
  52. ).toBeInTheDocument();
  53. });
  54. it('should be able to create a quality gate then delete it', async () => {
  55. const user = userEvent.setup();
  56. handler.setIsAdmin(true);
  57. renderQualityGateApp();
  58. let createButton = await screen.findByRole('button', { name: 'create' });
  59. // Using keyboard
  60. await user.click(createButton);
  61. await act(async () => {
  62. await user.click(screen.getByRole('textbox', { name: /name.*/ }));
  63. await user.keyboard('testone{Enter}');
  64. });
  65. expect(await screen.findByRole('link', { name: 'testone' })).toBeInTheDocument();
  66. // Using modal button
  67. createButton = await screen.findByRole('button', { name: 'create' });
  68. await user.click(createButton);
  69. const saveButton = screen.getByRole('button', { name: 'save' });
  70. expect(saveButton).toBeDisabled();
  71. const nameInput = screen.getByRole('textbox', { name: /name.*/ });
  72. await act(async () => {
  73. await user.click(nameInput);
  74. await user.keyboard('testtwo');
  75. await user.click(saveButton);
  76. });
  77. const newQG = await screen.findByRole('link', { name: 'testtwo' });
  78. expect(newQG).toBeInTheDocument();
  79. // Delete the quality gate
  80. await user.click(newQG);
  81. const deleteButton = await screen.findByRole('button', { name: 'delete' });
  82. await user.click(deleteButton);
  83. const popup = screen.getByRole('dialog');
  84. const dialogDeleteButton = within(popup).getByRole('button', { name: 'delete' });
  85. await user.click(dialogDeleteButton);
  86. await waitFor(() => {
  87. expect(screen.queryByRole('link', { name: 'testtwo' })).not.toBeInTheDocument();
  88. });
  89. });
  90. it('should be able to copy a quality gate which is CAYC compliant', async () => {
  91. const user = userEvent.setup();
  92. handler.setIsAdmin(true);
  93. renderQualityGateApp();
  94. const notDefaultQualityGate = await screen.findByText('Sonar way');
  95. await user.click(notDefaultQualityGate);
  96. const copyButton = await screen.findByRole('button', { name: 'copy' });
  97. await user.click(copyButton);
  98. const nameInput = screen.getByRole('textbox', { name: /name.*/ });
  99. expect(nameInput).toBeInTheDocument();
  100. await act(async () => {
  101. await user.click(nameInput);
  102. await user.keyboard(' bis{Enter}');
  103. });
  104. expect(await screen.findByRole('link', { name: /.* bis/ })).toBeInTheDocument();
  105. });
  106. it('should not be able to copy a quality gate which is not CAYC compliant', async () => {
  107. const user = userEvent.setup();
  108. handler.setIsAdmin(true);
  109. renderQualityGateApp();
  110. const notDefaultQualityGate = await screen.findByText('SonarSource way - CFamily');
  111. await user.click(notDefaultQualityGate);
  112. const copyButton = await screen.findByRole('button', { name: 'copy' });
  113. expect(copyButton).toBeDisabled();
  114. });
  115. it('should be able to rename a quality gate', async () => {
  116. const user = userEvent.setup();
  117. handler.setIsAdmin(true);
  118. renderQualityGateApp();
  119. const renameButton = await screen.findByRole('button', { name: 'rename' });
  120. await user.click(renameButton);
  121. const nameInput = screen.getByRole('textbox', { name: /name.*/ });
  122. expect(nameInput).toBeInTheDocument();
  123. await user.click(nameInput);
  124. await user.keyboard('{Control>}a{/Control}New Name{Enter}');
  125. expect(await screen.findByRole('link', { name: /New Name.*/ })).toBeInTheDocument();
  126. });
  127. it('should not be able to set as default a quality gate which is not CAYC compliant', async () => {
  128. const user = userEvent.setup();
  129. handler.setIsAdmin(true);
  130. renderQualityGateApp();
  131. const notDefaultQualityGate = await screen.findByText('SonarSource way - CFamily');
  132. await user.click(notDefaultQualityGate);
  133. const setAsDefaultButton = screen.getByRole('button', { name: 'set_as_default' });
  134. expect(setAsDefaultButton).toBeDisabled();
  135. });
  136. it('should be able to set as default a quality gate which is CAYC compliant', async () => {
  137. const user = userEvent.setup();
  138. handler.setIsAdmin(true);
  139. renderQualityGateApp();
  140. const notDefaultQualityGate = await screen.findByText('Sonar way');
  141. await user.click(notDefaultQualityGate);
  142. const setAsDefaultButton = screen.getByRole('button', { name: 'set_as_default' });
  143. await user.click(setAsDefaultButton);
  144. expect(screen.getAllByRole('link')[2]).toHaveTextContent('default');
  145. });
  146. it('should be able to add a condition', async () => {
  147. const user = userEvent.setup();
  148. handler.setIsAdmin(true);
  149. renderQualityGateApp();
  150. await user.click(await screen.findByText('SonarSource way - CFamily'));
  151. // On new code
  152. await user.click(await screen.findByText('quality_gates.add_condition'));
  153. let dialog = within(screen.getByRole('dialog'));
  154. await user.click(dialog.getByRole('radio', { name: 'quality_gates.conditions.new_code' }));
  155. await selectEvent.select(dialog.getByRole('textbox'), ['Issues']);
  156. await user.click(dialog.getByRole('textbox', { name: 'quality_gates.conditions.value' }));
  157. await user.keyboard('12{Enter}');
  158. const newConditions = within(await screen.findByTestId('quality-gates__conditions-new'));
  159. expect(await newConditions.findByRole('cell', { name: 'Issues' })).toBeInTheDocument();
  160. expect(await newConditions.findByRole('cell', { name: '12' })).toBeInTheDocument();
  161. // On overall code
  162. await user.click(await screen.findByText('quality_gates.add_condition'));
  163. dialog = within(screen.getByRole('dialog'));
  164. await selectEvent.select(dialog.getByRole('textbox'), ['Info Issues']);
  165. await user.click(dialog.getByRole('radio', { name: 'quality_gates.conditions.overall_code' }));
  166. await user.click(dialog.getByLabelText('quality_gates.conditions.operator'));
  167. await user.click(dialog.getByText('quality_gates.operator.LT'));
  168. await user.click(dialog.getByRole('textbox', { name: 'quality_gates.conditions.value' }));
  169. await user.keyboard('42{Enter}');
  170. const overallConditions = within(await screen.findByTestId('quality-gates__conditions-overall'));
  171. expect(await overallConditions.findByRole('cell', { name: 'Info Issues' })).toBeInTheDocument();
  172. expect(await overallConditions.findByRole('cell', { name: '42' })).toBeInTheDocument();
  173. // Select a rating
  174. await user.click(await screen.findByText('quality_gates.add_condition'));
  175. dialog = within(screen.getByRole('dialog'));
  176. await user.click(dialog.getByRole('radio', { name: 'quality_gates.conditions.overall_code' }));
  177. await selectEvent.select(dialog.getByRole('textbox'), ['Maintainability Rating']);
  178. await user.click(dialog.getByLabelText('quality_gates.conditions.value'));
  179. await user.click(dialog.getByText('B'));
  180. await user.click(dialog.getByRole('button', { name: 'quality_gates.add_condition' }));
  181. expect(
  182. await overallConditions.findByRole('cell', { name: 'Maintainability Rating' })
  183. ).toBeInTheDocument();
  184. expect(await overallConditions.findByRole('cell', { name: 'B' })).toBeInTheDocument();
  185. });
  186. it('should be able to edit a condition', async () => {
  187. const user = userEvent.setup();
  188. handler.setIsAdmin(true);
  189. renderQualityGateApp();
  190. const newConditions = within(await screen.findByTestId('quality-gates__conditions-new'));
  191. await user.click(
  192. newConditions.getByLabelText('quality_gates.condition.edit.Coverage on New Code')
  193. );
  194. const dialog = within(screen.getByRole('dialog'));
  195. await user.click(dialog.getByRole('textbox', { name: 'quality_gates.conditions.value' }));
  196. await user.keyboard('{Backspace}{Backspace}23{Enter}');
  197. expect(await newConditions.findByText('Coverage')).toBeInTheDocument();
  198. expect(await newConditions.findByText('23.0%')).toBeInTheDocument();
  199. });
  200. it('should be able to handle duplicate or deprecated condition', async () => {
  201. const user = userEvent.setup();
  202. handler.setIsAdmin(true);
  203. renderQualityGateApp();
  204. await user.click(
  205. // make it a regexp to ignore badges:
  206. await screen.findByRole('link', { name: new RegExp(handler.getCorruptedQualityGateName()) })
  207. );
  208. expect(await screen.findByText('quality_gates.duplicated_conditions')).toBeInTheDocument();
  209. expect(
  210. await screen.findByRole('cell', { name: 'Complexity / Function deprecated' })
  211. ).toBeInTheDocument();
  212. });
  213. it('should be able to handle delete condition', async () => {
  214. const user = userEvent.setup();
  215. handler.setIsAdmin(true);
  216. renderQualityGateApp();
  217. await user.click(await screen.findByText('Non Cayc QG'));
  218. const newConditions = within(await screen.findByTestId('quality-gates__conditions-new'));
  219. await user.click(
  220. newConditions.getByLabelText('quality_gates.condition.delete.Coverage on New Code')
  221. );
  222. const dialog = within(screen.getByRole('dialog'));
  223. await user.click(dialog.getByRole('button', { name: 'delete' }));
  224. await waitFor(() => {
  225. expect(newConditions.queryByRole('cell', { name: 'Coverage' })).not.toBeInTheDocument();
  226. });
  227. });
  228. it('should explain condition on branch', async () => {
  229. renderQualityGateApp({ featureList: [Feature.BranchSupport] });
  230. expect(
  231. await screen.findByText('quality_gates.conditions.new_code.description')
  232. ).toBeInTheDocument();
  233. expect(
  234. await screen.findByText('quality_gates.conditions.overall_code.description')
  235. ).toBeInTheDocument();
  236. });
  237. it('should show warning banner when CAYC condition is not properly set and should be able to update them', async () => {
  238. const user = userEvent.setup();
  239. handler.setIsAdmin(true);
  240. renderQualityGateApp();
  241. const qualityGate = await screen.findByText('SonarSource way - CFamily');
  242. await user.click(qualityGate);
  243. expect(screen.getByText('quality_gates.cayc_missing.banner.title')).toBeInTheDocument();
  244. expect(screen.getByText('quality_gates.cayc_missing.banner.description')).toBeInTheDocument();
  245. expect(
  246. screen.getByRole('button', { name: 'quality_gates.cayc_condition.review_update' })
  247. ).toBeInTheDocument();
  248. await user.click(
  249. screen.getByRole('button', { name: 'quality_gates.cayc_condition.review_update' })
  250. );
  251. expect(
  252. screen.getByRole('dialog', {
  253. name: 'quality_gates.cayc.review_update_modal.header.SonarSource way - CFamily',
  254. })
  255. ).toBeInTheDocument();
  256. expect(
  257. screen.getByText('quality_gates.cayc.review_update_modal.description1')
  258. ).toBeInTheDocument();
  259. expect(
  260. screen.getByText('quality_gates.cayc.review_update_modal.description2')
  261. ).toBeInTheDocument();
  262. expect(
  263. screen.getByRole('button', { name: 'quality_gates.cayc.review_update_modal.confirm_text' })
  264. ).toBeInTheDocument();
  265. await user.click(
  266. screen.getByRole('button', { name: 'quality_gates.cayc.review_update_modal.confirm_text' })
  267. );
  268. const conditionsWrapper = within(await screen.findByTestId('quality-gates__conditions-new'));
  269. expect(conditionsWrapper.getByText('Maintainability Rating')).toBeInTheDocument();
  270. expect(conditionsWrapper.getByText('Reliability Rating')).toBeInTheDocument();
  271. expect(conditionsWrapper.getByText('Security Hotspots Reviewed')).toBeInTheDocument();
  272. expect(conditionsWrapper.getByText('Security Rating')).toBeInTheDocument();
  273. expect(conditionsWrapper.getAllByText('Coverage')).toHaveLength(2); // This quality gate has duplicate condition
  274. expect(conditionsWrapper.getByText('Duplicated Lines (%)')).toBeInTheDocument();
  275. const overallConditionsWrapper = within(
  276. await screen.findByTestId('quality-gates__conditions-overall')
  277. );
  278. expect(overallConditionsWrapper.getByText('Complexity / Function')).toBeInTheDocument();
  279. });
  280. it('should show success banner when quality gate is CAYC compliant', async () => {
  281. const user = userEvent.setup();
  282. handler.setIsAdmin(true);
  283. renderQualityGateApp();
  284. const qualityGate = await screen.findByText('SonarSource way');
  285. await user.click(qualityGate);
  286. expect(screen.getByText('quality_gates.cayc.banner.title')).toBeInTheDocument();
  287. expect(
  288. screen.getByText('quality_gates.cayc.banner.description1', { exact: false })
  289. ).toBeInTheDocument();
  290. expect(
  291. screen.queryByText('quality_gates.cayc_condition.missing_warning.title')
  292. ).not.toBeInTheDocument();
  293. expect(
  294. screen.queryByRole('button', { name: 'quality_gates.cayc_condition.review_update' })
  295. ).not.toBeInTheDocument();
  296. const conditionsWrapper = within(await screen.findByTestId('quality-gates__conditions-new'));
  297. expect(await conditionsWrapper.findByText('Maintainability Rating')).toBeInTheDocument();
  298. expect(await conditionsWrapper.findByText('Reliability Rating')).toBeInTheDocument();
  299. expect(await conditionsWrapper.findByText('Security Hotspots Reviewed')).toBeInTheDocument();
  300. expect(await conditionsWrapper.findByText('Security Rating')).toBeInTheDocument();
  301. expect(await conditionsWrapper.findByText('Coverage')).toBeInTheDocument();
  302. expect(await conditionsWrapper.findByText('Duplicated Lines (%)')).toBeInTheDocument();
  303. });
  304. it('should show info banner when quality gate is CAYC over-compliant', async () => {
  305. const user = userEvent.setup();
  306. handler.setIsAdmin(true);
  307. renderQualityGateApp();
  308. const qualityGate = await screen.findByText('Over Compliant CAYC QG');
  309. await user.click(qualityGate);
  310. expect(screen.getByText('quality_gates.cayc.banner.title')).toBeInTheDocument();
  311. expect(
  312. screen.getByText('quality_gates.cayc.banner.description1', { exact: false })
  313. ).toBeInTheDocument();
  314. expect(screen.getByText('quality_gates.cayc_over_compliant.banner.title')).toBeInTheDocument();
  315. expect(
  316. screen.queryByText('quality_gates.cayc_condition.missing_warning.title')
  317. ).not.toBeInTheDocument();
  318. expect(
  319. screen.queryByRole('button', { name: 'quality_gates.cayc_condition.review_update' })
  320. ).not.toBeInTheDocument();
  321. expect(screen.getByText('quality_gates.cayc.unlock_edit')).toBeInTheDocument();
  322. });
  323. it('should unlock editing option for CAYC conditions', async () => {
  324. const user = userEvent.setup();
  325. handler.setIsAdmin(true);
  326. renderQualityGateApp();
  327. const qualityGate = await screen.findByText('SonarSource way');
  328. await user.click(qualityGate);
  329. expect(screen.getByText('quality_gates.cayc.unlock_edit')).toBeInTheDocument();
  330. expect(
  331. screen.queryByRole('button', {
  332. name: 'quality_gates.condition.edit.Security Rating on New Code',
  333. })
  334. ).not.toBeInTheDocument();
  335. expect(
  336. screen.queryByRole('button', {
  337. name: 'quality_gates.condition.delete.Security Rating on New Code',
  338. })
  339. ).not.toBeInTheDocument();
  340. await user.click(screen.getByText('quality_gates.cayc.unlock_edit'));
  341. expect(
  342. screen.getByRole('button', { name: 'quality_gates.condition.edit.Security Rating on New Code' })
  343. ).toBeInTheDocument();
  344. expect(
  345. screen.getByRole('button', {
  346. name: 'quality_gates.condition.delete.Security Rating on New Code',
  347. })
  348. ).toBeInTheDocument();
  349. });
  350. describe('The Project section', () => {
  351. it('should render list of projects correctly in different tabs', async () => {
  352. const user = userEvent.setup();
  353. handler.setIsAdmin(true);
  354. renderQualityGateApp();
  355. const notDefaultQualityGate = await screen.findByText('SonarSource way - CFamily');
  356. await user.click(notDefaultQualityGate);
  357. // by default it shows "selected" values
  358. expect(screen.getAllByRole('checkbox')).toHaveLength(2);
  359. // change tabs to show deselected projects
  360. await user.click(screen.getByRole('button', { name: 'quality_gates.projects.without' }));
  361. expect(screen.getAllByRole('checkbox')).toHaveLength(2);
  362. // change tabs to show all projects
  363. await user.click(screen.getByRole('button', { name: 'quality_gates.projects.all' }));
  364. expect(screen.getAllByRole('checkbox')).toHaveLength(4);
  365. });
  366. it('should handle select and deselect correctly', async () => {
  367. const user = userEvent.setup();
  368. handler.setIsAdmin(true);
  369. renderQualityGateApp();
  370. const notDefaultQualityGate = await screen.findByText('SonarSource way - CFamily');
  371. await user.click(notDefaultQualityGate);
  372. const checkedProjects = screen.getAllByRole('checkbox')[0];
  373. expect(screen.getAllByRole('checkbox')).toHaveLength(2);
  374. await user.click(checkedProjects);
  375. const reloadButton = screen.getByRole('button', { name: 'reload' });
  376. expect(reloadButton).toBeInTheDocument();
  377. await user.click(reloadButton);
  378. // FP
  379. // eslint-disable-next-line jest-dom/prefer-in-document
  380. expect(screen.getAllByRole('checkbox')).toHaveLength(1);
  381. // change tabs to show deselected projects
  382. await user.click(screen.getByRole('button', { name: 'quality_gates.projects.without' }));
  383. const uncheckedProjects = screen.getAllByRole('checkbox')[0];
  384. expect(screen.getAllByRole('checkbox')).toHaveLength(3);
  385. await user.click(uncheckedProjects);
  386. expect(reloadButton).toBeInTheDocument();
  387. await user.click(reloadButton);
  388. expect(screen.getAllByRole('checkbox')).toHaveLength(2);
  389. });
  390. it('should handle the search of projects', async () => {
  391. const user = userEvent.setup();
  392. handler.setIsAdmin(true);
  393. renderQualityGateApp();
  394. const notDefaultQualityGate = await screen.findByText('SonarSource way - CFamily');
  395. await user.click(notDefaultQualityGate);
  396. const searchInput = screen.getByRole('searchbox', { name: 'search_verb' });
  397. expect(searchInput).toBeInTheDocument();
  398. await user.click(searchInput);
  399. await user.keyboard('test2{Enter}');
  400. // FP
  401. // eslint-disable-next-line jest-dom/prefer-in-document
  402. expect(screen.getAllByRole('checkbox')).toHaveLength(1);
  403. });
  404. it('should display show more button if there are multiple pages of data', async () => {
  405. (searchProjects as jest.Mock).mockResolvedValueOnce({
  406. paging: { pageIndex: 2, pageSize: 3, total: 55 },
  407. results: [],
  408. });
  409. const user = userEvent.setup();
  410. handler.setIsAdmin(true);
  411. renderQualityGateApp();
  412. const notDefaultQualityGate = await screen.findByText('SonarSource way - CFamily');
  413. await user.click(notDefaultQualityGate);
  414. expect(screen.getByRole('button', { name: 'show_more' })).toBeInTheDocument();
  415. });
  416. });
  417. describe('The Permissions section', () => {
  418. it('should not show button to grant permission when user is not admin', async () => {
  419. renderQualityGateApp();
  420. // await just to make sure we've loaded the page
  421. expect(
  422. await screen.findByRole('link', {
  423. name: `${handler.getDefaultQualityGate().name} default`,
  424. })
  425. ).toBeInTheDocument();
  426. expect(screen.queryByText('quality_gates.permissions')).not.toBeInTheDocument();
  427. });
  428. it('should show button to grant permission when user is admin', async () => {
  429. handler.setIsAdmin(true);
  430. renderQualityGateApp();
  431. const grantPermissionButton = await screen.findByRole('button', {
  432. name: 'quality_gates.permissions.grant',
  433. });
  434. expect(screen.getByText('quality_gates.permissions')).toBeInTheDocument();
  435. expect(grantPermissionButton).toBeInTheDocument();
  436. });
  437. it('should assign permission to a user and delete it later', async () => {
  438. const user = userEvent.setup();
  439. handler.setIsAdmin(true);
  440. renderQualityGateApp();
  441. expect(screen.queryByText('userlogin')).not.toBeInTheDocument();
  442. // Granting permission to a user
  443. const grantPermissionButton = await screen.findByRole('button', {
  444. name: 'quality_gates.permissions.grant',
  445. });
  446. await user.click(grantPermissionButton);
  447. const popup = screen.getByRole('dialog');
  448. const searchUserInput = within(popup).getByRole('textbox');
  449. expect(searchUserInput).toBeInTheDocument();
  450. const addUserButton = screen.getByRole('button', {
  451. name: 'add_verb',
  452. });
  453. expect(addUserButton).toBeDisabled();
  454. await user.click(searchUserInput);
  455. expect(screen.getAllByTestId('qg-add-permission-option')).toHaveLength(2);
  456. await user.click(screen.getByText('userlogin'));
  457. expect(addUserButton).toBeEnabled();
  458. await user.click(addUserButton);
  459. expect(screen.getByText('userlogin')).toBeInTheDocument();
  460. // Cancel granting permission
  461. await user.click(grantPermissionButton);
  462. await user.click(searchUserInput);
  463. await user.keyboard('test{Enter}');
  464. const cancelButton = screen.getByRole('button', {
  465. name: 'cancel',
  466. });
  467. await user.click(cancelButton);
  468. const permissionList = within(await screen.findByTestId('quality-gate-permissions'));
  469. expect(permissionList.getByRole('listitem')).toBeInTheDocument();
  470. // Delete the user permission
  471. const deleteButton = screen.getByTestId('permission-delete-button');
  472. await user.click(deleteButton);
  473. const deletePopup = screen.getByRole('dialog');
  474. const dialogDeleteButton = within(deletePopup).getByRole('button', { name: 'remove' });
  475. await user.click(dialogDeleteButton);
  476. expect(permissionList.queryByRole('listitem')).not.toBeInTheDocument();
  477. });
  478. it('should assign permission to a group and delete it later', async () => {
  479. const user = userEvent.setup();
  480. handler.setIsAdmin(true);
  481. renderQualityGateApp();
  482. expect(screen.queryByText('userlogin')).not.toBeInTheDocument();
  483. // Granting permission to a group
  484. const grantPermissionButton = await screen.findByRole('button', {
  485. name: 'quality_gates.permissions.grant',
  486. });
  487. await user.click(grantPermissionButton);
  488. const popup = screen.getByRole('dialog');
  489. const searchUserInput = within(popup).getByRole('textbox');
  490. const addUserButton = screen.getByRole('button', {
  491. name: 'add_verb',
  492. });
  493. await user.click(searchUserInput);
  494. expect(screen.getAllByTestId('qg-add-permission-option')).toHaveLength(2);
  495. await user.click(screen.getAllByTestId('qg-add-permission-option')[1]);
  496. await user.click(addUserButton);
  497. expect(screen.getByText('Foo')).toBeInTheDocument();
  498. // Delete the group permission
  499. const deleteButton = screen.getByTestId('permission-delete-button');
  500. await user.click(deleteButton);
  501. const deletePopup = screen.getByRole('dialog');
  502. const dialogDeleteButton = within(deletePopup).getByRole('button', { name: 'remove' });
  503. await user.click(dialogDeleteButton);
  504. const permissionList = within(await screen.findByTestId('quality-gate-permissions'));
  505. expect(permissionList.queryByRole('listitem')).not.toBeInTheDocument();
  506. });
  507. it('should handle searchUser service failure', async () => {
  508. (searchUsers as jest.Mock).mockRejectedValue('error');
  509. const user = userEvent.setup();
  510. handler.setIsAdmin(true);
  511. renderQualityGateApp();
  512. const grantPermissionButton = await screen.findByRole('button', {
  513. name: 'quality_gates.permissions.grant',
  514. });
  515. await user.click(grantPermissionButton);
  516. const popup = screen.getByRole('dialog');
  517. const searchUserInput = within(popup).getByRole('textbox');
  518. await user.click(searchUserInput);
  519. expect(screen.getByText('no_results')).toBeInTheDocument();
  520. });
  521. });
  522. function renderQualityGateApp(context?: RenderContext) {
  523. renderAppRoutes('quality_gates', routes, context);
  524. }