dissociateGateWithProject,
fetchQualityGate,
fetchQualityGates,
+ getGateForProject,
renameQualityGate,
searchGroups,
searchProjects,
readOnlyList: QualityGate[];
list: QualityGate[];
projects: Project[];
+ getGateForProjectGateName: string;
+ throwOnGetGateForProject: boolean;
- constructor(list?: QualityGate[], defaultId = 'AWBWEMe2qGAMGEYPjJlm') {
+ constructor(list?: QualityGate[]) {
this.readOnlyList = list || [
mockQualityGate({
- id: defaultId,
name: 'SonarSource way',
conditions: [
{ id: 'AXJMbIUGPAOIsUIE3eNC', metric: 'new_coverage', op: 'LT', error: '85' },
isCaycCompliant: true,
}),
mockQualityGate({
- id: 'AXGYZrDqC-YjVCvvbRDY',
name: 'SonarSource way - CFamily',
conditions: [
{ id: 'AXJMbIUHPAOIsUIE3eOu', metric: 'new_coverage', op: 'LT', error: '0' },
isBuiltIn: false,
}),
mockQualityGate({
- id: 'AWBWEMe4qGAMGEYPjJlr',
name: 'Sonar way',
conditions: [
{ id: 'AXJMbIUHPAOIsUIE3eNs', metric: 'new_security_rating', op: 'GT', error: '1' },
isCaycCompliant: true,
}),
mockQualityGate({
- id: 'AWBWEMe4qGAMGEYPjJlruit',
name: 'Non Cayc QG',
conditions: [
{ id: 'AXJMbIUHPAOIsUIE3eNs', metric: 'new_security_rating', op: 'GT', error: '1' },
isBuiltIn: false,
isCaycCompliant: false,
}),
+ mockQualityGate({
+ name: 'QG without conditions',
+ conditions: [],
+ isDefault: false,
+ isBuiltIn: false,
+ isCaycCompliant: false,
+ }),
+ mockQualityGate({
+ name: 'QG without new code conditions',
+ conditions: [
+ { id: 'AXJMbIUHPAOIsUIE3eNs', metric: 'security_rating', op: 'GT', error: '1' },
+ ],
+ isDefault: false,
+ isBuiltIn: false,
+ isCaycCompliant: false,
+ }),
];
this.list = cloneDeep(this.readOnlyList);
{ key: 'test4', name: 'test4', selected: true },
];
+ this.getGateForProjectGateName = 'SonarSource way';
+ this.throwOnGetGateForProject = false;
+
(fetchQualityGate as jest.Mock).mockImplementation(this.showHandler);
(fetchQualityGates as jest.Mock).mockImplementation(this.listHandler);
(createQualityGate as jest.Mock).mockImplementation(this.createHandler);
(associateGateWithProject as jest.Mock).mockImplementation(this.selectHandler);
(dissociateGateWithProject as jest.Mock).mockImplementation(this.deSelectHandler);
(setQualityGateAsDefault as jest.Mock).mockImplementation(this.setDefaultHandler);
+ (getGateForProject as jest.Mock).mockImplementation(this.projectGateHandler);
// To be implemented.
(addUser as jest.Mock).mockResolvedValue({});
reset() {
this.setIsAdmin(false);
this.list = cloneDeep(this.readOnlyList);
+ this.getGateForProjectGateName = 'SonarSource way';
}
getDefaultQualityGate() {
this.isAdmin = isAdmin;
}
+ setGetGateForProjectName(name: string) {
+ this.getGateForProjectGateName = name;
+ }
+
+ setThrowOnGetGateForProject(value: boolean) {
+ this.throwOnGetGateForProject = value;
+ }
+
computeActions(q: QualityGate) {
return {
rename: q.isBuiltIn ? false : this.isAdmin,
...q,
actions: this.computeActions(q),
})),
- default: this.getDefaultQualityGate().id,
+ default: this.getDefaultQualityGate().name,
actions: { create: this.isAdmin },
});
};
};
createHandler = ({ name }: { name: string }) => {
- const newId = `newId${this.list.length}`;
this.list.push(
mockQualityGate({
- id: newId,
name,
conditions: [
mockCondition({
})
);
return this.reply({
- id: newId,
name,
});
};
});
}
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,
});
};
}
renameQG.name = name;
return this.reply({
- id: renameQG.id,
name,
});
};
createConditionHandler = (
data: {
- gateId: string;
+ gateName: string;
} & Omit<Condition, 'id'>
) => {
- const { metric, gateId, op, error } = data;
- const qg = this.list.find((q) => q.id === gateId);
+ const { metric, gateName, op, error } = data;
+ const qg = this.list.find((q) => q.name === gateName);
if (qg === undefined) {
return Promise.reject({
- errors: [{ msg: `No quality gate has been found for id ${gateId}` }],
+ errors: [{ msg: `No quality gate has been found for name ${gateName}` }],
});
}
const conditions = qg.conditions || [];
- const id = `condId${qg.id}${conditions.length}`;
- const newCondition = { id, metric, op, error };
+ const id = `condId${qg.name}${conditions.length}`;
+ const newCondition = { metric, op, error, id };
+
conditions.push(newCondition);
qg.conditions = conditions;
return this.reply(newCondition);
return Promise.resolve();
};
+ projectGateHandler = () => {
+ if (this.throwOnGetGateForProject) {
+ return Promise.reject('unknown');
+ }
+
+ return this.reply(this.list.find((qg) => qg.name === this.getGateForProjectGateName));
+ };
+
reply<T>(response: T): Promise<T> {
return Promise.resolve(cloneDeep(response));
}
export function createCondition(
data: {
- gateId: string;
+ gateName: string;
} & Omit<Condition, 'id'>
): Promise<Condition> {
return postJSON('/api/qualitygates/create_condition', data).catch(throwGlobalError);
}
export function associateGateWithProject(data: {
- gateId: string;
+ gateName: string;
projectKey: string;
}): Promise<void | Response> {
return post('/api/qualitygates/select', data).catch(throwGlobalError);
} from '../../api/quality-gates';
import withComponentContext from '../../app/components/componentContext/withComponentContext';
import handleRequiredAuthorization from '../../app/utils/handleRequiredAuthorization';
-import { addGlobalSuccessMessage } from '../../helpers/globalMessages';
+import {
+ addGlobalErrorMessageFromAPI,
+ addGlobalSuccessMessage,
+} from '../../helpers/globalMessages';
import { translate } from '../../helpers/l10n';
import { Component, QualityGate } from '../../types/types';
import { USE_SYSTEM_DEFAULT } from './constants';
allQualityGates?: QualityGate[];
currentQualityGate?: QualityGate;
loading: boolean;
- selectedQualityGateId: string;
+ selectedQualityGateName: string;
submitting: boolean;
}
-export class ProjectQualityGateApp extends React.PureComponent<Props, State> {
+class ProjectQualityGateApp extends React.PureComponent<Props, State> {
mounted = false;
state: State = {
loading: true,
- selectedQualityGateId: USE_SYSTEM_DEFAULT,
+ selectedQualityGateName: USE_SYSTEM_DEFAULT,
submitting: false,
};
const [allQualityGates, currentQualityGate] = await Promise.all([
this.fetchDetailedQualityGates(),
getGateForProject({ project: component.key }),
- ]).catch(() => []);
+ ]).catch((error) => {
+ addGlobalErrorMessageFromAPI(error);
+ return [];
+ });
if (allQualityGates && currentQualityGate) {
const usingDefault = await this.isUsingDefault(currentQualityGate);
this.setState({
allQualityGates,
currentQualityGate,
- selectedQualityGateId: usingDefault ? USE_SYSTEM_DEFAULT : currentQualityGate.id,
+ selectedQualityGateName: usingDefault ? USE_SYSTEM_DEFAULT : currentQualityGate.name,
loading: false,
});
}
}
};
- handleSelect = (selectedQualityGateId: string) => {
- this.setState({ selectedQualityGateId });
+ handleSelect = (selectedQualityGateName: string) => {
+ this.setState({ selectedQualityGateName });
};
handleSubmit = async () => {
const { component } = this.props;
- const { allQualityGates, currentQualityGate, selectedQualityGateId } = this.state;
+ const { allQualityGates, currentQualityGate, selectedQualityGateName } = this.state;
if (allQualityGates === undefined || currentQualityGate === undefined) {
return;
this.setState({ submitting: true });
- if (selectedQualityGateId === USE_SYSTEM_DEFAULT) {
+ if (selectedQualityGateName === USE_SYSTEM_DEFAULT) {
await dissociateGateWithProject({
projectKey: component.key,
}).catch(() => {
});
} else {
await associateGateWithProject({
- gateId: selectedQualityGateId,
+ gateName: selectedQualityGateName,
projectKey: component.key,
}).catch(() => {
/* noop */
addGlobalSuccessMessage(translate('project_quality_gate.successfully_updated'));
const newGate =
- selectedQualityGateId === USE_SYSTEM_DEFAULT
+ selectedQualityGateName === USE_SYSTEM_DEFAULT
? allQualityGates.find((gate) => gate.isDefault)
- : allQualityGates.find((gate) => gate.id === selectedQualityGateId);
+ : allQualityGates.find((gate) => gate.name === selectedQualityGateName);
if (newGate) {
this.setState({ currentQualityGate: newGate, submitting: false });
return null;
}
- const { allQualityGates, currentQualityGate, loading, selectedQualityGateId, submitting } =
+ const { allQualityGates, currentQualityGate, loading, selectedQualityGateName, submitting } =
this.state;
return (
loading={loading}
onSubmit={this.handleSubmit}
onSelect={this.handleSelect}
- selectedQualityGateId={selectedQualityGateId}
+ selectedQualityGateName={selectedQualityGateName}
submitting={submitting}
/>
);
loading: boolean;
onSelect: (id: string) => void;
onSubmit: () => void;
- selectedQualityGateId: string;
+ selectedQualityGateName: string;
submitting: boolean;
}
}
export default function ProjectQualityGateAppRenderer(props: ProjectQualityGateAppRendererProps) {
- const { allQualityGates, currentQualityGate, loading, selectedQualityGateId, submitting } = props;
+ const { allQualityGates, currentQualityGate, loading, selectedQualityGateName, submitting } =
+ props;
const defaultQualityGate = allQualityGates?.find((g) => g.isDefault);
if (loading) {
return null;
}
- const usesDefault = selectedQualityGateId === USE_SYSTEM_DEFAULT;
+ const usesDefault = selectedQualityGateName === USE_SYSTEM_DEFAULT;
const needsReanalysis = usesDefault
? // currentQualityGate.isDefault is not always up to date. We need to check
// against defaultQualityGate explicitly.
- defaultQualityGate.id !== currentQualityGate.id
- : selectedQualityGateId !== currentQualityGate.id;
+ defaultQualityGate.name !== currentQualityGate.name
+ : selectedQualityGateName !== currentQualityGate.name;
- const selectedQualityGate = allQualityGates.find((qg) => qg.id === selectedQualityGateId);
+ const selectedQualityGate = allQualityGates.find((qg) => qg.name === selectedQualityGateName);
const options: QualityGateOption[] = allQualityGates.map((g) => ({
isDisabled: g.conditions === undefined || g.conditions.length === 0,
label: g.name,
- value: g.id,
+ value: g.name,
}));
return (
props.onSelect(value);
}
}}
- value={!usesDefault ? selectedQualityGateId : currentQualityGate.id}
+ value={!usesDefault ? selectedQualityGateName : currentQualityGate.name}
>
<div className="spacer-left">
<div className="little-spacer-bottom">
onChange={({ value }: QualityGateOption) => {
props.onSelect(value);
}}
+ aria-label={translate('project_quality_gate.select_specific_qg')}
options={options}
- value={options.find((o) => o.value === selectedQualityGateId)}
+ value={options.find((o) => o.value === selectedQualityGateName)}
/>
</div>
</div>
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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 userEvent from '@testing-library/user-event';
+import selectEvent from 'react-select-event';
+import { byRole, byText } from 'testing-library-selector';
+import { QualityGatesServiceMock } from '../../../api/mocks/QualityGatesServiceMock';
+import handleRequiredAuthorization from '../../../app/utils/handleRequiredAuthorization';
+import { mockComponent } from '../../../helpers/mocks/component';
+import {
+ renderAppWithComponentContext,
+ RenderContext,
+} from '../../../helpers/testReactTestingUtils';
+import { Component } from '../../../types/types';
+import routes from '../routes';
+
+jest.mock('../../../api/quality-gates');
+
+jest.mock('../../../app/utils/handleRequiredAuthorization');
+
+let handler: QualityGatesServiceMock;
+
+const ui = {
+ qualityGateHeading: byRole('heading', { name: 'project_quality_gate.page' }),
+ defaultRadioQualityGate: byRole('radio', {
+ name: /project_quality_gate.always_use_default/,
+ }),
+ specificRadioQualityGate: byRole('radio', { name: /project_quality_gate.always_use_specific/ }),
+ qualityGatesSelect: byRole('textbox', { name: 'project_quality_gate.select_specific_qg' }),
+ QGWithoutConditionsOptionLabel: byRole('radio', {
+ name: /option QG without conditions selected/,
+ }),
+ QGWithoutNewCodeConditionOptionLabel: byRole('radio', {
+ name: 'project_quality_gate.always_use_specific QG without new code conditions',
+ }),
+ saveButton: byRole('button', { name: 'save' }),
+ statusMessage: byRole('status'),
+ noConditionsNewCodeWarning: byText('project_quality_gate.no_condition_on_new_code'),
+ alertMessage: byRole('alert'),
+};
+
+beforeAll(() => {
+ handler = new QualityGatesServiceMock();
+});
+
+afterEach(() => handler.reset());
+
+it('should require authorization if no permissions set', () => {
+ renderProjectQualityGateApp({}, {});
+ expect(handleRequiredAuthorization).toHaveBeenCalled();
+ expect(ui.qualityGateHeading.query()).not.toBeInTheDocument();
+});
+
+it('should be able to select and save specific Quality Gate', async () => {
+ renderProjectQualityGateApp();
+
+ expect(await ui.qualityGateHeading.find()).toBeInTheDocument();
+ expect(ui.defaultRadioQualityGate.get()).toBeChecked();
+
+ await userEvent.click(ui.specificRadioQualityGate.get());
+ expect(ui.qualityGatesSelect.get()).toBeEnabled();
+
+ await selectEvent.select(ui.qualityGatesSelect.get(), 'Sonar way');
+ await userEvent.click(ui.saveButton.get());
+ expect(ui.statusMessage.get()).toHaveTextContent(/project_quality_gate.success/);
+
+ // Set back default QG
+ await userEvent.click(ui.defaultRadioQualityGate.get());
+ expect(ui.qualityGatesSelect.get()).toBeDisabled();
+ expect(ui.defaultRadioQualityGate.get()).toBeChecked();
+
+ await userEvent.click(ui.saveButton.get());
+ expect(ui.statusMessage.getAll()[1]).toHaveTextContent(/project_quality_gate.success/);
+});
+
+it('shows warning for quality gate that doesnt have conditions on new code', async () => {
+ handler.setGetGateForProjectName('Sonar way');
+ renderProjectQualityGateApp();
+
+ await userEvent.click(await ui.specificRadioQualityGate.find());
+ await selectEvent.select(ui.qualityGatesSelect.get(), 'QG without conditions');
+ expect(ui.QGWithoutConditionsOptionLabel.query()).not.toBeInTheDocument();
+
+ await selectEvent.select(ui.qualityGatesSelect.get(), 'QG without new code conditions');
+ expect(ui.QGWithoutNewCodeConditionOptionLabel.get()).toBeInTheDocument();
+ expect(ui.noConditionsNewCodeWarning.get()).toBeInTheDocument();
+});
+
+it('renders nothing and shows alert when any API fails', async () => {
+ handler.setThrowOnGetGateForProject(true);
+ renderProjectQualityGateApp();
+
+ expect(await ui.alertMessage.find()).toHaveTextContent('unknown');
+ expect(ui.qualityGateHeading.query()).not.toBeInTheDocument();
+});
+
+function renderProjectQualityGateApp(
+ context?: RenderContext,
+ componentOverrides: Partial<Component> = { configuration: { showQualityGates: true } }
+) {
+ renderAppWithComponentContext('project/quality_gate', routes, context, {
+ component: mockComponent(componentOverrides),
+ });
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 {
- associateGateWithProject,
- dissociateGateWithProject,
- fetchQualityGates,
- getGateForProject,
- searchProjects,
-} from '../../../api/quality-gates';
-import handleRequiredAuthorization from '../../../app/utils/handleRequiredAuthorization';
-import { mockComponent } from '../../../helpers/mocks/component';
-import { mockQualityGate } from '../../../helpers/mocks/quality-gates';
-import { waitAndUpdate } from '../../../helpers/testUtils';
-import { USE_SYSTEM_DEFAULT } from '../constants';
-import { ProjectQualityGateApp } from '../ProjectQualityGateApp';
-
-jest.mock('../../../api/quality-gates', () => {
- const { mockQualityGate } = jest.requireActual('../../../helpers/mocks/quality-gates');
- const { mockCondition } = jest.requireActual('../../../helpers/testMocks');
-
- const conditions = [mockCondition(), mockCondition({ metric: 'new_bugs' })];
- const gates = {
- gate1: mockQualityGate({ id: 'gate1' }),
- gate2: mockQualityGate({ id: 'gate2', isBuiltIn: true }),
- gate3: mockQualityGate({ id: 'gate3', isDefault: true }),
- gate4: mockQualityGate({ id: 'gate4' }),
- };
-
- return {
- associateGateWithProject: jest.fn().mockResolvedValue(null),
- dissociateGateWithProject: jest.fn().mockResolvedValue(null),
- fetchQualityGates: jest.fn().mockResolvedValue({
- qualitygates: Object.values(gates),
- }),
- fetchQualityGate: jest.fn().mockImplementation((qg: { id: keyof typeof gates }) => {
- if (qg.id === 'gate4') {
- return Promise.reject();
- }
- return Promise.resolve({ conditions, ...gates[qg.id] });
- }),
- getGateForProject: jest.fn().mockResolvedValue(gates.gate2),
- searchProjects: jest.fn().mockResolvedValue({ results: [] }),
- };
-});
-
-jest.mock('../../../helpers/globalMessages', () => ({
- addGlobalSuccessMessage: jest.fn(),
-}));
-
-jest.mock('../../../app/utils/handleRequiredAuthorization', () => jest.fn());
-
-beforeEach(jest.clearAllMocks);
-
-it('renders correctly', async () => {
- const wrapper = shallowRender();
- await waitAndUpdate(wrapper);
- expect(wrapper).toMatchSnapshot();
-});
-
-it('correctly checks user permissions', () => {
- shallowRender({ component: mockComponent({ configuration: { showQualityGates: false } }) });
- expect(handleRequiredAuthorization).toHaveBeenCalled();
-});
-
-it('correctly loads Quality Gate data', async () => {
- const wrapper = shallowRender();
- await waitAndUpdate(wrapper);
-
- expect(fetchQualityGates).toHaveBeenCalled();
- expect(getGateForProject).toHaveBeenCalledWith({ project: 'foo' });
-
- expect(wrapper.state().allQualityGates).toHaveLength(4);
- expect(wrapper.state().currentQualityGate?.id).toBe('gate2');
- expect(wrapper.state().selectedQualityGateId).toBe('gate2');
-});
-
-it('correctly fallbacks to the default Quality Gate', async () => {
- (getGateForProject as jest.Mock).mockResolvedValueOnce(
- mockQualityGate({ id: 'gate3', isDefault: true })
- );
- const wrapper = shallowRender();
- await waitAndUpdate(wrapper);
-
- expect(searchProjects).toHaveBeenCalled();
-
- expect(wrapper.state().currentQualityGate?.id).toBe('gate3');
- expect(wrapper.state().selectedQualityGateId).toBe(USE_SYSTEM_DEFAULT);
-});
-
-it('correctly detects if the default Quality Gate was explicitly selected', async () => {
- (getGateForProject as jest.Mock).mockResolvedValueOnce(
- mockQualityGate({ id: 'gate3', isDefault: true })
- );
- (searchProjects as jest.Mock).mockResolvedValueOnce({
- results: [{ key: 'foo', selected: true }],
- });
- const wrapper = shallowRender();
- await waitAndUpdate(wrapper);
-
- expect(searchProjects).toHaveBeenCalled();
-
- expect(wrapper.state().currentQualityGate?.id).toBe('gate3');
- expect(wrapper.state().selectedQualityGateId).toBe('gate3');
-});
-
-it('correctly associates a selected Quality Gate', async () => {
- const wrapper = shallowRender();
- await waitAndUpdate(wrapper);
-
- wrapper.instance().handleSelect('gate3');
- wrapper.instance().handleSubmit();
-
- expect(associateGateWithProject).toHaveBeenCalledWith({
- gateId: 'gate3',
- projectKey: 'foo',
- });
-});
-
-it('correctly associates a project with the system default Quality Gate', async () => {
- const wrapper = shallowRender();
- await waitAndUpdate(wrapper);
-
- wrapper.setState({
- currentQualityGate: mockQualityGate({ id: 'gate1' }),
- selectedQualityGateId: USE_SYSTEM_DEFAULT,
- });
- wrapper.instance().handleSubmit();
-
- expect(dissociateGateWithProject).toHaveBeenCalledWith({
- projectKey: 'foo',
- });
-});
-
-it("correctly doesn't do anything if the system default was selected, and the project had no prior Quality Gate associated with it", async () => {
- const wrapper = shallowRender();
- await waitAndUpdate(wrapper);
-
- wrapper.setState({ currentQualityGate: undefined, selectedQualityGateId: USE_SYSTEM_DEFAULT });
- wrapper.instance().handleSubmit();
-
- expect(associateGateWithProject).not.toHaveBeenCalled();
- expect(dissociateGateWithProject).not.toHaveBeenCalled();
-});
-
-it('correctly handles WS errors', async () => {
- (fetchQualityGates as jest.Mock).mockRejectedValueOnce(null);
- (getGateForProject as jest.Mock).mockRejectedValueOnce(null);
- (searchProjects as jest.Mock).mockRejectedValueOnce(null);
-
- const wrapper = shallowRender();
- await waitAndUpdate(wrapper);
-
- expect(wrapper.state().allQualityGates).toBeUndefined();
- expect(wrapper.state().currentQualityGate).toBeUndefined();
- expect(wrapper.state().loading).toBe(false);
-
- const result = await wrapper.instance().isUsingDefault(mockQualityGate());
- expect(result).toBe(false);
-});
-
-function shallowRender(props: Partial<ProjectQualityGateApp['props']> = {}) {
- return shallow<ProjectQualityGateApp>(
- <ProjectQualityGateApp
- component={mockComponent({ key: 'foo', configuration: { showQualityGates: true } })}
- onComponentChange={jest.fn()}
- {...props}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 Radio from '../../../components/controls/Radio';
-import Select from '../../../components/controls/Select';
-import { mockQualityGate } from '../../../helpers/mocks/quality-gates';
-import { mockCondition } from '../../../helpers/testMocks';
-import { submit } from '../../../helpers/testUtils';
-import { MetricKey } from '../../../types/metrics';
-import { USE_SYSTEM_DEFAULT } from '../constants';
-import ProjectQualityGateAppRenderer, {
- ProjectQualityGateAppRendererProps,
-} from '../ProjectQualityGateAppRenderer';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot('default');
- expect(shallowRender({ loading: true })).toMatchSnapshot('loading');
- expect(shallowRender({ submitting: true })).toMatchSnapshot('submitting');
- expect(
- shallowRender({
- currentQualityGate: mockQualityGate({ id: '2', isDefault: true }),
- selectedQualityGateId: USE_SYSTEM_DEFAULT,
- })
- ).toMatchSnapshot('always use system default');
- expect(shallowRender({ selectedQualityGateId: '3' })).toMatchSnapshot('show new code warning');
- expect(
- shallowRender({
- selectedQualityGateId: '5',
- })
- ).toMatchSnapshot('show warning');
- expect(
- shallowRender({
- selectedQualityGateId: USE_SYSTEM_DEFAULT,
- })
- ).toMatchSnapshot('show warning if not using default');
- expect(shallowRender({ allQualityGates: undefined }).type()).toBeNull(); // no quality gates
-});
-
-it('should render select options correctly', () => {
- return new Promise<void>((resolve) => {
- const wrapper = shallowRender();
- const render = wrapper.find(Select).props().components.Option;
-
- expect(render).toBeDefined();
-
- expect(render({ data: { value: '1', label: 'Gate 1' } })).toMatchSnapshot('default');
- resolve();
- });
-});
-
-it('should correctly handle changes', () => {
- const wrapper = shallowRender();
- const onSelect = jest.fn((selectedQualityGateId) => {
- wrapper.setProps({ selectedQualityGateId });
- });
- wrapper.setProps({ onSelect });
-
- wrapper.find(Radio).at(0).props().onCheck(USE_SYSTEM_DEFAULT);
- expect(onSelect).toHaveBeenLastCalledWith(USE_SYSTEM_DEFAULT);
-
- wrapper.find(Radio).at(1).props().onCheck('1');
- expect(onSelect).toHaveBeenLastCalledWith('1');
-
- wrapper.find(Select).props().onChange!({ value: '2' });
- expect(onSelect).toHaveBeenLastCalledWith('2');
-});
-
-it('should correctly handle form submission', () => {
- const onSubmit = jest.fn();
- const wrapper = shallowRender({ onSubmit });
- submit(wrapper.find('form'));
- expect(onSubmit).toHaveBeenCalled();
-});
-
-function shallowRender(props: Partial<ProjectQualityGateAppRendererProps> = {}) {
- const conditions = [mockCondition(), mockCondition({ metric: MetricKey.new_bugs })];
- const conditionsEmptyOnNew = [mockCondition({ metric: MetricKey.bugs })];
- return shallow<ProjectQualityGateAppRendererProps>(
- <ProjectQualityGateAppRenderer
- allQualityGates={[
- mockQualityGate({ conditions }),
- mockQualityGate({ id: '2', isDefault: true, conditions }),
- mockQualityGate({ id: '3', isDefault: true, conditions: conditionsEmptyOnNew }),
- ]}
- currentQualityGate={mockQualityGate({ id: '1' })}
- loading={false}
- onSelect={jest.fn()}
- onSubmit={jest.fn()}
- selectedQualityGateId="1"
- submitting={false}
- {...props}
- />
- );
-}
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders correctly 1`] = `
-<ProjectQualityGateAppRenderer
- allQualityGates={
- [
- {
- "conditions": [
- {
- "error": "10",
- "id": "1",
- "metric": "coverage",
- "op": "LT",
- },
- {
- "error": "10",
- "id": "1",
- "metric": "new_bugs",
- "op": "LT",
- },
- ],
- "id": "gate1",
- "name": "qualitygate",
- },
- {
- "conditions": [
- {
- "error": "10",
- "id": "1",
- "metric": "coverage",
- "op": "LT",
- },
- {
- "error": "10",
- "id": "1",
- "metric": "new_bugs",
- "op": "LT",
- },
- ],
- "id": "gate2",
- "isBuiltIn": true,
- "name": "qualitygate",
- },
- {
- "conditions": [
- {
- "error": "10",
- "id": "1",
- "metric": "coverage",
- "op": "LT",
- },
- {
- "error": "10",
- "id": "1",
- "metric": "new_bugs",
- "op": "LT",
- },
- ],
- "id": "gate3",
- "isDefault": true,
- "name": "qualitygate",
- },
- {
- "conditions": [
- {
- "error": "10",
- "id": "1",
- "metric": "coverage",
- "op": "LT",
- },
- {
- "error": "10",
- "id": "1",
- "metric": "new_bugs",
- "op": "LT",
- },
- ],
- "id": "gate4",
- "name": "qualitygate",
- },
- ]
- }
- currentQualityGate={
- {
- "id": "gate2",
- "isBuiltIn": true,
- "name": "qualitygate",
- }
- }
- loading={false}
- onSelect={[Function]}
- onSubmit={[Function]}
- selectedQualityGateId="gate2"
- submitting={false}
-/>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: always use system default 1`] = `
-<div
- className="page page-limited"
- id="project-quality-gate"
->
- <Suggestions
- suggestions="project_quality_gate"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="project_quality_gate.page"
- />
- <A11ySkipTarget
- anchor="qg_main"
- />
- <header
- className="page-header"
- >
- <div
- className="page-title display-flex-center"
- >
- <h1>
- project_quality_gate.page
- </h1>
- <HelpTooltip
- className="spacer-left"
- overlay={
- <div
- className="big-padded-top big-padded-bottom"
- >
- quality_gates.projects.help
- </div>
- }
- />
- </div>
- </header>
- <div
- className="boxed-group"
- >
- <h2
- className="boxed-group-header"
- >
- project_quality_gate.subtitle
- </h2>
- <form
- className="boxed-group-inner"
- onSubmit={[Function]}
- >
- <p
- className="big-spacer-bottom"
- >
- project_quality_gate.page.description
- </p>
- <div
- className="big-spacer-bottom"
- >
- <Radio
- checked={true}
- className="display-flex-start"
- disabled={false}
- onCheck={[Function]}
- value="-1"
- >
- <div
- className="spacer-left"
- >
- <div
- className="little-spacer-bottom"
- >
- project_quality_gate.always_use_default
- </div>
- <div
- className="display-flex-center"
- >
- <span
- className="text-muted little-spacer-right"
- >
- current_noun
- :
- </span>
- qualitygate
- </div>
- </div>
- </Radio>
- </div>
- <div
- className="big-spacer-bottom"
- >
- <Radio
- checked={false}
- className="display-flex-start"
- disabled={false}
- onCheck={[Function]}
- value="2"
- >
- <div
- className="spacer-left"
- >
- <div
- className="little-spacer-bottom"
- >
- project_quality_gate.always_use_specific
- </div>
- <div
- className="display-flex-center"
- >
- <Select
- className="abs-width-300 it__project-quality-gate-select"
- components={
- {
- "Option": [Function],
- }
- }
- isClearable={true}
- isDisabled={true}
- onChange={[Function]}
- options={
- [
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "1",
- },
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "2",
- },
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "3",
- },
- ]
- }
- />
- </div>
- </div>
- </Radio>
- </div>
- <div>
- <SubmitButton
- disabled={false}
- >
- save
- </SubmitButton>
- </div>
- </form>
- </div>
-</div>
-`;
-
-exports[`should render correctly: default 1`] = `
-<div
- className="page page-limited"
- id="project-quality-gate"
->
- <Suggestions
- suggestions="project_quality_gate"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="project_quality_gate.page"
- />
- <A11ySkipTarget
- anchor="qg_main"
- />
- <header
- className="page-header"
- >
- <div
- className="page-title display-flex-center"
- >
- <h1>
- project_quality_gate.page
- </h1>
- <HelpTooltip
- className="spacer-left"
- overlay={
- <div
- className="big-padded-top big-padded-bottom"
- >
- quality_gates.projects.help
- </div>
- }
- />
- </div>
- </header>
- <div
- className="boxed-group"
- >
- <h2
- className="boxed-group-header"
- >
- project_quality_gate.subtitle
- </h2>
- <form
- className="boxed-group-inner"
- onSubmit={[Function]}
- >
- <p
- className="big-spacer-bottom"
- >
- project_quality_gate.page.description
- </p>
- <div
- className="big-spacer-bottom"
- >
- <Radio
- checked={false}
- className="display-flex-start"
- disabled={false}
- onCheck={[Function]}
- value="-1"
- >
- <div
- className="spacer-left"
- >
- <div
- className="little-spacer-bottom"
- >
- project_quality_gate.always_use_default
- </div>
- <div
- className="display-flex-center"
- >
- <span
- className="text-muted little-spacer-right"
- >
- current_noun
- :
- </span>
- qualitygate
- </div>
- </div>
- </Radio>
- </div>
- <div
- className="big-spacer-bottom"
- >
- <Radio
- checked={true}
- className="display-flex-start"
- disabled={false}
- onCheck={[Function]}
- value="1"
- >
- <div
- className="spacer-left"
- >
- <div
- className="little-spacer-bottom"
- >
- project_quality_gate.always_use_specific
- </div>
- <div
- className="display-flex-center"
- >
- <Select
- className="abs-width-300 it__project-quality-gate-select"
- components={
- {
- "Option": [Function],
- }
- }
- isClearable={false}
- isDisabled={false}
- onChange={[Function]}
- options={
- [
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "1",
- },
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "2",
- },
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "3",
- },
- ]
- }
- value={
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "1",
- }
- }
- />
- </div>
- </div>
- </Radio>
- </div>
- <div>
- <SubmitButton
- disabled={false}
- >
- save
- </SubmitButton>
- </div>
- </form>
- </div>
-</div>
-`;
-
-exports[`should render correctly: loading 1`] = `
-<i
- className="spinner"
-/>
-`;
-
-exports[`should render correctly: show new code warning 1`] = `
-<div
- className="page page-limited"
- id="project-quality-gate"
->
- <Suggestions
- suggestions="project_quality_gate"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="project_quality_gate.page"
- />
- <A11ySkipTarget
- anchor="qg_main"
- />
- <header
- className="page-header"
- >
- <div
- className="page-title display-flex-center"
- >
- <h1>
- project_quality_gate.page
- </h1>
- <HelpTooltip
- className="spacer-left"
- overlay={
- <div
- className="big-padded-top big-padded-bottom"
- >
- quality_gates.projects.help
- </div>
- }
- />
- </div>
- </header>
- <div
- className="boxed-group"
- >
- <h2
- className="boxed-group-header"
- >
- project_quality_gate.subtitle
- </h2>
- <form
- className="boxed-group-inner"
- onSubmit={[Function]}
- >
- <p
- className="big-spacer-bottom"
- >
- project_quality_gate.page.description
- </p>
- <div
- className="big-spacer-bottom"
- >
- <Radio
- checked={false}
- className="display-flex-start"
- disabled={false}
- onCheck={[Function]}
- value="-1"
- >
- <div
- className="spacer-left"
- >
- <div
- className="little-spacer-bottom"
- >
- project_quality_gate.always_use_default
- </div>
- <div
- className="display-flex-center"
- >
- <span
- className="text-muted little-spacer-right"
- >
- current_noun
- :
- </span>
- qualitygate
- </div>
- </div>
- </Radio>
- </div>
- <div
- className="big-spacer-bottom"
- >
- <Radio
- checked={true}
- className="display-flex-start"
- disabled={false}
- onCheck={[Function]}
- value="3"
- >
- <div
- className="spacer-left"
- >
- <div
- className="little-spacer-bottom"
- >
- project_quality_gate.always_use_specific
- </div>
- <div
- className="display-flex-center"
- >
- <Select
- className="abs-width-300 it__project-quality-gate-select"
- components={
- {
- "Option": [Function],
- }
- }
- isClearable={false}
- isDisabled={false}
- onChange={[Function]}
- options={
- [
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "1",
- },
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "2",
- },
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "3",
- },
- ]
- }
- value={
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "3",
- }
- }
- />
- </div>
- </div>
- </Radio>
- <Alert
- className="abs-width-600 spacer-top"
- variant="warning"
- >
- <FormattedMessage
- defaultMessage="project_quality_gate.no_condition_on_new_code"
- id="project_quality_gate.no_condition_on_new_code"
- values={
- {
- "link": <ForwardRef(Link)
- to={
- {
- "pathname": "/quality_gates/show/qualitygate",
- }
- }
- >
- project_quality_gate.no_condition.link
- </ForwardRef(Link)>,
- }
- }
- />
- </Alert>
- <Alert
- className="big-spacer-top abs-width-600"
- variant="warning"
- >
- project_quality_gate.requires_new_analysis
- </Alert>
- </div>
- <div>
- <SubmitButton
- disabled={false}
- >
- save
- </SubmitButton>
- </div>
- </form>
- </div>
-</div>
-`;
-
-exports[`should render correctly: show warning 1`] = `
-<div
- className="page page-limited"
- id="project-quality-gate"
->
- <Suggestions
- suggestions="project_quality_gate"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="project_quality_gate.page"
- />
- <A11ySkipTarget
- anchor="qg_main"
- />
- <header
- className="page-header"
- >
- <div
- className="page-title display-flex-center"
- >
- <h1>
- project_quality_gate.page
- </h1>
- <HelpTooltip
- className="spacer-left"
- overlay={
- <div
- className="big-padded-top big-padded-bottom"
- >
- quality_gates.projects.help
- </div>
- }
- />
- </div>
- </header>
- <div
- className="boxed-group"
- >
- <h2
- className="boxed-group-header"
- >
- project_quality_gate.subtitle
- </h2>
- <form
- className="boxed-group-inner"
- onSubmit={[Function]}
- >
- <p
- className="big-spacer-bottom"
- >
- project_quality_gate.page.description
- </p>
- <div
- className="big-spacer-bottom"
- >
- <Radio
- checked={false}
- className="display-flex-start"
- disabled={false}
- onCheck={[Function]}
- value="-1"
- >
- <div
- className="spacer-left"
- >
- <div
- className="little-spacer-bottom"
- >
- project_quality_gate.always_use_default
- </div>
- <div
- className="display-flex-center"
- >
- <span
- className="text-muted little-spacer-right"
- >
- current_noun
- :
- </span>
- qualitygate
- </div>
- </div>
- </Radio>
- </div>
- <div
- className="big-spacer-bottom"
- >
- <Radio
- checked={true}
- className="display-flex-start"
- disabled={false}
- onCheck={[Function]}
- value="5"
- >
- <div
- className="spacer-left"
- >
- <div
- className="little-spacer-bottom"
- >
- project_quality_gate.always_use_specific
- </div>
- <div
- className="display-flex-center"
- >
- <Select
- className="abs-width-300 it__project-quality-gate-select"
- components={
- {
- "Option": [Function],
- }
- }
- isClearable={false}
- isDisabled={false}
- onChange={[Function]}
- options={
- [
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "1",
- },
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "2",
- },
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "3",
- },
- ]
- }
- />
- </div>
- </div>
- </Radio>
- <Alert
- className="big-spacer-top abs-width-600"
- variant="warning"
- >
- project_quality_gate.requires_new_analysis
- </Alert>
- </div>
- <div>
- <SubmitButton
- disabled={false}
- >
- save
- </SubmitButton>
- </div>
- </form>
- </div>
-</div>
-`;
-
-exports[`should render correctly: show warning if not using default 1`] = `
-<div
- className="page page-limited"
- id="project-quality-gate"
->
- <Suggestions
- suggestions="project_quality_gate"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="project_quality_gate.page"
- />
- <A11ySkipTarget
- anchor="qg_main"
- />
- <header
- className="page-header"
- >
- <div
- className="page-title display-flex-center"
- >
- <h1>
- project_quality_gate.page
- </h1>
- <HelpTooltip
- className="spacer-left"
- overlay={
- <div
- className="big-padded-top big-padded-bottom"
- >
- quality_gates.projects.help
- </div>
- }
- />
- </div>
- </header>
- <div
- className="boxed-group"
- >
- <h2
- className="boxed-group-header"
- >
- project_quality_gate.subtitle
- </h2>
- <form
- className="boxed-group-inner"
- onSubmit={[Function]}
- >
- <p
- className="big-spacer-bottom"
- >
- project_quality_gate.page.description
- </p>
- <div
- className="big-spacer-bottom"
- >
- <Radio
- checked={true}
- className="display-flex-start"
- disabled={false}
- onCheck={[Function]}
- value="-1"
- >
- <div
- className="spacer-left"
- >
- <div
- className="little-spacer-bottom"
- >
- project_quality_gate.always_use_default
- </div>
- <div
- className="display-flex-center"
- >
- <span
- className="text-muted little-spacer-right"
- >
- current_noun
- :
- </span>
- qualitygate
- </div>
- </div>
- </Radio>
- </div>
- <div
- className="big-spacer-bottom"
- >
- <Radio
- checked={false}
- className="display-flex-start"
- disabled={false}
- onCheck={[Function]}
- value="1"
- >
- <div
- className="spacer-left"
- >
- <div
- className="little-spacer-bottom"
- >
- project_quality_gate.always_use_specific
- </div>
- <div
- className="display-flex-center"
- >
- <Select
- className="abs-width-300 it__project-quality-gate-select"
- components={
- {
- "Option": [Function],
- }
- }
- isClearable={true}
- isDisabled={true}
- onChange={[Function]}
- options={
- [
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "1",
- },
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "2",
- },
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "3",
- },
- ]
- }
- />
- </div>
- </div>
- </Radio>
- <Alert
- className="big-spacer-top abs-width-600"
- variant="warning"
- >
- project_quality_gate.requires_new_analysis
- </Alert>
- </div>
- <div>
- <SubmitButton
- disabled={false}
- >
- save
- </SubmitButton>
- </div>
- </form>
- </div>
-</div>
-`;
-
-exports[`should render correctly: submitting 1`] = `
-<div
- className="page page-limited"
- id="project-quality-gate"
->
- <Suggestions
- suggestions="project_quality_gate"
- />
- <Helmet
- defer={false}
- encodeSpecialCharacters={true}
- prioritizeSeoTags={false}
- title="project_quality_gate.page"
- />
- <A11ySkipTarget
- anchor="qg_main"
- />
- <header
- className="page-header"
- >
- <div
- className="page-title display-flex-center"
- >
- <h1>
- project_quality_gate.page
- </h1>
- <HelpTooltip
- className="spacer-left"
- overlay={
- <div
- className="big-padded-top big-padded-bottom"
- >
- quality_gates.projects.help
- </div>
- }
- />
- </div>
- </header>
- <div
- className="boxed-group"
- >
- <h2
- className="boxed-group-header"
- >
- project_quality_gate.subtitle
- </h2>
- <form
- className="boxed-group-inner"
- onSubmit={[Function]}
- >
- <p
- className="big-spacer-bottom"
- >
- project_quality_gate.page.description
- </p>
- <div
- className="big-spacer-bottom"
- >
- <Radio
- checked={false}
- className="display-flex-start"
- disabled={true}
- onCheck={[Function]}
- value="-1"
- >
- <div
- className="spacer-left"
- >
- <div
- className="little-spacer-bottom"
- >
- project_quality_gate.always_use_default
- </div>
- <div
- className="display-flex-center"
- >
- <span
- className="text-muted little-spacer-right"
- >
- current_noun
- :
- </span>
- qualitygate
- </div>
- </div>
- </Radio>
- </div>
- <div
- className="big-spacer-bottom"
- >
- <Radio
- checked={true}
- className="display-flex-start"
- disabled={true}
- onCheck={[Function]}
- value="1"
- >
- <div
- className="spacer-left"
- >
- <div
- className="little-spacer-bottom"
- >
- project_quality_gate.always_use_specific
- </div>
- <div
- className="display-flex-center"
- >
- <Select
- className="abs-width-300 it__project-quality-gate-select"
- components={
- {
- "Option": [Function],
- }
- }
- isClearable={false}
- isDisabled={true}
- onChange={[Function]}
- options={
- [
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "1",
- },
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "2",
- },
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "3",
- },
- ]
- }
- value={
- {
- "isDisabled": false,
- "label": "qualitygate",
- "value": "1",
- }
- }
- />
- </div>
- </div>
- </Radio>
- </div>
- <div>
- <SubmitButton
- disabled={true}
- >
- save
- </SubmitButton>
- <i
- className="spinner spacer-left"
- />
- </div>
- </form>
- </div>
-</div>
-`;
-
-exports[`should render select options correctly: default 1`] = `
-<Option
- data={
- {
- "label": "Gate 1",
- "value": "1",
- }
- }
->
- <div>
- <DisableableSelectOption
- className="abs-width-100"
- disableTooltipOverlay={[Function]}
- disabledReason="project_quality_gate.no_condition.reason"
- option={
- {
- "label": "Gate 1",
- "value": "1",
- }
- }
- />
- </div>
-</Option>
-`;
this.setState(({ qualityGates }) => {
return {
qualityGates: qualityGates.map((candidate) => {
- if (candidate.isDefault || candidate.id === qualityGate.id) {
- return { ...candidate, isDefault: candidate.id === qualityGate.id };
+ if (candidate.isDefault || candidate.name === qualityGate.name) {
+ return { ...candidate, isDefault: candidate.name === qualityGate.name };
}
return candidate;
}),
};
const submitPromise = condition
? updateCondition({ id: condition.id, ...newCondition })
- : createCondition({ gateId: qualityGate.id, ...newCondition });
+ : createCondition({ gateName: qualityGate.name, ...newCondition });
return submitPromise.then(this.props.onAddCondition);
};
promiseArr.push(
createCondition({
...getCorrectCaycCondition(condition),
- gateId: qualityGate.id,
+ gateName: qualityGate.name,
})
.then((resultCondition) => this.props.onAddCondition(resultCondition))
.catch(() => undefined)
<Projects
canEdit={actions.associateProjects}
// pass unique key to re-mount the component when the quality gate changes
- key={qualityGate.id}
+ key={qualityGate.name}
qualityGate={qualityGate}
/>
)}
<NavLink
className="list-group-item display-flex-center"
aria-current={currentQualityGate === qualityGate.name && 'page'}
- data-id={qualityGate.id}
- key={qualityGate.id}
+ key={qualityGate.name}
to={getQualityGateUrl(qualityGate.name)}
>
<span className="flex-1 text-ellipsis" title={qualityGate.name}>
handleSelect = (key: string) =>
associateGateWithProject({
- gateId: this.props.qualityGate.id,
+ gateName: this.props.qualityGate.name,
projectKey: key,
}).then(() => {
if (this.mounted) {
}
componentDidUpdate(newProps: Props) {
- if (this.props.qualityGate.id !== newProps.qualityGate.id) {
+ if (this.props.qualityGate.name !== newProps.qualityGate.name) {
this.fetchPermissions();
}
}
}
export function checkIfDefault(qualityGate: QualityGate, list: QualityGate[]): boolean {
- const finding = list.find((candidate) => candidate.id === qualityGate.id);
- return (finding && finding.isDefault) || false;
+ return list.find((candidate) => candidate.name === qualityGate.name)?.isDefault ?? false;
}
export function addCondition(qualityGate: QualityGate, condition: Condition): QualityGate {
export function mockQualityGate(overrides: Partial<QualityGate> = {}): QualityGate {
return {
- id: '1',
name: 'qualitygate',
...overrides,
};
setAsDefault?: boolean;
};
conditions?: Condition[];
- id: string;
isBuiltIn?: boolean;
isCaycCompliant?: boolean;
isDefault?: boolean;
project_quality_gate.subtitle=Manage project Quality Gate
project_quality_gate.always_use_default=Always use the instance default Quality Gate
project_quality_gate.always_use_specific=Always use a specific Quality Gate
+project_quality_gate.select_specific_qg=Select Quality Gate
project_quality_gate.requires_new_analysis=Changes will be applied after the next analysis.
project_quality_gate.no_condition=This Quality Gate is empty. To make it usable, add conditions to the {link}.
project_quality_gate.no_condition_on_new_code=This Quality Gate sets conditions on overall code but not on new code. It will not appear on pull requests. To enable it for pull requests, add conditions to the {link}.