aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-web/src/main')
-rw-r--r--server/sonar-web/src/main/js/api/mocks/QualityGatesServiceMock.ts294
-rw-r--r--server/sonar-web/src/main/js/api/quality-gates.ts2
-rw-r--r--server/sonar-web/src/main/js/apps/projectQualityGate/__tests__/__snapshots__/ProjectQualityGateApp-test.tsx.snap12
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/Condition.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/ConditionModal.tsx27
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/ConditionOperator.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx35
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/CopyQualityGateForm.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/Details.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/List.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/RenameQualityGateForm.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/ThresholdInput.tsx20
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/App-it.tsx274
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/BuiltInQualityGateBadge-test.tsx30
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Condition-test.tsx57
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ConditionModal-test.tsx102
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ConditionOperator-test.tsx37
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Conditions-test.tsx80
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/CopyQualityGateForm-test.tsx68
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/CreateQualityGateForm-test.tsx71
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/DeleteQualityGateForm-test.tsx57
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Details-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/List-test.tsx39
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ListHeader-test.tsx33
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/MetricSelect-test.tsx47
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/RenameQualityGateForm-test.tsx64
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ThresholdInput-test.tsx74
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/BuiltInQualityGateBadge-test.tsx.snap13
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/Condition-test.tsx.snap203
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ConditionModal-test.tsx.snap364
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ConditionOperator-test.tsx.snap26
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/Conditions-test.tsx.snap660
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/CopyQualityGateForm-test.tsx.snap36
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/CreateQualityGateForm-test.tsx.snap36
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DeleteQualityGateForm-test.tsx.snap13
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsContent-test.tsx.snap10
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsHeader-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/List-test.tsx.snap73
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ListHeader-test.tsx.snap40
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/MetricSelect-test.tsx.snap19
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/RenameQualityGateForm-test.tsx.snap36
-rw-r--r--server/sonar-web/src/main/js/components/controls/Tooltip.tsx8
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/Tooltip-test.tsx10
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ActionsDropdown-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Tooltip-test.tsx.snap8
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/buttons-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/controls/buttons.tsx1
-rw-r--r--server/sonar-web/src/main/js/helpers/testMocks.ts2
-rw-r--r--server/sonar-web/src/main/js/types/types.ts2
50 files changed, 651 insertions, 2370 deletions
diff --git a/server/sonar-web/src/main/js/api/mocks/QualityGatesServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/QualityGatesServiceMock.ts
new file mode 100644
index 00000000000..0ca29ed0d63
--- /dev/null
+++ b/server/sonar-web/src/main/js/api/mocks/QualityGatesServiceMock.ts
@@ -0,0 +1,294 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { cloneDeep, flatten, omit, remove } from 'lodash';
+import { mockQualityGate } from '../../helpers/mocks/quality-gates';
+import { Condition, QualityGate } from '../../types/types';
+import {
+ copyQualityGate,
+ createCondition,
+ createQualityGate,
+ deleteCondition,
+ deleteQualityGate,
+ fetchQualityGate,
+ fetchQualityGates,
+ renameQualityGate,
+ searchGroups,
+ searchProjects,
+ searchUsers,
+ updateCondition
+} from '../quality-gates';
+
+export class QualityGatesServiceMock {
+ isAdmin = false;
+ readOnlyList: QualityGate[];
+ list: QualityGate[];
+
+ constructor(list?: QualityGate[], defaultId = 'AWBWEMe2qGAMGEYPjJlm') {
+ this.readOnlyList = list || [
+ mockQualityGate({
+ id: defaultId,
+ name: 'SonarSource way',
+ conditions: [
+ { id: 'AXJMbIUGPAOIsUIE3eNC', metric: 'new_coverage', op: 'LT', error: '85' },
+ { id: 'AXJMbIUGPAOIsUIE3eNE', metric: 'reliability_rating', op: 'GT', error: '4' },
+ { id: 'AXJMbIUGPAOIsUIE3eND', metric: 'security_rating', op: 'GT', error: '4' },
+ {
+ id: 'AXJMbIUGPAOIsUIE3eNT',
+ metric: 'new_maintainability_rating',
+ op: 'GT',
+ error: '1'
+ },
+ { id: 'AXJMbIUGPAOIsUIE3eNU', metric: 'new_reliability_rating', op: 'GT', error: '1' },
+ { id: 'AXJMbIUGPAOIsUIE3eNV', metric: 'new_security_rating', op: 'GT', error: '1' },
+ {
+ id: 'AXJMbIUHPAOIsUIE3eNc',
+ metric: 'new_duplicated_lines_density',
+ op: 'GT',
+ error: '3'
+ },
+ {
+ id: 'AXJMbIUHPAOIsUIE3eOi',
+ metric: 'new_security_hotspots_reviewed',
+ op: 'LT',
+ error: '100'
+ }
+ ],
+ isDefault: true,
+ isBuiltIn: false
+ }),
+ mockQualityGate({
+ id: 'AXGYZrDqC-YjVCvvbRDY',
+ name: 'SonarSource way - CFamily',
+ conditions: [
+ { id: 'AXJMbIUHPAOIsUIE3eOu', metric: 'new_coverage', op: 'LT', error: '0' },
+ { id: 'AXJMbIUHPAOIsUIE3eOubis', metric: 'new_coverage', op: 'LT', error: '1' },
+ { id: 'deprecated', metric: 'function_complexity', op: 'LT', error: '1' }
+ ],
+ isDefault: false,
+ isBuiltIn: false
+ }),
+ mockQualityGate({
+ id: 'AWBWEMe4qGAMGEYPjJlr',
+ name: 'Sonar way',
+ conditions: [
+ { id: 'AXJMbIUHPAOIsUIE3eNs', metric: 'new_security_rating', op: 'GT', error: '1' },
+ { id: 'AXJMbIUHPAOIsUIE3eOD', metric: 'new_reliability_rating', op: 'GT', error: '1' },
+ {
+ id: 'AXJMbIUHPAOIsUIE3eOE',
+ metric: 'new_maintainability_rating',
+ op: 'GT',
+ error: '1'
+ },
+ { id: 'AXJMbIUHPAOIsUIE3eOF', metric: 'new_coverage', op: 'LT', error: '80' },
+ {
+ id: 'AXJMbIUHPAOIsUIE3eOG',
+ metric: 'new_duplicated_lines_density',
+ op: 'GT',
+ error: '3'
+ },
+ {
+ id: 'AXJMbIUHPAOIsUIE3eOk',
+ metric: 'new_security_hotspots_reviewed',
+ op: 'LT',
+ error: '100'
+ }
+ ],
+ isDefault: false,
+ isBuiltIn: true
+ })
+ ];
+
+ this.list = cloneDeep(this.readOnlyList);
+
+ (fetchQualityGate as jest.Mock).mockImplementation(this.showHandler);
+ (fetchQualityGates as jest.Mock).mockImplementation(this.listHandler);
+ (createQualityGate as jest.Mock).mockImplementation(this.createHandler);
+ (deleteQualityGate as jest.Mock).mockImplementation(this.destroyHandler);
+ (copyQualityGate as jest.Mock).mockImplementation(this.copyHandler);
+ (renameQualityGate as jest.Mock).mockImplementation(this.renameHandler);
+ (createCondition as jest.Mock).mockImplementation(this.createConditionHandler);
+ (updateCondition as jest.Mock).mockImplementation(this.updateConditionHandler);
+ (deleteCondition as jest.Mock).mockImplementation(this.deleteConditionHandler);
+
+ // To be implemented.
+ (searchUsers as jest.Mock).mockResolvedValue({ users: [] });
+ (searchGroups as jest.Mock).mockResolvedValue({ groups: [] });
+ (searchProjects as jest.Mock).mockResolvedValue({
+ paging: {
+ pageIndex: 1,
+ pageSize: 100,
+ total: 0
+ },
+ results: []
+ });
+ }
+
+ getCorruptedQualityGateName() {
+ return 'SonarSource way - CFamily';
+ }
+
+ reset() {
+ this.setIsAdmin(false);
+ this.list = cloneDeep(this.readOnlyList);
+ }
+
+ getDefaultQualityGate() {
+ return this.list.find(q => q.isDefault) || mockQualityGate({ isDefault: true });
+ }
+
+ getBuiltInQualityGate() {
+ return this.list.find(q => q.isBuiltIn) || mockQualityGate({ isBuiltIn: true });
+ }
+
+ setIsAdmin(isAdmin: boolean) {
+ this.isAdmin = isAdmin;
+ }
+
+ computeActions(q: QualityGate) {
+ return {
+ rename: q.isBuiltIn ? false : this.isAdmin,
+ setAsDefault: q.isDefault ? false : this.isAdmin,
+ copy: this.isAdmin,
+ associateProjects: this.isAdmin,
+ delete: q.isBuiltIn ? false : this.isAdmin,
+ manageConditions: this.isAdmin,
+ delegate: this.isAdmin
+ };
+ }
+
+ listHandler = () => {
+ return this.reply({
+ qualitygates: this.list
+ .map(q => omit(q, 'conditions'))
+ .map(q => ({
+ ...q,
+ actions: this.computeActions(q)
+ })),
+ default: this.getDefaultQualityGate().id,
+ actions: { create: this.isAdmin }
+ });
+ };
+
+ showHandler = ({ id }: { id: string }) => {
+ const qualityGate = omit(
+ this.list.find(q => q.id === id),
+ 'isDefault'
+ );
+ return this.reply({ ...qualityGate, actions: this.computeActions(qualityGate) });
+ };
+
+ createHandler = ({ name }: { name: string }) => {
+ const newId = `newId${this.list.length}`;
+ this.list.push(
+ mockQualityGate({
+ id: newId,
+ name,
+ conditions: [],
+ isDefault: false,
+ isBuiltIn: false
+ })
+ );
+ return this.reply({
+ id: newId,
+ name
+ });
+ };
+
+ destroyHandler = ({ id }: { id: string }) => {
+ this.list = this.list.filter(q => q.id !== id);
+ return Promise.resolve();
+ };
+
+ copyHandler = ({ id, name }: { id: string; name: string }) => {
+ const newQG = cloneDeep(this.list.find(q => q.id === id));
+ if (newQG === undefined) {
+ return Promise.reject({ errors: [{ msg: `No quality gate has been found for id ${id}` }] });
+ }
+ newQG.name = name;
+ newQG.id = `newId${this.list.length}`;
+
+ newQG.isDefault = false;
+ newQG.isBuiltIn = false;
+
+ this.list.push(newQG);
+
+ return this.reply({
+ id: newQG.id,
+ name
+ });
+ };
+
+ renameHandler = ({ id, name }: { id: string; name: string }) => {
+ const renameQG = this.list.find(q => q.id === id);
+ if (renameQG === undefined) {
+ return Promise.reject({ errors: [{ msg: `No quality gate has been found for id ${id}` }] });
+ }
+ renameQG.name = name;
+ return this.reply({
+ id: renameQG.id,
+ name
+ });
+ };
+
+ createConditionHandler = (
+ data: {
+ gateId: string;
+ } & Omit<Condition, 'id'>
+ ) => {
+ const { metric, gateId, op, error } = data;
+ const qg = this.list.find(q => q.id === gateId);
+ if (qg === undefined) {
+ return Promise.reject({
+ errors: [{ msg: `No quality gate has been found for id ${gateId}` }]
+ });
+ }
+
+ const conditions = qg.conditions || [];
+ const id = `condId${qg.id}${conditions.length}`;
+ const newCondition = { id, metric, op, error };
+ conditions.push(newCondition);
+ qg.conditions = conditions;
+ return this.reply(newCondition);
+ };
+
+ updateConditionHandler = ({ id, metric, op, error }: Condition) => {
+ const condition = flatten(this.list.map(q => q.conditions || [])).find(q => q.id === id);
+ if (condition === undefined) {
+ return Promise.reject({ errors: [{ msg: `No condition has been found for id ${id}` }] });
+ }
+
+ condition.metric = metric;
+ condition.op = op;
+ condition.error = error;
+
+ return this.reply(condition);
+ };
+
+ deleteConditionHandler = ({ id }: { id: string }) => {
+ this.list.forEach(q => {
+ remove(q.conditions || [], c => c.id === id);
+ });
+ return Promise.resolve();
+ };
+
+ reply<T>(response: T): Promise<T> {
+ return Promise.resolve(cloneDeep(response));
+ }
+}
diff --git a/server/sonar-web/src/main/js/api/quality-gates.ts b/server/sonar-web/src/main/js/api/quality-gates.ts
index af4bad890cb..cc3d83be8e0 100644
--- a/server/sonar-web/src/main/js/api/quality-gates.ts
+++ b/server/sonar-web/src/main/js/api/quality-gates.ts
@@ -73,7 +73,7 @@ export function updateCondition(data: Condition): Promise<Condition> {
return postJSON('/api/qualitygates/update_condition', data).catch(throwGlobalError);
}
-export function deleteCondition(data: { id: number }): Promise<void> {
+export function deleteCondition(data: { id: string }): Promise<void> {
return post('/api/qualitygates/delete_condition', data);
}
diff --git a/server/sonar-web/src/main/js/apps/projectQualityGate/__tests__/__snapshots__/ProjectQualityGateApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectQualityGate/__tests__/__snapshots__/ProjectQualityGateApp-test.tsx.snap
index 03331f67dba..13d8d45338b 100644
--- a/server/sonar-web/src/main/js/apps/projectQualityGate/__tests__/__snapshots__/ProjectQualityGateApp-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projectQualityGate/__tests__/__snapshots__/ProjectQualityGateApp-test.tsx.snap
@@ -8,13 +8,13 @@ exports[`renders correctly 1`] = `
"conditions": Array [
Object {
"error": "10",
- "id": 1,
+ "id": "1",
"metric": "coverage",
"op": "LT",
},
Object {
"error": "10",
- "id": 1,
+ "id": "1",
"metric": "new_bugs",
"op": "LT",
},
@@ -26,13 +26,13 @@ exports[`renders correctly 1`] = `
"conditions": Array [
Object {
"error": "10",
- "id": 1,
+ "id": "1",
"metric": "coverage",
"op": "LT",
},
Object {
"error": "10",
- "id": 1,
+ "id": "1",
"metric": "new_bugs",
"op": "LT",
},
@@ -45,13 +45,13 @@ exports[`renders correctly 1`] = `
"conditions": Array [
Object {
"error": "10",
- "id": 1,
+ "id": "1",
"metric": "coverage",
"op": "LT",
},
Object {
"error": "10",
- "id": 1,
+ "id": "1",
"metric": "new_bugs",
"op": "LT",
},
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/Condition.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/Condition.tsx
index 4f1b4203e15..97d3834a76b 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/Condition.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Condition.tsx
@@ -108,12 +108,14 @@ export class ConditionComponent extends React.PureComponent<Props, State> {
<>
<td className="text-center thin">
<EditButton
+ aria-label={translateWithParameters('quality_gates.condition.edit', metric.name)}
data-test="quality-gates__condition-update"
onClick={this.handleOpenUpdate}
/>
</td>
<td className="text-center thin">
<DeleteButton
+ aria-label={translateWithParameters('quality_gates.condition.delete', metric.name)}
data-test="quality-gates__condition-delete"
onClick={this.handleDeleteClick}
/>
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionModal.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionModal.tsx
index 03e45767dec..be2a2129959 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionModal.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionModal.tsx
@@ -65,19 +65,16 @@ export default class ConditionModal extends React.PureComponent<Props, State> {
}
handleFormSubmit = () => {
- if (this.state.metric) {
- const { condition, qualityGate } = this.props;
- const newCondition: Omit<Condition, 'id'> = {
- metric: this.state.metric.key,
- op: this.getSinglePossibleOperator(this.state.metric) || this.state.op,
- error: this.state.error
- };
- const submitPromise = condition
- ? updateCondition({ id: condition.id, ...newCondition })
- : createCondition({ gateId: qualityGate.id, ...newCondition });
- return submitPromise.then(this.props.onAddCondition);
- }
- return Promise.reject();
+ const { condition, qualityGate } = this.props;
+ const newCondition: Omit<Condition, 'id'> = {
+ metric: this.state.metric!.key,
+ op: this.getSinglePossibleOperator(this.state.metric!) || this.state.op,
+ error: this.state.error
+ };
+ const submitPromise = condition
+ ? updateCondition({ id: condition.id, ...newCondition })
+ : createCondition({ gateId: qualityGate.id, ...newCondition });
+ return submitPromise.then(this.props.onAddCondition);
};
handleScopeChange = (scope: 'new' | 'overall') => {
@@ -160,7 +157,7 @@ export default class ConditionModal extends React.PureComponent<Props, State> {
{metric && (
<>
<div className="modal-field display-inline-block">
- <label htmlFor="condition-operator">
+ <label id="condition-operator-label">
{translate('quality_gates.conditions.operator')}
</label>
<ConditionOperator
@@ -170,7 +167,7 @@ export default class ConditionModal extends React.PureComponent<Props, State> {
/>
</div>
<div className="modal-field display-inline-block spacer-left">
- <label htmlFor="condition-threshold">
+ <label id="condition-threshold-label">
{translate('quality_gates.conditions.value')}
</label>
<ThresholdInput
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionOperator.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionOperator.tsx
index a104ce59671..ebad2b9038c 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionOperator.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/ConditionOperator.tsx
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import SelectLegacy from '../../../components/controls/SelectLegacy';
+import Select from '../../../components/controls/Select';
import { translate } from '../../../helpers/l10n';
import { Metric } from '../../../types/types';
import { getPossibleOperators } from '../utils';
@@ -50,16 +50,17 @@ export default class ConditionOperator extends React.PureComponent<Props> {
});
return (
- <SelectLegacy
+ <Select
autoFocus={true}
+ aria-labelledby="condition-operator-label"
className="input-medium"
- clearable={false}
+ isClearable={false}
id="condition-operator"
name="operator"
onChange={this.handleChange}
options={operatorOptions}
- searchable={false}
- value={this.props.op}
+ isSearchable={false}
+ value={operatorOptions.filter(o => o.value === this.props.op)}
/>
);
} else {
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx
index 81dbb739289..ef92de4334b 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.tsx
@@ -47,7 +47,7 @@ interface Props {
onRemoveCondition: (Condition: ConditionType) => void;
onSaveCondition: (newCondition: ConditionType, oldCondition: ConditionType) => void;
qualityGate: QualityGate;
- updatedConditionId?: number;
+ updatedConditionId?: string;
}
const FORBIDDEN_METRIC_TYPES = ['DATA', 'DISTRIB', 'STRING', 'BOOL'];
@@ -61,6 +61,7 @@ const FORBIDDEN_METRICS: string[] = [
export class Conditions extends React.PureComponent<Props> {
renderConditionsTable = (conditions: ConditionType[], scope: 'new' | 'overall') => {
const {
+ appState,
qualityGate,
metrics,
canEdit,
@@ -68,8 +69,22 @@ export class Conditions extends React.PureComponent<Props> {
onSaveCondition,
updatedConditionId
} = this.props;
+
+ const captionTranslationId =
+ scope === 'new'
+ ? 'quality_gates.conditions.new_code'
+ : 'quality_gates.conditions.overall_code';
return (
<table className="data zebra" data-test={`quality-gates__conditions-${scope}`}>
+ <caption>
+ <h4>{translate(captionTranslationId, 'long')}</h4>
+
+ {appState.branchesEnabled && (
+ <p className="spacer-top spacer-bottom">
+ {translate(captionTranslationId, 'description')}
+ </p>
+ )}
+ </caption>
<thead>
<tr>
<th className="nowrap" style={{ width: 300 }}>
@@ -104,7 +119,7 @@ export class Conditions extends React.PureComponent<Props> {
};
render() {
- const { appState, conditions, metrics, canEdit } = this.props;
+ const { conditions, metrics, canEdit } = this.props;
const existingConditions = conditions.filter(condition => metrics[condition.metric]);
const sortedConditions = sortBy(
@@ -194,28 +209,12 @@ export class Conditions extends React.PureComponent<Props> {
{sortedConditionsOnNewMetrics.length > 0 && (
<div className="big-spacer-top">
- <h4>{translate('quality_gates.conditions.new_code.long')}</h4>
-
- {appState.branchesEnabled && (
- <p className="spacer-top spacer-bottom">
- {translate('quality_gates.conditions.new_code.description')}
- </p>
- )}
-
{this.renderConditionsTable(sortedConditionsOnNewMetrics, 'new')}
</div>
)}
{sortedConditionsOnOverallMetrics.length > 0 && (
<div className="big-spacer-top">
- <h4>{translate('quality_gates.conditions.overall_code.long')}</h4>
-
- {appState.branchesEnabled && (
- <p className="spacer-top spacer-bottom">
- {translate('quality_gates.conditions.overall_code.description')}
- </p>
- )}
-
{this.renderConditionsTable(sortedConditionsOnOverallMetrics, 'overall')}
</div>
)}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/CopyQualityGateForm.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/CopyQualityGateForm.tsx
index b90652e8739..a76b8214625 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/CopyQualityGateForm.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/CopyQualityGateForm.tsx
@@ -52,10 +52,6 @@ export class CopyQualityGateForm extends React.PureComponent<Props, State> {
const { qualityGate } = this.props;
const { name } = this.state;
- if (!name) {
- return undefined;
- }
-
return copyQualityGate({ id: qualityGate.id, name }).then(newQualityGate => {
this.props.onCopy();
this.props.router.push(getQualityGateUrl(String(newQualityGate.id)));
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/Details.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/Details.tsx
index b63b1eed83d..3132ff9cffb 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/Details.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Details.tsx
@@ -38,7 +38,7 @@ interface Props {
interface State {
loading: boolean;
qualityGate?: QualityGate;
- updatedConditionId?: number;
+ updatedConditionId?: string;
}
export default class Details extends React.PureComponent<Props, State> {
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.tsx
index 6542c48f1ae..51ee032d762 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.tsx
@@ -32,7 +32,7 @@ export interface DetailsContentProps {
onRemoveCondition: (Condition: Condition) => void;
onSaveCondition: (newCondition: Condition, oldCondition: Condition) => void;
qualityGate: QualityGate;
- updatedConditionId?: number;
+ updatedConditionId?: string;
}
export function DetailsContent(props: DetailsContentProps) {
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/List.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/List.tsx
index c8ab508b2ca..b6d63e089e1 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/List.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/List.tsx
@@ -30,11 +30,12 @@ interface Props {
export default function List({ qualityGates }: Props) {
return (
- <div className="list-group">
+ <div className="list-group" role="menu">
{qualityGates.map(qualityGate => (
<Link
activeClassName="active"
className="list-group-item display-flex-center"
+ role="menuitem"
data-id={qualityGate.id}
key={qualityGate.id}
to={getQualityGateUrl(String(qualityGate.id))}>
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/RenameQualityGateForm.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/RenameQualityGateForm.tsx
index 10d431f44c5..aae7c62b03c 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/RenameQualityGateForm.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/RenameQualityGateForm.tsx
@@ -49,10 +49,6 @@ export default class RenameQualityGateForm extends React.PureComponent<Props, St
const { qualityGate } = this.props;
const { name } = this.state;
- if (!name) {
- return undefined;
- }
-
return renameQualityGate({ id: qualityGate.id, name }).then(() => this.props.onRename());
};
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/ThresholdInput.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/ThresholdInput.tsx
index 0739643b577..58c69ce43bd 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/ThresholdInput.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/ThresholdInput.tsx
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import SelectLegacy from '../../../components/controls/SelectLegacy';
+import Select from '../../../components/controls/Select';
import { Metric } from '../../../types/types';
interface Props {
@@ -33,12 +33,8 @@ export default class ThresholdInput extends React.PureComponent<Props> {
this.props.onChange(e.currentTarget.value);
};
- handleSelectChange = (option: { value: string } | null) => {
- if (option) {
- this.props.onChange(option.value);
- } else {
- this.props.onChange('');
- }
+ handleSelectChange = (option: { value: string }) => {
+ this.props.onChange(option.value);
};
renderRatingInput() {
@@ -52,16 +48,17 @@ export default class ThresholdInput extends React.PureComponent<Props> {
];
return (
- <SelectLegacy
+ <Select
className="input-tiny text-middle"
- clearable={true}
+ aria-labelledby="condition-threshold-label"
+ isClearable={false}
id="condition-threshold"
name={name}
onChange={this.handleSelectChange}
options={options}
placeholder=""
- searchable={false}
- value={value}
+ isSearchable={false}
+ value={options.find(o => o.value === value)}
/>
);
}
@@ -76,6 +73,7 @@ export default class ThresholdInput extends React.PureComponent<Props> {
return (
<input
className="input-tiny text-middle"
+ aria-labelledby="condition-threshold-label"
data-type={metric.type}
id="condition-threshold"
name={name}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/App-it.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/App-it.tsx
new file mode 100644
index 00000000000..d02fc407aa5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/App-it.tsx
@@ -0,0 +1,274 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { screen, waitFor, within } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { QualityGatesServiceMock } from '../../../../api/mocks/QualityGatesServiceMock';
+import { mockAppState } from '../../../../helpers/testMocks';
+import { renderApp } from '../../../../helpers/testReactTestingUtils';
+import { AppState } from '../../../../types/types';
+import routes from '../../routes';
+
+jest.mock('../../../../api/quality-gates');
+
+let handler: QualityGatesServiceMock;
+
+beforeAll(() => {
+ handler = new QualityGatesServiceMock();
+});
+
+afterEach(() => handler.reset());
+
+jest.setTimeout(10_000);
+
+it('should open the default quality gates', async () => {
+ renderQualityGateApp();
+
+ expect(await screen.findAllByRole('menuitem')).toHaveLength(handler.list.length);
+
+ const defaultQualityGate = handler.getDefaultQualityGate();
+ expect(await screen.findAllByText(defaultQualityGate.name)).toHaveLength(2);
+});
+
+it('should list all quality gates', async () => {
+ renderQualityGateApp();
+
+ expect(
+ await screen.findByRole('menuitem', {
+ name: `${handler.getDefaultQualityGate().name} default`
+ })
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByRole('menuitem', {
+ name: `${handler.getBuiltInQualityGate().name} quality_gates.built_in`
+ })
+ ).toBeInTheDocument();
+});
+
+it('should be able to create a quality gate then delete it', async () => {
+ const user = userEvent.setup();
+ handler.setIsAdmin(true);
+ renderQualityGateApp();
+
+ let createButton = await screen.findByRole('button', { name: 'create' });
+
+ // Using keyboard
+ await user.click(createButton);
+ let nameInput = screen.getByRole('textbox', { name: /name.*/ });
+ expect(nameInput).toBeInTheDocument();
+ await user.click(nameInput);
+ await user.keyboard('testone{Enter}');
+ expect(await screen.findByRole('menuitem', { name: 'testone' })).toBeInTheDocument();
+
+ // Using modal button
+ createButton = await screen.findByRole('button', { name: 'create' });
+ await user.click(createButton);
+ nameInput = screen.getByRole('textbox', { name: /name.*/ });
+ const saveButton = screen.getByRole('button', { name: 'save' });
+
+ expect(saveButton).toBeDisabled();
+ await user.click(nameInput);
+ await user.keyboard('testtwo');
+ await user.click(saveButton);
+
+ const newQG = await screen.findByRole('menuitem', { name: 'testtwo' });
+ expect(newQG).toBeInTheDocument();
+
+ // Delete the quality gate
+ await user.click(newQG);
+ const deleteButton = await screen.findByRole('button', { name: 'delete' });
+ await user.click(deleteButton);
+ const popup = screen.getByRole('dialog');
+ const dialogDeleteButton = within(popup).getByRole('button', { name: 'delete' });
+ await user.click(dialogDeleteButton);
+
+ await waitFor(() => {
+ expect(screen.queryByRole('menuitem', { name: 'testtwo' })).not.toBeInTheDocument();
+ });
+});
+
+it('should be able to copy a quality gate', async () => {
+ const user = userEvent.setup();
+ handler.setIsAdmin(true);
+ renderQualityGateApp();
+
+ const copyButton = await screen.findByRole('button', { name: 'copy' });
+
+ await user.click(copyButton);
+ const nameInput = screen.getByRole('textbox', { name: /name.*/ });
+ expect(nameInput).toBeInTheDocument();
+ await user.click(nameInput);
+ await user.keyboard(' bis{Enter}');
+
+ expect(await screen.findByRole('menuitem', { name: /.* bis/ })).toBeInTheDocument();
+});
+
+it('should be able to rename a quality gate', async () => {
+ const user = userEvent.setup();
+ handler.setIsAdmin(true);
+ renderQualityGateApp();
+
+ const renameButton = await screen.findByRole('button', { name: 'rename' });
+
+ await user.click(renameButton);
+ const nameInput = screen.getByRole('textbox', { name: /name.*/ });
+ expect(nameInput).toBeInTheDocument();
+ await user.click(nameInput);
+ await user.keyboard('{Control>}a{/Control}New Name{Enter}');
+
+ expect(await screen.findByRole('menuitem', { name: /New Name.*/ })).toBeInTheDocument();
+});
+
+it('should be able to add a condition', async () => {
+ const user = userEvent.setup();
+ handler.setIsAdmin(true);
+ renderQualityGateApp();
+
+ // On new code
+ await user.click(await screen.findByText('quality_gates.add_condition'));
+
+ let dialog = within(screen.getByRole('dialog'));
+
+ await user.click(dialog.getByRole('radio', { name: 'quality_gates.conditions.new_code' }));
+ await user.click(dialog.getByRole('combobox'));
+ await user.click(dialog.getByRole('option', { name: 'Issues' }));
+ await user.click(dialog.getByRole('textbox', { name: 'quality_gates.conditions.value' }));
+ await user.keyboard('12{Enter}');
+
+ const newConditions = within(
+ await screen.findByRole('table', { name: 'quality_gates.conditions.new_code.long' })
+ );
+ expect(await newConditions.findByRole('cell', { name: 'Issues' })).toBeInTheDocument();
+ expect(await newConditions.findByRole('cell', { name: '12' })).toBeInTheDocument();
+
+ // On overall code
+ await user.click(await screen.findByText('quality_gates.add_condition'));
+
+ dialog = within(screen.getByRole('dialog'));
+
+ await user.click(dialog.getByLabelText('quality_gates.conditions.fails_when'));
+ await user.click(dialog.getByRole('option', { name: 'Info Issues' }));
+ await user.click(dialog.getByRole('radio', { name: 'quality_gates.conditions.overall_code' }));
+ await user.click(dialog.getByLabelText('quality_gates.conditions.operator'));
+
+ await user.click(dialog.getByText('quality_gates.operator.LT'));
+ await user.click(dialog.getByRole('textbox', { name: 'quality_gates.conditions.value' }));
+ await user.keyboard('42{Enter}');
+
+ let overallConditions = within(
+ await screen.findByRole('table', { name: 'quality_gates.conditions.overall_code.long' })
+ );
+
+ expect(await overallConditions.findByRole('cell', { name: 'Info Issues' })).toBeInTheDocument();
+ expect(await overallConditions.findByRole('cell', { name: '42' })).toBeInTheDocument();
+
+ // Select a rating
+ await user.click(await screen.findByText('quality_gates.add_condition'));
+
+ dialog = within(screen.getByRole('dialog'));
+ await user.click(dialog.getByRole('radio', { name: 'quality_gates.conditions.overall_code' }));
+ await user.click(dialog.getByLabelText('quality_gates.conditions.fails_when'));
+ await user.click(dialog.getByRole('option', { name: 'Maintainability Rating' }));
+ await user.click(dialog.getByLabelText('quality_gates.conditions.value'));
+ await user.click(dialog.getByText('B'));
+ await user.click(dialog.getByRole('button', { name: 'quality_gates.add_condition' }));
+
+ overallConditions = within(
+ await screen.findByRole('table', { name: 'quality_gates.conditions.overall_code.long' })
+ );
+
+ expect(
+ await overallConditions.findByRole('cell', { name: 'Maintainability Rating' })
+ ).toBeInTheDocument();
+ expect(await overallConditions.findByRole('cell', { name: 'B' })).toBeInTheDocument();
+});
+
+it('should be able to edit a condition', async () => {
+ const user = userEvent.setup();
+ handler.setIsAdmin(true);
+ renderQualityGateApp();
+
+ const newConditions = within(
+ await screen.findByRole('table', {
+ name: 'quality_gates.conditions.new_code.long'
+ })
+ );
+
+ await user.click(
+ newConditions.getByLabelText('quality_gates.condition.edit.Coverage on New Code')
+ );
+ const dialog = within(screen.getByRole('dialog'));
+ await user.click(dialog.getByRole('textbox', { name: 'quality_gates.conditions.value' }));
+ await user.keyboard('{Backspace}{Backspace}23{Enter}');
+
+ expect(await newConditions.findByText('Coverage')).toBeInTheDocument();
+ expect(await newConditions.findByText('23.0%')).toBeInTheDocument();
+});
+
+it('should be able to handle duplicate or deprecated condition', async () => {
+ const user = userEvent.setup();
+ handler.setIsAdmin(true);
+ renderQualityGateApp();
+ await user.click(
+ await screen.findByRole('menuitem', { name: handler.getCorruptedQualityGateName() })
+ );
+
+ expect(await screen.findByText('quality_gates.duplicated_conditions')).toBeInTheDocument();
+ expect(
+ await screen.findByRole('cell', { name: 'Complexity / Function deprecated' })
+ ).toBeInTheDocument();
+});
+
+it('should be able to handle delete condition', async () => {
+ const user = userEvent.setup();
+ handler.setIsAdmin(true);
+ renderQualityGateApp();
+
+ const newConditions = within(
+ await screen.findByRole('table', {
+ name: 'quality_gates.conditions.new_code.long'
+ })
+ );
+
+ await user.click(
+ newConditions.getByLabelText('quality_gates.condition.delete.Coverage on New Code')
+ );
+
+ const dialog = within(screen.getByRole('dialog'));
+ await user.click(dialog.getByRole('button', { name: 'delete' }));
+
+ await waitFor(() => {
+ expect(newConditions.queryByRole('cell', { name: 'Coverage' })).not.toBeInTheDocument();
+ });
+});
+
+it('should explain condition on branch', async () => {
+ renderQualityGateApp(mockAppState({ branchesEnabled: true }));
+
+ expect(
+ await screen.findByText('quality_gates.conditions.new_code.description')
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText('quality_gates.conditions.overall_code.description')
+ ).toBeInTheDocument();
+});
+
+function renderQualityGateApp(appState?: AppState) {
+ renderApp('quality_gates', routes, { appState });
+}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/BuiltInQualityGateBadge-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/BuiltInQualityGateBadge-test.tsx
deleted file mode 100644
index b07bce79ce1..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/BuiltInQualityGateBadge-test.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import BuiltInQualityGateBadge from '../BuiltInQualityGateBadge';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender(props = {}) {
- return shallow(<BuiltInQualityGateBadge {...props} />);
-}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Condition-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Condition-test.tsx
deleted file mode 100644
index 94805c615da..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Condition-test.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockQualityGate } from '../../../../helpers/mocks/quality-gates';
-import { mockCondition, mockMetric } from '../../../../helpers/testMocks';
-import { ConditionComponent } from '../Condition';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot('default');
- expect(shallowRender({ canEdit: true })).toMatchSnapshot('with edit rights');
- expect(shallowRender({ updated: true })).toMatchSnapshot('updated');
-});
-
-it('should render the update modal correctly', () => {
- const wrapper = shallowRender({ canEdit: true });
- wrapper.instance().handleOpenUpdate();
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should render the delete modal correctly', () => {
- const wrapper = shallowRender({ canEdit: true });
- wrapper.instance().handleDeleteClick();
- expect(wrapper).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<ConditionComponent['props']> = {}) {
- return shallow<ConditionComponent>(
- <ConditionComponent
- canEdit={false}
- condition={mockCondition()}
- metric={mockMetric()}
- metrics={{}}
- onRemoveCondition={jest.fn()}
- onSaveCondition={jest.fn()}
- qualityGate={mockQualityGate()}
- {...props}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ConditionModal-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ConditionModal-test.tsx
deleted file mode 100644
index 41e1ca0fd44..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ConditionModal-test.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { createCondition, updateCondition } from '../../../../api/quality-gates';
-import { mockQualityGate } from '../../../../helpers/mocks/quality-gates';
-import { mockCondition, mockMetric } from '../../../../helpers/testMocks';
-import { MetricKey } from '../../../../types/metrics';
-import ConditionModal from '../ConditionModal';
-
-jest.mock('../../../../api/quality-gates', () => ({
- createCondition: jest.fn().mockResolvedValue({}),
- updateCondition: jest.fn().mockResolvedValue({})
-}));
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
- expect(shallowRender({ metric: mockMetric() })).toMatchSnapshot();
-});
-
-it('should correctly handle a metric selection', () => {
- const wrapper = shallowRender();
- const metric = mockMetric();
-
- expect(wrapper.find('withMetricsContext(MetricSelectComponent)').prop('metric')).toBeUndefined();
-
- wrapper.instance().handleMetricChange(metric);
- expect(wrapper.find('withMetricsContext(MetricSelectComponent)').prop('metric')).toEqual(metric);
-});
-
-it('should correctly switch scope', () => {
- const wrapper = shallowRender({
- metrics: [
- mockMetric({ key: MetricKey.new_coverage }),
- mockMetric({
- key: MetricKey.new_duplicated_lines
- }),
- mockMetric(),
- mockMetric({ key: MetricKey.duplicated_lines })
- ]
- });
- expect(wrapper).toMatchSnapshot();
-
- wrapper.instance().handleScopeChange('overall');
- expect(wrapper).toMatchSnapshot();
-
- wrapper.instance().handleScopeChange('new');
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should handle submission', async () => {
- const onAddCondition = jest.fn();
- const wrapper = shallowRender({ onAddCondition });
-
- wrapper.setState({ metric: mockMetric() });
-
- await wrapper.instance().handleFormSubmit();
-
- expect(createCondition).toBeCalled();
- expect(updateCondition).not.toBeCalled();
-
- jest.clearAllMocks();
-
- wrapper.setProps({ condition: mockCondition() });
- await wrapper.instance().handleFormSubmit();
-
- expect(createCondition).not.toBeCalled();
- expect(updateCondition).toBeCalled();
-});
-
-function shallowRender(props: Partial<ConditionModal['props']> = {}) {
- return shallow<ConditionModal>(
- <ConditionModal
- header="header"
- metrics={[
- mockMetric({ key: MetricKey.new_coverage }),
- mockMetric({ key: MetricKey.new_duplicated_lines })
- ]}
- onAddCondition={jest.fn()}
- onClose={jest.fn()}
- qualityGate={mockQualityGate()}
- {...props}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ConditionOperator-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ConditionOperator-test.tsx
deleted file mode 100644
index ac76272280b..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ConditionOperator-test.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import ConditionOperator from '../ConditionOperator';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<ConditionOperator['props']> = {}) {
- return shallow(
- <ConditionOperator
- metric={{ id: '1', key: 'foo', name: 'Foo', type: 'PERCENT' }}
- onOperatorChange={jest.fn()}
- op="LT"
- {...props}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Conditions-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Conditions-test.tsx
deleted file mode 100644
index 2963e8c21f2..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Conditions-test.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockQualityGate } from '../../../../helpers/mocks/quality-gates';
-import { mockAppState, mockCondition, mockMetric } from '../../../../helpers/testMocks';
-import { MetricKey } from '../../../../types/metrics';
-import { Conditions } from '../Conditions';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should render correctly with an updated condition', () => {
- expect(shallowRender({ updatedConditionId: mockCondition().id })).toMatchSnapshot();
-});
-
-it('should render correctly with new code conditions', () => {
- const wrapper = shallowRender({
- conditions: [
- mockCondition(),
- mockCondition({ id: 2, metric: MetricKey.duplicated_lines }),
- mockCondition({ id: 3, metric: MetricKey.new_coverage }),
- mockCondition({ id: 4, metric: MetricKey.new_duplicated_lines })
- ]
- });
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should render correctly for no conditions', () => {
- const wrapper = shallowRender({ conditions: [] });
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should render the add conditions button and modal', () => {
- const wrapper = shallowRender({ canEdit: true });
- expect(wrapper).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<Conditions['props']> = {}) {
- return shallow<Conditions>(
- <Conditions
- appState={mockAppState({ branchesEnabled: true })}
- canEdit={false}
- conditions={[mockCondition(), mockCondition({ id: 2, metric: MetricKey.duplicated_lines })]}
- metrics={{
- [MetricKey.coverage]: mockMetric(),
- [MetricKey.duplicated_lines]: mockMetric({ key: MetricKey.duplicated_lines }),
- [MetricKey.new_coverage]: mockMetric({
- key: MetricKey.new_coverage
- }),
- [MetricKey.new_duplicated_lines]: mockMetric({
- key: MetricKey.new_duplicated_lines
- })
- }}
- onAddCondition={jest.fn()}
- onRemoveCondition={jest.fn()}
- onSaveCondition={jest.fn()}
- qualityGate={mockQualityGate()}
- {...props}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/CopyQualityGateForm-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/CopyQualityGateForm-test.tsx
deleted file mode 100644
index 89ba4ea7736..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/CopyQualityGateForm-test.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { copyQualityGate } from '../../../../api/quality-gates';
-import { mockQualityGate } from '../../../../helpers/mocks/quality-gates';
-import { mockRouter } from '../../../../helpers/testMocks';
-import { CopyQualityGateForm } from '../CopyQualityGateForm';
-
-jest.mock('../../../../api/quality-gates', () => ({
- copyQualityGate: jest.fn().mockResolvedValue({})
-}));
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should handle copy', async () => {
- const onCopy = jest.fn();
- const router = mockRouter();
- const qualityGate = mockQualityGate();
- const wrapper = shallowRender({ onCopy, qualityGate, router });
-
- const name = 'name';
- wrapper.setState({ name });
-
- await wrapper.instance().handleCopy();
-
- expect(copyQualityGate).toBeCalledWith({ id: qualityGate.id, name });
- expect(onCopy).toBeCalled();
- expect(router.push).toBeCalled();
-
- jest.clearAllMocks();
-
- wrapper.setState({ name: '' });
- await wrapper.instance().handleCopy();
-
- expect(copyQualityGate).not.toBeCalled();
-});
-
-function shallowRender(overrides: Partial<CopyQualityGateForm['props']> = {}) {
- return shallow<CopyQualityGateForm>(
- <CopyQualityGateForm
- onClose={jest.fn()}
- onCopy={jest.fn()}
- qualityGate={mockQualityGate()}
- router={mockRouter()}
- {...overrides}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/CreateQualityGateForm-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/CreateQualityGateForm-test.tsx
deleted file mode 100644
index 0755def01fd..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/CreateQualityGateForm-test.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { createQualityGate } from '../../../../api/quality-gates';
-import ConfirmModal from '../../../../components/controls/ConfirmModal';
-import { mockRouter } from '../../../../helpers/testMocks';
-import { change, waitAndUpdate } from '../../../../helpers/testUtils';
-import { getQualityGateUrl } from '../../../../helpers/urls';
-import { CreateQualityGateForm } from '../CreateQualityGateForm';
-
-jest.mock('../../../../api/quality-gates', () => ({
- createQualityGate: jest.fn().mockResolvedValue({ id: '1', name: 'newValue' })
-}));
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should correctly handle create', async () => {
- const onCreate = jest.fn().mockResolvedValue(undefined);
- const push = jest.fn();
- const wrapper = shallowRender({ onCreate, router: mockRouter({ push }) });
-
- wrapper
- .find(ConfirmModal)
- .props()
- .onConfirm();
- expect(createQualityGate).not.toHaveBeenCalled();
-
- change(wrapper.find('#quality-gate-form-name'), 'newValue');
- expect(wrapper.state().name).toBe('newValue');
-
- wrapper
- .find(ConfirmModal)
- .props()
- .onConfirm();
- expect(createQualityGate).toHaveBeenCalledWith({ name: 'newValue' });
-
- await waitAndUpdate(wrapper);
- expect(onCreate).toHaveBeenCalled();
- expect(push).toHaveBeenCalledWith(getQualityGateUrl('1'));
-});
-
-function shallowRender(props: Partial<CreateQualityGateForm['props']> = {}) {
- return shallow<CreateQualityGateForm>(
- <CreateQualityGateForm
- onClose={jest.fn()}
- onCreate={jest.fn()}
- router={mockRouter()}
- {...props}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/DeleteQualityGateForm-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/DeleteQualityGateForm-test.tsx
deleted file mode 100644
index 56cb8fca107..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/DeleteQualityGateForm-test.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { deleteQualityGate } from '../../../../api/quality-gates';
-import { mockQualityGate } from '../../../../helpers/mocks/quality-gates';
-import { mockRouter } from '../../../../helpers/testMocks';
-import { DeleteQualityGateForm } from '../DeleteQualityGateForm';
-
-jest.mock('../../../../api/quality-gates', () => ({
- deleteQualityGate: jest.fn().mockResolvedValue({})
-}));
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should handle onDelete', async () => {
- const onDelete = jest.fn();
- const router = mockRouter();
- const qualityGate = mockQualityGate();
- const wrapper = shallowRender({ onDelete, qualityGate, router });
-
- await wrapper.instance().onDelete();
-
- expect(deleteQualityGate).toBeCalledWith({ id: qualityGate.id });
- expect(onDelete).toBeCalled();
- expect(router.push).toBeCalled();
-});
-
-function shallowRender(overrides: Partial<DeleteQualityGateForm['props']> = {}) {
- return shallow<DeleteQualityGateForm>(
- <DeleteQualityGateForm
- onDelete={jest.fn()}
- qualityGate={mockQualityGate()}
- router={mockRouter()}
- {...overrides}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Details-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Details-test.tsx
index 960f7379dd1..3f634504205 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Details-test.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Details-test.tsx
@@ -78,7 +78,7 @@ it('should correctly add/replace/remove conditions', async () => {
await waitAndUpdate(wrapper);
- const newCondition = mockCondition({ metric: 'bugs', id: 2 });
+ const newCondition = mockCondition({ metric: 'bugs', id: '2' });
instance.handleAddCondition(newCondition);
expect(addCondition).toBeCalledWith(qualityGate, newCondition);
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/List-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/List-test.tsx
deleted file mode 100644
index 790f5d013ab..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/List-test.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockQualityGate } from '../../../../helpers/mocks/quality-gates';
-import List from '../List';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender() {
- return shallow(
- <List
- qualityGates={[
- mockQualityGate(),
- mockQualityGate({ isBuiltIn: true }),
- mockQualityGate({ isDefault: true })
- ]}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ListHeader-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ListHeader-test.tsx
deleted file mode 100644
index d63656c0645..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ListHeader-test.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import ListHeader from '../ListHeader';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
- const wrapper = shallowRender({ canCreate: true });
- expect(wrapper.find('ModalButton').exists()).toBe(true);
- expect(wrapper.find('ModalButton').dive()).toMatchSnapshot();
-});
-
-function shallowRender(props = {}) {
- return shallow(<ListHeader canCreate={false} refreshQualityGates={jest.fn()} {...props} />);
-}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/MetricSelect-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/MetricSelect-test.tsx
deleted file mode 100644
index 2368e5cf37b..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/MetricSelect-test.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockMetric } from '../../../../helpers/testMocks';
-import { MetricSelectComponent } from '../MetricSelect';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should correctly handle change', () => {
- const onMetricChange = jest.fn();
- const metric = mockMetric();
- const metricsArray = [mockMetric({ key: 'duplication' }), metric];
- const wrapper = shallowRender({ metricsArray, onMetricChange });
- wrapper.instance().handleChange({ label: metric.name, value: metric.key });
- expect(onMetricChange).toBeCalledWith(metric);
-});
-
-function shallowRender(props: Partial<MetricSelectComponent['props']> = {}) {
- return shallow<MetricSelectComponent>(
- <MetricSelectComponent
- metricsArray={[mockMetric()]}
- metrics={{}}
- onMetricChange={jest.fn()}
- {...props}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/RenameQualityGateForm-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/RenameQualityGateForm-test.tsx
deleted file mode 100644
index 1ae772374d5..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/RenameQualityGateForm-test.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { renameQualityGate } from '../../../../api/quality-gates';
-import { mockQualityGate } from '../../../../helpers/mocks/quality-gates';
-import RenameQualityGateForm from '../RenameQualityGateForm';
-
-jest.mock('../../../../api/quality-gates', () => ({
- renameQualityGate: jest.fn().mockResolvedValue({})
-}));
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should handle rename', async () => {
- const qualityGate = mockQualityGate();
- const wrapper = shallowRender({ qualityGate });
-
- const name = 'new name';
-
- wrapper.setState({ name });
-
- await wrapper.instance().handleRename();
-
- expect(renameQualityGate).toBeCalledWith({ ...qualityGate, name });
-
- jest.clearAllMocks();
-
- wrapper.setState({ name: '' });
-
- await wrapper.instance().handleRename();
-
- expect(renameQualityGate).not.toBeCalled();
-});
-
-function shallowRender(overrides: Partial<RenameQualityGateForm['props']> = {}) {
- return shallow<RenameQualityGateForm>(
- <RenameQualityGateForm
- onClose={jest.fn()}
- onRename={jest.fn()}
- qualityGate={mockQualityGate()}
- {...overrides}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ThresholdInput-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ThresholdInput-test.tsx
deleted file mode 100644
index e51ec27a4f8..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/ThresholdInput-test.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import SelectLegacy from '../../../../components/controls/SelectLegacy';
-import { change } from '../../../../helpers/testUtils';
-import ThresholdInput from '../ThresholdInput';
-
-describe('on strings', () => {
- const metric = { id: '1', key: 'foo', name: 'Foo', type: 'INTEGER' };
- it('should render text input', () => {
- const input = shallow(
- <ThresholdInput metric={metric} name="foo" onChange={jest.fn()} value="2" />
- ).find('input');
- expect(input.length).toEqual(1);
- expect(input.prop('name')).toEqual('foo');
- expect(input.prop('value')).toEqual('2');
- });
-
- it('should change', () => {
- const onChange = jest.fn();
- const input = shallow(
- <ThresholdInput metric={metric} name="foo" onChange={onChange} value="2" />
- ).find('input');
- change(input, 'bar');
- expect(onChange).toBeCalledWith('bar');
- });
-});
-
-describe('on ratings', () => {
- const metric = { id: '1', key: 'foo', name: 'Foo', type: 'RATING' };
- it('should render Select', () => {
- const select = shallow(
- <ThresholdInput metric={metric} name="foo" onChange={jest.fn()} value="2" />
- ).find(SelectLegacy);
- expect(select.length).toEqual(1);
- expect(select.prop('value')).toEqual('2');
- });
-
- it('should set', () => {
- const onChange = jest.fn();
- const select = shallow(
- <ThresholdInput metric={metric} name="foo" onChange={onChange} value="2" />
- ).find(SelectLegacy);
- (select.prop('onChange') as Function)({ label: 'D', value: '4' });
- expect(onChange).toBeCalledWith('4');
- });
-
- it('should unset', () => {
- const onChange = jest.fn();
- const select = shallow(
- <ThresholdInput metric={metric} name="foo" onChange={onChange} value="2" />
- ).find(SelectLegacy);
- (select.prop('onChange') as Function)(null);
- expect(onChange).toBeCalledWith('');
- });
-});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/BuiltInQualityGateBadge-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/BuiltInQualityGateBadge-test.tsx.snap
deleted file mode 100644
index 04bf352dbe8..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/BuiltInQualityGateBadge-test.tsx.snap
+++ /dev/null
@@ -1,13 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Tooltip
- overlay="quality_gates.built_in.help"
->
- <div
- className="badge"
- >
- quality_gates.built_in
- </div>
-</Tooltip>
-`;
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/Condition-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/Condition-test.tsx.snap
deleted file mode 100644
index 50f735e44ee..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/Condition-test.tsx.snap
+++ /dev/null
@@ -1,203 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: default 1`] = `
-<tr
- className=""
->
- <td
- className="text-middle"
- >
- Coverage
- </td>
- <td
- className="text-middle nowrap"
- >
- quality_gates.operator.LT
- </td>
- <td
- className="text-middle nowrap"
- >
- 10.0%
- </td>
-</tr>
-`;
-
-exports[`should render correctly: updated 1`] = `
-<tr
- className="highlighted"
->
- <td
- className="text-middle"
- >
- Coverage
- </td>
- <td
- className="text-middle nowrap"
- >
- quality_gates.operator.LT
- </td>
- <td
- className="text-middle nowrap"
- >
- 10.0%
- </td>
-</tr>
-`;
-
-exports[`should render correctly: with edit rights 1`] = `
-<tr
- className=""
->
- <td
- className="text-middle"
- >
- Coverage
- </td>
- <td
- className="text-middle nowrap"
- >
- quality_gates.operator.LT
- </td>
- <td
- className="text-middle nowrap"
- >
- 10.0%
- </td>
- <td
- className="text-center thin"
- >
- <EditButton
- data-test="quality-gates__condition-update"
- onClick={[Function]}
- />
- </td>
- <td
- className="text-center thin"
- >
- <DeleteButton
- data-test="quality-gates__condition-delete"
- onClick={[Function]}
- />
- </td>
-</tr>
-`;
-
-exports[`should render the delete modal correctly 1`] = `
-<tr
- className=""
->
- <td
- className="text-middle"
- >
- Coverage
- </td>
- <td
- className="text-middle nowrap"
- >
- quality_gates.operator.LT
- </td>
- <td
- className="text-middle nowrap"
- >
- 10.0%
- </td>
- <td
- className="text-center thin"
- >
- <EditButton
- data-test="quality-gates__condition-update"
- onClick={[Function]}
- />
- </td>
- <td
- className="text-center thin"
- >
- <DeleteButton
- data-test="quality-gates__condition-delete"
- onClick={[Function]}
- />
- </td>
- <ConfirmModal
- confirmButtonText="delete"
- confirmData={
- Object {
- "error": "10",
- "id": 1,
- "metric": "coverage",
- "op": "LT",
- }
- }
- header="quality_gates.delete_condition"
- isDestructive={true}
- onClose={[Function]}
- onConfirm={[Function]}
- >
- quality_gates.delete_condition.confirm.message.Coverage
- </ConfirmModal>
-</tr>
-`;
-
-exports[`should render the update modal correctly 1`] = `
-<tr
- className=""
->
- <td
- className="text-middle"
- >
- Coverage
- </td>
- <td
- className="text-middle nowrap"
- >
- quality_gates.operator.LT
- </td>
- <td
- className="text-middle nowrap"
- >
- 10.0%
- </td>
- <td
- className="text-center thin"
- >
- <EditButton
- data-test="quality-gates__condition-update"
- onClick={[Function]}
- />
- </td>
- <td
- className="text-center thin"
- >
- <DeleteButton
- data-test="quality-gates__condition-delete"
- onClick={[Function]}
- />
- </td>
- <ConditionModal
- condition={
- Object {
- "error": "10",
- "id": 1,
- "metric": "coverage",
- "op": "LT",
- }
- }
- header="quality_gates.update_condition"
- metric={
- Object {
- "id": "coverage",
- "key": "coverage",
- "name": "Coverage",
- "type": "PERCENT",
- }
- }
- onAddCondition={[Function]}
- onClose={[Function]}
- qualityGate={
- Object {
- "id": "1",
- "name": "qualitygate",
- }
- }
- />
-</tr>
-`;
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ConditionModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ConditionModal-test.tsx.snap
deleted file mode 100644
index 29e4136d15e..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ConditionModal-test.tsx.snap
+++ /dev/null
@@ -1,364 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should correctly switch scope 1`] = `
-<ConfirmModal
- confirmButtonText="header"
- confirmDisable={true}
- header="header"
- onClose={[MockFunction]}
- onConfirm={[Function]}
- size="small"
->
- <div
- className="modal-field display-flex-center"
- >
- <Radio
- checked={true}
- onCheck={[Function]}
- value="new"
- >
- <span
- data-test="quality-gates__condition-scope-new"
- >
- quality_gates.conditions.new_code
- </span>
- </Radio>
- <Radio
- checked={false}
- className="big-spacer-left"
- onCheck={[Function]}
- value="overall"
- >
- <span
- data-test="quality-gates__condition-scope-overall"
- >
- quality_gates.conditions.overall_code
- </span>
- </Radio>
- </div>
- <div
- className="modal-field"
- >
- <label
- htmlFor="condition-metric"
- >
- quality_gates.conditions.fails_when
- </label>
- <withMetricsContext(MetricSelectComponent)
- metricsArray={
- Array [
- Object {
- "id": "new_coverage",
- "key": "new_coverage",
- "name": "New_coverage",
- "type": "PERCENT",
- },
- Object {
- "id": "new_duplicated_lines",
- "key": "new_duplicated_lines",
- "name": "New_duplicated_lines",
- "type": "PERCENT",
- },
- ]
- }
- onMetricChange={[Function]}
- />
- </div>
-</ConfirmModal>
-`;
-
-exports[`should correctly switch scope 2`] = `
-<ConfirmModal
- confirmButtonText="header"
- confirmDisable={true}
- header="header"
- onClose={[MockFunction]}
- onConfirm={[Function]}
- size="small"
->
- <div
- className="modal-field display-flex-center"
- >
- <Radio
- checked={false}
- onCheck={[Function]}
- value="new"
- >
- <span
- data-test="quality-gates__condition-scope-new"
- >
- quality_gates.conditions.new_code
- </span>
- </Radio>
- <Radio
- checked={true}
- className="big-spacer-left"
- onCheck={[Function]}
- value="overall"
- >
- <span
- data-test="quality-gates__condition-scope-overall"
- >
- quality_gates.conditions.overall_code
- </span>
- </Radio>
- </div>
- <div
- className="modal-field"
- >
- <label
- htmlFor="condition-metric"
- >
- quality_gates.conditions.fails_when
- </label>
- <withMetricsContext(MetricSelectComponent)
- metricsArray={
- Array [
- Object {
- "id": "coverage",
- "key": "coverage",
- "name": "Coverage",
- "type": "PERCENT",
- },
- Object {
- "id": "duplicated_lines",
- "key": "duplicated_lines",
- "name": "Duplicated_lines",
- "type": "PERCENT",
- },
- ]
- }
- onMetricChange={[Function]}
- />
- </div>
-</ConfirmModal>
-`;
-
-exports[`should correctly switch scope 3`] = `
-<ConfirmModal
- confirmButtonText="header"
- confirmDisable={true}
- header="header"
- onClose={[MockFunction]}
- onConfirm={[Function]}
- size="small"
->
- <div
- className="modal-field display-flex-center"
- >
- <Radio
- checked={true}
- onCheck={[Function]}
- value="new"
- >
- <span
- data-test="quality-gates__condition-scope-new"
- >
- quality_gates.conditions.new_code
- </span>
- </Radio>
- <Radio
- checked={false}
- className="big-spacer-left"
- onCheck={[Function]}
- value="overall"
- >
- <span
- data-test="quality-gates__condition-scope-overall"
- >
- quality_gates.conditions.overall_code
- </span>
- </Radio>
- </div>
- <div
- className="modal-field"
- >
- <label
- htmlFor="condition-metric"
- >
- quality_gates.conditions.fails_when
- </label>
- <withMetricsContext(MetricSelectComponent)
- metricsArray={
- Array [
- Object {
- "id": "new_coverage",
- "key": "new_coverage",
- "name": "New_coverage",
- "type": "PERCENT",
- },
- Object {
- "id": "new_duplicated_lines",
- "key": "new_duplicated_lines",
- "name": "New_duplicated_lines",
- "type": "PERCENT",
- },
- ]
- }
- onMetricChange={[Function]}
- />
- </div>
-</ConfirmModal>
-`;
-
-exports[`should render correctly 1`] = `
-<ConfirmModal
- confirmButtonText="header"
- confirmDisable={true}
- header="header"
- onClose={[MockFunction]}
- onConfirm={[Function]}
- size="small"
->
- <div
- className="modal-field display-flex-center"
- >
- <Radio
- checked={true}
- onCheck={[Function]}
- value="new"
- >
- <span
- data-test="quality-gates__condition-scope-new"
- >
- quality_gates.conditions.new_code
- </span>
- </Radio>
- <Radio
- checked={false}
- className="big-spacer-left"
- onCheck={[Function]}
- value="overall"
- >
- <span
- data-test="quality-gates__condition-scope-overall"
- >
- quality_gates.conditions.overall_code
- </span>
- </Radio>
- </div>
- <div
- className="modal-field"
- >
- <label
- htmlFor="condition-metric"
- >
- quality_gates.conditions.fails_when
- </label>
- <withMetricsContext(MetricSelectComponent)
- metricsArray={
- Array [
- Object {
- "id": "new_coverage",
- "key": "new_coverage",
- "name": "New_coverage",
- "type": "PERCENT",
- },
- Object {
- "id": "new_duplicated_lines",
- "key": "new_duplicated_lines",
- "name": "New_duplicated_lines",
- "type": "PERCENT",
- },
- ]
- }
- onMetricChange={[Function]}
- />
- </div>
-</ConfirmModal>
-`;
-
-exports[`should render correctly 2`] = `
-<ConfirmModal
- confirmButtonText="header"
- confirmDisable={false}
- header="header"
- onClose={[MockFunction]}
- onConfirm={[Function]}
- size="small"
->
- <div
- className="modal-field"
- >
- <label
- htmlFor="condition-metric"
- >
- quality_gates.conditions.fails_when
- </label>
- <withMetricsContext(MetricSelectComponent)
- metric={
- Object {
- "id": "coverage",
- "key": "coverage",
- "name": "Coverage",
- "type": "PERCENT",
- }
- }
- metricsArray={
- Array [
- Object {
- "id": "new_coverage",
- "key": "new_coverage",
- "name": "New_coverage",
- "type": "PERCENT",
- },
- Object {
- "id": "new_duplicated_lines",
- "key": "new_duplicated_lines",
- "name": "New_duplicated_lines",
- "type": "PERCENT",
- },
- ]
- }
- onMetricChange={[Function]}
- />
- <span
- className="note"
- >
- Coverage
- </span>
- </div>
- <div
- className="modal-field display-inline-block"
- >
- <label
- htmlFor="condition-operator"
- >
- quality_gates.conditions.operator
- </label>
- <ConditionOperator
- metric={
- Object {
- "id": "coverage",
- "key": "coverage",
- "name": "Coverage",
- "type": "PERCENT",
- }
- }
- onOperatorChange={[Function]}
- />
- </div>
- <div
- className="modal-field display-inline-block spacer-left"
- >
- <label
- htmlFor="condition-threshold"
- >
- quality_gates.conditions.value
- </label>
- <ThresholdInput
- metric={
- Object {
- "id": "coverage",
- "key": "coverage",
- "name": "Coverage",
- "type": "PERCENT",
- }
- }
- name="error"
- onChange={[Function]}
- value=""
- />
- </div>
-</ConfirmModal>
-`;
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ConditionOperator-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ConditionOperator-test.tsx.snap
deleted file mode 100644
index b5271694236..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ConditionOperator-test.tsx.snap
+++ /dev/null
@@ -1,26 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<SelectLegacy
- autoFocus={true}
- className="input-medium"
- clearable={false}
- id="condition-operator"
- name="operator"
- onChange={[Function]}
- options={
- Array [
- Object {
- "label": "quality_gates.operator.LT",
- "value": "LT",
- },
- Object {
- "label": "quality_gates.operator.GT",
- "value": "GT",
- },
- ]
- }
- searchable={false}
- value="LT"
-/>
-`;
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/Conditions-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/Conditions-test.tsx.snap
deleted file mode 100644
index 66ea9de3960..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/Conditions-test.tsx.snap
+++ /dev/null
@@ -1,660 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<div
- className="quality-gate-section"
->
- <header
- className="display-flex-center spacer-bottom"
- >
- <h3>
- quality_gates.conditions
- </h3>
- <DocumentationTooltip
- className="spacer-left"
- content="quality_gates.conditions.help"
- links={
- Array [
- Object {
- "href": "/documentation/user-guide/clean-as-you-code/",
- "label": "quality_gates.conditions.help.link",
- },
- ]
- }
- />
- </header>
- <div
- className="big-spacer-top"
- >
- <h4>
- quality_gates.conditions.overall_code.long
- </h4>
- <p
- className="spacer-top spacer-bottom"
- >
- quality_gates.conditions.overall_code.description
- </p>
- <table
- className="data zebra"
- data-test="quality-gates__conditions-overall"
- >
- <thead>
- <tr>
- <th
- className="nowrap"
- style={
- Object {
- "width": 300,
- }
- }
- >
- quality_gates.conditions.metric
- </th>
- <th
- className="nowrap"
- >
- quality_gates.conditions.operator
- </th>
- <th
- className="nowrap"
- >
- quality_gates.conditions.value
- </th>
- </tr>
- </thead>
- <tbody>
- <withMetricsContext(ConditionComponent)
- canEdit={false}
- condition={
- Object {
- "error": "10",
- "id": 1,
- "metric": "coverage",
- "op": "LT",
- }
- }
- key="1"
- metric={
- Object {
- "id": "coverage",
- "key": "coverage",
- "name": "Coverage",
- "type": "PERCENT",
- }
- }
- onRemoveCondition={[MockFunction]}
- onSaveCondition={[MockFunction]}
- qualityGate={
- Object {
- "id": "1",
- "name": "qualitygate",
- }
- }
- updated={false}
- />
- <withMetricsContext(ConditionComponent)
- canEdit={false}
- condition={
- Object {
- "error": "10",
- "id": 2,
- "metric": "duplicated_lines",
- "op": "LT",
- }
- }
- key="2"
- metric={
- Object {
- "id": "duplicated_lines",
- "key": "duplicated_lines",
- "name": "Duplicated_lines",
- "type": "PERCENT",
- }
- }
- onRemoveCondition={[MockFunction]}
- onSaveCondition={[MockFunction]}
- qualityGate={
- Object {
- "id": "1",
- "name": "qualitygate",
- }
- }
- updated={false}
- />
- </tbody>
- </table>
- </div>
-</div>
-`;
-
-exports[`should render correctly for no conditions 1`] = `
-<div
- className="quality-gate-section"
->
- <header
- className="display-flex-center spacer-bottom"
- >
- <h3>
- quality_gates.conditions
- </h3>
- <DocumentationTooltip
- className="spacer-left"
- content="quality_gates.conditions.help"
- links={
- Array [
- Object {
- "href": "/documentation/user-guide/clean-as-you-code/",
- "label": "quality_gates.conditions.help.link",
- },
- ]
- }
- />
- </header>
- <div
- className="big-spacer-top"
- >
- quality_gates.no_conditions
- </div>
-</div>
-`;
-
-exports[`should render correctly with an updated condition 1`] = `
-<div
- className="quality-gate-section"
->
- <header
- className="display-flex-center spacer-bottom"
- >
- <h3>
- quality_gates.conditions
- </h3>
- <DocumentationTooltip
- className="spacer-left"
- content="quality_gates.conditions.help"
- links={
- Array [
- Object {
- "href": "/documentation/user-guide/clean-as-you-code/",
- "label": "quality_gates.conditions.help.link",
- },
- ]
- }
- />
- </header>
- <div
- className="big-spacer-top"
- >
- <h4>
- quality_gates.conditions.overall_code.long
- </h4>
- <p
- className="spacer-top spacer-bottom"
- >
- quality_gates.conditions.overall_code.description
- </p>
- <table
- className="data zebra"
- data-test="quality-gates__conditions-overall"
- >
- <thead>
- <tr>
- <th
- className="nowrap"
- style={
- Object {
- "width": 300,
- }
- }
- >
- quality_gates.conditions.metric
- </th>
- <th
- className="nowrap"
- >
- quality_gates.conditions.operator
- </th>
- <th
- className="nowrap"
- >
- quality_gates.conditions.value
- </th>
- </tr>
- </thead>
- <tbody>
- <withMetricsContext(ConditionComponent)
- canEdit={false}
- condition={
- Object {
- "error": "10",
- "id": 1,
- "metric": "coverage",
- "op": "LT",
- }
- }
- key="1"
- metric={
- Object {
- "id": "coverage",
- "key": "coverage",
- "name": "Coverage",
- "type": "PERCENT",
- }
- }
- onRemoveCondition={[MockFunction]}
- onSaveCondition={[MockFunction]}
- qualityGate={
- Object {
- "id": "1",
- "name": "qualitygate",
- }
- }
- updated={true}
- />
- <withMetricsContext(ConditionComponent)
- canEdit={false}
- condition={
- Object {
- "error": "10",
- "id": 2,
- "metric": "duplicated_lines",
- "op": "LT",
- }
- }
- key="2"
- metric={
- Object {
- "id": "duplicated_lines",
- "key": "duplicated_lines",
- "name": "Duplicated_lines",
- "type": "PERCENT",
- }
- }
- onRemoveCondition={[MockFunction]}
- onSaveCondition={[MockFunction]}
- qualityGate={
- Object {
- "id": "1",
- "name": "qualitygate",
- }
- }
- updated={false}
- />
- </tbody>
- </table>
- </div>
-</div>
-`;
-
-exports[`should render correctly with new code conditions 1`] = `
-<div
- className="quality-gate-section"
->
- <header
- className="display-flex-center spacer-bottom"
- >
- <h3>
- quality_gates.conditions
- </h3>
- <DocumentationTooltip
- className="spacer-left"
- content="quality_gates.conditions.help"
- links={
- Array [
- Object {
- "href": "/documentation/user-guide/clean-as-you-code/",
- "label": "quality_gates.conditions.help.link",
- },
- ]
- }
- />
- </header>
- <div
- className="big-spacer-top"
- >
- <h4>
- quality_gates.conditions.new_code.long
- </h4>
- <p
- className="spacer-top spacer-bottom"
- >
- quality_gates.conditions.new_code.description
- </p>
- <table
- className="data zebra"
- data-test="quality-gates__conditions-new"
- >
- <thead>
- <tr>
- <th
- className="nowrap"
- style={
- Object {
- "width": 300,
- }
- }
- >
- quality_gates.conditions.metric
- </th>
- <th
- className="nowrap"
- >
- quality_gates.conditions.operator
- </th>
- <th
- className="nowrap"
- >
- quality_gates.conditions.value
- </th>
- </tr>
- </thead>
- <tbody>
- <withMetricsContext(ConditionComponent)
- canEdit={false}
- condition={
- Object {
- "error": "10",
- "id": 3,
- "metric": "new_coverage",
- "op": "LT",
- }
- }
- key="3"
- metric={
- Object {
- "id": "new_coverage",
- "key": "new_coverage",
- "name": "New_coverage",
- "type": "PERCENT",
- }
- }
- onRemoveCondition={[MockFunction]}
- onSaveCondition={[MockFunction]}
- qualityGate={
- Object {
- "id": "1",
- "name": "qualitygate",
- }
- }
- updated={false}
- />
- <withMetricsContext(ConditionComponent)
- canEdit={false}
- condition={
- Object {
- "error": "10",
- "id": 4,
- "metric": "new_duplicated_lines",
- "op": "LT",
- }
- }
- key="4"
- metric={
- Object {
- "id": "new_duplicated_lines",
- "key": "new_duplicated_lines",
- "name": "New_duplicated_lines",
- "type": "PERCENT",
- }
- }
- onRemoveCondition={[MockFunction]}
- onSaveCondition={[MockFunction]}
- qualityGate={
- Object {
- "id": "1",
- "name": "qualitygate",
- }
- }
- updated={false}
- />
- </tbody>
- </table>
- </div>
- <div
- className="big-spacer-top"
- >
- <h4>
- quality_gates.conditions.overall_code.long
- </h4>
- <p
- className="spacer-top spacer-bottom"
- >
- quality_gates.conditions.overall_code.description
- </p>
- <table
- className="data zebra"
- data-test="quality-gates__conditions-overall"
- >
- <thead>
- <tr>
- <th
- className="nowrap"
- style={
- Object {
- "width": 300,
- }
- }
- >
- quality_gates.conditions.metric
- </th>
- <th
- className="nowrap"
- >
- quality_gates.conditions.operator
- </th>
- <th
- className="nowrap"
- >
- quality_gates.conditions.value
- </th>
- </tr>
- </thead>
- <tbody>
- <withMetricsContext(ConditionComponent)
- canEdit={false}
- condition={
- Object {
- "error": "10",
- "id": 1,
- "metric": "coverage",
- "op": "LT",
- }
- }
- key="1"
- metric={
- Object {
- "id": "coverage",
- "key": "coverage",
- "name": "Coverage",
- "type": "PERCENT",
- }
- }
- onRemoveCondition={[MockFunction]}
- onSaveCondition={[MockFunction]}
- qualityGate={
- Object {
- "id": "1",
- "name": "qualitygate",
- }
- }
- updated={false}
- />
- <withMetricsContext(ConditionComponent)
- canEdit={false}
- condition={
- Object {
- "error": "10",
- "id": 2,
- "metric": "duplicated_lines",
- "op": "LT",
- }
- }
- key="2"
- metric={
- Object {
- "id": "duplicated_lines",
- "key": "duplicated_lines",
- "name": "Duplicated_lines",
- "type": "PERCENT",
- }
- }
- onRemoveCondition={[MockFunction]}
- onSaveCondition={[MockFunction]}
- qualityGate={
- Object {
- "id": "1",
- "name": "qualitygate",
- }
- }
- updated={false}
- />
- </tbody>
- </table>
- </div>
-</div>
-`;
-
-exports[`should render the add conditions button and modal 1`] = `
-<div
- className="quality-gate-section"
->
- <div
- className="pull-right"
- >
- <ModalButton
- modal={[Function]}
- >
- <Component />
- </ModalButton>
- </div>
- <header
- className="display-flex-center spacer-bottom"
- >
- <h3>
- quality_gates.conditions
- </h3>
- <DocumentationTooltip
- className="spacer-left"
- content="quality_gates.conditions.help"
- links={
- Array [
- Object {
- "href": "/documentation/user-guide/clean-as-you-code/",
- "label": "quality_gates.conditions.help.link",
- },
- ]
- }
- />
- </header>
- <div
- className="big-spacer-top"
- >
- <h4>
- quality_gates.conditions.overall_code.long
- </h4>
- <p
- className="spacer-top spacer-bottom"
- >
- quality_gates.conditions.overall_code.description
- </p>
- <table
- className="data zebra"
- data-test="quality-gates__conditions-overall"
- >
- <thead>
- <tr>
- <th
- className="nowrap"
- style={
- Object {
- "width": 300,
- }
- }
- >
- quality_gates.conditions.metric
- </th>
- <th
- className="nowrap"
- >
- quality_gates.conditions.operator
- </th>
- <th
- className="nowrap"
- >
- quality_gates.conditions.value
- </th>
- <th
- className="thin"
- >
- edit
- </th>
- <th
- className="thin"
- >
- delete
- </th>
- </tr>
- </thead>
- <tbody>
- <withMetricsContext(ConditionComponent)
- canEdit={true}
- condition={
- Object {
- "error": "10",
- "id": 1,
- "metric": "coverage",
- "op": "LT",
- }
- }
- key="1"
- metric={
- Object {
- "id": "coverage",
- "key": "coverage",
- "name": "Coverage",
- "type": "PERCENT",
- }
- }
- onRemoveCondition={[MockFunction]}
- onSaveCondition={[MockFunction]}
- qualityGate={
- Object {
- "id": "1",
- "name": "qualitygate",
- }
- }
- updated={false}
- />
- <withMetricsContext(ConditionComponent)
- canEdit={true}
- condition={
- Object {
- "error": "10",
- "id": 2,
- "metric": "duplicated_lines",
- "op": "LT",
- }
- }
- key="2"
- metric={
- Object {
- "id": "duplicated_lines",
- "key": "duplicated_lines",
- "name": "Duplicated_lines",
- "type": "PERCENT",
- }
- }
- onRemoveCondition={[MockFunction]}
- onSaveCondition={[MockFunction]}
- qualityGate={
- Object {
- "id": "1",
- "name": "qualitygate",
- }
- }
- updated={false}
- />
- </tbody>
- </table>
- </div>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/CopyQualityGateForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/CopyQualityGateForm-test.tsx.snap
deleted file mode 100644
index f3607739dd5..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/CopyQualityGateForm-test.tsx.snap
+++ /dev/null
@@ -1,36 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<ConfirmModal
- confirmButtonText="copy"
- confirmDisable={true}
- header="quality_gates.copy"
- onClose={[MockFunction]}
- onConfirm={[Function]}
- size="small"
->
- <MandatoryFieldsExplanation
- className="modal-field"
- />
- <div
- className="modal-field"
- >
- <label
- htmlFor="quality-gate-form-name"
- >
- name
- <MandatoryFieldMarker />
- </label>
- <input
- autoFocus={true}
- id="quality-gate-form-name"
- maxLength={100}
- onChange={[Function]}
- required={true}
- size={50}
- type="text"
- value="qualitygate"
- />
- </div>
-</ConfirmModal>
-`;
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/CreateQualityGateForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/CreateQualityGateForm-test.tsx.snap
deleted file mode 100644
index f81416622fd..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/CreateQualityGateForm-test.tsx.snap
+++ /dev/null
@@ -1,36 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<ConfirmModal
- confirmButtonText="save"
- confirmDisable={true}
- header="quality_gates.create"
- onClose={[MockFunction]}
- onConfirm={[Function]}
- size="small"
->
- <MandatoryFieldsExplanation
- className="modal-field"
- />
- <div
- className="modal-field"
- >
- <label
- htmlFor="quality-gate-form-name"
- >
- name
- <MandatoryFieldMarker />
- </label>
- <input
- autoFocus={true}
- id="quality-gate-form-name"
- maxLength={100}
- onChange={[Function]}
- required={true}
- size={50}
- type="text"
- value=""
- />
- </div>
-</ConfirmModal>
-`;
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DeleteQualityGateForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DeleteQualityGateForm-test.tsx.snap
deleted file mode 100644
index d99d75037ab..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DeleteQualityGateForm-test.tsx.snap
+++ /dev/null
@@ -1,13 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<ConfirmButton
- confirmButtonText="delete"
- isDestructive={true}
- modalBody="quality_gates.delete.confirm.message.qualitygate"
- modalHeader="quality_gates.delete"
- onConfirm={[Function]}
->
- <Component />
-</ConfirmButton>
-`;
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsContent-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsContent-test.tsx.snap
index 7354a115ce8..74843ff5fe9 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsContent-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsContent-test.tsx.snap
@@ -86,7 +86,7 @@ exports[`should render correctly: is default 1`] = `
Array [
Object {
"error": "10",
- "id": 1,
+ "id": "1",
"metric": "coverage",
"op": "LT",
},
@@ -100,7 +100,7 @@ exports[`should render correctly: is default 1`] = `
"conditions": Array [
Object {
"error": "10",
- "id": 1,
+ "id": "1",
"metric": "coverage",
"op": "LT",
},
@@ -204,7 +204,7 @@ exports[`should render correctly: is not default 1`] = `
Array [
Object {
"error": "10",
- "id": 1,
+ "id": "1",
"metric": "coverage",
"op": "LT",
},
@@ -218,7 +218,7 @@ exports[`should render correctly: is not default 1`] = `
"conditions": Array [
Object {
"error": "10",
- "id": 1,
+ "id": "1",
"metric": "coverage",
"op": "LT",
},
@@ -259,7 +259,7 @@ exports[`should render correctly: is not default 1`] = `
"conditions": Array [
Object {
"error": "10",
- "id": 1,
+ "id": "1",
"metric": "coverage",
"op": "LT",
},
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsHeader-test.tsx.snap
index 614efb96ed8..a1106a333c9 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsHeader-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsHeader-test.tsx.snap
@@ -55,7 +55,7 @@ exports[`should render correctly: admin actions 1`] = `
"conditions": Array [
Object {
"error": "10",
- "id": 1,
+ "id": "1",
"metric": "coverage",
"op": "LT",
},
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/List-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/List-test.tsx.snap
deleted file mode 100644
index 9fad96d9809..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/List-test.tsx.snap
+++ /dev/null
@@ -1,73 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<div
- className="list-group"
->
- <Link
- activeClassName="active"
- className="list-group-item display-flex-center"
- data-id="1"
- key="1"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/quality_gates/show/1",
- }
- }
- >
- <span
- className="flex-1"
- >
- qualitygate
- </span>
- </Link>
- <Link
- activeClassName="active"
- className="list-group-item display-flex-center"
- data-id="1"
- key="1"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/quality_gates/show/1",
- }
- }
- >
- <span
- className="flex-1"
- >
- qualitygate
- </span>
- <BuiltInQualityGateBadge
- className="little-spacer-left"
- />
- </Link>
- <Link
- activeClassName="active"
- className="list-group-item display-flex-center"
- data-id="1"
- key="1"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/quality_gates/show/1",
- }
- }
- >
- <span
- className="flex-1"
- >
- qualitygate
- </span>
- <span
- className="badge little-spacer-left"
- >
- default
- </span>
- </Link>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ListHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ListHeader-test.tsx.snap
deleted file mode 100644
index 0913eca851b..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/ListHeader-test.tsx.snap
+++ /dev/null
@@ -1,40 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<header
- className="page-header"
->
- <div
- className="display-flex-center"
- >
- <h1
- className="page-title"
- >
- quality_gates.page
- </h1>
- <DocumentationTooltip
- className="spacer-left"
- content="quality_gates.help"
- links={
- Array [
- Object {
- "href": "/documentation/user-guide/quality-gates/",
- "label": "learn_more",
- },
- ]
- }
- />
- </div>
-</header>
-`;
-
-exports[`should render correctly 2`] = `
-<Fragment>
- <Button
- data-test="quality-gates__add"
- onClick={[Function]}
- >
- create
- </Button>
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/MetricSelect-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/MetricSelect-test.tsx.snap
deleted file mode 100644
index 1d2d3106f23..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/MetricSelect-test.tsx.snap
+++ /dev/null
@@ -1,19 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<SelectLegacy
- className="text-middle quality-gate-metric-select"
- id="condition-metric"
- onChange={[Function]}
- options={
- Array [
- Object {
- "domain": undefined,
- "label": "Coverage",
- "value": "coverage",
- },
- ]
- }
- placeholder="search.search_for_metrics"
-/>
-`;
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/RenameQualityGateForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/RenameQualityGateForm-test.tsx.snap
deleted file mode 100644
index b0220d9f9ed..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/RenameQualityGateForm-test.tsx.snap
+++ /dev/null
@@ -1,36 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<ConfirmModal
- confirmButtonText="rename"
- confirmDisable={true}
- header="quality_gates.rename"
- onClose={[MockFunction]}
- onConfirm={[Function]}
- size="small"
->
- <MandatoryFieldsExplanation
- className="modal-field"
- />
- <div
- className="modal-field"
- >
- <label
- htmlFor="quality-gate-form-name"
- >
- name
- <MandatoryFieldMarker />
- </label>
- <input
- autoFocus={true}
- id="quality-gate-form-name"
- maxLength={100}
- onChange={[Function]}
- required={true}
- size={50}
- type="text"
- value="qualitygate"
- />
- </div>
-</ConfirmModal>
-`;
diff --git a/server/sonar-web/src/main/js/components/controls/Tooltip.tsx b/server/sonar-web/src/main/js/components/controls/Tooltip.tsx
index 80cb8cd2292..e52cd274a8d 100644
--- a/server/sonar-web/src/main/js/components/controls/Tooltip.tsx
+++ b/server/sonar-web/src/main/js/components/controls/Tooltip.tsx
@@ -346,8 +346,8 @@ export class TooltipInner extends React.Component<TooltipProps, State> {
return (
<div
className={`${classNameSpace} ${currentPlacement}`}
- onMouseEnter={this.handleOverlayMouseEnter}
- onMouseLeave={this.handleOverlayMouseLeave}
+ onPointerEnter={this.handleOverlayMouseEnter}
+ onPointerLeave={this.handleOverlayMouseLeave}
ref={this.tooltipNodeRef}
style={style}>
<div className={`${classNameSpace}-inner`}>{this.props.overlay}</div>
@@ -367,8 +367,8 @@ export class TooltipInner extends React.Component<TooltipProps, State> {
return (
<>
{React.cloneElement(this.props.children, {
- onMouseEnter: this.handleMouseEnter,
- onMouseLeave: this.handleMouseLeave
+ onPointerEnter: this.handleMouseEnter,
+ onPointerLeave: this.handleMouseLeave
})}
{this.isVisible() && (
<TooltipPortal>
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/Tooltip-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/Tooltip-test.tsx
index aa0450f088d..bd79b3c3ca8 100644
--- a/server/sonar-web/src/main/js/components/controls/__tests__/Tooltip-test.tsx
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/Tooltip-test.tsx
@@ -56,26 +56,26 @@ it('should open & close', () => {
const onHide = jest.fn();
const wrapper = shallowRenderTooltipInner({ onHide, onShow });
- wrapper.find('#tooltip').simulate('mouseenter');
+ wrapper.find('#tooltip').simulate('pointerenter');
jest.runOnlyPendingTimers();
wrapper.update();
expect(wrapper.find('TooltipPortal').exists()).toBe(true);
expect(onShow).toBeCalled();
- wrapper.find('#tooltip').simulate('mouseleave');
+ wrapper.find('#tooltip').simulate('pointerleave');
jest.runOnlyPendingTimers();
wrapper.update();
expect(wrapper.find('TooltipPortal').exists()).toBe(false);
expect(onHide).toBeCalled();
});
-it('should not open when mouse goes away quickly', () => {
+it('should not open when pointer goes away quickly', () => {
const onShow = jest.fn();
const onHide = jest.fn();
const wrapper = shallowRenderTooltipInner({ onHide, onShow });
- wrapper.find('#tooltip').simulate('mouseenter');
- wrapper.find('#tooltip').simulate('mouseleave');
+ wrapper.find('#tooltip').simulate('pointerenter');
+ wrapper.find('#tooltip').simulate('pointerleave');
jest.runOnlyPendingTimers();
wrapper.update();
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ActionsDropdown-test.tsx.snap b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ActionsDropdown-test.tsx.snap
index 4bd6a2ea46d..42636f02080 100644
--- a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ActionsDropdown-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ActionsDropdown-test.tsx.snap
@@ -123,8 +123,8 @@ exports[`ActionsDropdownItem should render correctly copy item 1`] = `
>
<li
data-clipboard-text="my content to copy to clipboard"
- onMouseEnter={[Function]}
- onMouseLeave={[Function]}
+ onPointerEnter={[Function]}
+ onPointerLeave={[Function]}
>
<a
className="foo"
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Tooltip-test.tsx.snap b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Tooltip-test.tsx.snap
index de3df03b6e5..50bceeb148b 100644
--- a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Tooltip-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Tooltip-test.tsx.snap
@@ -16,8 +16,8 @@ exports[`should render 1`] = `
<Fragment>
<div
id="tooltip"
- onMouseEnter={[Function]}
- onMouseLeave={[Function]}
+ onPointerEnter={[Function]}
+ onPointerLeave={[Function]}
/>
</Fragment>
`;
@@ -26,8 +26,8 @@ exports[`should render 2`] = `
<Fragment>
<div
id="tooltip"
- onMouseEnter={[Function]}
- onMouseLeave={[Function]}
+ onPointerEnter={[Function]}
+ onPointerLeave={[Function]}
/>
<TooltipPortal>
<ScreenPositionFixer
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/buttons-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/buttons-test.tsx
index e0a23d842bb..811bf2380c6 100644
--- a/server/sonar-web/src/main/js/components/controls/__tests__/buttons-test.tsx
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/buttons-test.tsx
@@ -48,7 +48,7 @@ describe('Button', () => {
const preventDefault = jest.fn();
const onClick = jest.fn();
const button = shallowRender({ disabled: true, onClick, preventDefault: false }).find('button');
- expect(button.props().disabled).toBeUndefined();
+ expect(button.props().disabled).toBe(true);
expect(button.props().className).toContain('disabled');
expect(button.props()['aria-disabled']).toBe(true);
click(button, mockEvent({ preventDefault }));
diff --git a/server/sonar-web/src/main/js/components/controls/buttons.tsx b/server/sonar-web/src/main/js/components/controls/buttons.tsx
index a1acfe858f0..8c0aa493d2a 100644
--- a/server/sonar-web/src/main/js/components/controls/buttons.tsx
+++ b/server/sonar-web/src/main/js/components/controls/buttons.tsx
@@ -76,6 +76,7 @@ export class Button extends React.PureComponent<ButtonProps> {
<button
{...props}
aria-disabled={disabled}
+ disabled={disabled}
className={classNames('button', className, { disabled })}
id={this.props.id}
onClick={this.handleClick}
diff --git a/server/sonar-web/src/main/js/helpers/testMocks.ts b/server/sonar-web/src/main/js/helpers/testMocks.ts
index ddbc2c8e952..fde809bb117 100644
--- a/server/sonar-web/src/main/js/helpers/testMocks.ts
+++ b/server/sonar-web/src/main/js/helpers/testMocks.ts
@@ -292,7 +292,7 @@ export function mockClusterSysInfo(overrides: Partial<any> = {}): SysInfoCluster
export function mockCondition(overrides: Partial<Condition> = {}): Condition {
return {
error: '10',
- id: 1,
+ id: '1',
metric: 'coverage',
op: 'LT',
...overrides
diff --git a/server/sonar-web/src/main/js/types/types.ts b/server/sonar-web/src/main/js/types/types.ts
index 01544faf61e..cbff79bdbc7 100644
--- a/server/sonar-web/src/main/js/types/types.ts
+++ b/server/sonar-web/src/main/js/types/types.ts
@@ -192,7 +192,7 @@ export interface ComponentMeasureEnhanced extends ComponentMeasureIntern {
export interface Condition {
error: string;
- id: number;
+ id: string;
metric: string;
op?: string;
}