aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main
diff options
context:
space:
mode:
authorJeremy Davis <jeremy.davis@sonarsource.com>2021-08-10 17:50:25 +0200
committersonartech <sonartech@sonarsource.com>2021-08-12 20:07:59 +0000
commit94ff5648e0af09146d3db4c9a2fdba1852911022 (patch)
tree3c93d4fd3143c8ed4f0c6e0d49baac8b2c71bc43 /server/sonar-web/src/main
parent5589d0e0da0168d6d4af8ac4611f2dfc63c1687f (diff)
downloadsonarqube-94ff5648e0af09146d3db4c9a2fdba1852911022.tar.gz
sonarqube-94ff5648e0af09146d3db4c9a2fdba1852911022.zip
SONAR-15258 Add App Report Settings
Diffstat (limited to 'server/sonar-web/src/main')
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx18
-rw-r--r--server/sonar-web/src/main/js/app/utils/startReactApp.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/application-settings/ApplicationSettingsApp.tsx136
-rw-r--r--server/sonar-web/src/main/js/apps/application-settings/ReportFrequencyForm.tsx96
-rw-r--r--server/sonar-web/src/main/js/apps/application-settings/__tests__/ApplicationSettingsApp-test.tsx70
-rw-r--r--server/sonar-web/src/main/js/apps/application-settings/__tests__/ReportFrequencyForm-test.tsx70
-rw-r--r--server/sonar-web/src/main/js/apps/application-settings/__tests__/__snapshots__/ApplicationSettingsApp-test.tsx.snap59
-rw-r--r--server/sonar-web/src/main/js/apps/application-settings/__tests__/__snapshots__/ReportFrequencyForm-test.tsx.snap108
-rw-r--r--server/sonar-web/src/main/js/apps/application-settings/routes.ts28
-rw-r--r--server/sonar-web/src/main/js/helpers/mocks/settings.ts15
-rw-r--r--server/sonar-web/src/main/js/types/settings.ts3
11 files changed, 603 insertions, 2 deletions
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx b/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx
index c56114349b5..eee1d068e57 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx
@@ -291,6 +291,7 @@ export class Menu extends React.PureComponent<Props> {
this.renderBranchesLink(query, isProject),
this.renderBaselineLink(query, isApplication, isPortfolio),
this.renderConsoleAppLink(query, isApplication),
+ this.renderReportSettingsLink(query, isApplication),
...this.renderAdminExtensions(query, isApplication),
this.renderProfilesLink(query),
this.renderQualityGateLink(query),
@@ -385,6 +386,23 @@ export class Menu extends React.PureComponent<Props> {
);
};
+ renderReportSettingsLink = (query: Query, isApplication: boolean) => {
+ const extensions = this.getConfiguration().extensions || [];
+ const hasGovernance = extensions.find(e => e.key === 'governance/console');
+
+ if (!isApplication || !hasGovernance) {
+ return null;
+ }
+
+ return (
+ <li key="report-settings">
+ <Link activeClassName="active" to={{ pathname: '/application/settings', query }}>
+ {translate('application_settings.report')}
+ </Link>
+ </li>
+ );
+ };
+
renderProfilesLink = (query: Query) => {
if (!this.getConfiguration().showQualityProfiles) {
return null;
diff --git a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
index f3d5ed8709b..9f1187c0720 100644
--- a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
+++ b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
@@ -32,6 +32,7 @@ import getHistory from 'sonar-ui-common/helpers/getHistory';
import aboutRoutes from '../../apps/about/routes';
import accountRoutes from '../../apps/account/routes';
import applicationConsoleRoutes from '../../apps/application-console/routes';
+import applicationSettingsRoutes from '../../apps/application-settings/routes';
import auditLogsRoutes from '../../apps/audit-logs/routes';
import backgroundTasksRoutes from '../../apps/background-tasks/routes';
import codeRoutes from '../../apps/code/routes';
@@ -203,6 +204,7 @@ function renderComponentRoutes() {
<RouteWithChildRoutes path="project/settings" childRoutes={settingsRoutes} />
<RouteWithChildRoutes path="project_roles" childRoutes={projectPermissionsRoutes} />
<RouteWithChildRoutes path="application/console" childRoutes={applicationConsoleRoutes} />
+ <RouteWithChildRoutes path="application/settings" childRoutes={applicationSettingsRoutes} />
<RouteWithChildRoutes path="project/webhooks" childRoutes={webhooksRoutes} />
<Route
path="project/deletion"
diff --git a/server/sonar-web/src/main/js/apps/application-settings/ApplicationSettingsApp.tsx b/server/sonar-web/src/main/js/apps/application-settings/ApplicationSettingsApp.tsx
new file mode 100644
index 00000000000..1bef269e394
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/application-settings/ApplicationSettingsApp.tsx
@@ -0,0 +1,136 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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 * as React from 'react';
+import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { getDefinitions, getValues, setSimpleSettingValue } from '../../api/settings';
+import { SettingCategoryDefinition, SettingsKey } from '../../types/settings';
+import ReportFrequencyForm from './ReportFrequencyForm';
+
+interface Props {
+ component: T.Component;
+}
+
+interface State {
+ definition?: SettingCategoryDefinition;
+ frequency?: string;
+ loading: boolean;
+}
+
+export default class ApplicationSettingsApp extends React.PureComponent<Props, State> {
+ mounted = false;
+
+ state: State = {
+ loading: true
+ };
+
+ componentDidMount() {
+ this.mounted = true;
+ this.fetchInitialData();
+ }
+
+ async componentDidUpdate(prevProps: Props) {
+ if (prevProps.component.key !== this.props.component.key) {
+ this.setState({ loading: true });
+ await this.fetchSetting();
+ this.setState({ loading: false });
+ }
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ fetchInitialData = async () => {
+ this.setState({ loading: true });
+ await Promise.all([this.fetchDefinition(), this.fetchSetting()]);
+ this.setState({ loading: false });
+ };
+
+ fetchDefinition = async () => {
+ const { component } = this.props;
+ try {
+ const definitions = await getDefinitions(component.key);
+ const definition = definitions.find(d => d.key === SettingsKey.ProjectReportFrequency);
+
+ if (this.mounted) {
+ this.setState({ definition });
+ }
+ } catch (_) {
+ /* do nothing */
+ }
+ };
+
+ fetchSetting = async () => {
+ const { component } = this.props;
+ try {
+ const setting = (
+ await getValues({ component: component.key, keys: SettingsKey.ProjectReportFrequency })
+ ).pop();
+
+ if (this.mounted) {
+ this.setState({
+ frequency: setting ? setting.value : undefined
+ });
+ }
+ } catch (_) {
+ /* do nothing */
+ }
+ };
+
+ handleSubmit = async (frequency: string) => {
+ const { component } = this.props;
+
+ try {
+ await setSimpleSettingValue({
+ component: component.key,
+ key: SettingsKey.ProjectReportFrequency,
+ value: frequency
+ });
+
+ this.setState({ frequency });
+ } catch (_) {
+ /* Do nothing */
+ }
+ };
+
+ render() {
+ const { definition, frequency, loading } = this.state;
+
+ return (
+ <div className="page page-limited application-settings">
+ <h1>{translate('application_settings.page')}</h1>
+ <div className="boxed-group big-padded big-spacer-top">
+ <DeferredSpinner loading={loading}>
+ <div>
+ {definition && frequency && (
+ <ReportFrequencyForm
+ definition={definition}
+ frequency={frequency}
+ onSave={this.handleSubmit}
+ />
+ )}
+ </div>
+ </DeferredSpinner>
+ </div>
+ </div>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/application-settings/ReportFrequencyForm.tsx b/server/sonar-web/src/main/js/apps/application-settings/ReportFrequencyForm.tsx
new file mode 100644
index 00000000000..2d8790133a9
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/application-settings/ReportFrequencyForm.tsx
@@ -0,0 +1,96 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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 * as React from 'react';
+import { Button, ResetButtonLink } from 'sonar-ui-common/components/controls/buttons';
+import Select from 'sonar-ui-common/components/controls/Select';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { sanitizeStringRestricted } from '../../helpers/sanitize';
+import { SettingCategoryDefinition } from '../../types/settings';
+
+export interface ReportFrequencyFormProps {
+ definition: SettingCategoryDefinition;
+ frequency: string;
+ onSave: (value: string) => Promise<void>;
+}
+
+export default function ReportFrequencyForm(props: ReportFrequencyFormProps) {
+ const { definition, frequency } = props;
+ const { defaultValue } = definition;
+
+ const [currentSelection, setCurrentSelection] = React.useState(frequency);
+
+ const options = props.definition.options.map(option => ({
+ label: option,
+ value: option
+ }));
+
+ const handleReset = () => {
+ if (defaultValue) {
+ setCurrentSelection(defaultValue);
+ props.onSave(defaultValue);
+ }
+ };
+
+ return (
+ <div>
+ <h2>{translate('application_settings.report.frequency')}</h2>
+ {definition.description && (
+ <div
+ className="markdown"
+ // eslint-disable-next-line react/no-danger
+ dangerouslySetInnerHTML={{
+ __html: sanitizeStringRestricted(definition.description)
+ }}
+ />
+ )}
+
+ <Select
+ className="input-medium"
+ clearable={false}
+ name={definition.name}
+ onChange={({ value }: { value: string }) => setCurrentSelection(value)}
+ options={options}
+ value={currentSelection}
+ />
+
+ <div className="display-flex-center big-spacer-top">
+ {frequency !== currentSelection && (
+ <Button
+ className="spacer-right button-success"
+ onClick={() => props.onSave(currentSelection)}>
+ {translate('save')}
+ </Button>
+ )}
+
+ {defaultValue !== undefined && frequency !== defaultValue && (
+ <Button className="spacer-right" onClick={handleReset}>
+ {translate('reset_verb')}
+ </Button>
+ )}
+
+ {frequency !== currentSelection && (
+ <ResetButtonLink className="spacer-right" onClick={() => setCurrentSelection(frequency)}>
+ {translate('cancel')}
+ </ResetButtonLink>
+ )}
+ </div>
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/application-settings/__tests__/ApplicationSettingsApp-test.tsx b/server/sonar-web/src/main/js/apps/application-settings/__tests__/ApplicationSettingsApp-test.tsx
new file mode 100644
index 00000000000..a2e8c06391c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/application-settings/__tests__/ApplicationSettingsApp-test.tsx
@@ -0,0 +1,70 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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 { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
+import { setSimpleSettingValue } from '../../../api/settings';
+import { mockComponent } from '../../../helpers/testMocks';
+import { SettingsKey } from '../../../types/settings';
+import ApplicationSettingsApp from '../ApplicationSettingsApp';
+
+jest.mock('../../../api/settings', () => {
+ const { mockDefinition } = jest.requireActual('../../../helpers/mocks/settings');
+
+ const definition = mockDefinition({
+ key: 'sonar.governance.report.project.branch.frequency', // SettingsKey.ProjectReportFrequency
+ defaultValue: 'Monthly',
+ description: 'description',
+ options: ['Daily', 'Weekly', 'Monthly']
+ });
+
+ return {
+ getDefinitions: jest.fn().mockResolvedValue([definition]),
+ getValues: jest.fn().mockResolvedValue([{ value: 'Monthly' }]),
+ setSimpleSettingValue: jest.fn().mockResolvedValue(undefined)
+ };
+});
+
+it('should render correctly', async () => {
+ const wrapper = shallowRender();
+ expect(wrapper).toMatchSnapshot('loading');
+ await waitAndUpdate(wrapper);
+ expect(wrapper).toMatchSnapshot('default');
+});
+
+it('should handle submission', async () => {
+ const component = mockComponent({ key: 'app-key' });
+ const wrapper = shallowRender({ component });
+ await waitAndUpdate(wrapper);
+
+ wrapper.instance().handleSubmit('Daily');
+
+ expect(setSimpleSettingValue).toBeCalledWith({
+ component: component.key,
+ key: SettingsKey.ProjectReportFrequency,
+ value: 'Daily'
+ });
+});
+
+function shallowRender(props: Partial<ApplicationSettingsApp['props']> = {}) {
+ return shallow<ApplicationSettingsApp>(
+ <ApplicationSettingsApp component={mockComponent()} {...props} />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/application-settings/__tests__/ReportFrequencyForm-test.tsx b/server/sonar-web/src/main/js/apps/application-settings/__tests__/ReportFrequencyForm-test.tsx
new file mode 100644
index 00000000000..b7a8b0cb9d5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/application-settings/__tests__/ReportFrequencyForm-test.tsx
@@ -0,0 +1,70 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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 { Button, ResetButtonLink } from 'sonar-ui-common/components/controls/buttons';
+import Select from 'sonar-ui-common/components/controls/Select';
+import { mockDefinition } from '../../../helpers/mocks/settings';
+import ReportFrequencyForm, { ReportFrequencyFormProps } from '../ReportFrequencyForm';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot('default');
+ expect(shallowRender({ frequency: 'Weekly' })).toMatchSnapshot('changed');
+ expect(shallowRender({ definition: mockDefinition() })).toMatchSnapshot('no description');
+});
+
+it('should handle changes', () => {
+ const onSave = jest.fn();
+ const wrapper = shallowRender({ onSave });
+
+ wrapper.find(Select).simulate('change', { value: 'Daily' });
+
+ expect(wrapper.find('.button-success').exists()).toBe(true);
+ expect(wrapper.find(ResetButtonLink).exists()).toBe(true);
+
+ wrapper.find(Button).simulate('click');
+
+ expect(onSave).toBeCalledWith('Daily');
+});
+
+it('should handle reset', () => {
+ const onSave = jest.fn();
+ const wrapper = shallowRender({ frequency: 'Weekly', onSave });
+
+ expect(wrapper.find('.button-success').exists()).toBe(false);
+ wrapper.find(Button).simulate('click');
+
+ expect(onSave).toBeCalledWith('Monthly');
+});
+
+function shallowRender(props: Partial<ReportFrequencyFormProps> = {}) {
+ return shallow<ReportFrequencyFormProps>(
+ <ReportFrequencyForm
+ definition={mockDefinition({
+ defaultValue: 'Monthly',
+ description: 'description',
+ options: ['Daily', 'Weekly', 'Monthly']
+ })}
+ frequency="Monthly"
+ onSave={jest.fn()}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/application-settings/__tests__/__snapshots__/ApplicationSettingsApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/application-settings/__tests__/__snapshots__/ApplicationSettingsApp-test.tsx.snap
new file mode 100644
index 00000000000..60191d42174
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/application-settings/__tests__/__snapshots__/ApplicationSettingsApp-test.tsx.snap
@@ -0,0 +1,59 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: default 1`] = `
+<div
+ className="page page-limited application-settings"
+>
+ <h1>
+ application_settings.page
+ </h1>
+ <div
+ className="boxed-group big-padded big-spacer-top"
+ >
+ <DeferredSpinner
+ loading={false}
+ >
+ <div>
+ <ReportFrequencyForm
+ definition={
+ Object {
+ "category": "foo category",
+ "defaultValue": "Monthly",
+ "description": "description",
+ "fields": Array [],
+ "key": "sonar.governance.report.project.branch.frequency",
+ "options": Array [
+ "Daily",
+ "Weekly",
+ "Monthly",
+ ],
+ "subCategory": "foo subCat",
+ }
+ }
+ frequency="Monthly"
+ onSave={[Function]}
+ />
+ </div>
+ </DeferredSpinner>
+ </div>
+</div>
+`;
+
+exports[`should render correctly: loading 1`] = `
+<div
+ className="page page-limited application-settings"
+>
+ <h1>
+ application_settings.page
+ </h1>
+ <div
+ className="boxed-group big-padded big-spacer-top"
+ >
+ <DeferredSpinner
+ loading={true}
+ >
+ <div />
+ </DeferredSpinner>
+ </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/application-settings/__tests__/__snapshots__/ReportFrequencyForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/application-settings/__tests__/__snapshots__/ReportFrequencyForm-test.tsx.snap
new file mode 100644
index 00000000000..dc8322658c5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/application-settings/__tests__/__snapshots__/ReportFrequencyForm-test.tsx.snap
@@ -0,0 +1,108 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: changed 1`] = `
+<div>
+ <h2>
+ application_settings.report.frequency
+ </h2>
+ <div
+ className="markdown"
+ dangerouslySetInnerHTML={
+ Object {
+ "__html": "description",
+ }
+ }
+ />
+ <Select
+ className="input-medium"
+ clearable={false}
+ onChange={[Function]}
+ options={
+ Array [
+ Object {
+ "label": "Daily",
+ "value": "Daily",
+ },
+ Object {
+ "label": "Weekly",
+ "value": "Weekly",
+ },
+ Object {
+ "label": "Monthly",
+ "value": "Monthly",
+ },
+ ]
+ }
+ value="Weekly"
+ />
+ <div
+ className="display-flex-center big-spacer-top"
+ >
+ <Button
+ className="spacer-right"
+ onClick={[Function]}
+ >
+ reset_verb
+ </Button>
+ </div>
+</div>
+`;
+
+exports[`should render correctly: default 1`] = `
+<div>
+ <h2>
+ application_settings.report.frequency
+ </h2>
+ <div
+ className="markdown"
+ dangerouslySetInnerHTML={
+ Object {
+ "__html": "description",
+ }
+ }
+ />
+ <Select
+ className="input-medium"
+ clearable={false}
+ onChange={[Function]}
+ options={
+ Array [
+ Object {
+ "label": "Daily",
+ "value": "Daily",
+ },
+ Object {
+ "label": "Weekly",
+ "value": "Weekly",
+ },
+ Object {
+ "label": "Monthly",
+ "value": "Monthly",
+ },
+ ]
+ }
+ value="Monthly"
+ />
+ <div
+ className="display-flex-center big-spacer-top"
+ />
+</div>
+`;
+
+exports[`should render correctly: no description 1`] = `
+<div>
+ <h2>
+ application_settings.report.frequency
+ </h2>
+ <Select
+ className="input-medium"
+ clearable={false}
+ onChange={[Function]}
+ options={Array []}
+ value="Monthly"
+ />
+ <div
+ className="display-flex-center big-spacer-top"
+ />
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/application-settings/routes.ts b/server/sonar-web/src/main/js/apps/application-settings/routes.ts
new file mode 100644
index 00000000000..18822c1779c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/application-settings/routes.ts
@@ -0,0 +1,28 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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 { lazyLoadComponent } from 'sonar-ui-common/components/lazyLoadComponent';
+
+const routes = [
+ {
+ indexRoute: { component: lazyLoadComponent(() => import('./ApplicationSettingsApp')) }
+ }
+];
+
+export default routes;
diff --git a/server/sonar-web/src/main/js/helpers/mocks/settings.ts b/server/sonar-web/src/main/js/helpers/mocks/settings.ts
index 59fe8ecb25c..14065d55238 100644
--- a/server/sonar-web/src/main/js/helpers/mocks/settings.ts
+++ b/server/sonar-web/src/main/js/helpers/mocks/settings.ts
@@ -17,7 +17,20 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { Setting, SettingWithCategory } from '../../types/settings';
+import { Setting, SettingCategoryDefinition, SettingWithCategory } from '../../types/settings';
+
+export function mockDefinition(
+ overrides: Partial<SettingCategoryDefinition> = {}
+): SettingCategoryDefinition {
+ return {
+ key: 'foo',
+ category: 'foo category',
+ fields: [],
+ options: [],
+ subCategory: 'foo subCat',
+ ...overrides
+ };
+}
export function mockSetting(overrides: Partial<Setting> = {}): Setting {
return {
diff --git a/server/sonar-web/src/main/js/types/settings.ts b/server/sonar-web/src/main/js/types/settings.ts
index 6432a176068..b272b5b9943 100644
--- a/server/sonar-web/src/main/js/types/settings.ts
+++ b/server/sonar-web/src/main/js/types/settings.ts
@@ -21,7 +21,8 @@ export const enum SettingsKey {
DaysBeforeDeletingInactiveBranchesAndPRs = 'sonar.dbcleaner.daysBeforeDeletingInactiveBranchesAndPRs',
DefaultProjectVisibility = 'projects.default.visibility',
ServerBaseUrl = 'sonar.core.serverBaseURL',
- PluginRiskConsent = 'sonar.plugins.risk.consent'
+ PluginRiskConsent = 'sonar.plugins.risk.consent',
+ ProjectReportFrequency = 'sonar.governance.report.project.branch.frequency'
}
export type Setting = SettingValue & { definition: SettingDefinition };