您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

QualityGate-it.tsx 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
  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 UsersServiceMock from '../../../../api/mocks/UsersServiceMock';
  25. import { searchProjects, searchUsers } from '../../../../api/quality-gates';
  26. import { dismissNotice } from '../../../../api/users';
  27. import { mockLoggedInUser } from '../../../../helpers/testMocks';
  28. import { RenderContext, renderAppRoutes } from '../../../../helpers/testReactTestingUtils';
  29. import { byRole } from '../../../../helpers/testSelector';
  30. import { Feature } from '../../../../types/features';
  31. import { NoticeType } from '../../../../types/users';
  32. import routes from '../../routes';
  33. let qualityGateHandler: QualityGatesServiceMock;
  34. let usersHandler: UsersServiceMock;
  35. beforeAll(() => {
  36. qualityGateHandler = new QualityGatesServiceMock();
  37. usersHandler = new UsersServiceMock();
  38. });
  39. afterEach(() => {
  40. qualityGateHandler.reset();
  41. usersHandler.reset();
  42. });
  43. it('should open the default quality gates', async () => {
  44. renderQualityGateApp();
  45. const defaultQualityGate = qualityGateHandler.getDefaultQualityGate();
  46. expect(
  47. await screen.findByRole('button', {
  48. current: 'page',
  49. name: `${defaultQualityGate.name} default`,
  50. }),
  51. ).toBeInTheDocument();
  52. });
  53. it('should list all quality gates', async () => {
  54. renderQualityGateApp();
  55. expect(
  56. await screen.findByRole('button', {
  57. name: `${qualityGateHandler.getDefaultQualityGate().name} default`,
  58. }),
  59. ).toBeInTheDocument();
  60. expect(
  61. screen.getByRole('button', {
  62. name: `${qualityGateHandler.getBuiltInQualityGate().name} quality_gates.built_in`,
  63. }),
  64. ).toBeInTheDocument();
  65. });
  66. it('should render the built-in quality gate properly', async () => {
  67. const user = userEvent.setup();
  68. renderQualityGateApp();
  69. const builtInQualityGate = await screen.findByText('Sonar way');
  70. await act(async () => {
  71. await user.click(builtInQualityGate);
  72. });
  73. expect(await screen.findByText(/quality_gates.is_built_in.cayc.description/)).toBeInTheDocument();
  74. expect(await screen.findByText(/quality_gates.is_built_in.description/)).toBeInTheDocument();
  75. });
  76. it('should be able to create a quality gate then delete it', async () => {
  77. const user = userEvent.setup();
  78. qualityGateHandler.setIsAdmin(true);
  79. renderQualityGateApp();
  80. let createButton = await screen.findByRole('button', { name: 'create' });
  81. // Using keyboard
  82. await user.click(createButton);
  83. await act(async () => {
  84. await user.click(screen.getByRole('textbox', { name: /name.*/ }));
  85. await user.keyboard('testone');
  86. await user.click(screen.getByRole('button', { name: 'quality_gate.create' }));
  87. });
  88. expect(await screen.findByRole('button', { name: 'testone' })).toBeInTheDocument();
  89. // Using modal button
  90. createButton = await screen.findByRole('button', { name: 'create' });
  91. await user.click(createButton);
  92. const saveButton = screen.getByRole('button', { name: 'quality_gate.create' });
  93. expect(saveButton).toBeDisabled();
  94. const nameInput = screen.getByRole('textbox', { name: /name.*/ });
  95. await act(async () => {
  96. await user.click(nameInput);
  97. await user.keyboard('testtwo');
  98. await user.click(saveButton);
  99. });
  100. const newQG = await screen.findByRole('button', { name: 'testtwo' });
  101. expect(newQG).toBeInTheDocument();
  102. // Delete the quality gate
  103. await user.click(newQG);
  104. await user.click(screen.getByLabelText('menu'));
  105. const deleteButton = screen.getByRole('menuitem', { name: 'delete' });
  106. await user.click(deleteButton);
  107. const popup = screen.getByRole('dialog');
  108. const dialogDeleteButton = within(popup).getByRole('button', { name: 'delete' });
  109. await user.click(dialogDeleteButton);
  110. await waitFor(() => {
  111. expect(screen.queryByRole('button', { name: 'testtwo' })).not.toBeInTheDocument();
  112. });
  113. });
  114. it('should be able to copy a quality gate which is CAYC compliant', async () => {
  115. const user = userEvent.setup();
  116. qualityGateHandler.setIsAdmin(true);
  117. renderQualityGateApp();
  118. const notDefaultQualityGate = await screen.findByText('Sonar way');
  119. await user.click(notDefaultQualityGate);
  120. await user.click(screen.getByLabelText('menu'));
  121. const copyButton = screen.getByRole('menuitem', { name: 'copy' });
  122. await user.click(copyButton);
  123. const nameInput = screen.getByRole('textbox', { name: /name.*/ });
  124. expect(nameInput).toBeInTheDocument();
  125. await act(async () => {
  126. await user.click(nameInput);
  127. await user.keyboard(' bis{Enter}');
  128. });
  129. expect(await screen.findByRole('button', { name: /.* bis/ })).toBeInTheDocument();
  130. });
  131. it('should not be able to copy a quality gate which is not CAYC compliant', async () => {
  132. const user = userEvent.setup();
  133. qualityGateHandler.setIsAdmin(true);
  134. renderQualityGateApp();
  135. const notDefaultQualityGate = await screen.findByText('SonarSource way - CFamily');
  136. await user.click(notDefaultQualityGate);
  137. await user.click(screen.getByLabelText('menu'));
  138. const copyButton = screen.getByRole('menuitem', { name: 'copy' });
  139. expect(copyButton).toBeDisabled();
  140. });
  141. it('should be able to rename a quality gate', async () => {
  142. const user = userEvent.setup();
  143. qualityGateHandler.setIsAdmin(true);
  144. renderQualityGateApp();
  145. await user.click(await screen.findByLabelText('menu'));
  146. const renameButton = screen.getByRole('menuitem', { name: 'rename' });
  147. await user.click(renameButton);
  148. const nameInput = screen.getByRole('textbox', { name: /name.*/ });
  149. expect(nameInput).toBeInTheDocument();
  150. await user.click(nameInput);
  151. await user.keyboard('{Control>}a{/Control}New Name{Enter}');
  152. expect(await screen.findByRole('button', { name: /New Name.*/ })).toBeInTheDocument();
  153. });
  154. it('should not be able to set as default a quality gate which is not CAYC compliant', async () => {
  155. const user = userEvent.setup();
  156. qualityGateHandler.setIsAdmin(true);
  157. renderQualityGateApp();
  158. const notDefaultQualityGate = await screen.findByText('SonarSource way - CFamily');
  159. await user.click(notDefaultQualityGate);
  160. await user.click(screen.getByLabelText('menu'));
  161. const setAsDefaultButton = screen.getByRole('menuitem', { name: 'set_as_default' });
  162. expect(setAsDefaultButton).toBeDisabled();
  163. });
  164. it('should be able to set as default a quality gate which is CAYC compliant', async () => {
  165. const user = userEvent.setup();
  166. qualityGateHandler.setIsAdmin(true);
  167. renderQualityGateApp();
  168. const notDefaultQualityGate = await screen.findByRole('button', { name: /Sonar way/ });
  169. await user.click(notDefaultQualityGate);
  170. await user.click(screen.getByLabelText('menu'));
  171. const setAsDefaultButton = screen.getByRole('menuitem', { name: 'set_as_default' });
  172. await user.click(setAsDefaultButton);
  173. expect(screen.getByRole('button', { name: /Sonar way default/ })).toBeInTheDocument();
  174. });
  175. it('should be able to add a condition', async () => {
  176. const user = userEvent.setup();
  177. qualityGateHandler.setIsAdmin(true);
  178. renderQualityGateApp();
  179. await user.click(await screen.findByText('SonarSource way - CFamily'));
  180. // On new code
  181. await user.click(await screen.findByText('quality_gates.add_condition'));
  182. let dialog = within(screen.getByRole('dialog'));
  183. await user.click(dialog.getByRole('radio', { name: 'quality_gates.conditions.new_code' }));
  184. await selectEvent.select(dialog.getByRole('combobox'), ['Issues']);
  185. await user.click(dialog.getByRole('textbox', { name: 'quality_gates.conditions.value' }));
  186. await user.keyboard('12');
  187. await user.click(dialog.getByRole('button', { name: 'quality_gates.add_condition' }));
  188. const newConditions = within(await screen.findByTestId('quality-gates__conditions-new'));
  189. expect(await newConditions.findByRole('cell', { name: 'Issues' })).toBeInTheDocument();
  190. expect(await newConditions.findByRole('cell', { name: '12' })).toBeInTheDocument();
  191. // On overall code
  192. await user.click(await screen.findByText('quality_gates.add_condition'));
  193. dialog = within(screen.getByRole('dialog'));
  194. await selectEvent.select(dialog.getByRole('combobox'), ['Info Issues']);
  195. await user.click(dialog.getByRole('radio', { name: 'quality_gates.conditions.overall_code' }));
  196. await user.click(dialog.getByLabelText('quality_gates.conditions.operator'));
  197. await user.click(dialog.getByText('quality_gates.operator.LT'));
  198. await user.click(dialog.getByRole('textbox', { name: 'quality_gates.conditions.value' }));
  199. await user.keyboard('42');
  200. await user.click(dialog.getByRole('button', { name: 'quality_gates.add_condition' }));
  201. const overallConditions = within(await screen.findByTestId('quality-gates__conditions-overall'));
  202. expect(await overallConditions.findByRole('cell', { name: 'Info Issues' })).toBeInTheDocument();
  203. expect(await overallConditions.findByRole('cell', { name: '42' })).toBeInTheDocument();
  204. // Select a rating
  205. await user.click(await screen.findByText('quality_gates.add_condition'));
  206. dialog = within(screen.getByRole('dialog'));
  207. await user.click(dialog.getByRole('radio', { name: 'quality_gates.conditions.overall_code' }));
  208. await selectEvent.select(dialog.getByRole('combobox'), ['Maintainability Rating']);
  209. await user.click(dialog.getByLabelText('quality_gates.conditions.value'));
  210. await user.click(dialog.getByText('B'));
  211. await user.click(dialog.getByRole('button', { name: 'quality_gates.add_condition' }));
  212. expect(
  213. await overallConditions.findByRole('cell', { name: 'Maintainability Rating' }),
  214. ).toBeInTheDocument();
  215. expect(await overallConditions.findByRole('cell', { name: 'B' })).toBeInTheDocument();
  216. });
  217. it('should be able to edit a condition', async () => {
  218. const user = userEvent.setup();
  219. qualityGateHandler.setIsAdmin(true);
  220. renderQualityGateApp();
  221. const newConditions = within(await screen.findByTestId('quality-gates__conditions-new'));
  222. await user.click(
  223. newConditions.getByLabelText('quality_gates.condition.edit.Line Coverage on New Code'),
  224. );
  225. const dialog = within(screen.getByRole('dialog'));
  226. const textBox = dialog.getByRole('textbox', { name: 'quality_gates.conditions.value' });
  227. await user.clear(textBox);
  228. await user.type(textBox, '23');
  229. await user.click(dialog.getByRole('button', { name: 'quality_gates.update_condition' }));
  230. expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
  231. expect(await newConditions.findByText('Line Coverage')).toBeInTheDocument();
  232. expect(
  233. await within(await screen.findByTestId('quality-gates__conditions-new')).findByText('23.0%'),
  234. ).toBeInTheDocument();
  235. });
  236. it('should be able to handle duplicate or deprecated condition', async () => {
  237. const user = userEvent.setup();
  238. qualityGateHandler.setIsAdmin(true);
  239. renderQualityGateApp();
  240. await user.click(
  241. // make it a regexp to ignore badges:
  242. await screen.findByRole('button', {
  243. name: new RegExp(qualityGateHandler.getCorruptedQualityGateName()),
  244. }),
  245. );
  246. expect(await screen.findByText('quality_gates.duplicated_conditions')).toBeInTheDocument();
  247. expect(
  248. await screen.findByRole('cell', { name: 'Complexity / Function deprecated' }),
  249. ).toBeInTheDocument();
  250. });
  251. it('should be able to handle delete condition', async () => {
  252. const user = userEvent.setup();
  253. qualityGateHandler.setIsAdmin(true);
  254. renderQualityGateApp();
  255. await user.click(await screen.findByText('Non Cayc QG'));
  256. const newConditions = within(await screen.findByTestId('quality-gates__conditions-new'));
  257. await user.click(
  258. newConditions.getByLabelText('quality_gates.condition.delete.Coverage on New Code'),
  259. );
  260. const dialog = within(screen.getByRole('dialog'));
  261. await user.click(dialog.getByRole('button', { name: 'delete' }));
  262. await waitFor(() => {
  263. expect(newConditions.queryByRole('cell', { name: 'Coverage' })).not.toBeInTheDocument();
  264. });
  265. });
  266. it('should explain condition on branch', async () => {
  267. renderQualityGateApp({ featureList: [Feature.BranchSupport] });
  268. expect(
  269. await screen.findByText('quality_gates.conditions.new_code.description'),
  270. ).toBeInTheDocument();
  271. expect(
  272. await screen.findByText('quality_gates.conditions.overall_code.description'),
  273. ).toBeInTheDocument();
  274. });
  275. it('should show warning banner when CAYC condition is not properly set and should be able to update them', async () => {
  276. const user = userEvent.setup();
  277. qualityGateHandler.setIsAdmin(true);
  278. renderQualityGateApp();
  279. const qualityGate = await screen.findByText('SonarSource way - CFamily');
  280. await user.click(qualityGate);
  281. expect(screen.getByText('quality_gates.cayc_missing.banner.title')).toBeInTheDocument();
  282. expect(screen.getByText('quality_gates.cayc_missing.banner.description')).toBeInTheDocument();
  283. expect(
  284. screen.getByRole('button', { name: 'quality_gates.cayc_condition.review_update' }),
  285. ).toBeInTheDocument();
  286. await user.click(
  287. screen.getByRole('button', { name: 'quality_gates.cayc_condition.review_update' }),
  288. );
  289. expect(
  290. screen.getByRole('dialog', {
  291. name: 'quality_gates.cayc.review_update_modal.header.SonarSource way - CFamily',
  292. }),
  293. ).toBeInTheDocument();
  294. expect(
  295. screen.getByText('quality_gates.cayc.review_update_modal.description1'),
  296. ).toBeInTheDocument();
  297. expect(
  298. screen.getByText('quality_gates.cayc.review_update_modal.description2'),
  299. ).toBeInTheDocument();
  300. expect(
  301. screen.getByRole('button', { name: 'quality_gates.cayc.review_update_modal.confirm_text' }),
  302. ).toBeInTheDocument();
  303. await user.click(
  304. screen.getByRole('button', { name: 'quality_gates.cayc.review_update_modal.confirm_text' }),
  305. );
  306. expect(await screen.findByText('quality_gates.cayc.banner.title')).toBeInTheDocument();
  307. const overallConditionsWrapper = within(
  308. await screen.findByTestId('quality-gates__conditions-overall'),
  309. );
  310. expect(overallConditionsWrapper.getByText('Complexity / Function')).toBeInTheDocument();
  311. });
  312. it('should show optimize banner when CAYC condition is not properly set and QG is compliant and should be able to update them', async () => {
  313. const user = userEvent.setup();
  314. qualityGateHandler.setIsAdmin(true);
  315. renderQualityGateApp();
  316. const qualityGate = await screen.findByText('Non Cayc Compliant QG');
  317. await user.click(qualityGate);
  318. expect(screen.getByText('quality_gates.cayc_optimize.banner.title')).toBeInTheDocument();
  319. expect(screen.getByText('quality_gates.cayc_optimize.banner.description')).toBeInTheDocument();
  320. expect(
  321. screen.getByRole('button', { name: 'quality_gates.cayc_condition.review_optimize' }),
  322. ).toBeInTheDocument();
  323. await user.click(
  324. screen.getByRole('button', { name: 'quality_gates.cayc_condition.review_optimize' }),
  325. );
  326. expect(
  327. screen.getByRole('dialog', {
  328. name: 'quality_gates.cayc.review_optimize_modal.header.Non Cayc Compliant QG',
  329. }),
  330. ).toBeInTheDocument();
  331. expect(
  332. screen.getByText('quality_gates.cayc.review_optimize_modal.description1'),
  333. ).toBeInTheDocument();
  334. expect(
  335. screen.getByText('quality_gates.cayc.review_update_modal.description2'),
  336. ).toBeInTheDocument();
  337. expect(
  338. screen.getByRole('button', { name: 'quality_gates.cayc.review_optimize_modal.confirm_text' }),
  339. ).toBeInTheDocument();
  340. await user.click(
  341. screen.getByRole('button', { name: 'quality_gates.cayc.review_optimize_modal.confirm_text' }),
  342. );
  343. });
  344. it('should not warn user when quality gate is not CAYC compliant and user has no permission to edit it', async () => {
  345. const user = userEvent.setup();
  346. renderQualityGateApp();
  347. const nonCompliantQualityGate = await screen.findByRole('button', { name: 'Non Cayc QG' });
  348. await user.click(nonCompliantQualityGate);
  349. expect(screen.queryByRole('alert')).not.toBeInTheDocument();
  350. expect(screen.queryByText('quality_gates.cayc.tooltip.message')).not.toBeInTheDocument();
  351. });
  352. it('should not show optimize banner when quality gate is compliant but non-CaYC and user has no permission to edit it', async () => {
  353. const user = userEvent.setup();
  354. renderQualityGateApp();
  355. const nonCompliantQualityGate = await screen.findByRole('button', {
  356. name: 'Non Cayc Compliant QG',
  357. });
  358. await user.click(nonCompliantQualityGate);
  359. expect(screen.queryByRole('alert')).not.toBeInTheDocument();
  360. expect(screen.queryByText('quality_gates.cayc.tooltip.message')).not.toBeInTheDocument();
  361. });
  362. it('should warn user when quality gate is not CAYC compliant and user has permission to edit it', async () => {
  363. const user = userEvent.setup();
  364. qualityGateHandler.setIsAdmin(true);
  365. renderQualityGateApp();
  366. const nonCompliantQualityGate = await screen.findByRole('button', { name: /Non Cayc QG/ });
  367. await user.click(nonCompliantQualityGate);
  368. // expect(screen.getByTestId('conditions')).toMatchSnapshot();
  369. expect(await screen.findByText(/quality_gates.cayc_missing.banner.title/)).toBeInTheDocument();
  370. expect(screen.getAllByText('quality_gates.cayc.tooltip.message').length).toBeGreaterThan(0);
  371. });
  372. it('should show optimize banner when quality gate is compliant but non-CaYC and user has permission to edit it', async () => {
  373. const user = userEvent.setup();
  374. qualityGateHandler.setIsAdmin(true);
  375. renderQualityGateApp();
  376. const nonCompliantQualityGate = await screen.findByRole('button', {
  377. name: /Non Cayc Compliant QG/,
  378. });
  379. await user.click(nonCompliantQualityGate);
  380. // expect(screen.getByTestId('conditions')).toMatchSnapshot();
  381. expect(await screen.findByText(/quality_gates.cayc_optimize.banner.title/)).toBeInTheDocument();
  382. expect(screen.getAllByText('quality_gates.cayc.tooltip.message').length).toBeGreaterThan(0);
  383. });
  384. it('should render CaYC conditions on a separate table if Sonar way', async () => {
  385. const user = userEvent.setup();
  386. qualityGateHandler.setIsAdmin(true);
  387. renderQualityGateApp();
  388. await user.click(await screen.findByText('Sonar way'));
  389. expect(screen.queryByText('quality_gates.cayc.banner.title')).not.toBeInTheDocument();
  390. expect(await screen.findByTestId('quality-gates__conditions-cayc')).toBeInTheDocument();
  391. });
  392. it('should display CaYC condition simplification tour for users who didnt dismissed it', async () => {
  393. const user = userEvent.setup();
  394. qualityGateHandler.setIsAdmin(true);
  395. renderQualityGateApp({ currentUser: mockLoggedInUser() });
  396. const qualityGate = await screen.findByText('Sonar way');
  397. await act(async () => {
  398. await user.click(qualityGate);
  399. });
  400. expect(await byRole('alertdialog').find()).toBeInTheDocument();
  401. expect(
  402. byRole('alertdialog')
  403. .byText('quality_gates.cayc.condition_simplification_tour.page_1.title')
  404. .get(),
  405. ).toBeInTheDocument();
  406. await act(async () => {
  407. await user.click(byRole('alertdialog').byRole('button', { name: 'next' }).get());
  408. });
  409. expect(
  410. byRole('alertdialog')
  411. .byText('quality_gates.cayc.condition_simplification_tour.page_2.title')
  412. .get(),
  413. ).toBeInTheDocument();
  414. await act(async () => {
  415. await user.click(byRole('alertdialog').byRole('button', { name: 'next' }).get());
  416. });
  417. expect(
  418. byRole('alertdialog')
  419. .byText('quality_gates.cayc.condition_simplification_tour.page_3.title')
  420. .get(),
  421. ).toBeInTheDocument();
  422. await act(async () => {
  423. await user.click(byRole('alertdialog').byRole('button', { name: 'dismiss' }).get());
  424. });
  425. expect(byRole('alertdialog').query()).not.toBeInTheDocument();
  426. expect(dismissNotice).toHaveBeenLastCalledWith(NoticeType.QG_CAYC_CONDITIONS_SIMPLIFICATION);
  427. });
  428. it('should not display CaYC condition simplification tour for users who dismissed it', async () => {
  429. const user = userEvent.setup();
  430. qualityGateHandler.setIsAdmin(true);
  431. renderQualityGateApp({
  432. currentUser: mockLoggedInUser({
  433. dismissedNotices: { [NoticeType.QG_CAYC_CONDITIONS_SIMPLIFICATION]: true },
  434. }),
  435. });
  436. const qualityGate = await screen.findByText('Sonar way');
  437. await act(async () => {
  438. await user.click(qualityGate);
  439. });
  440. expect(byRole('alertdialog').query()).not.toBeInTheDocument();
  441. });
  442. describe('The Project section', () => {
  443. it('should render list of projects correctly in different tabs', async () => {
  444. const user = userEvent.setup();
  445. qualityGateHandler.setIsAdmin(true);
  446. renderQualityGateApp();
  447. const notDefaultQualityGate = await screen.findByText('SonarSource way - CFamily');
  448. await user.click(notDefaultQualityGate);
  449. // by default it shows "selected" values
  450. expect(screen.getAllByRole('checkbox')).toHaveLength(2);
  451. // change tabs to show deselected projects
  452. await user.click(screen.getByRole('radio', { name: 'quality_gates.projects.without' }));
  453. expect(screen.getAllByRole('checkbox')).toHaveLength(2);
  454. // change tabs to show all projects
  455. await user.click(screen.getByRole('radio', { name: 'quality_gates.projects.all' }));
  456. expect(screen.getAllByRole('checkbox')).toHaveLength(4);
  457. });
  458. it('should handle select and deselect correctly', async () => {
  459. const user = userEvent.setup();
  460. qualityGateHandler.setIsAdmin(true);
  461. renderQualityGateApp();
  462. const notDefaultQualityGate = await screen.findByText('SonarSource way - CFamily');
  463. await user.click(notDefaultQualityGate);
  464. const checkedProjects = screen.getAllByRole('checkbox')[0];
  465. expect(screen.getAllByRole('checkbox')).toHaveLength(2);
  466. await user.click(checkedProjects);
  467. const reloadButton = screen.getByRole('button', { name: 'reload' });
  468. expect(reloadButton).toBeInTheDocument();
  469. await user.click(reloadButton);
  470. // FP
  471. // eslint-disable-next-line jest-dom/prefer-in-document
  472. expect(screen.getAllByRole('checkbox')).toHaveLength(1);
  473. // change tabs to show deselected projects
  474. await user.click(screen.getByRole('radio', { name: 'quality_gates.projects.without' }));
  475. const uncheckedProjects = screen.getAllByRole('checkbox')[0];
  476. expect(screen.getAllByRole('checkbox')).toHaveLength(3);
  477. await user.click(uncheckedProjects);
  478. expect(reloadButton).toBeInTheDocument();
  479. await user.click(reloadButton);
  480. expect(screen.getAllByRole('checkbox')).toHaveLength(2);
  481. });
  482. it('should handle the search of projects', async () => {
  483. const user = userEvent.setup();
  484. qualityGateHandler.setIsAdmin(true);
  485. renderQualityGateApp();
  486. const notDefaultQualityGate = await screen.findByText('SonarSource way - CFamily');
  487. await user.click(notDefaultQualityGate);
  488. const searchInput = screen.getByRole('searchbox', { name: 'search_verb' });
  489. expect(searchInput).toBeInTheDocument();
  490. await user.click(searchInput);
  491. await user.keyboard('test2{Enter}');
  492. // FP
  493. // eslint-disable-next-line jest-dom/prefer-in-document
  494. expect(screen.getAllByRole('checkbox')).toHaveLength(1);
  495. });
  496. it('should display show more button if there are multiple pages of data', async () => {
  497. (searchProjects as jest.Mock).mockResolvedValueOnce({
  498. paging: { pageIndex: 2, pageSize: 3, total: 55 },
  499. results: [],
  500. });
  501. const user = userEvent.setup();
  502. qualityGateHandler.setIsAdmin(true);
  503. renderQualityGateApp();
  504. const notDefaultQualityGate = await screen.findByText('SonarSource way - CFamily');
  505. await user.click(notDefaultQualityGate);
  506. expect(screen.getByRole('button', { name: 'show_more' })).toBeInTheDocument();
  507. });
  508. });
  509. describe('The Permissions section', () => {
  510. it('should not show button to grant permission when user is not admin', async () => {
  511. renderQualityGateApp();
  512. // await just to make sure we've loaded the page
  513. expect(
  514. await screen.findByRole('button', {
  515. name: `${qualityGateHandler.getDefaultQualityGate().name} default`,
  516. }),
  517. ).toBeInTheDocument();
  518. expect(screen.queryByText('quality_gates.permissions')).not.toBeInTheDocument();
  519. });
  520. it('should show button to grant permission when user is admin', async () => {
  521. qualityGateHandler.setIsAdmin(true);
  522. renderQualityGateApp();
  523. const grantPermissionButton = await screen.findByRole('button', {
  524. name: 'quality_gates.permissions.grant',
  525. });
  526. expect(screen.getByText('quality_gates.permissions')).toBeInTheDocument();
  527. expect(grantPermissionButton).toBeInTheDocument();
  528. });
  529. it('should assign permission to a user and delete it later', async () => {
  530. const user = userEvent.setup();
  531. qualityGateHandler.setIsAdmin(true);
  532. renderQualityGateApp();
  533. expect(screen.queryByText('userlogin')).not.toBeInTheDocument();
  534. // Granting permission to a user
  535. const grantPermissionButton = await screen.findByRole('button', {
  536. name: 'quality_gates.permissions.grant',
  537. });
  538. await user.click(grantPermissionButton);
  539. const popup = screen.getByRole('dialog');
  540. const searchUserInput = within(popup).getByRole('combobox', {
  541. name: 'quality_gates.permissions.search',
  542. });
  543. expect(searchUserInput).toBeInTheDocument();
  544. const addUserButton = screen.getByRole('button', {
  545. name: 'add_verb',
  546. });
  547. expect(addUserButton).toBeDisabled();
  548. await user.click(searchUserInput);
  549. await user.click(screen.getByText('userlogin'));
  550. expect(addUserButton).toBeEnabled();
  551. await user.click(addUserButton);
  552. expect(screen.getByText('userlogin')).toBeInTheDocument();
  553. // Cancel granting permission
  554. await user.click(grantPermissionButton);
  555. await user.click(searchUserInput);
  556. await user.keyboard('test{Enter}');
  557. const cancelButton = screen.getByRole('button', {
  558. name: 'cancel',
  559. });
  560. await user.click(cancelButton);
  561. const permissionList = within(await screen.findByTestId('quality-gate-permissions'));
  562. expect(permissionList.getByRole('row')).toBeInTheDocument();
  563. // Delete the user permission
  564. const deleteButton = screen.getByTestId('permission-delete-button');
  565. await user.click(deleteButton);
  566. const deletePopup = screen.getByRole('dialog');
  567. const dialogDeleteButton = within(deletePopup).getByRole('button', { name: 'remove' });
  568. await user.click(dialogDeleteButton);
  569. expect(permissionList.queryByRole('row')).not.toBeInTheDocument();
  570. });
  571. it('should assign permission to a group and delete it later', async () => {
  572. const user = userEvent.setup();
  573. qualityGateHandler.setIsAdmin(true);
  574. renderQualityGateApp();
  575. expect(screen.queryByText('userlogin')).not.toBeInTheDocument();
  576. // Granting permission to a group
  577. const grantPermissionButton = await screen.findByRole('button', {
  578. name: 'quality_gates.permissions.grant',
  579. });
  580. await user.click(grantPermissionButton);
  581. const popup = screen.getByRole('dialog');
  582. const searchUserInput = within(popup).getByRole('combobox', {
  583. name: 'quality_gates.permissions.search',
  584. });
  585. const addUserButton = screen.getByRole('button', {
  586. name: 'add_verb',
  587. });
  588. await user.click(searchUserInput);
  589. await user.click(within(popup).getByLabelText('Foo'));
  590. await user.click(addUserButton);
  591. expect(screen.getByText('Foo')).toBeInTheDocument();
  592. // Delete the group permission
  593. const deleteButton = screen.getByTestId('permission-delete-button');
  594. await user.click(deleteButton);
  595. const deletePopup = screen.getByRole('dialog');
  596. const dialogDeleteButton = within(deletePopup).getByRole('button', { name: 'remove' });
  597. await user.click(dialogDeleteButton);
  598. const permissionList = within(await screen.findByTestId('quality-gate-permissions'));
  599. expect(permissionList.queryByRole('listitem')).not.toBeInTheDocument();
  600. });
  601. it('should handle searchUser service failure', async () => {
  602. (searchUsers as jest.Mock).mockRejectedValue('error');
  603. const user = userEvent.setup();
  604. qualityGateHandler.setIsAdmin(true);
  605. renderQualityGateApp();
  606. const grantPermissionButton = await screen.findByRole('button', {
  607. name: 'quality_gates.permissions.grant',
  608. });
  609. await user.click(grantPermissionButton);
  610. const popup = screen.getByRole('dialog');
  611. const searchUserInput = within(popup).getByRole('combobox', {
  612. name: 'quality_gates.permissions.search',
  613. });
  614. await user.click(searchUserInput);
  615. expect(screen.getByText('no_results')).toBeInTheDocument();
  616. });
  617. });
  618. function renderQualityGateApp(context?: RenderContext) {
  619. return renderAppRoutes('quality_gates', routes, context);
  620. }