diff options
Diffstat (limited to 'server/sonar-web/src/main')
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; } |