--- /dev/null
+/*
+ * 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));
+ }
+}
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);
}
"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",
},
"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",
},
"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",
},
<>
<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}
/>
}
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') => {
{metric && (
<>
<div className="modal-field display-inline-block">
- <label htmlFor="condition-operator">
+ <label id="condition-operator-label">
{translate('quality_gates.conditions.operator')}
</label>
<ConditionOperator
/>
</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
* 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';
});
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 {
onRemoveCondition: (Condition: ConditionType) => void;
onSaveCondition: (newCondition: ConditionType, oldCondition: ConditionType) => void;
qualityGate: QualityGate;
- updatedConditionId?: number;
+ updatedConditionId?: string;
}
const FORBIDDEN_METRIC_TYPES = ['DATA', 'DISTRIB', 'STRING', 'BOOL'];
export class Conditions extends React.PureComponent<Props> {
renderConditionsTable = (conditions: ConditionType[], scope: 'new' | 'overall') => {
const {
+ appState,
qualityGate,
metrics,
canEdit,
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 }}>
};
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(
{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>
)}
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)));
interface State {
loading: boolean;
qualityGate?: QualityGate;
- updatedConditionId?: number;
+ updatedConditionId?: string;
}
export default class Details extends React.PureComponent<Props, State> {
onRemoveCondition: (Condition: Condition) => void;
onSaveCondition: (newCondition: Condition, oldCondition: Condition) => void;
qualityGate: QualityGate;
- updatedConditionId?: number;
+ updatedConditionId?: string;
}
export function DetailsContent(props: DetailsContentProps) {
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))}>
const { qualityGate } = this.props;
const { name } = this.state;
- if (!name) {
- return undefined;
- }
-
return renameQualityGate({ id: qualityGate.id, name }).then(() => this.props.onRename());
};
* 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 {
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() {
];
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)}
/>
);
}
return (
<input
className="input-tiny text-middle"
+ aria-labelledby="condition-threshold-label"
data-type={metric.type}
id="condition-threshold"
name={name}
--- /dev/null
+/*
+ * 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 });
+}
+++ /dev/null
-/*
- * 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} />);
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
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);
+++ /dev/null
-/*
- * 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 })
- ]}
- />
- );
-}
+++ /dev/null
-/*
- * 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} />);
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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('');
- });
-});
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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"
-/>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
Array [
Object {
"error": "10",
- "id": 1,
+ "id": "1",
"metric": "coverage",
"op": "LT",
},
"conditions": Array [
Object {
"error": "10",
- "id": 1,
+ "id": "1",
"metric": "coverage",
"op": "LT",
},
Array [
Object {
"error": "10",
- "id": 1,
+ "id": "1",
"metric": "coverage",
"op": "LT",
},
"conditions": Array [
Object {
"error": "10",
- "id": 1,
+ "id": "1",
"metric": "coverage",
"op": "LT",
},
"conditions": Array [
Object {
"error": "10",
- "id": 1,
+ "id": "1",
"metric": "coverage",
"op": "LT",
},
"conditions": Array [
Object {
"error": "10",
- "id": 1,
+ "id": "1",
"metric": "coverage",
"op": "LT",
},
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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"
-/>
-`;
+++ /dev/null
-// 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>
-`;
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>
return (
<>
{React.cloneElement(this.props.children, {
- onMouseEnter: this.handleMouseEnter,
- onMouseLeave: this.handleMouseLeave
+ onPointerEnter: this.handleMouseEnter,
+ onPointerLeave: this.handleMouseLeave
})}
{this.isVisible() && (
<TooltipPortal>
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();
>
<li
data-clipboard-text="my content to copy to clipboard"
- onMouseEnter={[Function]}
- onMouseLeave={[Function]}
+ onPointerEnter={[Function]}
+ onPointerLeave={[Function]}
>
<a
className="foo"
<Fragment>
<div
id="tooltip"
- onMouseEnter={[Function]}
- onMouseLeave={[Function]}
+ onPointerEnter={[Function]}
+ onPointerLeave={[Function]}
/>
</Fragment>
`;
<Fragment>
<div
id="tooltip"
- onMouseEnter={[Function]}
- onMouseLeave={[Function]}
+ onPointerEnter={[Function]}
+ onPointerLeave={[Function]}
/>
<TooltipPortal>
<ScreenPositionFixer
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 }));
<button
{...props}
aria-disabled={disabled}
+ disabled={disabled}
className={classNames('button', className, { disabled })}
id={this.props.id}
onClick={this.handleClick}
export function mockCondition(overrides: Partial<Condition> = {}): Condition {
return {
error: '10',
- id: 1,
+ id: '1',
metric: 'coverage',
op: 'LT',
...overrides
export interface Condition {
error: string;
- id: number;
+ id: string;
metric: string;
op?: string;
}
quality_gates.projects=Projects
quality_gates.projects.help=The Default gate is applied to all projects not explicitly assigned to a gate. Quality Gate administrators can assign projects to a non-default gate, or always make it follow the system default. Project administrators may choose any gate.
quality_gates.add_condition=Add Condition
+quality_gates.condition.edit=Edit condition on {0}
+quality_gates.condition.delete=Delete condition on {0}
quality_gates.condition_added=Successfully added condition.
quality_gates.update_condition=Update Condition
quality_gates.condition_updated=Successfully updated condition.