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.

QualityProfilesServiceMock.ts 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789
  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 { cloneDeep } from 'lodash';
  21. import { ProfileChangelogEvent } from '../../apps/quality-profiles/types';
  22. import { RequestData } from '../../helpers/request';
  23. import {
  24. mockCompareResult,
  25. mockGroup,
  26. mockPaging,
  27. mockQualityProfile,
  28. mockQualityProfileChangelogEvent,
  29. mockRuleDetails,
  30. mockUserSelected,
  31. } from '../../helpers/testMocks';
  32. import {
  33. CleanCodeAttribute,
  34. CleanCodeAttributeCategory,
  35. SoftwareImpactSeverity,
  36. SoftwareQuality,
  37. } from '../../types/clean-code-taxonomy';
  38. import { SearchRulesResponse } from '../../types/coding-rules';
  39. import { IssueSeverity } from '../../types/issues';
  40. import { SearchRulesQuery } from '../../types/rules';
  41. import { Dict, Paging, ProfileInheritanceDetails, RuleDetails } from '../../types/types';
  42. import {
  43. CompareResponse,
  44. Profile,
  45. ProfileProject,
  46. SearchQualityProfilesParameters,
  47. SearchQualityProfilesResponse,
  48. activateRule,
  49. addGroup,
  50. addUser,
  51. associateProject,
  52. changeProfileParent,
  53. compareProfiles,
  54. copyProfile,
  55. createQualityProfile,
  56. deactivateRule,
  57. deleteProfile,
  58. dissociateProject,
  59. getExporters,
  60. getImporters,
  61. getProfileChangelog,
  62. getProfileInheritance,
  63. getProfileProjects,
  64. getQualityProfile,
  65. getQualityProfileExporterUrl,
  66. removeGroup,
  67. removeUser,
  68. renameProfile,
  69. restoreQualityProfile,
  70. searchGroups,
  71. searchQualityProfiles,
  72. searchUsers,
  73. setDefaultProfile,
  74. } from '../quality-profiles';
  75. import { getRuleDetails, searchRules, listRules } from '../rules';
  76. jest.mock('../../api/rules');
  77. export default class QualityProfilesServiceMock {
  78. isAdmin = false;
  79. listQualityProfile: Profile[] = [];
  80. languageMapping: Dict<Partial<Profile>> = {
  81. c: { language: 'c', languageName: 'C' },
  82. php: { language: 'php', languageName: 'PHP' },
  83. java: { language: 'java', languageName: 'Java' },
  84. };
  85. comparisonResult: CompareResponse = mockCompareResult();
  86. searchRulesResponse: SearchRulesResponse = {
  87. rules: [],
  88. paging: mockPaging(),
  89. };
  90. profileProjects: {
  91. [profileKey: string]: ProfileProject[];
  92. } = {};
  93. changelogEvents: ProfileChangelogEvent[] = [];
  94. constructor() {
  95. this.resetQualityProfile();
  96. this.resetComparisonResult();
  97. this.resetChangelogEvents();
  98. jest.mocked(searchQualityProfiles).mockImplementation(this.handleSearchQualityProfiles);
  99. jest.mocked(createQualityProfile).mockImplementation(this.handleCreateQualityProfile);
  100. jest.mocked(changeProfileParent).mockImplementation(this.handleChangeProfileParent);
  101. jest.mocked(getProfileInheritance).mockImplementation(this.handleGetProfileInheritance);
  102. jest.mocked(getProfileProjects).mockImplementation(this.handleGetProfileProjects);
  103. jest.mocked(copyProfile).mockImplementation(this.handleCopyProfile);
  104. jest.mocked(getImporters).mockImplementation(this.handleGetImporters);
  105. jest.mocked(searchRules).mockImplementation(this.handleSearchRules);
  106. jest.mocked(listRules).mockImplementation(this.handleListRules);
  107. jest.mocked(compareProfiles).mockImplementation(this.handleCompareQualityProfiles);
  108. jest.mocked(activateRule).mockImplementation(this.handleActivateRule);
  109. jest.mocked(deactivateRule).mockImplementation(this.handleDeactivateRule);
  110. jest.mocked(getRuleDetails).mockImplementation(this.handleGetRuleDetails);
  111. jest.mocked(restoreQualityProfile).mockImplementation(this.handleRestoreQualityProfile);
  112. jest.mocked(searchUsers).mockImplementation(this.handleSearchUsers);
  113. jest.mocked(addUser).mockImplementation(this.handleAddUser);
  114. jest.mocked(searchGroups).mockImplementation(this.handleSearchGroups);
  115. jest.mocked(addGroup).mockImplementation(this.handleAddGroup);
  116. jest.mocked(removeGroup).mockImplementation(this.handleRemoveGroup);
  117. jest.mocked(removeUser).mockImplementation(this.handleRemoveUser);
  118. jest.mocked(associateProject).mockImplementation(this.handleAssociateProject);
  119. jest.mocked(getProfileChangelog).mockImplementation(this.handleGetProfileChangelog);
  120. jest.mocked(dissociateProject).mockImplementation(this.handleDissociateProject);
  121. jest.mocked(getQualityProfile).mockImplementation(this.handleGetQualityProfile);
  122. jest.mocked(getExporters).mockImplementation(this.handleGetExporters);
  123. jest.mocked(deleteProfile).mockImplementation(this.handleDeleteProfile);
  124. jest.mocked(renameProfile).mockImplementation(this.handleRenameProfile);
  125. jest.mocked(setDefaultProfile).mockImplementation(this.handleSetDefaultProfile);
  126. jest
  127. .mocked(getQualityProfileExporterUrl)
  128. .mockImplementation(() => '/api/qualityprofiles/export');
  129. }
  130. resetQualityProfile() {
  131. this.listQualityProfile = [
  132. mockQualityProfile({
  133. key: 'c-qp',
  134. language: 'c',
  135. languageName: 'C',
  136. name: 'c quality profile',
  137. activeDeprecatedRuleCount: 0,
  138. }),
  139. mockQualityProfile({
  140. key: 'java-qp',
  141. language: 'java',
  142. languageName: 'Java',
  143. name: 'java quality profile',
  144. activeDeprecatedRuleCount: 0,
  145. }),
  146. mockQualityProfile({
  147. key: 'java-qp-1',
  148. language: 'java',
  149. languageName: 'Java',
  150. name: 'java quality profile #2',
  151. activeDeprecatedRuleCount: 1,
  152. actions: {
  153. edit: this.isAdmin,
  154. },
  155. }),
  156. mockQualityProfile({
  157. key: 'sonar',
  158. language: 'java',
  159. languageName: 'Java',
  160. name: 'Sonar way',
  161. isBuiltIn: true,
  162. isDefault: true,
  163. }),
  164. mockQualityProfile({
  165. key: 'old-php-qp',
  166. language: 'php',
  167. languageName: 'PHP',
  168. name: 'Good old PHP quality profile',
  169. activeDeprecatedRuleCount: 8,
  170. rulesUpdatedAt: '2019-09-16T21:10:36+0000',
  171. parentKey: 'php-sonar-way-1',
  172. actions: {
  173. edit: this.isAdmin,
  174. associateProjects: this.isAdmin,
  175. delete: this.isAdmin,
  176. setAsDefault: this.isAdmin,
  177. copy: this.isAdmin,
  178. },
  179. }),
  180. mockQualityProfile({
  181. key: 'no-rule-qp',
  182. activeRuleCount: 0,
  183. actions: {
  184. associateProjects: true,
  185. setAsDefault: this.isAdmin,
  186. },
  187. }),
  188. mockQualityProfile({
  189. activeRuleCount: 6,
  190. isBuiltIn: true,
  191. key: 'php-sonar-way-1',
  192. name: 'PHP Sonar way 1',
  193. language: 'php',
  194. languageName: 'PHP',
  195. }),
  196. mockQualityProfile({
  197. activeRuleCount: 6,
  198. isBuiltIn: false,
  199. key: 'php-sonar-way-2',
  200. name: 'PHP Sonar way 2',
  201. language: 'php',
  202. languageName: 'PHP',
  203. }),
  204. mockQualityProfile({
  205. activeRuleCount: 3,
  206. isBuiltIn: false,
  207. key: 'php-sonar-way',
  208. parentKey: 'old-php-qp',
  209. name: 'PHP way',
  210. language: 'php',
  211. languageName: 'PHP',
  212. }),
  213. ];
  214. }
  215. resetComparisonResult() {
  216. this.comparisonResult = mockCompareResult();
  217. }
  218. resetChangelogEvents() {
  219. this.changelogEvents = [
  220. mockQualityProfileChangelogEvent({
  221. date: '2019-05-23T04:12:32+0100',
  222. }),
  223. mockQualityProfileChangelogEvent({
  224. date: '2019-05-23T03:12:32+0100',
  225. action: 'DEACTIVATED',
  226. ruleKey: 'php:rule1',
  227. ruleName: 'PHP Rule',
  228. params: {
  229. severity: IssueSeverity.Critical,
  230. newCleanCodeAttribute: CleanCodeAttribute.Complete,
  231. newCleanCodeAttributeCategory: CleanCodeAttributeCategory.Intentional,
  232. oldCleanCodeAttribute: CleanCodeAttribute.Clear,
  233. oldCleanCodeAttributeCategory: CleanCodeAttributeCategory.Responsible,
  234. },
  235. }),
  236. mockQualityProfileChangelogEvent({
  237. date: '2019-05-23T03:12:32+0100',
  238. action: 'ACTIVATED',
  239. ruleKey: 'c:rule0',
  240. ruleName: 'Rule 0',
  241. params: {},
  242. }),
  243. mockQualityProfileChangelogEvent({
  244. date: '2019-04-23T03:12:32+0100',
  245. action: 'DEACTIVATED',
  246. ruleKey: 'c:rule0',
  247. ruleName: 'Rule 0',
  248. }),
  249. mockQualityProfileChangelogEvent({
  250. date: '2019-04-23T03:12:32+0100',
  251. action: 'DEACTIVATED',
  252. ruleKey: 'c:rule1',
  253. ruleName: 'Rule 1',
  254. params: {
  255. severity: IssueSeverity.Critical,
  256. newCleanCodeAttribute: CleanCodeAttribute.Complete,
  257. newCleanCodeAttributeCategory: CleanCodeAttributeCategory.Intentional,
  258. oldCleanCodeAttribute: CleanCodeAttribute.Lawful,
  259. oldCleanCodeAttributeCategory: CleanCodeAttributeCategory.Responsible,
  260. impactChanges: [
  261. {
  262. newSeverity: SoftwareImpactSeverity.Medium,
  263. newSoftwareQuality: SoftwareQuality.Reliability,
  264. },
  265. {
  266. oldSeverity: SoftwareImpactSeverity.High,
  267. oldSoftwareQuality: SoftwareQuality.Maintainability,
  268. },
  269. ],
  270. },
  271. }),
  272. mockQualityProfileChangelogEvent({
  273. date: '2019-04-23T02:12:32+0100',
  274. action: 'DEACTIVATED',
  275. ruleKey: 'c:rule2',
  276. ruleName: 'Rule 2',
  277. authorName: 'John Doe',
  278. }),
  279. mockQualityProfileChangelogEvent({
  280. date: '2019-03-23T02:12:32+0100',
  281. ruleKey: 'c:rule2',
  282. ruleName: 'Rule 2',
  283. authorName: 'John Doe',
  284. params: {
  285. severity: IssueSeverity.Critical,
  286. credentialWords: 'foo,bar',
  287. },
  288. }),
  289. ];
  290. }
  291. resetSearchRulesResponse() {
  292. this.searchRulesResponse = {
  293. facets: [
  294. {
  295. property: 'types',
  296. values: [
  297. { val: 'CODE_SMELL', count: 250 },
  298. { val: 'BUG', count: 60 },
  299. { val: 'VULNERABILITY', count: 40 },
  300. { val: 'SECURITY_HOTSPOT', count: 50 },
  301. ],
  302. },
  303. ],
  304. rules: [],
  305. paging: {
  306. pageIndex: 1,
  307. pageSize: 400,
  308. total: 400,
  309. },
  310. };
  311. }
  312. resetProfileProjects() {
  313. this.profileProjects = {
  314. 'old-php-qp': [
  315. {
  316. key: 'Benflix',
  317. name: 'Benflix',
  318. selected: true,
  319. },
  320. {
  321. key: 'Twitter',
  322. name: 'Twitter',
  323. selected: false,
  324. },
  325. ],
  326. };
  327. }
  328. handleGetImporters = () => {
  329. return this.reply([
  330. {
  331. key: 'sonar-importer-a',
  332. name: 'Importer A',
  333. languages: ['c'],
  334. },
  335. {
  336. key: 'sonar-importer-b',
  337. name: 'Importer B',
  338. languages: ['c'],
  339. },
  340. ]);
  341. };
  342. handleCopyProfile = (fromKey: string, name: string): Promise<Profile> => {
  343. const profile = this.listQualityProfile.find((p) => p.key === fromKey);
  344. if (!profile) {
  345. return Promise.reject({
  346. errors: [{ msg: `No profile has been found for ${fromKey}` }],
  347. });
  348. }
  349. const copiedQualityProfile = mockQualityProfile({
  350. ...profile,
  351. name,
  352. key: `qp${this.listQualityProfile.length}`,
  353. });
  354. this.listQualityProfile.push(copiedQualityProfile);
  355. return this.reply(copiedQualityProfile);
  356. };
  357. handleGetProfileProjects = (
  358. data: RequestData,
  359. ): Promise<{
  360. more: boolean;
  361. paging: Paging;
  362. results: ProfileProject[];
  363. }> => {
  364. const results = (this.profileProjects[data.key] ?? []).filter(
  365. (project) =>
  366. project.selected ===
  367. (data.selected !== undefined ? Boolean(data.selected === 'selected') : true),
  368. );
  369. return this.reply({
  370. more: false,
  371. paging: {
  372. pageIndex: 0,
  373. pageSize: 10,
  374. total: 0,
  375. },
  376. results,
  377. });
  378. };
  379. handleGetProfileInheritance = ({
  380. language,
  381. name,
  382. }: Profile): Promise<{
  383. ancestors: ProfileInheritanceDetails[];
  384. children: ProfileInheritanceDetails[];
  385. profile: ProfileInheritanceDetails;
  386. }> => {
  387. const profileToProfileInheritanceDetails = (profile: Profile): ProfileInheritanceDetails => ({
  388. ...profile,
  389. inactiveRuleCount: 3,
  390. isBuiltIn: false,
  391. });
  392. const profile = this.listQualityProfile.find((p) => p.name === name && p.language === language);
  393. if (!profile) {
  394. return Promise.reject({
  395. errors: [{ msg: `No profile has been found for ${language} ${name}` }],
  396. });
  397. }
  398. const ancestors = this.listQualityProfile
  399. .filter((p) => p.key === profile.parentKey)
  400. .map(profileToProfileInheritanceDetails);
  401. const children = this.listQualityProfile
  402. .filter((p) => p.parentKey === profile.key)
  403. .map(profileToProfileInheritanceDetails);
  404. return this.reply({
  405. ancestors,
  406. children,
  407. profile: profileToProfileInheritanceDetails(profile),
  408. });
  409. };
  410. handleChangeProfileParent = ({ language, name }: Profile, parentProfile?: Profile) => {
  411. const profile = this.listQualityProfile.find((p) => p.name === name && p.language === language);
  412. if (!profile) {
  413. return Promise.reject({
  414. errors: [{ msg: `No profile has been found for ${language} ${name}` }],
  415. });
  416. }
  417. profile.parentKey = parentProfile?.key;
  418. profile.parentName = parentProfile?.name;
  419. return Promise.resolve({});
  420. };
  421. handleCreateQualityProfile = (data: RequestData | FormData) => {
  422. if (data instanceof FormData) {
  423. const name = data.get('name') as string;
  424. const language = data.get('language') as string;
  425. const newQualityProfile = mockQualityProfile({
  426. name,
  427. ...this.languageMapping[language],
  428. key: `qp${this.listQualityProfile.length}`,
  429. });
  430. this.listQualityProfile.push(newQualityProfile);
  431. return this.reply({ profile: newQualityProfile });
  432. }
  433. const newQualityProfile = mockQualityProfile({
  434. name: data.name,
  435. ...this.languageMapping[data.language],
  436. key: `qp${this.listQualityProfile.length}`,
  437. });
  438. this.listQualityProfile.push(newQualityProfile);
  439. return this.reply({ profile: newQualityProfile });
  440. };
  441. handleSearchRules = (data: SearchRulesQuery): Promise<SearchRulesResponse> => {
  442. // Special case when we want rule breakdown
  443. if (data.facets === 'cleanCodeAttributeCategories,impactSoftwareQualities') {
  444. const activation = data.activation === 'true';
  445. return this.reply({
  446. facets: [
  447. {
  448. property: 'cleanCodeAttributeCategories',
  449. values: [
  450. {
  451. val: CleanCodeAttributeCategory.Intentional,
  452. count: activation ? 23 : 27,
  453. },
  454. {
  455. val: CleanCodeAttributeCategory.Consistent,
  456. count: activation ? 2 : 20,
  457. },
  458. {
  459. val: CleanCodeAttributeCategory.Adaptable,
  460. count: activation ? 1 : 12,
  461. },
  462. {
  463. val: CleanCodeAttributeCategory.Responsible,
  464. count: 0,
  465. },
  466. ],
  467. },
  468. {
  469. property: 'impactSoftwareQualities',
  470. values: [
  471. {
  472. val: SoftwareQuality.Maintainability,
  473. count: activation ? 9 : 53,
  474. },
  475. {
  476. val: SoftwareQuality.Reliability,
  477. count: activation ? 16 : 17,
  478. },
  479. {
  480. val: SoftwareQuality.Security,
  481. count: activation ? 0 : 14,
  482. },
  483. ],
  484. },
  485. ],
  486. rules: [],
  487. paging: mockPaging(),
  488. });
  489. }
  490. return this.reply(this.searchRulesResponse);
  491. };
  492. handleListRules = (data: SearchRulesQuery): Promise<SearchRulesResponse> => {
  493. // Both APIs are mocked the same way, this method is only here to make it explicit.
  494. return this.handleSearchRules(data);
  495. };
  496. handleGetQualityProfile = () => {
  497. return this.reply({
  498. profile: mockQualityProfile(),
  499. compareToSonarWay: {
  500. profile: '',
  501. profileName: 'Sonar way',
  502. missingRuleCount: 29,
  503. },
  504. });
  505. };
  506. handleSearchQualityProfiles = (
  507. parameters: SearchQualityProfilesParameters = {},
  508. ): Promise<SearchQualityProfilesResponse> => {
  509. const { language } = parameters;
  510. let profiles = this.listQualityProfile;
  511. if (language) {
  512. profiles = profiles.filter((p) => p.language === language);
  513. }
  514. if (this.isAdmin) {
  515. profiles = profiles.map((p) => ({ ...p, actions: { ...p.actions, copy: true } }));
  516. }
  517. return this.reply({
  518. actions: { create: this.isAdmin },
  519. profiles,
  520. });
  521. };
  522. handleActivateRule = (data: {
  523. key: string;
  524. params?: Dict<string>;
  525. reset?: boolean;
  526. rule: string;
  527. severity?: string;
  528. }): Promise<undefined> => {
  529. this.comparisonResult.inRight = this.comparisonResult.inRight.filter(
  530. ({ key }) => key !== data.rule,
  531. );
  532. return this.reply(undefined);
  533. };
  534. handleDeactivateRule = (data: { key: string; rule: string }) => {
  535. this.comparisonResult.inLeft = this.comparisonResult.inLeft.filter(
  536. ({ key }) => key !== data.rule,
  537. );
  538. return this.reply(undefined);
  539. };
  540. handleCompareQualityProfiles = (leftKey: string, rightKey: string): Promise<CompareResponse> => {
  541. const comparedProfiles = this.listQualityProfile.reduce((profiles, profile) => {
  542. if (profile.key === leftKey || profile.key === rightKey) {
  543. profiles.push(profile);
  544. }
  545. return profiles;
  546. }, [] as Profile[]);
  547. const [leftName, rightName] = comparedProfiles.map((profile) => profile.name);
  548. this.comparisonResult.left = { name: leftName };
  549. this.comparisonResult.right = { name: rightName };
  550. return this.reply(this.comparisonResult);
  551. };
  552. handleGetRuleDetails = (params: { key: string }): Promise<{ rule: RuleDetails }> => {
  553. return this.reply({
  554. rule: mockRuleDetails({
  555. key: params.key,
  556. }),
  557. });
  558. };
  559. handleRestoreQualityProfile = () => {
  560. return this.reply({
  561. profile: {
  562. key: 'c-sonarsource-06756',
  563. name: 'SonarSource',
  564. language: 'c',
  565. isDefault: false,
  566. isInherited: false,
  567. languageName: 'C',
  568. },
  569. ruleSuccesses: 231,
  570. ruleFailures: 0,
  571. });
  572. };
  573. handleSearchUsers = () => {
  574. return this.reply({
  575. users: [
  576. mockUserSelected({
  577. login: 'buzz',
  578. name: 'Buzz',
  579. }),
  580. ],
  581. paging: mockPaging(),
  582. });
  583. };
  584. handleAddUser = () => {
  585. return this.reply(undefined);
  586. };
  587. handleRemoveUser = () => {
  588. return this.reply(undefined);
  589. };
  590. handleSearchGroups = () => {
  591. return this.reply({
  592. groups: [mockGroup({ name: 'ACDC' })],
  593. paging: mockPaging(),
  594. });
  595. };
  596. handleAddGroup = () => {
  597. return this.reply(undefined);
  598. };
  599. handleRemoveGroup = () => {
  600. return this.reply(undefined);
  601. };
  602. handleAssociateProject = ({ key }: Profile, project: string) => {
  603. const projects = this.profileProjects[key].map((profileProject) => {
  604. if (profileProject.key === project) {
  605. return {
  606. ...profileProject,
  607. selected: true,
  608. };
  609. }
  610. return profileProject;
  611. });
  612. this.profileProjects[key] = projects;
  613. return this.reply({});
  614. };
  615. handleDissociateProject = ({ key }: Profile, project: string) => {
  616. const projects = this.profileProjects[key].map((profileProject) => {
  617. if (profileProject.key === project) {
  618. return {
  619. ...profileProject,
  620. selected: false,
  621. };
  622. }
  623. return profileProject;
  624. });
  625. this.profileProjects[key] = projects;
  626. return this.reply({});
  627. };
  628. handleGetProfileChangelog: typeof getProfileChangelog = (since, to, { language }, page) => {
  629. const PAGE_SIZE = 50;
  630. const p = page || 1;
  631. const events = this.changelogEvents.filter((event) => {
  632. if (event.ruleKey.split(':')[0] !== language) {
  633. return false;
  634. }
  635. if (since && new Date(since) >= new Date(event.date)) {
  636. return false;
  637. }
  638. if (to && new Date(to) <= new Date(event.date)) {
  639. return false;
  640. }
  641. return true;
  642. });
  643. return this.reply({
  644. events: events.slice((p - 1) * PAGE_SIZE, (p - 1) * PAGE_SIZE + PAGE_SIZE),
  645. paging: mockPaging({
  646. total: events.length,
  647. pageSize: PAGE_SIZE,
  648. pageIndex: p,
  649. }),
  650. });
  651. };
  652. handleGetExporters = () => {
  653. return this.reply([
  654. {
  655. key: 'sonarlint-vs',
  656. name: 'SonarLint for Visual Studio',
  657. languages: ['php'],
  658. },
  659. {
  660. key: 'sonarlint-eclipse',
  661. name: 'SonarLint for Eclipse',
  662. languages: ['php'],
  663. },
  664. ]);
  665. };
  666. handleDeleteProfile = ({ name }: Profile) => {
  667. // delete Children
  668. const qualityProfileToDelete = this.listQualityProfile.find((profile) => profile.name === name);
  669. this.listQualityProfile = this.listQualityProfile.filter(
  670. (profile) => profile.parentKey !== qualityProfileToDelete?.key,
  671. );
  672. // delete profile
  673. this.listQualityProfile = this.listQualityProfile.filter((profile) => profile.name !== name);
  674. return this.reply({});
  675. };
  676. handleRenameProfile = (key: string, newName: string) => {
  677. this.listQualityProfile = this.listQualityProfile.map((profile) => {
  678. if (profile.key === key) {
  679. return {
  680. ...profile,
  681. name: newName,
  682. };
  683. }
  684. return profile;
  685. });
  686. return this.reply({});
  687. };
  688. handleSetDefaultProfile = ({ name }: Profile) => {
  689. this.listQualityProfile = this.listQualityProfile.map((profile) => {
  690. if (profile.name === name) {
  691. return {
  692. ...profile,
  693. isDefault: true,
  694. };
  695. }
  696. return profile;
  697. });
  698. return Promise.resolve();
  699. };
  700. setAdmin() {
  701. this.isAdmin = true;
  702. this.resetQualityProfile();
  703. }
  704. setRulesSearchResponse(overrides: Partial<SearchRulesResponse>) {
  705. this.searchRulesResponse = {
  706. ...this.searchRulesResponse,
  707. ...overrides,
  708. };
  709. }
  710. reset() {
  711. this.isAdmin = false;
  712. this.resetQualityProfile();
  713. this.resetComparisonResult();
  714. this.resetSearchRulesResponse();
  715. this.resetProfileProjects();
  716. this.resetChangelogEvents();
  717. }
  718. reply<T>(response: T): Promise<T> {
  719. return Promise.resolve(cloneDeep(response));
  720. }
  721. }